> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 1c69476c8a09..89579165ef4d 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -2577,6 +2577,16 @@ __bpf_kfunc int bpf_list_push_back_impl(struct 
> bpf_list_head *head,
>       return bpf_list_push_back(head, node, meta__ign, off);
>  }
>
> +__bpf_kfunc int bpf_list_add(struct bpf_list_head *head, struct 
> bpf_list_node *new,
> +                          struct bpf_list_node *prev__nonown_allowed,
> +                          struct btf_struct_meta *meta, u64 off)
> +{
> +     struct bpf_list_node_kern *n = (void *)new, *p = (void 
> *)prev__nonown_allowed;
> +     struct list_head *prev_ptr = &p->list_head;
> +
> +     return __bpf_list_add(n, head, &prev_ptr, meta ? meta->record : NULL, 
> off);
> +}

If __bpf_list_add() fails at runtime (e.g., because prev is not in the
list indicated by head), it drops the 'new' node via __bpf_obj_drop_impl
in the fail path.

However, the verifier unconditionally converts 'new' into a non-owning
reference during check_kfunc_call(). If __bpf_list_add() fails, the BPF
program retains this non-owning reference to a freed node.

Could this allow the program to trigger a use-after-free within the lock
critical section?

([email protected], v10 RESEND)
https://lore.kernel.org/bpf/[email protected]/

>
>  static struct bpf_list_node *__bpf_list_del(struct bpf_list_head *head,
>                                           struct list_head *n)

[ ... ]

> @@ -4756,6 +4766,7 @@ BTF_ID_FLAGS(func, bpf_list_push_front, 
> KF_IMPLICIT_ARGS)
>  BTF_ID_FLAGS(func, bpf_list_push_front_impl)
>  BTF_ID_FLAGS(func, bpf_list_push_back, KF_IMPLICIT_ARGS)
>  BTF_ID_FLAGS(func, bpf_list_push_back_impl)
> +BTF_ID_FLAGS(func, bpf_list_add, KF_IMPLICIT_ARGS)

Since bpf_list_add is marked with KF_IMPLICIT_ARGS, won't the verifier
look for a bpf_list_add_impl counterpart during program load?

Without defining and registering a bpf_list_add_impl function,
find_kfunc_impl_proto() will fail to find the required _impl suffix in
the vmlinux BTF.

Will this cause the verifier to unconditionally reject the kfunc?

([email protected], v10 RESEND)
https://lore.kernel.org/bpf/[email protected]/

>  BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL)
>  BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL)
>  BTF_ID_FLAGS(func, bpf_list_del, KF_ACQUIRE | KF_RET_NULL)

[ ... ]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 35eebb5e7769..662ad7312697 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c

[ ... ]

> @@ -19524,8 +19527,11 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env 
> *env, struct bpf_insn *insn,
>               int struct_meta_reg = BPF_REG_3;
>               int node_offset_reg = BPF_REG_4;
>
> -             /* rbtree_add has extra 'less' arg, so args-to-fixup are in 
> diff regs */
> -             if (is_bpf_rbtree_add_kfunc(desc->func_id)) {
> +             /* list_add/rbtree_add have an extra arg (prev/less),
> +              * so args-to-fixup are in diff regs.
> +              */

This isn't a bug, but the multi-line comment format doesn't follow the
BPF subsystem style. The preferred form in kernel/bpf/ has the opening
/* on its own line.

Additionally, consider adding an is_bpf_list_add_kfunc() helper instead
of direct comparison with special_kfunc_list[KF_bpf_list_add].

([email protected] and Eduard Zingerman, v10 RESEND)
https://lore.kernel.org/bpf/f573f7e0a5140fe6254d2ddfd3f69add455fa25c67c020c0312e2c2ceeca7...@mail.kernel.org/

> +             if (desc->func_id == special_kfunc_list[KF_bpf_list_add] ||
> +                 is_bpf_rbtree_add_kfunc(desc->func_id)) {
>                       struct_meta_reg = BPF_REG_4;
>                       node_offset_reg = BPF_REG_5;
>               }


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

Reply via email to