| Age | Commit message (Collapse) | Author |
|
removexattrat(2) (Linux 6.13+) was the one xattr *at-variant lacking
integration coverage: xattr_test.go exercised getxattrat/listxattrat
(READ-classified byte counts) and the path-based setxattr, but never the
REMOVE *at variant. Unlike its getxattrat/listxattrat siblings,
removexattrat returns a 0/-1 status (not a byte count), so its exit must
be UNCLASSIFIED — matching removexattr/lremovexattr/fremovexattr.
Add an ioworkload scenario (xattr-removexattrat) that setxattr's a
user.* attribute via a real path then removes it via raw
removexattrat(AT_FDCWD, path, 0, name), plus TestXattrRemovexattrat
asserting the path (args[1], after the dirfd) is captured (never the
xattr name at args[3]) and that accounted bytes are exactly zero
(guarding against wrongly READ-classifying it like getxattrat/
listxattrat). Distinct from the fd-based fremovexattr gap (task 8i0).
Classification verified by inspection: FamilyFS (xattr marker),
KindPathname at the pathname field (ctx->args[1]), and absent from the
ret-classification table => UNCLASSIFIED. No generator change needed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
fanotify_init(2) creates and initializes an fanotify notification group
and returns an event-queue file descriptor. It is the direct analog of
inotify_init1 (both are filesystem-event notification facilities whose
group-creating syscall is a flags-taking fd-creator). inotify_init/
inotify_init1 are FamilyIPC alongside the other fd-based event-notification
primitives (eventfd, signalfd, timerfd, userfaultfd), yet fanotify_init
fell through to FamilyMisc by omission from the explicit family table -- an
alarm/adjtimex-style misclassification inconsistent with its siblings.
Add fanotify_init to the IPC family map and regenerate. Kind
(KindEventfd, flags at args[0]) and ret (UNCLASSIFIED, returned fd captured
via the fd mechanism) were already correct and are unchanged. fanotify_mark
stays in Misc (path-marking, not fd creation). Docs plan updated to keep
the docs-drift test in sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Extend the mountfs-management scenario with best-effort fsconfig (KindFd),
fspick (KindPathname), and open_tree (KindOpen) calls to complete
new-mount-API end-to-end coverage. fsconfig reuses the fscontext fd from
fsopen (FSCONFIG_SET_STRING + FSCONFIG_CMD_CREATE), fspick targets "/"
with FSPICK_NO_AUTOMOUNT, and open_tree clones the scenario mount point
with OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC. All returned fds are closed and
all errno values are ignored, so ENOSYS/EPERM/EINVAL/EBADF are tolerated;
the sys_enter_ tracepoints fire on kernel entry regardless, creating no
mounts on the host.
Assert enter_fsconfig/enter_fspick/enter_open_tree (MinCount>=1) in
TestMountFsManagementSyscalls and add the three syscalls to the trace
filter. Gating is unchanged (root-only via the shared harness).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
mq_timedsend(2)/mq_send(3) return 0 on success or -1 on error; the
payload size msg_len is an INPUT argument, never the return value. It was
wrongly listed in retClassifications as WriteClassified, which made
bytesFromRet attribute its 0 return as "bytes written" (the stats engine
WriteClassified path). Remove it so its return stays UNCLASSIFIED,
consistent with its POSIX mq sibling mq_timedreceive (which legitimately
stays ReadClassified because it returns the received byte count). This is
the exact same defect just fixed for SysV msgsnd (5057bd9) and mirrors
the msgrcv/msgsnd asymmetry.
Regenerated tracepoints/docs accordingly and updated the pre-existing
classify unit test and the TestPosixMqBasic integration assertion: the
mq_timedsend send no longer asserts a write byte count (now expects 0),
while mq_timedreceive keeps its received-byte-count assertion. Verified:
mage generate idempotent, mage build OK, internal/generate tests pass.
TestPosixMqBasic skips in this sandbox (mq_open: permission denied) but
compiles with the corrected assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The xattr integration tests previously only asserted on getxattrat/
listxattrat (path/dirfd READ-classified variants). The path-based
setxattr(2) was traced by the existing xattr-getxattrat scenario
(workload calls syscall.Setxattr; xattrTraceArgs includes setxattr) but
never asserted. Add TestXattrSetxattr to verify end-to-end that:
- enter_setxattr captures the filesystem PATH at args[0] (kind=pathname),
never the xattr NAME at args[1];
- exit_setxattr is UNCLASSIFIED: setxattr returns a 0/-1 status, not a
byte count (the size arg is the INPUT value length), so accounted
bytes must be exactly zero. This guards against the msgsnd-style bug
of treating a status return as bytes written, and contrasts with
getxattr/listxattr which DO return byte counts (READ-classified).
This is the PATH-based set complement to filed task 8i0 (fd-based
fsetxattr/fgetxattr/flistxattr/fremovexattr); it does not duplicate it.
Classification (FamilyFS, KindPathname enter, UNCLASSIFIED ret) is
verified by inspection per task guidance, not by a unit test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The sched-basic scenario already issues sched_rr_get_interval(0, &ts)
via schedQueryPriorityRange, but TestSchedBasic only asserted the other
sched_get* queries. Add an explicit enter_sched_rr_get_interval
assertion to lock in its KindNull/UNCLASSIFIED tracing end-to-end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
msgsnd(2) returns 0 on success or -1 on error; the payload size msgsz is
an INPUT argument, never the return value. It was wrongly listed in
retClassifications as WriteClassified, which made the stats engine treat
its 0 return as "bytes written". Remove it so its return stays
UNCLASSIFIED, consistent with its SysV IPC siblings (msgrcv legitimately
stays ReadClassified because it returns a received byte count).
Regenerated tracepoints/docs accordingly. Verified: mage generate
idempotent, mage build OK, internal/generate tests pass, and the
TestSysVMsgBasic integration test (added in task 7i0) still passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
alarm(2) arranges for a SIGALRM after a given number of seconds; it is a
simplified setitimer(ITIMER_REAL) and, per alarm(2) NOTES, "alarm() and
setitimer(2) share the same timer; calls to one will interfere with use of
the other." The syscallFamilies table omitted alarm, so it fell through to
FamilyMisc while its siblings setitimer/getitimer/timer_create were correctly
FamilyTime — an adjtimex-style misclassification (cf. 7243b7c). Add
alarm -> FamilyTime and move it from Misc to Time in the tracing plan;
regenerate the family maps (trace IDs 468/469 now FamilyTime,
"alarm": "Time").
Kind classification (KindNull/null_event: the single arg is an unsigned int
seconds, no fd/path) and the UNCLASSIFIED return (seconds remaining, not a
byte count; alarm never fails) were already correct.
Also harden the misc-basic integration test with a deterministic
enter_alarm assertion (alarm(0) is issued unconditionally by the scenario;
the syscall-entry tracepoint always fires) so the alarm enter path is
covered end-to-end even though alarm is now FamilyTime rather than Misc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Add sysv-msg-basic and sysv-sem-basic ioworkload scenarios that exercise
the SysV message-queue and semaphore families end-to-end via raw syscalls,
mirroring the existing sysv-shm-basic scenario.
sysv-msg-basic: msgget(IPC_PRIVATE) -> msgsnd -> msgrcv -> msgctl(IPC_RMID),
using a struct msgbuf {int64 mtype; [16]byte mtext} and msgsz = body length
(excluding mtype). sysv-sem-basic: semget(IPC_PRIVATE, 1) -> semop(+1) ->
semop(-1) -> semctl(IPC_RMID), incrementing before decrementing so the
operation can never block. Both defer IPC_RMID right after the get so no
kernel IPC object leaks even on partial failure.
Add TestSysVMsgBasic and TestSysVSemBasic asserting the enter_ events for
msgget/msgsnd/msgrcv/msgctl and semget/semop/semctl are traced with
MinCount>=1 and positive duration, plus PID/comm hermetic guards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Add a self-targeted, non-disruptive sched-basic ioworkload scenario and a
dedicated TestSchedBasic integration test. The scenario pins to one OS thread
(LockOSThread) and exercises only safe Sched syscalls: sched_yield;
sched_getaffinity then sched_setaffinity re-applying the identical mask (a
no-op); and read-only sched_getscheduler, sched_getparam, sched_getattr,
sched_get_priority_max/min, and sched_rr_get_interval. sched_setscheduler,
sched_setattr, and sched_setparam are intentionally excluded.
The test scopes -trace-syscalls to the sched_* family, guards on PID and comm,
and asserts enter_ tracepoints fire (MinCount>=1) for sched_yield,
sched_getaffinity, sched_getscheduler, and sched_getparam.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Add a misc-basic ioworkload scenario and an end-to-end integration test
for the previously-uncovered Misc syscall family.
The scenario exercises only the safe, unprivileged, non-blocking,
side-effect-free Misc syscalls: getcpu (raw SYS_GETCPU), uname /
sys_newuname (unix.Uname), sysinfo (unix.Sysinfo), vmsplice into a
self-created and self-drained pipe with a tiny buffer, and alarm(0) to
cancel any pending alarm. Code comments document why the remaining Misc
syscalls are intentionally excluded (CAP_SYS_ADMIN / global host
mutation, CAP_SYS_RAWIO / x86-only, Linux 6.13+ availability,
runtime-managed, or not user-callable).
misc_test.go asserts enter_getcpu, enter_newuname, and enter_sysinfo are
each traced at least once for the ioworkload process, restricting tracing
to the issued syscalls and keeping the existing PID/comm hermetic guards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The SIGNALS syscall family previously had zero end-to-end coverage
(signalfd/signalfd4 are IPC-family fd creators, not Signals). Add a
self-targeting ioworkload scenario and an integration test that assert
the family's tracepoints fire.
scenario_signals.go (signals-basic) issues, all self-directed so it
mutates no other process:
- rt_sigaction : install SIG_IGN disposition for SIGUSR1
- rt_sigprocmask: BLOCK SIGUSR1 before sending, so self-delivery only
marks it pending (never runs a handler / kills us)
- sigaltstack : set then disable an alternate signal stack
- kill/tgkill/tkill/rt_sigqueueinfo: send SIGUSR1 to self four ways
- rt_sigpending : query the pending mask
- rt_sigtimedwait: reap the pending signal with a SHORT 100ms timeout
(hang guard); EAGAIN tolerated
Safety: signal is blocked before any send; rt_sigtimedwait uses a 100ms
timeout so it cannot hang; the original signal mask and SIGUSR1
disposition are restored and the alt stack disabled on exit. The
goroutine is pinned with LockOSThread so gettid() matches the
tgkill/tkill target and the per-thread mask applies to the waiting
thread. Raw syscalls are issued directly so the tracepoints fire
regardless of the Go runtime's own signal handling. pause (noreturn) and
rt_sigreturn (handler-return only) are deliberately excluded.
signals_test.go asserts enter_ tracepoints with MinCount>=1 for
rt_sigaction, rt_sigprocmask, rt_sigpending, sigaltstack, kill, tgkill,
and rt_sigtimedwait, plus a positive duration for the rt_sigtimedwait
enter/exit pair.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
TestCommPropagation and TestEventTypeFiltering/FdEventFiltering were
flaky in full `mage test` runs (passing in isolation). For synthetic
events whose tid was not in the comm cache, the event loop fell back to
commResolver's default resolveFn, which reads /proc/<tid>/comm on the
host. The fixed test pids/tids are small (e.g. defaultTid+100 == 111,
defaultPid+1 == 11) and collide with real transient kernel threads
(e.g. kworker/0:1-events), so the resolved comm depended on what
happened to be running on the host at that instant.
Fix: use commResolver's existing injectable resolveFn seam. Add a
newHermeticCommResolver() test helper whose resolveFn returns ("", nil)
and never touches /proc, and inject it into TestCommPropagation (via
eventLoopConfig.commResolver) and newEventLoopWithFilter (used by
TestEventTypeFiltering). No production code changes.
Assertions are unchanged: positive comm names still come from the
synthetic OpenEvent.Comm bytes; cache-miss tids now deterministically
resolve to empty regardless of host state. Updated the stale
"use a very large TID to avoid /proc collisions" comment accordingly.
Verified: -count=50 (affected tests) and -race -count=10 green, full
`mage test` and `mage build` green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The SysV shared-memory family (shmget/shmat/shmdt/shmctl) had no
end-to-end integration coverage. Add an ioworkload `sysv-shm-basic`
scenario that, without privileges, runs shmget(IPC_PRIVATE) -> shmat ->
write into the mapped segment -> shmdt -> shmctl(IPC_RMID), always
issuing IPC_RMID (via defer) so no kernel segment leaks.
Add TestSysVShmBasic asserting enter_shmget/enter_shmat/enter_shmdt/
enter_shmctl are each traced with a positive (paired enter/exit)
duration.
msg/sem coverage is scoped out and tracked as a follow-up task (7i0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Add write-side positional vectored coverage to close the remaining gap
in the pwrite64-family byte-accounting validation. The retbytes/readwrite
integration suite already exercised pwrite64 (scalar) and the read-side
preadv/preadv2, but the WRITE_CLASSIFIED byte attribution for the
vectored positional writes pwritev/pwritev2 was only covered by unit
tests, not end-to-end.
New ioworkload scenarios:
- readwrite-pwritev: issues pwritev (syscall.SYS_PWRITEV) writing a known
two-iovec payload at offset 0 to a temp file.
- readwrite-pwritev2: issues pwritev2 via the explicit syscall number
(328 amd64 / 287 arm64, mirroring preadv2SyscallNr) with offset 0 and
no flags.
New integration tests assert enter_pwritev/enter_pwritev2 fired and that
the attributed retbytes equal the exact iovec total, validating
WRITE_CLASSIFIED end-to-end. Both pass as root.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The BPF side reserves sizeof(struct ...) via bpf_ringbuf_reserve, so the
userspace ringbuf sample length equals the C struct size INCLUDING trailing
alignment padding. For five event structs whose last field is 32-bit but
which also contain a __u64 (forcing 8-byte trailing pad), the *EventSize
fast-decode gate constants were set to the unpadded field-sum size instead
of sizeof:
open_event 300 -> 304
fd_event 28 -> 32
ret_event 36 -> 40
socket_event 36 -> 40
open_by_handle_at_event 28 -> 32
Because the kernel payload length never equalled those constants, the fast
explicit-offset decoders were silently bypassed and every such event
(including the very hot ret_event on each syscall exit, and fd_event on
read/write/etc enter) fell back to the slow reflection-based binary.Read
path. binary.Read reads only the field bytes and ignores trailing padding,
so values were always correct -- this was a performance regression, not a
correctness bug; verified by decoding padded payloads.
Fix mirrors the existing socketpair/accept/pipe/eventfd/poll V1/V2 handling:
the size gate now accepts both the kernel sizeof and the legacy field-sum
size (the latter still emitted by Go binary.Write in tests and Bytes()), and
the trailing pad bytes are ignored. Added kernel-layout tests feeding the
padded payload for all five fixed decoders.
Audit (task i20) confirmed every other event struct's Go layout, field order,
and fast-decode offsets match the C side byte-for-byte.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Classification correctness (which family/kind/return-class a syscall maps
to) is verified by inspection against the man pages and the classifier
rules, not by dedicated unit tests. The tracing-relevant outcome — which
fd/path/byte-count the generated BPF C actually captures — is covered by
the GenerateTracepointsC codegen tests and the end-to-end integration
tests, all of which are retained.
Removed:
- internal/generate/family_test.go (ClassifySyscallFamily / .Family table)
- internal/generate/retclassify_test.go (ClassifyRet read/write/transfer/
unclassified tables)
- ~70 pure-classification tests trimmed from classify_test.go, keeping only
the GenerateTracepointsC codegen/tracing tests plus the shared helpers
(mustParseAll, mqFormats, phaseAFormats, syntheticEnter/Exit, itoa) used
by codegen_test.go.
- pure-classification funcs interleaved in codegen_test.go
(TestClassifyRet*Unclassified, TestClassifyTkillFallsThroughToNull,
Test{Mkdirat,Rmdir}FamilyAndKindMatchSiblings).
Kept all TestGenerate* handler tests (they assert the generated BPF C
captures the correct fd/path/arg-index/return classification), the
isNoreturnSyscall tests, docs-drift guards, eventloop dispatch tests, and
the integration suite — so every affected syscall still has tracing
coverage. No tracing gaps discovered.
generate package: go test (incl. -race) green; mage build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
adjtimex(2) and clock_adjtime(2) share one man page: both tune or query
the kernel clock (clock_adjtime is adjtimex with an explicit clockid) and
return a clock-state code or -1. The syscallFamilies table omitted
adjtimex, so it fell through to FamilyMisc while its sibling clock_adjtime
was correctly FamilyTime. Add adjtimex -> FamilyTime and move it from
Misc to Time in the tracing plan; regenerate the family maps (trace IDs
418/419 now FamilyTime, "adjtimex": "Time").
Kind classification (KindNull/null_event) and the UNCLASSIFIED return
(a clock-state code, not a byte count) were already correct.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
readahead(2) was traced (KindFd enter fd_event from args[0], UNCLASSIFIED
exit ret_event) but had no integration or ioworkload scenario coverage,
unlike its sibling sync_file_range. Add readwrite-readahead and
readwrite-readahead-ebadf scenarios plus TestReadwriteReadahead /
TestReadwriteReadaheadEbadf, asserting enter_readahead capture with path
attribution, zero attributed bytes (readahead returns 0/-1, not a byte
count, so it is correctly UNCLASSIFIED), and positive end-to-end duration.
No classification change: inspection confirms KindFd / UNCLASSIFIED is
correct per man 2 readahead; bytesFromRet returns 0 for UNCLASSIFIED so
the 0/-1 return is never misattributed as bytes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
listxattrat(2) (Linux 6.13+) returns the size in bytes of the list of
extended attribute names, exactly like listxattr/llistxattr/flistxattr,
but its exit was classified UNCLASSIFIED, so its read bytes were dropped
from I/O totals. Classify it as ReadClassified and regenerate the BPF
handler (ret_type now READ_CLASSIFIED). This mirrors the getxattrat fix
(task ku, commit c3177bd) and completes xattr-family consistency:
get-family and list-family are READ_CLASSIFIED while set-family and
remove-family stay UNCLASSIFIED (they return 0/-1).
Update the docs ReadClassified list and the retclassify expectation, and
add an ioworkload scenario plus integration test: the workload sets a
user xattr then lists names via the raw listxattrat(2) syscall with
AT_FDCWD, and the test asserts enter_listxattrat captures the file path
and accounts the returned name-list size as read bytes.
Task: r20
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
getxattrat(2) (Linux 6.13+) returns the xattr value size in bytes,
exactly like getxattr/lgetxattr/fgetxattr, but its exit was classified
UNCLASSIFIED, so its read bytes were dropped from I/O totals. Classify
it as ReadClassified and regenerate the BPF handler (ret_type now
READ_CLASSIFIED). Path extraction (args[1], after the dirfd) and the
name-not-captured-as-path behaviour were already correct.
Update the docs ReadClassified list and the retclassify expectation,
and add the first xattr integration coverage: an ioworkload scenario
that sets then getxattrat-reads a user xattr on tmpfs, plus a test that
asserts enter_getxattrat captures the file path (not the xattr name)
and accounts the returned value size as read bytes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The POSIX per-process timer family (timer_create, timer_settime,
timer_gettime, timer_getoverrun, timer_delete) had no end-to-end
integration coverage; only the unrelated fd-returning sibling
timerfd_create was exercised. Add a posix-timer-lifecycle workload
scenario and TestPosixTimerLifecycle to validate these are traced as
null_events, and guard against timer_create being misclassified like
timerfd_create (timer_create returns a timer_t via an output pointer,
not an fd, so its records must carry no 'timerfd:' descriptor path).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The retbytes integration coverage exercised read/write/sendto/etc but the
positional read p-variants only had presence assertions (pread64) or no
coverage at all (preadv/preadv2), so their READ_CLASSIFIED byte accounting
was validated only by unit tests, not end-to-end.
Add a positive byte-count assertion to TestReadwritePread and new
readwrite-preadv / readwrite-preadv2 workload scenarios plus integration
tests that read a known payload and assert the attributed byte count,
mirroring the existing pwrite64 assertion. preadv2 lacks a Go
syscall.SYS_PREADV2 constant, so its number is provided per-GOARCH
(amd64=327, arm64=286) following the securitySyscallNumbers pattern.
Addresses the read side of b20.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
openat2(2) is the one open-family syscall with a structurally distinct
argument layout: flags/mode live inside the open_how struct (args[2]),
not as a plain int, and args[3] is the struct size. The tracer correctly
reads the path from args[1] and omits flags (ev->flags = -1) rather than
misreading the struct ptr/size, and registers the returned fd->path
mapping via the shared handleOpenExit path. This was verified by
inspection but had no integration scenario, unlike open/openat/creat/
open_by_handle_at.
Add an open-openat2 ioworkload scenario issuing the raw openat2 syscall
(Go has no wrapper and routes Open through openat) and a TestOpenOpenat2
integration test asserting the enter_openat2 tracepoint captures the
path. Verified passing as root.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The mountfs-management integration scenario covered the new mount API
syscalls fsmount/move_mount/mount/umount/pivot_root but not fsopen, the
API's entry point and a direct eventfd-kind sibling of fsmount. Add a
best-effort fsopen("tmpfs", FSOPEN_CLOEXEC) call (closing the returned
context fd on success) and assert enter_fsopen is traced.
fsopen's tracing is otherwise correct: args[1] flags captured, args[0]
fsname (a filesystem TYPE, not a path) deliberately not treated as a
pathname, returned fd registered as the 'fsopenfd:<flags>' descriptor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of io_submit tracing (task 0v) confirmed the tracer is correct by
inspection: KindNull (sys_enter_io_ prefix rule) so ctx_id/nr/iocbpp are
opaque and no fd/path is captured; FamilyAIO; return is UNCLASSIFIED (the
return is a count of iocbs submitted, not a byte count, so it must not
inflate READ/WRITE/TRANSFER totals). Enter/exit are paired and timed. No
implementation discrepancy and no docs drift.
Add a genuine end-to-end test: new aio-submit ioworkload scenario sets up
an AIO context and submits one real IOCB_CMD_PWRITE iocb against a temp
file via raw syscalls, then tears the context down. TestAioSubmit asserts
the enter_io_submit tracepoint fires for the AIO family workload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The classic Linux AIO family (io_setup/io_submit/io_getevents/io_cancel/
io_destroy) had no integration coverage: family_test.go exercises only
FS/Memory/IPC/Network/Process/Sched/Time, and iouring_test.go covers only
the distinct io_uring_* family. io_setup is classified KindNull/FamilyAIO,
which is correct by inspection against man 2 io_setup (nr_events is a count,
ctx_idp an output pointer, so no fd/path is captured), so the tracer itself
needed no change.
Add an ioworkload AIO scenario that drives io_setup(2)/io_destroy(2) raw
(no privileges, no libaio) plus an EINVAL variant, and integration tests
that assert ior records the enter_io_setup tracepoint end-to-end, mirroring
the existing iouring scenario/test pattern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of semctl(2) confirmed the implementation is correct: it is
classified KindSysVOp in FamilyIPC, consistent with its SysV
control-syscall siblings msgctl/shmctl (and semget/semop/semtimedop).
The enter handler emits a null_event and captures no argument, so the
semid at args[0] -- a System V IPC identifier, NOT a file descriptor --
is correctly not recorded as an fd. The exit handler reports the raw
op-dependent int status (value or -1) as UNCLASSIFIED, never a byte
count.
The classification table already covered semctl, but only msgctl's
generated handler body was directly asserted. Add dedicated lock-in
tests mirroring TestGenerateMsgctlHandler:
- TestGenerateSemctlHandler: enter emits null_event, no ctx->args[]
capture, no ev->fd; exit ret_type UNCLASSIFIED.
- TestClassifyRetSemctlUnclassified: ret is UNCLASSIFIED.
No classification, generated C, docs, or runtime behavior changed
(mage generate produces no diff), so this is a test-only addition.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
rt_sigreturn(2) restores the pre-signal execution context off the signal
stack frame and resumes the interrupted instruction; it never returns to
the instruction after the syscall. man sigreturn(2) states plainly that
"sigreturn() never returns", and tracing against /sys/kernel/tracing
confirms it: sys_enter_rt_sigreturn fires once per signal-handler return
while sys_exit_rt_sigreturn never fires.
The generator previously emitted a dead handle_sys_exit_rt_sigreturn (it
can never run) and recorded a per-tid syscall_enter_state_map entry on the
enter path that nothing would ever delete (no exit fires), leaking entries
in the bounded map on every signal-handler return.
Add rt_sigreturn to noreturnSyscalls so codegen suppresses the dead exit
handler and routes the enter handler through ior_on_noreturn_syscall_enter
(sampling decision only, no map write), exactly like exit/exit_group. The
enter null_event is still emitted, and the FamilySignals/KindNull
classification is unchanged. Regenerated the C/Go artifacts and the result
baseline accordingly, and generalized the related comments.
Lock-in tests: TestRtSigreturnIsNoreturn asserts rt_sigreturn is noreturn;
TestRtSigSiblingsAreNotNoreturn guards that the returning rt_sig* siblings
are not; TestGenerateExitNoreturnHandlers now also covers rt_sigreturn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of pwritev(2) confirmed the existing classification is correct:
pwritev returns the number of bytes written, so its exit is
WRITE_CLASSIFIED (matching write/pwrite64/writev/pwritev2), fd is at
args[0] (KindFd), and it lives in the FS family. The read-side sibling
preadv stays READ_CLASSIFIED. No implementation changes were needed.
Add TestClassifyPwritevWriteByteCount as a lock-in test mirroring the
prior pwritev2/pwrite64 audits, with a preadv off-by-one contrast guard
and transfer/unclassified negative checks across the whole p/readv/writev
family so any stray reclassification trips the test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of pipe(2)/pipe2(2) (task dx) confirmed the tracing implementation is
correct: KindPipe (not KindFd, since args[0] is an output ptr to int[2], not an
fd), FamilyIPC, and an UNCLASSIFIED int return. Enter stashes the output ptr
(flags=0 for pipe, args[1] for pipe2); exit reads the fd pair via
bpf_probe_read_user guarded by ret==0, mirroring the socketpair pipe-like
pattern. The only gaps were missing lock-in tests, now added:
- codegen: assert the exit handler reads the fd pair from the stashed output
buffer (ret==0 guard, bpf_probe_read_user, fd0/fd1) and that the flag-less
pipe variant hardcodes flags=0 and never reads args[1].
- classify: pipe/pipe2 are never KindFd and stay UNCLASSIFIED on ret.
- runtime: a failed pipe (ret==-1) tracks no descriptors and attaches no file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audited perf_event_open(2) against the man page: it returns a new fd (or
-1), args[0] is a struct perf_event_attr* userspace pointer (NOT an fd),
args[1] is a monitored pid, and only args[3] group_fd is a real fd.
The existing implementation is correct (KindPerfOpen by name, not KindFd;
FamilySecurity; exit as UNCLASSIFIED RetEvent). Add lock-in tests:
- codegen: assert args[0] is read via bpf_probe_read_user as the attr
struct and never captured as an fd (negative assertions on args[0]/args[1]).
- eventloop: a failed return (-1) registers no fd in fdState.
- perfDescriptorName format pin (perf: prefix).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
msgctl(2) operates on a System V message queue identified by msqid
(args[0]). msqid is a SysV IPC id returned by msgget, NOT a file
descriptor; capturing it as an fd would corrupt the fd-resource view.
ior already classifies msgctl as KindSysVOp in FamilyIPC (consistent
with siblings semctl/shmctl/msgget/msgsnd/msgrcv), emits a null_event
with no arg capture, and reports the int status as UNCLASSIFIED.
Add lock-in tests pinning this behavior:
- TestGenerateMsgctlHandler: KindSysVOp for msgctl/semctl/shmctl, all
FamilyIPC; generated enter handler emits null_event and captures no
args (asserts no ctx->args[ and no ev->fd); exit ret is UNCLASSIFIED.
- TestClassifyRetMsgctlUnclassified: msgctl ret is never a byte count.
No implementation change needed; mage generate produces no diff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of mlockall(2) (task 6w). mlockall(int flags) locks ALL process
memory and takes a single flags bitmask (MCL_CURRENT/MCL_FUTURE/
MCL_ONFAULT) with NO address range, unlike its KindMem siblings
mlock/mlock2/munlock (which take addr+len). It is therefore correctly
classified KindNull in FamilyMemory, matching its sibling munlockall(2).
All existing classification (classify.go, family.go, generated artifacts,
docs plan) already match; no fixes needed. Add two lock-in tests
documenting the reasoning: TestGenerateNullHandlerMlockall asserts the
enter handler emits a null_event and never captures the flags int as an
addr/fd/path, and TestClassifyRetMlockallUnclassified asserts the 0/-1
return is UNCLASSIFIED (not a byte count).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of dup(2) found the tracing implementation already correct and
consistent with its dup2/dup3 siblings: dup(int oldfd) takes a single fd
argument (the sys_enter_dup tracepoint exposes it as field "fildes",
unsigned int, at args[0]). It is classified KindFd (a plain fd_event),
the enter handler captures ev->fd from args[0] per the KindFd
convention, it is in the FS family (fd grouping), and its exit returns
the new (lowest-numbered unused) descriptor or -1 as a plain UNCLASSIFIED
ret_event (never a byte-count transfer). Like dup2, dup carries no flags
and clears FD_CLOEXEC on the duplicate; the eventloop registerDup path
registers the returned newfd onto the same underlying file with flags=0,
which it already honors (applyFdTransferOp handles SYS_ENTER_DUP).
Docs (FS, fd) and the drift tests are in sync; existing coverage already
includes TestClassifyDup, the makeFdDupTestData full-lifecycle eventloop
test, and integration TestDupBasic/TestDupInvalidFd.
No discrepancies were found, so add a lock-in test (matching the dup2
audit) asserting the generated BPF C for dup captures fd from args[0]
(not args[1]), emits an fd_event (not a dup3_event), wires no flags, and
classifies the exit UNCLASSIFIED. Adds FormatExitDup testdata to drive
the exit handler assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of the arch_prctl(2) syscall found the tracing implementation
already correct and consistent with the man page:
- enter classifies as KindNull (op/addr never captured as fd/path)
- exit is a ret_event with UNCLASSIFIED ret_type (int 0/-1 status)
- family is Process (deliberately, unlike its x86 siblings
ioperm/iopl/modify_ldt which are Misc), in sync with the docs and
the tracepoints drift tests
Add dedicated lock-in tests mirroring the prior iopl audit, using the
real kernel tracepoint fields (option/arg2 on enter, ret on exit) so
the heuristics are proven safe even without the name-only mapping.
Also add explicit FamilyProcess assertions for arch_prctl and
personality to guard against drift toward Misc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of syscall listen(2): int listen(int sockfd, int backlog). Confirmed
the tracing implementation already matches the man page and its socket
siblings (bind/connect/accept/getsockname/getpeername):
- KindFd, capturing ev->fd = args[0] (sockfd)
- FamilyNetwork
- exit ret_event UNCLASSIFIED (returns 0/-1, no byte count)
listen was already covered by the name-based classify/family/retclassify
tests but lacked a dedicated generated-handler lock-in test like its
bind/getsockname siblings. Add FormatListen/FormatExitListen tracepoint
fixtures and TestGenerateListenHandler asserting the enter captures
fd=args[0] (and never backlog at args[1]) and the exit stays UNCLASSIFIED.
No classification or generated-code changes; mage generate produces no diff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of kexec_file_load(2) against the man page confirmed the existing
classification is already correct and consistent: KindFd capturing
kernel_fd at args[0], FamilySecurity (matching its sibling kexec_load
after task 6v), and an UNCLASSIFIED ret_event exit (returns 0/-1).
The cmdline argument (args[3]) is a kernel command-line STRING, not a
filesystem path, and is correctly never read as a path; the second fd
initrd_fd (args[1]) is not captured, per the single-fd KindFd convention.
Add a dedicated lock-in test plus real-kernel-format fixtures so future
refactors cannot silently regress the fd wiring: assert ev->fd=args[0],
no args[1] fd capture, no bpf_probe_read_user_str on the cmdline, and an
UNCLASSIFIED (never READ/WRITE/TRANSFER) exit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of socketpair(2) found the tracing implementation already correct:
KindSocketpair captures the two output fds from the sv[2] buffer (args[3])
at exit and never treats args[0] (the address-family/domain constant) as a
file descriptor. Family=Network and UNCLASSIFIED ret are consistent with the
socket/accept siblings and the docs.
Add regression lock-in tests so a future field-shape or classification change
cannot silently regress to recording the domain integer as a bogus fd:
- TestClassifySocketpairNotFd: pins the name-based override so socketpair is
KindSocketpair, never the generic KindFd path that reads args[0].
- TestHandleSocketpairExitDoesNotTrackDomainAsFd: uses AF_INET6 (10), distinct
from the returned fds, and asserts fd 10 is never tracked while sv0/sv1 are.
- TestHandleSocketpairExitDropsFdsOnError: on ret!=0 no descriptors are tracked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of setsockopt(2) found the tracing implementation already correct:
sockfd captured at ctx->args[0] (KindFd), exit ret_event UNCLASSIFIED, and
FamilyNetwork — matching the man page and the bind/connect/getsockname/
getpeername/getsockopt siblings, with generated C/Go and docs all consistent.
Add lock-in tests mirroring prior per-syscall audits:
- TestClassifySetsockoptEnterFd: enter is KindFd with no pathname capture,
asserted against the real sockfd/level/optname/optval/optlen fields.
- TestClassifyExitSetsockoptUnclassifiedRet: exit is KindRet + UNCLASSIFIED
(0/-1 status, not a byte count).
- TestClassifyExitGetsockoptUnclassifiedRet: same for the read-side sibling.
- TestClassifySyscallFamily: pin setsockopt (enter+exit) and getsockopt to
FamilyNetwork.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of the rmdir(2) syscall found the tracing implementation already
correct and fully consistent with its siblings: rmdir is in the FS family,
classified KindPathname with the pathname captured from args[0] (its generated
BPF C handler is byte-identical to unlink's), and its exit is a ret_event with
UNCLASSIFIED ret_type (rmdir returns int 0/-1, not a byte count). The docs and
drift tests, integration tests (unlink-rmdir success and unlink-rmdir-notempty
ENOTEMPTY failure), and retclassify coverage all already match.
To guard against future drift, add a dedicated rmdir lock-in:
- FormatRmdir tracepoint fixture (single const char * pathname at args[0],
mirroring the real sys_enter_rmdir format and unlink's shape).
- TestGenerateRmdirHandlerCapturesPathFromArgs0: asserts the generated handler
reads the path from args[0] (with a negative guard against args[1], since
rmdir has no dirfd) and that the exit stays UNCLASSIFIED.
- TestRmdirFamilyAndKindMatchSiblings: asserts rmdir shares FamilyFS and
KindPathname/pathname with unlink/unlinkat/mkdir.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of request_key(2) found the tracing implementation already
correct and consistent with the man page and the prior keyctl audit
(task 7v): request_key classifies as KindKeyctl/FamilySecurity, the
BPF handler captures option=-2 sentinel and key_serial=dest_keyring
(args[3]) with no path/string capture of the const char *
type/description/callout_info key-metadata args, and the exit returns
a key serial / -1 that stays UNCLASSIFIED.
Strengthen the dedicated TestClassifyRequestKey beyond a bare kind
check to also assert PathnameField stays empty (string args are key
metadata, not paths), family is Security on enter and exit, and the
return is UNCLASSIFIED — bringing it to parity with the add_key
contrast assertion. No code/generated changes; mage generate produces
no diff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Add TestClassifyPwrite64WriteByteCount pinning the pwrite64(2) audit:
fd at args[0] (KindFd), FS family, and WRITE_CLASSIFIED return (the
syscall returns the number of bytes written). Asserts pread64 stays
READ_CLASSIFIED as the read-side positional contrast, guards against
transfer/unclassified misclassification, and checks the write/pread
sibling group so a stray reclassification trips the test.
No implementation changes: classify.go, family.go, generated C/Go, and
docs/syscall-tracing-plan.md were already consistent and correct.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of lseek(2) confirmed the tracing implementation is already correct:
enter is a KindFd fd_event capturing the fd from args[0], the syscall is
FamilyFS alongside its read/write/fsync siblings, and the exit is a plain
ret_event that stays UNCLASSIFIED. lseek returns the RESULTING file offset
(off_t, bytes from the start of the file), which is a file position, NOT a
count of bytes transferred — so it must never be READ/WRITE/TRANSFER
classified, which would wrongly inflate I/O byte totals.
Add lock-in tests pinning that behaviour so a future reclassification trips:
- FormatLseek/FormatExitLseek tracepoint fixtures.
- TestClassifyFdLseek: enter resolves to KindFd (fd at args[0]).
- TestClassifyRetExitLseek: exit is KindRet and ClassifyRet stays UNCLASSIFIED.
- lseek entry in TestClassifySyscallPairAccepted (end-to-end pair).
- FS-family asserts for sys_enter/exit_lseek in family_test.
- Enriched UNCLASSIFIED comment in retclassify_test explaining offset != bytes.
No generated-artifact changes (mage generate produces no diff); no in-scope
bugs and no out-of-scope follow-ups found.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
kexec_load(2) and kexec_file_load are documented together on the same
man page and both load a new kernel for later execution by reboot(2).
kexec_file_load was already FamilySecurity, but kexec_load fell through
to FamilyMisc. Move kexec_load to FamilySecurity so the siblings share
a family. Kind classification was already correct: kexec_load takes raw
user pointers (KindNull, no fd/path) while kexec_file_load takes fds
(KindFd); the return value (long 0/-1, no byte count) stays UNCLASSIFIED.
Update docs/syscall-tracing-plan.md to match, regenerate artifacts, and
add lock-in tests for the family and UNCLASSIFIED return of both kexec
syscalls plus reboot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of io_uring_register(2) confirmed the existing tracing is correct:
KindFd with the io_uring fd captured at args[0], FamilyAIO (matching
io_uring_setup/io_uring_enter), and an UNCLASSIFIED ret_event exit. The
sys_enter_io_ KindNull prefix rule does NOT mis-catch it because
classifyNameOnly consults the exact nameOnlyKindsTable (KindFd) before the
prefix list.
Add two lock-in tests to guard these invariants:
- TestIoUringRegisterTablePrecedenceOverIoPrefix: the explicit KindFd table
entry wins over the sys_enter_io_ KindNull prefix rule (with an io_submit
sanity check that the prefix rule still yields KindNull for fd-less AIO
siblings).
- TestIoUringRegisterReturnUnclassified: the exit returns 0/small-positive,
never a byte count, so the io_uring group stays out of retClassifications.
No code, docs, or generated artifacts changed; mage generate produces no diff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of getsockname(2) confirmed correct tracing: enter is KindFd with
the sockfd captured from args[0], family is FamilyNetwork, and the exit
ret_event is UNCLASSIFIED (0/-1, no byte count) — matching the man page
and its bind/connect/listen/accept/getpeername siblings. Integration
coverage already exists (ioworkload calls Getsockname; TestSocketIntro-
spection asserts enter_getsockname).
Add lock-in tests symmetric with the existing getpeername coverage:
- TestClassifyExitGetsockname: exit tracepoint maps to KindRet.
- TestGenerateGetsocknameHandler: enter captures fd=args[0]; the addr
output pointer (args[1]) and addrlen in/out pointer (args[2]) are not
captured, and the exit stays UNCLASSIFIED.
- FormatGetsockname/FormatExitGetsockname fixtures copied verbatim from
the real kernel tracepoint format (third arg is a pointer, unlike
bind's by-value addrlen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of the getcwd(2) tracing path. getcwd's args[0] is a char *buf
OUTPUT buffer: the kernel writes the absolute cwd path into it and the
contents are only valid AFTER the syscall returns. Reading it at enter
would capture an empty/garbage string, so getcwd is correctly KindNull
at enter and the cwd is resolved at EXIT from /proc/<tid>/cwd when the
return value is positive (handleNullExit). Family FS, docs and drift
tests already aligned; no behavior change required.
Add lock-in tests pinning the correct behavior:
- generate: strengthen TestClassifyNullGetcwd to assert the enter kind
is never KindPathname/KindName and no pathname field is captured;
add TestClassifyByFieldGetcwdBufNotPath proving the generic field
classifier never treats char *buf as a path (defense-in-depth).
- eventloop: add GetcwdFailureEventTest asserting that a failed getcwd
(negative errno, e.g. -ERANGE) attaches no cwd path, and document the
output-buffer nuance in the success-case test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of kill(2) (pid_t pid, int sig): the pid at args[0] is a process/
process-group identifier and sig a signal number, neither an fd nor a
path, so the enter tracepoint is KindNull and the int 0/-1 return is a
status code (UNCLASSIFIED), not a transferred byte count. Classification
and docs (Signals/null) already matched and need no change.
Add TestClassifyExitKillUnclassifiedRet (the return-value lock-in its
signal siblings tkill/tgkill/rt_sigqueueinfo already have) and harden
TestClassifyKillExplicitNull to assert no PathnameField is captured,
documenting the deliberate contrast with pidfd_send_signal (KindFd/IPC,
args[0] is a real pidfd).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Audit of iopl(2) (task wu). iopl(int level) changes the x86 I/O
privilege level of the calling thread and returns int 0/-1. The
existing coverage only asserted KindNull via a synthetic arg0 field
(TestClassifyE7NullNameOnlyKinds) and the FamilyMisc family tag (from
the prior ioperm audit, task uu). Add dedicated lock-in tests that use
the real 'int level' tracepoint field to prove it is never captured as
an fd or path, and that the sys_exit_iopl ret stays KindRet/UNCLASSIFIED
(a status code, not a transferred byte count). No implementation,
generated-artifact, or docs changes were needed - everything already
matched the man page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|