AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/10 22:52 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series modifies documentation (YAML specs in Documentation/netlink/specs/), user-space tooling/linters (tools/net/ynl/), and an include guard in include/uapi/linux/snmp.h. No executable kernel code or data structures are modified.",
  "WorthFuzzing": false
}

1/1 2026/09/10 22:52 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 2d10be2f0039de1958b2b55bb02a45a1225aa1fc\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Sep 10 22:52:26 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml\nindex b1eb102ab8432..543dcddb04647 100644\n--- a/Documentation/netlink/specs/conntrack.yaml\n+++ b/Documentation/netlink/specs/conntrack.yaml\n@@ -2,6 +2,7 @@\n ---\n name: conntrack\n protocol: netlink-raw\n+uapi-header: linux/netfilter/nf_conntrack_common.h\n protonum: 12\n \n doc: \u003e-\ndiff --git a/Documentation/netlink/specs/nftables.yaml b/Documentation/netlink/specs/nftables.yaml\nindex 21edf3d25f34b..76f69ed82a24d 100644\n--- a/Documentation/netlink/specs/nftables.yaml\n+++ b/Documentation/netlink/specs/nftables.yaml\n@@ -2,6 +2,7 @@\n ---\n name: nftables\n protocol: netlink-raw\n+uapi-header: linux/netfilter/nf_tables.h\n protonum: 12\n \n doc: \u003e-\ndiff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml\nindex 61ebb9a2bad51..fe52d508d6075 100644\n--- a/Documentation/netlink/specs/rt-link.yaml\n+++ b/Documentation/netlink/specs/rt-link.yaml\n@@ -25,17 +25,17 @@ definitions:\n       -\n         name: loopback\n       -\n-        name: point-to-point\n+        name: pointopoint\n       -\n-        name: no-trailers\n+        name: notrailers\n       -\n         name: running\n       -\n-        name: no-arp\n+        name: noarp\n       -\n         name: promisc\n       -\n-        name: all-multi\n+        name: allmulti\n       -\n         name: master\n       -\n@@ -45,7 +45,7 @@ definitions:\n       -\n         name: portsel\n       -\n-        name: auto-media\n+        name: automedia\n       -\n         name: dynamic\n       -\n@@ -58,6 +58,7 @@ definitions:\n     name: vlan-protocols\n     type: enum\n     enum-name:\n+    name-prefix: eth-p-\n     entries:\n       -\n         name: 8021q\n@@ -305,10 +306,13 @@ definitions:\n   -\n     name: ipv4-devconf\n     enum-name:\n+    name-prefix: ipv4-devconf-\n+    header: linux/ip.h\n     type: enum\n     entries:\n       -\n         name: forwarding\n+        value: 1\n       -\n         name: mc-forwarding\n       -\n@@ -376,6 +380,8 @@ definitions:\n   -\n     name: ipv6-devconf\n     enum-name:\n+    name-prefix: devconf-\n+    header: linux/ipv6.h\n     type: enum\n     entries:\n       -\n@@ -501,6 +507,8 @@ definitions:\n   -\n     name: ifla-icmp6-stats\n     enum-name:\n+    name-prefix: icmp6-mib-\n+    header: linux/snmp.h\n     type: enum\n     entries:\n       -\n@@ -520,6 +528,8 @@ definitions:\n   -\n     name: ifla-inet6-stats\n     enum-name:\n+    name-prefix: ipstats-mib-\n+    header: linux/snmp.h\n     type: enum\n     entries:\n       -\n@@ -531,11 +541,21 @@ definitions:\n       -\n         name: indelivers\n       -\n-        name: outforwdatagrams\n+        name: noectpkts\n+      -\n+        name: ect1pkts\n+      -\n+        name: ect0pkts\n+      -\n+        name: cepkts\n+      -\n+        name: outrequests\n       -\n         name: outpkts\n       -\n         name: outoctets\n+      -\n+        name: outforwdatagrams\n       -\n         name: inhdrerrors\n       -\n@@ -586,14 +606,6 @@ definitions:\n         name: outbcastoctets\n       -\n         name: csumerrors\n-      -\n-        name: noectpkts\n-      -\n-        name: ect1-pkts\n-      -\n-        name: ect0-pkts\n-      -\n-        name: cepkts\n       -\n         name: reasm-overlaps\n   - name: br-boolopt-multi\n@@ -640,6 +652,8 @@ definitions:\n     name: vlan-flags\n     type: flags\n     enum-name:\n+    name-prefix: vlan-flag-\n+    header: linux/if_vlan.h\n     entries:\n       - reorder-hdr\n       - gvrp\n@@ -727,6 +741,8 @@ definitions:\n     name: ifla-vf-link-state-enum\n     type: enum\n     enum-name:\n+    name-prefix: ifla-vf-link-state-\n+    header: linux/if_link.h\n     entries:\n       - auto\n       - enable\n@@ -794,6 +810,7 @@ definitions:\n     name: rtext-filter\n     type: flags\n     enum-name:\n+    name-prefix: rtext-filter-\n     entries:\n       - vf\n       - brvlan\n@@ -806,18 +823,20 @@ definitions:\n   -\n     name: netkit-policy\n     type: enum\n-    enum-name:\n+    enum-name: netkit-action\n+    name-prefix: netkit-\n     entries:\n       -\n-        name: forward\n+        name: pass\n         value: 0\n       -\n-        name: blackhole\n+        name: drop\n         value: 2\n   -\n     name: netkit-mode\n     type: enum\n     enum-name: netkit-mode\n+    name-prefix: netkit-\n     entries:\n       - name: l2\n       - name: l3\n@@ -825,7 +844,8 @@ definitions:\n   -\n     name: netkit-scrub\n     type: enum\n-    enum-name:\n+    enum-name: netkit-scrub\n+    name-prefix: netkit-scrub-\n     entries:\n       - name: none\n       - name: default\n@@ -833,13 +853,14 @@ definitions:\n     name: netkit-pairing\n     type: enum\n     enum-name: netkit-pairing\n+    name-prefix: netkit-device-\n     entries:\n       - name: pair\n       - name: single\n   -\n     name: ovpn-mode\n     enum-name: ovpn-mode\n-    name-prefix: ovpn-mode\n+    name-prefix: ovpn-mode-\n     type: enum\n     entries:\n       - p2p\n@@ -848,6 +869,7 @@ definitions:\n     name: br-stp-mode\n     type: enum\n     enum-name: br-stp-mode\n+    name-prefix: br-stp-mode-\n     entries:\n       - auto\n       - user\ndiff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h\nindex 49f5640092a0d..26bdabc502fea 100644\n--- a/include/uapi/linux/snmp.h\n+++ b/include/uapi/linux/snmp.h\n@@ -5,8 +5,8 @@\n  * Author: Hideaki YOSHIFUJI \u003cyoshfuji@linux-ipv6.org\u003e\n  */\n \n-#ifndef _LINUX_SNMP_H\n-#define _LINUX_SNMP_H\n+#ifndef _UAPI_LINUX_SNMP_H\n+#define _UAPI_LINUX_SNMP_H\n \n /* ipstats mib definitions */\n /*\n@@ -372,4 +372,4 @@ enum\n \t__LINUX_MIB_TLSMAX\n };\n \n-#endif\t/* _LINUX_SNMP_H */\n+#endif\t/* _UAPI_LINUX_SNMP_H */\ndiff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps\nindex 2771375339d9c..8e136a5df8991 100644\n--- a/tools/net/ynl/Makefile.deps\n+++ b/tools/net/ynl/Makefile.deps\n@@ -40,8 +40,15 @@ CFLAGS_ovs_vport:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)\n CFLAGS_psp:=$(call get_hdr_inc,_LINUX_PSP_H,psp.h)\n CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \\\n \t$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)\n-CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \\\n-\t$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)\n+CFLAGS_rt-link:=$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h) \\\n+\t$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \\\n+\t$(call get_hdr_inc,_LINUX_IP_H,ip.h) \\\n+\t$(call get_hdr_inc,_IPV6_H,ipv6.h) \\\n+\t$(call get_hdr_inc,_LINUX_IF_BRIDGE_H,if_bridge.h) \\\n+\t$(call get_hdr_inc,_IF_TUNNEL_H_,if_tunnel.h) \\\n+\t$(call get_hdr_inc,_LINUX_IF_VLAN_H_,if_vlan.h) \\\n+\t$(call get_hdr_inc,_LINUX_SNMP_H,snmp.h) \\\n+\t$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h)\n CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \\\n \t$(call get_hdr_inc,__LINUX_NEIGHBOUR_H,neighbour.h)\n CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)\ndiff --git a/tools/net/ynl/generated/.gitignore b/tools/net/ynl/generated/.gitignore\nindex 859a6fb446e14..20dd70cd709ee 100644\n--- a/tools/net/ynl/generated/.gitignore\n+++ b/tools/net/ynl/generated/.gitignore\n@@ -1,3 +1,6 @@\n *-user.c\n *-user.h\n+*-linter.c\n+*-linter.h\n+*-linter\n *.rst\ndiff --git a/tools/net/ynl/generated/Makefile b/tools/net/ynl/generated/Makefile\nindex 5a186349b5a86..bcf7f45563130 100644\n--- a/tools/net/ynl/generated/Makefile\n+++ b/tools/net/ynl/generated/Makefile\n@@ -33,8 +33,9 @@ OBJS=$(patsubst %,%-user.o,${GENS})\n SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)\n SPECS=$(patsubst $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS})\n RSTS=$(patsubst %,%.rst,${SPECS})\n+LINTS=$(patsubst %,%-lint,${SPECS})\n \n-all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) $(RSTS)\n+all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) $(RSTS) $(LINTS)\n \n protos.a: $(OBJS)\n \t@echo -e \"\\tAR $@\"\n@@ -56,11 +57,29 @@ protos.a: $(OBJS)\n \t@echo -e \"\\tGEN_RST $@\"\n \t@$(TOOL_RST) -o $@ -i $\u003c\n \n+%-linter.c: $(SPECS_DIR)/%.yaml $(TOOL) ../linter/linter.h\n+\t@echo -e \"\\tGEN $@\"\n+\t@$(TOOL) --mode user --linter --spec $\u003c -o $@ $(YNL_GEN_ARG_$*)\n+\n+%-linter.h: %-linter.c ../linter/gen-h.sh\n+\t@echo -e \"\\tGEN $@\"\n+\t@../linter/gen-h.sh $(CFLAGS) $(CFLAGS_$*) \u003c $\u003c \u003e $@\n+\n+%-linter: %-linter.c %-linter.h ../linter/linter.h\n+\t@echo -e \"\\tCC $@\"\n+\t@$(CC) $(CFLAGS) $(CFLAGS_$*) -include $*-linter.h -o $@ $\u003c\n+\n+%-lint: %-linter\n+\t@echo -e \"\\tLINT $@\"\n+\t@./$\u003c $*\n+\n+lint: $(LINTS)\n+\n clean:\n \trm -f *.o\n \n distclean: clean\n-\trm -f *.c *.h *.a *.rst\n+\trm -f *.c *.h *.a *.rst *-linter\n \n regen:\n \t@../ynl-regen.sh\n@@ -68,7 +87,7 @@ regen:\n install-headers: $(HDRS)\n \t@echo -e \"\\tINSTALL generated headers\"\n \t@$(INSTALL) -d $(DESTDIR)$(includedir)/ynl\n-\t@$(INSTALL) -m 0644 *.h $(DESTDIR)$(includedir)/ynl/\n+\t@$(INSTALL) -m 0644 $(KHDRS) $(DESTDIR)$(includedir)/ynl/\n \n install-rsts: $(RSTS)\n \t@echo -e \"\\tINSTALL generated docs\"\n@@ -84,5 +103,5 @@ install-specs:\n \n install: install-headers install-rsts install-specs\n \n-.PHONY: all clean distclean regen install install-headers install-rsts install-specs\n+.PHONY: all clean distclean regen install install-headers install-rsts install-specs lint\n .DEFAULT_GOAL: all\ndiff --git a/tools/net/ynl/linter/gen-h.sh b/tools/net/ynl/linter/gen-h.sh\nnew file mode 100755\nindex 0000000000000..247493600034d\n--- /dev/null\n+++ b/tools/net/ynl/linter/gen-h.sh\n@@ -0,0 +1,23 @@\n+#!/bin/sh\n+# SPDX-License-Identifier: GPL-2.0\n+\n+# Usage: ./gen-h.sh $CFLAGS \u003c in.c \u003e out.h\n+#\n+# Extract valid symbols, and define that they are available. This is a\n+# workaround, to avoid compile failures due to non-existent enum members.\n+#\n+# Capitalized keywords found in the preprocessor output, are mostly enum\n+# members.\n+#\n+# As an example, existence of FOO can be checked with:\n+#   #if defined(FOO) || defined(LINTER_HAS_FOO)\n+# thus checking for FOO either as a macro or as an enum members.\n+\n+set -e\n+\n+echo '/* This is an auto-generated file */'\n+grep '^#include \u003clinux/' |\n+\tcpp -x c \"$@\" - |\n+\tgrep -wo '[A-Z][A-Z0-9_]\\+' |\n+\tsort | uniq |\n+\tsed -e 's/^/#define LINTER_HAS_/g'\ndiff --git a/tools/net/ynl/linter/linter.h b/tools/net/ynl/linter/linter.h\nnew file mode 100644\nindex 0000000000000..0d1c28c9f2140\n--- /dev/null\n+++ b/tools/net/ynl/linter/linter.h\n@@ -0,0 +1,152 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+\n+#include \u003cstdio.h\u003e\n+#include \u003cstdbool.h\u003e\n+\n+struct enum_kv {\n+\tconst char *name;\n+\tconst long long val;\n+\tconst bool is_undef:1;\n+};\n+\n+struct enum_entry {\n+\tconst struct enum_kv ynl;\n+\tconst struct enum_kv c;\n+\tconst bool is_sentinal:1;\n+};\n+\n+#define YNL_ENUM_ENTRY(YNL_NAME, C_NAME, YNL_VALUE) \\\n+\t{ \\\n+\t\t.ynl = { .name = YNL_NAME, .val = YNL_VALUE }, \\\n+\t\t.c = { .name = #C_NAME, .val = C_NAME }, \\\n+\t}\n+\n+#define YNL_ENUM_BAD_ENTRY(YNL_NAME, C_NAME, YNL_VALUE) \\\n+\t{ \\\n+\t\t.ynl = { .name = YNL_NAME, .val = YNL_VALUE }, \\\n+\t\t.c = { .name = #C_NAME, .is_undef = true }, \\\n+\t}\n+\n+#define YNL_ENUM_SENTINAL(C_NAME, YNL_VALUE) \\\n+\t{ \\\n+\t\t.ynl = { .name = \"MAX\", .val = YNL_VALUE }, \\\n+\t\t.c = { .name = #C_NAME, .val = C_NAME }, \\\n+\t\t.is_sentinal = true, \\\n+\t}\n+\n+enum ynl_enum_type {\n+\tYNL_ENUM,\n+\tYNL_FLAGS,\n+};\n+\n+struct enum_set {\n+\tconst char *name;\n+\tconst struct enum_entry *entry;\n+\tenum ynl_enum_type type;\n+};\n+\n+struct linter_ctx {\n+\tint errors;\n+\tint warnings;\n+\tconst char *name;\n+};\n+\n+#define errf(fmt, ...) \\\n+\tdo { \\\n+\t\tfprintf(stderr, \"%s: ERROR: \" fmt, ctx-\u003ename, __VA_ARGS__); \\\n+\t\tctx-\u003eerrors++; \\\n+\t} while (0)\n+\n+#define warnf(fmt, ...) \\\n+\tdo { \\\n+\t\tfprintf(stderr, \"%s: WARN: \" fmt, ctx-\u003ename, __VA_ARGS__); \\\n+\t\tctx-\u003ewarnings++; \\\n+\t} while (0)\n+\n+static inline long long find_next_value(const struct enum_set *es,\n+\t\t\t\t\tconst long long last_val)\n+{\n+\tswitch (es-\u003etype) {\n+\tcase YNL_ENUM:\n+\t\treturn last_val + 1;\n+\tcase YNL_FLAGS:\n+\t\treturn last_val \u003c\u003c 1;\n+\tdefault:\n+\t\t/* unreachable */\n+\t\tabort();\n+\t}\n+}\n+\n+static inline void lint_enum_entry(struct linter_ctx *ctx,\n+\t\t\t\t   const struct enum_set *es,\n+\t\t\t\t   const struct enum_entry *entry,\n+\t\t\t\t   long long *last_val, const int i,\n+\t\t\t\t   const int cnt)\n+{\n+\tconst long long val = entry-\u003ec.val;\n+\n+\tif (i \u003e 0 \u0026\u0026 val != *last_val \u0026\u0026 val != find_next_value(es, *last_val))\n+\t\twarnf(\"%s: Possible missing member before %s (%lld -\u003e %lld)\\n\",\n+\t\t      es-\u003ename, entry-\u003ec.name, *last_val, val);\n+\t*last_val = entry-\u003ec.val;\n+\n+\tif (i == cnt - 1 \u0026\u0026 strcmp(entry-\u003eynl.name, \"max\") == 0)\n+\t\terrf(\"%s: Sentinal used in YNL spec\\n\", es-\u003ename);\n+\n+\tif (entry-\u003ec.is_undef) {\n+\t\terrf(\"%s: %s: %s not found\\n\", es-\u003ename, entry-\u003eynl.name,\n+\t\t     entry-\u003ec.name);\n+\t\treturn;\n+\t}\n+\n+\tif (entry-\u003eynl.val == entry-\u003ec.val)\n+\t\treturn;\n+\n+\tif (entry-\u003eis_sentinal) {\n+\t\tif (es-\u003etype == YNL_ENUM \u0026\u0026 entry-\u003eynl.val + 1 == entry-\u003ec.val)\n+\t\t\treturn; /* eg. DEVCONF_MAX is the storage size */\n+\t\twarnf(\"%s: Sentinal mismatch: %lld != %lld (Last YNL != %s)\\n\",\n+\t\t      es-\u003ename, entry-\u003eynl.val, entry-\u003ec.val, entry-\u003ec.name);\n+\t} else {\n+\t\terrf(\"%s: Value mismatch: %lld != %lld (%s != %s)\\n\", es-\u003ename,\n+\t\t     entry-\u003eynl.val, entry-\u003ec.val, entry-\u003eynl.name,\n+\t\t     entry-\u003ec.name);\n+\t}\n+}\n+\n+static inline void lint_enum(struct linter_ctx *ctx, const struct enum_set *es)\n+{\n+\tconst struct enum_entry *entry = es-\u003eentry;\n+\tlong long last_val;\n+\tint cnt = 0;\n+\tint i = 0;\n+\n+\twhile (entry-\u003eynl.name) {\n+\t\tif (!entry-\u003eis_sentinal)\n+\t\t\tcnt++;\n+\t\tentry++;\n+\t}\n+\tentry = es-\u003eentry;\n+\n+\twhile (entry-\u003eynl.name)\n+\t\tlint_enum_entry(ctx, es, entry++, \u0026last_val, i++, cnt);\n+}\n+\n+static inline int linter_run(const int argc, const char **argv,\n+\t\t\t     const struct enum_set *es)\n+{\n+\tstruct linter_ctx ctx = {\n+\t\t.name = argv[argc \u003e 1 ? 1 : 0],\n+\t};\n+\n+\twhile (es-\u003ename) {\n+\t\tlint_enum(\u0026ctx, es);\n+\t\tes++;\n+\t}\n+\n+\tif (ctx.errors || ctx.warnings)\n+\t\tfprintf(stderr, \"%s: Linter summary: %d errors, %d warnings\\n\",\n+\t\t\tctx.name, ctx.errors, ctx.warnings);\n+\n+\treturn EXIT_SUCCESS;\n+}\ndiff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py\nindex 2b3483db1b60c..5bbe3cba8f759 100755\n--- a/tools/net/ynl/pyynl/ynl_gen_c.py\n+++ b/tools/net/ynl/pyynl/ynl_gen_c.py\n@@ -1688,6 +1688,8 @@ class CodeWriter:\n         self.close_out_file()\n \n     def close_out_file(self):\n+        if self._block_end:\n+            self._out.write('\\t' * self._ind + '}\\n')\n         if self._out == os.sys.stdout:\n             return\n         # Avoid modifying the file if contents didn't change\n@@ -2084,6 +2086,30 @@ def put_enum_to_str(_family, cw, enum):\n     _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)\n \n \n+def put_enum_to_linter(family, cw, enum):\n+    name_pfx = enum.get('name-prefix', f\"{family.ident_name}-{enum['name']}-\")\n+    max_name = c_upper(name_pfx + 'max')\n+    map_name = f'{enum.render_name}_entries'\n+    cw.block_start(line=f\"static const struct enum_entry {map_name}[] =\")\n+    val = 0\n+    for entry in enum.entries.values():\n+        val = entry.user_value()\n+        c_name = entry.c_name\n+        cw.p(f'#if defined({c_name}) || defined(LINTER_HAS_{c_name})')\n+        cw.p(f'YNL_ENUM_ENTRY(\"{entry.name}\", {c_name}, {val}),')\n+        cw.p('#else')\n+        cw.p(f'YNL_ENUM_BAD_ENTRY(\"{entry.name}\", {c_name}, {val}),')\n+        cw.p('#endif')\n+    cw.p(f'#if defined({max_name}) || defined(LINTER_HAS_{max_name})')\n+    cw.p(f'YNL_ENUM_SENTINAL({max_name}, {val}),')\n+    cw.p('#endif')\n+    cw.p('{},')\n+    cw.block_end(line=';')\n+    cw.nl()\n+    enum_type = enum['type'].upper()\n+    return f'{{\"{enum.name}\", \u0026{map_name}[0], YNL_{enum_type}}},'\n+\n+\n def put_local_vars(struct):\n     local_vars = []\n     has_array = False\n@@ -3462,6 +3488,7 @@ def main():\n     parser.add_argument('--spec', dest='spec', type=str, required=True)\n     parser.add_argument('--header', dest='header', action='store_true', default=None)\n     parser.add_argument('--source', dest='header', action='store_false')\n+    parser.add_argument('--linter', dest='linter', action='store_true')\n     parser.add_argument('--user-header', nargs='+', default=[])\n     parser.add_argument('--cmp-out', action='store_true', default=None,\n                         help='Do not overwrite the output file if the new output is identical to the old')\n@@ -3470,8 +3497,10 @@ def main():\n     parser.add_argument('--function-prefix', dest='fn_prefix', type=str)\n     args = parser.parse_args()\n \n+    if args.linter:\n+        args.header = False\n     if args.header is None:\n-        parser.error(\"--header or --source is required\")\n+        parser.error(\"--header, --source or --linter is required\")\n \n     exclude_ops = [re.compile(expr) for expr in args.exclude_op]\n \n@@ -3494,17 +3523,19 @@ def main():\n         cw.p(f'// SPDX-License-Identifier: {parsed.license}')\n     cw.p(\"/* Do not edit directly, auto-generated from: */\")\n     cw.p(f\"/*\\t{spec_kernel} */\")\n-    cw.p(f\"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */\")\n-    if args.exclude_op or args.user_header or args.fn_prefix:\n-        line = ''\n-        if args.user_header:\n-            line += ' --user-header '.join([''] + args.user_header)\n-        if args.exclude_op:\n-            line += ' --exclude-op '.join([''] + args.exclude_op)\n-        if args.fn_prefix:\n-            line += f' --function-prefix {args.fn_prefix}'\n-        cw.p(f'/* YNL-ARG{line} */')\n-    cw.p('/* To regenerate run: tools/net/ynl/ynl-regen.sh */')\n+    if not args.linter:\n+        ynl_arg = 'header' if args.header else 'source'\n+        cw.p(f\"/* YNL-GEN {args.mode} {ynl_arg} */\")\n+        if args.exclude_op or args.user_header or args.fn_prefix:\n+            line = ''\n+            if args.user_header:\n+                line += ' --user-header '.join([''] + args.user_header)\n+            if args.exclude_op:\n+                line += ' --exclude-op '.join([''] + args.exclude_op)\n+            if args.fn_prefix:\n+                line += f' --function-prefix {args.fn_prefix}'\n+            cw.p(f'/* YNL-ARG{line} */')\n+        cw.p('/* To regenerate run: tools/net/ynl/ynl-regen.sh */')\n     cw.nl()\n \n     if args.mode == 'uapi':\n@@ -3539,6 +3570,8 @@ def main():\n             cw.p('#include \u003clinux/types.h\u003e')\n             if family_contains_bitfield32(parsed):\n                 cw.p('#include \u003clinux/netlink.h\u003e')\n+        elif args.linter:\n+            cw.p('#include \"../linter/linter.h\"')\n         else:\n             cw.p(f'#include \"{hdr_file}\"')\n             cw.p('#include \"ynl.h\"')\n@@ -3690,6 +3723,21 @@ def main():\n                     cw.nl()\n                     print_wrapped_type(ri)\n             cw.nl()\n+        elif args.linter:\n+            enum_sets = []\n+            for name, const in parsed.consts.items():\n+                if isinstance(const, EnumSet):\n+                    enum_sets.append(put_enum_to_linter(parsed, cw, const))\n+            cw.nl()\n+            cw.block_start(line=\"static const struct enum_set enums[] =\")\n+            for enum_set in enum_sets:\n+                cw.p(enum_set)\n+            cw.p('{},')\n+            cw.block_end(line=';')\n+            cw.nl()\n+            cw.write_func('int', 'main',\n+                          args=['const int argc', 'const char **argv'],\n+                          body=['return linter_run(argc, argv, \u0026enums[0]);'])\n         else:\n             cw.p('/* Enums */')\n             put_op_name(parsed, cw)\n"
}

