Re: [PATCH bpf-next v4 08/12] bpf: Unify release handling for helpers and kfuncs
Amery Hung <[email protected]> Thu, 7 May 2026 15:23:26 +0200
| Newsgroups | dev.linux.lists.sashiko,org.kernel.vger.bpf |
|---|---|
| Message-ID | <CAMB2axM9DM+6CD02sr7Qx56V-yp4Mr_=yRebC0PktthNTh227g@mail.gmail.com> |
On Thu, May 7, 2026 at 12:59 AM <[email protected]> wrote: > > Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: > - [High] Hardcoding meta->release_regno = BPF_REG_1 for KF_RELEASE kfuncs creates a Use-After-Free vulnerability for kfuncs releasing non-first arguments. > - [Medium] Incorrect evaluation of the release_dynptr flag leads to false rejection of valid BPF programs. > -- > > commit e0e1bbafcfda45d42ce5635352bc4b69c0d713c6 > Author: Amery Hung <[email protected]> > > bpf: Unify release handling for helpers and kfuncs > > Introduce release_reg() to consolidate the release logic shared by both > helpers and kfuncs. The patch determines release_regno from the function > prototype or metadata before argument checking. > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > > index 542912c7983f7..e40f09cefa291 100644 > > --- a/kernel/bpf/verifier.c > > +++ b/kernel/bpf/verifier.c > [ ... ] > > @@ -9870,37 +9928,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn > > } > > > > if (meta.release_regno) { > [ ... ] > > + struct bpf_reg_state *reg = ®s[meta.release_regno]; > > + bool convert_rcu = (func_id == BPF_FUNC_kptr_xchg) && in_rcu_cs(env) && > > + (reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU); > > + > > + err = release_reg(env, reg, convert_rcu, !!meta.dynptr.ref_obj_id); > > if (err) > > return err; > > } > > Does passing !!meta.dynptr.ref_obj_id as the release_dynptr flag cause valid > programs to be rejected? > > If a kfunc or helper accepts both a normal PTR_TO_BTF_ID to be released and > a referenced dynptr as a normal (non-release) argument, > meta.dynptr.ref_obj_id would be set. This would cause release_reg() to > mistakenly invoke unmark_stack_slots_dynptr() on the non-dynptr release > register, returning -EINVAL. > > Could the release condition be strictly derived from the argument type of > the specific release register instead? No existing helper and kfunc releases a ref_obj that also has another dynptr argument. Kfunc already use this check and I choose to make helper align with kfunc to make it the same without overcomplicating things. In the future when there is a universal proto for heleprs and kfuncs, we can check meta->fn->arg_type[release_regno] to make it more robust. > > [ ... ] > > @@ -12083,6 +12107,9 @@ int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env, > > > > meta->kfunc_flags = *kfunc.flags; > > > > + if (is_kfunc_release(meta)) > > + meta->release_regno = BPF_REG_1; > > + > > return 0; > > } > > By unconditionally hardcoding BPF_REG_1 here, could this create a > use-after-free if a kernel module defines a release kfunc that frees an > argument other than the first? > > If a BPF program passes a dummy referenced object to R1 and the actual > target object to R2, the verifier might drop the reference for R1 while > retaining R2, even though the kfunc frees R2 at runtime. > > Should we still rely on dynamically discovering the release register based > on the argument type to avoid this? If anyone is developing a new kfunc whose ref_obj to be released is not BPF_REG_1, the verifier should scream "unreleased reference", so the bug is unlikely to make it into the kernel. Some reason for this approach: The longterm goal is to remove the dependency of bpf_reg_state from kfunc proto generation. In this case, I want to tag OBJ_RELEASE by looking at the proto. This is currently impossible since we haved enable decl_tag for kfunc arguments yet. I think hardcode to BPF_REG_1 is a reasonable workaround for now. If this sounds okay. I will add this temporary constraint to kfuncs.rst. > > [ ... ] > > @@ -12622,13 +12638,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, > [ ... ] > > if (meta.release_regno) { > > - struct bpf_reg_state *reg = ®s[meta.release_regno]; > > - > > - if (meta.dynptr.ref_obj_id) { > > - err = unmark_stack_slots_dynptr(env, reg); > > - } else { > > - err = release_reference(env, reg->ref_obj_id); > > - } > > + err = release_reg(env, ®s[meta.release_regno], false, !!meta.dynptr.ref_obj_id); > > if (err) > > return err; > > } > > Similar to the check_helper_call() case above, does this global dynptr > metadata check safely handle mixed arguments when releasing registers? > > -- > Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8