lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20251220-helper_proto-v1-2-2206e0d9422d@gmail.com>
Date: Sat, 20 Dec 2025 19:35:05 +0800
From: Zesen Liu <ftyghome@...il.com>
To: Alexei Starovoitov <ast@...nel.org>, 
 Daniel Borkmann <daniel@...earbox.net>, Andrii Nakryiko <andrii@...nel.org>, 
 Martin KaFai Lau <martin.lau@...ux.dev>, 
 Eduard Zingerman <eddyz87@...il.com>, Song Liu <song@...nel.org>, 
 Yonghong Song <yonghong.song@...ux.dev>, 
 John Fastabend <john.fastabend@...il.com>, KP Singh <kpsingh@...nel.org>, 
 Stanislav Fomichev <sdf@...ichev.me>, Hao Luo <haoluo@...gle.com>, 
 Jiri Olsa <jolsa@...nel.org>, Matt Bobrowski <mattbobrowski@...gle.com>, 
 Steven Rostedt <rostedt@...dmis.org>, 
 Masami Hiramatsu <mhiramat@...nel.org>, 
 Mathieu Desnoyers <mathieu.desnoyers@...icios.com>, 
 "David S. Miller" <davem@...emloft.net>, Eric Dumazet <edumazet@...gle.com>, 
 Jakub Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>, 
 Simon Horman <horms@...nel.org>, Daniel Xu <dxu@...uu.xyz>, 
 Shuah Khan <shuah@...nel.org>
Cc: bpf@...r.kernel.org, linux-kernel@...r.kernel.org, 
 linux-trace-kernel@...r.kernel.org, netdev@...r.kernel.org, 
 linux-kselftest@...r.kernel.org, Zesen Liu <ftyghome@...il.com>, 
 Shuran Liu <electronlsr@...il.com>, Peili Gao <gplhust955@...il.com>, 
 Haoran Ni <haoran.ni.cs@...il.com>
Subject: [RFC PATCH bpf 2/2] selftests/bpf: add regression tests for
 snprintf and get_stack helpers

Add regression tests for bpf_snprintf(), bpf_snprintf_btf(), and
bpf_get_stack() to cover incorrect verifier assumptions caused by
incorrect function prototypes.

These tests reproduce the scenario where the verifier previously
incorrectly assumed that the destination buffer remained unwritten
across the helper call. The tests call these helpers and verify that
subsequent reads see the updated data, ensuring that the verifier
correctly marks the memory as clobbered and does not optimize away
the reads based on stale assumptions.

Co-developed-by: Shuran Liu <electronlsr@...il.com>
Signed-off-by: Shuran Liu <electronlsr@...il.com>
Co-developed-by: Peili Gao <gplhust955@...il.com>
Signed-off-by: Peili Gao <gplhust955@...il.com>
Co-developed-by: Haoran Ni <haoran.ni.cs@...il.com>
Signed-off-by: Haoran Ni <haoran.ni.cs@...il.com>
Signed-off-by: Zesen Liu <ftyghome@...il.com>
---
 tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c | 15 +++++++++++++--
 tools/testing/selftests/bpf/prog_tests/snprintf.c         |  6 ++++++
 tools/testing/selftests/bpf/prog_tests/snprintf_btf.c     |  3 +++
 tools/testing/selftests/bpf/progs/netif_receive_skb.c     | 13 ++++++++++++-
 tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c  | 11 ++++++++++-
 tools/testing/selftests/bpf/progs/test_snprintf.c         | 12 ++++++++++++
 6 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c b/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
index 858e0575f502..7c2774b49138 100644
--- a/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
+++ b/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
@@ -87,13 +87,13 @@ void test_get_stack_raw_tp(void)
 	const char *file = "./test_get_stack_rawtp.bpf.o";
 	const char *file_err = "./test_get_stack_rawtp_err.bpf.o";
 	const char *prog_name = "bpf_prog1";
