BPF List
 help / color / mirror / Atom feed
From: Daniel Borkmann <daniel@iogearbox.net>
To: memxor@gmail.com
Cc: eddyz87@gmail.com, puranjay@kernel.org, info@starlabs.sg,
	bpf@vger.kernel.org
Subject: [PATCH bpf-next 3/4] selftests/bpf: Add tests for sub-register zext across state pruning
Date: Wed,  5 Aug 2026 20:44:43 +0200	[thread overview]
Message-ID: <20260805184444.292828-3-daniel@iogearbox.net> (raw)
In-Reply-To: <20260805184444.292828-1-daniel@iogearbox.net>

Add the example walked through in the previous patch as a test case: define
r6 twice, by a 64-bit write on the path the verifier explores first and by
a 32-bit write on the path explored second, and read it 64-bit after the
two paths meet. The second path is pruned at the merge, so the w6 definition
never reaches the 64-bit read, and its zero extension must be marked at the
pruning point instead.

The second test moves the pruning point into a callee, so that the marking
walks the caller frames as well. Their caller saved registers are NOT_INIT
while the callee runs, and marking those would set zext_dst on the call
insn, which has no destination register to zero extend.

The third test uses a BPF_CMPXCHG fetching into r0 as the 32-bit define.
Unlike the other definitions this one is patched by the fixup pass even
where the JIT does not ask for zero extension, so it also pins down that
the marks added at a pruning point reach x86-64.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t subreg
  [...]
  #668/1   verifier_subreg/add32 reg zero extend check:OK
  #668/2   verifier_subreg/add32 reg zero extend check @unpriv:OK
  [...]
  #668/80  verifier_subreg/lsh32_imm31_value:OK
  #668/81  verifier_subreg/rsh32_imm31_value:OK
  #668/82  verifier_subreg/arsh32_imm31_value:OK
  #668/83  verifier_subreg/lsh32_unknown_precise_bounds:OK
  #668/84  verifier_subreg/rsh32_unknown_bounds:OK
  #668/85  verifier_subreg/subreg zero extend check across state pruning:OK
  #668/86  verifier_subreg/subreg zero extend check across state pruning in a callee:OK
  #668/87  verifier_subreg/subreg zero extend check across state pruning with cmpxchg:OK
  #668     verifier_subreg:OK
  Summary: 1/87 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 .../selftests/bpf/progs/verifier_subreg.c     | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/verifier_subreg.c b/tools/testing/selftests/bpf/progs/verifier_subreg.c
index 73b5b0cf6706..101f2a8bff7f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_subreg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_subreg.c
@@ -3,6 +3,7 @@
 
 #include <linux/bpf.h>
 #include <bpf/bpf_helpers.h>
