[PATCH bpf v5 0/2] bpf: Fix kfunc implicit arg injection and add selftest

chenyuan_fl@163.com posted 2 patches 2 weeks, 5 days ago
There is a newer version of this series
kernel/bpf/verifier.c                              | 19 +++++++++++++++--
.../testing/selftests/bpf/test_kmods/bpf_testmod.c |  6 +++++-
2 files changed, 22 insertions(+), 3 deletions(-)
[PATCH bpf v5 0/2] bpf: Fix kfunc implicit arg injection and add selftest
Posted by chenyuan_fl@163.com 2 weeks, 5 days ago
From: Yuan Chen <chenyuan@kylinos.cn>

v4: https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/

v1 made btf_types_are_same() cross-BTF aware by comparing kind, size
and name.  Alan Maguire pointed out that this would be too permissive
since two different structs can share the same name and size.

v2 used a name-based classifier (get_kfunc_arg_inject_type).

v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit
check in check_kfunc_args(), so that an implicit argument reaching
is_kfunc_arg_implicit() without any prior handler is rejected with
-EFAULT instead of silently skipped (Eduard Zingerman).  This prevents
the silent fallthrough that occurred when module BTF was inconsistent
with vmlinux (e.g. pahole 1.30 breaking distilled base dedup).

v4: fix a build error in the selftest.

v5 changes:
- Patch 1: extend the kfunc allow-list in check_kfunc_args() to cover
  all kfuncs with implicit args handled in bpf_fixup_kfunc_call():
  obj_new, percpu_obj_new, obj_drop, percpu_obj_drop,
  refcount_acquire, list_push, rbtree_add (Eduard Zingerman).
- Patch 2: simplify the selftest by verifying aux->name in
  bpf_kfunc_implicit_arg() instead of adding a separate struct_ops
  test program with inline assembly (Eduard Zingerman).  This is
  a single-file change exercised by the existing kfunc_implicit_args
  selftest.

Yuan Chen (2):
  bpf: Fix kfunc implicit arg inject type detection to prevent invalid
    pointer deref
  selftests/bpf: strengthen bpf_kfunc_implicit_arg to verify aux injection

 kernel/bpf/verifier.c                              | 19 +++++++++++++++--
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  6 +++++-
 2 files changed, 22 insertions(+), 3 deletions(-)

-- 
2.54.0
[PATCH bpf v6 0/2] bpf: Fix kfunc implicit arg injection and add selftest
Posted by chenyuan_fl@163.com 2 weeks, 4 days ago
From: Yuan Chen <chenyuan@kylinos.cn>

v5: https://lore.kernel.org/bpf/20260608142618.3064380-1-chenyuan_fl@163.com/

v1 made btf_types_are_same() cross-BTF aware by comparing kind, size
and name.  Alan Maguire pointed out that this would be too permissive
since two different structs can share the same name and size.

v2 used a name-based classifier (get_kfunc_arg_inject_type).

v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit
check in check_kfunc_args(), so that an implicit argument reaching
is_kfunc_arg_implicit() without any prior handler is rejected with
-EFAULT instead of silently skipped (Eduard Zingerman).  This prevents
the silent fallthrough that occurred when module BTF was inconsistent
with vmlinux (e.g. pahole 1.30 breaking distilled base dedup).

v4: fix a build error in the selftest.

v5 changes:
- Patch 1: extend the kfunc allow-list to cover all KF_IMPLICIT_ARGS
  kfuncs handled in bpf_fixup_kfunc_call(): obj_new, percpu_obj_new,
  obj_drop, percpu_obj_drop, refcount_acquire, list_push, rbtree_add.
- Patch 2: simplify the selftest by verifying aux->name in
  bpf_kfunc_implicit_arg() instead of adding a separate struct_ops
  test program with inline assembly.

v6 changes:
- Patch 1: fix comment formatting to follow kernel style (Eduard
  Zingerman).

