]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: Unify release handling for helpers and kfuncs
authorAmery Hung <ameryhung@gmail.com>
Fri, 29 May 2026 01:49:31 +0000 (18:49 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 01:31:41 +0000 (18:31 -0700)
Introduce release_reg() to consolidate the release logic shared by both
helpers and kfuncs: dynptr release, kptr_xchg percpu-to-RCU conversion,
regular reference release, and NULL pass-through. NULL pass-through is
only allowed if the prototype indicates the argument may be null.

Determine release_regno from the function prototype/metadata before
argument checking, rather than discovering it dynamically during
argument processing. For helpers, scan the arg_type array in
check_func_proto() via check_proto_release_reg(). For kfuncs, set
release_regno to BPF_REG_1 in bpf_fetch_kfunc_arg_meta() when
KF_RELEASE is set. In the future when we start adding decl_tag to
kfunc arguments, we can just look at the function prototype instead
of a release_regno.

Extract ref_convert_alloc_rcu_protected() and
invalidate_rcu_protected_refs() to make it more clear what the code is
doing. For ref_convert_alloc_rcu_protected(), it pre-converts
MEM_ALLOC | MEM_PERCPU registers to MEM_RCU (clearing id so they
survive), then calls release_reference() to invalidate the remaining
registers and release the reference state.

Add KF_RELEASE to bpf_dynptr_file_discard() so its release_regno is set
via fetch_kfunc_meta rather than being assigned manually in the dynptr
argument processing. Set arg_type to ARG_PTR_TO_DYNPTR for
KF_ARG_PTR_TO_DYNPTR so that check_func_arg_reg_off() correctly allows
non-zero stack offsets for dynptr release arguments same as helper.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/20260529014936.2811085-9-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
12 files changed:
include/linux/bpf_verifier.h
kernel/bpf/helpers.c
kernel/bpf/verifier.c
tools/testing/selftests/bpf/prog_tests/cb_refs.c
tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
tools/testing/selftests/bpf/progs/map_kptr_fail.c
tools/testing/selftests/bpf/progs/task_kfunc_failure.c
tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c
tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
tools/testing/selftests/bpf/progs/verifier_sock.c
tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
tools/testing/selftests/bpf/progs/wakeup_source_fail.c

index b0521ba7787a36887537944a9e7554c946f10f6c..3dd2d21230afd1832408b599fde9724fc273a94d 100644 (file)
@@ -1426,9 +1426,9 @@ struct bpf_dynptr_desc {
 
 /*
  * The last seen rereferenced object; Updated by update_ref_obj() when a register refers to a
- * referenced object. Used when the helper or kfunc is releasing a referenced object, casting
- * a referenced object, returning allocated memory derived from referenced object or creating
- * a dynptr with a referenced object as parent.
+ * referenced object. Used when the helper or kfunc is casting a referenced object, returning
+ * allocated memory derived from referenced object or creating a dynptr with a referenced
+ * object as parent.
  */
 struct ref_obj_desc {
        u32 id;
index 9ca1951046679a07ef33ad9fc4bf1b1003163cc7..03004e4451f5a2fafcf90cb37a13ddbdb320b231 100644 (file)
@@ -4957,7 +4957,7 @@ BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_dynptr_from_file)
-BTF_ID_FLAGS(func, bpf_dynptr_file_discard)
+BTF_ID_FLAGS(func, bpf_dynptr_file_discard, KF_RELEASE)
 BTF_ID_FLAGS(func, bpf_timer_cancel_async)
 BTF_KFUNCS_END(common_btf_ids)
 
index bc8a09c858d8999865bab0732d509e5914fa7d53..caa455fad877ed02f4c2d8531471edb4845c915a 100644 (file)
@@ -8225,17 +8225,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
                return err;
 
 skip_type_check:
-       if (arg_type_is_release(arg_type)) {
-               if (!arg_type_is_dynptr(arg_type) && !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
-                       verbose(env, "R%d must be referenced when passed to release function\n",
-                               regno);
-                       return -EINVAL;
-               }
-               if (meta->release_regno) {
-                       verifier_bug(env, "more than one release argument");
-                       return -EFAULT;
-               }
-               meta->release_regno = regno;
+       if (arg_type_is_release(arg_type) && !arg_type_is_dynptr(arg_type) &&
+           !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
+               verbose(env, "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
+                       func_id_name(meta->func_id), reg_arg_name(env, argno));
+               return -EINVAL;
        }
 
        if (reg_is_referenced(env, reg))
@@ -8798,11 +8792,29 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn)
        return true;
 }
 
-static int check_func_proto(const struct bpf_func_proto *fn)
+static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
+               enum bpf_arg_type arg_type = fn->arg_type[i];
+
+               if (arg_type_is_release(arg_type)) {
+                       if (meta->release_regno)
+                               return false;
+                       meta->release_regno = i + 1;
+               }
+       }
+
+       return true;
+}
+
+static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
 {
        return check_raw_mode_ok(fn) &&
               check_arg_pair_ok(fn) &&
               check_mem_arg_rw_flag_ok(fn) &&
+              check_proto_release_reg(fn, meta) &&
               check_btf_id_ok(fn) ? 0 : -EINVAL;
 }
 