+#include "../../../include/linux/filter.h"
 #include "bpf_misc.h"
 
 /* This file contains sub-register zero extension checks for insns defining
@@ -990,4 +991,91 @@ l0_%=:	r0 = r6;					\
 	: __clobber_all);
 }
 
+SEC("socket")
+__description("subreg zero extend check across state pruning")
+__flag(BPF_F_TEST_RND_HI32)
+__flag(BPF_F_TEST_STATE_FREQ)
+__success __retval(0)
+__naked void subreg_zero_extend_check_pruning(void)
+{
+	asm volatile ("					\
+	r7 = *(u32 *)(r1 + %[__sk_buff_len]);		\
+	r6 = 0;			/* 64-bit define */	\
+	if r7 != 0 goto l1_%=;				\
+	goto l0_%=;					\
+l1_%=:	w6 = 0;			/* 32-bit define */	\
+l0_%=:	r0 = r6;		/* 64-bit read */	\
+	r0 >>= 32;					\
+	exit;						\
+"	:
+	: __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len))
+	: __clobber_all);
+}
+
+/*
+ * Same as the previous test, but with the pruning point inside a callee. The
+ * marking then also walks the caller frames, whose caller saved registers are
+ * NOT_INIT while the callee runs, and must not mark the call insn.
+ */
+SEC("socket")
+__description("subreg zero extend check across state pruning in a callee")
+__flag(BPF_F_TEST_RND_HI32)
+__flag(BPF_F_TEST_STATE_FREQ)
+__success __retval(0)
+__naked void subreg_zero_extend_check_pruning_callee(void)
+{
+	asm volatile ("					\
+	r1 = *(u32 *)(r1 + %[__sk_buff_len]);		\
+	call subreg_zero_extend_check_pruning_subprog;	\
+	r0 >>= 32;					\
+	exit;						\
+"	:
+	: __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len))
+	: __clobber_all);
+}
+
+static __used __naked void subreg_zero_extend_check_pruning_subprog(void)
+{
+	asm volatile ("					\
+	r0 = 0;			/* 64-bit define */	\
+	if r1 != 0 goto l1_%=;				\
+	goto l0_%=;					\
+l1_%=:	w0 = 0;			/* 32-bit define */	\
+l0_%=:	exit;			/* 64-bit read */	\
+"	::: __clobber_all);
+}
+
+/*
+ * Same as the first test, but with the 32-bit define coming from a BPF_CMPXCHG
+ * fetching into r0. Unlike the other definitions this one is patched even where
+ * the JIT does not ask for zero extension, see bpf_opt_subreg_zext_lo32_rnd_hi32().
+ * The stack slot is left as STACK_MISC by the initial 32-bit store so that the
+ * cmpxchg does not alter it, otherwise the two paths would not converge.
+ */
+SEC("socket")
+__description("subreg zero extend check across state pruning with cmpxchg")
+__flag(BPF_F_TEST_RND_HI32)
+__flag(BPF_F_TEST_STATE_FREQ)
+__success __retval(0)
+__naked void subreg_zero_extend_check_pruning_cmpxchg(void)
+{
+	asm volatile ("					\
+	r7 = *(u32 *)(r1 + %[__sk_buff_len]);		\
+	r1 = 1;						\
+	*(u32 *)(r10 - 4) = r1;				\
+	call %[bpf_get_prandom_u32];	/* 64-bit define */\
+	if r7 != 0 goto l1_%=;				\
+	goto l0_%=;					\
+l1_%=:	r2 = 2;						\
+	.8byte %[cmpxchg32];		/* 32-bit define */\
+l0_%=:	r0 >>= 32;			/* 64-bit read */\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32),
+	  __imm_const(__sk_buff_len, offsetof(struct __sk_buff, len)),
+	  __imm_insn(cmpxchg32,
+		     BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -4))
+	: __clobber_all);
+}
+
 char _license[] SEC("license") = "GPL";
-- 
2.43.0


  parent reply	other threads:[~2026-08-05 18:44 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-05 18:44 [PATCH bpf-next 1/4] bpf: Mark pending sub-register zero extension before pruning a state Daniel Borkmann
2026-08-05 18:44 ` [PATCH bpf-next 2/4] bpf: Mark pending zero extension of arena ptrs " Daniel Borkmann
2026-08-05 18:44 ` Daniel Borkmann [this message]
2026-08-05 18:44 ` [PATCH bpf-next 4/4] selftests/bpf: Add test for arena pointer zext across state pruning Daniel Borkmann
2026-08-05 20:14   ` sashiko-bot
2026-08-05 19:11 ` [PATCH bpf-next 1/4] bpf: Mark pending sub-register zero extension before pruning a state Eduard Zingerman
2026-08-05 19:54   ` Daniel Borkmann
2026-08-05 20:36     ` Eduard Zingerman
2026-08-05 20:22 ` sashiko-bot

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=20260805184444.292828-3-daniel@iogearbox.net \
    --to=daniel@iogearbox.net \
    --cc=bpf@vger.kernel.org \
    --cc=eddyz87@gmail.com \
    --cc=info@starlabs.sg \
    --cc=memxor@gmail.com \
    --cc=puranjay@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox