AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/12 06:09 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch only modifies user-space BPF selftests and the veristat tool. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

1/1 2026/08/12 06:09 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 5714622a10a9258a50c4d5bdd2c592045badd884\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Aug 12 06:09:24 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/bpf/prog_tests/test_veristat.c b/tools/testing/selftests/bpf/prog_tests/test_veristat.c\nindex 9aff08ac55c07..11f3de2b66ad1 100644\n--- a/tools/testing/selftests/bpf/prog_tests/test_veristat.c\n+++ b/tools/testing/selftests/bpf/prog_tests/test_veristat.c\n@@ -37,6 +37,14 @@ static struct fixture *init_fixture(void)\n \treturn fix;\n }\n \n+static void read_output(struct fixture *fix)\n+{\n+\tssize_t len = pread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz - 1, 0);\n+\n+\tfix-\u003eoutput[len \u003c 0 ? 0 : len] = 0;\n+\tASSERT_GE(len, 0, \"pread\");\n+}\n+\n static void teardown_fixture(struct fixture *fix)\n {\n \tfree(fix-\u003eoutput);\n@@ -74,7 +82,7 @@ static void test_set_global_vars_succeeds(void)\n \t    \" -G \\\"struct11 [ 7 ] [ 5 ] .struct2[0][1].u.mat[3][0] = 175\\\" \" \\\n \t    \" -vl2 \u003e %s\", fix-\u003everistat, fix-\u003etmpfile);\n \n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"=0xf000000000000001 \", \"var_s64 = 0xf000000000000001\");\n \t__CHECK_STR(\"=0xfedcba9876543210 \", \"var_u64 = 0xfedcba9876543210\");\n \t__CHECK_STR(\"=0x80000000 \", \"var_s32 = -0x80000000\");\n@@ -116,7 +124,7 @@ static void test_set_global_vars_from_file_succeeds(void)\n \tsyncfs(fd);\n \tSYS(out, \"%s set_global_vars.bpf.o -G \\\"@%s\\\" -vl2 \u003e %s\",\n \t    fix-\u003everistat, input_file, fix-\u003etmpfile);\n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"=0x8000 \", \"var_s16 = -32768\");\n \t__CHECK_STR(\"=0xecec \", \"var_u16 = 60652\");\n \n@@ -134,7 +142,7 @@ static void test_set_global_vars_out_of_range(void)\n \t\t \"%s set_global_vars.bpf.o -G \\\"var_s32 = 2147483648\\\" -vl2 2\u003e %s\",\n \t\t fix-\u003everistat, fix-\u003etmpfile);\n \n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"is out of range [-2147483648; 2147483647]\", \"out of range\");\n \n out:\n@@ -149,7 +157,7 @@ static void test_unsupported_ptr_array_type(void)\n \t\t \"%s set_global_vars.bpf.o -G \\\"ptr_arr[0] = 0\\\" -vl2 2\u003e %s\",\n \t\t fix-\u003everistat, fix-\u003etmpfile);\n \n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"Can't set ptr_arr[0]. Only ints and enums are supported\", \"ptr_arr\");\n \n out:\n@@ -164,7 +172,7 @@ static void test_array_out_of_bounds(void)\n \t\t \"%s set_global_vars.bpf.o -G \\\"arr[99] = 0\\\" -vl2 2\u003e %s\",\n \t\t fix-\u003everistat, fix-\u003etmpfile);\n \n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"Array index 99 is out of bounds\", \"arr[99]\");\n \n out:\n@@ -179,7 +187,7 @@ static void test_array_index_not_found(void)\n \t\t \"%s set_global_vars.bpf.o -G \\\"arr[EG2] = 0\\\" -vl2 2\u003e %s\",\n \t\t fix-\u003everistat, fix-\u003etmpfile);\n \n-\tread(fix-\u003efd, fix-\u003eoutput, fix-\u003esz);\n+\tread_output(fix);\n \t__CHECK_STR(\"Can't resolve enum value EG2\", \"arr[EG2]\");\n \n out:\n@@ -230,6 +238,97 @@ static void test_no_array_index_for_array(void)\n \tteardown_fixture(fix);\n }\n \n+/*\n+ * Name filter tests below run veristat on veristat_foo.bpf.o and\n+ * veristat_bar.bpf.o, both defining programs 'foo', 'bar' and 'buz'.\n+ * Every entry describes a single (filters, file, prog) combination and\n+ * tells whether that program is expected in the veristat output:\n+ * 'true' if it is, 'false' if it is not and -1 if veristat is expected\n+ * to reject the filter.\n+ */\n+#define FILTER_OBJS \"veristat_foo.bpf.o veristat_bar.bpf.o\"\n+\n+static const struct name_filter_case {\n+\tconst char *filters;\n+\tconst char *file;\n+\tconst char *prog;\n+\tint included;\n+} name_filter_cases[] = {\n+\t/* no filters, every program is processed */\n+\t{ \"\",\t\t\t\"foo\", \"foo\", true  },\n+\t{ \"\",\t\t\t\"foo\", \"bar\", true  },\n+\t{ \"\",\t\t\t\"foo\", \"buz\", true  },\n+\t{ \"\",\t\t\t\"bar\", \"foo\", true  },\n+\t{ \"\",\t\t\t\"bar\", \"bar\", true  },\n+\t{ \"\",\t\t\t\"bar\", \"buz\", true  },\n+\t/* deny filters */\n+\t{ \"-f '!*foo*'\",\t\"foo\", \"bar\", false },\n+\t{ \"-f '!*foo*'\",\t\"bar\", \"foo\", false },\n+\t{ \"-f '!*foo*'\",\t\"bar\", \"bar\", true  },\n+\t{ \"-f '!*foo*/bar'\",\t\"foo\", \"bar\", false },\n+\t{ \"-f '!*foo*/bar'\",\t\"foo\", \"buz\", true  },\n+\t{ \"-f '!*foo*/bar'\",\t\"bar\", \"bar\", true  },\n+\t{ \"-f '!*foo*/'\",\t\"foo\", \"bar\", false },\n+\t{ \"-f '!*foo*/'\",\t\"bar\", \"bar\", true  },\n+\t{ \"-f '!/bar'\",\t\t\"foo\", \"bar\", false },\n+\t{ \"-f '!/bar'\",\t\t\"foo\", \"foo\", true  },\n+\t{ \"-f '!/'\",\t\t\"foo\", \"bar\", -1    },\n+\t{ \"-f '!'\",\t\t\"foo\", \"bar\", -1    },\n+\t/* allow filters */\n+\t{ \"-f '*foo*'\",\t\t\"foo\", \"bar\", true  },\n+\t{ \"-f '*foo*'\",\t\t\"bar\", \"foo\", true  },\n+\t{ \"-f '*foo*'\",\t\t\"bar\", \"bar\", false },\n+\t{ \"-f '*foo*/bar'\",\t\"foo\", \"bar\", true  },\n+\t{ \"-f '*foo*/bar'\",\t\"foo\", \"buz\", false },\n+\t{ \"-f '*foo*/bar'\",\t\"bar\", \"bar\", false },\n+\t{ \"-f '*foo*/'\",\t\"foo\", \"bar\", true  },\n+\t{ \"-f '*foo*/'\",\t\"bar\", \"bar\", false },\n+\t{ \"-f '/bar'\",\t\t\"foo\", \"bar\", true  },\n+\t{ \"-f '/bar'\",\t\t\"foo\", \"foo\", false },\n+\t{ \"-f '/'\",\t\t\"foo\", \"bar\", -1    },\n+\t{ \"-f ''\",\t\t\"foo\", \"bar\", -1    },\n+\t/* allow and deny filters combined */\n+\t{ \"-f '*foo*/' -f '!/bar'\", \"foo\", \"foo\", true  },\n+\t{ \"-f '*foo*/' -f '!/bar'\", \"foo\", \"bar\", false },\n+\t{ \"-f '*foo*/' -f '!/bar'\", \"bar\", \"foo\", false },\n+};\n+\n+static void test_name_filters(void)\n+{\n+\tstruct fixture *fix = init_fixture();\n+\tconst struct name_filter_case *t;\n+\tchar cmd[512], row[64], name[128];\n+\tint i, err;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(name_filter_cases); i++) {\n+\t\tt = \u0026name_filter_cases[i];\n+\t\t/* stderr is merged with stdout in order to catch error messages */\n+\t\tsnprintf(cmd, sizeof(cmd), \"%s \" FILTER_OBJS \" -q -o csv -e file,prog %s \u003e %s 2\u003e\u00261\",\n+\t\t\t fix-\u003everistat, t-\u003efilters, fix-\u003etmpfile);\n+\t\terr = system(cmd);\n+\t\tread_output(fix);\n+\n+\t\tsnprintf(row, sizeof(row), \"veristat_%s.bpf.o,%s\", t-\u003efile, t-\u003eprog);\n+\t\tsnprintf(name, sizeof(name), \"veristat %s: %s\", t-\u003efilters, row);\n+\t\tswitch (t-\u003eincluded) {\n+\t\tcase true:\n+\t\t\tASSERT_OK(err, name);\n+\t\t\tASSERT_HAS_SUBSTR(fix-\u003eoutput, row, name);\n+\t\t\tbreak;\n+\t\tcase false:\n+\t\t\tASSERT_OK(err, name);\n+\t\t\tASSERT_FALSE(!!strstr(fix-\u003eoutput, row), name);\n+\t\t\tbreak;\n+\t\tcase -1:\n+\t\t\tASSERT_NEQ(err, 0, name);\n+\t\t\tASSERT_HAS_SUBSTR(fix-\u003eoutput, \"Invalid filter\", name);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tteardown_fixture(fix);\n+}\n+\n void test_veristat(void)\n {\n \tif (test__start_subtest(\"set_global_vars_succeeds\"))\n@@ -256,6 +355,8 @@ void test_veristat(void)\n \tif (test__start_subtest(\"test_no_array_index_for_array\"))\n \t\ttest_no_array_index_for_array();\n \n+\tif (test__start_subtest(\"name_filters\"))\n+\t\ttest_name_filters();\n }\n \n #undef __CHECK_STR\ndiff --git a/tools/testing/selftests/bpf/progs/veristat_bar.c b/tools/testing/selftests/bpf/progs/veristat_bar.c\nnew file mode 100644\nindex 0000000000000..83d2a2a1dfc96\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/veristat_bar.c\n@@ -0,0 +1,3 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+#include \"veristat_foo.c\"\ndiff --git a/tools/testing/selftests/bpf/progs/veristat_foo.c b/tools/testing/selftests/bpf/progs/veristat_foo.c\nnew file mode 100644\nindex 0000000000000..bd24b97664b49\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/veristat_foo.c\n@@ -0,0 +1,31 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */\n+#include \u003clinux/bpf.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+\n+/*\n+ * Programs below exist only to exercise veristat's -f name filters,\n+ * their bodies are irrelevant, only the names matter.\n+ * This file is also included by veristat_bar.c, so that the same set of\n+ * program names is available in two differently named object files.\n+ */\n+\n+SEC(\"socket\")\n+int foo(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"socket\")\n+int bar(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"socket\")\n+int buz(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c\nindex c9c257784ee3d..6aaa06790daa2 100644\n--- a/tools/testing/selftests/bpf/veristat.c\n+++ b/tools/testing/selftests/bpf/veristat.c\n@@ -514,6 +514,40 @@ static bool is_bpf_obj_file(const char *path) {\n \treturn err == 0;\n }\n \n+/* Exact filter match */\n+static bool name_filter_matches(struct filter *f, const char *filename, const char *prog_name)\n+{\n+\tif (f-\u003eany_glob)\n+\t\treturn glob_matches(filename, f-\u003eany_glob) ||\n+\t\t       (prog_name \u0026\u0026 glob_matches(prog_name, f-\u003eany_glob));\n+\tif (f-\u003efile_glob \u0026\u0026 f-\u003eprog_glob)\n+\t\treturn prog_name \u0026\u0026\n+\t\t       glob_matches(filename, f-\u003efile_glob) \u0026\u0026\n+\t\t       glob_matches(prog_name, f-\u003eprog_glob);\n+\tif (f-\u003efile_glob)\n+\t\treturn glob_matches(filename, f-\u003efile_glob);\n+\tif (f-\u003eprog_glob)\n+\t\treturn prog_name \u0026\u0026 glob_matches(prog_name, f-\u003eprog_glob);\n+\treturn false;\n+}\n+\n+/* Check if the filter does not outright reject the file name */\n+static bool name_filter_may_match(struct filter *f, const char *filename)\n+{\n+\tif (f-\u003efile_glob)\n+\t\treturn glob_matches(filename, f-\u003efile_glob);\n+\t/*\n+\t * If we don't know program name yet, any_glob filter\n+\t * has to assume that current BPF object file might be\n+\t * relevant; we'll check again later on after opening\n+\t * BPF object file, at which point program name will\n+\t * be known finally.\n+\t */\n+\tif (f-\u003eany_glob || f-\u003eprog_glob)\n+\t\treturn true;\n+\treturn false;\n+}\n+\n static bool should_process_file_prog(const char *filename, const char *prog_name)\n {\n \tstruct filter *f;\n@@ -521,16 +555,7 @@ static bool should_process_file_prog(const char *filename, const char *prog_name\n \n \tfor (i = 0; i \u003c env.deny_filter_cnt; i++) {\n \t\tf = \u0026env.deny_filters[i];\n-\t\tif (f-\u003ekind != FILTER_NAME)\n-\t\t\tcontinue;\n-\n-\t\tif (f-\u003eany_glob \u0026\u0026 glob_matches(filename, f-\u003eany_glob))\n-\t\t\treturn false;\n-\t\tif (f-\u003eany_glob \u0026\u0026 prog_name \u0026\u0026 glob_matches(prog_name, f-\u003eany_glob))\n-\t\t\treturn false;\n-\t\tif (f-\u003efile_glob \u0026\u0026 glob_matches(filename, f-\u003efile_glob))\n-\t\t\treturn false;\n-\t\tif (f-\u003eprog_glob \u0026\u0026 prog_name \u0026\u0026 glob_matches(prog_name, f-\u003eprog_glob))\n+\t\tif (f-\u003ekind == FILTER_NAME \u0026\u0026 name_filter_matches(f, filename, prog_name))\n \t\t\treturn false;\n \t}\n \n@@ -540,24 +565,15 @@ static bool should_process_file_prog(const char *filename, const char *prog_name\n \t\t\tcontinue;\n \n \t\tallow_cnt++;\n-\t\tif (f-\u003eany_glob) {\n-\t\t\tif (glob_matches(filename, f-\u003eany_glob))\n-\t\t\t\treturn true;\n-\t\t\t/* If we don't know program name yet, any_glob filter\n-\t\t\t * has to assume that current BPF object file might be\n-\t\t\t * relevant; we'll check again later on after opening\n-\t\t\t * BPF object file, at which point program name will\n-\t\t\t * be known finally.\n-\t\t\t */\n-\t\t\tif (!prog_name || glob_matches(prog_name, f-\u003eany_glob))\n-\t\t\t\treturn true;\n-\t\t} else {\n-\t\t\tif (f-\u003efile_glob \u0026\u0026 !glob_matches(filename, f-\u003efile_glob))\n-\t\t\t\tcontinue;\n-\t\t\tif (f-\u003eprog_glob \u0026\u0026 prog_name \u0026\u0026 !glob_matches(prog_name, f-\u003eprog_glob))\n-\t\t\t\tcontinue;\n+\t\tif (prog_name \u0026\u0026 name_filter_matches(f, filename, prog_name))\n+\t\t\treturn true;\n+\t\t/*\n+\t\t * If there is no prog_name and the file name is not blocked by\n+\t\t * the filter, allow to open the file. Afterwards there would be\n+\t\t * a second refining query with prog_name set.\n+\t\t */\n+\t\tif (!prog_name \u0026\u0026 name_filter_may_match(f, filename))\n \t\t\treturn true;\n-\t\t}\n \t}\n \n \t/* if there are no file/prog name allow filters, allow all progs,\n@@ -703,6 +719,12 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)\n \t\t}\n \t}\n \n+\tif ((!f-\u003eany_glob \u0026\u0026 !f-\u003efile_glob \u0026\u0026 !f-\u003eprog_glob) ||\n+\t    (f-\u003eany_glob \u0026\u0026 strcmp(f-\u003eany_glob, \"\") == 0)) {\n+\t\tfprintf(stderr, \"Invalid filter: '%s'\\n\", str);\n+\t\treturn -EINVAL;\n+\t}\n+\n \t*cnt += 1;\n \treturn 0;\n }\n"
}