2/1 2026/09/10 22:52 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies documentation (YAML specs in Documentation/netlink/specs/), user-space tooling/linters (tools/net/ynl/), and an include guard in include/uapi/linux/snmp.h. No executable kernel code or data structures are modified.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 2d10be2f0039de1958b2b55bb02a45a1225aa1fc
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Sep 10 22:52:26 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml
index b1eb102ab8432..543dcddb04647 100644
--- a/Documentation/netlink/specs/conntrack.yaml
+++ b/Documentation/netlink/specs/conntrack.yaml
@@ -2,6 +2,7 @@
 ---
 name: conntrack
 protocol: netlink-raw
+uapi-header: linux/netfilter/nf_conntrack_common.h
 protonum: 12
 
 doc: >-
diff --git a/Documentation/netlink/specs/nftables.yaml b/Documentation/netlink/specs/nftables.yaml
index 21edf3d25f34b..76f69ed82a24d 100644
--- a/Documentation/netlink/specs/nftables.yaml
+++ b/Documentation/netlink/specs/nftables.yaml
@@ -2,6 +2,7 @@
 ---
 name: nftables
 protocol: netlink-raw
+uapi-header: linux/netfilter/nf_tables.h
 protonum: 12
 
 doc: >-
diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index 61ebb9a2bad51..fe52d508d6075 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -25,17 +25,17 @@ definitions:
       -
         name: loopback
       -