@@ -8956,6 +8968,42 @@ static void invalidate_non_owning_refs(struct bpf_verifier_env *env)
        }));
 }
 
+static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env)
+{
+       struct bpf_stack_state *stack;
+       struct bpf_func_state *state;
+       struct bpf_reg_state *reg;
+       u32 clear_mask = (1 << STACK_SPILL) | (1 << STACK_ITER);
+
+       bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({
+               if (reg->type & MEM_RCU) {
+                       reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
+                       reg->type |= PTR_UNTRUSTED;
+               }
+       }));
+}
+
+static int ref_convert_alloc_rcu_protected(struct bpf_verifier_env *env, u32 id)
+{
+       struct bpf_func_state *state;
+       struct bpf_reg_state *reg;
+       int err;
+
+       err = release_reference_nomark(env->cur_state, id);
+
+       bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({
+               if (reg->id != id)
+                       continue;
+               if ((reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU)) {
+                       reg->id = 0;
+                       reg->type &= ~MEM_ALLOC;
+                       reg->type |= MEM_RCU;
+               }
+       }));
+
+       return err;
+}
+
 static void clear_caller_saved_regs(struct bpf_verifier_env *env,
                                    struct bpf_reg_state *regs)
 {
@@ -10028,6 +10076,24 @@ static const char *non_sleepable_context_description(struct bpf_verifier_env *en
        return "non-sleepable prog";
 }
 
+static int release_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+                      bool convert_rcu, bool release_dynptr)
+{
+       int err = -EINVAL;
+
+       if (bpf_register_is_null(reg))
+               return 0;
+
+       if (release_dynptr)
+               err = unmark_stack_slots_dynptr(env, reg);
+       else if (convert_rcu)
+               err = ref_convert_alloc_rcu_protected(env, reg->id);
+       else if (reg_is_referenced(env, reg))
+               err = release_reference(env, reg->id);
+
+       return err;
+}
+
 static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                             int *insn_idx_p)
 {
@@ -10077,7 +10143,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
        memset(&meta, 0, sizeof(meta));
        meta.pkt_access = fn->pkt_access;
 
-       err = check_func_proto(fn);
+       err = check_func_proto(fn, &meta);
        if (err) {
                verifier_bug(env, "incorrect func proto %s#%d", func_id_name(func_id), func_id);
                return err;
@@ -10122,37 +10188,11 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
        }
 
        if (meta.release_regno) {
-               err = -EINVAL;
-               if (arg_type_is_dynptr(fn->arg_type[meta.release_regno - BPF_REG_1])) {
-                       err = unmark_stack_slots_dynptr(env, &regs[meta.release_regno]);
-               } else if (func_id == BPF_FUNC_kptr_xchg && meta.ref_obj.id) {
-                       u32 id = meta.ref_obj.id;
-                       bool in_rcu = in_rcu_cs(env);
-                       struct bpf_func_state *state;
-                       struct bpf_reg_state *reg;
-
-                       err = release_reference_nomark(env->cur_state, id);
-                       if (!err) {
-                               bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({
-                                       if (reg->id == id) {
-                                               if (in_rcu && (reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU)) {
-                                                       reg->id = 0;
-                                                       reg->type &= ~MEM_ALLOC;
-                                                       reg->type |= MEM_RCU;
-                                               } else {
-                                                       mark_reg_invalid(env, reg);
-                                               }
-                                       }
-                               }));
-                       }
-               } else if (meta.ref_obj.id) {
-                       err = release_reference(env, meta.ref_obj.id);
-               } else if (bpf_register_is_null(&regs[meta.release_regno])) {
-                       /* meta.ref_obj.id can only be 0 if register that is meant to be
-                        * released is NULL, which must be > R0.
-                        */
-                       err = 0;
-               }
+               struct bpf_reg_state *reg = &regs[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.id);
                if (err)
                        return err;
        }
