Subprog and helper/kfunc dynptr arguments ultimately use the same process_dynptr_func() validation. Route the subprog dynptr branch through check_func_arg() so register type, offset, and dynptr state are checked in the common order. The existing wrong-register-type test passed a NULL local to a callee that did not use the argument. Clang left the context pointer in R1, while GCC materialized zero. The common checker rejected the values at different stages and emitted different diagnostics. Obtain a PTR_TO_BTF_ID from bpf_get_current_task_btf() and keep its callee argument live. This makes both compilers exercise the intended register-type mismatch. Signed-off-by: Amery Hung --- kernel/bpf/verifier.c | 11 +---------- tools/testing/selftests/bpf/progs/dynptr_fail.c | 11 +++++------ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 237b81ee3159..fb0e2649bab3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -10882,7 +10882,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, nslots = btf_arg_slots(t); if (arg_type == ARG_SCALAR || arg_type == ARG_IGNORE || - arg_type == ARG_PTR_TO_CTX || + arg_type == ARG_PTR_TO_CTX || arg_type == ARG_PTR_TO_DYNPTR || base_type(arg_type) == ARG_PTR_TO_ARENA) { ret = check_func_arg(env, arg, slot, 0, &meta, env->insn_idx); if (ret) @@ -10911,15 +10911,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, reg_arg_name(env, argno)); return -EINVAL; } - } else if (arg_type == ARG_PTR_TO_DYNPTR) { - ret = check_func_arg_reg_off(env, reg, argno, ARG_PTR_TO_DYNPTR); - if (ret) - return ret; - - ret = process_dynptr_func(env, reg, argno, env->insn_idx, - arg_type, &meta); - if (ret) - return ret; } else if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) { int err; diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c index 9418dfe4d7b7..148cf4417322 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_fail.c +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c @@ -2053,19 +2053,18 @@ __noinline long global_call_bpf_dynptr(const struct bpf_dynptr *dynptr) /* Avoid leaving this global function empty to avoid having the compiler * optimize away the call to this global function. */ + __sink(dynptr); __sink(ret); return ret; } SEC("?raw_tp") -__failure __msg("R1 expected pointer to stack or const struct bpf_dynptr") +__failure __msg("R1 type=trusted_ptr_ expected=fp, dynptr_ptr") int test_dynptr_reg_type(void *ctx) { - struct task_struct *current = NULL; - /* R1 should be holding a PTR_TO_BTF_ID, so this shouldn't be a - * reg->type that can be passed to a function accepting a - * ARG_PTR_TO_DYNPTR | MEM_RDONLY. process_dynptr_func() should catch - * this. + struct task_struct *current = bpf_get_current_task_btf(); + /* R1 holds a PTR_TO_BTF_ID, which cannot be passed to a function + * accepting ARG_PTR_TO_DYNPTR | MEM_RDONLY. */ global_call_bpf_dynptr((const struct bpf_dynptr *)current); return 0; -- 2.52.0