-        name: point-to-point
+        name: pointopoint
       -
-        name: no-trailers
+        name: notrailers
       -
         name: running
       -
-        name: no-arp
+        name: noarp
       -
         name: promisc
       -
-        name: all-multi
+        name: allmulti
       -
         name: master
       -
@@ -45,7 +45,7 @@ definitions:
       -
         name: portsel
       -
-        name: auto-media
+        name: automedia
       -
         name: dynamic
       -
@@ -58,6 +58,7 @@ definitions:
     name: vlan-protocols
     type: enum
     enum-name:
+    name-prefix: eth-p-
     entries:
       -
         name: 8021q
@@ -305,10 +306,13 @@ definitions:
   -
     name: ipv4-devconf
     enum-name:
+    name-prefix: ipv4-devconf-
+    header: linux/ip.h
     type: enum
     entries:
       -
         name: forwarding
+        value: 1
       -
         name: mc-forwarding
       -
@@ -376,6 +380,8 @@ definitions:
   -
     name: ipv6-devconf
     enum-name:
+    name-prefix: devconf-
+    header: linux/ipv6.h
     type: enum
     entries:
       -
@@ -501,6 +507,8 @@ definitions:
   -
     name: ifla-icmp6-stats
     enum-name:
+    name-prefix: icmp6-mib-
+    header: linux/snmp.h
     type: enum
     entries:
       -