@@ -10547,7 +10587,6 @@ static bool is_kfunc_release(struct bpf_kfunc_call_arg_meta *meta)
        return meta->kfunc_flags & KF_RELEASE;
 }
 
-
 static bool is_kfunc_destructive(struct bpf_kfunc_call_arg_meta *meta)
 {
        return meta->kfunc_flags & KF_DESTRUCTIVE;
@@ -11912,24 +11951,16 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                        return -EACCES;
                }
 
-               if (reg_is_referenced(env, reg)) {
-                       if (is_kfunc_release(meta) && meta->ref_obj.cnt) {
-                               verbose(env, "more than one arg with referenced id %s %u %u",
-                                       reg_arg_name(env, argno), reg->id,
-                                       meta->ref_obj.id);
-                               return -EFAULT;
-                       }
-                       update_ref_obj(&meta->ref_obj, reg);
-                       if (is_kfunc_release(meta)) {
-                               if (regno < 0) {
-                                       verbose(env, "%s release arg cannot be a stack argument\n",
-                                               reg_arg_name(env, argno));
-                                       return -EINVAL;
-                               }
-                               meta->release_regno = regno;
-                       }
+               if (regno == meta->release_regno && !is_kfunc_arg_dynptr(meta->btf, &args[i]) &&
+                   !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
+                       verbose(env, "release kfunc %s expects referenced PTR_TO_BTF_ID passed to %s\n",
+                               func_name, reg_arg_name(env, argno));
+                       return -EINVAL;
                }
 
+               if (reg_is_referenced(env, reg))
+                       update_ref_obj(&meta->ref_obj, reg);
+
                ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
                ref_tname = btf_name_by_offset(btf, ref_t->name_off);
 
@@ -11993,7 +12024,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                                }
                        }
                        fallthrough;
-               case KF_ARG_PTR_TO_DYNPTR:
                case KF_ARG_PTR_TO_ITER:
                case KF_ARG_PTR_TO_LIST_HEAD:
                case KF_ARG_PTR_TO_LIST_NODE:
@@ -12010,6 +12040,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                case KF_ARG_PTR_TO_IRQ_FLAG:
                case KF_ARG_PTR_TO_RES_SPIN_LOCK:
                        break;
+               case KF_ARG_PTR_TO_DYNPTR:
+                       arg_type = ARG_PTR_TO_DYNPTR;
+                       break;
                case KF_ARG_PTR_TO_CTX:
                        arg_type = ARG_PTR_TO_CTX;
                        break;
@@ -12018,7 +12051,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                        return -EFAULT;
                }
 
-               if (is_kfunc_release(meta) && reg_is_referenced(env, reg))
+               if (regno == meta->release_regno)
                        arg_type |= OBJ_RELEASE;
                ret = check_func_arg_reg_off(env, reg, argno, arg_type);
                if (ret < 0)
@@ -12083,12 +12116,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
                                dynptr_arg_type |= DYNPTR_TYPE_FILE;
                        } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_file_discard]) {
                                dynptr_arg_type |= DYNPTR_TYPE_FILE | OBJ_RELEASE;
-                               if (regno < 0) {
-                                       verbose(env, "%s release arg cannot be a stack argument\n",
-                                               reg_arg_name(env, argno));
-                                       return -EINVAL;
-                               }
-                               meta->release_regno = regno;
                        } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
                                   (dynptr_arg_type & MEM_UNINIT)) {
                                enum bpf_dynptr_type parent_type = meta->dynptr.type;
@@ -12377,12 +12404,6 @@ check_ok:
                }
        }
 
-       if (is_kfunc_release(meta) && !meta->release_regno) {
-               verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
-                       func_name);
-               return -EINVAL;
-       }
-
        return 0;
 }
 
@@ -12409,6 +12430,10 @@ int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
 
        meta->kfunc_flags = *kfunc.flags;
 
+       /* Only support release referenced argument passed by register */
+       if (is_kfunc_release(meta))
+               meta->release_regno = BPF_REG_1;
+
        return 0;
 }
 