Yuan Chen (2):
  bpf: Fix kfunc implicit arg inject type detection to prevent invalid
    pointer deref
  selftests/bpf: strengthen bpf_kfunc_implicit_arg to verify aux injection

 kernel/bpf/verifier.c                              | 20 +++++++++++++++++++-
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  6 +++++-
 2 files changed, 24 insertions(+), 2 deletions(-)

-- 
2.54.0
[PATCH bpf v6 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
Posted by chenyuan_fl@163.com 2 weeks, 4 days ago
From: Yuan Chen <chenyuan@kylinos.cn>

When a module kfunc declares an implicit struct bpf_prog_aux * argument,
the verifier must identify it so the kernel injects env->prog->aux into
the correct register at runtime.  The original check used
is_kfunc_arg_prog_aux() which calls btf_types_are_same() to compare the
module BTF type against vmlinux.

Root Cause

This issue was triggered by pahole 1.30 generating module BTF with
incorrect type information, which caused the kernel's distilled base
BTF deduplication for modules to fail.  As a result, the module retained
its own copy of struct bpf_prog_aux with a different BTF ID than
vmlinux's definition.  While pahole 1.31 fixed the BTF generation issue,
the kernel must be robust against such inconsistencies: a BTF mismatch
should result in a clean rejection, not a kernel crash or information
disclosure.

When the distilled base dedup fails and btf_types_are_same() cannot
match the module's bpf_prog_aux type against vmlinux's,
is_kfunc_arg_prog_aux() returned false and the code fell through
silently without setting arg_prog.  The kfunc then received whatever
value was in the argument register and dereferenced it as a
bpf_prog_aux pointer, leading to:

  BUG: kernel invalid pointer dereference, address: 00000000000009e2
  RIP: bpf_prog_get_assoc_struct_ops+0xa/0xc0
  RDI: 0x000000000000046d (stale register value)

In the observed crash the stale value was the process PID, causing a
dereference within the unmapped NULL page.  However, an attacker able
to control the register value -- for example by writing a BPF program
that explicitly sets R2 before calling a KF_IMPLICIT_ARGS kfunc --
could redirect the dereference to arbitrary kernel memory, turning
this into an information disclosure.  The fix ensures the verifier
either validates and injects the correct bpf_prog_aux pointer, or
rejects the program outright -- no silent fallthrough that could
be exploited.

Crash Stack Trace

  PID: 1133     TASK: ffff8881057d3900  CPU: 3    COMMAND: "test_progs"
   #0 machine_kexec at ffffffff812f6e26
   #1 __crash_kexec at ffffffff8145a788
   #2 crash_kexec at ffffffff8145ac24
   #3 oops_end at ffffffff812bb67c
   #4 page_fault_oops at ffffffff813053a1
   #5 exc_page_fault at ffffffff828e60a1
   #6 asm_exc_page_fault at ffffffff810012a6
      [exception RIP: bpf_prog_get_assoc_struct_ops+10]
      RIP: ffffffff815c024a  RSP: ffffc90001b57e48  RFLAGS: 00010283
      RAX: ffff8881057d3900  RBX: ffffc90001b57e68  RCX: ffff8881057d3900
      RDX: 0000607d4d1768b8  RSI: 000000000000046d  RDI: 000000000000046d
   #7 bpf_kfunc_multi_st_ops_test_1_assoc at ffffffffc0013a85 [bpf_testmod]
   #8 bpf_trace_run2 at ffffffff814f8332
   #9 __traceiter_sys_enter at ffffffff81415f45
  #10 trace_syscall_enter at ffffffff81416735
  #11 do_syscall_64 at ffffffff828e06a1

Fix

Split the combined is_kfunc_arg_ignore() || is_kfunc_arg_implicit()
check in check_kfunc_args() so that an implicit argument reaching
is_kfunc_arg_implicit() without being handled by a prior handler is
rejected with -EFAULT, instead of silently skipped.  Existing implicit
      args in bpf_fixup_kfunc_call() (obj_new, percpu_obj_new, obj_drop,
      percpu_obj_drop, refcount_acquire, list_push, rbtree_add) are
      explicitly allowed.

Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 kernel/bpf/verifier.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8ed484cb1a8a..0d598a419008 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11885,9 +11885,28 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 			continue;
 		}
 