@@ -520,6 +528,8 @@ definitions:
   -
     name: ifla-inet6-stats
     enum-name:
+    name-prefix: ipstats-mib-
+    header: linux/snmp.h
     type: enum
     entries:
       -
@@ -531,11 +541,21 @@ definitions:
       -
         name: indelivers
       -
-        name: outforwdatagrams
+        name: noectpkts
+      -
+        name: ect1pkts
+      -
+        name: ect0pkts
+      -
+        name: cepkts
+      -
+        name: outrequests
       -
         name: outpkts
       -
         name: outoctets
+      -
+        name: outforwdatagrams
       -
         name: inhdrerrors
       -
@@ -586,14 +606,6 @@ definitions:
         name: outbcastoctets
       -
         name: csumerrors
-      -
-        name: noectpkts
-      -
-        name: ect1-pkts
-      -
-        name: ect0-pkts
-      -
-        name: cepkts
       -
         name: reasm-overlaps
   - name: br-boolopt-multi
@@ -640,6 +652,8 @@ definitions:
     name: vlan-flags
     type: flags
     enum-name:
+    name-prefix: vlan-flag-
+    header: linux/if_vlan.h
     entries:
       - reorder-hdr
       - gvrp
@@ -727,6 +741,8 @@ definitions:
     name: ifla-vf-link-state-enum
     type: enum
     enum-name:
