* [PATCH bpf 2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements
2026-09-10 12:22 [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk() Jiayuan Chen
@ 2026-09-10 12:22 ` Jiayuan Chen
2026-09-10 22:01 ` Eduard Zingerman
2026-09-10 21:49 ` [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk() Eduard Zingerman
2026-09-11 0:00 ` patchwork-bot+netdevbpf
2 siblings, 1 reply; 5+ messages in thread
From: Jiayuan Chen @ 2026-09-10 12:22 UTC (permalink / raw)
To: bpf
Cc: Jiayuan Chen, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Shuah Khan, Yazhou Tang,
Amery Hung, Matt Bobrowski, Tejun Heo, linux-kernel,
linux-kselftest
The program stashes a bpf_obj_new() object whose type ends with a flexible
array of empty structs, then reads it back as an untrusted kptr. Without
the previous patch this divides by zero in btf_struct_walk() instead of
being rejected.
# ./test_progs -t verifier_btf_flex_array
...
#602 verifier_btf_flex_array:OK
Summary: 1/1 PASSED, 0 SKIPPED, 0/0 FAILED
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../bpf/progs/verifier_btf_flex_array.c | 56 +++++++++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_btf_flex_array.c
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index f7f94ccebce2..33a5da37e3ad 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -24,6 +24,7 @@
#include "verifier_bpf_trap.skel.h"
#include "verifier_bswap.skel.h"
#include "verifier_btf_ctx_access.skel.h"
+#include "verifier_btf_flex_array.skel.h"
#include "verifier_btf_unreliable_prog.skel.h"
#include "verifier_call_large_imm.skel.h"
#include "verifier_cfg.skel.h"
@@ -188,6 +189,7 @@ void test_verifier_bpf_get_stack(void) { RUN(verifier_bpf_get_stack); }
void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); }
void test_verifier_bswap(void) { RUN(verifier_bswap); }
void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); }
+void test_verifier_btf_flex_array(void) { RUN(verifier_btf_flex_array); }
void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); }
void test_verifier_call_large_imm(void) { RUN(verifier_call_large_imm); }
void test_verifier_cfg(void) { RUN(verifier_cfg); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_btf_flex_array.c b/tools/testing/selftests/bpf/progs/verifier_btf_flex_array.c
new file mode 100644
index 000000000000..59b84261f622
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_btf_flex_array.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+#include "bpf_experimental.h"
+#include "bpf_misc.h"
+
+struct test_empty_event {};
+
+struct test_flex_batch {
+ int nr;
+ struct test_empty_event events[];
+};
+
+struct map_value {
+ struct test_flex_batch __kptr *batch;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __type(key, int);
+ __type(value, struct map_value);
+ __uint(max_entries, 1);
+} batches SEC(".maps");
+
+SEC("syscall")
+__description("btf walk into flexible array of zero-sized elements")
+__failure __msg("access beyond struct test_flex_batch at off 4 size 1")
+int stash_and_peek(void *ctx)
+{
+ struct test_flex_batch *b, *old;
+ struct map_value *v;
+ int key = 0;
+
+ v = bpf_map_lookup_elem(&batches, &key);
+ if (!v)
+ return 0;
+
+ b = bpf_obj_new(struct test_flex_batch);
+ if (!b)
+ return 0;
+ b->nr = 1;
+
+ old = bpf_kptr_xchg(&v->batch, b);
+ if (old)
+ bpf_obj_drop(old);
+
+ b = v->batch;
+ if (!b)
+ return 0;
+
+ return b->nr + *(char *)&b->events[0];
+}
+
+char _license[] SEC("license") = "GPL";
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH bpf 2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements
2026-09-10 12:22 ` [PATCH bpf 2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements Jiayuan Chen
@ 2026-09-10 22:01 ` Eduard Zingerman
0 siblings, 0 replies; 5+ messages in thread
From: Eduard Zingerman @ 2026-09-10 22:01 UTC (permalink / raw)
To: Jiayuan Chen, bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
Shuah Khan, Yazhou Tang, Amery Hung, Matt Bobrowski, Tejun Heo,
linux-kernel, linux-kselftest
On Thu, 2026-09-10 at 20:22 +0800, Jiayuan Chen wrote:
> The program stashes a bpf_obj_new() object whose type ends with a flexible
> array of empty structs, then reads it back as an untrusted kptr. Without
> the previous patch this divides by zero in btf_struct_walk() instead of
> being rejected.
>
> # ./test_progs -t verifier_btf_flex_array
> ...
> #602 verifier_btf_flex_array:OK
> Summary: 1/1 PASSED, 0 SKIPPED, 0/0 FAILED
>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
> ---
Please simplify test case as follows:
SEC("syscall")
__failure __msg("access beyond struct test_flex_batch at off 4 size 1")
int stash_and_peek(void *ctx)
{
struct map_value *v;
int key = 0;
v = bpf_map_lookup_elem(&batches, &key);
if (!v || !v->batch)
return 0;
return *(char *)&v->batch->events[0];
}
Also, aren't there already test cases for flex arrays somewhere?
Probably no need to add a new file.
...
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk()
2026-09-10 12:22 [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk() Jiayuan Chen
2026-09-10 12:22 ` [PATCH bpf 2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements Jiayuan Chen
@ 2026-09-10 21:49 ` Eduard Zingerman
2026-09-11 0:00 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 5+ messages in thread
From: Eduard Zingerman @ 2026-09-10 21:49 UTC (permalink / raw)
To: Jiayuan Chen, bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
Shuah Khan, Amery Hung, Tejun Heo, Matt Bobrowski, linux-kernel,
linux-kselftest
On Thu, 2026-09-10 at 20:22 +0800, Jiayuan Chen wrote:
> When an access goes past the struct and the last member is a flexible
> array, btf_struct_walk() folds the offset back into a single element with
> (off - moff) % t->size, but never checks that the element type has a size.
>
> BTF takes an empty struct, so this in program BTF
>
> /* event could be empty */
> struct event {
> #ifdef HAVE_TIMESTAMP
> __u64 ts;
> #endif
> };
>
> struct batch {
> int nr;
> struct event events[];
> };
>
> divides by zero at prog load time. Getting there needs a PTR_TO_BTF_ID that
> is not MEM_ALLOC, e.g. a plain read of a local kptr stashed in a map from a
> sleepable program.
>
> Oops: divide error: 0000 [#1] SMP KASAN PTI
> RIP: 0010:btf_struct_walk+0x53f/0x1570
> Call Trace:
> <TASK>
> btf_struct_access+0x42a/0xcd0
> check_ptr_to_btf_access+0x4dc/0x1160
> check_mem_access+0x3a45/0x8740
> check_load_mem+0x36a/0xd10
> do_check_common+0x3ef0/0xb210
> bpf_check+0x6d3b/0x8580
> bpf_prog_load+0xf7c/0x2720
> __sys_bpf+0xa83/0x3690
> __x64_sys_bpf+0xc7/0x150
> x64_sys_call+0x1f3f/0x27e0
> do_syscall_64+0xe5/0x610
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
> </TASK>
>
> Reject a zero-sized element type. The fixed array path in the same function
> already bails out on the same thing:
>
> btf_struct_walk()
> ...
> /* skip empty array */
> if (moff == mtrue_end)
> continue;
>
> msize /= total_nelems;
>
> Fixes: 9c5f8a1008a1 ("bpf: Support variable length array in tracing programs")
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk()
2026-09-10 12:22 [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk() Jiayuan Chen
2026-09-10 12:22 ` [PATCH bpf 2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements Jiayuan Chen
2026-09-10 21:49 ` [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk() Eduard Zingerman
@ 2026-09-11 0:00 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-11 0:00 UTC (permalink / raw)
To: Jiayuan Chen
Cc: bpf, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, shuah, ameryhung, tj,
mattbobrowski, linux-kernel, linux-kselftest
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Thu, 10 Sep 2026 20:22:55 +0800 you wrote:
> When an access goes past the struct and the last member is a flexible
> array, btf_struct_walk() folds the offset back into a single element with
> (off - moff) % t->size, but never checks that the element type has a size.
>
> BTF takes an empty struct, so this in program BTF
>
> /* event could be empty */
> struct event {
> #ifdef HAVE_TIMESTAMP
> __u64 ts;
> #endif
> };
>
> [...]
Here is the summary with links:
- [bpf,1/2] bpf: Fix divide-by-zero in btf_struct_walk()
https://git.kernel.org/bpf/bpf/c/b0b3dc665296
- [bpf,2/2] selftests/bpf: Test BTF walk into a flexible array of zero-sized elements
https://git.kernel.org/bpf/bpf/c/f77d21245710
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 5+ messages in thread