From: Mykyta Yatsenko Add functional tests for sleepable tracepoint programs that attach to the nanosleep syscall and use bpf_copy_from_user() to read user memory: - tp_btf: BTF-based raw tracepoint using SEC("tp_btf.s/sys_enter") with PT_REGS_PARM1_SYSCALL (non-CO-RE macro for BTF programs). - classic: Classic raw tracepoint using SEC("raw_tp.s/sys_enter") with PT_REGS_PARM1_CORE_SYSCALL (CO-RE macro needed for classic). - tracepoint: Classic tracepoint using SEC("tp.s/syscalls/sys_enter_nanosleep") receiving struct syscall_trace_enter with direct access to args[]. Add a negative test (test_sleepable_raw_tp_fail) that verifies sleepable programs are rejected on non-faultable tracepoints (sched_switch). Update verifier/sleepable.c tests: - Add "sleepable raw tracepoint accept" test for sys_enter. - Rename reject test and update error message to match the new descriptive "Sleepable program cannot attach to non-faultable tracepoint" message. Signed-off-by: Mykyta Yatsenko --- .../bpf/prog_tests/sleepable_tracepoints.c | 57 ++++++++++++++++ .../bpf/progs/test_sleepable_tracepoints.c | 75 ++++++++++++++++++++++ tools/testing/selftests/bpf/verifier/sleepable.c | 17 ++++- 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sleepable_tracepoints.c b/tools/testing/selftests/bpf/prog_tests/sleepable_tracepoints.c new file mode 100644 index 000000000000..308d53c31632 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/sleepable_tracepoints.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include "test_sleepable_tracepoints.skel.h" + +static void run_test(struct bpf_program *prog, + struct test_sleepable_tracepoints *skel) +{ + struct bpf_link *link; + + link = bpf_program__attach(prog); + if (!ASSERT_OK_PTR(link, "prog_attach")) + return; + + skel->bss->target_pid = getpid(); + skel->bss->triggered = 0; + skel->bss->err = 0; + skel->bss->copied_tv_nsec = 0; + + syscall(__NR_nanosleep, &(struct timespec){ .tv_nsec = 555 }, NULL); + + ASSERT_EQ(skel->bss->triggered, 1, "triggered"); + ASSERT_EQ(skel->bss->err, 0, "err"); + ASSERT_EQ(skel->bss->copied_tv_nsec, 555, "copied_tv_nsec"); + + bpf_link__destroy(link); +} + +void test_sleepable_tracepoints(void) +{ + struct test_sleepable_tracepoints *skel; + + skel = test_sleepable_tracepoints__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.handle_sched_switch, false); + + if (!ASSERT_OK(test_sleepable_tracepoints__load(skel), "skel_load")) + goto cleanup; + + if (test__start_subtest("tp_btf")) + run_test(skel->progs.handle_sys_enter_tp_btf, skel); + + if (test__start_subtest("classic")) + run_test(skel->progs.handle_sys_enter_raw_tp, skel); + + if (test__start_subtest("tracepoint")) + run_test(skel->progs.handle_sys_enter_tp, skel); + +cleanup: + test_sleepable_tracepoints__destroy(skel); + + RUN_TESTS(test_sleepable_tracepoints); +} diff --git a/tools/testing/selftests/bpf/progs/test_sleepable_tracepoints.c b/tools/testing/selftests/bpf/progs/test_sleepable_tracepoints.c new file mode 100644 index 000000000000..37e14f5e9f14 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_sleepable_tracepoints.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +int target_pid; +int triggered; +long err; +long copied_tv_nsec; + +static int copy_nanosleep_arg(struct __kernel_timespec *ts) +{ + long tv_nsec; + + err = bpf_copy_from_user(&tv_nsec, sizeof(tv_nsec), &ts->tv_nsec); + if (err) + return err; + + copied_tv_nsec = tv_nsec; + triggered = 1; + return 0; +} + +SEC("tp_btf.s/sys_enter") +int BPF_PROG(handle_sys_enter_tp_btf, struct pt_regs *regs, long id) +{ + struct task_struct *task = bpf_get_current_task_btf(); + + if (task->pid != target_pid) + return 0; + + if (id != __NR_nanosleep) + return 0; + + return copy_nanosleep_arg((void *)PT_REGS_PARM1_SYSCALL(regs)); +} + +SEC("raw_tp.s/sys_enter") +int BPF_PROG(handle_sys_enter_raw_tp, struct pt_regs *regs, long id) +{ + struct task_struct *task = bpf_get_current_task_btf(); + + if (task->pid != target_pid) + return 0; + + if (id != __NR_nanosleep) + return 0; + + return copy_nanosleep_arg((void *)PT_REGS_PARM1_CORE_SYSCALL(regs)); +} + +SEC("tp.s/syscalls/sys_enter_nanosleep") +int handle_sys_enter_tp(struct syscall_trace_enter *args) +{ + if ((bpf_get_current_pid_tgid() >> 32) != target_pid) + return 0; + + return copy_nanosleep_arg((void *)args->args[0]); +} + +/* Sleepable program on a non-faultable tracepoint should fail to load */ +SEC("tp_btf.s/sched_switch") +__failure __msg("Sleepable program cannot attach to non-faultable tracepoint") +int BPF_PROG(handle_sched_switch, bool preempt, + struct task_struct *prev, struct task_struct *next) +{ + return 0; +} diff --git a/tools/testing/selftests/bpf/verifier/sleepable.c b/tools/testing/selftests/bpf/verifier/sleepable.c index 1f0d2bdc673f..6dabc5522945 100644 --- a/tools/testing/selftests/bpf/verifier/sleepable.c +++ b/tools/testing/selftests/bpf/verifier/sleepable.c @@ -76,7 +76,20 @@ .runs = -1, }, { - "sleepable raw tracepoint reject", + "sleepable raw tracepoint accept", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_TRACING, + .expected_attach_type = BPF_TRACE_RAW_TP, + .kfunc = "sys_enter", + .result = ACCEPT, + .flags = BPF_F_SLEEPABLE, + .runs = -1, +}, +{ + "sleepable raw tracepoint reject non-faultable", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), @@ -85,7 +98,7 @@ .expected_attach_type = BPF_TRACE_RAW_TP, .kfunc = "sched_switch", .result = REJECT, - .errstr = "Only fentry/fexit/fmod_ret, lsm, iter, uprobe, and struct_ops programs can be sleepable", + .errstr = "Sleepable program cannot attach to non-faultable tracepoint", .flags = BPF_F_SLEEPABLE, .runs = -1, }, -- 2.52.0