Harden BCC syscall probe attachment

This commit is contained in:
Luna 2026-07-08 17:04:45 -07:00
parent 2d23e8b878
commit 93ac47dd82
2 changed files with 44 additions and 6 deletions

View file

@ -22,7 +22,7 @@ _BPF_PROGRAM = r"""
#include <uapi/linux/ptrace.h> #include <uapi/linux/ptrace.h>
#include <linux/sched.h> #include <linux/sched.h>
#define ARGLEN 160 #define ARGLEN 128
struct data_t { struct data_t {
u32 pid; u32 pid;
@ -64,6 +64,42 @@ int syscall__execve(struct pt_regs *ctx,
""" """
def attach_kprobe_candidates(bpf, syscall: str, fn_name: str,
extra: tuple[str, ...] = ()) -> str:
"""Attach a kprobe to the first syscall symbol that exists on this kernel."""
candidates = []
try:
candidates.append(bpf.get_syscall_fnname(syscall))
except Exception:
pass
candidates.extend(extra)
candidates.extend([
f"__x64_sys_{syscall}",
f"__x64_sys_{syscall}at",
f"__arm64_sys_{syscall}",
f"__arm64_sys_{syscall}at",
f"__ia32_sys_{syscall}",
f"__ia32_sys_{syscall}at",
f"__sys_{syscall}",
f"sys_{syscall}",
])
seen: set[str] = set()
last_exc: Exception | None = None
for fnname in candidates:
if not fnname or fnname in seen:
continue
seen.add(fnname)
try:
bpf.attach_kprobe(event=fnname, fn_name=fn_name)
return fnname
except Exception as exc:
last_exc = exc
continue
raise RuntimeError(
f"could not attach {syscall} kprobe (tried {', '.join(sorted(seen))})"
) from last_exc
def available() -> tuple[bool, str]: def available() -> tuple[bool, str]:
"""Return (ok, reason). ok=True means the probe can be loaded.""" """Return (ok, reason). ok=True means the probe can be loaded."""
if os.geteuid() != 0: if os.geteuid() != 0:
@ -91,11 +127,14 @@ class BccExecSource:
from bcc import BPF from bcc import BPF
self._bpf = BPF(text=_BPF_PROGRAM) self._bpf = BPF(text=_BPF_PROGRAM)
fnname = self._bpf.get_syscall_fnname("execve") fnname = self._attach_execve_probe()
self._bpf.attach_kprobe(event=fnname, fn_name="syscall__execve")
self._bpf["events"].open_perf_buffer(self._handle, page_cnt=64) self._bpf["events"].open_perf_buffer(self._handle, page_cnt=64)
self._running = True self._running = True
def _attach_execve_probe(self) -> str:
assert self._bpf is not None
return attach_kprobe_candidates(self._bpf, "execve", "syscall__execve")
def poll(self, timeout_ms: int = 200) -> None: def poll(self, timeout_ms: int = 200) -> None:
if self._bpf is not None: if self._bpf is not None:
self._bpf.perf_buffer_poll(timeout=timeout_ms) self._bpf.perf_buffer_poll(timeout=timeout_ms)

View file

@ -4,7 +4,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from .bcc_source import _decode, available from .bcc_source import _decode, attach_kprobe_candidates, available
from .syscall_event import SyscallEvent from .syscall_event import SyscallEvent
_BPF_PROGRAM = r""" _BPF_PROGRAM = r"""
@ -180,8 +180,7 @@ class BccSyscallSource:
attached = 0 attached = 0
for syscall, fn_name in _PROBES.items(): for syscall, fn_name in _PROBES.items():
try: try:
event = self._bpf.get_syscall_fnname(syscall) attach_kprobe_candidates(self._bpf, syscall, fn_name)
self._bpf.attach_kprobe(event=event, fn_name=fn_name)
attached += 1 attached += 1
except Exception as exc: except Exception as exc:
self.attach_errors.append(f"{syscall}: {exc!r}") self.attach_errors.append(f"{syscall}: {exc!r}")