bpf: Fix NULL-ptr-derefs when showing a void BTF type
Subsystems (3)
v2 cover bpf: Fix NULL-ptr-derefs when showing a void BTF type
This series fixes two NULL-ptr-derefs in BTF handling.
Patch 1 handles the syzbot report. A key-less BTF (btf_key_type_id == 0) used to be rejected for hash maps, until htab and rhtab gained a ->map_check_btf (to register a dtor) that does not look at the key, so a key-less hash map is now accepted. Dumping it through bpffs feeds the key type_id 0 into btf_type_seq_show() and NULL-derefs in btf_type_show(). Reject it again.
Patch 2 fixes a related, pre-existing crash reachable via bpf_snprintf_btf(). A "const void" type_id (a modifier that resolves to void, present in the vmlinux BTF) NULL-derefs in btf_modifier_show() - void has no ->show op. void has no size and nothing to render, so route the show call sites through a helper that falls back to btf_df_show() - the "<unsupported kind:N>" placeholder already used for FWD/FUNC/FLOAT/DECL_TAG.
Patches 3 and 4 add selftests for the two cases. They are meant to reproduce the crashes: each deliberately walks the faulting path, so on an unfixed kernel it oopses the task (and panics it under panic_on_oops). That is intentional - the tests verify the fix and reproduce the bug - so a static review flagging them for crashing an unfixed kernel can be ignored.
v1 -> v2: AI reported a pre-exist issue. Let's fold it in this series.
v1: https://lore.kernel.org/bpf/[email protected]/
Jiayuan Chen (4):
bpf: Reject key-less BTF for hash maps
bpf: Fix NULL-ptr-deref when showing a void BTF type selftests/bpf: Add test for key-less BTF hash map selftests/bpf: Add test for showing a void BTF type
kernel/bpf/btf.c | 10 ++- kernel/bpf/hashtab.c | 8 ++ .../bpf/prog_tests/btf_map_keyless.c | 83 +++++++++++++++++++ .../selftests/bpf/prog_tests/btf_show_void.c | 57 +++++++++++++ .../selftests/bpf/progs/btf_show_void.c | 22 +++++ 5 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_show_void.c create mode 100644 tools/testing/selftests/bpf/progs/btf_show_void.c
Signature · 1 line
2.43.0
PATCH v2 1/4 bpf: Reject key-less BTF for hash maps
map_check_btf() allows a key-less BTF (btf_key_type_id == 0) only for maps that have a ->map_check_btf callback, and leaves the actual decision to that callback. Hash maps used to have no ->map_check_btf, so a key-less BTF was rejected outright.
Since commit 1df97a7453ee ("bpf: Register dtor for freeing special fields") htab and rhtab got a ->map_check_btf to register a dtor, but it does not look at the key, so a key-less hash map now passes map_check_btf() and gets created. Reading it back through bpffs then feeds the key type_id 0 into btf_type_seq_show(); btf_type_by_id() returns the void type, kind_ops[BTF_KIND_UNKN] is NULL, and btf_type_show() dereferences it:
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:btf_type_show (kernel/bpf/btf.c:8251) Call Trace: <TASK> btf_type_seq_show_flags (kernel/bpf/btf.c:8269) btf_type_seq_show (kernel/bpf/btf.c:8277) htab_map_seq_show_elem (kernel/bpf/hashtab.c:1669) map_seq_show (kernel/bpf/inode.c:293) seq_read_iter (fs/seq_file.c:273) seq_read (fs/seq_file.c:163) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) __x64_sys_read (fs/read_write.c:725) do_syscall_64 (arch/x86/entry/syscall_64.c:61) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Reject a key-less BTF in htab_map_check_btf() and rhtab_map_check_btf(), restoring the previous behavior.
--- kernel/bpf/hashtab.c | 8 ++++++++ 1 file changed, 8 insertions(+)
kernel/bpf/hashtab.c +8 -0
@@ -530,6 +530,10 @@ static int htab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); +/* Hash maps have no use for a key-less BTF. */ +if (btf_type_is_void (key_type)) +return -EINVAL ; +if (htab_is_prealloc (htab))return 0 ;/*
@@ -3110,6 +3114,10 @@ static int rhtab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map); +/* Hash maps have no use for a key-less BTF. */ +if (btf_type_is_void (key_type)) +return -EINVAL ; +return bpf_ma_set_dtor (map, &rhtab->ma, rhtab_mem_dtor); }
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH bpf v2 1/4] bpf: Reject key-less BTF for hash maps
Is this attribution accurate? Commit 1df97a7453ee did add htab_map_check_btf() to the four htab ops structs (htab_map_ops, htab_lru_map_ops, htab_percpu_map_ops, htab_lru_percpu_map_ops), but…
Quoted 9 lines
map_check_btf()allows a key-less BTF (btf_key_type_id== 0) only for maps that have a ->map_check_btf callback, and leaves the actual decision to that callback. Hash maps used to have no ->map_check_btf, so a key-less BTF was rejected outright.Since commit
1df97a7453ee("bpf: Register dtor for freeing special fields") htab and rhtab got a ->map_check_btf to register a dtor, but it does not look at the key, so a key-less hash map now passesmap_check_btf()and gets created.
Is this attribution accurate? Commit 1df97a7453ee did add htab_map_check_btf() to the four htab ops structs (htab_map_ops, htab_lru_map_ops, htab_percpu_map_ops, htab_lru_percpu_map_ops), but BPF_MAP_TYPE_RHASH did not exist at that commit.
The rhtab regression was introduced three months later by the second
hashtab"), which added rhtab_map_check_btf() and rhtab_map_ops for the first time.
Could the commit message distinguish which regression each Fixes: tag addresses? As written, the sentence attributes both regressions to 1df97a7453ee.
Quoted 8 lines
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:btf_type_show (kernel/bpf/btf.c:8251) Call Trace: <TASK> btf_type_seq_show_flags (kernel/bpf/btf.c:8269) btf_type_seq_show (kernel/bpf/btf.c:8277) htab_map_seq_show_elem (kernel/bpf/hashtab.c:1669) map_seq_show (kernel/bpf/inode.c:293)
Quoted 8 lines
seq_read_iter (fs/seq_file.c:273) seq_read (fs/seq_file.c:163) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) __x64_sys_read (fs/read_write.c:725) do_syscall_64 (arch/x86/entry/syscall_64.c:61) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Does this match the syzbot report? Looking at the actual report at the
Closes: link, several details differ from what was quoted:
The syscall in the report was pread64:
ksys_pread64 [inline] __x64_sys_pread64+0x1eb/0x250 fs/read_write.c:769
But the quoted trace shows read (ksys_read, __x64_sys_read).
The report shows traverse() frames that are missing here:
traverse.part.0 fs/seq_file.c:112 traverse fs/seq_file.c:99 [inline] seq_read_iter+0x93f/0x1270 fs/seq_file.c:196
And seq_read_iter's line number is 196 in the report, not 273.
There is an extra btf_type_seq_show frame inserted here that is not in the report.
The btf.c line numbers are shifted by +19: the report shows btf_type_show at 8232, btf_type_seq_show_flags at 8250, and btf_type_seq_show at 8258, but the quoted trace adds 19 to each.
Should the crash trace be quoted verbatim from the syzbot report rather than edited?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33300136294
PATCH v2 2/4 bpf: Fix NULL-ptr-deref when showing a void BTF type
In reply to Jiayuan Chenbtf_modifier_show() resolves the modifier and then calls btf_type_ops(t)->show() unconditionally. For the void type (type_id 0, BTF_KIND_UNKN) kind_ops[] has no entry, so ->show is NULL.
The map dump path cannot reach a void type (a map key/value must have a size and void has none), but bpf_snprintf_btf() takes a type_id straight from the BPF program, and a "const void" (a modifier that resolves to void, present in the vmlinux BTF) NULL-derefs there:
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:btf_modifier_show (kernel/bpf/btf.c:2914) Call Trace: <TASK> btf_type_show (kernel/bpf/btf.c:8251) btf_type_snprintf_show (kernel/bpf/btf.c:8321) bpf_snprintf_btf (kernel/trace/bpf_trace.c:1047) bpf_prog_test_run_raw_tp (net/bpf/test_run.c:829) __sys_bpf (kernel/bpf/syscall.c:4804) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
void has no size, so there is nothing to render - not even a byte length to fall back to dumping as raw hex. btf_df_show() is already the ->show for the other kinds that carry no value to print - FWD, FUNC, FUNC_PROTO, FLOAT and DECL_TAG - and emits an "<unsupported kind:N>" placeholder; void belongs to the same group and only lacks a show op because it has no kind_ops[] entry at all. Fall back to btf_df_show() in btf_modifier_show() when the resolved type has no show op, so a void type prints that placeholder instead of crashing; bpf_snprintf_btf() then returns the length as usual.
--- kernel/bpf/btf.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +9 -1
@@ -2911,7 +2911,15 @@ static void btf_modifier_show(const struct btf *btf,else t =btf_type_skip_modifiers (btf, type_id,NULL ); -btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); +/* +* A modifier can resolve to the void type (e.g. "const void"), which +* has no show op (kind_ops[BTF_KIND_UNKN] is NULL). Print a placeholder +* instead of dereferencing NULL. +*/ +if (!btf_type_ops (t)) +btf_df_show (btf, t, type_id, data, bits_offset, show); +else +btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); } static void btf_var_show(const struct btf *btf, const struct btf_type *t,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH bpf v2 2/4] bpf: Fix NULL-ptr-deref when showing a void BTF type
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] NULL pointer dereference in btf_var_show() when rendering vmlinux BTF types via bpf_snprintf_btf(). --
commit f9ef7fc1b664c580ca28b8d18cae855dd7b0e940
Author: Jiayuan Chen <[email protected]>
bpf: Fix NULL-ptr-deref when showing a void BTF type
This patch fixes a NULL pointer dereference when btf_modifier_show() attempts to render a void type. Because void lacks a show op, it falls back to btf_df_show() instead of dereferencing a NULL pointer.
Quoted 21 lines
kernel/bpf/btf.c +9
-1@@ -2911,7 +2911,15 @@ static void btf_modifier_show(const struct btf *btf,else t =btf_type_skip_modifiers (btf, type_id,NULL ); -btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); +/* +* A modifier can resolve to the void type (e.g. "const void"), which +* has no show op (kind_ops[BTF_KIND_UNKN] is NULL). Print a placeholder +* instead of dereferencing NULL. +*/ +if (!btf_type_ops (t)) +btf_df_show (btf, t, type_id, data, bits_offset, show); +else +btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); } static void btf_var_show(const struct btf *btf, const struct btf_type *t,
kernel/bpf/btf.c
Quoted 4 lines
kernel/bpf/btf.c +0
-0u32 type_id, void *data, u8 bits_offset, struct btf_show *show) { t =btf_type_id_resolve (btf, &type_id);
[Severity: High]
This isn't a bug introduced by this patch, but while looking at this fix, I noticed a potential issue in btf_var_show() just below it.
Can btf_var_show() cause a NULL pointer dereference when rendering vmlinux BTF types via bpf_snprintf_btf()?
If a BPF program calls the bpf_snprintf_btf() helper and passes the type ID of a BTF_KIND_VAR from the vmlinux BTF, formatting will dispatch to btf_var_show().
This function unconditionally calls btf_type_id_resolve(), which attempts to dereference btf->resolved_ids.
However, since type resolution is skipped for base BTF during parsing, btf->resolved_ids is NULL for vmlinux BTF. Does this unconditionally result in a NULL pointer dereference inside btf_type_id_resolve()?
Quoted 3 lines
btf_type_ops(t)->show(btf, t,
type_id, data,bits_offset, show);
}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v2 3/4 selftests/bpf: Add test for key-less BTF hash map
In reply to Jiayuan ChenCreate a hash and an rhash map with btf_key_type_id == 0 and expect bpf_map_create() to fail with -EINVAL; a positive control with a real key type confirms the rejection is about the key-less BTF. On an unfixed kernel the map is created, and the test pins and reads it back to walk the bpffs dump path, which reproduces the btf_type_show() NULL-deref - so running it on an unfixed kernel oopses the reading task (and panics it under panic_on_oops).
--- .../bpf/prog_tests/btf_map_keyless.c | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c
tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c new file +83 -0
@@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <test_progs.h> +#include <bpf/btf.h> + +/* +* A hash map with a key-less BTF (btf_key_type_id == 0) used to NULL-deref in +* btf_type_show() when dumped via bpffs. +* A fixed kernel rejects such a map at creation; on an unfixed kernel the +* pin-and-read below deliberately walks that bpffs dump path, so it doubles +* as a reproducer: it oopses an unfixed kernel (and panics it under +* panic_on_oops). +*/ +static void check_keyless (int map_type,__u32 map_flags,int btf_fd,int val_id) +{ +LIBBPF_OPTS (bpf_map_create_opts, opts); +const char *path ="/sys/fs/bpf/keyless_map" ; +__u32 key =1 , val =0x41424344 ; +char buf[256 ]; +int map_fd; +FILE *f; + + opts.map_flags = map_flags; + opts.btf_fd = btf_fd; + opts.btf_value_type_id = val_id; + +/* +* Positive control: the same map with a real key type must be accepted, +* so the -EINVAL below is about the key-less BTF and not some unrelated +* rejection (e.g. an unknown map type). +*/ + opts.btf_key_type_id = val_id; + map_fd =bpf_map_create (map_type,"keyed_map" ,4 ,4 ,8 , &opts); +if (!ASSERT_GE (map_fd,0 ,"keyed create is accepted" )) +return ; +close (map_fd); + +/* A key-less BTF must be rejected. */ + opts.btf_key_type_id =0 ; + map_fd =bpf_map_create (map_type,"keyless_map" ,4 ,4 ,8 , &opts); + +if (map_fd >=0 ) { +/* Unfixed kernel: reproduce the oops via the bpffs dump path. */ + (void )bpf_map_update_elem (map_fd, &key, &val,0 ); +if (bpf_obj_pin (map_fd, path) ==0 ) { + f =fopen (path,"r" ); +if (f) { +while (fgets (buf,sizeof (buf), f)) + ; +fclose (f); + } +unlink (path); + } +close (map_fd); + } + +ASSERT_EQ (map_fd, -EINVAL ,"key-less create is rejected" ); +} + +void test_btf_map_keyless (void ) +{ +int btf_fd, val_id; +struct btf *btf; + + btf =btf__new_empty (); +if (!ASSERT_OK_PTR (btf,"btf__new_empty" )) +return ; + + val_id =btf__add_int (btf,"int" ,4 ,BTF_INT_SIGNED ); +if (!ASSERT_GT (val_id,0 ,"btf__add_int" )) + goto out; + +if (!ASSERT_OK (btf__load_into_kernel (btf),"btf__load_into_kernel" )) + goto out; + btf_fd =btf__fd (btf); + +if (test__start_subtest ("hash" )) +check_keyless (BPF_MAP_TYPE_HASH ,0 , btf_fd, val_id); +if (test__start_subtest ("rhash" )) +check_keyless (BPF_MAP_TYPE_RHASH ,BPF_F_NO_PREALLOC , btf_fd, val_id); +out: +btf__free (btf); +}
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH bpf v2 3/4] selftests/bpf: Add test for key-less BTF hash map
This isn't a bug, but is the #define _GNU_SOURCE needed here? Everything the file uses looks like plain POSIX (fopen/fgets/fclose from stdio.h, unlink/close from unistd.h, plus libbpf/BTF calls)…
tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c line 2
Quoted 8 lines
tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c new file +2
-0@@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE
This isn't a bug, but is the #define _GNU_SOURCE needed here? Everything the file uses looks like plain POSIX (fopen/fgets/fclose from stdio.h, unlink/close from unistd.h, plus libbpf/BTF calls), and lib.mk already passes -D_GNU_SOURCE= to selftest builds.
tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c line 4
Quoted 2 lines
tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c new file +2
-0+#include <test_progs.h> +#include <bpf/btf.h>
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33300136294
PATCH v2 4/4 selftests/bpf: Add test for showing a void BTF type
In reply to Jiayuan ChenCall bpf_snprintf_btf() with the type_id of a "const void" from the vmlinux BTF and check the rendered output is the "<unsupported kind:0>" placeholder. On an unfixed kernel this used to NULL-deref in btf_modifier_show(), so the test doubles as a reproducer: it oopses the task (and panics it under panic_on_oops).
--- .../selftests/bpf/prog_tests/btf_show_void.c | 57 +++++++++++++++++++ .../selftests/bpf/progs/btf_show_void.c | 22 +++++++ 2 files changed, 79 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_show_void.c create mode 100644 tools/testing/selftests/bpf/progs/btf_show_void.c
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +57 -0
@@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <bpf/btf.h> +#include "btf_show_void.skel.h" + +/* +* bpf_snprintf_btf() with the type_id of a "const void" (a modifier that +* resolves to void, present in the vmlinux BTF) used to NULL-deref in +* btf_modifier_show(). A fixed kernel prints the "<unsupported kind:0>" +* placeholder; on an unfixed kernel this oopses the task (and panics it under +* panic_on_oops), so it doubles as a reproducer. +*/ +void test_btf_show_void (void ) +{ +LIBBPF_OPTS (bpf_test_run_opts, topts); +const struct btf_type *t; +struct btf_show_void *skel; +int i, n, cv =0 , err; +char ctx[16 ] = {}; +struct btf *btf; + + btf =btf__parse ("/sys/kernel/btf/vmlinux" ,NULL ); +if (!ASSERT_OK_PTR (btf,"btf__parse vmlinux" )) +return ; + + n =btf__type_cnt (btf); +for (i =1 ; i < n; i++) { + t =btf__type_by_id (btf, i); +if (btf_kind (t) ==BTF_KIND_CONST && t->type ==0 ) { + cv = i; +break ; + } + } +if (!ASSERT_GT (cv,0 ,"find const void in vmlinux BTF" )) + goto out_btf; + + skel =btf_show_void__open (); +if (!ASSERT_OK_PTR (skel,"skel_open" )) + goto out_btf; + skel->rodata->const_void_id = cv; +if (!ASSERT_OK (btf_show_void__load (skel),"skel_load" )) + goto out_skel; + + topts.ctx_in = ctx; + topts.ctx_size_in =sizeof (ctx); + err =bpf_prog_test_run_opts (bpf_program__fd (skel->progs.dump_const_void), + &topts); +if (!ASSERT_OK (err,"test_run" )) + goto out_skel; + +ASSERT_EQ (skel->bss->ret,sizeof ("<unsupported kind:0>" ) -1 ,"ret" ); +ASSERT_STREQ (skel->bss->out,"<unsupported kind:0>" ,"placeholder" ); +out_skel: +btf_show_void__destroy (skel); +out_btf: +btf__free (btf); +}
tools/testing/selftests/bpf/progs/btf_show_void.c new file +22 -0
@@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "btf_ptr.h" +#include <bpf/bpf_helpers.h> + +const volatile __u32 const_void_id; +char out[64 ]; +long ret; + +SEC("raw_tp/sys_enter") +int dump_const_void (void *ctx) +{ +struct btf_ptr ptr = { + .ptr = ctx, + .type_id = const_void_id, + .flags =0 , + }; + + ret =bpf_snprintf_btf (out,sizeof (out), &ptr,sizeof (ptr),0 ); +return 0 ; +} + +char _license[] SEC("license") = "GPL";
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH bpf v2 4/4] selftests/bpf: Add test for showing a void BTF type
A subsystem pattern flags this as potentially concerning: this adds a new standalone test file pair for one narrow case (a modifier resolving to void in bpf_snprintf_btf(), exercising the…
Quoted 9 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +3
-0@@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <bpf/btf.h>
tools/testing/selftests/bpf/prog_tests/btf_show_void.c line 13
Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +10
-0+#include "btf_show_void.skel.h" + +/* +* bpf_snprintf_btf() with the type_id of a "const void" (a modifier that +* resolves to void, present in the vmlinux BTF) used to NULL-deref in +* btf_modifier_show(). A fixed kernel prints the "<unsupported kind:0>" +* placeholder; on an unfixed kernel this oopses the task (and panics it under +* panic_on_oops), so it doubles as a reproducer. +*/ +void test_btf_show_void (void )
A subsystem pattern flags this as potentially concerning: this adds a new standalone test file pair for one narrow case (a modifier resolving to void in bpf_snprintf_btf(), exercising the btf_modifier_show() -> btf_df_show() fallback). The directory already has prog_tests/snprintf_btf.c (serial_test_snprintf_btf, driving progs/netif_receive_skb.c which includes progs/btf_ptr.h) that exercises bpf_snprintf_btf() output formatting for a range of types.
Would this case fit better as an additional case in that existing test rather than as a new binary/skeleton pair?
The counter-argument: snprintf_btf.c is structured very differently - it attaches a netif_receive_skb raw tracepoint and triggers it with system("ping -c 1 127.0.0.1"), and still uses the deprecated CHECK() macros. The new test instead needs a type_id discovered from the vmlinux BTF at runtime injected through rodata, and drives the program with bpf_prog_test_run_opts() rather than a real event. Folding it into the existing test would mean adding a second program and a second run mechanism to that file. That is a plausible engineering reason for a separate file, and the new file is the more modern idiom.
tools/testing/selftests/bpf/prog_tests/btf_show_void.c line 24
Quoted 11 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +11
-0+{ +LIBBPF_OPTS (bpf_test_run_opts, topts); +const struct btf_type *t; +struct btf_show_void *skel; +int i, n, cv =0 , err; +char ctx[16 ] = {}; +struct btf *btf; + + btf =btf__parse ("/sys/kernel/btf/vmlinux" ,NULL ); +if (!ASSERT_OK_PTR (btf,"btf__parse vmlinux" )) +return ;
Both environment prerequisites of this test are reported as hard failures rather than skips.
On a kernel built without CONFIG_DEBUG_INFO_BTF the /sys/kernel/btf/vmlinux file does not exist, btf__parse() fails with ENOENT, and the test reports a failure instead of a skip. The path is also hardcoded; btf__load_vmlinux_btf() (used by 14 other prog_tests, e.g. fill_link_info.c, tracing_multi.c, btf_module.c) additionally searches /boot/vmlinux-$(uname -r) and the module build dirs.
tools/testing/selftests/bpf/prog_tests/btf_show_void.c line 35
Quoted 11 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +11
-0+ + n =btf__type_cnt (btf); +for (i =1 ; i < n; i++) { + t =btf__type_by_id (btf, i); +if (btf_kind (t) ==BTF_KIND_CONST && t->type ==0 ) { + cv = i; +break ; + } + } +if (!ASSERT_GT (cv,0 ,"find const void in vmlinux BTF" )) + goto out_btf;
The test scans the vmlinux BTF for the first BTF_KIND_CONST whose ->type == 0 and hard-fails via ASSERT_GT when none exists. Whether a standalone const void node survives in a given kernel's BTF is a property of that build (pahole version, dedup, whether anything in that kernel actually uses const void *), not a property of the kernel logic under test. If it is ever absent the test reports a kernel regression that is not there, with no indication that the input the test needed simply was not present.
Would a test__skip() with a reason string on both arms (the pattern used in prog_tests/snprintf_btf.c) keep an environment difference from being read as a failure?
Mitigating context: all BPF selftests already require CONFIG_DEBUG_INFO_BTF=y, and the exact idiom btf__parse("/sys/kernel/btf/vmlinux", NULL) followed by ASSERT_OK_PTR is used unchanged in prog_tests/libbpf_str.c (4 sites), prog_tests/libbpf_probes.c (2 sites) and prog_tests/btf_sysfs.c, so the first arm matches existing neighbours. const void is also very likely present in any vmlinux BTF, since const void * appears in many exported prototypes. The concern is robustness/diagnosability of the failure mode, not a functional defect.
Quoted 12 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +12
-0+ + skel =btf_show_void__open (); +if (!ASSERT_OK_PTR (skel,"skel_open" )) + goto out_btf; + skel->rodata->const_void_id = cv; +if (!ASSERT_OK (btf_show_void__load (skel),"skel_load" )) + goto out_skel; + + topts.ctx_in = ctx; + topts.ctx_size_in =sizeof (ctx); + err =bpf_prog_test_run_opts (bpf_program__fd (skel->progs.dump_const_void), + &topts);
tools/testing/selftests/bpf/prog_tests/btf_show_void.c line 57
Quoted 10 lines
tools/testing/selftests/bpf/prog_tests/btf_show_void.c new file +10
-0+if (!ASSERT_OK (err,"test_run" )) + goto out_skel; + +ASSERT_EQ (skel->bss->ret,sizeof ("<unsupported kind:0>" ) -1 ,"ret" ); +ASSERT_STREQ (skel->bss->out,"<unsupported kind:0>" ,"placeholder" ); +out_skel: +btf_show_void__destroy (skel); +out_btf: +btf__free (btf); +}
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33300136294