2/1 2026/08/12 06:09 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space BPF selftests and the veristat tool. It does not make any functional changes to the kernel code itself.",
  "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 5714622a10a9258a50c4d5bdd2c592045badd884
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Aug 12 06:09:24 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/bpf/prog_tests/test_veristat.c b/tools/testing/selftests/bpf/prog_tests/test_veristat.c
index 9aff08ac55c07..11f3de2b66ad1 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_veristat.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_veristat.c
@@ -37,6 +37,14 @@ static struct fixture *init_fixture(void)
 	return fix;
 }
 
+static void read_output(struct fixture *fix)
+{
+	ssize_t len = pread(fix->fd, fix->output, fix->sz - 1, 0);
+
+	fix->output[len < 0 ? 0 : len] = 0;
+	ASSERT_GE(len, 0, "pread");
+}
+
 static void teardown_fixture(struct fixture *fix)
 {
 	free(fix->output);
@@ -74,7 +82,7 @@ static void test_set_global_vars_succeeds(void)
 	    " -G \"struct11 [ 7 ] [ 5 ] .struct2[0][1].u.mat[3][0] = 175\" " \
 	    " -vl2 > %s", fix->veristat, fix->tmpfile);
 
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("=0xf000000000000001 ", "var_s64 = 0xf000000000000001");
 	__CHECK_STR("=0xfedcba9876543210 ", "var_u64 = 0xfedcba9876543210");
 	__CHECK_STR("=0x80000000 ", "var_s32 = -0x80000000");
