diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-29 22:32:55 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-29 22:32:55 +0300 |
| commit | 18c8e8f5f3d7cc9bbbcb9b8b9be65477110363a7 (patch) | |
| tree | 5a27462787e0591991558056fc3f152ca6bbe260 /internal/generate/codegen_test.go | |
| parent | 5aadfad3a145de9967120065587d830f09ad87aa (diff) | |
test(generate): lock in access/faccessat path classification
Audit of access(2) found the tracing implementation already correct:
FS family, KindPathname capturing the real path, and an UNCLASSIFIED
int 0/-1 ret_event on exit. access(2) captures its path from args[0]
(no dirfd), while siblings faccessat(2)/faccessat2(2) capture from
args[1] (dfd precedes the path). mage generate produces no diff and the
docs/integration coverage already match.
Add unit lock-in tests mirroring prior syscall audits:
- FormatAccess/FormatFaccessat tracepoint fixtures (real kernel formats).
- classify tests asserting both classify as KindPathname/"filename".
- family_test cluster asserting access/faccessat/faccessat2 stay FamilyFS.
- codegen test proving access reads ctx->args[0] while faccessat reads
ctx->args[1], guarding against a wrong-arg or dropped-path regression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'internal/generate/codegen_test.go')
| -rw-r--r-- | internal/generate/codegen_test.go | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 814f114..de64e3c 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -602,6 +602,35 @@ func TestGeneratePathnameHandler(t *testing.T) { requireContains(t, output, "bpf_probe_read_user_str(ev->pathname, sizeof(ev->pathname), (void*)ctx->args[0]);") } +// TestGenerateAccessFaccessatHandlers locks in the generated BPF C for +// access(2) and its dirfd-relative sibling faccessat(2). Both capture a real +// path into a path_event's pathname member, but from DIFFERENT argument slots: +// access(2) has no dirfd so its path is at args[0], whereas faccessat(2) takes +// dfd at args[0] and the path at args[1]. This guards against a regression that +// would read the wrong arg (e.g. capturing faccessat's dirfd as a path, or +// dropping access's path entirely). The exit side is a ret_event (int 0/-1, +// UNCLASSIFIED) — verified via the shared ret_event handler shape. +func TestGenerateAccessFaccessatHandlers(t *testing.T) { + exitAccess := strings.Replace(FormatExitRead, "sys_exit_read", "sys_exit_access", 1) + exitAccess = strings.Replace(exitAccess, "ID: 843", "ID: 816", 1) + accessOut := generateFromPair(t, FormatAccess, exitAccess) + requireContains(t, accessOut, `SEC("tracepoint/syscalls/sys_enter_access")`) + requireContains(t, accessOut, "struct path_event *ev") + requireContains(t, accessOut, "ev->event_type = ENTER_PATH_EVENT;") + requireContains(t, accessOut, "ev->trace_id = SYS_ENTER_ACCESS;") + // access(2): path (filename) is at args[0] — no dirfd precedes it. + requireContains(t, accessOut, "bpf_probe_read_user_str(ev->pathname, sizeof(ev->pathname), (void*)ctx->args[0]);") + + exitFaccessat := strings.Replace(FormatExitRead, "sys_exit_read", "sys_exit_faccessat", 1) + exitFaccessat = strings.Replace(exitFaccessat, "ID: 843", "ID: 820", 1) + faccessatOut := generateFromPair(t, FormatFaccessat, exitFaccessat) + requireContains(t, faccessatOut, `SEC("tracepoint/syscalls/sys_enter_faccessat")`) + requireContains(t, faccessatOut, "struct path_event *ev") + requireContains(t, faccessatOut, "ev->trace_id = SYS_ENTER_FACCESSAT;") + // faccessat(2): dfd is at args[0], so the path (filename) is at args[1]. + requireContains(t, faccessatOut, "bpf_probe_read_user_str(ev->pathname, sizeof(ev->pathname), (void*)ctx->args[1]);") +} + func TestGenerateFcntlHandler(t *testing.T) { output := generateFromPair(t, FormatFcntl, FormatExitFcntl) |
