[PATCH bpf] bpf: Fix wrong frame passed to check_fastcall_stack_contract() in check_stack_read_fixed_off()

From: Ömer Mete Kaya

Date: Sun Sep 27 2026 - 18:28:01 EST


check_stack_read_fixed_off() calls check_fastcall_stack_contract() with
'state' (the current frame, i.e. the callee) instead of 'reg_state'
(the frame that owns the stack being read, i.e. the caller).

In a bpf2bpf call where a callee reads from a PTR_TO_STACK pointing
into the caller's frame, 'reg_state' and 'state' are different:
'reg_state' is the caller's frame, while 'state' is the callee's.
Passing 'state' causes the fastcall contract to be checked against the
callee's subprog instead of the caller's, leaving the caller's fastcall
region unprotected and potentially can cause an incorrect fastcall
rewrite.

The three sibling functions are all correct:
check_stack_write_fixed_off -> passes 'state' (writes to curframe)
check_stack_write_var_off -> passes 'state' (writes to curframe)
check_stack_read_var_off -> passes 'ptr_state' (reads from ptr's frame)

Fix check_stack_read_fixed_off() to match the read pattern and pass
'reg_state' instead of 'state'.

Fixes: 5b5f51bff1b6 ("bpf: no_caller_saved_registers attribute for helper calls")
Signed-off-by: Ömer Mete Kaya <omermetekaya0@xxxxxxxxx>
---
kernel/bpf/verifier.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1cd92336309b..5c523a9bde6b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4020,7 +4020,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
reg = &reg_state->stack[spi].spilled_ptr;

mark_stack_slot_scratched(env, spi);
- check_fastcall_stack_contract(env, state, env->insn_idx, off);
+ check_fastcall_stack_contract(env, reg_state, env->insn_idx, off);

/*
* Refine the in-progress load record's origin to the source stack slot.
--
2.55.0