+    name-prefix: ifla-vf-link-state-
+    header: linux/if_link.h
     entries:
       - auto
       - enable
@@ -794,6 +810,7 @@ definitions:
     name: rtext-filter
     type: flags
     enum-name:
+    name-prefix: rtext-filter-
     entries:
       - vf
       - brvlan
@@ -806,18 +823,20 @@ definitions:
   -
     name: netkit-policy
     type: enum
-    enum-name:
+    enum-name: netkit-action
+    name-prefix: netkit-
     entries:
       -
-        name: forward
+        name: pass
         value: 0
       -
-        name: blackhole
+        name: drop
         value: 2
   -
     name: netkit-mode
     type: enum
     enum-name: netkit-mode
+    name-prefix: netkit-
     entries:
       - name: l2
       - name: l3
@@ -825,7 +844,8 @@ definitions:
   -
     name: netkit-scrub
     type: enum
-    enum-name:
+    enum-name: netkit-scrub
+    name-prefix: netkit-scrub-
     entries:
       - name: none
       - name: default
@@ -833,13 +853,14 @@ definitions:
     name: netkit-pairing
     type: enum
     enum-name: netkit-pairing
+    name-prefix: netkit-device-
     entries:
       - name: pair
       - name: single
   -
     name: ovpn-mode
     enum-name: ovpn-mode
-    name-prefix: ovpn-mode
+    name-prefix: ovpn-mode-
     type: enum
     entries:
       - p2p
@@ -848,6 +869,7 @@ definitions:
     name: br-stp-mode
     type: enum
     enum-name: br-stp-mode
+    name-prefix: br-stp-mode-
     entries:
       - auto
       - user
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index 49f5640092a0d..26bdabc502fea 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -5,8 +5,8 @@
  * Author: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
  */
 
-#ifndef _LINUX_SNMP_H
-#define _LINUX_SNMP_H
+#ifndef _UAPI_LINUX_SNMP_H
+#define _UAPI_LINUX_SNMP_H
 
 /* ipstats mib definitions */
 /*
@@ -372,4 +372,4 @@ enum
 	__LINUX_MIB_TLSMAX
 };
 
-#endif	/* _LINUX_SNMP_H */
+#endif	/* _UAPI_LINUX_SNMP_H */
diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps
index 2771375339d9c..8e136a5df8991 100644
--- a/tools/net/ynl/Makefile.deps
+++ b/tools/net/ynl/Makefile.deps
@@ -40,8 +40,15 @@ CFLAGS_ovs_vport:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
 CFLAGS_psp:=$(call get_hdr_inc,_LINUX_PSP_H,psp.h)
 CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)
-CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
-	$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
+CFLAGS_rt-link:=$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h) \
+	$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
+	$(call get_hdr_inc,_LINUX_IP_H,ip.h) \
+	$(call get_hdr_inc,_IPV6_H,ipv6.h) \
+	$(call get_hdr_inc,_LINUX_IF_BRIDGE_H,if_bridge.h) \
+	$(call get_hdr_inc,_IF_TUNNEL_H_,if_tunnel.h) \
+	$(call get_hdr_inc,_LINUX_IF_VLAN_H_,if_vlan.h) \
+	$(call get_hdr_inc,_LINUX_SNMP_H,snmp.h) \
+	$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h)
 CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
 	$(call get_hdr_inc,__LINUX_NEIGHBOUR_H,neighbour.h)
 CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
diff --git a/tools/net/ynl/generated/.gitignore b/tools/net/ynl/generated/.gitignore
index 859a6fb446e14..20dd70cd709ee 100644
--- a/tools/net/ynl/generated/.gitignore
+++ b/tools/net/ynl/generated/.gitignore
@@ -1,3 +1,6 @@
 *-user.c
 *-user.h
+*-linter.c
+*-linter.h
+*-linter
 *.rst
diff --git a/tools/net/ynl/generated/Makefile b/tools/net/ynl/generated/Makefile
index 5a186349b5a86..bcf7f45563130 100644
--- a/tools/net/ynl/generated/Makefile
+++ b/tools/net/ynl/generated/Makefile
@@ -33,8 +33,9 @@ OBJS=$(patsubst %,%-user.o,${GENS})
 SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
 SPECS=$(patsubst $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS})
 RSTS=$(patsubst %,%.rst,${SPECS})
+LINTS=$(patsubst %,%-lint,${SPECS})
 
-all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) $(RSTS)
+all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) $(RSTS) $(LINTS)
 
 protos.a: $(OBJS)
 	@echo -e "\tAR $@"
@@ -56,11 +57,29 @@ protos.a: $(OBJS)
 	@echo -e "\tGEN_RST $@"
 	@$(TOOL_RST) -o $@ -i $<
 
+%-linter.c: $(SPECS_DIR)/%.yaml $(TOOL) ../linter/linter.h
+	@echo -e "\tGEN $@"
+	@$(TOOL) --mode user --linter --spec $< -o $@ $(YNL_GEN_ARG_$*)
+
+%-linter.h: %-linter.c ../linter/gen-h.sh
+	@echo -e "\tGEN $@"
+	@../linter/gen-h.sh $(CFLAGS) $(CFLAGS_$*) < $< > $@
+
+%-linter: %-linter.c %-linter.h ../linter/linter.h
+	@echo -e "\tCC $@"
+	@$(CC) $(CFLAGS) $(CFLAGS_$*) -include $*-linter.h -o $@ $<
+
+%-lint: %-linter
+	@echo -e "\tLINT $@"
+	@./$< $*
+
+lint: $(LINTS)
+
 clean:
 	rm -f *.o
 
 distclean: clean
-	rm -f *.c *.h *.a *.rst
+	rm -f *.c *.h *.a *.rst *-linter
 
 regen:
 	@../ynl-regen.sh
@@ -68,7 +87,7 @@ regen:
 install-headers: $(HDRS)
 	@echo -e "\tINSTALL generated headers"
 	@$(INSTALL) -d $(DESTDIR)$(includedir)/ynl
-	@$(INSTALL) -m 0644 *.h $(DESTDIR)$(includedir)/ynl/
+	@$(INSTALL) -m 0644 $(KHDRS) $(DESTDIR)$(includedir)/ynl/
 
 install-rsts: $(RSTS)
 	@echo -e "\tINSTALL generated docs"
@@ -84,5 +103,5 @@ install-specs:
 
 install: install-headers install-rsts install-specs
 
-.PHONY: all clean distclean regen install install-headers install-rsts install-specs
+.PHONY: all clean distclean regen install install-headers install-rsts install-specs lint
 .DEFAULT_GOAL: all
