All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison
@ 2026-04-07  8:09 chenyuan_fl
  2026-04-07  8:58 ` Leon Hwang
                   ` (4 more replies)
  0 siblings, 5 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-04-07  8:09 UTC (permalink / raw)
  To: martin.lau, ast, daniel, andrii, eddyz87, memxor, song,
	yonghong.song, jolsa
  Cc: bpf, linux-kernel, Yuan Chen

From: Yuan Chen <chenyuan@kylinos.cn>

When comparing types from different BTF objects (e.g., module BTF vs
vmlinux BTF), the original btf_types_are_same() returns false because:
  - Type IDs are local to each BTF
  - Pointer comparison of btf_type_by_id results always fails

This prevents kfuncs with KF_IMPLICIT_ARGS flag from modules (like
bpf_kfunc_multi_st_ops_test_1_assoc) from properly recognizing implicit
arguments such as 'struct bpf_prog_aux *', causing the verifier to not
inject the aux pointer value during fixup.

Fix by comparing actual type content (kind, size, name) when BTFs are
different instead of comparing pointers.

Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 kernel/bpf/btf.c | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index a62d78581207..daad28ae32e5 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7432,15 +7432,39 @@ int btf_struct_access(struct bpf_verifier_log *log,
  * the same. Trivial ID check is not enough due to module BTFs, because we can
  * end up with two different module BTFs, but IDs point to the common type in
  * vmlinux BTF.
+ *
+ * When comparing types across different BTF objects (e.g., module BTF vs
+ * vmlinux BTF), we need to compare the actual type content (name, kind, size)
+ * since type IDs may differ between BTF objects even for the same type.
  */
 bool btf_types_are_same(const struct btf *btf1, u32 id1,
 			const struct btf *btf2, u32 id2)
 {
-	if (id1 != id2)
-		return false;
+	const struct btf_type *t1, *t2;
+
+	/* If same BTF object, ID comparison is sufficient */
 	if (btf1 == btf2)
-		return true;
-	return btf_type_by_id(btf1, id1) == btf_type_by_id(btf2, id2);
+		return id1 == id2;
+
+	/* Different BTF objects - compare actual type content.
+	 * Type IDs may differ between module BTF and vmlinux BTF,
+	 * so we need to check if the types are semantically identical.
+	 */
+	t1 = btf_type_by_id(btf1, id1);
+	t2 = btf_type_by_id(btf2, id2);
+	if (!t1 || !t2)
+		return false;
+
+	/* Must be same kind and have same name */
+	if (BTF_INFO_KIND(t1->info) != BTF_INFO_KIND(t2->info))
+		return false;
+	if (t1->size != t2->size)
+		return false;
+	if (strcmp(__btf_name_by_offset(btf1, t1->name_off),
+		   __btf_name_by_offset(btf2, t2->name_off)) != 0)
+		return false;
+
+	return true;
 }
 
 bool btf_struct_ids_match(struct bpf_verifier_log *log,
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison
  2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
@ 2026-04-07  8:58 ` Leon Hwang
  2026-04-07  9:01 ` bot+bpf-ci
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: Leon Hwang @ 2026-04-07  8:58 UTC (permalink / raw)
  To: chenyuan_fl, martin.lau, ast, daniel, andrii, eddyz87, memxor,
	song, yonghong.song, jolsa
  Cc: bpf, linux-kernel, Yuan Chen

pls also check sashiko's review:
https://sashiko.dev/#/patchset/20260407080900.551797-1-chenyuan_fl%40163.com.

Target tree should be specified: [PATCH bpf] bpf: ...

On 7/4/26 16:09, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
> 
> When comparing types from different BTF objects (e.g., module BTF vs
> vmlinux BTF), the original btf_types_are_same() returns false because:
>   - Type IDs are local to each BTF
>   - Pointer comparison of btf_type_by_id results always fails
> 
> This prevents kfuncs with KF_IMPLICIT_ARGS flag from modules (like
> bpf_kfunc_multi_st_ops_test_1_assoc) from properly recognizing implicit
> arguments such as 'struct bpf_prog_aux *', causing the verifier to not
> inject the aux pointer value during fixup.

Should add a selftest to verify the BUG and this fix.

Thanks,
Leon

> 
> Fix by comparing actual type content (kind, size, name) when BTFs are
> different instead of comparing pointers.
> 
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
[...]


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison
  2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
  2026-04-07  8:58 ` Leon Hwang
@ 2026-04-07  9:01 ` bot+bpf-ci
  2026-04-07 11:19 ` Alan Maguire
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: bot+bpf-ci @ 2026-04-07  9:01 UTC (permalink / raw)
  To: chenyuan_fl, martin.lau, ast, daniel, andrii, eddyz87, memxor,
	song, yonghong.song, jolsa
  Cc: bpf, linux-kernel, chenyuan, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 788 bytes --]

> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index a62d78581207..daad28ae32e5 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -7432,15 +7432,39 @@ int btf_struct_access(struct bpf_verifier_log *log,

[ ... ]

> +	return true;
>  }

This looks like a bug fix for btf_types_are_same() which was introduced
by commit 2ab3b3808eb1 ("bpf: Make BTF type match stricter for release
arguments"). Should this patch include a Fixes: tag?

Suggested tag:

Fixes: 2ab3b3808eb1 ("bpf: Make BTF type match stricter for release arguments")


---
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/24072076699

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison
  2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
  2026-04-07  8:58 ` Leon Hwang
  2026-04-07  9:01 ` bot+bpf-ci
@ 2026-04-07 11:19 ` Alan Maguire
  2026-05-15 18:27 ` Ihor Solodrai
  2026-06-01  6:46 ` [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  4 siblings, 0 replies; 35+ messages in thread
From: Alan Maguire @ 2026-04-07 11:19 UTC (permalink / raw)
  To: chenyuan_fl, martin.lau, ast, daniel, andrii, eddyz87, memxor,
	song, yonghong.song, jolsa
  Cc: bpf, linux-kernel, Yuan Chen

On 07/04/2026 09:09, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
> 
> When comparing types from different BTF objects (e.g., module BTF vs
> vmlinux BTF), the original btf_types_are_same() returns false because:
>   - Type IDs are local to each BTF
>   - Pointer comparison of btf_type_by_id results always fails
> 
> This prevents kfuncs with KF_IMPLICIT_ARGS flag from modules (like
> bpf_kfunc_multi_st_ops_test_1_assoc) from properly recognizing implicit
> arguments such as 'struct bpf_prog_aux *', causing the verifier to not
> inject the aux pointer value during fixup.
> 
> Fix by comparing actual type content (kind, size, name) when BTFs are
> different instead of comparing pointers.
> 
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
>  kernel/bpf/btf.c | 32 ++++++++++++++++++++++++++++----
>  1 file changed, 28 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index a62d78581207..daad28ae32e5 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -7432,15 +7432,39 @@ int btf_struct_access(struct bpf_verifier_log *log,
>   * the same. Trivial ID check is not enough due to module BTFs, because we can
>   * end up with two different module BTFs, but IDs point to the common type in
>   * vmlinux BTF.
> + *
> + * When comparing types across different BTF objects (e.g., module BTF vs
> + * vmlinux BTF), we need to compare the actual type content (name, kind, size)
> + * since type IDs may differ between BTF objects even for the same type.
>   */
>  bool btf_types_are_same(const struct btf *btf1, u32 id1,
>  			const struct btf *btf2, u32 id2)
>  {
> -	if (id1 != id2)
> -		return false;
> +	const struct btf_type *t1, *t2;it wo
> +
> +	/* If same BTF object, ID comparison is sufficient */
>  	if (btf1 == btf2)
> -		return true;
> -	return btf_type_by_id(btf1, id1) == btf_type_by_id(btf2, id2);
> +		return id1 == id2;
> +
> +	/* Different BTF objects - compare actual type content.
> +	 * Type IDs may differ between module BTF and vmlinux BTF,
> +	 * so we need to check if the types are semantically identical.
> +	 */
> +	t1 = btf_type_by_id(btf1, id1);
> +	t2 = btf_type_by_id(btf2, id2);
> +	if (!t1 || !t2)
> +		return false;
> +
> +	/* Must be same kind and have same name */
> +	if (BTF_INFO_KIND(t1->info) != BTF_INFO_KIND(t2->info))
> +		return false;
> +	if (t1->size != t2->size)
> +		return false;
> +	if (strcmp(__btf_name_by_offset(btf1, t1->name_off),
> +		   __btf_name_by_offset(btf2, t2->name_off)) != 0)
> +		return false;
> +
> +	return true;
>  }
>  
>  bool btf_struct_ids_match(struct bpf_verifier_log *log,

This feels insufficient as a type equality check; for pointer types
as cited in the commit message, it will only really work if the
pointed-to type ids (t1->size, t2->size above) are identical ids. 
That may work narrowly in the specific case you're dealing with but I'd suggest
looking at how libbpf dedup handles this and figure out a depth-limited
recursive equivalence check that winds up comparing structs/base types rather than
reference types.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison
  2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
                   ` (2 preceding siblings ...)
  2026-04-07 11:19 ` Alan Maguire
@ 2026-05-15 18:27 ` Ihor Solodrai
  2026-06-01  6:46 ` [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  4 siblings, 0 replies; 35+ messages in thread
From: Ihor Solodrai @ 2026-05-15 18:27 UTC (permalink / raw)
  To: chenyuan_fl, martin.lau, ast, daniel, andrii, eddyz87, memxor,
	song, yonghong.song, jolsa
  Cc: bpf, linux-kernel, Yuan Chen

On 4/7/26 1:09 AM, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
> 
> When comparing types from different BTF objects (e.g., module BTF vs
> vmlinux BTF), the original btf_types_are_same() returns false because:
>   - Type IDs are local to each BTF
>   - Pointer comparison of btf_type_by_id results always fails
> 
> This prevents kfuncs with KF_IMPLICIT_ARGS flag from modules (like
> bpf_kfunc_multi_st_ops_test_1_assoc) from properly recognizing implicit
> arguments such as 'struct bpf_prog_aux *', causing the verifier to not
> inject the aux pointer value during fixup.

Hi Yuan,

Could you please provide an example of what is failing? For example, a
selftest, verifier log, or at least the (btf, type_id) pair?

I am guessing the root cause might be that the distill_base deletes the
types used by implicit args (such as bpf_prog_aux), causing .BTF.base to
be incomplete, and module BTF duplicating kernel types. But I'm speculating
here due to lack of an example.

Module BTF is supposed to reference base BTF types through the
distilled base. If struct bpf_prog_aux ends up as a separate
module-local type instead of resolving to the vmlinux BTF type, we
should fix the distill_base/relocation handling so the module BTF
points at the canonical vmlinux type ID. Then the existing strict
comparison will work unchanged.

To Alan's point, changing btf_types_are_same() to compare only kind,
size and name makes it too permissive. Two different structs can have
the same name and size, but different members or semantics.

I think we should identify the root cause and fix it.
This patch is a no go IMO.

Thanks.


> 
> Fix by comparing actual type content (kind, size, name) when BTFs are
> different instead of comparing pointers.
> 
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
>  kernel/bpf/btf.c | 32 ++++++++++++++++++++++++++++----
>  1 file changed, 28 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index a62d78581207..daad28ae32e5 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -7432,15 +7432,39 @@ int btf_struct_access(struct bpf_verifier_log *log,
>   * the same. Trivial ID check is not enough due to module BTFs, because we can
>   * end up with two different module BTFs, but IDs point to the common type in
>   * vmlinux BTF.
> + *
> + * When comparing types across different BTF objects (e.g., module BTF vs
> + * vmlinux BTF), we need to compare the actual type content (name, kind, size)
> + * since type IDs may differ between BTF objects even for the same type.
>   */
>  bool btf_types_are_same(const struct btf *btf1, u32 id1,
>  			const struct btf *btf2, u32 id2)
>  {
> -	if (id1 != id2)
> -		return false;
> +	const struct btf_type *t1, *t2;
> +
> +	/* If same BTF object, ID comparison is sufficient */
>  	if (btf1 == btf2)
> -		return true;
> -	return btf_type_by_id(btf1, id1) == btf_type_by_id(btf2, id2);
> +		return id1 == id2;
> +
> +	/* Different BTF objects - compare actual type content.
> +	 * Type IDs may differ between module BTF and vmlinux BTF,
> +	 * so we need to check if the types are semantically identical.
> +	 */
> +	t1 = btf_type_by_id(btf1, id1);
> +	t2 = btf_type_by_id(btf2, id2);
> +	if (!t1 || !t2)
> +		return false;
> +
> +	/* Must be same kind and have same name */
> +	if (BTF_INFO_KIND(t1->info) != BTF_INFO_KIND(t2->info))
> +		return false;
> +	if (t1->size != t2->size)
> +		return false;
> +	if (strcmp(__btf_name_by_offset(btf1, t1->name_off),
> +		   __btf_name_by_offset(btf2, t2->name_off)) != 0)
> +		return false;
> +
> +	return true;
>  }
>  
>  bool btf_struct_ids_match(struct bpf_verifier_log *log,


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest
  2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
                   ` (3 preceding siblings ...)
  2026-05-15 18:27 ` Ihor Solodrai
@ 2026-06-01  6:46 ` chenyuan_fl
  2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-01  6:46   ` [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register chenyuan_fl
  4 siblings, 2 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-01  6:46 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song

From: Yuan Chen <chenyuan@kylinos.cn>

v1: https://lore.kernel.org/bpf/20260407080900.551797-1-chenyuan_fl@163.com/

v1 took the approach of making btf_types_are_same() cross-BTF aware by
comparing kind, size and name.  This was correctly rejected by reviewers
(Alan Maguire, sashiko) because:
  - Two different structs can have the same name and size but different
    members, making this too permissive
  - The real root cause is that pahole 1.30 generated BTF that broke
    the distilled base deduplication for modules, causing the module to
    retain its own copy of struct bpf_prog_aux instead of resolving to
    the vmlinux definition

v2 takes a different approach: instead of weakening btf_types_are_same(),
it introduces a two-layer detection in the verifier's check_kfunc_args()
that cleanly handles the mismatch case.  A selftest is added (patch 2) as
requested by Leon and Alan.

Yuan Chen (2):
  bpf: Fix kfunc implicit arg inject type detection to prevent invalid
    pointer deref
  selftests/bpf: Add regression test for kfunc implicit arg injection
    with stale register

 kernel/bpf/verifier.c                         | 48 ++++++++++++++++++-
 .../bpf/prog_tests/test_struct_ops_assoc.c    |  5 ++
 .../selftests/bpf/progs/struct_ops_assoc.c    | 40 ++++++++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    |  9 ++++
 .../bpf/test_kmods/bpf_testmod_kfunc.h        |  1 +
 5 files changed, 101 insertions(+), 2 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-01  6:46 ` [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
@ 2026-06-01  6:46   ` chenyuan_fl
  2026-06-01  7:42     ` bot+bpf-ci
                       ` (2 more replies)
  2026-06-01  6:46   ` [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register chenyuan_fl
  1 sibling, 3 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-01  6:46 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song

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
---

Introduce a two-layer argument-injection detection:

  1. get_kfunc_arg_inject_type() -- lightweight name-based classification
     of injectable types (currently only KF_INJECT_ARG_PROG_AUX). This
     ensures we recognize injection candidates regardless of BTF type IDs.

  2. is_kfunc_arg_prog_aux() -- strict type validation within the inject
     case; if validation fails the program is rejected with -EINVAL instead
     of silently bypassing injection setup.

This design ensures that BTF inconsistencies result in a clean verification
failure instead of a crash or a potential information disclosure, and the
approach is extensible for future injection types.

Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 kernel/bpf/verifier.c | 48 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8dd79b735a69..928b6c42a4bf 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10857,6 +10857,39 @@ static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param
 	return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_PROG_AUX_ID);
 }
 
+/*
+ * Injectable argument types are implicit kfunc arguments whose value is
+ * injected by the kernel at call time rather than received from the BPF
+ * program.  Use name-based matching for initial detection to avoid false
+ * negatives when a module's BTF references the type via a different BTF ID
+ * than vmlinux's.  Actual type compatibility is still validated by the
+ * caller with btf_types_are_same().
+ */
+enum kfunc_inject_arg_type {
+	KF_INJECT_ARG_NONE = 0,
+	KF_INJECT_ARG_PROG_AUX,
+};
+
+static enum kfunc_inject_arg_type get_kfunc_arg_inject_type(
+	const struct btf *btf, const struct btf_param *arg)
+{
+	const struct btf_type *t;
+	u32 res_id;
+
+	t = btf_type_skip_modifiers(btf, arg->type, NULL);
+	if (!t || !btf_type_is_ptr(t))
+		return KF_INJECT_ARG_NONE;
+
+	t = btf_type_skip_modifiers(btf, t->type, &res_id);
+	if (!t)
+		return KF_INJECT_ARG_NONE;
+
+	if (strcmp(btf_type_name(btf, res_id), "bpf_prog_aux") == 0)
+		return KF_INJECT_ARG_PROG_AUX;
+
+	return KF_INJECT_ARG_NONE;
+}
+
 /*
  * A kfunc with KF_IMPLICIT_ARGS has two prototypes in BTF:
  *   - the _impl prototype with full arg list (meta->func_proto)
@@ -11899,8 +11932,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 		u32 ref_id, type_size;
 		bool is_ret_buf_sz = false;
 		int kf_arg_type;
-
-		if (is_kfunc_arg_prog_aux(btf, &args[i])) {
+		enum kfunc_inject_arg_type inject_type;
+
+		inject_type = get_kfunc_arg_inject_type(btf, &args[i]);
+		switch (inject_type) {
+		case KF_INJECT_ARG_PROG_AUX:
+			/* Validate the arg type against vmlinux's definition */
+			if (!is_kfunc_arg_prog_aux(btf, &args[i])) {
+				verbose(env, "arg#%d implicit argument type mismatch, "
+					"expected struct bpf_prog_aux *\n", i);
+				return -EINVAL;
+			}
 			/* Reject repeated use bpf_prog_aux */
 			if (meta->arg_prog) {
 				verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
@@ -11914,6 +11956,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 			meta->arg_prog = true;
 			cur_aux(env)->arg_prog = regno;
 			continue;
+		default:
+			break;
 		}
 
 		if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register
  2026-06-01  6:46 ` [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-01  6:46   ` chenyuan_fl
  2026-06-01  7:08     ` sashiko-bot
  2026-06-01 17:17     ` Yonghong Song
  1 sibling, 2 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-01  6:46 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song

From: Yuan Chen <chenyuan@kylinos.cn>

The preceding patch fixes a bug where the BPF verifier failed to detect
an implicit struct bpf_prog_aux * argument in a module kfunc, causing
the kernel to dereference a stale register value as a pointer and crash.

Add a selftest that deliberately contaminates BPF R2 (the register slot
for the 2nd kfunc argument, which maps to the implicit bpf_prog_aux *)
with a known magic value 0xDEAD via inline assembly, then calls a
KF_IMPLICIT_ARGS kfunc that validates whether injection occurred:

  - If the kernel correctly injects env->prog->aux into R2, the kfunc
    receives a real bpf_prog_aux pointer (≠ 0xDEAD) and returns the
    caller-supplied marker value.

  - If injection is skipped (the original bug), the kfunc receives the
    stale 0xDEAD value and returns -EINVAL, which the BPF program
    detects as a test failure.

The magic value 0xDEAD is chosen with bit 31 clear to avoid BPF ALU64
sign-extension when used as a 32-bit immediate, ensuring the comparison
in the kfunc matches the actual register value.  The kfunc is associated
with a struct_ops map to exercise the exact call path (struct_ops →
kfunc with KF_IMPLICIT_ARGS → bpf_prog_aux dereference) that triggered
the original crash.

Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 .../bpf/prog_tests/test_struct_ops_assoc.c    |  5 +++
 .../selftests/bpf/progs/struct_ops_assoc.c    | 40 +++++++++++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    |  9 +++++
 .../bpf/test_kmods/bpf_testmod_kfunc.h        |  1 +
 4 files changed, 55 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
index 461ded722351..123bd2c7a292 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
@@ -35,6 +35,10 @@ static void test_st_ops_assoc(void)
 					    skel->maps.st_ops_map_b, NULL);
 	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_b, st_ops_map_b)");
 
+	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_test_aux_inject,
+					    skel->maps.st_ops_map_a, NULL);
+	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_test_aux_inject, st_ops_map_a)");
+
 	/* sys_enter_prog_a already associated with map_a */
 	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
 					    skel->maps.st_ops_map_b, NULL);
@@ -52,6 +56,7 @@ static void test_st_ops_assoc(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 	/* run syscall_prog that calls .test_1 and checks return */
 	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_a), NULL);
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
index 68842e3f936b..dd322d43ff5e 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
@@ -103,3 +103,43 @@ SEC(".struct_ops.link")
 struct bpf_testmod_multi_st_ops st_ops_map_b = {
 	.test_1 = (void *)test_1_b,
 };
+
+/* Test for aux injection with stale register contamination.
+ *
+ * This test reproduces the scenario where the BPF verifier fails to
+ * inject the implicit bpf_prog_aux pointer for kfuncs with
+ * KF_IMPLICIT_ARGS.  The program uses inline assembly to explicitly
+ * set R2 (the register for the 2nd kfunc argument, which maps to
+ * the implicit bpf_prog_aux *) to a known magic value (0xDEAD,
+ * chosen with bit 31 clear to avoid BPF ALU64 sign-extension):
+ *
+ *   asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+ *
+ * Then bpf_kfunc_aux_inject_stale() is called.  The kernel verifier
+ * should inject the real bpf_prog_aux into R2, overriding the magic
+ * value.  The kfunc compares aux against 0xDEAD:
+ *
+ *   - aux == 0xDEAD  → kernel failed to inject aux → test fails
+ *   - aux != 0xDEAD  → kernel correctly injected aux → test passes
+ */
+int test_err_inject;
+
+SEC("tp_btf/sys_enter")
+int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
+{
+	struct task_struct *task;
+	int marker = 0x5A5A;
+	int ret;
+
+	task = bpf_get_current_task_btf();
+	if (!test_pid || task->pid != test_pid)
+		return 0;
+
+	asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+
+	ret = bpf_kfunc_aux_inject_stale(marker);
+	if (ret != marker)
+		test_err_inject++;
+
+	return 0;
+}
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 0be918fe3021..da95a6de3bbf 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1316,6 +1316,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
 __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
+__bpf_kfunc int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux);
 
 /* hook targets */
 noinline void bpf_testmod_test_hardirq_fn(void) { barrier(); }
@@ -1399,6 +1400,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_assoc, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
+BTF_ID_FLAGS(func, bpf_kfunc_aux_inject_stale, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_trigger_ctx_check)
 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
 
@@ -1916,6 +1918,13 @@ int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux)
 	return bpf_kfunc_implicit_arg_legacy(a, b, aux);
 }
 
+int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux)
+{
+	if ((unsigned long)aux == 0xDEAD)
+		return -EINVAL;
+	return marker;
+}
+
 static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
 {
 	struct bpf_testmod_multi_st_ops *st_ops =
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 2edc36b66de9..c18791e96b21 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -192,6 +192,7 @@ int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
 #ifndef __KERNEL__
 extern int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __weak __ksym;
 extern int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args) __weak __ksym;
+extern int bpf_kfunc_aux_inject_stale(int marker) __weak __ksym;
 #endif
 
 struct prog_test_member *bpf_kfunc_get_default_trusted_ptr_test(void) __ksym;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register
  2026-06-01  6:46   ` [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register chenyuan_fl
@ 2026-06-01  7:08     ` sashiko-bot
  2026-06-01 17:17     ` Yonghong Song
  1 sibling, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-06-01  7:08 UTC (permalink / raw)
  To: chenyuan_fl; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The newly added multi-line comment documenting the aux injection test violates the BPF subsystem comment style guidelines by placing text on the same line as the opening `/*`.
--

commit 1ef24f7e0744d3c963c7f542a86739e51e7b0b41
Author: Yuan Chen <chenyuan@kylinos.cn>

selftests/bpf: Add regression test for kfunc implicit arg injection with stale register

This commit adds a selftest to verify that the BPF verifier correctly injects
the implicit bpf_prog_aux pointer for KF_IMPLICIT_ARGS kfuncs. It explicitly
contaminates the register mapping to the second argument with a magic value
to ensure the kernel overwrites it with the real pointer.

> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b9..dd322d43ff5e4 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")
>  struct bpf_testmod_multi_st_ops st_ops_map_b = {
>  	.test_1 = (void *)test_1_b,
>  };
> +
> +/* Test for aux injection with stale register contamination.
> + *
> + * This test reproduces the scenario where the BPF verifier fails to

[Severity: Low]
This isn't a bug, but does this multi-line comment follow the preferred BPF
subsystem comment style? The guidelines require multi-line comments to have
the opening /* on its own line, rather than having text on the same line as
the opening marker.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601064611.807637-1-chenyuan_fl@163.com?part=2

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-01  7:42     ` bot+bpf-ci
  2026-06-01 19:32       ` Eduard Zingerman
  2026-06-01 17:12     ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref Yonghong Song
  2026-06-01 21:36     ` Eduard Zingerman
  2 siblings, 1 reply; 35+ messages in thread
From: bot+bpf-ci @ 2026-06-01  7:42 UTC (permalink / raw)
  To: chenyuan_fl, chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song, ast, andrii, daniel,
	martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2875 bytes --]

> bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
>
> 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.

This looks like a fix for a real bug, but it does not carry a Fixes:
tag.  Should it point at the change that introduced the implicit
prog_aux argument handling, is_kfunc_arg_prog_aux() and KF_ARG_PROG_AUX_ID?

That code appears to come from "bpf: Support __magic prog_aux arguments
for kfuncs" (Ihor Solodrai, lore message-id
20251029190113.3323406-5-ihor.solodrai@linux.dev).

The merged SHA could not be resolved from this repository, so the tag
would need to be filled in by hand, for example:

  Fixes: <sha> ("bpf: Support __magic prog_aux arguments for kfuncs")


---
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/26740141551

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-01  7:42     ` bot+bpf-ci
@ 2026-06-01 17:12     ` Yonghong Song
  2026-06-01 21:36     ` Eduard Zingerman
  2 siblings, 0 replies; 35+ messages in thread
From: Yonghong Song @ 2026-06-01 17:12 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song



On 5/31/26 11:46 PM, 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
> ---
>
> Introduce a two-layer argument-injection detection:
>
>    1. get_kfunc_arg_inject_type() -- lightweight name-based classification
>       of injectable types (currently only KF_INJECT_ARG_PROG_AUX). This
>       ensures we recognize injection candidates regardless of BTF type IDs.
>
>    2. is_kfunc_arg_prog_aux() -- strict type validation within the inject
>       case; if validation fails the program is rejected with -EINVAL instead
>       of silently bypassing injection setup.
>
> This design ensures that BTF inconsistencies result in a clean verification
> failure instead of a crash or a potential information disclosure, and the
> approach is extensible for future injection types.
>
> Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
>   kernel/bpf/verifier.c | 48 +++++++++++++++++++++++++++++++++++++++++--
>   1 file changed, 46 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8dd79b735a69..928b6c42a4bf 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -10857,6 +10857,39 @@ static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param
>   	return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_PROG_AUX_ID);
>   }
>   
> +/*
> + * Injectable argument types are implicit kfunc arguments whose value is
> + * injected by the kernel at call time rather than received from the BPF
> + * program.  Use name-based matching for initial detection to avoid false
> + * negatives when a module's BTF references the type via a different BTF ID
> + * than vmlinux's.  Actual type compatibility is still validated by the
> + * caller with btf_types_are_same().
> + */
> +enum kfunc_inject_arg_type {
> +	KF_INJECT_ARG_NONE = 0,
> +	KF_INJECT_ARG_PROG_AUX,
> +};
> +
> +static enum kfunc_inject_arg_type get_kfunc_arg_inject_type(
> +	const struct btf *btf, const struct btf_param *arg)
> +{
> +	const struct btf_type *t;
> +	u32 res_id;
> +
> +	t = btf_type_skip_modifiers(btf, arg->type, NULL);
> +	if (!t || !btf_type_is_ptr(t))
> +		return KF_INJECT_ARG_NONE;
> +
> +	t = btf_type_skip_modifiers(btf, t->type, &res_id);
> +	if (!t)
> +		return KF_INJECT_ARG_NONE;
> +
> +	if (strcmp(btf_type_name(btf, res_id), "bpf_prog_aux") == 0)
> +		return KF_INJECT_ARG_PROG_AUX;
> +
> +	return KF_INJECT_ARG_NONE;
> +}
> +
>   /*
>    * A kfunc with KF_IMPLICIT_ARGS has two prototypes in BTF:
>    *   - the _impl prototype with full arg list (meta->func_proto)
> @@ -11899,8 +11932,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
>   		u32 ref_id, type_size;
>   		bool is_ret_buf_sz = false;
>   		int kf_arg_type;
> -
> -		if (is_kfunc_arg_prog_aux(btf, &args[i])) {
> +		enum kfunc_inject_arg_type inject_type;
> +
> +		inject_type = get_kfunc_arg_inject_type(btf, &args[i]);
> +		switch (inject_type) {
> +		case KF_INJECT_ARG_PROG_AUX:
> +			/* Validate the arg type against vmlinux's definition */
> +			if (!is_kfunc_arg_prog_aux(btf, &args[i])) {
> +				verbose(env, "arg#%d implicit argument type mismatch, "
> +					"expected struct bpf_prog_aux *\n", i);

Change the above verbose(env, ...) to
verbose(env, "%s implicit argument type mismatch, expected struct bpf_prog_aux *\n",
	reg_arg_name(env, argno));

Please do not break strings in two lines.

> +				return -EINVAL;
> +			}
>   			/* Reject repeated use bpf_prog_aux */
>   			if (meta->arg_prog) {
>   				verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
> @@ -11914,6 +11956,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
>   			meta->arg_prog = true;
>   			cur_aux(env)->arg_prog = regno;
>   			continue;
> +		default:
> +			break;
>   		}
>   
>   		if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))

I think we can do some checking in is_kfunc_arg_implicit(meta, i) to validate
the result of is_kfunc_arg_prog_aux(). This will make code simpler.

Also, please add some comments in the code to explain the reason of this code is due to pahole 1.30.
So later on at some point, this code can be removed.



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register
  2026-06-01  6:46   ` [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register chenyuan_fl
  2026-06-01  7:08     ` sashiko-bot
@ 2026-06-01 17:17     ` Yonghong Song
  1 sibling, 0 replies; 35+ messages in thread
From: Yonghong Song @ 2026-06-01 17:17 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, eddyz87, jolsa, linux-kernel,
	martin.lau, memxor, song



On 5/31/26 11:46 PM, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> The preceding patch fixes a bug where the BPF verifier failed to detect
> an implicit struct bpf_prog_aux * argument in a module kfunc, causing
> the kernel to dereference a stale register value as a pointer and crash.
>
> Add a selftest that deliberately contaminates BPF R2 (the register slot
> for the 2nd kfunc argument, which maps to the implicit bpf_prog_aux *)
> with a known magic value 0xDEAD via inline assembly, then calls a
> KF_IMPLICIT_ARGS kfunc that validates whether injection occurred:
>
>    - If the kernel correctly injects env->prog->aux into R2, the kfunc
>      receives a real bpf_prog_aux pointer (≠ 0xDEAD) and returns the
>      caller-supplied marker value.
>
>    - If injection is skipped (the original bug), the kfunc receives the
>      stale 0xDEAD value and returns -EINVAL, which the BPF program
>      detects as a test failure.
>
> The magic value 0xDEAD is chosen with bit 31 clear to avoid BPF ALU64
> sign-extension when used as a 32-bit immediate, ensuring the comparison
> in the kfunc matches the actual register value.  The kfunc is associated
> with a struct_ops map to exercise the exact call path (struct_ops →
> kfunc with KF_IMPLICIT_ARGS → bpf_prog_aux dereference) that triggered
> the original crash.

Not sure whether this patch makes sense or not. The CI has pahole 1.31
so the test will always succeed.

>
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
>   .../bpf/prog_tests/test_struct_ops_assoc.c    |  5 +++
>   .../selftests/bpf/progs/struct_ops_assoc.c    | 40 +++++++++++++++++++
>   .../selftests/bpf/test_kmods/bpf_testmod.c    |  9 +++++
>   .../bpf/test_kmods/bpf_testmod_kfunc.h        |  1 +
>   4 files changed, 55 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> index 461ded722351..123bd2c7a292 100644
> --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> @@ -35,6 +35,10 @@ static void test_st_ops_assoc(void)
>   					    skel->maps.st_ops_map_b, NULL);
>   	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_b, st_ops_map_b)");
>   
> +	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_test_aux_inject,
> +					    skel->maps.st_ops_map_a, NULL);
> +	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_test_aux_inject, st_ops_map_a)");
> +
>   	/* sys_enter_prog_a already associated with map_a */
>   	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
>   					    skel->maps.st_ops_map_b, NULL);
> @@ -52,6 +56,7 @@ static void test_st_ops_assoc(void)
>   
>   	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
>   	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
> +	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
>   
>   	/* run syscall_prog that calls .test_1 and checks return */
>   	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_a), NULL);
> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b..dd322d43ff5e 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")
>   struct bpf_testmod_multi_st_ops st_ops_map_b = {
>   	.test_1 = (void *)test_1_b,
>   };
> +
> +/* Test for aux injection with stale register contamination.
> + *
> + * This test reproduces the scenario where the BPF verifier fails to
> + * inject the implicit bpf_prog_aux pointer for kfuncs with
> + * KF_IMPLICIT_ARGS.  The program uses inline assembly to explicitly
> + * set R2 (the register for the 2nd kfunc argument, which maps to
> + * the implicit bpf_prog_aux *) to a known magic value (0xDEAD,
> + * chosen with bit 31 clear to avoid BPF ALU64 sign-extension):
> + *
> + *   asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
> + *
> + * Then bpf_kfunc_aux_inject_stale() is called.  The kernel verifier
> + * should inject the real bpf_prog_aux into R2, overriding the magic
> + * value.  The kfunc compares aux against 0xDEAD:
> + *
> + *   - aux == 0xDEAD  → kernel failed to inject aux → test fails
> + *   - aux != 0xDEAD  → kernel correctly injected aux → test passes
> + */
> +int test_err_inject;
> +
> +SEC("tp_btf/sys_enter")
> +int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
> +{
> +	struct task_struct *task;
> +	int marker = 0x5A5A;
> +	int ret;
> +
> +	task = bpf_get_current_task_btf();
> +	if (!test_pid || task->pid != test_pid)
> +		return 0;
> +
> +	asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
> +
> +	ret = bpf_kfunc_aux_inject_stale(marker);
> +	if (ret != marker)
> +		test_err_inject++;
> +
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> index 0be918fe3021..da95a6de3bbf 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> @@ -1316,6 +1316,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
>   __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
>   __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
>   __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
> +__bpf_kfunc int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux);
>   
>   /* hook targets */
>   noinline void bpf_testmod_test_hardirq_fn(void) { barrier(); }
> @@ -1399,6 +1400,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_assoc, KF_IMPLICIT_ARGS)
>   BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
>   BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
>   BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
> +BTF_ID_FLAGS(func, bpf_kfunc_aux_inject_stale, KF_IMPLICIT_ARGS)
>   BTF_ID_FLAGS(func, bpf_kfunc_trigger_ctx_check)
>   BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
>   
> @@ -1916,6 +1918,13 @@ int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux)
>   	return bpf_kfunc_implicit_arg_legacy(a, b, aux);
>   }
>   
> +int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux)
> +{
> +	if ((unsigned long)aux == 0xDEAD)
> +		return -EINVAL;
> +	return marker;
> +}
> +
>   static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
>   {
>   	struct bpf_testmod_multi_st_ops *st_ops =
> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
> index 2edc36b66de9..c18791e96b21 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
> @@ -192,6 +192,7 @@ int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
>   #ifndef __KERNEL__
>   extern int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __weak __ksym;
>   extern int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args) __weak __ksym;
> +extern int bpf_kfunc_aux_inject_stale(int marker) __weak __ksym;
>   #endif
>   
>   struct prog_test_member *bpf_kfunc_get_default_trusted_ptr_test(void) __ksym;


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-01  7:42     ` bot+bpf-ci
@ 2026-06-01 19:32       ` Eduard Zingerman
  2026-06-02  8:58         ` [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  2026-06-02  9:38         ` [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  0 siblings, 2 replies; 35+ messages in thread
From: Eduard Zingerman @ 2026-06-01 19:32 UTC (permalink / raw)
  To: bot+bpf-ci, chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song, martin.lau, clm,
	ihor.solodrai

On Mon, 2026-06-01 at 07:42 +0000, bot+bpf-ci@kernel.org wrote:
> > bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
> > 
> > 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.
> 
> This looks like a fix for a real bug, but it does not carry a Fixes:
> tag.  Should it point at the change that introduced the implicit
> prog_aux argument handling, is_kfunc_arg_prog_aux() and KF_ARG_PROG_AUX_ID?
> 
> That code appears to come from "bpf: Support __magic prog_aux arguments
> for kfuncs" (Ihor Solodrai, lore message-id
> 20251029190113.3323406-5-ihor.solodrai@linux.dev).
> 
> The merged SHA could not be resolved from this repository, so the tag
> would need to be filled in by hand, for example:
> 
>   Fixes: <sha> ("bpf: Support __magic prog_aux arguments for kfuncs")
> 
> 
> ---
> 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/26740141551

The robot (and b4 utility as well) is confused by the following
formatting quirk:

> ...
> Fix
> ---
> ...

The triple dash marks the start of the diff section of the patch.
Hence, when I apply this series the commit message is truncated after
this point.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-01  7:42     ` bot+bpf-ci
  2026-06-01 17:12     ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref Yonghong Song
@ 2026-06-01 21:36     ` Eduard Zingerman
  2 siblings, 0 replies; 35+ messages in thread
From: Eduard Zingerman @ 2026-06-01 21:36 UTC (permalink / raw)
  To: chenyuan_fl
  Cc: andrii, ast, bpf, chenyuan, daniel, jolsa, linux-kernel,
	martin.lau, memxor, song, yonghong.song

On Mon, 2026-06-01 at 14:46 +0800, chenyuan_fl@163.com wrote:

[...]

> @@ -11899,8 +11932,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
>  		u32 ref_id, type_size;
>  		bool is_ret_buf_sz = false;
>  		int kf_arg_type;
> -
> -		if (is_kfunc_arg_prog_aux(btf, &args[i])) {


Thank you for figuring out this corner case.
I don't think I like matching the type by name here, though.
Wdyt about a somewhat more generic solution described below?
bpf_fixup_kfunc_call() lists all the cases when verifier injects an
implicit argument:
- functions with KF_IMPLICIT_ARGS flag and a parameter of type bpf_prog_aux.
- functions with KF_IMPLICIT_ARGS allowed by is_bpf_list_push_kfunc() /
  is_bpf_rbtree_add_kfunc().
  
Meaning that current check_kfunc_arg() code can be changed as follows:

       if (is_kfunc_arg_prog_aux(btf, &args[i])) {
               ... unmodified ...
               continue;
       }
       if (is_bpf_list_push_kfunc(...) || is_bpf_rbtree_add_kfunc(...)) {
               ...
               continue;
       }
       if (is_kfunc_arg_implicit(meta, i)) {
               ... report error ...
               return -EFAULT;
       }

Thus catching all the cases.

> +		enum kfunc_inject_arg_type inject_type;
> +
> +		inject_type = get_kfunc_arg_inject_type(btf, &args[i]);
> +		switch (inject_type) {
> +		case KF_INJECT_ARG_PROG_AUX:
> +			/* Validate the arg type against vmlinux's definition */
> +			if (!is_kfunc_arg_prog_aux(btf, &args[i])) {
> +				verbose(env, "arg#%d implicit argument type mismatch, "
> +					"expected struct bpf_prog_aux *\n", i);
> +				return -EINVAL;
> +			}
>  			/* Reject repeated use bpf_prog_aux */
>  			if (meta->arg_prog) {
>  				verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");

[...]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest
  2026-06-01 19:32       ` Eduard Zingerman
@ 2026-06-02  8:58         ` chenyuan_fl
  2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  8:58           ` [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  2026-06-02  9:38         ` [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  1 sibling, 2 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  8:58 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

From: Yuan Chen <chenyuan@kylinos.cn>

v1: https://lore.kernel.org/bpf/20260407080900.551797-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 (this revision) 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).

Patch 2 adds a positive regression test for the injection path.
Yonghong Song pointed out that with pahole 1.31 available in CI the
test will always pass, so it cannot reproduce the original BTF mismatch
scenario.  The test still serves as a useful regression guard for the
injection mechanism itself, however, and would catch any accidental
breakage in the future.

Thanks to everyone who reviewed and provided feedback on this series.

Yuan Chen (2):
  bpf: Fix kfunc implicit arg inject type detection to prevent invalid
    pointer deref
  selftests/bpf: Add regression test for kfunc implicit arg injection

 kernel/bpf/verifier.c                              | 15 +++++++-
 .../bpf/prog_tests/test_struct_ops_assoc.c         |  7 ++++
 .../testing/selftests/bpf/progs/struct_ops_assoc.c | 40 ++++++++++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  9 +++++
 .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |  1 +
 5 files changed, 71 insertions(+), 1 deletion(-)

-- 
2.47.2


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  8:58         ` [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
@ 2026-06-02  8:58           ` chenyuan_fl
  2026-06-02  9:23             ` sashiko-bot
                               ` (2 more replies)
  2026-06-02  8:58           ` [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  1 sibling, 3 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  8:58 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

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.  Recognized cases:

  - struct bpf_prog_aux *  : is_kfunc_arg_prog_aux()
  - __ign suffix args      : is_kfunc_arg_ignore()
  - list_push/rbtree_add   : is_bpf_list_push_kfunc() / is_bpf_rbtree_add_kfunc()

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 | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8dd79b735a69..55c74d064e4e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11916,9 +11916,22 @@ 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)) {
+			/* list_push / rbtree_add kfuncs have implicit args
+			 * (e.g. 'off' parameter) handled during verification
+			 * in bpf_fixup_kfunc_call().  Don't flag them.
+			 */
+			if (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


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  8:58         ` [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-02  8:58           ` chenyuan_fl
  2026-06-02  9:31             ` sashiko-bot
  2026-06-02  9:44             ` bot+bpf-ci
  1 sibling, 2 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  8:58 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

From: Yuan Chen <chenyuan@kylinos.cn>

The preceding patch fixes a silent fallthrough in check_kfunc_args()
that could cause the verifier to skip bpf_prog_aux injection for
KF_IMPLICIT_ARGS kfuncs when module BTF is inconsistent with vmlinux
(e.g. pahole 1.30 breaking distilled base dedup).

Add a positive regression test that verifies the injection path works
correctly under normal conditions (pahole 1.31+).  The test contaminates
BPF R2 with a magic value 0xDEAD via inline assembly before calling a
KF_IMPLICIT_ARGS kfunc associated with a struct_ops map.  The kfunc
validates that the kernel overwrote R2 with the real bpf_prog_aux
pointer rather than leaving the stale value.

The specific pahole 1.30 BTF mismatch scenario cannot be tested with
CI (which uses pahole 1.31), but this test ensures the injection
mechanism remains correct and does not regress.

Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 .../bpf/prog_tests/test_struct_ops_assoc.c    |  7 ++++
 .../selftests/bpf/progs/struct_ops_assoc.c    | 40 +++++++++++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    |  9 +++++
 .../bpf/test_kmods/bpf_testmod_kfunc.h        |  1 +
 4 files changed, 57 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
index 461ded722351..fb4d06dd6c4d 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
@@ -35,6 +35,10 @@ static void test_st_ops_assoc(void)
 					    skel->maps.st_ops_map_b, NULL);
 	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_b, st_ops_map_b)");
 
+	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_test_aux_inject,
+					skel->maps.st_ops_map_a, NULL);
+	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_test_aux_inject, st_ops_map_a)");
+
 	/* sys_enter_prog_a already associated with map_a */
 	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
 					    skel->maps.st_ops_map_b, NULL);
@@ -52,6 +56,7 @@ static void test_st_ops_assoc(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 	/* run syscall_prog that calls .test_1 and checks return */
 	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_a), NULL);
@@ -62,6 +67,7 @@ static void test_st_ops_assoc(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 out:
 	struct_ops_assoc__destroy(skel);
@@ -97,6 +103,7 @@ static void test_st_ops_assoc_reuse(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 out:
 	struct_ops_assoc_reuse__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
index 68842e3f936b..ed0084453d56 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
@@ -103,3 +103,43 @@ SEC(".struct_ops.link")
 struct bpf_testmod_multi_st_ops st_ops_map_b = {
 	.test_1 = (void *)test_1_b,
 };
+
+/* Test for aux injection with stale register contamination.
+ *
+ * This test verifies that the kernel correctly injects the implicit
+ * bpf_prog_aux pointer for kfuncs with KF_IMPLICIT_ARGS.  The program
+ * uses inline assembly to contaminate R2 with a known magic value
+ * before calling the kfunc:
+ *
+ *   asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+ *
+ * The kernel must inject env->prog->aux into R2, overriding the magic
+ * value.  The kfunc compares the received aux pointer against 0xDEAD:
+ *
+ *   - aux == 0xDEAD  → kernel failed to inject → kfunc returns -EINVAL
+ *   - aux != 0xDEAD  → kernel correctly injected → kfunc returns marker
+ *
+ * 0xDEAD is chosen with bit 31 clear to avoid BPF ALU64 sign-extension
+ * when used as a 32-bit immediate.
+ */
+int test_err_inject;
+
+SEC("tp_btf/sys_enter")
+int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
+{
+	struct task_struct *task;
+	int marker = 0x5A5A;
+	int ret;
+
+	task = bpf_get_current_task_btf();
+	if (!test_pid || task->pid != test_pid)
+		return 0;
+
+	asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+
+	ret = bpf_kfunc_aux_inject_stale(marker);
+	if (ret != marker)
+		test_err_inject++;
+
+	return 0;
+}
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 0be918fe3021..8e979f0fa56d 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1315,6 +1315,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
 
 __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
+__bpf_kfunc int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
 
 /* hook targets */
@@ -1399,6 +1400,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_assoc, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
+BTF_ID_FLAGS(func, bpf_kfunc_aux_inject_stale, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_trigger_ctx_check)
 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
 
@@ -1916,6 +1918,13 @@ int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux)
 	return bpf_kfunc_implicit_arg_legacy(a, b, aux);
 }
 
+int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux)
+{
+	if ((unsigned long)aux == 0xDEAD)
+		return -EINVAL;
+	return marker;
+}
+
 static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
 {
 	struct bpf_testmod_multi_st_ops *st_ops =
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 2edc36b66de9..c18791e96b21 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -192,6 +192,7 @@ int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
 #ifndef __KERNEL__
 extern int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __weak __ksym;
 extern int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args) __weak __ksym;
+extern int bpf_kfunc_aux_inject_stale(int marker) __weak __ksym;
 #endif
 
 struct prog_test_member *bpf_kfunc_get_default_trusted_ptr_test(void) __ksym;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-02  9:23             ` sashiko-bot
  2026-06-02  9:44             ` bot+bpf-ci
  2026-06-02 18:52             ` Ihor Solodrai
  2 siblings, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-06-02  9:23 UTC (permalink / raw)
  To: chenyuan_fl; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [Low] The patch introduces a fatal compilation error by using an undeclared variable `argno` and an undefined function `reg_arg_name`.

Pre-existing issues:
- [Critical] Module BTF dedup failures cause special pointer types (like bpf_timer, bpf_list_head, bpf_spin_lock) to fall back to KF_ARG_PTR_TO_MEM, allowing BPF programs to pass attacker-controlled memory instead of valid map-backed objects.
--

commit 198d635479b994ffecc7d0ff675e91259613ce61
Author: Yuan Chen <chenyuan@kylinos.cn>
Subject: bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref

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. This patch ensures the verifier either
validates and injects the correct bpf_prog_aux pointer, or rejects the
program outright if BTF type matching fails.

> 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.

[Severity: Critical]
This is a pre-existing issue, but since module BTF dedup failures cause
btf_types_are_same() to return false, does this also affect explicit special
kfunc arguments?

When __is_kfunc_ptr_arg_type() in kernel/bpf/verifier.c fails to match types
like bpf_timer or bpf_list_head, the verifier falls through to the fallback
logic in get_kfunc_ptr_arg_type():

kernel/bpf/verifier.c:get_kfunc_ptr_arg_type() {
    ...
	if (!btf_type_is_scalar(ref_t) && !__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0) &&
	    (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) {
		...
	}
	return arg_mem_size ? KF_ARG_PTR_TO_MEM_SIZE : KF_ARG_PTR_TO_MEM;
}

Because structs like bpf_timer and bpf_list_head are composed solely of
scalars, __btf_type_is_scalar_struct() evaluates to true, and they are
categorized as KF_ARG_PTR_TO_MEM.

Does this allow BPF programs to pass a pointer to attacker-controlled memory
(like a stack buffer) to a module kfunc expecting a valid, map-backed kernel
object?

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4d..57e5fdf9f7e06 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c

[ ... ]

> @@ -12082,9 +12082,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */
> +			if (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));

[Severity: Low]
Will this compile?  It looks like argno is neither declared nor defined in
check_kfunc_args(), and reg_arg_name() is undefined in the kernel source tree.

Should this use regno instead of argno, and a different way to format the
register name?

> +			return -EFAULT;
> +		}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602085827.2562222-1-chenyuan_fl@163.com?part=1

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  8:58           ` [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
@ 2026-06-02  9:31             ` sashiko-bot
  2026-06-02  9:44             ` bot+bpf-ci
  1 sibling, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-06-02  9:31 UTC (permalink / raw)
  To: chenyuan_fl; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Multi-line comment does not follow the BPF subsystem comment style.
- [Low] Build failure in selftests due to accessing a non-existent BSS field in the reused struct_ops skeleton.
--

commit 70edd2df92a9670c07a307bbdf1081733e04e3dd
Author: Yuan Chen <chenyuan@kylinos.cn>

selftests/bpf: Add regression test for kfunc implicit arg injection

This commit adds a regression test to verify that the BPF verifier correctly
injects the bpf_prog_aux pointer for KF_IMPLICIT_ARGS kfuncs. It uses inline
assembly to contaminate R2 before calling a kfunc to ensure the kernel properly
overwrites it with the valid pointer.

> diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> index 461ded7223515..fb4d06dd6c4dc 100644
> --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c

[ ... ]

> @@ -97,6 +103,7 @@ static void test_st_ops_assoc_reuse(void)
>  
>  	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
>  	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
> +	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");

[Severity: Low]
Will this cause a build failure? It looks like test_err_inject is defined
in struct_ops_assoc.c, but this function is testing the skeleton for
struct_ops_assoc_reuse.c.

Since the variable isn't defined in the reused program's source, the
generated struct_ops_assoc_reuse skeleton won't contain the test_err_inject
BSS field, which would cause a compiler error when accessing
skel->bss->test_err_inject here.

>  
>  out:
>  	struct_ops_assoc_reuse__destroy(skel);
> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b9..ed0084453d560 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")
>  struct bpf_testmod_multi_st_ops st_ops_map_b = {
>  	.test_1 = (void *)test_1_b,
>  };
> +
> +/* Test for aux injection with stale register contamination.
> + *

[Severity: Low]
This isn't a bug, but does this violate the BPF subsystem multi-line
comment style? The guidelines require the opening /* to be on its own
line, rather than starting the text on the same line.

> + * This test verifies that the kernel correctly injects the implicit
> + * bpf_prog_aux pointer for kfuncs with KF_IMPLICIT_ARGS.  The program
> + * uses inline assembly to contaminate R2 with a known magic value
> + * before calling the kfunc:

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602085827.2562222-1-chenyuan_fl@163.com?part=2

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest
  2026-06-01 19:32       ` Eduard Zingerman
  2026-06-02  8:58         ` [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
@ 2026-06-02  9:38         ` chenyuan_fl
  2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  1 sibling, 2 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  9:38 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

From: Yuan Chen <chenyuan@kylinos.cn>

v1: https://lore.kernel.org/bpf/20260407080900.551797-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 (test_st_ops_assoc_reuse used
the wrong skeleton for the new variable).

Patch 2 adds a positive regression test for the injection path.
Yonghong Song pointed out that with pahole 1.31 available in CI the
test will always pass, so it cannot reproduce the original BTF mismatch
scenario.  The test still serves as a useful regression guard for the
injection mechanism itself, however, and would catch any accidental
breakage in the future.

Thanks to everyone who reviewed and provided feedback on this series.

Yuan Chen (2):
  bpf: Fix kfunc implicit arg inject type detection to prevent invalid
    pointer deref
  selftests/bpf: Add regression test for kfunc implicit arg injection

 kernel/bpf/verifier.c                              | 15 +++++++-
 .../bpf/prog_tests/test_struct_ops_assoc.c         |  6 ++++
 .../testing/selftests/bpf/progs/struct_ops_assoc.c | 40 ++++++++++++++++++++++
 .../testing/selftests/bpf/test_kmods/bpf_testmod.c |  9 +++++
 .../selftests/bpf/test_kmods/bpf_testmod_kfunc.h   |  1 +
 5 files changed, 70 insertions(+), 1 deletion(-)

-- 
2.47.2


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  9:38         ` [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
@ 2026-06-02  9:38           ` chenyuan_fl
  2026-06-02  9:58             ` sashiko-bot
                               ` (2 more replies)
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  1 sibling, 3 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  9:38 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

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.  Recognized cases:

  - struct bpf_prog_aux *  : is_kfunc_arg_prog_aux()
  - __ign suffix args      : is_kfunc_arg_ignore()
  - list_push/rbtree_add   : is_bpf_list_push_kfunc() / is_bpf_rbtree_add_kfunc()

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 | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8dd79b735a69..55c74d064e4e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11916,9 +11916,22 @@ 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)) {
+			/* list_push / rbtree_add kfuncs have implicit args
+			 * (e.g. 'off' parameter) handled during verification
+			 * in bpf_fixup_kfunc_call().  Don't flag them.
+			 */
+			if (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


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38         ` [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
  2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-02  9:38           ` chenyuan_fl
  2026-06-02 10:06             ` sashiko-bot
                               ` (4 more replies)
  1 sibling, 5 replies; 35+ messages in thread
From: chenyuan_fl @ 2026-06-02  9:38 UTC (permalink / raw)
  To: eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

From: Yuan Chen <chenyuan@kylinos.cn>

The preceding patch fixes a silent fallthrough in check_kfunc_args()
that could cause the verifier to skip bpf_prog_aux injection for
KF_IMPLICIT_ARGS kfuncs when module BTF is inconsistent with vmlinux
(e.g. pahole 1.30 breaking distilled base dedup).

Add a positive regression test that verifies the injection path works
correctly under normal conditions (pahole 1.31+).  The test contaminates
BPF R2 with a magic value 0xDEAD via inline assembly before calling a
KF_IMPLICIT_ARGS kfunc associated with a struct_ops map.  The kfunc
validates that the kernel overwrote R2 with the real bpf_prog_aux
pointer rather than leaving the stale value.

The specific pahole 1.30 BTF mismatch scenario cannot be tested with
CI (which uses pahole 1.31), but this test ensures the injection
mechanism remains correct and does not regress.

Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
 .../bpf/prog_tests/test_struct_ops_assoc.c    |  6 +++
 .../selftests/bpf/progs/struct_ops_assoc.c    | 40 +++++++++++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    |  9 +++++
 .../bpf/test_kmods/bpf_testmod_kfunc.h        |  1 +
 4 files changed, 56 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
index 461ded722351..192fd3166f38 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
@@ -35,6 +35,10 @@ static void test_st_ops_assoc(void)
 					    skel->maps.st_ops_map_b, NULL);
 	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_b, st_ops_map_b)");
 
+	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_test_aux_inject,
+					skel->maps.st_ops_map_a, NULL);
+	ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_test_aux_inject, st_ops_map_a)");
+
 	/* sys_enter_prog_a already associated with map_a */
 	err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
 					    skel->maps.st_ops_map_b, NULL);
@@ -52,6 +56,7 @@ static void test_st_ops_assoc(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 	/* run syscall_prog that calls .test_1 and checks return */
 	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_a), NULL);
@@ -62,6 +67,7 @@ static void test_st_ops_assoc(void)
 
 	ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
 	ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+	ASSERT_EQ(skel->bss->test_err_inject, 0, "skel->bss->test_err_inject");
 
 out:
 	struct_ops_assoc__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
index 68842e3f936b..ed0084453d56 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
@@ -103,3 +103,43 @@ SEC(".struct_ops.link")
 struct bpf_testmod_multi_st_ops st_ops_map_b = {
 	.test_1 = (void *)test_1_b,
 };
+
+/* Test for aux injection with stale register contamination.
+ *
+ * This test verifies that the kernel correctly injects the implicit
+ * bpf_prog_aux pointer for kfuncs with KF_IMPLICIT_ARGS.  The program
+ * uses inline assembly to contaminate R2 with a known magic value
+ * before calling the kfunc:
+ *
+ *   asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+ *
+ * The kernel must inject env->prog->aux into R2, overriding the magic
+ * value.  The kfunc compares the received aux pointer against 0xDEAD:
+ *
+ *   - aux == 0xDEAD  → kernel failed to inject → kfunc returns -EINVAL
+ *   - aux != 0xDEAD  → kernel correctly injected → kfunc returns marker
+ *
+ * 0xDEAD is chosen with bit 31 clear to avoid BPF ALU64 sign-extension
+ * when used as a 32-bit immediate.
+ */
+int test_err_inject;
+
+SEC("tp_btf/sys_enter")
+int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
+{
+	struct task_struct *task;
+	int marker = 0x5A5A;
+	int ret;
+
+	task = bpf_get_current_task_btf();
+	if (!test_pid || task->pid != test_pid)
+		return 0;
+
+	asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
+
+	ret = bpf_kfunc_aux_inject_stale(marker);
+	if (ret != marker)
+		test_err_inject++;
+
+	return 0;
+}
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 0be918fe3021..8e979f0fa56d 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1315,6 +1315,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
 
 __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
+__bpf_kfunc int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux);
 __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
 
 /* hook targets */
@@ -1399,6 +1400,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_assoc, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
+BTF_ID_FLAGS(func, bpf_kfunc_aux_inject_stale, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_trigger_ctx_check)
 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
 
@@ -1916,6 +1918,13 @@ int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux)
 	return bpf_kfunc_implicit_arg_legacy(a, b, aux);
 }
 
