* [PATCH] bpf: fix out-of-bounds frame access for async callback stack args
@ 2026-10-27 15:02 Henry Martin
0 siblings, 0 replies; only message in thread
From: Henry Martin @ 2026-10-27 15:02 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai
Cc: bpf, linux-kernel, Henry Martin
check_stack_arg_read() resolves the caller frame with
caller = vstate->frame[vstate->curframe - 1];
without checking whether the current frame is the outermost one
(curframe == 0). Regular programs always run with curframe > 0 inside
check_stack_arg_read(), but async callbacks do not: push_async_cb()
builds a fresh verifier state with frame[0] as the only frame and
curframe left at its kzalloc default 0.
A static subprogram invoked as an async callback (e.g. via
bpf_timer_set_callback) may declare up to MAX_BPF_FUNC_ARGS (12)
arguments, and arguments beyond the fifth are passed on the stack. When
such a callback executes an r11-based incoming stack argument load
(is_stack_arg_ldx: src_reg == BPF_REG_PARAMS, off > 0, 8-aligned), the
spi bound check against bpf_in_stack_arg_cnt() passes while curframe is
0, and the code above evaluates frame[-1]: an out-of-bounds read behind
the bpf_verifier_state.frame array. frame[] is embedded in struct
bpf_verifier_state, so for async callbacks frame[-1] reads the last 8
bytes of the object preceding the stack element in the verifier state
allocator, and the resulting garbage pointer is immediately used as
arg = &caller->stack_arg_regs[spi];
whose contents are copied into the verifier register state. The main
program path is not affected: do_check_common() forces arg_cnt == 1 for
subprog 0, so bpf_in_stack_arg_cnt() is 0 there and the bound check
rejects the load.
Reject r11-based incoming stack argument loads when the current frame
has no caller (curframe == 0): async callback prototypes cannot declare
stack arguments in the first place (e.g. the timer callback prototype
has only 3 arguments), so such a load can never be valid.
This issue was discovered by Tencent CodeBuddy Security.
Fixes: 0f6bd5e7a804 ("bpf: Support stack arguments for bpf functions")
Signed-off-by: Henry Martin <bsdhenrymartin@gmail.com>
---
kernel/bpf/verifier.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5f874979b8d75..1fb3e89ac59b5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4232,6 +4232,11 @@ static int check_stack_arg_read(struct bpf_verifier_env *env, struct bpf_func_st
return -EACCES;
}
+ if (vstate->curframe == 0) {
+ verbose(env, "stack arg read with no caller frame\n");
+ return -EINVAL;
+ }
+
caller = vstate->frame[vstate->curframe - 1];
arg = &caller->stack_arg_regs[spi];
cur = vstate->frame[vstate->curframe];
--
2.43.7
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-27 15:02 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27 15:02 [PATCH] bpf: fix out-of-bounds frame access for async callback stack args Henry Martin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®