diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 427a6d828e01..cdbe9fdf474f 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2609,7 +2609,7 @@ struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct goto finalize; if (IS_ENABLED(CONFIG_BPF_JIT_ALWAYS_ON) || - bpf_prog_has_kfunc_call(fp) || (env && env->subprog_info[0].stack_arg_cnt)) + bpf_prog_has_kfunc_call(fp)) jit_needed = true; if (!bpf_prog_select_interpreter(fp)) diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 19056016eed8..2cec4e8cd4a0 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -1407,7 +1407,7 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env) verbose(env, "calling kernel functions are not allowed in non-JITed programs\n"); return -EINVAL; } - for (i = 1; i < env->subprog_cnt; i++) { + for (i = 0; i < env->subprog_cnt; i++) { if (bpf_in_stack_arg_cnt(&env->subprog_info[i])) { verbose(env, "stack args are not supported in non-JITed programs\n"); return -EINVAL; diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 7f4a0e4c2c49..0aadfbae0acc 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c @@ -806,7 +806,9 @@ static bool arg_track_join(struct bpf_verifier_env *env, int idx, int target, in return true; verbose(env, "arg JOIN insn %d -> %d ", idx, target); - if (r >= 0) + if (r >= MAX_BPF_REG) + verbose(env, "sa%d: ", r - MAX_BPF_REG); + else if (r >= 0) verbose(env, "r%d: ", r); else verbose(env, "fp%+d: ", r * 8); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 76a07f09ab64..8dd79b735a69 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9118,11 +9118,17 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, struct bpf_func_state *caller = cur_func(env); struct bpf_verifier_log *log = &env->log; u32 i; - int ret; + int ret, err; ret = btf_prepare_func_args(env, subprog); - if (ret) + if (ret) { + if (bpf_in_stack_arg_cnt(sub) > 0) { + err = check_outgoing_stack_args(env, caller, sub->arg_cnt); + if (err) + return err; + } return ret; + } ret = check_outgoing_stack_args(env, caller, sub->arg_cnt); if (ret) diff --git a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c index da34e8456b6c..99bc115f8380 100644 --- a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c +++ b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c @@ -21,6 +21,10 @@ int subprog_pruning_call_before_load_6args(int a, int b, int c, int d, int e, in return a + b + c + d + e + f; } +void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g) +{ +} + #else int subprog_bad_order_6args(void) @@ -38,4 +42,8 @@ int subprog_pruning_call_before_load_6args(void) return 0; } +void subprog_bad_ptr_7args(void) +{ +} + #endif diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c index d43a9b42034c..7e0ce5db28a0 100644 --- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c +++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c @@ -114,8 +114,10 @@ __naked void stack_arg_gap_at_minus8(void) SEC("tc") __description("stack_arg: pruning with different stack arg types") -__failure +__failure __log_level(2) __flag(BPF_F_TEST_STATE_FREQ) +__msg("arg JOIN insn 9 -> 10 r1: fp0-8 + _ => fp0-8|fp0+0") +__msg("arg JOIN insn 9 -> 10 sa0: fp0-8 + _ => fp0-8|fp0+0") __msg("R{{[0-9]}} invalid mem access 'scalar'") __naked void stack_arg_pruning_type_mismatch(void) { @@ -152,7 +154,7 @@ __naked void stack_arg_pruning_type_mismatch(void) SEC("tc") __description("stack_arg: release_reference invalidates stack arg slot") __failure -__msg("R{{[0-9]}} !read_ok") +__msg("callee expects 6 args, stack arg1 is not initialized") __naked void stack_arg_release_ref(void) { asm volatile ( @@ -201,7 +203,7 @@ __naked void stack_arg_release_ref(void) SEC("tc") __description("stack_arg: pkt pointer in stack arg slot invalidated after pull_data") __failure -__msg("R{{[0-9]}} !read_ok") +__msg("callee expects 6 args, stack arg1 is not initialized") __naked void stack_arg_stale_pkt_ptr(void) { asm volatile ( diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c index 1240cf8a40d6..c9fe4857da3f 100644 --- a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c +++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c @@ -112,6 +112,64 @@ __naked void stack_arg_pruning_load_after_call(void) ); } +/* + * "bad_ptr": the first arg is 'long *', which is not a recognized pointer + * type for static subprogs (not ctx, dynptr, or tagged). btf_prepare_func_args() + * sets arg_cnt = 7 / stack_arg_cnt = 2, then fails with -EINVAL. The subprog + * is marked unreliable but the call still proceeds for static subprogs. + */ +__noinline __used __naked +static void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g) +{ + asm volatile ( + "r0 = *(u64 *)(r11 + 8);" + "r1 = *(u64 *)(r11 + 16);" + "exit;" + ::: __clobber_all + ); +} + +SEC("tc") +__description("stack_arg: read without caller write") +__failure +__msg("callee expects 7 args, stack arg1 is not initialized") +__btf_func_path("btf__verifier_stack_arg_order.bpf.o") +__naked void stack_arg_read_without_write_1(void) +{ + asm volatile ( + "r1 = 0;" + "r2 = 0;" + "r3 = 0;" + "r4 = 0;" + "r5 = 0;" + "call subprog_bad_ptr_7args;" + "exit;" + ::: __clobber_all + ); +} + +SEC("tc") +__description("stack_arg: read with not-initialized caller write") +__failure +__msg("R0 !read_ok") +__btf_func_path("btf__verifier_stack_arg_order.bpf.o") +__naked void stack_arg_read_without_write_2(void) +{ + asm volatile ( + "r1 = 0;" + "r2 = 0;" + "r3 = 0;" + "r4 = 0;" + "r5 = 0;" + "*(u64 *)(r11 - 8) = 0;" + "*(u64 *)(r11 - 16) = 0;" + "call subprog_bad_ptr_7args;" + "call subprog_bad_ptr_7args;" + "exit;" + ::: __clobber_all + ); +} + #else SEC("socket")