From 77515ab12e4983e6416f8c35039a3f0c0822ac70 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:19 +0200 Subject: [PATCH 01/10] bpf: Mark signal tracepoint siginfo arguments as scalar The signal_generate and signal_deliver tracepoints declare their info argument as a struct kernel_siginfo pointer. btf_ctx_access() therefore treats it as a trusted pointer for tp_btf programs. Signal delivery also uses SEND_SIG_NOINFO and SEND_SIG_PRIV as special values for this argument. Those values are zero and one respectively, and are not pointers. A tp_btf program can currently dereference either value and fault the kernel. In particular, signal_generate can run from timer interrupt context, turning the fault into a kernel panic. Record both tracepoints in raw_tp_null_args[] and mark argument one as a non-pointer. This preserves scalar access to the cookie while rejecting direct and helper-mediated pointer use. Merely marking it nullable would not suffice because SEND_SIG_PRIV is nonzero. Fixes: 838a10bd2ebf ("bpf: Augment raw_tp arguments with PTR_MAYBE_NULL") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-2-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/btf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 85ae92c920e4..b5aa802451bd 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6732,6 +6732,9 @@ static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { { "rxrpc_resend", 0x10 }, { "rxrpc_tq", 0x10 }, { "rxrpc_client", 0x1 }, + /* signal */ + { "signal_generate", 0x20 }, + { "signal_deliver", 0x20 }, /* skb */ {"kfree_skb", 0x1000}, /* sunrpc */ From d7719a1736e6be77d0682f7395acd3701949f1fa Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:20 +0200 Subject: [PATCH 02/10] selftests/bpf: Cover signal tracepoint siginfo sentinels Add load-only verifier coverage for the signal_generate and signal_deliver info arguments. The signal_generate case performs a NULL check before dereferencing info, ensuring that merely making it nullable cannot satisfy the test when the nonzero SEND_SIG_PRIV sentinel is used. Both programs load successfully without the verifier fix, contrary to their expected-failure annotations. With the fix, info is a scalar and the attempted dereferences are rejected. Also add success cases showing that plain raw tracepoint and tp_btf programs can continue to read and compare the context word as a scalar. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-3-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/raw_tp_null_fail.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c index 0d58114a4955..7e8842bf9000 100644 --- a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c +++ b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c @@ -22,3 +22,39 @@ int test_raw_tp_null_sched_pi_setprio_arg_2(void *ctx) { asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u64 *)(r1 +0);" ::: __clobber_all); return 0; } + +/* Plain raw tracepoint arguments remain scalar values. */ +SEC("raw_tp/signal_generate") +__success +int test_raw_tp_signal_generate_info_scalar(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all); + return 0; +} + +/* tp_btf programs may inspect the sentinel as a scalar value. */ +SEC("tp_btf/signal_generate") +__success +int test_tp_btf_signal_generate_info_scalar(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all); + return 0; +} + +/* SEND_SIG_PRIV is non-NULL, so a NULL check cannot make info safe. */ +SEC("tp_btf/signal_generate") +__failure __msg("R1 invalid mem access 'scalar'") +int test_tp_btf_signal_generate_info_no_deref(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 == 0 goto +1; " + "r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} + +SEC("tp_btf/signal_deliver") +__failure __msg("R1 invalid mem access 'scalar'") +int test_tp_btf_signal_deliver_info_no_deref(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} From 266aa4ad0b2e82397cd9045752c9bff03d98eddd Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:21 +0200 Subject: [PATCH 03/10] bpf: Reject tail calls directly from callback frames A tail call from a non-zero frame is modeled as a return from that frame. The verifier makes R0 unknown and calls prepare_func_exit() for the taken branch. When the current frame is a synchronous callback, prepare_func_exit() enforces the callback return-value contract and marks R0 precise. Since the tail-call path synthesized R0 rather than deriving it from an instruction, precision backtracking reaches the callback-calling instruction with R0 still requested and triggers the "callback unexpected regs" verifier bug. A CAP_BPF task can therefore cause a WARN and an -EFAULT BPF_PROG_LOAD. Tail calls reachable from callbacks are already rejected later by check_max_stack_depth(). Reject a tail call made directly by a callback before constructing the inconsistent return state, using the existing diagnostic. Tail calls from ordinary subprograms keep their current behavior. Fixes: e3245f899043 ("bpf: properly verify tail call behavior") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7d8ddb1bee00..f540279ff4ab 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11228,6 +11228,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (env->cur_state->curframe) { struct bpf_verifier_state *branch; + /* + * A taken tail call is modeled as a return from the current + * frame. A callback frame cannot be left that way because + * prepare_func_exit() would apply its return contract to the + * unknown R0 synthesized below. Stack-depth validation rejects + * this construct anyway. + */ + if (cur_func(env)->in_callback_fn) { + verbose(env, "cannot tail call within callback\n"); + return -EINVAL; + } mark_reg_scratched(env, BPF_REG_0); branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false); if (IS_ERR(branch)) From d9ae3e4c7fb5bfaccc9ca295692d54130862f3b2 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:22 +0200 Subject: [PATCH 04/10] selftests/bpf: Test direct tail calls from callbacks tailcall_callback tests a tail call one static subprogram below a callback. That reaches the later stack-depth rejection, but it does not exercise the tail-call helper while the current frame is itself a callback. Add a callback that calls bpf_tail_call directly and expect the existing "cannot tail call within callback" diagnostic. On an affected kernel, the load instead reaches the "callback unexpected regs" verifier bug, so the expected message is absent and the test fails. The existing ordinary subprogram case remains a success control for legitimate tail calls. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-5-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/tailcall_callback.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/tailcall_callback.c b/tools/testing/selftests/bpf/progs/tailcall_callback.c index c41632cf423b..14fa7a87028e 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_callback.c +++ b/tools/testing/selftests/bpf/progs/tailcall_callback.c @@ -44,6 +44,13 @@ int callback_loop(int index, void **cb_ctx) return ret ? 1 : 0; } +static __noinline +int callback_tail(int index, void **cb_ctx) +{ + bpf_tail_call_static(*cb_ctx, &jmp_table, 0); + return 0; +} + static __noinline int callback_empty(int index, void *data) { @@ -78,4 +85,13 @@ int tailcall_callback_2(struct __sk_buff *skb) return 0; } +/* callback with a direct tail call is rejected without a verifier bug */ +SEC("tc") +__failure __msg("cannot tail call within callback") +int tailcall_callback_3(struct __sk_buff *skb) +{ + bpf_loop(1, callback_tail, &skb, 0); + return 0; +} + char __license[] SEC("license") = "GPL"; From 7b7b8b5960102566bd625ae829d1f330c5b5d104 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:23 +0200 Subject: [PATCH 05/10] bpf: Reject resilient lock operations in rbtree callbacks __bpf_rbtree_add() keeps parent and link pointers live across calls to the program-supplied comparison callback. The verifier therefore requires the root's lock to remain held throughout the callback. The helper path enforces this rule for bpf_spin_lock() and bpf_spin_unlock(), but the resilient lock kfunc argument path does not. Since resilient locks may protect BPF rbtree roots, a callback can release the root lock and let another CPU remove and free the node referenced by the in-progress tree walk. The walk then resumes using freed pointers. Reject resilient lock kfuncs in an rbtree comparison callback, matching the existing policy for the spin lock helpers. Resilient-lock-protected trees remain valid when their comparison callbacks leave lock state alone. Fixes: 0de2046137f9 ("bpf: Implement verifier support for rqspinlock") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-6-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f540279ff4ab..32d31fa67036 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13241,6 +13241,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me { int flags = PROCESS_RES_LOCK; + if (in_rbtree_lock_required_cb(env)) { + verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n"); + return -EACCES; + } + if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { verbose(env, "%s doesn't point to map value or allocated object\n", reg_arg_name(env, argno)); From 08b4dc83d981bf9136d37aaa4f5cd021ba0d8f2b Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:24 +0200 Subject: [PATCH 06/10] selftests/bpf: Reject resilient unlock in rbtree callback Add a load-only verifier regression for a resilient lock operation in an rbtree comparison callback. The program holds the rbtree's regular spin lock and a separate resilient lock, then releases the resilient lock from the callback. This isolates the missing kfunc policy check without running a concurrent tree mutation. Release the resilient lock before the regular lock on the outer fall-through. The broken verifier therefore accepts the balanced program, while the fixed verifier rejects the resilient unlock specifically while verifying the callback. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-7-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../testing/selftests/bpf/progs/rbtree_fail.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c index 555379952dcc..803419a47c62 100644 --- a/tools/testing/selftests/bpf/progs/rbtree_fail.c +++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c @@ -16,6 +16,7 @@ struct node_data { private(A) struct bpf_spin_lock glock; private(A) struct bpf_rb_root groot __contains(node_data, node); private(A) struct bpf_rb_root groot2 __contains(node_data, node); +private(B) struct bpf_res_spin_lock res_glock; static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b) { @@ -265,6 +266,12 @@ static bool less__bad_fn_call_first_unlock_after(struct bpf_rb_node *a, const st return node_a->key < node_b->key; } +static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b) +{ + bpf_res_spin_unlock(&res_glock); + return false; +} + static __always_inline long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b)) { @@ -301,4 +308,26 @@ long rbtree_api_add_bad_cb_bad_fn_call_first_unlock_after(void *ctx) return add_with_cb(less__bad_fn_call_first_unlock_after); } +SEC("?tc") +__failure __msg("can't res_spin_{lock,unlock} in rbtree cb") +long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx) +{ + struct node_data *n; + + n = bpf_obj_new(typeof(*n)); + if (!n) + return 1; + + bpf_spin_lock(&glock); + if (bpf_res_spin_lock(&res_glock)) { + bpf_spin_unlock(&glock); + bpf_obj_drop(n); + return 1; + } + bpf_rbtree_add(&groot, &n->node, less__bad_res_spin_unlock); + bpf_res_spin_unlock(&res_glock); + bpf_spin_unlock(&glock); + return 0; +} + char _license[] SEC("license") = "GPL"; From a453d6e3b8e8e1a321c8744d6189d763af9287d0 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:25 +0200 Subject: [PATCH 07/10] bpf: Mark sched_process_wait argument as nullable do_wait() passes wo->wo_pid to the sched_process_wait tracepoint. kernel_wait4() leaves wo_pid NULL for wait4(-1), and kernel_waitid_prepare() does likewise for waitid(P_ALL). btf_ctx_access() currently types argument 0 as PTR_TO_BTF_ID | PTR_TRUSTED. Without PTR_MAYBE_NULL, the verifier accepts an unchecked dereference. Trusted pointer loads have no fault protection, so a wait for any child can then cause a NULL pointer dereference in JITed BPF code. Add sched_process_wait to raw_tp_null_args[] with argument 0 marked nullable. The verifier rejects an unchecked dereference while preserving access after the program checks the pointer for NULL. Fixes: 838a10bd2ebf ("bpf: Augment raw_tp arguments with PTR_MAYBE_NULL") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-8-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/btf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index b5aa802451bd..5d93fd82e764 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6672,6 +6672,10 @@ struct bpf_raw_tp_null_args { static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { /* sched */ { "sched_pi_setprio", 0x10 }, + /* + * do_wait() passes NULL for wait4(-1) and waitid(P_ALL). + */ + { "sched_process_wait", 0x1 }, /* ... from sched_numa_pair_template event class */ { "sched_stick_numa", 0x100 }, { "sched_swap_numa", 0x100 }, From c1992ba73b0339166906eb2224494e04052a8150 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:26 +0200 Subject: [PATCH 08/10] selftests/bpf: Test sched_process_wait nullable argument Add a load-time verifier test that dereferences argument 0 of the sched_process_wait tp_btf program without checking it. The test expects the nullable-pointer diagnostic, so it is accepted unexpectedly before the fix and rejected as expected after it. Add a successful control that checks the argument for NULL before the dereference. This ensures the nullable marking preserves legitimate access to the pid when the tracepoint supplies one. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-9-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/progs/raw_tp_null_fail.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c index 7e8842bf9000..725d73c9ffe1 100644 --- a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c +++ b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c @@ -58,3 +58,20 @@ int test_tp_btf_signal_deliver_info_no_deref(void *ctx) asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u32 *)(r1 +0);" ::: __clobber_all); return 0; } + +SEC("tp_btf/sched_process_wait") +__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +int test_raw_tp_null_sched_process_wait_arg_1(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +0); r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} + +SEC("tp_btf/sched_process_wait") +__success +int test_raw_tp_null_sched_process_wait_arg_1_checked(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +0); if r1 == 0 goto +1; " + "r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} From d05524794240b52fdc3b6c1220dd05505715824d Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:27 +0200 Subject: [PATCH 09/10] bpf: Mark syscall helpers as sleepable bpf_sys_bpf() executes the bpf(2) syscall body, which can take mutexes, allocate with GFP_KERNEL, and wait for an RCU grace period. bpf_sys_close() reaches close_fd() and filp_close(), which can sleep as well. Both helpers are limited to BPF_PROG_TYPE_SYSCALL, whose main program is sleepable. That does not make every callback sleepable: a syscall program can register a bpf_timer callback, and the verifier checks that callback in a non-sleepable context while retaining the syscall helper set. Without .might_sleep on the prototypes, such a callback can invoke bpf_sys_bpf() from hrtimer softirq context and trigger a scheduling-while-atomic failure. bpf_sys_close() is exposed through the same missing context check. Set .might_sleep on both prototypes so the existing helper-context check rejects them from timer callbacks and other atomic regions. Calls from the sleepable main body remain valid. Fixes: 79a7f8bdb159 ("bpf: Introduce bpf_sys_bpf() helper and program type.") Fixes: 3abea089246f ("bpf: Add bpf_sys_close() helper.") Reported-by: Nicholas Carlini Suggested-by: Nicholas Carlini Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-10-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/syscall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6874ba1424af..c7bc9ba9b331 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -6568,6 +6568,7 @@ EXPORT_SYMBOL_NS(kern_sys_bpf, "BPF_INTERNAL"); static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_ANYTHING, .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, @@ -6593,6 +6594,7 @@ BPF_CALL_1(bpf_sys_close, u32, fd) static const struct bpf_func_proto bpf_sys_close_proto = { .func = bpf_sys_close, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_ANYTHING, }; From 26a3a510cd3433e15f37ea1d5a6f2c17a0170316 Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Thu, 3 Sep 2026 16:44:28 +0200 Subject: [PATCH 10/10] selftests/bpf: Check syscall helpers in timer callbacks A BPF_PROG_TYPE_SYSCALL program is sleepable, but its bpf_timer callbacks run in a non-sleepable hrtimer softirq context. Add verifier cases that call bpf_sys_bpf() and bpf_sys_close() from timer callbacks. Without the syscall helper prototype annotations these programs load, so their failure expectations expose the bug. Also add successful controls that call each helper from the syscall program main body, ensuring that the intended sleepable use remains accepted. Signed-off-by: Kumar Kartikeya Dwivedi Link: https://lore.kernel.org/r/20260903144433.1716731-11-memxor@gmail.com Signed-off-by: Alexei Starovoitov --- .../bpf/progs/verifier_async_cb_context.c | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c index 6bf95550a024..a7c84d3fa4c7 100644 --- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c +++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c @@ -62,6 +62,70 @@ int timer_sleepable_prog(void *ctx) return 0; } +static int timer_sys_bpf_cb(void *map, int *key, struct bpf_timer *timer) +{ + __u64 attr = 0; + + bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); + return 0; +} + +SEC("syscall") +__failure __msg("sleepable helper bpf_sys_bpf#{{[0-9]+}} in non-sleepable prog") +int timer_sys_bpf_prog(void *ctx) +{ + struct timer_elem *val; + int key = 0; + + val = bpf_map_lookup_elem(&timer_map, &key); + if (!val) + return 0; + + bpf_timer_init(&val->t, &timer_map, 0); + bpf_timer_set_callback(&val->t, timer_sys_bpf_cb); + return 0; +} + +static int timer_sys_close_cb(void *map, int *key, struct bpf_timer *timer) +{ + bpf_sys_close(0); + return 0; +} + +SEC("syscall") +__failure __msg("sleepable helper bpf_sys_close#{{[0-9]+}} in non-sleepable prog") +int timer_sys_close_prog(void *ctx) +{ + struct timer_elem *val; + int key = 0; + + val = bpf_map_lookup_elem(&timer_map, &key); + if (!val) + return 0; + + bpf_timer_init(&val->t, &timer_map, 0); + bpf_timer_set_callback(&val->t, timer_sys_close_cb); + return 0; +} + +SEC("syscall") +__success +int syscall_sys_bpf_prog(void *ctx) +{ + __u64 attr = 0; + + bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); + return 0; +} + +SEC("syscall") +__success +int syscall_sys_close_prog(void *ctx) +{ + bpf_sys_close(0); + return 0; +} + /* Workqueue tests */ struct wq_elem {