diff --git a/tools/net/ynl/linter/gen-h.sh b/tools/net/ynl/linter/gen-h.sh
new file mode 100755
index 0000000000000..247493600034d
--- /dev/null
+++ b/tools/net/ynl/linter/gen-h.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+# Usage: ./gen-h.sh $CFLAGS < in.c > out.h
+#
+# Extract valid symbols, and define that they are available. This is a
+# workaround, to avoid compile failures due to non-existent enum members.
+#
+# Capitalized keywords found in the preprocessor output, are mostly enum
+# members.
+#
+# As an example, existence of FOO can be checked with:
+#   #if defined(FOO) || defined(LINTER_HAS_FOO)
+# thus checking for FOO either as a macro or as an enum members.
+
+set -e
+
+echo '/* This is an auto-generated file */'
+grep '^#include <linux/' |
+	cpp -x c "$@" - |
+	grep -wo '[A-Z][A-Z0-9_]\+' |
+	sort | uniq |
+	sed -e 's/^/#define LINTER_HAS_/g'
diff --git a/tools/net/ynl/linter/linter.h b/tools/net/ynl/linter/linter.h
new file mode 100644
index 0000000000000..0d1c28c9f2140
--- /dev/null
+++ b/tools/net/ynl/linter/linter.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <stdio.h>
+#include <stdbool.h>
+
+struct enum_kv {
+	const char *name;
+	const long long val;
+	const bool is_undef:1;
+};
+
+struct enum_entry {
+	const struct enum_kv ynl;
+	const struct enum_kv c;
+	const bool is_sentinal:1;
+};
+
+#define YNL_ENUM_ENTRY(YNL_NAME, C_NAME, YNL_VALUE) \
+	{ \
+		.ynl = { .name = YNL_NAME, .val = YNL_VALUE }, \
+		.c = { .name = #C_NAME, .val = C_NAME }, \
+	}
+
+#define YNL_ENUM_BAD_ENTRY(YNL_NAME, C_NAME, YNL_VALUE) \
+	{ \
+		.ynl = { .name = YNL_NAME, .val = YNL_VALUE }, \
+		.c = { .name = #C_NAME, .is_undef = true }, \
+	}
+
+#define YNL_ENUM_SENTINAL(C_NAME, YNL_VALUE) \
+	{ \
+		.ynl = { .name = "MAX", .val = YNL_VALUE }, \
+		.c = { .name = #C_NAME, .val = C_NAME }, \
+		.is_sentinal = true, \
+	}
+
+enum ynl_enum_type {
+	YNL_ENUM,
+	YNL_FLAGS,
+};
+
+struct enum_set {
+	const char *name;
+	const struct enum_entry *entry;
+	enum ynl_enum_type type;
+};
+
+struct linter_ctx {
+	int errors;
+	int warnings;
+	const char *name;
+};
+
+#define errf(fmt, ...) \
+	do { \
+		fprintf(stderr, "%s: ERROR: " fmt, ctx->name, __VA_ARGS__); \
+		ctx->errors++; \
+	} while (0)
+
+#define warnf(fmt, ...) \
+	do { \
+		fprintf(stderr, "%s: WARN: " fmt, ctx->name, __VA_ARGS__); \
+		ctx->warnings++; \
+	} while (0)
+
+static inline long long find_next_value(const struct enum_set *es,
+					const long long last_val)
+{
+	switch (es->type) {
+	case YNL_ENUM:
+		return last_val + 1;
+	case YNL_FLAGS:
+		return last_val << 1;
+	default:
+		/* unreachable */
+		abort();
+	}
+}
+
+static inline void lint_enum_entry(struct linter_ctx *ctx,
+				   const struct enum_set *es,
+				   const struct enum_entry *entry,
+				   long long *last_val, const int i,
+				   const int cnt)
+{
+	const long long val = entry->c.val;
+
+	if (i > 0 && val != *last_val && val != find_next_value(es, *last_val))
+		warnf("%s: Possible missing member before %s (%lld -> %lld)\n",
+		      es->name, entry->c.name, *last_val, val);
+	*last_val = entry->c.val;
+
+	if (i == cnt - 1 && strcmp(entry->ynl.name, "max") == 0)
+		errf("%s: Sentinal used in YNL spec\n", es->name);
+
+	if (entry->c.is_undef) {
+		errf("%s: %s: %s not found\n", es->name, entry->ynl.name,
+		     entry->c.name);
+		return;
+	}
+
+	if (entry->ynl.val == entry->c.val)
+		return;
+
+	if (entry->is_sentinal) {
+		if (es->type == YNL_ENUM && entry->ynl.val + 1 == entry->c.val)
+			return; /* eg. DEVCONF_MAX is the storage size */
+		warnf("%s: Sentinal mismatch: %lld != %lld (Last YNL != %s)\n",
+		      es->name, entry->ynl.val, entry->c.val, entry->c.name);
+	} else {
+		errf("%s: Value mismatch: %lld != %lld (%s != %s)\n", es->name,
+		     entry->ynl.val, entry->c.val, entry->ynl.name,
+		     entry->c.name);
+	}
+}
+
+static inline void lint_enum(struct linter_ctx *ctx, const struct enum_set *es)
+{
+	const struct enum_entry *entry = es->entry;
+	long long last_val;
+	int cnt = 0;
+	int i = 0;
+
+	while (entry->ynl.name) {
+		if (!entry->is_sentinal)
+			cnt++;
+		entry++;
+	}
+	entry = es->entry;
+
+	while (entry->ynl.name)
+		lint_enum_entry(ctx, es, entry++, &last_val, i++, cnt);
+}
+
+static inline int linter_run(const int argc, const char **argv,
+			     const struct enum_set *es)
+{
+	struct linter_ctx ctx = {
+		.name = argv[argc > 1 ? 1 : 0],
+	};
+
+	while (es->name) {
+		lint_enum(&ctx, es);
+		es++;
+	}
+
+	if (ctx.errors || ctx.warnings)
+		fprintf(stderr, "%s: Linter summary: %d errors, %d warnings\n",
+			ctx.name, ctx.errors, ctx.warnings);
+
+	return EXIT_SUCCESS;
+}
diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 2b3483db1b60c..5bbe3cba8f759 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -1688,6 +1688,8 @@ class CodeWriter:
         self.close_out_file()
 
     def close_out_file(self):
+        if self._block_end:
+            self._out.write('\t' * self._ind + '}\n')
         if self._out == os.sys.stdout:
             return
         # Avoid modifying the file if contents didn't change
@@ -2084,6 +2086,30 @@ def put_enum_to_str(_family, cw, enum):
     _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
 
 
+def put_enum_to_linter(family, cw, enum):
+    name_pfx = enum.get('name-prefix', f"{family.ident_name}-{enum['name']}-")
+    max_name = c_upper(name_pfx + 'max')
+    map_name = f'{enum.render_name}_entries'
+    cw.block_start(line=f"static const struct enum_entry {map_name}[] =")
+    val = 0
+    for entry in enum.entries.values():
+        val = entry.user_value()
+        c_name = entry.c_name
+        cw.p(f'#if defined({c_name}) || defined(LINTER_HAS_{c_name})')
+        cw.p(f'YNL_ENUM_ENTRY("{entry.name}", {c_name}, {val}),')
+        cw.p('#else')
+        cw.p(f'YNL_ENUM_BAD_ENTRY("{entry.name}", {c_name}, {val}),')
+        cw.p('#endif')
+    cw.p(f'#if defined({max_name}) || defined(LINTER_HAS_{max_name})')
+    cw.p(f'YNL_ENUM_SENTINAL({max_name}, {val}),')
+    cw.p('#endif')
+    cw.p('{},')
+    cw.block_end(line=';')
+    cw.nl()
+    enum_type = enum['type'].upper()
+    return f'{{"{enum.name}", &{map_name}[0], YNL_{enum_type}}},'
+
+
 def put_local_vars(struct):
     local_vars = []
     has_array = False
@@ -3462,6 +3488,7 @@ def main():
     parser.add_argument('--spec', dest='spec', type=str, required=True)
     parser.add_argument('--header', dest='header', action='store_true', default=None)
     parser.add_argument('--source', dest='header', action='store_false')
+    parser.add_argument('--linter', dest='linter', action='store_true')
     parser.add_argument('--user-header', nargs='+', default=[])
     parser.add_argument('--cmp-out', action='store_true', default=None,
                         help='Do not overwrite the output file if the new output is identical to the old')
@@ -3470,8 +3497,10 @@ def main():
     parser.add_argument('--function-prefix', dest='fn_prefix', type=str)
     args = parser.parse_args()
 
+    if args.linter:
+        args.header = False
     if args.header is None:
-        parser.error("--header or --source is required")
+        parser.error("--header, --source or --linter is required")
 
     exclude_ops = [re.compile(expr) for expr in args.exclude_op]
 
@@ -3494,17 +3523,19 @@ def main():
         cw.p(f'// SPDX-License-Identifier: {parsed.license}')
     cw.p("/* Do not edit directly, auto-generated from: */")
     cw.p(f"/*\t{spec_kernel} */")
-    cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
-    if args.exclude_op or args.user_header or args.fn_prefix:
-        line = ''
-        if args.user_header:
-            line += ' --user-header '.join([''] + args.user_header)
-        if args.exclude_op:
-            line += ' --exclude-op '.join([''] + args.exclude_op)
-        if args.fn_prefix:
-            line += f' --function-prefix {args.fn_prefix}'
-        cw.p(f'/* YNL-ARG{line} */')
-    cw.p('/* To regenerate run: tools/net/ynl/ynl-regen.sh */')
+    if not args.linter:
+        ynl_arg = 'header' if args.header else 'source'
+        cw.p(f"/* YNL-GEN {args.mode} {ynl_arg} */")
+        if args.exclude_op or args.user_header or args.fn_prefix:
+            line = ''
+            if args.user_header:
+                line += ' --user-header '.join([''] + args.user_header)
+            if args.exclude_op:
+                line += ' --exclude-op '.join([''] + args.exclude_op)
+            if args.fn_prefix:
+                line += f' --function-prefix {args.fn_prefix}'
+            cw.p(f'/* YNL-ARG{line} */')
+        cw.p('/* To regenerate run: tools/net/ynl/ynl-regen.sh */')
     cw.nl()
 
     if args.mode == 'uapi':
@@ -3539,6 +3570,8 @@ def main():
             cw.p('#include <linux/types.h>')
             if family_contains_bitfield32(parsed):
                 cw.p('#include <linux/netlink.h>')
+        elif args.linter:
+            cw.p('#include "../linter/linter.h"')
         else:
             cw.p(f'#include "{hdr_file}"')
             cw.p('#include "ynl.h"')
@@ -3690,6 +3723,21 @@ def main():
                     cw.nl()
                     print_wrapped_type(ri)
             cw.nl()
+        elif args.linter:
+            enum_sets = []
+            for name, const in parsed.consts.items():
+                if isinstance(const, EnumSet):
+                    enum_sets.append(put_enum_to_linter(parsed, cw, const))
+            cw.nl()
+            cw.block_start(line="static const struct enum_set enums[] =")
+            for enum_set in enum_sets:
+                cw.p(enum_set)
+            cw.p('{},')
+            cw.block_end(line=';')
+            cw.nl()
+            cw.write_func('int', 'main',
+                          args=['const int argc', 'const char **argv'],
+                          body=['return linter_run(argc, argv, &enums[0]);'])
         else:
             cw.p('/* Enums */')
             put_op_name(parsed, cw)

3/2 2026/09/10 22:52 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 10353
							output: 22
							thoughts: 494
						

Thoughts:
**Analyzing the Patch: Focus on Netlink Specs and UAPI Headers**

Okay, let's break this down. My initial impression is that this patch is primarily focused on updating the user-space API (UAPI) definitions, specifically related to Netlink and the underlying header files. The core of the change seems to be in the `Documentation/netlink/specs/` directory, which is where the heart of Netlink's specification lies.

My first thought is to dig into the YAML files. It looks like the patch is adding or modifying specifications for `conntrack`, `nftables`, and `rt-link` within the Netlink framework. I need to understand the impact of those YAML changes – field names, prefixes, how they map to kernel structures, and so on.

The `rt-link` updates are particularly interesting because they likely signal improvements to the routing table and link management APIs, which are always critical. I need to see if these spec modifications reflect new features, bug fixes, or simply clarifications and better organization.

Then there's the `include/uapi/linux/snmp.h` change. It looks like a straightforward include guard update, changing the define name from `_LINUX_SNMP_H` to `_UAPI_LINUX_SNMP_H`. This is important for ensuring these user-space headers don't collide with internal kernel definitions or other UAPI components. I want to check if the corresponding `include/linux/snmp.h` exists to be sure we are not deprecating it.

Finally, the `tools/net/ynl/` directory seems to be related to some sort of code generation and linting process, especially around the Netlink definitions. I see additions for linter ignores, targets, a new shell script (`gen-h.sh`), a header (`linter.h`), and Python code updates. This likely automates validation of Netlink specifications and/or generates C code based on those specifications. It's a nice improvement for maintainability. I want to look more into this, particularly the Python code, to understand exactly how the linter functions.

My next step is to examine the `rt-link.yaml` file to understand the scope and nature of the updates, followed by the linter code to see how it works. I'll need to use tools to verify and understand these files.



4/2 2026/09/10 22:52 tool grepper
0m Args:
{
  "Expression": "_LINUX_SNMP_H"
}

Results:
{
  "Output": "include/uapi/linux/snmp.h-7-\ninclude/uapi/linux/snmp.h:8:#ifndef _UAPI_LINUX_SNMP_H\ninclude/uapi/linux/snmp.h:9:#define _UAPI_LINUX_SNMP_H\ninclude/uapi/linux/snmp.h-10-\n--\ninclude/uapi/linux/snmp.h=352=enum\n--\ninclude/uapi/linux/snmp.h-374-\ninclude/uapi/linux/snmp.h:375:#endif\t/* _UAPI_LINUX_SNMP_H */\n--\ntools/net/ynl/Makefile.deps=43=CFLAGS_rt-link:=$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h) \\\n--\ntools/net/ynl/Makefile.deps-49-\t$(call get_hdr_inc,_LINUX_IF_VLAN_H_,if_vlan.h) \\\ntools/net/ynl/Makefile.deps:50:\t$(call get_hdr_inc,_LINUX_SNMP_H,snmp.h) \\\ntools/net/ynl/Makefile.deps-51-\t$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h)\n"
}

5/2 2026/09/10 22:52 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11706
							output: 27
							thoughts: 18
						

Thoughts:
Here's my summary, as though I were thinking through the process:

**Diving into the SNMP Header**

Okay, so I need to understand how this system handles SNMP. My first step is to quickly confirm the existence and contents of the core header file. I'm going to check for `include/linux/snmp.h`. That's where I'd expect to find the fundamental definitions, structures, and possibly even some basic function declarations related to the Linux kernel's SNMP implementation. Knowing its presence (or absence) provides a critical starting point to understanding the architecture. I'll need to know whether the existing code has its own custom extensions, or leverages the standard SNMP interface. That header file is the key.



6/2 2026/09/10 22:52 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "snmp\\.h",
  "PathPrefix": "include/linux"
}

Results:
{
  "error": "no matches"
}

7/2 2026/09/10 22:52 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11918
							output: 89
							thoughts: 564
						

Thoughts:
**My Assessment: WorthFuzzing = False**

Okay, let's break this down. First, I need to consult Section 2 of the instructions – the negative criteria for returning `WorthFuzzing=false`. The relevant categories are those that don't involve kernel executable logic changes. Specifically, I'm looking for things like documentation updates, changes to user-space tools, and purely decorative logging.

Now, I'll go through the diff file by file. I see:

1.  `Documentation/netlink/specs/conntrack.yaml` – **Documentation**
2.  `Documentation/netlink/specs/nftables.yaml` – **Documentation**
3.  `Documentation/netlink/specs/rt-link.yaml` – **Documentation**
4.  `include/uapi/linux/snmp.h` – This is a header file, but it's a simple `#ifndef` guard change. No actual executable code, data structures, or definitions are being modified. **Header Guard Change**
5.  `tools/net/ynl/Makefile.deps` – **tools/**
6.  `tools/net/ynl/generated/.gitignore` – **tools/**
7.  `tools/net/ynl/generated/Makefile` – **tools/**
8.  `tools/net/ynl/linter/gen-h.sh` – **tools/**
9.  `tools/net/ynl/linter/linter.h` – **tools/**
10. `tools/net/ynl/pyynl/ynl_gen_c.py` – **tools/**

Analyzing the diff, I can confirm that all the changes fall squarely into the categories I'm supposed to ignore. It's documentation, tools (YNL netlink tool/linter support), and a trivial header guard rename. No modifications affect the kernel's executable logic, data structures, control flow, or overall behavior. Therefore, given the criteria defined, `WorthFuzzing` must be set to `false`.

Just to be absolutely certain, I'll quickly double-check:

*   `WorthFuzzing`: false - **Checked**
*   `FocusSymbols`: \[ ] - **Implicitly Empty**
*   `EnableConfigs`: \[ ] - **Implicitly Empty**
*   `Reasoning`: Concise explanation, which I've formulated. - **Checked**

With that, I'm confident in my assessment and ready to call `set-results`.



8/2 2026/09/10 22:52 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies documentation (YAML specs in Documentation/netlink/specs/), user-space tooling/linters (tools/net/ynl/), and an include guard in include/uapi/linux/snmp.h. No executable kernel code or data structures are modified.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies documentation (YAML specs in Documentation/netlink/specs/), user-space tooling/linters (tools/net/ynl/), and an include guard in include/uapi/linux/snmp.h. No executable kernel code or data structures are modified.",
  "WorthFuzzing": false
}

9/1 2026/09/10 22:52 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)