* [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 22:40 ` Eduard Zingerman
2026-09-03 22:56 ` bot+bpf-ci
2026-09-03 21:47 ` [PATCH bpf v1 2/8] selftests/bpf: Check rbtree callback restrictions in subprogs Kumar Kartikeya Dwivedi
` (7 subsequent siblings)
8 siblings, 2 replies; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Nicholas Carlini, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Eduard Zingerman, Emil Tsalapatis, kkd,
kernel-team
bpf_rbtree_add() invokes its comparator while the caller holds the root
lock. The native insertion code retains raw parent and link pointers across
the callback, so the verifier prohibits unlocking, consuming tree nodes,
or changing RCU state from that callback.
in_rbtree_lock_required_cb() only checks the innermost verifier frame.
Static subprogram calls are permitted while holding a spin lock, and such a
call pushes a frame without in_callback_fn set. Consequently, all callback
restrictions disappear in the nested frame. The subprogram can unlock the
tree, remove and drop the node being compared, then relock. Native insertion
resumes with the stale parent pointer and links freed memory into the tree.
Walk all active frames for the rbtree callback instead. Benign static
subprograms remain permitted, while callback restrictions follow execution
into nested frames.
Fixes: a44b1334aadd ("bpf: Allow calling static subprogs while holding a bpf_spin_lock")
Reported-by: Nicholas Carlini <npc@anthropic.com>
Suggested-by: Nicholas Carlini <npc@anthropic.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/verifier.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 32d31fa67036..26139fa09f12 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10235,9 +10235,10 @@ static void account_current_path(struct bpf_verifier_env *env)
frame ? state->frame[frame - 1] : NULL);
}
-/* Are we currently verifying the callback for a rbtree helper that must
- * be called with lock held? If so, no need to complain about unreleased
- * lock
+/*
+ * Are we currently verifying the callback for an rbtree kfunc that must
+ * be called with a lock held, or one of that callback's subprogs? If so,
+ * no need to complain about an unreleased lock.
*/
static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
{
@@ -10245,17 +10246,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
struct bpf_insn *insn = env->prog->insnsi;
struct bpf_func_state *callee;
int kfunc_btf_id;
+ u32 frame;
- if (!state->curframe)
- return false;
-
- callee = state->frame[state->curframe];
+ for (frame = state->curframe; frame; frame--) {
+ callee = state->frame[frame];
+ if (!callee->in_callback_fn)
+ continue;
- if (!callee->in_callback_fn)
- return false;
+ kfunc_btf_id = insn[callee->callsite].imm;
+ if (is_rbtree_lock_required_kfunc(kfunc_btf_id))
+ return true;
+ }
- kfunc_btf_id = insn[callee->callsite].imm;
- return is_rbtree_lock_required_kfunc(kfunc_btf_id);
+ return false;
}
static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks
2026-09-03 21:47 ` [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks Kumar Kartikeya Dwivedi
@ 2026-09-03 22:40 ` Eduard Zingerman
2026-09-03 22:56 ` bot+bpf-ci
1 sibling, 0 replies; 19+ messages in thread
From: Eduard Zingerman @ 2026-09-03 22:40 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Nicholas Carlini, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Emil Tsalapatis, kkd, kernel-team
On Thu, 2026-09-03 at 23:47 +0200, Kumar Kartikeya Dwivedi wrote:
> bpf_rbtree_add() invokes its comparator while the caller holds the root
> lock. The native insertion code retains raw parent and link pointers across
> the callback, so the verifier prohibits unlocking, consuming tree nodes,
> or changing RCU state from that callback.
>
> in_rbtree_lock_required_cb() only checks the innermost verifier frame.
> Static subprogram calls are permitted while holding a spin lock, and such a
> call pushes a frame without in_callback_fn set. Consequently, all callback
> restrictions disappear in the nested frame. The subprogram can unlock the
> tree, remove and drop the node being compared, then relock. Native insertion
> resumes with the stale parent pointer and links freed memory into the tree.
>
> Walk all active frames for the rbtree callback instead. Benign static
> subprograms remain permitted, while callback restrictions follow execution
> into nested frames.
>
> Fixes: a44b1334aadd ("bpf: Allow calling static subprogs while holding a bpf_spin_lock")
> Reported-by: Nicholas Carlini <npc@anthropic.com>
> Suggested-by: Nicholas Carlini <npc@anthropic.com>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
...
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks
2026-09-03 21:47 ` [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks Kumar Kartikeya Dwivedi
2026-09-03 22:40 ` Eduard Zingerman
@ 2026-09-03 22:56 ` bot+bpf-ci
1 sibling, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-09-03 22:56 UTC (permalink / raw)
To: memxor, bpf
Cc: npc, ast, andrii, daniel, eddyz87, emil, kkd, kernel-team, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3302 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 32d31fa67036b..26139fa09f12d 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -10235,9 +10235,10 @@ static void account_current_path(struct bpf_verifier_env *env)
> frame ? state->frame[frame - 1] : NULL);
> }
>
> -/* Are we currently verifying the callback for a rbtree helper that must
> - * be called with lock held? If so, no need to complain about unreleased
> - * lock
> +/*
> + * Are we currently verifying the callback for an rbtree kfunc that must
> + * be called with a lock held, or one of that callback's subprogs? If so,
> + * no need to complain about an unreleased lock.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> */
> static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
> {
Does this comment accurately describe what the function does? Looking at
how in_rbtree_lock_required_cb() is used in verifier.c, all four call
sites treat a true return as grounds to reject an operation, not to
suppress a diagnostic:
check_func_arg():
if (in_rbtree_lock_required_cb(env)) {
verbose(env, "can't spin_{lock,unlock} in rbtree cb\n");
return -EACCES;
check_kfunc_args() [non-add branch]:
if (in_rbtree_lock_required_cb(env)) {
verbose(env, "%s not allowed in rbtree cb\n", func_name);
return -EINVAL;
check_kfunc_args() [RES_SPIN_LOCK]:
if (in_rbtree_lock_required_cb(env)) {
verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n");
return -EACCES;
check_kfunc_call():
if (in_rbtree_lock_required_cb(env) && (rcu_lock || rcu_unlock)) {
verbose(env, "Calling bpf_rcu_read_{lock,unlock} in ...\n");
return -EACCES;
The comment states the function exists so there is "no need to complain
about an unreleased lock", but verifier.c contains no unreleased-lock
suppression that consults this helper. The actual unreleased-lock check
in check_resource_leak() operates only on frame 0 and never calls this
function.
Should the comment explain that the function identifies whether we're in
an rbtree comparator (or its subprogram) so that operations which would
break the native insertion's raw parent and link pointers can be
rejected?
> @@ -10245,17 +10246,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
> struct bpf_insn *insn = env->prog->insnsi;
> struct bpf_func_state *callee;
> int kfunc_btf_id;
> + u32 frame;
>
> - if (!state->curframe)
> - return false;
> -
> - callee = state->frame[state->curframe];
> + for (frame = state->curframe; frame; frame--) {
> + callee = state->frame[frame];
> + if (!callee->in_callback_fn)
> + continue;
>
> - if (!callee->in_callback_fn)
> - return false;
> + kfunc_btf_id = insn[callee->callsite].imm;
> + if (is_rbtree_lock_required_kfunc(kfunc_btf_id))
> + return true;
> + }
>
> - kfunc_btf_id = insn[callee->callsite].imm;
> - return is_rbtree_lock_required_kfunc(kfunc_btf_id);
> + return false;
> }
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33811770362
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 2/8] selftests/bpf: Check rbtree callback restrictions in subprogs
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
2026-09-03 21:47 ` [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 22:56 ` bot+bpf-ci
2026-09-03 21:47 ` [PATCH bpf v1 3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable Kumar Kartikeya Dwivedi
` (6 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
Add a verifier failure case where an rbtree comparator enters two nested
static subprograms and the innermost subprogram unlocks and relocks the
tree. Restoring the lock keeps the surrounding callback state balanced,
so the test specifically exercises whether the callback restriction follows
the nested calls.
Also add a load-only positive control whose comparator calls a harmless
static subprogram. This preserves the intended support for verified static
subprogram calls while holding the tree lock.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../testing/selftests/bpf/progs/rbtree_fail.c | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
index 803419a47c62..4504608196ab 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
@@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb
return false;
}
+static __noinline void rbtree_cb_unlock_relock(void)
+{
+ bpf_spin_unlock(&glock);
+ bpf_spin_lock(&glock);
+}
+
+static __noinline void rbtree_cb_nested_unlock(void)
+{
+ rbtree_cb_unlock_relock();
+ asm volatile ("");
+}
+
+static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_nested_unlock();
+
+ return node_a->key < node_b->key;
+}
+
+static __noinline void rbtree_cb_noop(void)
+{
+ asm volatile ("");
+}
+
+static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_noop();
+
+ return node_a->key < node_b->key;
+}
+
static __always_inline
long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))
{
@@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
return 0;
}
+SEC("?tc")
+__failure __msg("can't spin_{lock,unlock} in rbtree cb")
+long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)
+{
+ return add_with_cb(less__bad_subprog_unlock);
+}
+
+SEC("?tc")
+__success
+long rbtree_api_add_cb_subprog_allowed(void *ctx)
+{
+ return add_with_cb(less__subprog_allowed);
+}
+
char _license[] SEC("license") = "GPL";
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 2/8] selftests/bpf: Check rbtree callback restrictions in subprogs
2026-09-03 21:47 ` [PATCH bpf v1 2/8] selftests/bpf: Check rbtree callback restrictions in subprogs Kumar Kartikeya Dwivedi
@ 2026-09-03 22:56 ` bot+bpf-ci
0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-09-03 22:56 UTC (permalink / raw)
To: memxor, bpf
Cc: ast, andrii, daniel, eddyz87, emil, npc, kkd, kernel-team, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2736 bytes --]
> diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
> index 803419a47c626..4504608196abd 100644
> --- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
> +++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
[ ... ]
> @@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb
> return false;
> }
>
> +static __noinline void rbtree_cb_unlock_relock(void)
> +{
> + bpf_spin_unlock(&glock);
> + bpf_spin_lock(&glock);
> +}
> +
> +static __noinline void rbtree_cb_nested_unlock(void)
> +{
> + rbtree_cb_unlock_relock();
> + asm volatile ("");
> +}
> +
> +static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
> +{
> + struct node_data *node_a;
> + struct node_data *node_b;
> +
> + node_a = container_of(a, struct node_data, node);
> + node_b = container_of(b, struct node_data, node);
> + rbtree_cb_nested_unlock();
> +
> + return node_a->key < node_b->key;
> +}
> +
> +static __noinline void rbtree_cb_noop(void)
> +{
> + asm volatile ("");
> +}
> +
> +static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)
> +{
> + struct node_data *node_a;
> + struct node_data *node_b;
> +
> + node_a = container_of(a, struct node_data, node);
> + node_b = container_of(b, struct node_data, node);
> + rbtree_cb_noop();
> +
> + return node_a->key < node_b->key;
> +}
> +
[ ... ]
> @@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
> return 0;
> }
>
> +SEC("?tc")
> +__failure __msg("can't spin_{lock,unlock} in rbtree cb")
> +long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)
> +{
> + return add_with_cb(less__bad_subprog_unlock);
> +}
This isn't a bug, but would it be worth routing one of the existing
bad-callback bodies (like less__bad_fn_call_remove) through the nested
subprog too? The companion verifier change walks frames for all four
restrictions gated by in_rbtree_lock_required_cb(), but only the
spin-lock gate gets nested-frame coverage here. The file already has
direct-callback cases for the kfunc/res_spin/RCU gates
(less__bad_fn_call_remove, less__bad_fn_call_first_unlock_after,
less__bad_res_spin_unlock), so extending one or two through
rbtree_cb_nested_unlock() would be a small addition.
> +
> +SEC("?tc")
> +__success
> +long rbtree_api_add_cb_subprog_allowed(void *ctx)
> +{
> + return add_with_cb(less__subprog_allowed);
> +}
> +
> char _license[] SEC("license") = "GPL";
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33811770362
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
2026-09-03 21:47 ` [PATCH bpf v1 1/8] bpf: Check ancestor frames for rbtree callbacks Kumar Kartikeya Dwivedi
2026-09-03 21:47 ` [PATCH bpf v1 2/8] selftests/bpf: Check rbtree callback restrictions in subprogs Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 22:15 ` Eduard Zingerman
2026-09-03 21:47 ` [PATCH bpf v1 4/8] selftests/bpf: Test btf lookup helper sleepability Kumar Kartikeya Dwivedi
` (5 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Sashiko, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
When bpf_btf_find_by_name_kind() finds a type in module BTF, it
returns a new BTF object fd through __btf_new_fd(). This reaches
anon_inode_getfd(), which can sleep while allocating or expanding the
current task fd table.
The helper prototype does not set might_sleep, so the verifier allows
the helper in non-sleepable contexts such as BPF timer callbacks. The
fd allocation can then sleep in softirq context and install the fd into
the interrupted task.
Mark the helper as sleepable. This preserves calls from the main body
of a sleepable syscall program while rejecting calls from its
non-sleepable regions.
Fixes: 3d78417b60fb ("bpf: Add bpf_btf_find_by_name_kind() helper.")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/bpf/20260903155150.D57251F000E9@smtp.kernel.org
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/btf.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 5d93fd82e764..9f33e95d5741 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8749,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int
const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.func = bpf_btf_find_by_name_kind,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable
2026-09-03 21:47 ` [PATCH bpf v1 3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable Kumar Kartikeya Dwivedi
@ 2026-09-03 22:15 ` Eduard Zingerman
0 siblings, 0 replies; 19+ messages in thread
From: Eduard Zingerman @ 2026-09-03 22:15 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Sashiko, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Emil Tsalapatis, Nicholas Carlini, kkd, kernel-team
On Thu, 2026-09-03 at 23:47 +0200, Kumar Kartikeya Dwivedi wrote:
> When bpf_btf_find_by_name_kind() finds a type in module BTF, it
> returns a new BTF object fd through __btf_new_fd(). This reaches
> anon_inode_getfd(), which can sleep while allocating or expanding the
> current task fd table.
>
> The helper prototype does not set might_sleep, so the verifier allows
> the helper in non-sleepable contexts such as BPF timer callbacks. The
> fd allocation can then sleep in softirq context and install the fd into
> the interrupted task.
>
> Mark the helper as sleepable. This preserves calls from the main body
> of a sleepable syscall program while rejecting calls from its
> non-sleepable regions.
>
> Fixes: 3d78417b60fb ("bpf: Add bpf_btf_find_by_name_kind() helper.")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Link: https://lore.kernel.org/bpf/20260903155150.D57251F000E9@smtp.kernel.org
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
...
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 4/8] selftests/bpf: Test btf lookup helper sleepability
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (2 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 21:47 ` [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable Kumar Kartikeya Dwivedi
` (4 subsequent siblings)
8 siblings, 0 replies; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
Add an expected failure case which calls
bpf_btf_find_by_name_kind() from a BPF timer callback. Without the
helper prototype being marked sleepable, the verifier accepts the
program and the load unexpectedly succeeds.
Also add a positive control which calls the helper directly from a
syscall program. This verifies that marking the helper sleepable only
rejects it in non-sleepable regions.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../bpf/progs/verifier_async_cb_context.c | 34 +++++++++++++++++++
1 file changed, 34 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 a7c84d3fa4c7..e0926767bbd3 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -108,6 +108,30 @@ int timer_sys_close_prog(void *ctx)
return 0;
}
+static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog")
+int timer_btf_find_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_btf_find_cb);
+ return 0;
+}
+
SEC("syscall")
__success
int syscall_sys_bpf_prog(void *ctx)
@@ -126,6 +150,16 @@ int syscall_sys_close_prog(void *ctx)
return 0;
}
+SEC("syscall")
+__success
+int syscall_btf_find_prog(void *ctx)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
/* Workqueue tests */
struct wq_elem {
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (3 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 4/8] selftests/bpf: Test btf lookup helper sleepability Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 22:00 ` Eduard Zingerman
2026-09-03 22:56 ` bot+bpf-ci
2026-09-03 21:47 ` [PATCH bpf v1 6/8] selftests/bpf: Check faultable stack helper contexts Kumar Kartikeya Dwivedi
` (3 subsequent siblings)
8 siblings, 2 replies; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
The faultable variants of bpf_get_stack() and bpf_get_task_stack() pass
may_fault=true into the common stack collection code. Resolving user-space
build IDs may then call build_id_parse_file() and block on filesystem
reads.
Neither helper prototype sets might_sleep. Since prototype selection uses
the sleepability of the whole program, the verifier can still allow these
helpers from a non-sleepable region within that program, such as an
explicit RCU or preemption-disabled region. The task-stack helper can also
be called from a non-sleepable timer callback of a sleepable program.
Mark both faultable prototypes as sleepable. The existing helper context
check then rejects these calls while continuing to allow them in genuinely
sleepable contexts.
Fixes: d4dd9775ec24 ("bpf: wire up sleepable bpf_get_stack() and bpf_get_task_stack() helpers")
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/stackmap.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d00..d09d4c3fe547 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si
const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.func = bpf_get_stack_sleepable,
.gpl_only = true,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
@@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf
const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.func = bpf_get_task_stack_sleepable,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable
2026-09-03 21:47 ` [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable Kumar Kartikeya Dwivedi
@ 2026-09-03 22:00 ` Eduard Zingerman
2026-09-03 22:56 ` bot+bpf-ci
1 sibling, 0 replies; 19+ messages in thread
From: Eduard Zingerman @ 2026-09-03 22:00 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Emil Tsalapatis, Nicholas Carlini, kkd, kernel-team
On Thu, 2026-09-03 at 23:47 +0200, Kumar Kartikeya Dwivedi wrote:
> The faultable variants of bpf_get_stack() and bpf_get_task_stack() pass
> may_fault=true into the common stack collection code. Resolving user-space
> build IDs may then call build_id_parse_file() and block on filesystem
> reads.
It's kinda in the helpers name.
> Neither helper prototype sets might_sleep. Since prototype selection uses
> the sleepability of the whole program, the verifier can still allow these
> helpers from a non-sleepable region within that program, such as an
> explicit RCU or preemption-disabled region. The task-stack helper can also
> be called from a non-sleepable timer callback of a sleepable program.
>
> Mark both faultable prototypes as sleepable. The existing helper context
> check then rejects these calls while continuing to allow them in genuinely
> sleepable contexts.
>
> Fixes: d4dd9775ec24 ("bpf: wire up sleepable bpf_get_stack() and bpf_get_task_stack() helpers")
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable
2026-09-03 21:47 ` [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable Kumar Kartikeya Dwivedi
2026-09-03 22:00 ` Eduard Zingerman
@ 2026-09-03 22:56 ` bot+bpf-ci
1 sibling, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-09-03 22:56 UTC (permalink / raw)
To: memxor, bpf
Cc: ast, andrii, daniel, eddyz87, emil, npc, kkd, kernel-team, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4106 bytes --]
> diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
> index a839041e0d008..d09d4c3fe547c 100644
> --- a/kernel/bpf/stackmap.c
> +++ b/kernel/bpf/stackmap.c
> @@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si
> const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
> .func = bpf_get_stack_sleepable,
> .gpl_only = true,
> + .might_sleep = true,
> .ret_type = RET_INTEGER,
> .arg1_type = ARG_PTR_TO_CTX,
> .arg2_type = ARG_PTR_TO_UNINIT_MEM,
Does the unconditional .might_sleep marking reject too much? The faultable
path only sleeps when BPF_F_USER_BUILD_ID is requested. Looking at
__bpf_get_stack() in kernel/bpf/stackmap.c, the may_fault=true argument
flows to callchain_finalize():
if (user_build_id)
stack_map_get_build_id_offset(buf, trace_nr, user, may_fault);
and stack_map_get_build_id_offset() only takes the blocking path when
may_fault && has_user_ctx. With flags that do not contain BPF_F_USER_BUILD_ID,
user_build_id is false and no sleeping callee (build_id_parse_file()) is
reached; everything else already runs under preempt_disable().
Because prototype selection is per-program (kprobe_prog_func_proto() in
kernel/trace/bpf_trace.c and bpf_base_func_proto() in kernel/bpf/helpers.c,
both keyed only on prog->sleepable), the new flag rejects every call from
a non-sleepable region of a sleepable program, including calls that provably
cannot sleep.
This previously-accepted program is now rejected:
SEC("uprobe.s/...")
int prog(struct pt_regs *ctx) {
bpf_rcu_read_lock();
...
bpf_get_stack(ctx, buf, sizeof(buf), BPF_F_USER_STACK);
bpf_rcu_read_unlock();
}
The verifier sets state->in_sleepable = prog->sleepable, bpf_rcu_read_lock()
raises active_rcu_locks, so in_sleepable_context() (kernel/bpf/verifier.c)
returns false and the load fails with "sleepable helper bpf_get_stack#... in
rcu_read_lock region".
The follow-up selftest commit f73872d1b9dba confirms these programs loaded
before the change ("Both programs load when the prototypes lack might_sleep").
The diagnostic advice emitted ("Move the helper call outside the critical
section, or use a non-sleepable helper") is also not actionable for the
second half: once prog->sleepable is set there is no way for a program to
reach bpf_get_stack_proto / bpf_get_task_stack_proto, so "use a
non-sleepable helper" has no corresponding UAPI.
The tightening itself is correct and intended, and workarounds exist (hoist
the call, or bpf_task_acquire() the task before leaving the RCU section),
so this is a scope observation rather than a memory-safety defect.
Note that the tree already has a narrower mechanism for exactly this
situation: insn_aux_data[insn_idx].non_sleepable (set for helpers in
kernel/bpf/verifier.c) is used to swap bpf_dynptr_from_file /
bpf_arena_alloc_pages / bpf_arena_free_pages for non-sleepable
implementations at the offending call site instead of rejecting the program.
That consumer only exists for kfuncs today; if the intent is that helper
call sites cannot be downgraded the same way, the changelog would be a good
place to say so, and to state explicitly that calls without
BPF_F_USER_BUILD_ID are rejected too.
> @@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf
> const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
> .func = bpf_get_task_stack_sleepable,
> .gpl_only = false,
> + .might_sleep = true,
> .ret_type = RET_INTEGER,
> .arg1_type = ARG_PTR_TO_BTF_ID,
> .arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
The same concern applies here: bpf_get_task_stack() in a sleepable program's
RCU section or timer callback now rejects unconditionally, even when the
call cannot reach build_id_parse_file().
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33811770362
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 6/8] selftests/bpf: Check faultable stack helper contexts
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (4 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 5/8] bpf: Mark faultable stack helpers as sleepable Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 22:56 ` bot+bpf-ci
2026-09-03 21:47 ` [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks Kumar Kartikeya Dwivedi
` (2 subsequent siblings)
8 siblings, 1 reply; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
Add verifier coverage for the sleepable bpf_get_stack() and
bpf_get_task_stack() implementations. Call each helper while preemption is
disabled and require the verifier to reject it as sleepable.
Both programs load when the prototypes lack might_sleep, so the
expected-failure tests fail. Keep success controls outside the
non-preemptible region to ensure ordinary calls from sleepable uprobes
remain valid.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/progs/preempt_lock.c | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c
index 6d5fce7e6ffc..81c459435680 100644
--- a/tools/testing/selftests/bpf/progs/preempt_lock.c
+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c
@@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx)
return 0;
}
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_stack#")
+int preempt_sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_preempt_disable();
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_task_stack#")
+int preempt_sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_preempt_disable();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
int preempt_sleepable_kfunc(void *ctx)
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 6/8] selftests/bpf: Check faultable stack helper contexts
2026-09-03 21:47 ` [PATCH bpf v1 6/8] selftests/bpf: Check faultable stack helper contexts Kumar Kartikeya Dwivedi
@ 2026-09-03 22:56 ` bot+bpf-ci
0 siblings, 0 replies; 19+ messages in thread
From: bot+bpf-ci @ 2026-09-03 22:56 UTC (permalink / raw)
To: memxor, bpf
Cc: ast, andrii, daniel, eddyz87, emil, npc, kkd, kernel-team, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1346 bytes --]
> Add verifier coverage for the sleepable bpf_get_stack() and
> bpf_get_task_stack() implementations. Call each helper while preemption is
> disabled and require the verifier to reject it as sleepable.
>
> Both programs load when the prototypes lack might_sleep, so the
> expected-failure tests fail. Keep success controls outside the
> non-preemptible region to ensure ordinary calls from sleepable uprobes
> remain valid.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This isn't a bug, but the phrasing "Both programs load when the prototypes
lack might_sleep, so the expected-failure tests fail" reads a bit oddly in
present tense when the patch is adding the tests. It could be read as
reporting that the tree currently has failing tests, though the intent seems
to be explaining why the negative tests are discriminating.
Also, which pair of programs does "both" refer to when four programs are
added?
Would it read more clearly as something like "Without the prototype change in
the previous patch both programs would load, so these expected-failure tests
pin the new rejection"?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33811770362
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (5 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 6/8] selftests/bpf: Check faultable stack helper contexts Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-03 21:58 ` Eduard Zingerman
2026-09-03 21:47 ` [PATCH bpf v1 8/8] selftests/bpf: " Kumar Kartikeya Dwivedi
2026-09-04 2:30 ` [PATCH bpf v1 0/8] Misc bug fixes - part 2 patchwork-bot+netdevbpf
8 siblings, 1 reply; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Sashiko, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
check_ld_abs() models a failed BPF_LD_ABS or BPF_LD_IND in a
subprogram as an implicit return with R0 set to zero. It calls
prepare_func_exit() to explore this synthesized path.
When the load is reached directly from a synchronous callback,
prepare_func_exit() enforces the callback return contract and marks R0
precise. R0 is not derived from a real instruction on this path, so
precision backtracking reaches the callback call with R0 still requested
and triggers the "callback unexpected regs" verifier bug. A privileged
program loader can therefore cause a verifier warning and an -EFAULT
BPF_PROG_LOAD.
These legacy packet-load instructions are deprecated. Reject them from
callbacks rather than complicating their implicit-return model. Check all
active frames before constructing the implicit return so nested static
subprograms cannot hide the callback context.
Global functions are verified independently with a fresh frame zero, so
an active-frame check cannot identify a global function called from a
callback. Also check the complete subprogram call graph during stack-depth
validation and reject a function containing a legacy load when any caller
is a callback. This covers global and static descendants without making
has_ld_abs transitive, preserving its per-function BTF return-type check.
Ordinary uses outside callbacks remain supported.
Fixes: ee861486e377 ("bpf: Fix ld_{abs,ind} failure path analysis in subprogs")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/bpf/20260903152147.C0E241F00A3A@smtp.kernel.org
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/verifier.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 26139fa09f12..2cb8fba68cd1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5302,6 +5302,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
if (!priv_stack_supported)
subprog[idx].priv_stack_mode = NO_PRIV_STACK;
process_func:
+ if (subprog[idx].has_ld_abs) {
+ for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
+ if (subprog[tmp].is_cb) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+ }
+
/* protect against potential stack overflow that might happen when
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack
* depth for such case down to 256 so that the worst case scenario
@@ -17182,6 +17191,7 @@ static bool may_access_skb(enum bpf_prog_type type)
*/
static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
+ struct bpf_verifier_state *state = env->cur_state;
struct bpf_reg_state *regs = cur_regs(env);
static const int ctx_reg = BPF_REG_6;
u8 mode = BPF_MODE(insn->code);
@@ -17192,6 +17202,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
+ for (i = state->curframe; i; i--) {
+ if (state->frame[i]->in_callback_fn) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+
if (!env->ops->gen_ld_abs) {
verifier_bug(env, "gen_ld_abs is null");
return -EFAULT;
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks
2026-09-03 21:47 ` [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks Kumar Kartikeya Dwivedi
@ 2026-09-03 21:58 ` Eduard Zingerman
2026-09-04 2:22 ` Alexei Starovoitov
0 siblings, 1 reply; 19+ messages in thread
From: Eduard Zingerman @ 2026-09-03 21:58 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Sashiko, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Emil Tsalapatis, Nicholas Carlini, kkd, kernel-team
On Thu, 2026-09-03 at 23:47 +0200, Kumar Kartikeya Dwivedi wrote:
...
> Global functions are verified independently with a fresh frame zero, so
> an active-frame check cannot identify a global function called from a
> callback. Also check the complete subprogram call graph during stack-depth
> validation and reject a function containing a legacy load when any caller
> is a callback. This covers global and static descendants without making
> has_ld_abs transitive, preserving its per-function BTF return-type check.
> Ordinary uses outside callbacks remain supported.
I agree on a first part, but why is this part necessary?
Return value from a global is modeled as an unknown scalar anyway,
hence ld_{ind,abs} short-circuiting it to return 0 should be fine.
Forgoing globals cuts this patch in half.
...
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks
2026-09-03 21:58 ` Eduard Zingerman
@ 2026-09-04 2:22 ` Alexei Starovoitov
0 siblings, 0 replies; 19+ messages in thread
From: Alexei Starovoitov @ 2026-09-04 2:22 UTC (permalink / raw)
To: Eduard Zingerman
Cc: Kumar Kartikeya Dwivedi, bpf, Sashiko, Alexei Starovoitov,
Andrii Nakryiko, Daniel Borkmann, Emil Tsalapatis,
Nicholas Carlini, kkd, Kernel Team
On Thu, Sep 3, 2026 at 2:58 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Thu, 2026-09-03 at 23:47 +0200, Kumar Kartikeya Dwivedi wrote:
>
> ...
>
> > Global functions are verified independently with a fresh frame zero, so
> > an active-frame check cannot identify a global function called from a
> > callback. Also check the complete subprogram call graph during stack-depth
> > validation and reject a function containing a legacy load when any caller
> > is a callback. This covers global and static descendants without making
> > has_ld_abs transitive, preserving its per-function BTF return-type check.
> > Ordinary uses outside callbacks remain supported.
>
> I agree on a first part, but why is this part necessary?
> Return value from a global is modeled as an unknown scalar anyway,
> hence ld_{ind,abs} short-circuiting it to return 0 should be fine.
> Forgoing globals cuts this patch in half.
Better safe than sorry. Soon global progs might return more than scalars
and we'd need to remember to revit this.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH bpf v1 8/8] selftests/bpf: Reject legacy packet loads from callbacks
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (6 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 7/8] bpf: Reject legacy packet loads from callbacks Kumar Kartikeya Dwivedi
@ 2026-09-03 21:47 ` Kumar Kartikeya Dwivedi
2026-09-04 2:30 ` [PATCH bpf v1 0/8] Misc bug fixes - part 2 patchwork-bot+netdevbpf
8 siblings, 0 replies; 19+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-09-03 21:47 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Emil Tsalapatis, Nicholas Carlini, kkd,
kernel-team
Add verifier coverage for the callback restriction on legacy packet
loads. Exercise BPF_LD_ABS directly in a bpf_loop callback and
BPF_LD_IND from a static subprogram called by the callback, ensuring that
callback context follows nested static calls.
Also exercise a callback which reaches BPF_LD_IND through a global
function and its static descendant. A sibling success case calls the same
global chain outside a callback, preserving support for ordinary global
packet loads. Existing success cases continue to cover loads from ordinary
static subprograms.
The failure cases expect the policy-specific rejection instead of reaching
the implicit-return path, triggering a verifier warning, or being accepted
through a function boundary.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/progs/verifier_ld_ind.c | 96 +++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
index 09e81b99eecb..32989f981fb6 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
@@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void)
::: __clobber_all);
}
+__naked __noinline __used
+static int ld_abs_callback(void)
+{
+ asm volatile (
+ "r6 = *(u64 *)(r2 + 0);"
+ ".8byte %[ld_abs];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("ld_abs: reject in callback")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_abs_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_abs_callback, &skb, 0);
+ return 0;
+}
+
+__naked __noinline __used
+static int ld_ind_callback_subprog(void)
+{
+ asm volatile (
+ "r6 = r1;"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_all);
+}
+
+__naked __noinline __used
+static int ld_ind_callback(void)
+{
+ asm volatile (
+ "r1 = *(u64 *)(r2 + 0);"
+ "call ld_ind_callback_subprog;"
+ "exit;"
+ ::: __clobber_all);
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_callback_subprog_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_callback, &skb, 0);
+ return 0;
+}
+
+static __noinline int ld_ind_global_static(struct __sk_buff *skb)
+{
+ asm volatile (
+ "r6 = %[skb];"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ :
+ : [skb] "r"(skb),
+ __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_common, "r6", "r7");
+ return skb->mark;
+}
+
+__noinline int ld_ind_global(struct __sk_buff *skb)
+{
+ return ld_ind_global_static(skb);
+}
+
+static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)
+{
+ ld_ind_global(*ctx);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback global subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_global_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_global_callback, &skb, 0);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: allow in non-callback global subprog")
+__success
+int ld_ind_global_subprog_ok(struct __sk_buff *skb)
+{
+ return ld_ind_global(skb);
+}
+
/*
* ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
* A test with void return must be rejected.
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH bpf v1 0/8] Misc bug fixes - part 2
2026-09-03 21:47 [PATCH bpf v1 0/8] Misc bug fixes - part 2 Kumar Kartikeya Dwivedi
` (7 preceding siblings ...)
2026-09-03 21:47 ` [PATCH bpf v1 8/8] selftests/bpf: " Kumar Kartikeya Dwivedi
@ 2026-09-04 2:30 ` patchwork-bot+netdevbpf
8 siblings, 0 replies; 19+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-04 2:30 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi
Cc: bpf, ast, andrii, daniel, eddyz87, emil, npc, kkd, kernel-team
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Thu, 3 Sep 2026 23:47:46 +0200 you wrote:
> A set of miscellaneous fixes for bugs reported by Nicholas, plus some
> new findings by GPT-5.6-Sol and Sashiko. See commit logs for details.
>
> Kumar Kartikeya Dwivedi (8):
> bpf: Check ancestor frames for rbtree callbacks
> selftests/bpf: Check rbtree callback restrictions in subprogs
> bpf: Mark bpf_btf_find_by_name_kind() as sleepable
> selftests/bpf: Test btf lookup helper sleepability
> bpf: Mark faultable stack helpers as sleepable
> selftests/bpf: Check faultable stack helper contexts
> bpf: Reject legacy packet loads from callbacks
> selftests/bpf: Reject legacy packet loads from callbacks
>
> [...]
Here is the summary with links:
- [bpf,v1,1/8] bpf: Check ancestor frames for rbtree callbacks
https://git.kernel.org/bpf/bpf/c/369f4ce73457
- [bpf,v1,2/8] selftests/bpf: Check rbtree callback restrictions in subprogs
https://git.kernel.org/bpf/bpf/c/22ab49afe1c9
- [bpf,v1,3/8] bpf: Mark bpf_btf_find_by_name_kind() as sleepable
https://git.kernel.org/bpf/bpf/c/620614bf7672
- [bpf,v1,4/8] selftests/bpf: Test btf lookup helper sleepability
https://git.kernel.org/bpf/bpf/c/687b2729ce4c
- [bpf,v1,5/8] bpf: Mark faultable stack helpers as sleepable
https://git.kernel.org/bpf/bpf/c/9d02927fdf4e
- [bpf,v1,6/8] selftests/bpf: Check faultable stack helper contexts
https://git.kernel.org/bpf/bpf/c/1ba0d0d8b675
- [bpf,v1,7/8] bpf: Reject legacy packet loads from callbacks
https://git.kernel.org/bpf/bpf/c/e7d28823c662
- [bpf,v1,8/8] selftests/bpf: Reject legacy packet loads from callbacks
https://git.kernel.org/bpf/bpf/c/23724e009f65
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 19+ messages in thread