| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/28 22:42 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch only modifies userspace tooling (bpftool) and BPF selftests under tools/, with no changes to kernel code or runtime behavior.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/28 22:42 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 54c129546f99040846d8d91c94a88c1e831aa55e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 28 22:42:53 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c\nindex c9589026da8d6..bca0a3982f09b 100644\n--- a/tools/bpf/bpftool/btf.c\n+++ b/tools/bpf/bpftool/btf.c\n@@ -805,9 +805,13 @@ static int dump_btf_c(const struct btf *btf,\n \n \t\tif (sort_dump)\n \t\t\tdatums = sort_btf_c(btf);\n-\t\tfor (i = 1; i \u003c cnt; i++) {\n+\t\tfor (i = 0; i \u003c cnt; i++) {\n \t\t\tint idx = datums ? datums[i].index : i;\n \n+\t\t\t/* type ID 0 is void, skip it */\n+\t\t\tif (idx == 0)\n+\t\t\t\tcontinue;\n+\n \t\t\terr = btf_dump__dump_type(d, idx);\n \t\t\tif (err)\n \t\t\t\tgoto done;\ndiff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile\nindex 5f1a3bfc0569f..d99581257e54f 100644\n--- a/tools/testing/selftests/bpf/Makefile\n+++ b/tools/testing/selftests/bpf/Makefile\n@@ -119,7 +119,9 @@ TEST_GEN_PROGS += test_progs-cpuv4\n TEST_INST_SUBDIRS += cpuv4\n endif\n \n-TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)\n+TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)\t\\\n+\t bpftool_btf_dump_sorted.expected\t\t\t\t\t\\\n+\t bpftool_btf_dump_unsorted.expected\n \n # Order correspond to 'make run_tests' order\n TEST_PROGS := test_kmod.sh \\\n@@ -887,7 +889,9 @@ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read\t\t\t\t\\\n \t\t ima_setup.sh \t\t\t\t\t\\\n \t\t $(VERIFY_SIG_SETUP)\t\t\t\t\\\n \t\t $(wildcard progs/btf_dump_test_case_*.c)\t\t\\\n-\t\t $(wildcard progs/*.bpf.o)\n+\t\t $(wildcard progs/*.bpf.o)\t\t\t\\\n+\t\t bpftool_btf_dump_sorted.expected\t\t\t\\\n+\t\t bpftool_btf_dump_unsorted.expected\n TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE\n TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS\n $(eval $(call DEFINE_TEST_RUNNER,test_progs))\ndiff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected\nnew file mode 100644\nindex 0000000000000..5470b2b432291\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected\n@@ -0,0 +1,48 @@\n+#ifndef __VMLINUX_H__\n+#define __VMLINUX_H__\n+\n+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n+#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n+#endif\n+\n+#ifndef __ksym\n+#define __ksym __attribute__((section(\".ksyms\")))\n+#endif\n+\n+#ifndef __weak\n+#define __weak __attribute__((weak))\n+#endif\n+\n+#ifndef __bpf_fastcall\n+#if __has_attribute(bpf_fastcall)\n+#define __bpf_fastcall __attribute__((bpf_fastcall))\n+#else\n+#define __bpf_fastcall\n+#endif\n+#endif\n+\n+enum {\n+\tE0 = 1,\n+};\n+\n+struct holey {\n+\tint c;\n+\tlong: 32;\n+\tlong: 32;\n+\tint tail;\n+};\n+\n+struct s {\n+\tint f;\n+};\n+\n+\n+/* BPF kfuncs */\n+#ifndef BPF_NO_KFUNC_PROTOTYPES\n+#endif\n+\n+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n+#pragma clang attribute pop\n+#endif\n+\n+#endif /* __VMLINUX_H__ */\ndiff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected\nnew file mode 100644\nindex 0000000000000..4310adc2ed514\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected\n@@ -0,0 +1,48 @@\n+#ifndef __VMLINUX_H__\n+#define __VMLINUX_H__\n+\n+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n+#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n+#endif\n+\n+#ifndef __ksym\n+#define __ksym __attribute__((section(\".ksyms\")))\n+#endif\n+\n+#ifndef __weak\n+#define __weak __attribute__((weak))\n+#endif\n+\n+#ifndef __bpf_fastcall\n+#if __has_attribute(bpf_fastcall)\n+#define __bpf_fastcall __attribute__((bpf_fastcall))\n+#else\n+#define __bpf_fastcall\n+#endif\n+#endif\n+\n+struct holey {\n+\tint c;\n+\tlong: 32;\n+\tlong: 32;\n+\tint tail;\n+};\n+\n+enum {\n+\tE0 = 1,\n+};\n+\n+struct s {\n+\tint f;\n+};\n+\n+\n+/* BPF kfuncs */\n+#ifndef BPF_NO_KFUNC_PROTOTYPES\n+#endif\n+\n+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n+#pragma clang attribute pop\n+#endif\n+\n+#endif /* __VMLINUX_H__ */\ndiff --git a/tools/testing/selftests/bpf/bpftool_helpers.c b/tools/testing/selftests/bpf/bpftool_helpers.c\nindex 0a2a4f0a2794b..c49fdd90eb033 100644\n--- a/tools/testing/selftests/bpf/bpftool_helpers.c\n+++ b/tools/testing/selftests/bpf/bpftool_helpers.c\n@@ -53,6 +53,7 @@ static int run_command(char *args, char *output_buf, size_t output_max_len)\n \tstatic char bpftool_path[PATH_MAX] = {};\n \tbool suppress_output = !(output_buf \u0026\u0026 output_max_len);\n \tchar command[BPFTOOL_FULL_CMD_MAX_LEN];\n+\tsize_t n;\n \tFILE *f;\n \tint ret;\n \n@@ -68,8 +69,10 @@ static int run_command(char *args, char *output_buf, size_t output_max_len)\n \tif (!f)\n \t\treturn 1;\n \n-\tif (!suppress_output)\n-\t\tfread(output_buf, 1, output_max_len, f);\n+\tif (!suppress_output) {\n+\t\tn = fread(output_buf, 1, output_max_len - 1, f);\n+\t\toutput_buf[n] = '\\0';\n+\t}\n \tret = pclose(f);\n \n \treturn ret;\ndiff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c\nnew file mode 100644\nindex 0000000000000..d5b25302b0c89\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c\n@@ -0,0 +1,178 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \u003ctest_progs.h\u003e\n+#include \u003cbpftool_helpers.h\u003e\n+#include \u003cbpf/btf.h\u003e\n+#include \u003climits.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \"btf_helpers.h\"\n+#include \"testing_helpers.h\"\n+\n+#define DUMP_BUF_SZ\t\t(16 * 1024)\n+\n+#define EXPECTED_SORTED\t\t\"bpftool_btf_dump_sorted.expected\"\n+#define EXPECTED_UNSORTED\t\"bpftool_btf_dump_unsorted.expected\"\n+\n+/*\n+ *\tstruct holey {\n+ *\t\tint c;\n+ *\t\t\u003c64-bit hole\u003e\n+ *\t\tint tail;\n+ *\t};\n+ *\tenum { E0 = 1 };\n+ *\tstruct s { int f; };\n+ */\n+static struct btf *mk_btf(void)\n+{\n+\tstruct btf *btf;\n+\n+\tbtf = btf__new_empty();\n+\tif (!ASSERT_OK_PTR(btf, \"new_empty\"))\n+\t\treturn NULL;\n+\n+\tbtf__add_int(btf, \"int\", 4, BTF_INT_SIGNED);\n+\tbtf__add_int(btf, \"long int\", 4, BTF_INT_SIGNED);\n+\n+\tbtf__add_struct(btf, \"holey\", 16);\n+\tbtf__add_field(btf, \"c\", 1, 0, 0);\n+\tbtf__add_field(btf, \"tail\", 1, 96, 0);\n+\n+\tbtf__add_enum(btf, NULL, 4);\n+\tbtf__add_enum_value(btf, \"E0\", 1);\n+\n+\tbtf__add_struct(btf, \"s\", 4);\n+\tbtf__add_field(btf, \"f\", 1, 0, 0);\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf,\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t\"[2] INT 'long int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t\"[3] STRUCT 'holey' size=16 vlen=2\\n\"\n+\t\t\"\\t'c' type_id=1 bits_offset=0\\n\"\n+\t\t\"\\t'tail' type_id=1 bits_offset=96\",\n+\t\t\"[4] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\\n\"\n+\t\t\"\\t'E0' val=1\",\n+\t\t\"[5] STRUCT 's' size=4 vlen=1\\n\"\n+\t\t\"\\t'f' type_id=1 bits_offset=0\");\n+\n+\treturn btf;\n+}\n+\n+static int btf_to_tmpfile(const struct btf *btf, char *path)\n+{\n+\tssize_t written;\n+\tconst void *raw;\n+\t__u32 sz;\n+\tint fd;\n+\n+\traw = btf__raw_data(btf, \u0026sz);\n+\tif (!ASSERT_OK_PTR(raw, \"raw_data\"))\n+\t\treturn -1;\n+\n+\tsnprintf(path, PATH_MAX, \"/tmp/bpftool_btf_dump.XXXXXX\");\n+\tfd = mkstemp(path);\n+\tif (!ASSERT_OK_FD(fd, \"mkstemp_btf\"))\n+\t\treturn -1;\n+\n+\twritten = write(fd, raw, sz);\n+\tclose(fd);\n+\tif (!ASSERT_EQ(written, sz, \"write_btf\")) {\n+\t\tunlink(path);\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static char *dump_c(const char *btf_path, bool sorted)\n+{\n+\tchar args[MAX_BPFTOOL_CMD_LEN];\n+\tchar *buf;\n+\tint err;\n+\n+\tbuf = malloc(DUMP_BUF_SZ);\n+\tif (!ASSERT_OK_PTR(buf, \"alloc_dump\"))\n+\t\treturn NULL;\n+\n+\tsnprintf(args, sizeof(args), \"btf dump file %s format c%s\",\n+\t\t btf_path, sorted ? \"\" : \" unsorted\");\n+\n+\terr = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);\n+\tif (!ASSERT_OK(err, \"btf_dump_format_c\")) {\n+\t\tfree(buf);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn buf;\n+}\n+\n+static char *read_expected(const char *path)\n+{\n+\tchar *buf = NULL;\n+\tsize_t cap = 0;\n+\tFILE *f;\n+\tint err;\n+\n+\tf = fopen(path, \"r\");\n+\tif (!f) {\n+\t\terr = errno;\n+\t\tPRINT_FAIL(\"can't open expected output '%s': errno %d\\n\", path, err);\n+\t\treturn NULL;\n+\t}\n+\n+\t/* no NUL in a generated header, so this reads to the end */\n+\tif (getdelim(\u0026buf, \u0026cap, '\\0', f) \u003c 0) {\n+\t\terr = errno;\n+\t\tPRINT_FAIL(\"can't read expected output '%s': errno %d\\n\", path, err);\n+\t\tfree(buf);\n+\t\tbuf = NULL;\n+\t}\n+\n+\tfclose(f);\n+\treturn buf;\n+}\n+\n+static void test_dump(const char *btf_path, bool sorted)\n+{\n+\tconst char *exp_path;\n+\tchar *dump, *exp;\n+\tint err;\n+\n+\texp_path = sorted ? EXPECTED_SORTED : EXPECTED_UNSORTED;\n+\n+\tdump = dump_c(btf_path, sorted);\n+\tif (!dump)\n+\t\treturn;\n+\n+\texp = read_expected(exp_path);\n+\tif (!exp)\n+\t\tgoto out_dump;\n+\n+\terr = compare_text_to_expected(dump, exp);\n+\tASSERT_OK(err, sorted ? \"cmp_sorted\" : \"cmp_unsorted\");\n+\n+\tfree(exp);\n+out_dump:\n+\tfree(dump);\n+}\n+\n+void test_bpftool_btf_dump(void)\n+{\n+\tchar path[PATH_MAX];\n+\tstruct btf *btf;\n+\n+\tbtf = mk_btf();\n+\tif (!btf)\n+\t\treturn;\n+\n+\tif (btf_to_tmpfile(btf, path))\n+\t\tgoto out_btf;\n+\n+\tif (test__start_subtest(\"c_sorted\"))\n+\t\ttest_dump(path, true);\n+\tif (test__start_subtest(\"c_unsorted\"))\n+\t\ttest_dump(path, false);\n+\n+\tunlink(path);\n+out_btf:\n+\tbtf__free(btf);\n+}\ndiff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c\nindex 9f1b50e07a298..fe04a955d46cc 100644\n--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c\n+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c\n@@ -1,8 +1,7 @@\n // SPDX-License-Identifier: GPL-2.0\n #include \u003ctest_progs.h\u003e\n #include \u003cbpf/btf.h\u003e\n-\n-static int duration = 0;\n+#include \"testing_helpers.h\"\n \n void btf_dump_printf(void *ctx, const char *fmt, va_list args)\n {\n@@ -45,12 +44,75 @@ static int btf_dump_all_types(const struct btf *btf, void *ctx)\n \treturn err;\n }\n \n+/*\n+ * Expected output is embedded in the test case source, between\n+ * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT markers. A region is either\n+ * plain C, where a declaration doubles as its own expectation, or C wrapped in\n+ * a block comment, where the rendered form differs from the source. All\n+ * regions of a file concatenate into one expectation, compared against one\n+ * whole-file dump.\n+ *\n+ * Returns a malloc'd buffer for the caller to free, or NULL on failure.\n+ */\n+static char *read_expected_output(const char *path)\n+{\n+\tsize_t out_sz = 0, line_cap = 0;\n+\tchar *out = NULL, *line = NULL;\n+\tbool in_region = false;\n+\tFILE *f, *out_file;\n+\n+\tf = fopen(path, \"r\");\n+\tif (!f)\n+\t\treturn NULL;\n+\n+\tout_file = open_memstream(\u0026out, \u0026out_sz);\n+\tif (!out_file) {\n+\t\tfclose(f);\n+\t\treturn NULL;\n+\t}\n+\n+\twhile (getline(\u0026line, \u0026line_cap, f) \u003e 0) {\n+\t\tconst char *p;\n+\n+\t\tif (strstr(line, \"START-EXPECTED-OUTPUT\")) {\n+\t\t\tin_region = true;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (strstr(line, \"END-EXPECTED-OUTPUT\"))\n+\t\t\tin_region = false;\n+\t\tif (!in_region)\n+\t\t\tcontinue;\n+\n+\t\tp = line + strspn(line, \" \\t\");\n+\n+\t\t/* opening or closing line of a commented out region */\n+\t\tif (!strncmp(p, \"/*\", 2) || !strncmp(p, \"*/\", 2))\n+\t\t\tcontinue;\n+\n+\t\t/*\n+\t\t * Only a '*' directly after the indentation is a comment\n+\t\t * prefix. Without one the line is taken as it is, leading\n+\t\t * whitespace included.\n+\t\t */\n+\t\tp = *p == '*' ? p + 1 : line;\n+\n+\t\tfputs(p, out_file);\n+\t}\n+\n+\tfree(line);\n+\tfclose(f);\n+\tfclose(out_file);\n+\treturn out;\n+}\n+\n static int test_btf_dump_case(int n, struct btf_dump_test_case *t)\n {\n-\tchar test_file[256], out_file[256], diff_cmd[1024];\n+\tchar *dump = NULL, *expected = NULL;\n \tstruct btf *btf = NULL;\n-\tint err = 0, fd = -1;\n-\tFILE *f = NULL;\n+\tchar test_file[256];\n+\tsize_t dump_sz = 0;\n+\tint err = 0;\n+\tFILE *f;\n \n \tsnprintf(test_file, sizeof(test_file), \"%s.bpf.o\", t-\u003efile);\n \n@@ -69,29 +131,21 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)\n \tif (!t-\u003eknown_ptr_sz) {\n \t\tbtf__set_pointer_size(btf, 8);\n \t} else {\n-\t\tCHECK(btf__pointer_size(btf) != 8, \"ptr_sz\", \"exp %d, got %zu\\n\",\n-\t\t 8, btf__pointer_size(btf));\n-\t}\n+\t\tsize_t ptr_sz = btf__pointer_size(btf);\n \n-\tsnprintf(out_file, sizeof(out_file), \"/tmp/%s.output.XXXXXX\", t-\u003efile);\n-\tfd = mkstemp(out_file);\n-\tif (!ASSERT_GE(fd, 0, \"create_tmp\")) {\n-\t\terr = fd;\n-\t\tgoto done;\n+\t\tASSERT_EQ(ptr_sz, (size_t)8, \"ptr_sz\");\n \t}\n-\tf = fdopen(fd, \"w\");\n-\tif (CHECK(f == NULL, \"open_tmp\", \"failed to open file: %s(%d)\\n\",\n-\t\t strerror(errno), errno)) {\n-\t\tclose(fd);\n+\n+\tf = open_memstream(\u0026dump, \u0026dump_sz);\n+\tif (!ASSERT_OK_PTR(f, \"open_memstream\")) {\n+\t\terr = -errno;\n \t\tgoto done;\n \t}\n \n \terr = btf_dump_all_types(btf, f);\n \tfclose(f);\n-\tclose(fd);\n-\tif (CHECK(err, \"btf_dump\", \"failure during C dumping: %d\\n\", err)) {\n+\tif (!ASSERT_OK(err, \"btf_dump\"))\n \t\tgoto done;\n-\t}\n \n \tsnprintf(test_file, sizeof(test_file), \"progs/%s.c\", t-\u003efile);\n \tif (access(test_file, R_OK) == -1)\n@@ -100,28 +154,23 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)\n \t\t * without preserving the directory structure.\n \t\t */\n \t\tsnprintf(test_file, sizeof(test_file), \"%s.c\", t-\u003efile);\n-\t/*\n-\t * Diff test output and expected test output, contained between\n-\t * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case.\n-\t * For expected output lines, everything before '*' is stripped out.\n-\t * Also lines containing comment start and comment end markers are\n-\t * ignored. \n-\t */\n-\tsnprintf(diff_cmd, sizeof(diff_cmd),\n-\t\t \"awk '/START-EXPECTED-OUTPUT/{out=1;next} \"\n-\t\t \"/END-EXPECTED-OUTPUT/{out=0} \"\n-\t\t \"/\\\\/\\\\*|\\\\*\\\\//{next} \" /* ignore comment start/end lines */\n-\t\t \"out {sub(/^[ \\\\t]*\\\\*/, \\\"\\\"); print}' '%s' | diff -u - '%s'\",\n-\t\t test_file, out_file);\n-\terr = system(diff_cmd);\n-\tif (CHECK(err, \"diff\",\n-\t\t \"differing test output, output=%s, err=%d, diff cmd:\\n%s\\n\",\n-\t\t out_file, err, diff_cmd))\n+\n+\texpected = read_expected_output(test_file);\n+\tif (!ASSERT_OK_PTR(expected, \"read_expected_output\")) {\n+\t\terr = -errno;\n \t\tgoto done;\n+\t}\n \n-\tremove(out_file);\n+\t/*\n+\t * The mismatch has already been reported, so this only has to\n+\t * register the failure. ASSERT_OK() would append a stale errno to it.\n+\t */\n+\terr = compare_text_to_expected(dump, expected);\n+\tASSERT_EQ(err, 0, \"compare_text_to_expected\");\n \n done:\n+\tfree(expected);\n+\tfree(dump);\n \tbtf__free(btf);\n \treturn err;\n }\ndiff --git a/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c b/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c\nindex 7607cfc2408c2..d111fe1054476 100644\n--- a/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c\n+++ b/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c\n@@ -179,3 +179,26 @@ void test_prog_tests_framework_expected_msgs(void)\n \t\t}\n \t}\n }\n+\n+void test_prog_tests_framework_compare_text(void)\n+{\n+\tint err;\n+\n+\tif (test__start_subtest(\"compare_text_match\")) {\n+\t\terr = compare_text_to_expected(\"same\\n\", \"same\\n\");\n+\t\tASSERT_EQ(err, 0, \"match_rc\");\n+\t\ttest__end_subtest();\n+\t}\n+\n+\tif (test__start_subtest(\"compare_text_mismatch\")) {\n+\t\terr = compare_text_to_expected(\"line two\\n\", \"line one\\n\");\n+\t\tfflush(stdout);\n+\n+\t\tASSERT_EQ(err, -1, \"mismatch_rc\");\n+\t\tASSERT_HAS_SUBSTR(env.subtest_state-\u003elog_buf, \"-line one\",\n+\t\t\t\t \"diff_has_expected\");\n+\t\tASSERT_HAS_SUBSTR(env.subtest_state-\u003elog_buf, \"+line two\",\n+\t\t\t\t \"diff_has_actual\");\n+\t\ttest__end_subtest();\n+\t}\n+}\ndiff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c\nindex c970e7793dfcb..3f037949e9789 100644\n--- a/tools/testing/selftests/bpf/testing_helpers.c\n+++ b/tools/testing/selftests/bpf/testing_helpers.c\n@@ -534,3 +534,46 @@ int stack_mprotect(void)\n \t\t PROT_READ | PROT_WRITE | PROT_EXEC);\n \treturn ret;\n }\n+\n+int compare_text_to_expected(const char *actual, const char *expected)\n+{\n+\tchar exp_path[] = \"/tmp/selftest_expected.XXXXXX\";\n+\tchar act_path[] = \"/tmp/selftest_actual.XXXXXX\";\n+\tchar buf[512], cmd[128];\n+\tint exp_fd, act_fd;\n+\tFILE *p;\n+\n+\tif (!strcmp(actual, expected))\n+\t\treturn 0;\n+\n+\texp_fd = mkstemp(exp_path);\n+\tact_fd = mkstemp(act_path);\n+\tif (exp_fd \u003c 0 || act_fd \u003c 0) {\n+\t\tfprintf(stdout, \"output differs, no temp file for a diff\\n\");\n+\t\tgoto out;\n+\t}\n+\n+\tdprintf(exp_fd, \"%s\", expected);\n+\tdprintf(act_fd, \"%s\", actual);\n+\n+\tsnprintf(cmd, sizeof(cmd), \"diff -u '%s' '%s'\", exp_path, act_path);\n+\tp = popen(cmd, \"r\");\n+\tif (!p) {\n+\t\tfprintf(stdout, \"output differs, '%s' did not run\\n\", cmd);\n+\t\tgoto out;\n+\t}\n+\twhile (fgets(buf, sizeof(buf), p))\n+\t\tfputs(buf, stdout);\n+\tpclose(p);\n+\n+out:\n+\tif (exp_fd \u003e= 0) {\n+\t\tclose(exp_fd);\n+\t\tunlink(exp_path);\n+\t}\n+\tif (act_fd \u003e= 0) {\n+\t\tclose(act_fd);\n+\t\tunlink(act_path);\n+\t}\n+\treturn -1;\n+}\ndiff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h\nindex 2edc6fb7fc521..1c58a2f08b645 100644\n--- a/tools/testing/selftests/bpf/testing_helpers.h\n+++ b/tools/testing/selftests/bpf/testing_helpers.h\n@@ -61,4 +61,7 @@ int testing_prog_flags(void);\n bool is_jit_enabled(void);\n int stack_mprotect(void);\n \n+/* Runs diff(1) on mismatch */\n+int compare_text_to_expected(const char *actual, const char *expected);\n+\n #endif /* __TESTING_HELPERS_H */\n"
} |
| 2/1 | 2026/08/28 22:42 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies userspace tooling (bpftool) and BPF selftests under tools/, with no changes to kernel code or runtime behavior.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 54c129546f99040846d8d91c94a88c1e831aa55e
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 28 22:42:53 2026 +0000
syz-cluster: applied patch under review
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index c9589026da8d6..bca0a3982f09b 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -805,9 +805,13 @@ static int dump_btf_c(const struct btf *btf,
if (sort_dump)
datums = sort_btf_c(btf);
- for (i = 1; i < cnt; i++) {
+ for (i = 0; i < cnt; i++) {
int idx = datums ? datums[i].index : i;
+ /* type ID 0 is void, skip it */
+ if (idx == 0)
+ continue;
+
err = btf_dump__dump_type(d, idx);
if (err)
goto done;
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 5f1a3bfc0569f..d99581257e54f 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -119,7 +119,9 @@ TEST_GEN_PROGS += test_progs-cpuv4
TEST_INST_SUBDIRS += cpuv4
endif
-TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
+TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c) \
+ bpftool_btf_dump_sorted.expected \
+ bpftool_btf_dump_unsorted.expected
# Order correspond to 'make run_tests' order
TEST_PROGS := test_kmod.sh \
@@ -887,7 +889,9 @@ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \
ima_setup.sh \
$(VERIFY_SIG_SETUP) \
$(wildcard progs/btf_dump_test_case_*.c) \
- $(wildcard progs/*.bpf.o)
+ $(wildcard progs/*.bpf.o) \
+ bpftool_btf_dump_sorted.expected \
+ bpftool_btf_dump_unsorted.expected
TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS
$(eval $(call DEFINE_TEST_RUNNER,test_progs))
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
new file mode 100644
index 0000000000000..5470b2b432291
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -0,0 +1,48 @@
+#ifndef __VMLINUX_H__
+#define __VMLINUX_H__
+
+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
+#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
+#endif
+
+#ifndef __ksym
+#define __ksym __attribute__((section(".ksyms")))
+#endif
+
+#ifndef __weak
+#define __weak __attribute__((weak))
+#endif
+
+#ifndef __bpf_fastcall
+#if __has_attribute(bpf_fastcall)
+#define __bpf_fastcall __attribute__((bpf_fastcall))
+#else
+#define __bpf_fastcall
+#endif
+#endif
+
+enum {
+ E0 = 1,
+};
+
+struct holey {
+ int c;
+ long: 32;
+ long: 32;
+ int tail;
+};
+
+struct s {
+ int f;
+};
+
+
+/* BPF kfuncs */
+#ifndef BPF_NO_KFUNC_PROTOTYPES
+#endif
+
+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
+#pragma clang attribute pop
+#endif
+
+#endif /* __VMLINUX_H__ */
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
new file mode 100644
index 0000000000000..4310adc2ed514
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -0,0 +1,48 @@
+#ifndef __VMLINUX_H__
+#define __VMLINUX_H__
+
+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
+#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
+#endif
+
+#ifndef __ksym
+#define __ksym __attribute__((section(".ksyms")))
+#endif
+
+#ifndef __weak
+#define __weak __attribute__((weak))
+#endif
+
+#ifndef __bpf_fastcall
+#if __has_attribute(bpf_fastcall)
+#define __bpf_fastcall __attribute__((bpf_fastcall))
+#else
+#define __bpf_fastcall
+#endif
+#endif
+
+struct holey {
+ int c;
+ long: 32;
+ long: 32;
+ int tail;
+};
+
+enum {
+ E0 = 1,
+};
+
+struct s {
+ int f;
+};
+
+
+/* BPF kfuncs */
+#ifndef BPF_NO_KFUNC_PROTOTYPES
+#endif
+
+#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
+#pragma clang attribute pop
+#endif
+
+#endif /* __VMLINUX_H__ */
diff --git a/tools/testing/selftests/bpf/bpftool_helpers.c b/tools/testing/selftests/bpf/bpftool_helpers.c
index 0a2a4f0a2794b..c49fdd90eb033 100644
--- a/tools/testing/selftests/bpf/bpftool_helpers.c
+++ b/tools/testing/selftests/bpf/bpftool_helpers.c
@@ -53,6 +53,7 @@ static int run_command(char *args, char *output_buf, size_t output_max_len)
static char bpftool_path[PATH_MAX] = {};
bool suppress_output = !(output_buf && output_max_len);
char command[BPFTOOL_FULL_CMD_MAX_LEN];
+ size_t n;
FILE *f;
int ret;
@@ -68,8 +69,10 @@ static int run_command(char *args, char *output_buf, size_t output_max_len)
if (!f)
return 1;
- if (!suppress_output)
- fread(output_buf, 1, output_max_len, f);
+ if (!suppress_output) {
+ n = fread(output_buf, 1, output_max_len - 1, f);
+ output_buf[n] = '\0';
+ }
ret = pclose(f);
return ret;
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
new file mode 100644
index 0000000000000..d5b25302b0c89
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <bpftool_helpers.h>
+#include <bpf/btf.h>
+#include <limits.h>
+#include <unistd.h>
+#include "btf_helpers.h"
+#include "testing_helpers.h"
+
+#define DUMP_BUF_SZ (16 * 1024)
+
+#define EXPECTED_SORTED "bpftool_btf_dump_sorted.expected"
+#define EXPECTED_UNSORTED "bpftool_btf_dump_unsorted.expected"
+
+/*
+ * struct holey {
+ * int c;
+ * <64-bit hole>
+ * int tail;
+ * };
+ * enum { E0 = 1 };
+ * struct s { int f; };
+ */
+static struct btf *mk_btf(void)
+{
+ struct btf *btf;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "new_empty"))
+ return NULL;
+
+ btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ btf__add_int(btf, "long int", 4, BTF_INT_SIGNED);
+
+ btf__add_struct(btf, "holey", 16);
+ btf__add_field(btf, "c", 1, 0, 0);
+ btf__add_field(btf, "tail", 1, 96, 0);
+
+ btf__add_enum(btf, NULL, 4);
+ btf__add_enum_value(btf, "E0", 1);
+
+ btf__add_struct(btf, "s", 4);
+ btf__add_field(btf, "f", 1, 0, 0);
+
+ VALIDATE_RAW_BTF(
+ btf,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] INT 'long int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[3] STRUCT 'holey' size=16 vlen=2\n"
+ "\t'c' type_id=1 bits_offset=0\n"
+ "\t'tail' type_id=1 bits_offset=96",
+ "[4] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+ "\t'E0' val=1",
+ "[5] STRUCT 's' size=4 vlen=1\n"
+ "\t'f' type_id=1 bits_offset=0");
+
+ return btf;
+}
+
+static int btf_to_tmpfile(const struct btf *btf, char *path)
+{
+ ssize_t written;
+ const void *raw;
+ __u32 sz;
+ int fd;
+
+ raw = btf__raw_data(btf, &sz);
+ if (!ASSERT_OK_PTR(raw, "raw_data"))
+ return -1;
+
+ snprintf(path, PATH_MAX, "/tmp/bpftool_btf_dump.XXXXXX");
+ fd = mkstemp(path);
+ if (!ASSERT_OK_FD(fd, "mkstemp_btf"))
+ return -1;
+
+ written = write(fd, raw, sz);
+ close(fd);
+ if (!ASSERT_EQ(written, sz, "write_btf")) {
+ unlink(path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *dump_c(const char *btf_path, bool sorted)
+{
+ char args[MAX_BPFTOOL_CMD_LEN];
+ char *buf;
+ int err;
+
+ buf = malloc(DUMP_BUF_SZ);
+ if (!ASSERT_OK_PTR(buf, "alloc_dump"))
+ return NULL;
+
+ snprintf(args, sizeof(args), "btf dump file %s format c%s",
+ btf_path, sorted ? "" : " unsorted");
+
+ err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
+ if (!ASSERT_OK(err, "btf_dump_format_c")) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static char *read_expected(const char *path)
+{
+ char *buf = NULL;
+ size_t cap = 0;
+ FILE *f;
+ int err;
+
+ f = fopen(path, "r");
+ if (!f) {
+ err = errno;
+ PRINT_FAIL("can't open expected output '%s': errno %d\n", path, err);
+ return NULL;
+ }
+
+ /* no NUL in a generated header, so this reads to the end */
+ if (getdelim(&buf, &cap, '\0', f) < 0) {
+ err = errno;
+ PRINT_FAIL("can't read expected output '%s': errno %d\n", path, err);
+ free(buf);
+ buf = NULL;
+ }
+
+ fclose(f);
+ return buf;
+}
+
+static void test_dump(const char *btf_path, bool sorted)
+{
+ const char *exp_path;
+ char *dump, *exp;
+ int err;
+
+ exp_path = sorted ? EXPECTED_SORTED : EXPECTED_UNSORTED;
+
+ dump = dump_c(btf_path, sorted);
+ if (!dump)
+ return;
+
+ exp = read_expected(exp_path);
+ if (!exp)
+ goto out_dump;
+
+ err = compare_text_to_expected(dump, exp);
+ ASSERT_OK(err, sorted ? "cmp_sorted" : "cmp_unsorted");
+
+ free(exp);
+out_dump:
+ free(dump);
+}
+
+void test_bpftool_btf_dump(void)
+{
+ char path[PATH_MAX];
+ struct btf *btf;
+
+ btf = mk_btf();
+ if (!btf)
+ return;
+
+ if (btf_to_tmpfile(btf, path))
+ goto out_btf;
+
+ if (test__start_subtest("c_sorted"))
+ test_dump(path, true);
+ if (test__start_subtest("c_unsorted"))
+ test_dump(path, false);
+
+ unlink(path);
+out_btf:
+ btf__free(btf);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index 9f1b50e07a298..fe04a955d46cc 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <bpf/btf.h>
-
-static int duration = 0;
+#include "testing_helpers.h"
void btf_dump_printf(void *ctx, const char *fmt, va_list args)
{
@@ -45,12 +44,75 @@ static int btf_dump_all_types(const struct btf *btf, void *ctx)
return err;
}
+/*
+ * Expected output is embedded in the test case source, between
+ * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT markers. A region is either
+ * plain C, where a declaration doubles as its own expectation, or C wrapped in
+ * a block comment, where the rendered form differs from the source. All
+ * regions of a file concatenate into one expectation, compared against one
+ * whole-file dump.
+ *
+ * Returns a malloc'd buffer for the caller to free, or NULL on failure.
+ */
+static char *read_expected_output(const char *path)
+{
+ size_t out_sz = 0, line_cap = 0;
+ char *out = NULL, *line = NULL;
+ bool in_region = false;
+ FILE *f, *out_file;
+
+ f = fopen(path, "r");
+ if (!f)
+ return NULL;
+
+ out_file = open_memstream(&out, &out_sz);
+ if (!out_file) {
+ fclose(f);
+ return NULL;
+ }
+
+ while (getline(&line, &line_cap, f) > 0) {
+ const char *p;
+
+ if (strstr(line, "START-EXPECTED-OUTPUT")) {
+ in_region = true;
+ continue;
+ }
+ if (strstr(line, "END-EXPECTED-OUTPUT"))
+ in_region = false;
+ if (!in_region)
+ continue;
+
+ p = line + strspn(line, " \t");
+
+ /* opening or closing line of a commented out region */
+ if (!strncmp(p, "/*", 2) || !strncmp(p, "*/", 2))
+ continue;
+
+ /*
+ * Only a '*' directly after the indentation is a comment
+ * prefix. Without one the line is taken as it is, leading
+ * whitespace included.
+ */
+ p = *p == '*' ? p + 1 : line;
+
+ fputs(p, out_file);
+ }
+
+ free(line);
+ fclose(f);
+ fclose(out_file);
+ return out;
+}
+
static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
{
- char test_file[256], out_file[256], diff_cmd[1024];
+ char *dump = NULL, *expected = NULL;
struct btf *btf = NULL;
- int err = 0, fd = -1;
- FILE *f = NULL;
+ char test_file[256];
+ size_t dump_sz = 0;
+ int err = 0;
+ FILE *f;
snprintf(test_file, sizeof(test_file), "%s.bpf.o", t->file);
@@ -69,29 +131,21 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
if (!t->known_ptr_sz) {
btf__set_pointer_size(btf, 8);
} else {
- CHECK(btf__pointer_size(btf) != 8, "ptr_sz", "exp %d, got %zu\n",
- 8, btf__pointer_size(btf));
- }
+ size_t ptr_sz = btf__pointer_size(btf);
- snprintf(out_file, sizeof(out_file), "/tmp/%s.output.XXXXXX", t->file);
- fd = mkstemp(out_file);
- if (!ASSERT_GE(fd, 0, "create_tmp")) {
- err = fd;
- goto done;
+ ASSERT_EQ(ptr_sz, (size_t)8, "ptr_sz");
}
- f = fdopen(fd, "w");
- if (CHECK(f == NULL, "open_tmp", "failed to open file: %s(%d)\n",
- strerror(errno), errno)) {
- close(fd);
+
+ f = open_memstream(&dump, &dump_sz);
+ if (!ASSERT_OK_PTR(f, "open_memstream")) {
+ err = -errno;
goto done;
}
err = btf_dump_all_types(btf, f);
fclose(f);
- close(fd);
- if (CHECK(err, "btf_dump", "failure during C dumping: %d\n", err)) {
+ if (!ASSERT_OK(err, "btf_dump"))
goto done;
- }
snprintf(test_file, sizeof(test_file), "progs/%s.c", t->file);
if (access(test_file, R_OK) == -1)
@@ -100,28 +154,23 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
* without preserving the directory structure.
*/
snprintf(test_file, sizeof(test_file), "%s.c", t->file);
- /*
- * Diff test output and expected test output, contained between
- * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case.
- * For expected output lines, everything before '*' is stripped out.
- * Also lines containing comment start and comment end markers are
- * ignored.
- */
- snprintf(diff_cmd, sizeof(diff_cmd),
- "awk '/START-EXPECTED-OUTPUT/{out=1;next} "
- "/END-EXPECTED-OUTPUT/{out=0} "
- "/\\/\\*|\\*\\//{next} " /* ignore comment start/end lines */
- "out {sub(/^[ \\t]*\\*/, \"\"); print}' '%s' | diff -u - '%s'",
- test_file, out_file);
- err = system(diff_cmd);
- if (CHECK(err, "diff",
- "differing test output, output=%s, err=%d, diff cmd:\n%s\n",
- out_file, err, diff_cmd))
+
+ expected = read_expected_output(test_file);
+ if (!ASSERT_OK_PTR(expected, "read_expected_output")) {
+ err = -errno;
goto done;
+ }
- remove(out_file);
+ /*
+ * The mismatch has already been reported, so this only has to
+ * register the failure. ASSERT_OK() would append a stale errno to it.
+ */
+ err = compare_text_to_expected(dump, expected);
+ ASSERT_EQ(err, 0, "compare_text_to_expected");
done:
+ free(expected);
+ free(dump);
btf__free(btf);
return err;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c b/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c
index 7607cfc2408c2..d111fe1054476 100644
--- a/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c
+++ b/tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c
@@ -179,3 +179,26 @@ void test_prog_tests_framework_expected_msgs(void)
}
}
}
+
+void test_prog_tests_framework_compare_text(void)
+{
+ int err;
+
+ if (test__start_subtest("compare_text_match")) {
+ err = compare_text_to_expected("same\n", "same\n");
+ ASSERT_EQ(err, 0, "match_rc");
+ test__end_subtest();
+ }
+
+ if (test__start_subtest("compare_text_mismatch")) {
+ err = compare_text_to_expected("line two\n", "line one\n");
+ fflush(stdout);
+
+ ASSERT_EQ(err, -1, "mismatch_rc");
+ ASSERT_HAS_SUBSTR(env.subtest_state->log_buf, "-line one",
+ "diff_has_expected");
+ ASSERT_HAS_SUBSTR(env.subtest_state->log_buf, "+line two",
+ "diff_has_actual");
+ test__end_subtest();
+ }
+}
diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c
index c970e7793dfcb..3f037949e9789 100644
--- a/tools/testing/selftests/bpf/testing_helpers.c
+++ b/tools/testing/selftests/bpf/testing_helpers.c
@@ -534,3 +534,46 @@ int stack_mprotect(void)
PROT_READ | PROT_WRITE | PROT_EXEC);
return ret;
}
+
+int compare_text_to_expected(const char *actual, const char *expected)
+{
+ char exp_path[] = "/tmp/selftest_expected.XXXXXX";
+ char act_path[] = "/tmp/selftest_actual.XXXXXX";
+ char buf[512], cmd[128];
+ int exp_fd, act_fd;
+ FILE *p;
+
+ if (!strcmp(actual, expected))
+ return 0;
+
+ exp_fd = mkstemp(exp_path);
+ act_fd = mkstemp(act_path);
+ if (exp_fd < 0 || act_fd < 0) {
+ fprintf(stdout, "output differs, no temp file for a diff\n");
+ goto out;
+ }
+
+ dprintf(exp_fd, "%s", expected);
+ dprintf(act_fd, "%s", actual);
+
+ snprintf(cmd, sizeof(cmd), "diff -u '%s' '%s'", exp_path, act_path);
+ p = popen(cmd, "r");
+ if (!p) {
+ fprintf(stdout, "output differs, '%s' did not run\n", cmd);
+ goto out;
+ }
+ while (fgets(buf, sizeof(buf), p))
+ fputs(buf, stdout);
+ pclose(p);
+
+out:
+ if (exp_fd >= 0) {
+ close(exp_fd);
+ unlink(exp_path);
+ }
+ if (act_fd >= 0) {
+ close(act_fd);
+ unlink(act_path);
+ }
+ return -1;
+}
diff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h
index 2edc6fb7fc521..1c58a2f08b645 100644
--- a/tools/testing/selftests/bpf/testing_helpers.h
+++ b/tools/testing/selftests/bpf/testing_helpers.h
@@ -61,4 +61,7 @@ int testing_prog_flags(void);
bool is_jit_enabled(void);
int stack_mprotect(void);
+/* Runs diff(1) on mismatch */
+int compare_text_to_expected(const char *actual, const char *expected);
+
#endif /* __TESTING_HELPERS_H */
|
| 3/2 | 2026/08/28 22:42 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9231 output: 62 thoughts: 744 Thoughts: **Analyzing the Patch for Fuzzing Worthiness** Okay, let's break this down. First, I need to understand what this patch is even doing. The modified files are all in the `tools/` directory, specifically `tools/bpf/bpftool/` and `tools/testing/selftests/bpf/`. *Crucially*, nothing is touching the kernel source directly – no `kernel/`, `net/`, `drivers/`, etc. This means we're dealing with userspace tools, `bpftool`, and its selftests. That's a huge clue. Now, I have to apply the criteria. We're looking for whether this patch is worth fuzzing from the perspective of kernel vulnerability discovery using syzkaller. The instructions say to return `WorthFuzzing=false` if it only modifies certain types of things: documentation, Kconfig files, comments, decorative changes like logging, constants that don't change execution, unreachable code, or very specific hardware-dependent drivers or teardown callbacks. I've seen these before, so I have a mental checklist. Looking at the files again, I see: * `tools/bpf/bpftool/btf.c`: bpftool, a userspace tool * `tools/testing/selftests/bpf/...`: Multiple selftest files related to bpftool. Everything is firmly in the `tools/` realm. These are *not* kernel modifications. Syzkaller targets the kernel itself, and changes in these locations don't touch the kernel binary or change its execution logic. Therefore, I'm confident that `WorthFuzzing` should be `false`. I don't see any other factors that would change that conclusion. No need to look for any other config or tools to verify. It is very unambiguous. Time to execute the `set-results` tool and move on. |
| 4/2 | 2026/08/28 22:43 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies userspace tooling (bpftool) and BPF selftests under tools/, with no changes to kernel code or runtime behavior.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies userspace tooling (bpftool) and BPF selftests under tools/, with no changes to kernel code or runtime behavior.",
"WorthFuzzing": false
} |
| 5/1 | 2026/08/28 22:43 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|