+int bpf_kfunc_aux_inject_stale(int marker, struct bpf_prog_aux *aux)
+{
+	if ((unsigned long)aux == 0xDEAD)
+		return -EINVAL;
+	return marker;
+}
+
 static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
 {
 	struct bpf_testmod_multi_st_ops *st_ops =
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 2edc36b66de9..c18791e96b21 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -192,6 +192,7 @@ int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
 #ifndef __KERNEL__
 extern int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __weak __ksym;
 extern int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args) __weak __ksym;
+extern int bpf_kfunc_aux_inject_stale(int marker) __weak __ksym;
 #endif
 
 struct prog_test_member *bpf_kfunc_get_default_trusted_ptr_test(void) __ksym;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  9:23             ` sashiko-bot
@ 2026-06-02  9:44             ` bot+bpf-ci
  2026-06-02 18:52             ` Ihor Solodrai
  2 siblings, 0 replies; 35+ messages in thread
From: bot+bpf-ci @ 2026-06-02  9:44 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2463 bytes --]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4..57e5fdf9f7e0 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12082,9 +12082,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */

This isn't a bug, but in the v2 review Yonghong Song asked for a comment
explaining that this code exists to work around the pahole 1.30 BTF
mismatch, so it can be removed once that toolchain issue is no longer
relevant:

  https://lore.kernel.org/bpf/7b3ba4a8-a785-4f70-879d-50e63d6de787@linux.dev/

  "Also, please add some comments in the code to explain the reason of
   this code is due to pahole 1.30. So later on at some point, this code
   can be removed."

The comment block here only documents the list_push/rbtree_add exception
and does not record the pahole 1.30 rationale.  Was that request meant
to be addressed in this version?

> +			if (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));

Where do reg_arg_name() and argno come from?

Grepping the tree finds reg_arg_name and argno only on this new line;
there is no definition anywhere else, and the enclosing for loop in
check_kfunc_args() declares the register index as:

	u32 regno = i + 1;

not argno.  Does this compile?  As written, reg_arg_name() looks like an
implicit/undeclared function reference and argno an undeclared
identifier.

Should this use the existing regno instead, for example:

	verbose(env, "R%d unrecognized implicit argument, possible BTF mismatch\n",
		regno);

> +			return -EFAULT;
> +		}
> +
>  		t = btf_type_skip_modifiers(btf, args[i].type, NULL);
>
>  		if (btf_type_is_scalar(t)) {


---
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/26810234837

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  8:58           ` [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  2026-06-02  9:31             ` sashiko-bot
@ 2026-06-02  9:44             ` bot+bpf-ci
  1 sibling, 0 replies; 35+ messages in thread
From: bot+bpf-ci @ 2026-06-02  9:44 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1554 bytes --]

> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b..ed0084453d56 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")

[ ... ]

> +int test_err_inject;
> +
> +SEC("tp_btf/sys_enter")
> +int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
> +{

This was raised on v2 by Yonghong Song, and it looks like it may still be
open:

  Not sure whether this patch makes sense or not. The CI has pahole 1.31
  so the test will always succeed.

  https://lore.kernel.org/bpf/9cc9ce47-c45e-4878-ad3a-6e6967399ab9@linux.dev/

The underlying bug only manifests with pahole 1.30, where module BTF is
inconsistent with vmlinux.  Under CI's pahole 1.31 the injection always
works, so does this test pass whether or not the fix is present?

In v3 the only change was an added paragraph in the commit message
acknowledging the pahole 1.30 scenario cannot be reproduced under CI, and
no code change was made to gate the test on the bug.  Yonghong has not
replied to v3.

Is there a way to make the test meaningfully exercise the regression rather
than always succeeding under the CI toolchain?

---
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/26810234837

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
@ 2026-06-02  9:58             ` sashiko-bot
  2026-06-02 10:42             ` bot+bpf-ci
  2026-06-05  0:42             ` Eduard Zingerman
  2 siblings, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-06-02  9:58 UTC (permalink / raw)
  To: chenyuan_fl; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch introduces undeclared identifiers `argno` and `reg_arg_name`, resulting in a fatal compilation error.
--

commit af92ff0b79cc212b53a40ea48cae9a7de79d1a76
Author: Yuan Chen <chenyuan@kylinos.cn>

bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref

This commit fixes a verifier issue where unrecognized implicit kfunc arguments
caused a silent fallthrough instead of being rejected. It updates the checks
in check_kfunc_args() to explicitly return -EFAULT for unrecognized implicit
arguments, preventing a potential invalid pointer dereference or information
disclosure.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4d..57e5fdf9f7e06 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12082,9 +12082,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */
> +			if (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));

[Severity: Low]
Will this compile cleanly? In check_kfunc_args(), it looks like argno is 
undeclared in this scope, as the local loop index appears to be i.

Also, does reg_arg_name() exist? I am unable to locate a definition for it in
the tree. Could this result in an implicit function declaration error during
the build?

> +			return -EFAULT;
> +		}
> +
>  		t = btf_type_skip_modifiers(btf, args[i].type, NULL);
>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602093836.2632714-1-chenyuan_fl@163.com?part=1

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
@ 2026-06-02 10:06             ` sashiko-bot
  2026-06-02 10:27             ` bot+bpf-ci
                               ` (3 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-06-02 10:06 UTC (permalink / raw)
  To: chenyuan_fl; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The multi-line comment in `struct_ops_assoc.c` violates the strict BPF subsystem comment style guidelines.
--

commit 0131da78b5d91ab9b0a63c8ffd845ab004048c55
Author: Yuan Chen <chenyuan@kylinos.cn>
selftests/bpf: Add regression test for kfunc implicit arg injection

This commit adds a positive regression test to verify that the injection
path for KF_IMPLICIT_ARGS kfuncs works correctly. It uses inline assembly
to contaminate BPF R2 before calling a kfunc associated with a struct_ops
map.

> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b9..ed0084453d560 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")
>  struct bpf_testmod_multi_st_ops st_ops_map_b = {
>  	.test_1 = (void *)test_1_b,
>  };
> +
> +/* Test for aux injection with stale register contamination.
> + *
> + * This test verifies that the kernel correctly injects the implicit

[Severity: Low]
Could this comment be updated to follow the BPF subsystem style?
Multi-line comments in this subsystem should have the opening /* on its own
line, with the text starting on the following line.

> + * bpf_prog_aux pointer for kfuncs with KF_IMPLICIT_ARGS.  The program
> + * uses inline assembly to contaminate R2 with a known magic value
> + * before calling the kfunc:

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602093836.2632714-1-chenyuan_fl@163.com?part=2

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  2026-06-02 10:06             ` sashiko-bot
@ 2026-06-02 10:27             ` bot+bpf-ci
  2026-06-02 17:36             ` kernel test robot
                               ` (2 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: bot+bpf-ci @ 2026-06-02 10:27 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3733 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> index 461ded722351..192fd3166f38 100644
> --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c

[ ... ]

> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> index 68842e3f936b..ed0084453d56 100644
> --- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
> @@ -103,3 +103,43 @@ SEC(".struct_ops.link")
>  struct bpf_testmod_multi_st_ops st_ops_map_b = {
>  	.test_1 = (void *)test_1_b,
>  };
> +
> +/* Test for aux injection with stale register contamination.
> + *
> + * This test verifies that the kernel correctly injects the implicit
> + * bpf_prog_aux pointer for kfuncs with KF_IMPLICIT_ARGS.  The program
> + * uses inline assembly to contaminate R2 with a known magic value
> + * before calling the kfunc:
> + *
> + *   asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
> + *
> + * The kernel must inject env->prog->aux into R2, overriding the magic
> + * value.  The kfunc compares the received aux pointer against 0xDEAD:
> + *
> + *   - aux == 0xDEAD  -> kernel failed to inject -> kfunc returns -EINVAL
> + *   - aux != 0xDEAD  -> kernel correctly injected -> kfunc returns marker
> + *
> + * 0xDEAD is chosen with bit 31 clear to avoid BPF ALU64 sign-extension
> + * when used as a 32-bit immediate.
> + */
> +int test_err_inject;
> +
> +SEC("tp_btf/sys_enter")
> +int BPF_PROG(sys_enter_prog_test_aux_inject, struct pt_regs *regs, long id)
> +{
> +	struct task_struct *task;
> +	int marker = 0x5A5A;
> +	int ret;
> +
> +	task = bpf_get_current_task_btf();
> +	if (!test_pid || task->pid != test_pid)
> +		return 0;
> +
> +	asm volatile("r2 = %[magic]" :: [magic] "ri"(0xDEAD) : "r2");
> +
> +	ret = bpf_kfunc_aux_inject_stale(marker);
> +	if (ret != marker)
> +		test_err_inject++;
> +
> +	return 0;
> +}

In v2, Yonghong Song questioned whether this selftest makes sense, given
that CI has pahole 1.31 so the test will always succeed:

https://lore.kernel.org/bpf/9cc9ce47-c45e-4878-ad3a-6e6967399ab9@linux.dev/

The commit message now acknowledges that "the specific pahole 1.30 BTF
mismatch scenario cannot be tested with CI", but it's unclear what value
this test provides if it cannot actually verify the bug condition.

Could you explain the rationale for including a test that validates the
injection mechanism works under normal conditions but cannot reproduce the
actual pahole 1.30 BTF inconsistency scenario the bug fix addresses?

[ ... ]

Unaddressed review comment from the preceding patch

The issue below applies to the preceding patch (kernel/bpf/verifier.c)
rather than this selftest, but remains unaddressed in the patch series:

In v2, Yonghong Song requested that code comments should explain the fix
is due to pahole 1.30 BTF generation issues, so the code can be removed
later when that toolchain is no longer relevant:

https://lore.kernel.org/bpf/7b3ba4a8-a785-4f70-879d-50e63d6de787@linux.dev/

The commit message in the preceding patch describes the pahole 1.30 issue
extensively, but the in-code documentation was never added in v3 or v4.

Should this documentation be added to check_kfunc_args() or
is_kfunc_arg_implicit() to explain the temporary nature of this fix?


---
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/26812478701

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  9:58             ` sashiko-bot
@ 2026-06-02 10:42             ` bot+bpf-ci
  2026-06-05  0:42             ` Eduard Zingerman
  2 siblings, 0 replies; 35+ messages in thread
From: bot+bpf-ci @ 2026-06-02 10:42 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, chenyuan_fl, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2732 bytes --]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4..57e5fdf9f7e0 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12082,9 +12082,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */

A small note here: the v2 review asked for a comment recording that this
workaround exists because of the pahole 1.30 BTF mismatch, so the code can be
removed once that toolchain issue is no longer relevant.  The comment block
here only documents the list_push/rbtree_add exception and does not mention the
pahole 1.30 rationale.  Should that be added?

> +			if (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));
                                ^^^^^^^^^^^^^^^^^^^^^^^

Will this compile?  I cannot find a declaration of argno or a definition of
reg_arg_name() anywhere in the tree at this commit.

The per-argument loop in check_kfunc_args() names its register index regno,
not argno:

kernel/bpf/verifier.c:check_kfunc_args() {
    ...
    for (i = 0; i < nargs; i++) {
        ...
        u32 regno = i + 1, ref_id, type_size;
        ...
    }
}

There is no argno in this function, and git grep for reg_arg_name at this
commit returns only this call site, with no definition or declaration.  That
would give "argno undeclared" and an implicit declaration of reg_arg_name(),
which is an error under -Werror=implicit-function-declaration, so
kernel/bpf/verifier.c (obj-y, always built) would fail to build.

The rest of this function uses regno with an R%d format, for example "R%d is
not a scalar".  Did you mean something like:

	verbose(env, "R%d unrecognized implicit argument, possible BTF mismatch\n",
		regno);

The bpf-ci bot and sashiko-bot both raised this on v3 and v4, and it still
reads reg_arg_name(env, argno) verbatim here:

  https://lore.kernel.org/bpf/20260602085827.2562222-2-chenyuan_fl@163.com/

> +			return -EFAULT;
> +		}
> +

---
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/26812478701

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
  2026-06-02 10:06             ` sashiko-bot
  2026-06-02 10:27             ` bot+bpf-ci
@ 2026-06-02 17:36             ` kernel test robot
  2026-06-02 18:37             ` kernel test robot
  2026-06-05  1:29             ` Eduard Zingerman
  4 siblings, 0 replies; 35+ messages in thread
From: kernel test robot @ 2026-06-02 17:36 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: llvm, oe-kbuild-all, andrii, ast, bot+bpf-ci, bpf, chenyuan,
	chenyuan_fl, clm, daniel, ihor.solodrai, jolsa, linux-kernel,
	martin.lau, martin.lau, memxor, song

Hi Yuan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf/master]

url:    https://github.com/intel-lab-lkp/linux/commits/chenyuan_fl-163-com/bpf-Fix-kfunc-implicit-arg-inject-type-detection-to-prevent-invalid-pointer-deref/20260602-175420
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git master
patch link:    https://lore.kernel.org/r/20260602093836.2632714-3-chenyuan_fl%40163.com
patch subject: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260602/202606021934.SXop4khA-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260602/202606021934.SXop4khA-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606021934.SXop4khA-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/bpf/verifier.c:12097:5: error: call to undeclared function 'reg_arg_name'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    12097 |                                 reg_arg_name(env, argno));
          |                                 ^