-	int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP;
+	int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP, key = 0, valid_top_stack = 0;
 	struct perf_buffer *pb = NULL;
 	struct bpf_link *link = NULL;
 	struct timespec tv = {0, 10};
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	struct bpf_map *map;
+	struct bpf_map *map, *bss_map;
 	cpu_set_t cpu_set;
 
 	err = bpf_prog_test_load(file_err, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
@@ -135,6 +135,17 @@ void test_get_stack_raw_tp(void)
 	for (i = 0; i < MAX_CNT_RAWTP; i++)
 		nanosleep(&tv, NULL);
 
+	bss_map = bpf_object__find_map_by_name(obj, ".bss");
+	if (CHECK(!bss_map, "find .bss map", "not found\n"))
+		goto close_prog;
+
+	err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &key, &valid_top_stack);
+	if (CHECK(err, "lookup .bss", "err %d errno %d\n", err, errno))
+		goto close_prog;
+
+	if (!ASSERT_EQ(valid_top_stack, 1, "valid_top_stack"))
+		goto close_prog;
+
 	while (exp_cnt > 0) {
 		err = perf_buffer__poll(pb, 100);
 		if (err < 0 && CHECK(err < 0, "pb__poll", "err %d\n", err))
diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf.c b/tools/testing/selftests/bpf/prog_tests/snprintf.c
index 594441acb707..80d6f2655b5f 100644
--- a/tools/testing/selftests/bpf/prog_tests/snprintf.c
+++ b/tools/testing/selftests/bpf/prog_tests/snprintf.c
@@ -33,6 +33,9 @@
 
 #define EXP_NO_BUF_RET 29
 
+#define EXP_STACK_OUT  "stack_out"
+#define EXP_STACK_RET  sizeof(EXP_STACK_OUT)
+
 static void test_snprintf_positive(void)
 {
 	char exp_addr_out[] = EXP_ADDR_OUT;
@@ -79,6 +82,9 @@ static void test_snprintf_positive(void)
 
 	ASSERT_EQ(skel->bss->nobuf_ret, EXP_NO_BUF_RET, "no_buf_ret");
 
+	ASSERT_EQ(skel->bss->stack_ret, EXP_STACK_RET, "stack_ret");
+	ASSERT_STREQ(skel->bss->stack_out, EXP_STACK_OUT, "stack_out");
+
 cleanup:
 	test_snprintf__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c b/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c
index dd41b826be30..a2e400a4880d 100644
--- a/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c
@@ -55,6 +55,9 @@ void serial_test_snprintf_btf(void)
 		  bss->ran_subtests))
 		goto cleanup;
 
+	if (!ASSERT_EQ(bss->stack_out_test_passed, 1, "stack output test failed"))
+		goto cleanup;
+
 cleanup:
 	netif_receive_skb__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/progs/netif_receive_skb.c b/tools/testing/selftests/bpf/progs/netif_receive_skb.c
index 9e067dcbf607..f78d5f56f6c9 100644
--- a/tools/testing/selftests/bpf/progs/netif_receive_skb.c
+++ b/tools/testing/selftests/bpf/progs/netif_receive_skb.c
@@ -12,9 +12,11 @@
 long ret = 0;
 int num_subtests = 0;
 int ran_subtests = 0;
+int stack_out_test_passed = 0;
 bool skip = false;
 
-#define STRSIZE			2048
+#define STRSIZE 2048
+#define STACK_STRSIZE 64
 #define EXPECTED_STRSIZE	256
 
 #if defined(bpf_target_s390)
@@ -98,6 +100,7 @@ int BPF_PROG(trace_netif_receive_skb, struct sk_buff *skb)
 	__u32 key = 0;
 	int i, __ret;
 	char *str;
+	char stack_out[STACK_STRSIZE] = { };
 
 #if __has_builtin(__builtin_btf_type_id)
 	str = bpf_map_lookup_elem(&strdata, &key);