-		if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
+		if (is_kfunc_arg_ignore(btf, &args[i]))
 			continue;
 
+		if (is_kfunc_arg_implicit(meta, i)) {
+			/*
+			 * This is an exhaustive list of the non-bpf_prog_aux
+			 * implicit args kfuncs, which are handled
+			 * post-verification by bpf_fixup_kfunc_call().
+			 */
+			if (is_bpf_obj_new_kfunc(meta->func_id) ||
+			    is_bpf_percpu_obj_new_kfunc(meta->func_id) ||
+			    is_bpf_obj_drop_kfunc(meta->func_id) ||
+			    is_bpf_percpu_obj_drop_kfunc(meta->func_id) ||
+			    is_bpf_refcount_acquire_kfunc(meta->func_id) ||
+			    is_bpf_list_push_kfunc(meta->func_id) ||
+			    is_bpf_rbtree_add_kfunc(meta->func_id))
+				continue;
+			verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
+				reg_arg_name(env, argno));
+			return -EFAULT;
+		}
+
 		t = btf_type_skip_modifiers(btf, args[i].type, NULL);
 
 		if (btf_type_is_scalar(t)) {
-- 
2.54.0
Re: [PATCH bpf v6 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
Posted by Eduard Zingerman 2 weeks, 3 days ago
On Tue, 2026-06-09 at 20:52 +0800, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
> 
> When a module kfunc declares an implicit struct bpf_prog_aux * argument,
> the verifier must identify it so the kernel injects env->prog->aux into
> the correct register at runtime.  The original check used
> is_kfunc_arg_prog_aux() which calls btf_types_are_same() to compare the
> module BTF type against vmlinux.
> 
> Root Cause
> 
> This issue was triggered by pahole 1.30 generating module BTF with
> incorrect type information, which caused the kernel's distilled base
> BTF deduplication for modules to fail.  As a result, the module retained
> its own copy of struct bpf_prog_aux with a different BTF ID than
> vmlinux's definition.  While pahole 1.31 fixed the BTF generation issue,
> the kernel must be robust against such inconsistencies: a BTF mismatch
> should result in a clean rejection, not a kernel crash or information
> disclosure.
> 
> When the distilled base dedup fails and btf_types_are_same() cannot
> match the module's bpf_prog_aux type against vmlinux's,
> is_kfunc_arg_prog_aux() returned false and the code fell through
> silently without setting arg_prog.  The kfunc then received whatever
> value was in the argument register and dereferenced it as a
> bpf_prog_aux pointer, leading to:
> 
>   BUG: kernel invalid pointer dereference, address: 00000000000009e2
>   RIP: bpf_prog_get_assoc_struct_ops+0xa/0xc0
>   RDI: 0x000000000000046d (stale register value)
> 
> In the observed crash the stale value was the process PID, causing a
> dereference within the unmapped NULL page.  However, an attacker able
> to control the register value -- for example by writing a BPF program
> that explicitly sets R2 before calling a KF_IMPLICIT_ARGS kfunc --
> could redirect the dereference to arbitrary kernel memory, turning
> this into an information disclosure.  The fix ensures the verifier
> either validates and injects the correct bpf_prog_aux pointer, or
> rejects the program outright -- no silent fallthrough that could
> be exploited.
> 
> Crash Stack Trace
> 
>   PID: 1133     TASK: ffff8881057d3900  CPU: 3    COMMAND: "test_progs"
>    #0 machine_kexec at ffffffff812f6e26
>    #1 __crash_kexec at ffffffff8145a788
>    #2 crash_kexec at ffffffff8145ac24
>    #3 oops_end at ffffffff812bb67c
>    #4 page_fault_oops at ffffffff813053a1
>    #5 exc_page_fault at ffffffff828e60a1
>    #6 asm_exc_page_fault at ffffffff810012a6
>       [exception RIP: bpf_prog_get_assoc_struct_ops+10]
>       RIP: ffffffff815c024a  RSP: ffffc90001b57e48  RFLAGS: 00010283
>       RAX: ffff8881057d3900  RBX: ffffc90001b57e68  RCX: ffff8881057d3900
>       RDX: 0000607d4d1768b8  RSI: 000000000000046d  RDI: 000000000000046d
>    #7 bpf_kfunc_multi_st_ops_test_1_assoc at ffffffffc0013a85 [bpf_testmod]
>    #8 bpf_trace_run2 at ffffffff814f8332
>    #9 __traceiter_sys_enter at ffffffff81415f45
>   #10 trace_syscall_enter at ffffffff81416735
>   #11 do_syscall_64 at ffffffff828e06a1
> 
> Fix
> 
> Split the combined is_kfunc_arg_ignore() || is_kfunc_arg_implicit()
> check in check_kfunc_args() so that an implicit argument reaching
> is_kfunc_arg_implicit() without being handled by a prior handler is
> rejected with -EFAULT, instead of silently skipped.  Existing implicit
>       args in bpf_fixup_kfunc_call() (obj_new, percpu_obj_new, obj_drop,
>       percpu_obj_drop, refcount_acquire, list_push, rbtree_add) are
>       explicitly allowed.
> 
> Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
> Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---

Please do not drop acks.
Also, wdyt about this:
https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/T/#m736b14f7e8b0a117bf0791ce79fc0896df8ca0f2
?

[...]
Re: [PATCH bpf v6 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
Posted by bot+bpf-ci@kernel.org 2 weeks, 4 days ago
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4..806c86388984 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12082,9 +12082,28 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
>  		continue;
>  	}
>
> -	if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> +	if (is_kfunc_arg_ignore(btf, &args[i]))
>  		continue;
>
> +	if (is_kfunc_arg_implicit(meta, i)) {
> +		/*
> +		 * This is an exhaustive list of the non-bpf_prog_aux
> +		 * implicit args kfuncs, which are handled
> +		 * post-verification by bpf_fixup_kfunc_call().
> +		 */
> +		if (is_bpf_obj_new_kfunc(meta->func_id) ||
> +		    is_bpf_percpu_obj_new_kfunc(meta->func_id) ||
> +		    is_bpf_obj_drop_kfunc(meta->func_id) ||
> +		    is_bpf_percpu_obj_drop_kfunc(meta->func_id) ||
> +		    is_bpf_refcount_acquire_kfunc(meta->func_id) ||
> +		    is_bpf_list_push_kfunc(meta->func_id) ||
> +		    is_bpf_rbtree_add_kfunc(meta->func_id))
> +			continue;
> +		verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
> +			reg_arg_name(env, argno));
> +		return -EFAULT;
> +	}
> +

Does this code compile? The call to reg_arg_name(env, argno) references
two symbols that don't exist at this commit. The function reg_arg_name()
is not defined in the tree: searching kernel/bpf/verifier.c finds the
identifier only once at this call site, and a tree-wide search yields no
definition. The variable argno is also undeclared: check_kfunc_args()
declares 'u32 regno = i + 1, ref_id, type_size;' but has no argno local.

Both trigger build failures (implicit declaration of function and
undeclared variable). The intended register is likely regno, matching the
pattern used elsewhere in this function:

    verbose(env, "R%d ...", regno);

Looking at the comment by Ihor Solodrai on Mon, 8 Jun 2026 17:54:36 -0700
(https://lore.kernel.org/bpf/eab4e479-2023-49b8-9915-1c8ef2356837@linux.dev/),
the list of allowed kfuncs is incomplete. It omits bpf_wq_set_callback,
bpf_stream_vprintk, bpf_stream_print_stack, bpf_task_work_schedule_signal,
and bpf_task_work_schedule_resume, all of which have KF_IMPLICIT_ARGS and
would trigger the new error path incorrectly.

Eduard Zingerman's response on Mon, 08 Jun 2026 23:20:36 -0700
(https://lore.kernel.org/bpf/0d63a8d68bf0778d6e931ed27892a5bcb151295d.camel@gmail.com/)
suggested an alternative fix: instead of enumerating kfuncs in the
verifier, check in bpf_fixup_kfunc_call() whether an implicit arg has the
KF_IMPLICIT_ARGS flag but wasn't filled by an earlier handler. This would
catch the BTF mismatch case without needing to maintain a list and without
breaking valid kfuncs.

The comment claims the list is exhaustive, but it's missing at least five
kfuncs. Should this use Eduard's suggested approach in bpf_fixup_kfunc_call()
instead?


---
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/27208267613
[PATCH bpf v6 2/2] selftests/bpf: strengthen bpf_kfunc_implicit_arg to verify aux injection
Posted by chenyuan_fl@163.com 2 weeks, 4 days ago
From: Yuan Chen <chenyuan@kylinos.cn>

Verify that the KF_IMPLICIT_ARGS injection path correctly passes
the bpf_prog_aux pointer by checking aux->name in
bpf_kfunc_implicit_arg() for the expected program name prefix.
If the verifier incorrectly skipped injection (as could happen
with pahole 1.30's BTF mismatch), the stale register would not
contain a valid aux pointer and the name check would fail.

This is a positive test exercised by the existing kfunc_implicit_args
selftest, which calls bpf_kfunc_implicit_arg(5) and expects a return
value of 5.

Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 tools/testing/selftests/bpf/test_kmods/bpf_testmod.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093c..624d57a5c79a 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1906,7 +1906,11 @@ int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, struct bpf_pro
 
 int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
 {
-	if (aux && a > 0)
+	/* Verify the kernel injected the correct bpf_prog_aux pointer
+	 * rather than leaving a stale register value. */
+	if (!aux || strncmp(aux->name, "test_kfunc", sizeof("test_kfunc") - 1))
+		return -EINVAL;
+	if (a > 0)
 		return a;
 	return -EINVAL;
 }
-- 
2.54.0
Re: [PATCH bpf v5 0/2] bpf: Fix kfunc implicit arg injection and add selftest
Posted by Alexei Starovoitov 2 weeks, 4 days ago
On Mon Jun 8, 2026 at 7:26 AM PDT, chenyuan_fl wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> v4: https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/
>
> v1 made btf_types_are_same() cross-BTF aware by comparing kind, size
> and name.  Alan Maguire pointed out that this would be too permissive
> since two different structs can share the same name and size.
>
> v2 used a name-based classifier (get_kfunc_arg_inject_type).
>
> v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit
> check in check_kfunc_args(), so that an implicit argument reaching
> is_kfunc_arg_implicit() without any prior handler is rejected with
> -EFAULT instead of silently skipped (Eduard Zingerman).  This prevents
> the silent fallthrough that occurred when module BTF was inconsistent
> with vmlinux (e.g. pahole 1.30 breaking distilled base dedup).
>
> v4: fix a build error in the selftest.
>
> v5 changes:
> - Patch 1: extend the kfunc allow-list in check_kfunc_args() to cover
>   all kfuncs with implicit args handled in bpf_fixup_kfunc_call():
>   obj_new, percpu_obj_new, obj_drop, percpu_obj_drop,
>   refcount_acquire, list_push, rbtree_add (Eduard Zingerman).
> - Patch 2: simplify the selftest by verifying aux->name in
>   bpf_kfunc_implicit_arg() instead of adding a separate struct_ops
>   test program with inline assembly (Eduard Zingerman).  This is
>   a single-file change exercised by the existing kfunc_implicit_args
>   selftest.

Pls address feedback from Eduard and respin with [PATCH bpf-next v6] subject.

pw-bot: cr