From a3863aa4f55e5c17f32e1fd64a0a64adf2af16d9 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Fri, 5 Jun 2026 13:20:52 -0700 Subject: [PATCH 1/5] bpf: Fix dead error check on acquire_reference() in check_kfunc_call acquire_reference() returns a signed int that may be a negative errno but was converted to unsigned, which makes the subsequent error check deadcode. Fix it by declaring 'id' as int so the error path is taken correctly. Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug") Acked-by: Eduard Zingerman Signed-off-by: Amery Hung Link: https://lore.kernel.org/r/20260605202056.1780352-2-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7d27ba396d32..a741bf447931 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12817,9 +12817,10 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, struct bpf_kfunc_call_arg_meta meta; struct bpf_insn_aux_data *insn_aux; int err, insn_idx = *insn_idx_p; - u32 i, nargs, ptr_type_id, id; const struct btf_param *args; + u32 i, nargs, ptr_type_id; struct btf *desc_btf; + int id; /* skip for now, but return error when we find this in fixup_kfunc_call */ if (!insn->imm) From 73d475dc6c13177fce0d9d892bff33299c8ad56a Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Fri, 5 Jun 2026 13:20:53 -0700 Subject: [PATCH 2/5] bpf: Check acquire_reference() error for "__ref" struct_ops arguments When acquiring references for struct_ops program arguments tagged with "__ref", the return value of acquire_reference() was stored directly into u32 ctx_arg_info[i].ref_id without checking for failure. acquire_reference() returns -ENOMEM when acquire_reference_state() fails to allocate, so the error was silently stored as a ref_id instead of aborting verification. Fix it by checking the return. Fixes: a687df2008f6 ("bpf: Support getting referenced kptr from struct_ops argument") Signed-off-by: Amery Hung Link: https://lore.kernel.org/r/20260605202056.1780352-3-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a741bf447931..3b874bbbaac0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -18363,9 +18363,13 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog) /* Acquire references for struct_ops program arguments tagged with "__ref" */ if (!subprog && env->prog->type == BPF_PROG_TYPE_STRUCT_OPS) { - for (i = 0; i < aux->ctx_arg_info_size; i++) - aux->ctx_arg_info[i].ref_id = aux->ctx_arg_info[i].refcounted ? - acquire_reference(env, 0, 0) : 0; + for (i = 0; i < aux->ctx_arg_info_size; i++) { + ret = aux->ctx_arg_info[i].refcounted ? acquire_reference(env, 0, 0) : 0; + if (ret < 0) + goto out; + + aux->ctx_arg_info[i].ref_id = ret; + } } ret = do_check(env); From 41025f441fe6addd93d2c333a3a184331e8ef6cf Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Fri, 5 Jun 2026 13:20:54 -0700 Subject: [PATCH 3/5] bpf: Compare parent_id in refsafe() for REF_TYPE_PTR refsafe() compared each reference's id and type but not its parent_id, so two states whose PTR references differ only in the parent object they were derived from could be wrongly treated as equivalent and pruned. Fix it by checking parent_id too. Fixes: 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug") Signed-off-by: Amery Hung Link: https://lore.kernel.org/r/20260605202056.1780352-4-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/states.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 5945956a7573..06d9ae24f006 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -890,6 +890,9 @@ static bool refsafe(struct bpf_verifier_state *old, struct bpf_verifier_state *c return false; switch (old->refs[i].type) { case REF_TYPE_PTR: + if (!check_ids(old->refs[i].parent_id, cur->refs[i].parent_id, idmap)) + return false; + break; case REF_TYPE_IRQ: break; case REF_TYPE_LOCK: From ac7f6c9da6b6b46bba34a45c51603c81e7d42eb2 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Fri, 5 Jun 2026 13:20:55 -0700 Subject: [PATCH 4/5] bpf: Remove WARN_ON_ONCE in check_ids() check_ids() warned when it ran out of idmap slots, assuming this was impossible because the slots are bounded by the number of registers and stack slots. That assumption no longer holds: referenced dynptrs acquire an intermediate reference that lives in refs[] but is not backed by any register or stack slot [0], so a program can accumulate more reference ids than the idmap can hold and exhaust it. Exhaustion is fine for verification correctness. check_ids() already returns false, which makes the states compare as not equivalent and prevents unsound pruning. The only effect of the WARN_ON_ONCE() is log noise, or a panic under panic_on_warn. Drop the warning and keep returning false. [0] 308c7a0ae885 ("bpf: Refactor object relationship tracking and fix dynptr UAF bug") Signed-off-by: Amery Hung Link: https://lore.kernel.org/r/20260605202056.1780352-5-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- kernel/bpf/states.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 06d9ae24f006..32f346ce3ffc 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -343,8 +343,12 @@ static bool check_ids(u32 old_id, u32 cur_id, struct bpf_idmap *idmap) return true; } - /* We ran out of idmap slots, which should be impossible */ - WARN_ON_ONCE(1); + /* + * idmap slots are bounded by the number of registers and stack slots. + * Since referenced dynptrs acquire intermediate references that do + * not live in either, so the map can be exhausted. Since it is unlikely, + * fail the verification by treating the states as not equivalent. + */ return false; } From d83d4f63cb8f92aa6254dfc001eac0e41f5b2c35 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Fri, 5 Jun 2026 13:20:56 -0700 Subject: [PATCH 5/5] selftests/bpf: Use bpf_dynptr_slice() to read file dynptr in leak test use_file_dynptr_slice_after_put_file() reads the dynptr via bpf_dynptr_data(), which always returns NULL for a read-only file dynptr, making the example confusing. Switch to bpf_dynptr_slice(), the correct read API for file dynptrs, and read (rather than write) the slice since it is read-only. The test still fails as expected. Acked-by: Eduard Zingerman Signed-off-by: Amery Hung Link: https://lore.kernel.org/r/20260605202056.1780352-6-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/file_reader_fail.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/file_reader_fail.c b/tools/testing/selftests/bpf/progs/file_reader_fail.c index d5fae5e4cf9a..3bb9e2612f8f 100644 --- a/tools/testing/selftests/bpf/progs/file_reader_fail.c +++ b/tools/testing/selftests/bpf/progs/file_reader_fail.c @@ -87,7 +87,8 @@ int use_file_dynptr_slice_after_put_file(void *ctx) struct task_struct *task = bpf_get_current_task_btf(); struct file *file = bpf_get_task_exe_file(task); struct bpf_dynptr dynptr; - char *data; + char buf[1]; + const char *data; if (!file) return 0; @@ -95,15 +96,14 @@ int use_file_dynptr_slice_after_put_file(void *ctx) if (bpf_dynptr_from_file(file, 0, &dynptr)) goto out; - data = bpf_dynptr_data(&dynptr, 0, 1); + data = bpf_dynptr_slice(&dynptr, 0, buf, sizeof(buf)); if (!data) goto out; /* this should fail - file dynptr should be discarded first to prevent resource leak */ bpf_put_file(file); - *data = 'x'; - return 0; + return data[0]; out: bpf_dynptr_file_discard(&dynptr);