@@ -12899,23 +12924,12 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        if (rcu_lock) {
                env->cur_state->active_rcu_locks++;
        } else if (rcu_unlock) {
-               struct bpf_stack_state *stack;
-               struct bpf_func_state *state;
-               struct bpf_reg_state *reg;
-               u32 clear_mask = (1 << STACK_SPILL) | (1 << STACK_ITER);
-
                if (env->cur_state->active_rcu_locks == 0) {
                        verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);
                        return -EINVAL;
                }
-               if (--env->cur_state->active_rcu_locks == 0) {
-                       bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({
-                               if (reg->type & MEM_RCU) {
-                                       reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
-                                       reg->type |= PTR_UNTRUSTED;
-                               }
-                       }));
-               }
+               if (--env->cur_state->active_rcu_locks == 0)
+                       invalidate_rcu_protected_refs(env);
        } else if (preempt_disable) {
                env->cur_state->active_preempt_locks++;
        } else if (preempt_enable) {
@@ -12946,13 +12960,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
         * PTR_TO_BTF_ID in bpf_kfunc_arg_meta, do the release now.
         */
        if (meta.release_regno) {
-               struct bpf_reg_state *reg = &regs[meta.release_regno];
-
-               if (meta.dynptr.id) {
-                       err = unmark_stack_slots_dynptr(env, reg);
-               } else {
-                       err = release_reference(env, reg->id);
-               }
+               err = release_reg(env, &regs[meta.release_regno], false, !!meta.dynptr.id);
                if (err)
                        return err;
        }
index 6300b67a3a84679b4c6b0e1f4fc82edbebb6d3e3..78566b817fd70a5e3d2bce0e599bccd46c76cc69 100644 (file)
@@ -11,7 +11,7 @@ struct {
        const char *prog_name;
        const char *err_msg;
 } cb_refs_tests[] = {
-       { "underflow_prog", "must point to scalar, or struct with scalar" },
+       { "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
        { "leak_prog", "Possibly NULL pointer passed to helper R2" },
        { "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
        { "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
index a875ba8e50070dcd7d36e3a02f9f809182c325d6..d0d65d6d450cf56f8ff1a8aeec0a1e84715c285e 100644 (file)
@@ -154,7 +154,7 @@ int BPF_PROG(cgrp_kfunc_xchg_unreleased, struct cgroup *cgrp, const char *path)
 }
 
 SEC("tp_btf/cgroup_mkdir")
-__failure __msg("must be referenced or trusted")
+__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(cgrp_kfunc_rcu_get_release, struct cgroup *cgrp, const char *path)
 {
        struct cgroup *kptr;
@@ -191,7 +191,7 @@ int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path
 }
 
 SEC("tp_btf/cgroup_mkdir")
-__failure __msg("R1 pointer type STRUCT cgroup must point")
+__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path)
 {
        struct cgroup *acquired = (struct cgroup *)&path;
@@ -237,7 +237,7 @@ int BPF_PROG(cgrp_kfunc_release_null, struct cgroup *cgrp, const char *path)
 }
 
 SEC("tp_btf/cgroup_mkdir")
-__failure __msg("release kernel function bpf_cgroup_release expects")
+__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(cgrp_kfunc_release_unacquired, struct cgroup *cgrp, const char *path)
 {
        /* Cannot release trusted cgroup pointer which was not acquired. */
index 8f36e74fd8f9dea77ff6948242501a098eb6be81..f11848dfa78f99d4c560d53635d68a519090a2a7 100644 (file)
@@ -252,7 +252,7 @@ int reject_untrusted_store_to_ref(struct __sk_buff *ctx)
 }
 
 SEC("?tc")
-__failure __msg("R2 must be referenced")
+__failure __msg("release helper bpf_kptr_xchg expects referenced PTR_TO_BTF_ID passed to R2")
 int reject_untrusted_xchg(struct __sk_buff *ctx)
 {
        struct prog_test_ref_kfunc *p;
index 41047d81ec428a3cc9beff06a4fb1cb9eb113ea9..8e947d445f8e889ae018fbfabe7bfeaf548d5443 100644 (file)
@@ -178,7 +178,7 @@ int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_f
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("R1 pointer type STRUCT task_struct must point")
+__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags)
 {
        struct task_struct *acquired = (struct task_struct *)&clone_flags;
@@ -224,7 +224,7 @@ int BPF_PROG(task_kfunc_release_null, struct task_struct *task, u64 clone_flags)
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("release kernel function bpf_task_release expects")
+__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_flags)
 {
        /* Cannot release trusted task pointer which was not acquired. */
@@ -313,7 +313,7 @@ int BPF_PROG(task_access_comm4, struct task_struct *task, const char *buf, bool
 }
 
 SEC("tp_btf/task_newtask")
-__failure __msg("R1 must be referenced or trusted")
+__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(task_kfunc_release_in_map, struct task_struct *task, u64 clone_flags)
 {
        struct task_struct *local;
index e7dae0cf9c17ce53a5c5723fa4df4aa84441ce93..ea273e15220938cc9b444697a539b5a6becc9ff7 100644 (file)
@@ -153,7 +153,7 @@ __weak int subprog_trusted_destroy(struct task_struct *task __arg_trusted)
 
 SEC("?tp_btf/task_newtask")
 __failure __log_level(2)
-__msg("release kernel function bpf_task_release expects refcounted PTR_TO_BTF_ID")
+__msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(trusted_destroy_fail, struct task_struct *task, u64 clone_flags)
 {
        return subprog_trusted_destroy(task);
index 139f70bb3595fe6c827555da9487c1361ead356c..199ad18f8eb584a8c26f589eed9969e34bb3167f 100644 (file)
@@ -1288,7 +1288,7 @@ l1_%=:    r1 = r6;                                        \
 
 SEC("tc")
 __description("reference tracking: bpf_sk_release(listen_sk)")
-__failure __msg("R1 must be referenced when passed to release function")
+__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
 __naked void bpf_sk_release_listen_sk(void)
 {
        asm volatile (
index a2132c72d3b806e18ce36fd7105e89b2f749e27e..9f680cf44512e76dd78ce582b1308699321400b4 100644 (file)
@@ -603,7 +603,7 @@ l2_%=:      r0 = *(u32*)(r0 + %[bpf_tcp_sock_snd_cwnd]);    \
 
 SEC("tc")
 __description("bpf_sk_release(skb->sk)")
-__failure __msg("R1 must be referenced when passed to release function")
+__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
 __naked void bpf_sk_release_skb_sk(void)
 {
        asm volatile ("                                 \
@@ -620,7 +620,7 @@ l0_%=:      r0 = 0;                                         \
 
 SEC("tc")
 __description("bpf_sk_release(bpf_sk_fullsock(skb->sk))")
-__failure __msg("R1 must be referenced when passed to release function")
+__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
 __naked void bpf_sk_fullsock_skb_sk(void)
 {
        asm volatile ("                                 \
@@ -644,7 +644,7 @@ l1_%=:      r1 = r0;                                        \
 
 SEC("tc")
 __description("bpf_sk_release(bpf_tcp_sock(skb->sk))")
-__failure __msg("R1 must be referenced when passed to release function")
+__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
 __naked void bpf_tcp_sock_skb_sk(void)
 {
        asm volatile ("                                 \
index 0990de0768440592108fd0399e6314aee45f66ea..2870738d93f7ec85b04377d63dd783322216c4e5 100644 (file)
@@ -80,7 +80,7 @@ int BPF_PROG(get_task_exe_file_kfunc_unreleased)
 }
 
 SEC("lsm.s/file_open")
-__failure __msg("release kernel function bpf_put_file expects")
+__failure __msg("release kfunc bpf_put_file expects referenced PTR_TO_BTF_ID passed to R1")
 int BPF_PROG(put_file_kfunc_unacquired, struct file *file)
 {
        /* Can't release an unacquired pointer. */
index b8bbb61d4d4e08b115ab7c9d9ae8577cb313280c..d4d0f1610853a8ac25ab631e5a0c424164390e57 100644 (file)
@@ -42,7 +42,7 @@ int wakeup_source_access_lock_fields(void *ctx)
 }
 
 SEC("syscall")
-__failure __msg("type=scalar expected=fp")
+__failure __msg("release kfunc bpf_wakeup_sources_read_unlock expects referenced PTR_TO_BTF_ID passed to R1")
 int wakeup_source_unlock_no_lock(void *ctx)
 {
        struct bpf_ws_lock *lock = (void *)0x1;