Currently, BPF helpers and kfuncs which take trusted pointer arguments i.e. those flagged w/ KF_TRUSTED_ARGS, KF_RELEASE, OBJ_RELEASE, all require an original/unmodified trusted pointer argument to be supplied to them. By original/unmodified, it means that the backing register holding the trusted pointer argument that is to be supplied to the BPF helper/kfunc must have its fixed offset set to zero, or else the BPF verifier will outright reject the BPF program load. However, this fixed offset constraint of zero enforced by the BPF verifier onto the trusted pointer arguments is rather unnecessary at times and limiting from a usability point of view, as it completely eliminates the possibility of constructing a derived trusted pointer from an original trusted pointer. A derived trusted pointer is simply a pointer pointing to one of the nested member fields of the object being pointed to by the original trusted pointer. This patch relaxes the zero fixed offset constraint that is enforced upon trusted pointer arguments such that the constraint is now only strictly enforced on a case-by-case basis. The updated semantics of when the zero fixed offset constraint is enforced and in turn relaxed may be summarized as follows: * For OBJ_RELEASE and KF_RELEASE BPF helpers and kfuncs: * If the expected argument type is of an untyped pointer i.e. void *, then we continue to enforce a zero fixed offset as we need to ensure that the correct referenced pointer is handed off correctly to the relevant deallocation routine * If the expected argument is backed by BTF, then we relax the strict zero fixed offset and allow it only if we successfully type matched between the register and argument. A failed type match between register and argument will result in the legacy strict zero offset semantics * For KF_TRUSTED_ARGS BPF kfuncs: * The fixed zero offset constraint has been lifted, such that KF_TRUSTED_ARGS BPF kfuncs can now accept a trusted pointer argument with a non-zero fixed offset providing that register and argument BTF has type matched successfully With these new fixed offset semantics in-place for trusted pointer arguments, we now have more flexibility when it comes to the BPF kfuncs that we're able to introduce moving forward, and increase the overall usability of BPF helpers and kfuncs that make use of trusted pointer arguments. For some early discussions covering the possibility of relaxing the zero fixed offset constraint can be found using the link below. This will provide more context on where all this has stemmed from: * https://lore.kernel.org/bpf/ZhkbrM55MKQ0KeIV@xxxxxxxxxx/ Signed-off-by: Matt Bobrowski <mattbobrowski@xxxxxxxxxx> --- kernel/bpf/verifier.c | 235 +++++++++++++----- .../selftests/bpf/prog_tests/linked_list.c | 2 +- .../bpf/progs/local_kptr_stash_fail.c | 2 +- .../bpf/progs/nested_trust_failure.c | 8 - .../bpf/progs/nested_trust_success.c | 8 + .../selftests/bpf/progs/user_ringbuf_fail.c | 4 +- .../bpf/progs/verifier_ref_tracking.c | 2 +- .../selftests/bpf/progs/verifier_ringbuf.c | 2 +- .../selftests/bpf/progs/verifier_sock.c | 6 +- tools/testing/selftests/bpf/verifier/calls.c | 4 +- 10 files changed, 198 insertions(+), 75 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index dcbbf5f64c5d..a5d18c742bdf 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8375,21 +8375,25 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, } if (!arg_btf_id) { - if (!compatible->btf_id) { - verbose(env, "verifier internal error: missing arg compatible BTF ID\n"); - return -EFAULT; - } - arg_btf_id = compatible->btf_id; + verbose(env, + "verifier internal error: missing BTF ID for BPF helper argument %d of type %d\n", + regno - BPF_REG_1, base_type(arg_type)); + return -EFAULT; } + /* arg_btf_id is allowed to be BPF_PTR_POISON at this point as + * map_kptr_match_type() dynamically fetches the BTF ID for the + * backing argument via the kptr_field residing in + * bpf_call_arg_meta. + */ if (meta->func_id == BPF_FUNC_kptr_xchg) { if (map_kptr_match_type(env, meta->kptr_field, reg, regno)) return -EACCES; } else { if (arg_btf_id == BPF_PTR_POISON) { - verbose(env, "verifier internal error:"); - verbose(env, "R%d has non-overwritten BPF_PTR_POISON type\n", - regno); + verbose(env, + "verifier internal error: BPF helper argument %d has non-overwritten BPF_PTR_POISON type\n", + regno - BPF_REG_1); return -EACCES; } @@ -8445,41 +8449,121 @@ reg_find_field_offset(const struct bpf_reg_state *reg, s32 off, u32 fields) return field; } -static int check_func_arg_reg_off(struct bpf_verifier_env *env, - const struct bpf_reg_state *reg, int regno, - enum bpf_arg_type arg_type) +static int check_release_arg_reg_off(struct bpf_verifier_env *env, + const enum bpf_arg_type arg_type, + const struct btf *arg_btf, + const u32 *arg_btf_id, const int regno, + const struct bpf_reg_state *reg, + const bool strict_type_match) { - u32 type = reg->type; + int ret; + bool fixed_off_ok; + const struct btf_type *arg_ref_t; - /* When referenced register is passed to release function, its fixed - * offset must be 0. - * - * We will check arg_type_is_release reg has ref_obj_id when storing - * meta->release_regno. + /* ARG_PTR_TO_DYNPTR with OBJ_RELEASE is a bit special, as it may not + * directly point to the object being released, but to a dynptr pointing + * to such object, which might be at some offset on the stack. In that + * case, we simply to fallback to the default handling. */ - if (arg_type_is_release(arg_type)) { - /* ARG_PTR_TO_DYNPTR with OBJ_RELEASE is a bit special, as it - * may not directly point to the object being released, but to - * dynptr pointing to such object, which might be at some offset - * on the stack. In that case, we simply to fallback to the - * default handling. - */ - if (arg_type_is_dynptr(arg_type) && type == PTR_TO_STACK) - return 0; + if (arg_type_is_dynptr(arg_type) && reg->type == PTR_TO_STACK) + return 0; - /* Doing check_ptr_off_reg check for the offset will catch this - * because fixed_off_ok is false, but checking here allows us - * to give the user a better error message. - */ + if (!reg->ref_obj_id) { + verbose(env, + "R%d must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc\n", + regno); + return -EINVAL; + } + + if (!arg_btf_id || arg_btf_id == BPF_PTR_POISON) { if (reg->off) { - verbose(env, "R%d must have zero offset when passed to release func or trusted arg to kfunc\n", + verbose(env, + "R%d must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *\n", regno); return -EINVAL; } - return __check_ptr_off_reg(env, reg, regno, false); + /* We have no suporting BTF type ID to work with, so just + * perform the conventional pointer offset register checks. + */ + return check_ptr_off_reg(env, reg, regno); } - switch (type) { + arg_btf = !arg_btf ? btf_vmlinux : arg_btf; + arg_ref_t = btf_type_skip_modifiers(arg_btf, *arg_btf_id, NULL); + if (!arg_ref_t) { + verbose(env, + "verifier internal error: failed to get argument BTF type information for BTF type ID %d", + *arg_btf_id); + return -EFAULT; + } + + /* For OBJ_RELEASE/KF_RELEASE flagged BPF helpers and kfuncs which + * take a void pointer as an argument, we must ensure that the supplied + * pointer is in its original unmodified form, or else we could end up + * in a situation where we pass the wrong pointer into the callee. + */ + if (reg->off && btf_type_is_void(arg_ref_t)) { + verbose(env, + "R%d must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *\n", + regno); + return -EINVAL; + } + + fixed_off_ok = false; + /* Don't attempt to perform a btf_struct_ids_match() on a MEM_ALLOC + * tagged register type as it will always result in a failed match. This + * is due to using 2 different BTF sources, being local and kernel. + */ + if (reg->off && ((base_type(reg->type) == PTR_TO_BTF_ID || + reg2btf_ids[reg->type]) && + !type_is_alloc(reg->type))) { + u32 reg_btf_id; + const struct btf *reg_btf; + + if (base_type(reg->type) == PTR_TO_BTF_ID) { + reg_btf = reg->btf; + reg_btf_id = reg->btf_id; + } else { + reg_btf = btf_vmlinux; + reg_btf_id = *reg2btf_ids[reg->type]; + } + + fixed_off_ok = btf_struct_ids_match(&env->log, reg_btf, + reg_btf_id, reg->off, + arg_btf, *arg_btf_id, + strict_type_match); + } + + ret = __check_ptr_off_reg(env, reg, regno, fixed_off_ok); + if (reg->off && ret) { + verbose(env, + "R%d must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *\n", + regno); + } + return ret; +} + +static int check_func_arg_reg_off(struct bpf_verifier_env *env, + const u32 func_id, + const enum bpf_arg_type arg_type, + const struct btf *arg_btf, + const u32 *arg_btf_id, const u8 regno, + const struct bpf_reg_state *reg) +{ + const u32 reg_type = reg->type; + + if (arg_type_is_release(arg_type)) { + /* BPF_FUNC_sk_release is rather polymorphic as it can accept + * mulitple types. Therefore, we make an exception for it when + * determining whether we should enforce strict type matching + * between BPF helper/kfunc argument and register. + */ + return check_release_arg_reg_off( + env, arg_type, arg_btf, arg_btf_id, regno, reg, + func_id != BPF_FUNC_sk_release); + } + + switch (reg_type) { /* Pointer types where both fixed and variable offset is explicitly allowed: */ case PTR_TO_STACK: case PTR_TO_PACKET: @@ -8630,6 +8714,33 @@ static int check_reg_const_str(struct bpf_verifier_env *env, return 0; } +static int *get_helper_arg_btf_id(const struct bpf_func_proto *fn, + const struct bpf_call_arg_meta *meta, + const u32 arg, + const enum bpf_arg_type arg_type) +{ + u32 *arg_btf_id = NULL; + const enum bpf_arg_type base_arg_type = base_type(arg_type); + + if (base_arg_type == ARG_PTR_TO_BTF_ID || + base_arg_type == ARG_PTR_TO_SPIN_LOCK) { + arg_btf_id = fn->arg_btf_id[arg]; + } + + if (arg_btf_id == BPF_PTR_POISON && + meta->func_id == BPF_FUNC_kptr_xchg) { + arg_btf_id = &meta->kptr_field->kptr.btf_id; + } + + if (!arg_btf_id) { + const struct bpf_reg_types *reg_types = + compatible_reg_types[base_arg_type]; + if (reg_types) + arg_btf_id = reg_types->btf_id; + } + return arg_btf_id; +} + static int check_func_arg(struct bpf_verifier_env *env, u32 arg, struct bpf_call_arg_meta *meta, const struct bpf_func_proto *fn, @@ -8639,7 +8750,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; enum bpf_arg_type arg_type = fn->arg_type[arg]; enum bpf_reg_type type = reg->type; - u32 *arg_btf_id = NULL; + u32 *arg_btf_id; int err = 0; if (arg_type == ARG_DONTCARE) @@ -8676,16 +8787,13 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, */ goto skip_type_check; - /* arg_btf_id and arg_size are in a union. */ - if (base_type(arg_type) == ARG_PTR_TO_BTF_ID || - base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK) - arg_btf_id = fn->arg_btf_id[arg]; - + arg_btf_id = get_helper_arg_btf_id(fn, meta, arg, arg_type); err = check_reg_type(env, regno, arg_type, arg_btf_id, meta); if (err) return err; - err = check_func_arg_reg_off(env, reg, regno, arg_type); + err = check_func_arg_reg_off(env, meta->func_id, arg_type, btf_vmlinux, + arg_btf_id, regno, reg); if (err) return err; @@ -9433,6 +9541,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, u32 regno = i + 1; struct bpf_reg_state *reg = ®s[regno]; struct bpf_subprog_arg_info *arg = &sub->args[i]; + const u32 arg_btf_id = arg->btf_id; if (arg->arg_type == ARG_ANYTHING) { if (reg->type != SCALAR_VALUE) { @@ -9440,7 +9549,9 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, return -EINVAL; } } else if (arg->arg_type == ARG_PTR_TO_CTX) { - ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE); + ret = check_func_arg_reg_off(env, /*func_id=*/0, + ARG_DONTCARE, btf, + &arg_btf_id, regno, reg); if (ret < 0) return ret; /* If function expects ctx type in BTF check that caller @@ -9451,7 +9562,9 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, return -EINVAL; } } else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) { - ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE); + ret = check_func_arg_reg_off(env, /*func_id=*/0, + ARG_DONTCARE, btf, + &arg_btf_id, regno, reg); if (ret < 0) return ret; if (check_mem_reg(env, reg, regno, arg->mem_size)) @@ -9485,7 +9598,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */ err = check_reg_type(env, regno, arg->arg_type, &arg->btf_id, &meta); - err = err ?: check_func_arg_reg_off(env, reg, regno, arg->arg_type); + err = err ?: + check_func_arg_reg_off(env, /*func_id=*/0, + arg->arg_type, btf, + &arg_btf_id, regno, + reg); if (err) return err; } else { @@ -11281,6 +11398,16 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, bool struct_same; u32 reg_ref_id; + /* + * For a BPF kfunc that posseses KF_RELEASE semantics, we should only + * allow a register with a non-zero fixed offset to be accepted if we + * can successfully type match between the type held by the register and + * the type associated with the given BPF kfunc argument. + */ + WARN_ON_ONCE(is_kfunc_release(meta) && btf_type_is_void(ref_t) && + (reg->off || !tnum_is_const(reg->var_off) || + reg->var_off.value)); + if (base_type(reg->type) == PTR_TO_BTF_ID) { reg_btf = reg->btf; reg_ref_id = reg->btf_id; @@ -11318,8 +11445,6 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, btf_type_ids_nocast_alias(&env->log, reg_btf, reg_ref_id, meta->btf, ref_id)) strict_type_match = true; - WARN_ON_ONCE(is_kfunc_trusted_args(meta) && reg->off); - reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, ®_ref_id); reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off); struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->off, meta->btf, ref_id, strict_type_match); @@ -11884,6 +12009,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ } meta->map.ptr = reg->map_ptr; meta->map.uid = reg->map_uid; + + /* If a BPF kfunc argument in suffixed with __map, + * expect a struct bpf_map *. + */ + ref_id = *reg2btf_ids[CONST_PTR_TO_MAP]; + ref_t = btf_type_by_id(btf_vmlinux, ref_id); + ref_tname = btf_name_by_offset(btf, ref_t->name_off); fallthrough; case KF_ARG_PTR_TO_ALLOC_BTF_ID: case KF_ARG_PTR_TO_BTF_ID: @@ -11900,12 +12032,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return -EINVAL; } } - fallthrough; case KF_ARG_PTR_TO_CTX: - /* Trusted arguments have the same offset checks as release arguments */ - arg_type |= OBJ_RELEASE; - break; case KF_ARG_PTR_TO_DYNPTR: case KF_ARG_PTR_TO_ITER: case KF_ARG_PTR_TO_LIST_HEAD: @@ -11918,7 +12046,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ case KF_ARG_PTR_TO_REFCOUNTED_KPTR: case KF_ARG_PTR_TO_CONST_STR: case KF_ARG_PTR_TO_WORKQUEUE: - /* Trusted by default */ break; default: WARN_ON_ONCE(1); @@ -11927,7 +12054,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ if (is_kfunc_release(meta) && reg->ref_obj_id) arg_type |= OBJ_RELEASE; - ret = check_func_arg_reg_off(env, reg, regno, arg_type); + ret = check_func_arg_reg_off(env, /*func_id=*/0, arg_type, btf, + &ref_id, regno, reg); if (ret < 0) return ret; @@ -12103,11 +12231,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ return ret; break; case KF_ARG_PTR_TO_MAP: - /* If argument has '__map' suffix expect 'struct bpf_map *' */ - ref_id = *reg2btf_ids[CONST_PTR_TO_MAP]; - ref_t = btf_type_by_id(btf_vmlinux, ref_id); - ref_tname = btf_name_by_offset(btf, ref_t->name_off); - fallthrough; case KF_ARG_PTR_TO_BTF_ID: /* Only base_type is checked, further checks are done here */ if ((base_type(reg->type) != PTR_TO_BTF_ID || diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 77d07e0a4a55..559abab81d0f 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -67,7 +67,7 @@ static struct { { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, { "obj_new_no_composite", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, { "obj_new_no_struct", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, - { "obj_drop_non_zero_off", "R1 must have zero offset when passed to release func" }, + { "obj_drop_non_zero_off", "R1 must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *" }, { "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" }, { "obj_new_acq", "Unreleased reference id=" }, { "use_after_drop", "invalid mem access 'scalar'" }, diff --git a/tools/testing/selftests/bpf/progs/local_kptr_stash_fail.c b/tools/testing/selftests/bpf/progs/local_kptr_stash_fail.c index fcf7a7567da2..829b726b6383 100644 --- a/tools/testing/selftests/bpf/progs/local_kptr_stash_fail.c +++ b/tools/testing/selftests/bpf/progs/local_kptr_stash_fail.c @@ -63,7 +63,7 @@ long stash_rb_nodes(void *ctx) } SEC("tc") -__failure __msg("R1 must have zero offset when passed to release func") +__failure __msg("R1 must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") long drop_rb_node_off(void *ctx) { struct map_value *mapval; diff --git a/tools/testing/selftests/bpf/progs/nested_trust_failure.c b/tools/testing/selftests/bpf/progs/nested_trust_failure.c index ea39497f11ed..3568ec450100 100644 --- a/tools/testing/selftests/bpf/progs/nested_trust_failure.c +++ b/tools/testing/selftests/bpf/progs/nested_trust_failure.c @@ -31,14 +31,6 @@ int BPF_PROG(test_invalid_nested_user_cpus, struct task_struct *task, u64 clone_ return 0; } -SEC("tp_btf/task_newtask") -__failure __msg("R1 must have zero offset when passed to release func or trusted arg to kfunc") -int BPF_PROG(test_invalid_nested_offset, struct task_struct *task, u64 clone_flags) -{ - bpf_cpumask_first_zero(&task->cpus_mask); - return 0; -} - /* Although R2 is of type sk_buff but sock_common is expected, we will hit untrusted ptr first. */ SEC("tp_btf/tcp_probe") __failure __msg("R2 type=untrusted_ptr_ expected=ptr_, trusted_ptr_, rcu_ptr_") diff --git a/tools/testing/selftests/bpf/progs/nested_trust_success.c b/tools/testing/selftests/bpf/progs/nested_trust_success.c index 833840bffd3b..2b66953ca82e 100644 --- a/tools/testing/selftests/bpf/progs/nested_trust_success.c +++ b/tools/testing/selftests/bpf/progs/nested_trust_success.c @@ -32,3 +32,11 @@ int BPF_PROG(test_skb_field, struct sock *sk, struct sk_buff *skb) bpf_sk_storage_get(&sk_storage_map, skb->sk, 0, 0); return 0; } + +SEC("tp_btf/task_newtask") +__success +int BPF_PROG(test_nested_offset, struct task_struct *task, u64 clone_flags) +{ + bpf_cpumask_first_zero(&task->cpus_mask); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c index 11ab25c42c36..2d0ae76083d8 100644 --- a/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c +++ b/tools/testing/selftests/bpf/progs/user_ringbuf_fail.c @@ -146,7 +146,7 @@ try_discard_dynptr(struct bpf_dynptr *dynptr, void *context) * not be able to read past the end of the pointer. */ SEC("?raw_tp") -__failure __msg("cannot release unowned const bpf_dynptr") +__failure __msg("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") int user_ringbuf_callback_discard_dynptr(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, try_discard_dynptr, NULL, 0); @@ -166,7 +166,7 @@ try_submit_dynptr(struct bpf_dynptr *dynptr, void *context) * not be able to read past the end of the pointer. */ SEC("?raw_tp") -__failure __msg("cannot release unowned const bpf_dynptr") +__failure __msg("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") int user_ringbuf_callback_submit_dynptr(void *ctx) { bpf_user_ringbuf_drain(&user_ringbuf, try_submit_dynptr, NULL, 0); diff --git a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c index c4c6da21265e..5195857dcfd0 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c +++ b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c @@ -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("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") __naked void bpf_sk_release_listen_sk(void) { asm volatile ( diff --git a/tools/testing/selftests/bpf/progs/verifier_ringbuf.c b/tools/testing/selftests/bpf/progs/verifier_ringbuf.c index ae1d521f326c..2052facaa383 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ringbuf.c +++ b/tools/testing/selftests/bpf/progs/verifier_ringbuf.c @@ -12,7 +12,7 @@ struct { SEC("socket") __description("ringbuf: invalid reservation offset 1") -__failure __msg("R1 must have zero offset when passed to release func") +__failure __msg("R1 must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *") __failure_unpriv __naked void ringbuf_invalid_reservation_offset_1(void) { diff --git a/tools/testing/selftests/bpf/progs/verifier_sock.c b/tools/testing/selftests/bpf/progs/verifier_sock.c index ee76b51005ab..ff5e954bb6eb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_sock.c +++ b/tools/testing/selftests/bpf/progs/verifier_sock.c @@ -600,7 +600,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("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") __naked void bpf_sk_release_skb_sk(void) { asm volatile (" \ @@ -617,7 +617,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("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") __naked void bpf_sk_fullsock_skb_sk(void) { asm volatile (" \ @@ -641,7 +641,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("R1 must be referenced when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc") __naked void bpf_tcp_sock_skb_sk(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index ab25a81fd3a1..316534b860ae 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -76,7 +76,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "R1 must have zero offset when passed to release func or trusted arg to kfunc", + .errstr = "arg#0 expected pointer to ctx, but got PTR", .fixup_kfunc_btf_id = { { "bpf_kfunc_call_test_pass_ctx", 2 }, }, @@ -132,7 +132,7 @@ }, .prog_type = BPF_PROG_TYPE_SCHED_CLS, .result = REJECT, - .errstr = "R1 must have zero offset when passed to release func", + .errstr = "R1 must have a fixed offset of 0 when passed to a OBJ_RELEASE/KF_RELEASE flagged BPF helper/kfunc which takes a void *", .fixup_kfunc_btf_id = { { "bpf_kfunc_call_test_acquire", 3 }, { "bpf_kfunc_call_memb_release", 8 }, -- 2.45.2.627.g7a2c4fd464-goog /M