@@ -116,7 +124,7 @@ static void test_set_global_vars_from_file_succeeds(void)
 	syncfs(fd);
 	SYS(out, "%s set_global_vars.bpf.o -G \"@%s\" -vl2 > %s",
 	    fix->veristat, input_file, fix->tmpfile);
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("=0x8000 ", "var_s16 = -32768");
 	__CHECK_STR("=0xecec ", "var_u16 = 60652");
 
@@ -134,7 +142,7 @@ static void test_set_global_vars_out_of_range(void)
 		 "%s set_global_vars.bpf.o -G \"var_s32 = 2147483648\" -vl2 2> %s",
 		 fix->veristat, fix->tmpfile);
 
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("is out of range [-2147483648; 2147483647]", "out of range");
 
 out:
@@ -149,7 +157,7 @@ static void test_unsupported_ptr_array_type(void)
 		 "%s set_global_vars.bpf.o -G \"ptr_arr[0] = 0\" -vl2 2> %s",
 		 fix->veristat, fix->tmpfile);
 
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("Can't set ptr_arr[0]. Only ints and enums are supported", "ptr_arr");
 
 out:
@@ -164,7 +172,7 @@ static void test_array_out_of_bounds(void)
 		 "%s set_global_vars.bpf.o -G \"arr[99] = 0\" -vl2 2> %s",
 		 fix->veristat, fix->tmpfile);
 
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("Array index 99 is out of bounds", "arr[99]");
 
 out:
