diff options
Diffstat (limited to 'internal/generate')
| -rw-r--r-- | internal/generate/codegen_test.go | 46 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 52 |
2 files changed, 98 insertions, 0 deletions
diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index 3178b1c..da86b86 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -132,6 +132,52 @@ func TestGeneratePidfdGetfdHandlerUsesPidfdArgument(t *testing.T) { requireContains(t, output, "ev->fd = (__s32)ctx->args[0];") } +// TestGenerateKexecFileLoadHandler locks in the generated BPF C for +// kexec_file_load(2): +// +// long kexec_file_load(int kernel_fd, int initrd_fd, +// unsigned long cmdline_len, const char *cmdline, +// unsigned long flags) +// +// kexec_file_load loads a new kernel (and optional initrd) from open file +// descriptors so it can later be booted by reboot(2); it returns 0 on success +// or -1 on error. The leading kernel_fd (args[0]) makes the enter a KindFd +// fd_event capturing ev->fd = args[0]. There are TWO fds (kernel_fd at args[0], +// initrd_fd at args[1]); by the single-fd KindFd convention only the first +// (kernel_fd) is captured, so the handler must NOT wire args[1]. Critically, +// cmdline_ptr (args[3]) is a command-line STRING for the new kernel, NOT a +// filesystem path, so it must NOT be read with bpf_probe_read_user_str. On exit +// kexec_file_load returns 0/-1 — UNCLASSIFIED (a plain ret_event, no +// read/write/transfer byte count). It shares FamilySecurity with its sibling +// kexec_load(2) (asserted in family_test.go). +func TestGenerateKexecFileLoadHandler(t *testing.T) { + output := generateFromPair(t, FormatKexecFileLoad, FormatExitKexecFileLoad) + + // Enter: KindFd fd_event capturing kernel_fd from args[0]. + requireContains(t, output, `SEC("tracepoint/syscalls/sys_enter_kexec_file_load")`) + requireContains(t, output, "struct fd_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct fd_event), 0);") + requireContains(t, output, "ev->event_type = ENTER_FD_EVENT;") + requireContains(t, output, "ev->trace_id = SYS_ENTER_KEXEC_FILE_LOAD;") + requireContains(t, output, "ev->fd = (__s32)ctx->args[0];") + + // Negative guards: only kernel_fd (args[0]) is captured — initrd_fd (args[1]) + // must not be wired as a second fd, and cmdline_ptr (args[3]) is a kernel + // command-line string, never a filesystem path, so it must not be slurped via + // bpf_probe_read_user_str. + requireNotContains(t, output, "ev->fd = (__s32)ctx->args[1];") + requireNotContains(t, output, "bpf_probe_read_user_str") + + // Exit: plain ret_event, UNCLASSIFIED (kexec_file_load returns 0/-1, no byte + // count). + requireContains(t, output, `SEC("tracepoint/syscalls/sys_exit_kexec_file_load")`) + requireContains(t, output, "struct ret_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct ret_event), 0);") + requireContains(t, output, "ev->ret = ctx->ret;") + requireContains(t, output, "ev->ret_type = UNCLASSIFIED;") + requireNotContains(t, output, "ev->ret_type = READ_CLASSIFIED;") + requireNotContains(t, output, "ev->ret_type = WRITE_CLASSIFIED;") + requireNotContains(t, output, "ev->ret_type = TRANSFER_CLASSIFIED;") +} + // TestGenerateProcessMadviseHandlerUsesFirstArgumentAsFd locks in the BPF // handler wiring for process_madvise(2): // diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go index 7631d61..d9fb2c0 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -2296,3 +2296,55 @@ format: print fmt: "0x%lx", REC->ret ` + +// FormatKexecFileLoad / FormatExitKexecFileLoad mirror the real kernel +// tracepoint format for kexec_file_load(2): +// +// long kexec_file_load(int kernel_fd, int initrd_fd, +// unsigned long cmdline_len, const char *cmdline, +// unsigned long flags) +// +// kexec_file_load loads a new kernel (and optional initrd) from open file +// descriptors so it can later be booted by reboot(2); it returns 0 on success +// or -1 on error. The leading "kernel_fd" field (args[0]) makes the enter a +// KindFd fd_event capturing ev->fd = args[0] — the FieldNumber("fd") lookup +// finds no field literally named "fd" here, so generateExtraFd falls back to +// args[0], which IS kernel_fd. There are TWO fds (kernel_fd at args[0], +// initrd_fd at args[1]); by the single-fd KindFd convention only the first +// (kernel_fd) is captured. Critically, cmdline_ptr (args[3]) is a command-line +// STRING for the new kernel, NOT a filesystem path, so it must NOT be read with +// bpf_probe_read_user_str. On exit kexec_file_load returns 0/-1, which is +// UNCLASSIFIED (a plain ret_event, no read/write/transfer byte count). Field +// names/offsets are copied verbatim from +// /sys/kernel/tracing/events/syscalls/sys_enter_kexec_file_load. +const FormatKexecFileLoad = `name: sys_enter_kexec_file_load +ID: 508 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int kernel_fd; offset:16; size:8; signed:0; + field:int initrd_fd; offset:24; size:8; signed:0; + field:unsigned long cmdline_len; offset:32; size:8; signed:0; + field:const char * cmdline_ptr; offset:40; size:8; signed:0; + field:unsigned long flags; offset:48; size:8; signed:0; + +print fmt: "kernel_fd: 0x%08lx, initrd_fd: 0x%08lx, cmdline_len: 0x%08lx, cmdline_ptr: 0x%08lx, flags: 0x%08lx", ((unsigned long)(REC->kernel_fd)), ((unsigned long)(REC->initrd_fd)), ((unsigned long)(REC->cmdline_len)), ((unsigned long)(REC->cmdline_ptr)), ((unsigned long)(REC->flags)) +` + +const FormatExitKexecFileLoad = `name: sys_exit_kexec_file_load +ID: 507 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` |
