BPF subprogram arguments beyond r1-r5 are kept separately in bpf_func_state::stack_arg_regs. State comparison and precision backtracking account for these arguments, but propagate_precision() does not seed backtracking from precise stack argument entries in an equivalent state. Consequently, when state pruning requires precision to be propagated, the producer of an outgoing scalar stack argument can remain imprecise. A later state can then be pruned despite a different argument value. This can cause the verifier to miss value-dependent safety checks and accept an unsafe program. The conservative fallback in bpf_mark_all_scalars_precise() has the same gap: it marks scalar registers and ordinary scalar spills, but not scalar stack arguments. Include precise outgoing stack arguments in the backtracking seed used by propagate_precision(), and mark them precise in the fallback path as well. Reaching the affected calling convention requires permission to load a BPF-to-BPF program and a JIT with stack-argument support. This is not an ordinary unprivileged-BPF path, but it is reachable through an appropriately delegated BPF token. An isolated x86-64 QEMU reproducer confirmed native kernel stack corruption, including a complete overwrite of a JIT return address. The reproducer is available privately on request. Fixes: 0f6bd5e7a804 ("bpf: Support stack arguments for bpf functions") Assisted-by: LLM Signed-off-by: Jan-Gerd Tenberge --- A source reproducer and benign load-only regression tests are available privately on request. They are intentionally not included publicly in accordance with Documentation/process/security-bugs.rst guidance for AI-assisted findings. Please queue this fix for stable kernels containing commit 0f6bd5e7a804 ("bpf: Support stack arguments for bpf functions"). kernel/bpf/backtrack.c | 10 ++++++++++ kernel/bpf/states.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c index 47282ffeeaf9..707cf12d5e91 100644 --- a/kernel/bpf/backtrack.c +++ b/kernel/bpf/backtrack.c @@ -705,6 +705,16 @@ void bpf_mark_all_scalars_precise(struct bpf_verifier_env *env, i, -(j + 1) * 8); } } + bpf_for_each_spilled_stack_arg(j, func, reg) { + if (!reg || reg->type != SCALAR_VALUE || reg->precise) + continue; + reg->precise = true; + if (env->log.level & BPF_LOG_LEVEL2) { + verbose(env, + "force_precise: frame%d: forcing sa%d to be precise\n", + i, j); + } + } } } } diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 012b82513a3b..7f995521baaf 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -1062,6 +1062,20 @@ static int propagate_precision(struct bpf_verifier_env *env, bpf_bt_set_frame_slot(&env->bt, fr, i); first = false; } + + bpf_for_each_spilled_stack_arg(i, state, state_reg) { + if (!state_reg || state_reg->type != SCALAR_VALUE || + !state_reg->precise) + continue; + if (env->log.level & BPF_LOG_LEVEL2) { + if (first) + verbose(env, "frame %d: propagating sa%d", fr, i); + else + verbose(env, ",sa%d", i); + } + bt_set_frame_stack_arg_slot(&env->bt, fr, i); + first = false; + } if (!first && (env->log.level & BPF_LOG_LEVEL2)) verbose(env, "\n"); } -- 2.54.0 (Apple Git-157)