Call bpf_snprintf_btf() with type_ids from the vmlinux BTF that used to
NULL-deref in the BTF show path: a "const void" (checked to render the
"<unsupported kind:0>" placeholder) and a BTF_KIND_VAR (checked to render
without crashing). On an unfixed kernel each oopses the task (and panics
it under panic_on_oops), so the test doubles as a reproducer.

Signed-off-by: Jiayuan Chen <[email protected]>
---
 .../selftests/bpf/prog_tests/btf_show_void.c  | 81 +++++++++++++++++++
 .../selftests/bpf/progs/btf_show_void.c       | 24 ++++++
 2 files changed, 105 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

diff --git a/tools/testing/selftests/bpf/prog_tests/btf_show_void.c 
b/tools/testing/selftests/bpf/prog_tests/btf_show_void.c
new file mode 100644
index 000000000000..d73232dc2a04
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_show_void.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include "btf_show_void.skel.h"
+
+/*
+ * bpf_snprintf_btf() renders a type_id taken straight from the vmlinux BTF.
+ * Two such type_ids used to NULL-deref in the BTF show path:
+ *   - a "const void" (a modifier resolving to void) in btf_modifier_show()
+ *   - a BTF_KIND_VAR in btf_var_show() (base BTF has no resolved_ids)
+ * A fixed kernel renders both without crashing.
+ */
+static long run(struct btf_show_void *skel, __u32 type_id)
+{
+       LIBBPF_OPTS(bpf_test_run_opts, topts);
+       char ctx[8] = {};
+
+       skel->bss->type_id = type_id;
+       topts.ctx_in = ctx;
+       topts.ctx_size_in = sizeof(ctx);
+       if 
(!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.dump_type),
+                                             &topts), "test_run"))
+               return -1;
+       return skel->bss->ret;
+}
+
+void test_btf_show_void(void)
+{
+       const struct btf_type *t;
+       struct btf_show_void *skel;
+       int i, n, cv = 0, var = 0;
+       struct btf *btf;
+
+       btf = btf__parse("/sys/kernel/btf/vmlinux", NULL);
+       if (!btf) {
+               test__skip();
+               return;
+       }
+
+       skel = btf_show_void__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+               goto out_btf;
+
+       n = btf__type_cnt(btf);
+       for (i = 1; i < n && !(cv && var); i++) {
+               t = btf__type_by_id(btf, i);
+               if (!cv && btf_kind(t) == BTF_KIND_CONST && t->type == 0)
+                       cv = i;
+               /* Pick a VAR small enough to render from the program's buffer. 
*/
+               if (!var && btf_kind(t) == BTF_KIND_VAR) {
+                       long sz = btf__resolve_size(btf, t->type);
+
+                       if (sz > 0 && sz <= (long)sizeof(skel->bss->obj))
+                               var = i;
+               }
+       }
+
+       /* "const void" renders the "<unsupported kind:0>" placeholder. */
+       if (test__start_subtest("const_void")) {
+               if (cv) {
+                       ASSERT_EQ(run(skel, cv),
+                                 sizeof("<unsupported kind:0>") - 1, "ret");
+                       ASSERT_STREQ(skel->bss->out, "<unsupported kind:0>",
+                                    "placeholder");
+               } else {
+                       test__skip();
+               }
+       }
+
+       /* A BTF_KIND_VAR must resolve and render without error. */
+       if (test__start_subtest("var")) {
+               if (var)
+                       ASSERT_GT(run(skel, var), 0, "ret");
+               else
+                       test__skip();
+       }
+
+       btf_show_void__destroy(skel);
+out_btf:
+       btf__free(btf);
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_show_void.c 
b/tools/testing/selftests/bpf/progs/btf_show_void.c
new file mode 100644
index 000000000000..44af80fbbb80
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_show_void.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "btf_ptr.h"
+#include <bpf/bpf_helpers.h>
+
+__u32 type_id;
+/* A buffer we own to render the selected type from, kept in bounds. */
+char obj[256];
+char out[64];
+long ret;
+
+SEC("raw_tp/sys_enter")
+int dump_type(void *ctx)
+{
+       struct btf_ptr ptr = {
+               .ptr = obj,
+               .type_id = type_id,
+               .flags = 0,
+       };
+
+       ret = bpf_snprintf_btf(out, sizeof(out), &ptr, sizeof(ptr), 0);
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.43.0


Reply via email to