All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Emil Tsalapatis <emil@etsalapatis.com>,
	kkd@meta.com, kernel-team@meta.com
Subject: [PATCH bpf-next v1 1/3] bpf: Check fastcall contract for helper and kfunc stack buffers
Date: Wed, 23 Sep 2026 10:41:56 +0200	[thread overview]
Message-ID: <20260923084201.2437625-2-memxor@gmail.com> (raw)
In-Reply-To: <20260923084201.2437625-1-memxor@gmail.com>

When the verifier inlines a bpf_fastcall helper or kfunc, it removes the
spill/fill pairs that clang emitted around the call and lowers the
subprogram's stack depth so that their slots are no longer part of the
frame. This is only safe if nothing else accesses those slots or any slot
below them. check_fastcall_stack_contract() enforces this: an access to
that region from outside a fastcall pattern disables the rewrite for the
subprogram.

Stack loads and stores call this check, but stack buffers passed to
helpers and kfuncs are validated by check_stack_range_initialized(),
which does not. If such a buffer is the only other access to the region,
the rewrite is still applied, the JIT reserves a frame that does not
contain the buffer, and the helper reads or writes live kernel stack
below the program's frame. For example, without CAP_PERFMON:

	*(u64 *)(r10 - 8) = r1;
	call bpf_get_smp_processor_id;
	r1 = *(u64 *)(r10 - 8);
	r2 = 0;
	r3 = r10;
	r3 += -64;
	r4 = 8;
	call bpf_skb_load_bytes;

is accepted with a stack depth of 0 instead of 64.

Some buffers got the check indirectly. For constant-sized outputs in raw
mode, mark_raw_stack() marks the buffer initialized through ordinary
byte stores, which call the check. Commit 5da4a9f26fca ("bpf: Preserve
stack initialization for generic output buffers") limited raw mode to
programs that may read uninitialized stack, so outputs of programs
without CAP_PERFMON lost the check, as in the example above. Buffers
that never used raw mode have lacked the check since fastcall support
was added: helper inputs such as a map key whose only store is a fastcall
spill, variable-sized outputs, and buffers at a variable stack offset.

Call check_fastcall_stack_contract() from check_stack_range_initialized()
for every nonempty access, before the raw-mode return. Use the frame that
owns the buffer, so that a callee passing a pointer into its caller's
stack disables the caller's rewrite. Zero-sized accesses touch no stack
and leave the rewrite enabled. Variable-offset stack reads now get the
check through check_stack_range_initialized(), so drop the separate call
in check_stack_read_var_off().

Fixes: 5b5f51bff1b6 ("bpf: no_caller_saved_registers attribute for helper calls")
Fixes: 5da4a9f26fca ("bpf: Preserve stack initialization for generic output buffers")
Link: https://lore.kernel.org/bpf/85f9995a43edb70c76615280e103dc5325742e88330f36c97962ee35f17e3e32@mail.kernel.org
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 kernel/bpf/verifier.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a7c9e2d8965d..1d9defa231d1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4138,7 +4138,6 @@ static int check_stack_read_var_off(struct bpf_verifier_env *env, struct bpf_reg
 				  dst_regno);
 	if (err)
 		return err;
-	check_fastcall_stack_contract(env, ptr_state, env->insn_idx, min_off);
 	return 0;
 }
 
@@ -7041,6 +7040,13 @@ static int check_stack_range_initialized(
 		max_off = reg_smax(reg) + off;
 	}
 
+	/*
+	 * Helpers and kfuncs can access stack slots without going through the
+	 * regular stack read/write paths, including when raw mode is disabled.
+	 */
+	if (access_size)
+		check_fastcall_stack_contract(env, state, env->insn_idx, min_off);
+
 	/* Unprivileged outputs retain each byte's initialization state. */
 	if (raw_mode) {
 		meta->arg_raw_mem.size[arg_slot] = access_size;
-- 
2.53.0


  reply	other threads:[~2026-09-23  8:42 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-23  8:41 [PATCH bpf-next v1 0/3] Fix fastcall rewrite with indirect stack accesses Kumar Kartikeya Dwivedi
2026-09-23  8:41 ` Kumar Kartikeya Dwivedi [this message]
2026-09-24  5:55   ` [PATCH bpf-next v1 1/3] bpf: Check fastcall contract for helper and kfunc stack buffers Alexei Starovoitov
2026-09-23  8:41 ` [PATCH bpf-next v1 2/3] bpf: Check fastcall contract of the frame a stack read targets Kumar Kartikeya Dwivedi
2026-09-23  8:41 ` [PATCH bpf-next v1 3/3] selftests/bpf: Test fastcall rewrite with indirect stack accesses Kumar Kartikeya Dwivedi
2026-09-23  9:33   ` bot+bpf-ci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260923084201.2437625-2-memxor@gmail.com \
    --to=memxor@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=kernel-team@meta.com \
    --cc=kkd@meta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.