@@ -179,7 +187,7 @@ static void test_array_index_not_found(void)
 		 "%s set_global_vars.bpf.o -G \"arr[EG2] = 0\" -vl2 2> %s",
 		 fix->veristat, fix->tmpfile);
 
-	read(fix->fd, fix->output, fix->sz);
+	read_output(fix);
 	__CHECK_STR("Can't resolve enum value EG2", "arr[EG2]");
 
 out:
@@ -230,6 +238,97 @@ static void test_no_array_index_for_array(void)
 	teardown_fixture(fix);
 }
 
+/*
+ * Name filter tests below run veristat on veristat_foo.bpf.o and
+ * veristat_bar.bpf.o, both defining programs 'foo', 'bar' and 'buz'.
+ * Every entry describes a single (filters, file, prog) combination and
+ * tells whether that program is expected in the veristat output:
+ * 'true' if it is, 'false' if it is not and -1 if veristat is expected
+ * to reject the filter.
+ */
+#define FILTER_OBJS "veristat_foo.bpf.o veristat_bar.bpf.o"
+
+static const struct name_filter_case {
+	const char *filters;
+	const char *file;
+	const char *prog;
+	int included;
+} name_filter_cases[] = {
+	/* no filters, every program is processed */
+	{ "",			"foo", "foo", true  },
+	{ "",			"foo", "bar", true  },
+	{ "",			"foo", "buz", true  },
+	{ "",			"bar", "foo", true  },
+	{ "",			"bar", "bar", true  },
+	{ "",			"bar", "buz", true  },
+	/* deny filters */
+	{ "-f '!*foo*'",	"foo", "bar", false },
+	{ "-f '!*foo*'",	"bar", "foo", false },
+	{ "-f '!*foo*'",	"bar", "bar", true  },
+	{ "-f '!*foo*/bar'",	"foo", "bar", false },
+	{ "-f '!*foo*/bar'",	"foo", "buz", true  },
+	{ "-f '!*foo*/bar'",	"bar", "bar", true  },
+	{ "-f '!*foo*/'",	"foo", "bar", false },
+	{ "-f '!*foo*/'",	"bar", "bar", true  },
+	{ "-f '!/bar'",		"foo", "bar", false },
+	{ "-f '!/bar'",		"foo", "foo", true  },
+	{ "-f '!/'",		"foo", "bar", -1    },
+	{ "-f '!'",		"foo", "bar", -1    },
+	/* allow filters */
+	{ "-f '*foo*'",		"foo", "bar", true  },
+	{ "-f '*foo*'",		"bar", "foo", true  },
+	{ "-f '*foo*'",		"bar", "bar", false },
+	{ "-f '*foo*/bar'",	"foo", "bar", true  },
+	{ "-f '*foo*/bar'",	"foo", "buz", false },
+	{ "-f '*foo*/bar'",	"bar", "bar", false },
+	{ "-f '*foo*/'",	"foo", "bar", true  },
+	{ "-f '*foo*/'",	"bar", "bar", false },
+	{ "-f '/bar'",		"foo", "bar", true  },
+	{ "-f '/bar'",		"foo", "foo", false },
+	{ "-f '/'",		"foo", "bar", -1    },
+	{ "-f ''",		"foo", "bar", -1    },
+	/* allow and deny filters combined */
+	{ "-f '*foo*/' -f '!/bar'", "foo", "foo", true  },
+	{ "-f '*foo*/' -f '!/bar'", "foo", "bar", false },
+	{ "-f '*foo*/' -f '!/bar'", "bar", "foo", false },
+};
+
+static void test_name_filters(void)
+{
+	struct fixture *fix = init_fixture();
+	const struct name_filter_case *t;
+	char cmd[512], row[64], name[128];
+	int i, err;
+
+	for (i = 0; i < ARRAY_SIZE(name_filter_cases); i++) {
+		t = &name_filter_cases[i];
+		/* stderr is merged with stdout in order to catch error messages */
+		snprintf(cmd, sizeof(cmd), "%s " FILTER_OBJS " -q -o csv -e file,prog %s > %s 2>&1",
+			 fix->veristat, t->filters, fix->tmpfile);
+		err = system(cmd);
+		read_output(fix);
+
+		snprintf(row, sizeof(row), "veristat_%s.bpf.o,%s", t->file, t->prog);
+		snprintf(name, sizeof(name), "veristat %s: %s", t->filters, row);
+		switch (t->included) {
+		case true:
+			ASSERT_OK(err, name);
+			ASSERT_HAS_SUBSTR(fix->output, row, name);
+			break;
+		case false:
+			ASSERT_OK(err, name);
+			ASSERT_FALSE(!!strstr(fix->output, row), name);
+			break;
+		case -1:
+			ASSERT_NEQ(err, 0, name);
+			ASSERT_HAS_SUBSTR(fix->output, "Invalid filter", name);
+			break;
+		}
+	}
+
+	teardown_fixture(fix);
+}
+
 void test_veristat(void)
 {
 	if (test__start_subtest("set_global_vars_succeeds"))
@@ -256,6 +355,8 @@ void test_veristat(void)
 	if (test__start_subtest("test_no_array_index_for_array"))
 		test_no_array_index_for_array();
 
+	if (test__start_subtest("name_filters"))
+		test_name_filters();
 }
 
 #undef __CHECK_STR