@@ -124,6 +127,13 @@ int BPF_PROG(trace_netif_receive_skb, struct sk_buff *skb)
 		ret = -ERANGE;
 	}
 
+	/* Check when writing to a buffer on the stack */
+	p.type_id = bpf_core_type_id_kernel(struct sk_buff);
+	p.ptr = skb;
+	ret = bpf_snprintf_btf(stack_out, STACK_STRSIZE, &p, sizeof(p), 0);
+	if (ret >= 0 && stack_out[0] != '\0')
+		stack_out_test_passed = 1;
+
 	/* Verify type display for various types. */
 
 	/* simple int */
@@ -242,6 +252,7 @@ int BPF_PROG(trace_netif_receive_skb, struct sk_buff *skb)
 	TEST_BTF(str, struct bpf_insn, BTF_F_NONAME, "{1,0x2,0x3,4,5,}",
 		 {.code = 1, .dst_reg = 0x2, .src_reg = 0x3, .off = 4,
 		  .imm = 5,});
+
 #else
 	skip = true;
 #endif
diff --git a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
index b6a6eb279e54..57723dc823a0 100644
--- a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
+++ b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
@@ -54,14 +54,17 @@ struct {
 	__type(value, __u64[2 * MAX_STACK_RAWTP]);
 } rawdata_map SEC(".maps");
 
+int valid_top_stack = 0;
+
 SEC("raw_tracepoint/sys_enter")
 int bpf_prog1(void *ctx)
 {
 	int max_len, max_buildid_len, total_size;
 	struct stack_trace_t *data;
-	long usize, ksize;
+	long usize, ksize, top_usize;
 	void *raw_data;
 	__u32 key = 0;
+	__u64 top_user_stack = 0;
 
 	data = bpf_map_lookup_elem(&stackdata_map, &key);
 	if (!data)
@@ -88,6 +91,12 @@ int bpf_prog1(void *ctx)
 	if (usize < 0)
 		return 0;
 
+	/* checks if the verifier correctly marks the stack variable as written. */
+	top_usize = bpf_get_stack(ctx, &top_user_stack, sizeof(__u64),
+				   BPF_F_USER_STACK);
+	if (top_usize > 0 && top_user_stack != 0)
+		valid_top_stack = 1;
+
 	ksize = bpf_get_stack(ctx, raw_data + usize, max_len - usize, 0);
 	if (ksize < 0)
 		return 0;
diff --git a/tools/testing/selftests/bpf/progs/test_snprintf.c b/tools/testing/selftests/bpf/progs/test_snprintf.c
index 8fda07544023..ce78fd7add03 100644
--- a/tools/testing/selftests/bpf/progs/test_snprintf.c
+++ b/tools/testing/selftests/bpf/progs/test_snprintf.c
@@ -32,6 +32,9 @@ long noarg_ret = 0;
 
 long nobuf_ret = 0;
 
+char stack_out[64] = {};
+long stack_ret = 0;
+
 extern const void schedule __ksym;
 
 SEC("raw_tp/sys_enter")
@@ -42,6 +45,7 @@ int handler(const void *ctx)
 	const __u8 ex_ipv6[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
 	static const char str1[] = "str1";
 	static const char longstr[] = "longstr";
+	char buf[64] = {};
 
 	if ((int)bpf_get_current_pid_tgid() != pid)
 		return 0;
@@ -71,6 +75,14 @@ int handler(const void *ctx)
 	/* No buffer */
 	nobuf_ret = BPF_SNPRINTF(NULL, 0, "only interested in length %d", 60);
 
+	/* Write to a buffer on the stack */
+	stack_ret = BPF_SNPRINTF(buf, sizeof(buf), "stack_out");
+    /* The condition is necessary to check if the verifier
+     * correctly marks the stack memory as written.
+     */
+	if (buf[0] != '\0')
+		__builtin_memcpy(stack_out, buf, 64);
+
 	return 0;
 }
 

-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