>> kernel/bpf/verifier.c:12097:23: error: use of undeclared identifier 'argno'
    12097 |                                 reg_arg_name(env, argno));
          |                                                   ^
   2 errors generated.


vim +/reg_arg_name +12097 kernel/bpf/verifier.c

 12044	
 12045	static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
 12046				    int insn_idx)
 12047	{
 12048		const char *func_name = meta->func_name, *ref_tname;
 12049		const struct btf *btf = meta->btf;
 12050		const struct btf_param *args;
 12051		struct btf_record *rec;
 12052		u32 i, nargs;
 12053		int ret;
 12054	
 12055		args = (const struct btf_param *)(meta->func_proto + 1);
 12056		nargs = btf_type_vlen(meta->func_proto);
 12057		if (nargs > MAX_BPF_FUNC_REG_ARGS) {
 12058			verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
 12059				MAX_BPF_FUNC_REG_ARGS);
 12060			return -EINVAL;
 12061		}
 12062	
 12063		/* Check that BTF function arguments match actual types that the
 12064		 * verifier sees.
 12065		 */
 12066		for (i = 0; i < nargs; i++) {
 12067			struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[i + 1];
 12068			const struct btf_type *t, *ref_t, *resolve_ret;
 12069			enum bpf_arg_type arg_type = ARG_DONTCARE;
 12070			u32 regno = i + 1, ref_id, type_size;
 12071			bool is_ret_buf_sz = false;
 12072			int kf_arg_type;
 12073	
 12074			if (is_kfunc_arg_prog_aux(btf, &args[i])) {
 12075				/* Reject repeated use bpf_prog_aux */
 12076				if (meta->arg_prog) {
 12077					verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
 12078					return -EFAULT;
 12079				}
 12080				meta->arg_prog = true;
 12081				cur_aux(env)->arg_prog = regno;
 12082				continue;
 12083			}
 12084	
 12085			if (is_kfunc_arg_ignore(btf, &args[i]))
 12086				continue;
 12087	
 12088			if (is_kfunc_arg_implicit(meta, i)) {
 12089				/* list_push / rbtree_add kfuncs have implicit args
 12090				 * (e.g. 'off' parameter) handled during verification
 12091				 * in bpf_fixup_kfunc_call().  Don't flag them.
 12092				 */
 12093				if (is_bpf_list_push_kfunc(meta->func_id) ||
 12094				    is_bpf_rbtree_add_kfunc(meta->func_id))
 12095					continue;
 12096				verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
 12097					reg_arg_name(env, argno));
 12098				return -EFAULT;
 12099			}
 12100	
 12101			t = btf_type_skip_modifiers(btf, args[i].type, NULL);
 12102	
 12103			if (btf_type_is_scalar(t)) {
 12104				if (reg->type != SCALAR_VALUE) {
 12105					verbose(env, "R%d is not a scalar\n", regno);
 12106					return -EINVAL;
 12107				}
 12108	
 12109				if (is_kfunc_arg_constant(meta->btf, &args[i])) {
 12110					if (meta->arg_constant.found) {
 12111						verifier_bug(env, "only one constant argument permitted");
 12112						return -EFAULT;
 12113					}
 12114					if (!tnum_is_const(reg->var_off)) {
 12115						verbose(env, "R%d must be a known constant\n", regno);
 12116						return -EINVAL;
 12117					}
 12118					ret = mark_chain_precision(env, regno);
 12119					if (ret < 0)
 12120						return ret;
 12121					meta->arg_constant.found = true;
 12122					meta->arg_constant.value = reg->var_off.value;
 12123				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
 12124					meta->r0_rdonly = true;
 12125					is_ret_buf_sz = true;
 12126				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
 12127					is_ret_buf_sz = true;
 12128				}
 12129	
 12130				if (is_ret_buf_sz) {
 12131					if (meta->r0_size) {
 12132						verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
 12133						return -EINVAL;
 12134					}
 12135	
 12136					if (!tnum_is_const(reg->var_off)) {
 12137						verbose(env, "R%d is not a const\n", regno);
 12138						return -EINVAL;
 12139					}
 12140	
 12141					meta->r0_size = reg->var_off.value;
 12142					ret = mark_chain_precision(env, regno);
 12143					if (ret)
 12144						return ret;
 12145				}
 12146				continue;
 12147			}
 12148	
 12149			if (!btf_type_is_ptr(t)) {
 12150				verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t));
 12151				return -EINVAL;
 12152			}
 12153	
 12154			if ((bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
 12155			    !is_kfunc_arg_nullable(meta->btf, &args[i])) {
 12156				verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
 12157				return -EACCES;
 12158			}
 12159	
 12160			if (reg->ref_obj_id) {
 12161				if (is_kfunc_release(meta) && meta->ref_obj_id) {
 12162					verifier_bug(env, "more than one arg with ref_obj_id R%d %u %u",
 12163						     regno, reg->ref_obj_id,
 12164						     meta->ref_obj_id);
 12165					return -EFAULT;
 12166				}
 12167				meta->ref_obj_id = reg->ref_obj_id;
 12168				if (is_kfunc_release(meta))
 12169					meta->release_regno = regno;
 12170			}
 12171	
 12172			ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
 12173			ref_tname = btf_name_by_offset(btf, ref_t->name_off);
 12174	
 12175			kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs);
 12176			if (kf_arg_type < 0)
 12177				return kf_arg_type;
 12178	
 12179			switch (kf_arg_type) {
 12180			case KF_ARG_PTR_TO_NULL:
 12181				continue;
 12182			case KF_ARG_PTR_TO_MAP:
 12183				if (!reg->map_ptr) {
 12184					verbose(env, "pointer in R%d isn't map pointer\n", regno);
 12185					return -EINVAL;
 12186				}
 12187				if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
 12188						      reg->map_ptr->record->task_work_off >= 0)) {
 12189					/* Use map_uid (which is unique id of inner map) to reject:
 12190					 * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
 12191					 * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
 12192					 * if (inner_map1 && inner_map2) {
 12193					 *     wq = bpf_map_lookup_elem(inner_map1);
 12194					 *     if (wq)
 12195					 *         // mismatch would have been allowed
 12196					 *         bpf_wq_init(wq, inner_map2);
 12197					 * }
 12198					 *
 12199					 * Comparing map_ptr is enough to distinguish normal and outer maps.
 12200					 */
 12201					if (meta->map.ptr != reg->map_ptr ||
 12202					    meta->map.uid != reg->map_uid) {
 12203						if (reg->map_ptr->record->task_work_off >= 0) {
 12204							verbose(env,
 12205								"bpf_task_work pointer in R2 map_uid=%d doesn't match map pointer in R3 map_uid=%d\n",
 12206								meta->map.uid, reg->map_uid);
 12207							return -EINVAL;
 12208						}
 12209						verbose(env,
 12210							"workqueue pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n",
 12211							meta->map.uid, reg->map_uid);
 12212						return -EINVAL;
 12213					}
 12214				}
 12215				meta->map.ptr = reg->map_ptr;
 12216				meta->map.uid = reg->map_uid;
 12217				fallthrough;
 12218			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
 12219			case KF_ARG_PTR_TO_BTF_ID:
 12220				if (!is_trusted_reg(reg)) {
 12221					if (!is_kfunc_rcu(meta)) {
 12222						verbose(env, "R%d must be referenced or trusted\n", regno);
 12223						return -EINVAL;
 12224					}
 12225					if (!is_rcu_reg(reg)) {
 12226						verbose(env, "R%d must be a rcu pointer\n", regno);
 12227						return -EINVAL;
 12228					}
 12229				}
 12230				fallthrough;
 12231			case KF_ARG_PTR_TO_DYNPTR:
 12232			case KF_ARG_PTR_TO_ITER:
 12233			case KF_ARG_PTR_TO_LIST_HEAD:
 12234			case KF_ARG_PTR_TO_LIST_NODE:
 12235			case KF_ARG_PTR_TO_RB_ROOT:
 12236			case KF_ARG_PTR_TO_RB_NODE:
 12237			case KF_ARG_PTR_TO_MEM:
 12238			case KF_ARG_PTR_TO_MEM_SIZE:
 12239			case KF_ARG_PTR_TO_CALLBACK:
 12240			case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
 12241			case KF_ARG_PTR_TO_CONST_STR:
 12242			case KF_ARG_PTR_TO_WORKQUEUE:
 12243			case KF_ARG_PTR_TO_TIMER:
 12244			case KF_ARG_PTR_TO_TASK_WORK:
 12245			case KF_ARG_PTR_TO_IRQ_FLAG:
 12246			case KF_ARG_PTR_TO_RES_SPIN_LOCK:
 12247				break;
 12248			case KF_ARG_PTR_TO_CTX:
 12249				arg_type = ARG_PTR_TO_CTX;
 12250				break;
 12251			default:
 12252				verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
 12253				return -EFAULT;
 12254			}
 12255	
 12256			if (is_kfunc_release(meta) && reg->ref_obj_id)
 12257				arg_type |= OBJ_RELEASE;
 12258			ret = check_func_arg_reg_off(env, reg, regno, arg_type);
 12259			if (ret < 0)
 12260				return ret;
 12261	
 12262			switch (kf_arg_type) {
 12263			case KF_ARG_PTR_TO_CTX:
 12264				if (reg->type != PTR_TO_CTX) {
 12265					verbose(env, "arg#%d expected pointer to ctx, but got %s\n",
 12266						i, reg_type_str(env, reg->type));
 12267					return -EINVAL;
 12268				}
 12269	
 12270				if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
 12271					ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
 12272					if (ret < 0)
 12273						return -EINVAL;
 12274					meta->ret_btf_id  = ret;
 12275				}
 12276				break;
 12277			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
 12278				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12279					if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
 12280						verbose(env, "arg#%d expected for bpf_obj_drop()\n", i);
 12281						return -EINVAL;
 12282					}
 12283				} else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
 12284					if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
 12285						verbose(env, "arg#%d expected for bpf_percpu_obj_drop()\n", i);
 12286						return -EINVAL;
 12287					}
 12288				} else {
 12289					verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12290					return -EINVAL;
 12291				}
 12292				if (!reg->ref_obj_id) {
 12293					verbose(env, "allocated object must be referenced\n");
 12294					return -EINVAL;
 12295				}
 12296				if (meta->btf == btf_vmlinux) {
 12297					meta->arg_btf = reg->btf;
 12298					meta->arg_btf_id = reg->btf_id;
 12299				}
 12300				break;
 12301			case KF_ARG_PTR_TO_DYNPTR:
 12302			{
 12303				enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
 12304				int clone_ref_obj_id = 0;
 12305	
 12306				if (reg->type == CONST_PTR_TO_DYNPTR)
 12307					dynptr_arg_type |= MEM_RDONLY;
 12308	
 12309				if (is_kfunc_arg_uninit(btf, &args[i]))
 12310					dynptr_arg_type |= MEM_UNINIT;
 12311	
 12312				if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) {
 12313					dynptr_arg_type |= DYNPTR_TYPE_SKB;
 12314				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) {
 12315					dynptr_arg_type |= DYNPTR_TYPE_XDP;
 12316				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_meta]) {
 12317					dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
 12318				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_file]) {
 12319					dynptr_arg_type |= DYNPTR_TYPE_FILE;
 12320				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_file_discard]) {
 12321					dynptr_arg_type |= DYNPTR_TYPE_FILE;
 12322					meta->release_regno = regno;
 12323				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
 12324					   (dynptr_arg_type & MEM_UNINIT)) {
 12325					enum bpf_dynptr_type parent_type = meta->initialized_dynptr.type;
 12326	
 12327					if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
 12328						verifier_bug(env, "no dynptr type for parent of clone");
 12329						return -EFAULT;
 12330					}
 12331	
 12332					dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
 12333					clone_ref_obj_id = meta->initialized_dynptr.ref_obj_id;
 12334					if (dynptr_type_refcounted(parent_type) && !clone_ref_obj_id) {
 12335						verifier_bug(env, "missing ref obj id for parent of clone");
 12336						return -EFAULT;
 12337					}
 12338				}
 12339	
 12340				ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id);
 12341				if (ret < 0)
 12342					return ret;
 12343	
 12344				if (!(dynptr_arg_type & MEM_UNINIT)) {
 12345					int id = dynptr_id(env, reg);
 12346	
 12347					if (id < 0) {
 12348						verifier_bug(env, "failed to obtain dynptr id");
 12349						return id;
 12350					}
 12351					meta->initialized_dynptr.id = id;
 12352					meta->initialized_dynptr.type = dynptr_get_type(env, reg);
 12353					meta->initialized_dynptr.ref_obj_id = dynptr_ref_obj_id(env, reg);
 12354				}
 12355	
 12356				break;
 12357			}
 12358			case KF_ARG_PTR_TO_ITER:
 12359				if (meta->func_id == special_kfunc_list[KF_bpf_iter_css_task_new]) {
 12360					if (!check_css_task_iter_allowlist(env)) {
 12361						verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
 12362						return -EINVAL;
 12363					}
 12364				}
 12365				ret = process_iter_arg(env, regno, insn_idx, meta);
 12366				if (ret < 0)
 12367					return ret;
 12368				break;
 12369			case KF_ARG_PTR_TO_LIST_HEAD:
 12370				if (reg->type != PTR_TO_MAP_VALUE &&
 12371				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12372					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
 12373					return -EINVAL;
 12374				}
 12375				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
 12376					verbose(env, "allocated object must be referenced\n");
 12377					return -EINVAL;
 12378				}
 12379				ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta);
 12380				if (ret < 0)
 12381					return ret;
 12382				break;
 12383			case KF_ARG_PTR_TO_RB_ROOT:
 12384				if (reg->type != PTR_TO_MAP_VALUE &&
 12385				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12386					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
 12387					return -EINVAL;
 12388				}
 12389				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
 12390					verbose(env, "allocated object must be referenced\n");
 12391					return -EINVAL;
 12392				}
 12393				ret = process_kf_arg_ptr_to_rbtree_root(env, reg, regno, meta);
 12394				if (ret < 0)
 12395					return ret;
 12396				break;
 12397			case KF_ARG_PTR_TO_LIST_NODE:
 12398				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12399					verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12400					return -EINVAL;
 12401				}
 12402				if (!reg->ref_obj_id) {
 12403					verbose(env, "allocated object must be referenced\n");
 12404					return -EINVAL;
 12405				}
 12406				ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta);
 12407				if (ret < 0)
 12408					return ret;
 12409				break;
 12410			case KF_ARG_PTR_TO_RB_NODE:
 12411				if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
 12412					if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12413						verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12414						return -EINVAL;
 12415					}
 12416					if (!reg->ref_obj_id) {
 12417						verbose(env, "allocated object must be referenced\n");
 12418						return -EINVAL;
 12419					}
 12420				} else {
 12421					if (!type_is_non_owning_ref(reg->type) && !reg->ref_obj_id) {
 12422						verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n", func_name);
 12423						return -EINVAL;
 12424					}
 12425					if (in_rbtree_lock_required_cb(env)) {
 12426						verbose(env, "%s not allowed in rbtree cb\n", func_name);
 12427						return -EINVAL;
 12428					}
 12429				}
 12430	
 12431				ret = process_kf_arg_ptr_to_rbtree_node(env, reg, regno, meta);
 12432				if (ret < 0)
 12433					return ret;
 12434				break;
 12435			case KF_ARG_PTR_TO_MAP:
 12436				/* If argument has '__map' suffix expect 'struct bpf_map *' */
 12437				ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
 12438				ref_t = btf_type_by_id(btf_vmlinux, ref_id);
 12439				ref_tname = btf_name_by_offset(btf, ref_t->name_off);
 12440				fallthrough;
 12441			case KF_ARG_PTR_TO_BTF_ID:
 12442				/* Only base_type is checked, further checks are done here */
 12443				if ((base_type(reg->type) != PTR_TO_BTF_ID ||
 12444				     (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
 12445				    !reg2btf_ids[base_type(reg->type)]) {
 12446					verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
 12447					verbose(env, "expected %s or socket\n",
 12448						reg_type_str(env, base_type(reg->type) |
 12449								  (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
 12450					return -EINVAL;
 12451				}
 12452				ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
 12453				if (ret < 0)
 12454					return ret;
 12455				break;
 12456			case KF_ARG_PTR_TO_MEM:
 12457				resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
 12458				if (IS_ERR(resolve_ret)) {
 12459					verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
 12460						i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
 12461					return -EINVAL;
 12462				}
 12463				ret = check_mem_reg(env, reg, regno, type_size);
 12464				if (ret < 0)
 12465					return ret;
 12466				break;
 12467			case KF_ARG_PTR_TO_MEM_SIZE:
 12468			{
 12469				struct bpf_reg_state *buff_reg = &regs[regno];
 12470				const struct btf_param *buff_arg = &args[i];
 12471				struct bpf_reg_state *size_reg = &regs[regno + 1];
 12472				const struct btf_param *size_arg = &args[i + 1];
 12473	
 12474				if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
 12475					ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
 12476					if (ret < 0) {
 12477						verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
 12478						return ret;
 12479					}
 12480				}
 12481	
 12482				if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
 12483					if (meta->arg_constant.found) {
 12484						verifier_bug(env, "only one constant argument permitted");
 12485						return -EFAULT;
 12486					}
 12487					if (!tnum_is_const(size_reg->var_off)) {
 12488						verbose(env, "R%d must be a known constant\n", regno + 1);
 12489						return -EINVAL;
 12490					}
 12491					meta->arg_constant.found = true;
 12492					meta->arg_constant.value = size_reg->var_off.value;
 12493				}
 12494	
 12495				/* Skip next '__sz' or '__szk' argument */
 12496				i++;
 12497				break;
 12498			}
 12499			case KF_ARG_PTR_TO_CALLBACK:
 12500				if (reg->type != PTR_TO_FUNC) {
 12501					verbose(env, "arg%d expected pointer to func\n", i);
 12502					return -EINVAL;
 12503				}
 12504				meta->subprogno = reg->subprogno;
 12505				break;
 12506			case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
 12507				if (!type_is_ptr_alloc_obj(reg->type)) {
 12508					verbose(env, "arg#%d is neither owning or non-owning ref\n", i);
 12509					return -EINVAL;
 12510				}
 12511				if (!type_is_non_owning_ref(reg->type))
 12512					meta->arg_owning_ref = true;
 12513	
 12514				rec = reg_btf_record(reg);
 12515				if (!rec) {
 12516					verifier_bug(env, "Couldn't find btf_record");
 12517					return -EFAULT;
 12518				}
 12519	
 12520				if (rec->refcount_off < 0) {
 12521					verbose(env, "arg#%d doesn't point to a type with bpf_refcount field\n", i);
 12522					return -EINVAL;
 12523				}
 12524	
 12525				meta->arg_btf = reg->btf;
 12526				meta->arg_btf_id = reg->btf_id;
 12527				break;
 12528			case KF_ARG_PTR_TO_CONST_STR:
 12529				if (reg->type != PTR_TO_MAP_VALUE) {
 12530					verbose(env, "arg#%d doesn't point to a const string\n", i);
 12531					return -EINVAL;
 12532				}
 12533				ret = check_reg_const_str(env, reg, regno);
 12534				if (ret)
 12535					return ret;
 12536				break;
 12537			case KF_ARG_PTR_TO_WORKQUEUE:
 12538				if (reg->type != PTR_TO_MAP_VALUE) {
 12539					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12540					return -EINVAL;
 12541				}
 12542				ret = check_map_field_pointer(env, regno, BPF_WORKQUEUE, &meta->map);
 12543				if (ret < 0)
 12544					return ret;
 12545				break;
 12546			case KF_ARG_PTR_TO_TIMER:
 12547				if (reg->type != PTR_TO_MAP_VALUE) {
 12548					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12549					return -EINVAL;
 12550				}
 12551				ret = process_timer_kfunc(env, regno, meta);
 12552				if (ret < 0)
 12553					return ret;
 12554				break;
 12555			case KF_ARG_PTR_TO_TASK_WORK:
 12556				if (reg->type != PTR_TO_MAP_VALUE) {
 12557					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12558					return -EINVAL;
 12559				}
 12560				ret = check_map_field_pointer(env, regno, BPF_TASK_WORK, &meta->map);
 12561				if (ret < 0)
 12562					return ret;
 12563				break;
 12564			case KF_ARG_PTR_TO_IRQ_FLAG:
 12565				if (reg->type != PTR_TO_STACK) {
 12566					verbose(env, "arg#%d doesn't point to an irq flag on stack\n", i);
 12567					return -EINVAL;
 12568				}
 12569				ret = process_irq_flag(env, regno, meta);
 12570				if (ret < 0)
 12571					return ret;
 12572				break;
 12573			case KF_ARG_PTR_TO_RES_SPIN_LOCK:
 12574			{
 12575				int flags = PROCESS_RES_LOCK;
 12576	
 12577				if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12578					verbose(env, "arg#%d doesn't point to map value or allocated object\n", i);
 12579					return -EINVAL;
 12580				}
 12581	
 12582				if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
 12583					return -EFAULT;
 12584				if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
 12585				    meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
 12586					flags |= PROCESS_SPIN_LOCK;
 12587				if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
 12588				    meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
 12589					flags |= PROCESS_LOCK_IRQ;
 12590				ret = process_spin_lock(env, regno, flags);
 12591				if (ret < 0)
 12592					return ret;
 12593				break;
 12594			}
 12595			}
 12596		}
 12597	
 12598		if (is_kfunc_release(meta) && !meta->release_regno) {
 12599			verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
 12600				func_name);
 12601			return -EINVAL;
 12602		}
 12603	
 12604		return 0;
 12605	}
 12606	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
                               ` (2 preceding siblings ...)
  2026-06-02 17:36             ` kernel test robot
@ 2026-06-02 18:37             ` kernel test robot
  2026-06-05  1:29             ` Eduard Zingerman
  4 siblings, 0 replies; 35+ messages in thread
From: kernel test robot @ 2026-06-02 18:37 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: oe-kbuild-all, andrii, ast, bot+bpf-ci, bpf, chenyuan,
	chenyuan_fl, clm, daniel, ihor.solodrai, jolsa, linux-kernel,
	martin.lau, martin.lau, memxor, song

Hi Yuan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf/master]

url:    https://github.com/intel-lab-lkp/linux/commits/chenyuan_fl-163-com/bpf-Fix-kfunc-implicit-arg-inject-type-detection-to-prevent-invalid-pointer-deref/20260602-175420
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git master
patch link:    https://lore.kernel.org/r/20260602093836.2632714-3-chenyuan_fl%40163.com
patch subject: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
config: x86_64-rhel-9.4 (https://download.01.org/0day-ci/archive/20260602/202606022025.lDSZsxCa-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260602/202606022025.lDSZsxCa-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606022025.lDSZsxCa-lkp@intel.com/

All errors (new ones prefixed by >>):

   kernel/bpf/verifier.c: In function 'check_kfunc_args':
>> kernel/bpf/verifier.c:12097:33: error: implicit declaration of function 'reg_arg_name' [-Wimplicit-function-declaration]
   12097 |                                 reg_arg_name(env, argno));
         |                                 ^~~~~~~~~~~~
>> kernel/bpf/verifier.c:12097:51: error: 'argno' undeclared (first use in this function)
   12097 |                                 reg_arg_name(env, argno));
         |                                                   ^~~~~
   kernel/bpf/verifier.c:12097:51: note: each undeclared identifier is reported only once for each function it appears in


vim +/reg_arg_name +12097 kernel/bpf/verifier.c

 12044	
 12045	static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
 12046				    int insn_idx)
 12047	{
 12048		const char *func_name = meta->func_name, *ref_tname;
 12049		const struct btf *btf = meta->btf;
 12050		const struct btf_param *args;
 12051		struct btf_record *rec;
 12052		u32 i, nargs;
 12053		int ret;
 12054	
 12055		args = (const struct btf_param *)(meta->func_proto + 1);
 12056		nargs = btf_type_vlen(meta->func_proto);
 12057		if (nargs > MAX_BPF_FUNC_REG_ARGS) {
 12058			verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
 12059				MAX_BPF_FUNC_REG_ARGS);
 12060			return -EINVAL;
 12061		}
 12062	
 12063		/* Check that BTF function arguments match actual types that the
 12064		 * verifier sees.
 12065		 */
 12066		for (i = 0; i < nargs; i++) {
 12067			struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[i + 1];
 12068			const struct btf_type *t, *ref_t, *resolve_ret;
 12069			enum bpf_arg_type arg_type = ARG_DONTCARE;
 12070			u32 regno = i + 1, ref_id, type_size;
 12071			bool is_ret_buf_sz = false;
 12072			int kf_arg_type;
 12073	
 12074			if (is_kfunc_arg_prog_aux(btf, &args[i])) {
 12075				/* Reject repeated use bpf_prog_aux */
 12076				if (meta->arg_prog) {
 12077					verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
 12078					return -EFAULT;
 12079				}
 12080				meta->arg_prog = true;
 12081				cur_aux(env)->arg_prog = regno;
 12082				continue;
 12083			}
 12084	
 12085			if (is_kfunc_arg_ignore(btf, &args[i]))
 12086				continue;
 12087	
 12088			if (is_kfunc_arg_implicit(meta, i)) {
 12089				/* list_push / rbtree_add kfuncs have implicit args
 12090				 * (e.g. 'off' parameter) handled during verification
 12091				 * in bpf_fixup_kfunc_call().  Don't flag them.
 12092				 */
 12093				if (is_bpf_list_push_kfunc(meta->func_id) ||
 12094				    is_bpf_rbtree_add_kfunc(meta->func_id))
 12095					continue;
 12096				verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
 12097					reg_arg_name(env, argno));
 12098				return -EFAULT;
 12099			}
 12100	
 12101			t = btf_type_skip_modifiers(btf, args[i].type, NULL);
 12102	
 12103			if (btf_type_is_scalar(t)) {
 12104				if (reg->type != SCALAR_VALUE) {
 12105					verbose(env, "R%d is not a scalar\n", regno);
 12106					return -EINVAL;
 12107				}
 12108	
 12109				if (is_kfunc_arg_constant(meta->btf, &args[i])) {
 12110					if (meta->arg_constant.found) {
 12111						verifier_bug(env, "only one constant argument permitted");
 12112						return -EFAULT;
 12113					}
 12114					if (!tnum_is_const(reg->var_off)) {
 12115						verbose(env, "R%d must be a known constant\n", regno);
 12116						return -EINVAL;
 12117					}
 12118					ret = mark_chain_precision(env, regno);
 12119					if (ret < 0)
 12120						return ret;
 12121					meta->arg_constant.found = true;
 12122					meta->arg_constant.value = reg->var_off.value;
 12123				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
 12124					meta->r0_rdonly = true;
 12125					is_ret_buf_sz = true;
 12126				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
 12127					is_ret_buf_sz = true;
 12128				}
 12129	
 12130				if (is_ret_buf_sz) {
 12131					if (meta->r0_size) {
 12132						verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
 12133						return -EINVAL;
 12134					}
 12135	
 12136					if (!tnum_is_const(reg->var_off)) {
 12137						verbose(env, "R%d is not a const\n", regno);
 12138						return -EINVAL;
 12139					}
 12140	
 12141					meta->r0_size = reg->var_off.value;
 12142					ret = mark_chain_precision(env, regno);
 12143					if (ret)
 12144						return ret;
 12145				}
 12146				continue;
 12147			}
 12148	
 12149			if (!btf_type_is_ptr(t)) {
 12150				verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t));
 12151				return -EINVAL;
 12152			}
 12153	
 12154			if ((bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
 12155			    !is_kfunc_arg_nullable(meta->btf, &args[i])) {
 12156				verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
 12157				return -EACCES;
 12158			}
 12159	
 12160			if (reg->ref_obj_id) {
 12161				if (is_kfunc_release(meta) && meta->ref_obj_id) {
 12162					verifier_bug(env, "more than one arg with ref_obj_id R%d %u %u",
 12163						     regno, reg->ref_obj_id,
 12164						     meta->ref_obj_id);
 12165					return -EFAULT;
 12166				}
 12167				meta->ref_obj_id = reg->ref_obj_id;
 12168				if (is_kfunc_release(meta))
 12169					meta->release_regno = regno;
 12170			}
 12171	
 12172			ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
 12173			ref_tname = btf_name_by_offset(btf, ref_t->name_off);
 12174	
 12175			kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs);
 12176			if (kf_arg_type < 0)
 12177				return kf_arg_type;
 12178	
 12179			switch (kf_arg_type) {
 12180			case KF_ARG_PTR_TO_NULL:
 12181				continue;
 12182			case KF_ARG_PTR_TO_MAP:
 12183				if (!reg->map_ptr) {
 12184					verbose(env, "pointer in R%d isn't map pointer\n", regno);
 12185					return -EINVAL;
 12186				}
 12187				if (meta->map.ptr && (reg->map_ptr->record->wq_off >= 0 ||
 12188						      reg->map_ptr->record->task_work_off >= 0)) {
 12189					/* Use map_uid (which is unique id of inner map) to reject:
 12190					 * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
 12191					 * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
 12192					 * if (inner_map1 && inner_map2) {
 12193					 *     wq = bpf_map_lookup_elem(inner_map1);
 12194					 *     if (wq)
 12195					 *         // mismatch would have been allowed
 12196					 *         bpf_wq_init(wq, inner_map2);
 12197					 * }
 12198					 *
 12199					 * Comparing map_ptr is enough to distinguish normal and outer maps.
 12200					 */
 12201					if (meta->map.ptr != reg->map_ptr ||
 12202					    meta->map.uid != reg->map_uid) {
 12203						if (reg->map_ptr->record->task_work_off >= 0) {
 12204							verbose(env,
 12205								"bpf_task_work pointer in R2 map_uid=%d doesn't match map pointer in R3 map_uid=%d\n",
 12206								meta->map.uid, reg->map_uid);
 12207							return -EINVAL;
 12208						}
 12209						verbose(env,
 12210							"workqueue pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n",
 12211							meta->map.uid, reg->map_uid);
 12212						return -EINVAL;
 12213					}
 12214				}
 12215				meta->map.ptr = reg->map_ptr;
 12216				meta->map.uid = reg->map_uid;
 12217				fallthrough;
 12218			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
 12219			case KF_ARG_PTR_TO_BTF_ID:
 12220				if (!is_trusted_reg(reg)) {
 12221					if (!is_kfunc_rcu(meta)) {
 12222						verbose(env, "R%d must be referenced or trusted\n", regno);
 12223						return -EINVAL;
 12224					}
 12225					if (!is_rcu_reg(reg)) {
 12226						verbose(env, "R%d must be a rcu pointer\n", regno);
 12227						return -EINVAL;
 12228					}
 12229				}
 12230				fallthrough;
 12231			case KF_ARG_PTR_TO_DYNPTR:
 12232			case KF_ARG_PTR_TO_ITER:
 12233			case KF_ARG_PTR_TO_LIST_HEAD:
 12234			case KF_ARG_PTR_TO_LIST_NODE:
 12235			case KF_ARG_PTR_TO_RB_ROOT:
 12236			case KF_ARG_PTR_TO_RB_NODE:
 12237			case KF_ARG_PTR_TO_MEM:
 12238			case KF_ARG_PTR_TO_MEM_SIZE:
 12239			case KF_ARG_PTR_TO_CALLBACK:
 12240			case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
 12241			case KF_ARG_PTR_TO_CONST_STR:
 12242			case KF_ARG_PTR_TO_WORKQUEUE:
 12243			case KF_ARG_PTR_TO_TIMER:
 12244			case KF_ARG_PTR_TO_TASK_WORK:
 12245			case KF_ARG_PTR_TO_IRQ_FLAG:
 12246			case KF_ARG_PTR_TO_RES_SPIN_LOCK:
 12247				break;
 12248			case KF_ARG_PTR_TO_CTX:
 12249				arg_type = ARG_PTR_TO_CTX;
 12250				break;
 12251			default:
 12252				verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
 12253				return -EFAULT;
 12254			}
 12255	
 12256			if (is_kfunc_release(meta) && reg->ref_obj_id)
 12257				arg_type |= OBJ_RELEASE;
 12258			ret = check_func_arg_reg_off(env, reg, regno, arg_type);
 12259			if (ret < 0)
 12260				return ret;
 12261	
 12262			switch (kf_arg_type) {
 12263			case KF_ARG_PTR_TO_CTX:
 12264				if (reg->type != PTR_TO_CTX) {
 12265					verbose(env, "arg#%d expected pointer to ctx, but got %s\n",
 12266						i, reg_type_str(env, reg->type));
 12267					return -EINVAL;
 12268				}
 12269	
 12270				if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
 12271					ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
 12272					if (ret < 0)
 12273						return -EINVAL;
 12274					meta->ret_btf_id  = ret;
 12275				}
 12276				break;
 12277			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
 12278				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12279					if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
 12280						verbose(env, "arg#%d expected for bpf_obj_drop()\n", i);
 12281						return -EINVAL;
 12282					}
 12283				} else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
 12284					if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
 12285						verbose(env, "arg#%d expected for bpf_percpu_obj_drop()\n", i);
 12286						return -EINVAL;
 12287					}
 12288				} else {
 12289					verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12290					return -EINVAL;
 12291				}
 12292				if (!reg->ref_obj_id) {
 12293					verbose(env, "allocated object must be referenced\n");
 12294					return -EINVAL;
 12295				}
 12296				if (meta->btf == btf_vmlinux) {
 12297					meta->arg_btf = reg->btf;
 12298					meta->arg_btf_id = reg->btf_id;
 12299				}
 12300				break;
 12301			case KF_ARG_PTR_TO_DYNPTR:
 12302			{
 12303				enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
 12304				int clone_ref_obj_id = 0;
 12305	
 12306				if (reg->type == CONST_PTR_TO_DYNPTR)
 12307					dynptr_arg_type |= MEM_RDONLY;
 12308	
 12309				if (is_kfunc_arg_uninit(btf, &args[i]))
 12310					dynptr_arg_type |= MEM_UNINIT;
 12311	
 12312				if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) {
 12313					dynptr_arg_type |= DYNPTR_TYPE_SKB;
 12314				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) {
 12315					dynptr_arg_type |= DYNPTR_TYPE_XDP;
 12316				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_meta]) {
 12317					dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
 12318				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_file]) {
 12319					dynptr_arg_type |= DYNPTR_TYPE_FILE;
 12320				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_file_discard]) {
 12321					dynptr_arg_type |= DYNPTR_TYPE_FILE;
 12322					meta->release_regno = regno;
 12323				} else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
 12324					   (dynptr_arg_type & MEM_UNINIT)) {
 12325					enum bpf_dynptr_type parent_type = meta->initialized_dynptr.type;
 12326	
 12327					if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
 12328						verifier_bug(env, "no dynptr type for parent of clone");
 12329						return -EFAULT;
 12330					}
 12331	
 12332					dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
 12333					clone_ref_obj_id = meta->initialized_dynptr.ref_obj_id;
 12334					if (dynptr_type_refcounted(parent_type) && !clone_ref_obj_id) {
 12335						verifier_bug(env, "missing ref obj id for parent of clone");
 12336						return -EFAULT;
 12337					}
 12338				}
 12339	
 12340				ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id);
 12341				if (ret < 0)
 12342					return ret;
 12343	
 12344				if (!(dynptr_arg_type & MEM_UNINIT)) {
 12345					int id = dynptr_id(env, reg);
 12346	
 12347					if (id < 0) {
 12348						verifier_bug(env, "failed to obtain dynptr id");
 12349						return id;
 12350					}
 12351					meta->initialized_dynptr.id = id;
 12352					meta->initialized_dynptr.type = dynptr_get_type(env, reg);
 12353					meta->initialized_dynptr.ref_obj_id = dynptr_ref_obj_id(env, reg);
 12354				}
 12355	
 12356				break;
 12357			}
 12358			case KF_ARG_PTR_TO_ITER:
 12359				if (meta->func_id == special_kfunc_list[KF_bpf_iter_css_task_new]) {
 12360					if (!check_css_task_iter_allowlist(env)) {
 12361						verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
 12362						return -EINVAL;
 12363					}
 12364				}
 12365				ret = process_iter_arg(env, regno, insn_idx, meta);
 12366				if (ret < 0)
 12367					return ret;
 12368				break;
 12369			case KF_ARG_PTR_TO_LIST_HEAD:
 12370				if (reg->type != PTR_TO_MAP_VALUE &&
 12371				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12372					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
 12373					return -EINVAL;
 12374				}
 12375				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
 12376					verbose(env, "allocated object must be referenced\n");
 12377					return -EINVAL;
 12378				}
 12379				ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta);
 12380				if (ret < 0)
 12381					return ret;
 12382				break;
 12383			case KF_ARG_PTR_TO_RB_ROOT:
 12384				if (reg->type != PTR_TO_MAP_VALUE &&
 12385				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12386					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
 12387					return -EINVAL;
 12388				}
 12389				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
 12390					verbose(env, "allocated object must be referenced\n");
 12391					return -EINVAL;
 12392				}
 12393				ret = process_kf_arg_ptr_to_rbtree_root(env, reg, regno, meta);
 12394				if (ret < 0)
 12395					return ret;
 12396				break;
 12397			case KF_ARG_PTR_TO_LIST_NODE:
 12398				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12399					verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12400					return -EINVAL;
 12401				}
 12402				if (!reg->ref_obj_id) {
 12403					verbose(env, "allocated object must be referenced\n");
 12404					return -EINVAL;
 12405				}
 12406				ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta);
 12407				if (ret < 0)
 12408					return ret;
 12409				break;
 12410			case KF_ARG_PTR_TO_RB_NODE:
 12411				if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
 12412					if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12413						verbose(env, "arg#%d expected pointer to allocated object\n", i);
 12414						return -EINVAL;
 12415					}
 12416					if (!reg->ref_obj_id) {
 12417						verbose(env, "allocated object must be referenced\n");
 12418						return -EINVAL;
 12419					}
 12420				} else {
 12421					if (!type_is_non_owning_ref(reg->type) && !reg->ref_obj_id) {
 12422						verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n", func_name);
 12423						return -EINVAL;
 12424					}
 12425					if (in_rbtree_lock_required_cb(env)) {
 12426						verbose(env, "%s not allowed in rbtree cb\n", func_name);
 12427						return -EINVAL;
 12428					}
 12429				}
 12430	
 12431				ret = process_kf_arg_ptr_to_rbtree_node(env, reg, regno, meta);
 12432				if (ret < 0)
 12433					return ret;
 12434				break;
 12435			case KF_ARG_PTR_TO_MAP:
 12436				/* If argument has '__map' suffix expect 'struct bpf_map *' */
 12437				ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
 12438				ref_t = btf_type_by_id(btf_vmlinux, ref_id);
 12439				ref_tname = btf_name_by_offset(btf, ref_t->name_off);
 12440				fallthrough;
 12441			case KF_ARG_PTR_TO_BTF_ID:
 12442				/* Only base_type is checked, further checks are done here */
 12443				if ((base_type(reg->type) != PTR_TO_BTF_ID ||
 12444				     (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
 12445				    !reg2btf_ids[base_type(reg->type)]) {
 12446					verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
 12447					verbose(env, "expected %s or socket\n",
 12448						reg_type_str(env, base_type(reg->type) |
 12449								  (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
 12450					return -EINVAL;
 12451				}
 12452				ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
 12453				if (ret < 0)
 12454					return ret;
 12455				break;
 12456			case KF_ARG_PTR_TO_MEM:
 12457				resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
 12458				if (IS_ERR(resolve_ret)) {
 12459					verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
 12460						i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
 12461					return -EINVAL;
 12462				}
 12463				ret = check_mem_reg(env, reg, regno, type_size);
 12464				if (ret < 0)
 12465					return ret;
 12466				break;
 12467			case KF_ARG_PTR_TO_MEM_SIZE:
 12468			{
 12469				struct bpf_reg_state *buff_reg = &regs[regno];
 12470				const struct btf_param *buff_arg = &args[i];
 12471				struct bpf_reg_state *size_reg = &regs[regno + 1];
 12472				const struct btf_param *size_arg = &args[i + 1];
 12473	
 12474				if (!bpf_register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) {
 12475					ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
 12476					if (ret < 0) {
 12477						verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
 12478						return ret;
 12479					}
 12480				}
 12481	
 12482				if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
 12483					if (meta->arg_constant.found) {
 12484						verifier_bug(env, "only one constant argument permitted");
 12485						return -EFAULT;
 12486					}
 12487					if (!tnum_is_const(size_reg->var_off)) {
 12488						verbose(env, "R%d must be a known constant\n", regno + 1);
 12489						return -EINVAL;
 12490					}
 12491					meta->arg_constant.found = true;
 12492					meta->arg_constant.value = size_reg->var_off.value;
 12493				}
 12494	
 12495				/* Skip next '__sz' or '__szk' argument */
 12496				i++;
 12497				break;
 12498			}
 12499			case KF_ARG_PTR_TO_CALLBACK:
 12500				if (reg->type != PTR_TO_FUNC) {
 12501					verbose(env, "arg%d expected pointer to func\n", i);
 12502					return -EINVAL;
 12503				}
 12504				meta->subprogno = reg->subprogno;
 12505				break;
 12506			case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
 12507				if (!type_is_ptr_alloc_obj(reg->type)) {
 12508					verbose(env, "arg#%d is neither owning or non-owning ref\n", i);
 12509					return -EINVAL;
 12510				}
 12511				if (!type_is_non_owning_ref(reg->type))
 12512					meta->arg_owning_ref = true;
 12513	
 12514				rec = reg_btf_record(reg);
 12515				if (!rec) {
 12516					verifier_bug(env, "Couldn't find btf_record");
 12517					return -EFAULT;
 12518				}
 12519	
 12520				if (rec->refcount_off < 0) {
 12521					verbose(env, "arg#%d doesn't point to a type with bpf_refcount field\n", i);
 12522					return -EINVAL;
 12523				}
 12524	
 12525				meta->arg_btf = reg->btf;
 12526				meta->arg_btf_id = reg->btf_id;
 12527				break;
 12528			case KF_ARG_PTR_TO_CONST_STR:
 12529				if (reg->type != PTR_TO_MAP_VALUE) {
 12530					verbose(env, "arg#%d doesn't point to a const string\n", i);
 12531					return -EINVAL;
 12532				}
 12533				ret = check_reg_const_str(env, reg, regno);
 12534				if (ret)
 12535					return ret;
 12536				break;
 12537			case KF_ARG_PTR_TO_WORKQUEUE:
 12538				if (reg->type != PTR_TO_MAP_VALUE) {
 12539					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12540					return -EINVAL;
 12541				}
 12542				ret = check_map_field_pointer(env, regno, BPF_WORKQUEUE, &meta->map);
 12543				if (ret < 0)
 12544					return ret;
 12545				break;
 12546			case KF_ARG_PTR_TO_TIMER:
 12547				if (reg->type != PTR_TO_MAP_VALUE) {
 12548					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12549					return -EINVAL;
 12550				}
 12551				ret = process_timer_kfunc(env, regno, meta);
 12552				if (ret < 0)
 12553					return ret;
 12554				break;
 12555			case KF_ARG_PTR_TO_TASK_WORK:
 12556				if (reg->type != PTR_TO_MAP_VALUE) {
 12557					verbose(env, "arg#%d doesn't point to a map value\n", i);
 12558					return -EINVAL;
 12559				}
 12560				ret = check_map_field_pointer(env, regno, BPF_TASK_WORK, &meta->map);
 12561				if (ret < 0)
 12562					return ret;
 12563				break;
 12564			case KF_ARG_PTR_TO_IRQ_FLAG:
 12565				if (reg->type != PTR_TO_STACK) {
 12566					verbose(env, "arg#%d doesn't point to an irq flag on stack\n", i);
 12567					return -EINVAL;
 12568				}
 12569				ret = process_irq_flag(env, regno, meta);
 12570				if (ret < 0)
 12571					return ret;
 12572				break;
 12573			case KF_ARG_PTR_TO_RES_SPIN_LOCK:
 12574			{
 12575				int flags = PROCESS_RES_LOCK;
 12576	
 12577				if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
 12578					verbose(env, "arg#%d doesn't point to map value or allocated object\n", i);
 12579					return -EINVAL;
 12580				}
 12581	
 12582				if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
 12583					return -EFAULT;
 12584				if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
 12585				    meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
 12586					flags |= PROCESS_SPIN_LOCK;
 12587				if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
 12588				    meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
 12589					flags |= PROCESS_LOCK_IRQ;
 12590				ret = process_spin_lock(env, regno, flags);
 12591				if (ret < 0)
 12592					return ret;
 12593				break;
 12594			}
 12595			}
 12596		}
 12597	
 12598		if (is_kfunc_release(meta) && !meta->release_regno) {
 12599			verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
 12600				func_name);
 12601			return -EINVAL;
 12602		}
 12603	
 12604		return 0;
 12605	}
 12606	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  9:23             ` sashiko-bot
  2026-06-02  9:44             ` bot+bpf-ci
@ 2026-06-02 18:52             ` Ihor Solodrai
  2026-06-04  9:14               ` chenyuan
  2 siblings, 1 reply; 35+ messages in thread
From: Ihor Solodrai @ 2026-06-02 18:52 UTC (permalink / raw)
  To: chenyuan_fl, eddyz87, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, clm, daniel, jolsa,
	linux-kernel, martin.lau, martin.lau, memxor, song

On 6/2/26 1:58 AM, 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,

Hi Yuan,

Could you please elaborate on the "incorrect type information" generated
by pahole 1.30?

My understanding is, the symptom of the problem you're trying to fix is
that module BTF with custom kfuncs with KF_IMPLICIT_ARGS ends up with a
copy of an arg type, instead of referencing the BTF ID in kernel BTF.

Do you know how this happens? Is it a bug in pahole or in resolve_btfids?

I have a suspicion we might not need changes in the verifier to fix this.
Might be wrong of course, would appreciate a bit more details.

> 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,

Dedup happens in pahole, but distill_base is done in resolve_btfids.
I think distill_base is supposed to remove the (copy of) target type from
module BTF. Can you confirm that it doesn't?

> 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.  Recognized cases:
> 
>   - struct bpf_prog_aux *  : is_kfunc_arg_prog_aux()
>   - __ign suffix args      : is_kfunc_arg_ignore()
>   - list_push/rbtree_add   : is_bpf_list_push_kfunc() / is_bpf_rbtree_add_kfunc()
> 
> 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 | 15 ++++++++++++++-
>  1 file changed, 14 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8dd79b735a69..55c74d064e4e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -11916,9 +11916,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */
> +			if (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)) {


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re:Re: [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02 18:52             ` Ihor Solodrai
@ 2026-06-04  9:14               ` chenyuan
  2026-06-04 10:21                 ` Alan Maguire
  0 siblings, 1 reply; 35+ messages in thread
From: chenyuan @ 2026-06-04  9:14 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: eddyz87, yonghong.song, andrii, ast, bot+bpf-ci, bpf, chenyuan,
	clm, daniel, jolsa, linux-kernel, martin.lau, martin.lau, memxor,
	song

Hi Ihor:
> Could you please elaborate on the "incorrect type information" generated 
by pahole 1.30?
I extracted the .BTF and .BTF.base sections from the same 
bpf_testmod.ko built with pahole 1.30 vs 1.31 to compare: 

  # pahole 1.30 — bpf_prog_aux in split BTF, NOT in base
  $ bpftool btf dump file btf_1_30_base.bin | grep bpf_prog_aux
    (empty)
  $ bpftool btf dump -B btf_1_30_base.bin file btf_1_30.bin | grep aux
    [3172] STRUCT 'bpf_prog_aux' size=2016 vlen=87

  # pahole 1.31 — bpf_prog_aux in base BTF, NOT in split
  $ bpftool btf dump file btf_1_31_base.bin | grep bpf_prog_aux
    [10] STRUCT 'bpf_prog_aux' size=2016 vlen=0
  $ bpftool btf dump -B btf_1_31_base.bin file btf_1_31.bin | grep aux 
    (empty) 

pahole 1.30 places struct bpf_prog_aux in the module's split BTF 
(instead of the distilled base).  pahole 1.31 correctly moves it to 
the base BTF. 

> Do you know how this happens? Is it a bug in pahole or in resolve_btfids?
It's a pahole issue.  pahole's BTF encoder decides which types go into        
.BTF.base (distilled base) vs .BTF (split).  In 1.30, types
referenced by kfunc declarations were not being correctly identified as
vmlinux types for the distilled base, so they ended up as "new" types in
the split BTF instead. 

> I have a suspicion we might not need changes in the verifier to fix this. 
The kernel's BTF dedup during module loading only operates on types in
the distilled base (.BTF.base), merging them with vmlinux BTF.
Types in the split BTF (.BTF) are treated as module-local and
assigned fresh IDs — they are never matched against vmlinux. 

This means when a toolchain bug puts a vmlinux type into split BTF 
instead of the base , the kernel has no mechanism to detect or fix it.
                                                        
btf_types_are_same() compares pointers across two different BTF                                                                        
objects — always fails for this case.  is_kfunc_arg_prog_aux()                                                                         
returns false, the argument is silently skipped, and the kfunc                                                                         
dereferences uninitialized register state.                                                                                             
                                                                                                                                       
While pahole 1.31 fixes the root cause, the verifier change is a
safety net: a crash becomes a clean rejection regardless of which
toolchain component introduces a BTF mismatch.  This is defense-in-depth, 
not a replacement  for the toolchain fix. 


At 2026-06-03 02:52:39, "Ihor Solodrai" <ihor.solodrai@linux.dev> wrote:
>On 6/2/26 1:58 AM, 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,
>
>Hi Yuan,
>
>Could you please elaborate on the "incorrect type information" generated
>by pahole 1.30?
>
>My understanding is, the symptom of the problem you're trying to fix is
>that module BTF with custom kfuncs with KF_IMPLICIT_ARGS ends up with a
>copy of an arg type, instead of referencing the BTF ID in kernel BTF.
>
>Do you know how this happens? Is it a bug in pahole or in resolve_btfids?
>
>I have a suspicion we might not need changes in the verifier to fix this.
>Might be wrong of course, would appreciate a bit more details.
>
>> 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,
>
>Dedup happens in pahole, but distill_base is done in resolve_btfids.
>I think distill_base is supposed to remove the (copy of) target type from
>module BTF. Can you confirm that it doesn't?
>
>> 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.  Recognized cases:
>> 
>>   - struct bpf_prog_aux *  : is_kfunc_arg_prog_aux()
>>   - __ign suffix args      : is_kfunc_arg_ignore()
>>   - list_push/rbtree_add   : is_bpf_list_push_kfunc() / is_bpf_rbtree_add_kfunc()
>> 
>> 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 | 15 ++++++++++++++-
>>  1 file changed, 14 insertions(+), 1 deletion(-)
>> 
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 8dd79b735a69..55c74d064e4e 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -11916,9 +11916,22 @@ 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)) {
>> +			/* list_push / rbtree_add kfuncs have implicit args
>> +			 * (e.g. 'off' parameter) handled during verification
>> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
>> +			 */
>> +			if (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)) {

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-04  9:14               ` chenyuan
@ 2026-06-04 10:21                 ` Alan Maguire
  0 siblings, 0 replies; 35+ messages in thread
From: Alan Maguire @ 2026-06-04 10:21 UTC (permalink / raw)
  To: chenyuan, Ihor Solodrai
  Cc: eddyz87, yonghong.song, andrii, ast, bot+bpf-ci, bpf, chenyuan,
	clm, daniel, jolsa, linux-kernel, martin.lau, martin.lau, memxor,
	song

On 04/06/2026 10:14, chenyuan wrote:
> Hi Ihor:
>> Could you please elaborate on the "incorrect type information" generated 
> by pahole 1.30?
> I extracted the .BTF and .BTF.base sections from the same 
> bpf_testmod.ko built with pahole 1.30 vs 1.31 to compare: 
> 
>   # pahole 1.30 — bpf_prog_aux in split BTF, NOT in base
>   $ bpftool btf dump file btf_1_30_base.bin | grep bpf_prog_aux
>     (empty)
>   $ bpftool btf dump -B btf_1_30_base.bin file btf_1_30.bin | grep aux
>     [3172] STRUCT 'bpf_prog_aux' size=2016 vlen=87
> 
>   # pahole 1.31 — bpf_prog_aux in base BTF, NOT in split
>   $ bpftool btf dump file btf_1_31_base.bin | grep bpf_prog_aux
>     [10] STRUCT 'bpf_prog_aux' size=2016 vlen=0
>   $ bpftool btf dump -B btf_1_31_base.bin file btf_1_31.bin | grep aux 
>     (empty) 
> 
> pahole 1.30 places struct bpf_prog_aux in the module's split BTF 
> (instead of the distilled base).  pahole 1.31 correctly moves it to 
> the base BTF. 

Would be worth digging a bit more here I think. To confirm is this
the v1.31 release where it is fixed or the post-1.31 pahole (i.e.
do you know the top commit for the fixed pahole version)? 

pahole uses the libbpf machinery to do BTF distillation in .BTF.base
so if we know which libbpf sync has the fix that will be helpful.

The most recent pahole HEAD is synced with libbpf v1.8, but I
suspect the previous sync that occured just after v1.30 (so v1.31
had the fix but v1.30 didn't):

042d73962d35 ("pahole: Sync with libbpf mainline")

is the one. From the commit log:
    
"To pull in dedup fix in
    
commit 8e64c387c942 ("libbpf: Add identical pointer detection to btf_dedup_is_equiv()")
    
sync with latest libbpf."

My suspicion is that since v1.30 didn't have that fix, dedup failed for 
struct bpf_prog_aux on pahole v1.30 so it wound up in split BTF and base.
v1.31 had the fix so the module bpf_prog_aux was correctly flagged as a
dup of the kernel one, therefore we did the distillation into .BTF.base
correctly.

Does this fit with your observations?

Thanks!

Alan

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref
  2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
  2026-06-02  9:58             ` sashiko-bot
  2026-06-02 10:42             ` bot+bpf-ci
@ 2026-06-05  0:42             ` Eduard Zingerman
  2 siblings, 0 replies; 35+ messages in thread
From: Eduard Zingerman @ 2026-06-05  0:42 UTC (permalink / raw)
  To: chenyuan_fl, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

On Tue, 2026-06-02 at 17:38 +0800, chenyuan_fl@163.com wrote:

[...]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8dd79b735a69..55c74d064e4e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -11916,9 +11916,22 @@ 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)) {
> +			/* list_push / rbtree_add kfuncs have implicit args
> +			 * (e.g. 'off' parameter) handled during verification
> +			 * in bpf_fixup_kfunc_call().  Don't flag them.
> +			 */
> +			if (is_bpf_list_push_kfunc(meta->func_id) ||
> +			    is_bpf_rbtree_add_kfunc(meta->func_id))

I took a second look at the verifier.c:bpf_fixup_kfunc_call() and it
appears it handles more functions with implicit args. The following
predicates are used there:
- is_bpf_obj_new_kfunc
- is_bpf_percpu_obj_new_kfunc
- is_bpf_obj_drop_kfunc
- is_bpf_percpu_obj_drop_kfunc
- is_bpf_refcount_acquire_kfunc
- is_bpf_list_push_kfunc
- is_bpf_rbtree_add_kfunc

Could you please extend this check?

> +				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)) {

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection
  2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
                               ` (3 preceding siblings ...)
  2026-06-02 18:37             ` kernel test robot
@ 2026-06-05  1:29             ` Eduard Zingerman
  4 siblings, 0 replies; 35+ messages in thread
From: Eduard Zingerman @ 2026-06-05  1:29 UTC (permalink / raw)
  To: chenyuan_fl, yonghong.song
  Cc: andrii, ast, bot+bpf-ci, bpf, chenyuan, clm, daniel,
	ihor.solodrai, jolsa, linux-kernel, martin.lau, martin.lau,
	memxor, song

On Tue, 2026-06-02 at 17:38 +0800, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
> 
> The preceding patch fixes a silent fallthrough in check_kfunc_args()
> that could cause the verifier to skip bpf_prog_aux injection for
> KF_IMPLICIT_ARGS kfuncs when module BTF is inconsistent with vmlinux
> (e.g. pahole 1.30 breaking distilled base dedup).
> 
> Add a positive regression test that verifies the injection path works
> correctly under normal conditions (pahole 1.31+).  The test contaminates
> BPF R2 with a magic value 0xDEAD via inline assembly before calling a
> KF_IMPLICIT_ARGS kfunc associated with a struct_ops map.  The kfunc
> validates that the kernel overwrote R2 with the real bpf_prog_aux
> pointer rather than leaving the stale value.
> 
> The specific pahole 1.30 BTF mismatch scenario cannot be tested with
> CI (which uses pahole 1.31), but this test ensures the injection
> mechanism remains correct and does not regress.
> 
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---

I think that such test makes sense, it allows to verify if implicit
struct bpf_prog_aux parameter works as expected when kfunc is defined
in a module.

However, I'd suggest to hijack bpf_testmod.c:bpf_kfunc_implicit_arg()
and verify a field like aux->name. Hence making sure that bpf_prog_aux
is passed correctly. E.g.:

  int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
  {
	if (strcmp(aux->name, "...expected name...") == 0 && a > 0)
		return a;
	return -EINVAL;
  }

Thus keeping the changes to a minimum.

[...]

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2026-06-05  1:29 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-07  8:09 [PATCH] bpf: fix btf_types_are_same for cross-BTF type comparison chenyuan_fl
2026-04-07  8:58 ` Leon Hwang
2026-04-07  9:01 ` bot+bpf-ci
2026-04-07 11:19 ` Alan Maguire
2026-05-15 18:27 ` Ihor Solodrai
2026-06-01  6:46 ` [PATCH bpf v2 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
2026-06-01  6:46   ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
2026-06-01  7:42     ` bot+bpf-ci
2026-06-01 19:32       ` Eduard Zingerman
2026-06-02  8:58         ` [PATCH bpf v3 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
2026-06-02  8:58           ` [PATCH bpf v3 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
2026-06-02  9:23             ` sashiko-bot
2026-06-02  9:44             ` bot+bpf-ci
2026-06-02 18:52             ` Ihor Solodrai
2026-06-04  9:14               ` chenyuan
2026-06-04 10:21                 ` Alan Maguire
2026-06-02  8:58           ` [PATCH bpf v3 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
2026-06-02  9:31             ` sashiko-bot
2026-06-02  9:44             ` bot+bpf-ci
2026-06-02  9:38         ` [PATCH bpf v4 0/2] bpf: Fix kfunc implicit arg injection and add selftest chenyuan_fl
2026-06-02  9:38           ` [PATCH bpf v4 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref chenyuan_fl
2026-06-02  9:58             ` sashiko-bot
2026-06-02 10:42             ` bot+bpf-ci
2026-06-05  0:42             ` Eduard Zingerman
2026-06-02  9:38           ` [PATCH bpf v4 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection chenyuan_fl
2026-06-02 10:06             ` sashiko-bot
2026-06-02 10:27             ` bot+bpf-ci
2026-06-02 17:36             ` kernel test robot
2026-06-02 18:37             ` kernel test robot
2026-06-05  1:29             ` Eduard Zingerman
2026-06-01 17:12     ` [PATCH bpf v2 1/2] bpf: Fix kfunc implicit arg inject type detection to prevent invalid pointer deref Yonghong Song
2026-06-01 21:36     ` Eduard Zingerman
2026-06-01  6:46   ` [PATCH bpf v2 2/2] selftests/bpf: Add regression test for kfunc implicit arg injection with stale register chenyuan_fl
2026-06-01  7:08     ` sashiko-bot
2026-06-01 17:17     ` Yonghong Song

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.