diff --git a/tools/testing/selftests/bpf/progs/veristat_bar.c b/tools/testing/selftests/bpf/progs/veristat_bar.c
new file mode 100644
index 0000000000000..83d2a2a1dfc96
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/veristat_bar.c
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include "veristat_foo.c"
diff --git a/tools/testing/selftests/bpf/progs/veristat_foo.c b/tools/testing/selftests/bpf/progs/veristat_foo.c
new file mode 100644
index 0000000000000..bd24b97664b49
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/veristat_foo.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+/*
+ * Programs below exist only to exercise veristat's -f name filters,
+ * their bodies are irrelevant, only the names matter.
+ * This file is also included by veristat_bar.c, so that the same set of
+ * program names is available in two differently named object files.
+ */
+
+SEC("socket")
+int foo(void *ctx)
+{
+	return 0;
+}
+
+SEC("socket")
+int bar(void *ctx)
+{
+	return 0;
+}
+
+SEC("socket")
+int buz(void *ctx)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
index c9c257784ee3d..6aaa06790daa2 100644
--- a/tools/testing/selftests/bpf/veristat.c
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -514,6 +514,40 @@ static bool is_bpf_obj_file(const char *path) {
 	return err == 0;
 }
 
+/* Exact filter match */
+static bool name_filter_matches(struct filter *f, const char *filename, const char *prog_name)
+{
+	if (f->any_glob)
+		return glob_matches(filename, f->any_glob) ||
+		       (prog_name && glob_matches(prog_name, f->any_glob));
+	if (f->file_glob && f->prog_glob)
+		return prog_name &&
+		       glob_matches(filename, f->file_glob) &&
+		       glob_matches(prog_name, f->prog_glob);
+	if (f->file_glob)
+		return glob_matches(filename, f->file_glob);
+	if (f->prog_glob)
+		return prog_name && glob_matches(prog_name, f->prog_glob);
+	return false;
+}
+
+/* Check if the filter does not outright reject the file name */
+static bool name_filter_may_match(struct filter *f, const char *filename)
+{
+	if (f->file_glob)
+		return glob_matches(filename, f->file_glob);
+	/*
+	 * If we don't know program name yet, any_glob filter
+	 * has to assume that current BPF object file might be
+	 * relevant; we'll check again later on after opening
+	 * BPF object file, at which point program name will
+	 * be known finally.
+	 */
+	if (f->any_glob || f->prog_glob)
+		return true;
+	return false;
+}
+
 static bool should_process_file_prog(const char *filename, const char *prog_name)
 {
 	struct filter *f;
@@ -521,16 +555,7 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
 
 	for (i = 0; i < env.deny_filter_cnt; i++) {
 		f = &env.deny_filters[i];
-		if (f->kind != FILTER_NAME)
-			continue;
-
-		if (f->any_glob && glob_matches(filename, f->any_glob))
-			return false;
-		if (f->any_glob && prog_name && glob_matches(prog_name, f->any_glob))
-			return false;
-		if (f->file_glob && glob_matches(filename, f->file_glob))
-			return false;
-		if (f->prog_glob && prog_name && glob_matches(prog_name, f->prog_glob))
+		if (f->kind == FILTER_NAME && name_filter_matches(f, filename, prog_name))
 			return false;
 	}
 
@@ -540,24 +565,15 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
 			continue;
 
 		allow_cnt++;
-		if (f->any_glob) {
-			if (glob_matches(filename, f->any_glob))
-				return true;
-			/* If we don't know program name yet, any_glob filter
-			 * has to assume that current BPF object file might be
-			 * relevant; we'll check again later on after opening
-			 * BPF object file, at which point program name will
-			 * be known finally.
-			 */
-			if (!prog_name || glob_matches(prog_name, f->any_glob))
-				return true;
-		} else {
-			if (f->file_glob && !glob_matches(filename, f->file_glob))
-				continue;
-			if (f->prog_glob && prog_name && !glob_matches(prog_name, f->prog_glob))
-				continue;
+		if (prog_name && name_filter_matches(f, filename, prog_name))
+			return true;
+		/*
+		 * If there is no prog_name and the file name is not blocked by
+		 * the filter, allow to open the file. Afterwards there would be
+		 * a second refining query with prog_name set.
+		 */
+		if (!prog_name && name_filter_may_match(f, filename))
 			return true;
-		}
 	}
 
 	/* if there are no file/prog name allow filters, allow all progs,
@@ -703,6 +719,12 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
 		}
 	}
 
+	if ((!f->any_glob && !f->file_glob && !f->prog_glob) ||
+	    (f->any_glob && strcmp(f->any_glob, "") == 0)) {
+		fprintf(stderr, "Invalid filter: '%s'\n", str);
+		return -EINVAL;
+	}
+
 	*cnt += 1;
 	return 0;
 }

3/2 2026/08/12 06:09 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6528
							output: 63
							thoughts: 345
						

Thoughts:
**Fuzzing Decision for This Patch Series**

Okay, let's break this down. My initial assessment is that this patch series doesn't necessitate fuzzing. My reasoning is straightforward: the changes are contained within the `tools/testing/selftests/bpf/` directory. Specifically, it touches `test_veristat.c`, `veristat_bar.c`, `veristat_foo.c`, and `veristat.c`. All indications are that these are *purely* selftests and the `veristat` tool itself. There's nothing here that impacts the core kernel, any drivers, or architecture-specific code. Therefore, from a functional perspective, there's nothing to fuzz.

Just to be absolutely certain, I'll quickly re-confirm the file list: `tools/testing/selftests/bpf/prog_tests/test_veristat.c`, `tools/testing/selftests/bpf/progs/veristat_bar.c`, `tools/testing/selftests/bpf/progs/veristat_foo.c`, and `tools/testing/selftests/bpf/veristat.c`. Yep, all in the test suite and user-space tooling. No kernel code is being modified here. I'm confident in calling this `WorthFuzzing=false`.



4/2 2026/08/12 06:09 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space BPF selftests and the veristat tool. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies user-space BPF selftests and the veristat tool. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

5/1 2026/08/12 06:09 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)