* [PATCH bpf 1/2] bpf: Fix divide-by-zero in btf_struct_walk()
@ 2026-09-10 12:22 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
` (2 more replies)
0 siblings, 3 replies; 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, Amery Hung,
Tejun Heo, Matt Bobrowski, linux-kernel, linux-kselftest
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>
---
kernel/bpf/btf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 31057c8f3a7c..1c5de50b9cab 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7203,7 +7203,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
if (btf_type_is_int(t))
return WALK_SCALAR;
- if (!btf_type_is_struct(t))
+ if (!btf_type_is_struct(t) || !t->size)
goto error;
off = (off - moff) % t->size;
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [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 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 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 ` [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
end of thread, other threads:[~2026-09-11 0:01 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 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
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®