| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/01 07:29 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies user-space Python tooling for Netlink (YNL) in tools/net/ynl/ and does not change any kernel code.",
"WorthFuzzing": false
} |
| 1/1 | 2026/07/01 07:29 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e7a3780e43d6f76f45093b9cd790caf690114a8d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 1 07:29:27 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/tools/net/ynl/pyynl/__init__.py b/tools/net/ynl/pyynl/__init__.py\nindex e69de29bb2d1d6..d8f59c132ab7ee 100644\n--- a/tools/net/ynl/pyynl/__init__.py\n+++ b/tools/net/ynl/pyynl/__init__.py\n@@ -0,0 +1,9 @@\n+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+\"\"\" Python YNL (YAML Netlink) library. \"\"\"\n+\n+# Re-export the public library API so it can be imported straight from the\n+# package, e.g. `from pyynl import YnlFamily`.\n+# pylint: disable=wildcard-import,unused-wildcard-import\n+from .lib import *\n+from .lib import __all__\ndiff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py\nindex 8275a806cf73d6..b6a6ce12b4a7f8 100755\n--- a/tools/net/ynl/pyynl/cli.py\n+++ b/tools/net/ynl/pyynl/cli.py\n@@ -17,9 +17,7 @@ import textwrap\n # pylint: disable=no-name-in-module,wrong-import-position\n sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())\n from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException\n-\n-SYS_SCHEMA_DIR='/usr/share/ynl'\n-RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'\n+from lib import list_families\n \n # pylint: disable=too-few-public-methods,too-many-locals\n class Colors:\n@@ -48,30 +46,6 @@ def term_width():\n \"\"\" Get terminal width in columns (80 if stdout is not a terminal) \"\"\"\n return shutil.get_terminal_size().columns\n \n-def schema_dir():\n- \"\"\"\n- Return the effective schema directory, preferring in-tree before\n- system schema directory.\n- \"\"\"\n- script_dir = os.path.dirname(os.path.abspath(__file__))\n- schema_dir_ = os.path.abspath(f\"{script_dir}/{RELATIVE_SCHEMA_DIR}\")\n- if not os.path.isdir(schema_dir_):\n- schema_dir_ = SYS_SCHEMA_DIR\n- if not os.path.isdir(schema_dir_):\n- raise YnlException(f\"Schema directory {schema_dir_} does not exist\")\n- return schema_dir_\n-\n-def spec_dir():\n- \"\"\"\n- Return the effective spec directory, relative to the effective\n- schema directory.\n- \"\"\"\n- spec_dir_ = schema_dir() + '/specs'\n- if not os.path.isdir(spec_dir_):\n- raise YnlException(f\"Spec directory {spec_dir_} does not exist\")\n- return spec_dir_\n-\n-\n class YnlEncoder(json.JSONEncoder):\n \"\"\"A custom encoder for emitting JSON with ynl-specific instance types\"\"\"\n def default(self, o):\n@@ -272,9 +246,8 @@ def main():\n pprint.pprint(msg, width=term_width(), compact=True)\n \n if args.list_families:\n- for filename in sorted(os.listdir(spec_dir())):\n- if filename.endswith('.yaml'):\n- print(filename.removesuffix('.yaml'))\n+ for family in list_families():\n+ print(family)\n return\n \n if args.no_schema:\n@@ -284,28 +257,23 @@ def main():\n if args.json_text:\n attrs = json.loads(args.json_text)\n \n- if args.family:\n- spec = f\"{spec_dir()}/{args.family}.yaml\"\n- else:\n- spec = args.spec\n- if not os.path.isfile(spec):\n- raise YnlException(f\"Spec file {spec} does not exist\")\n+ if args.spec and not os.path.isfile(args.spec):\n+ raise YnlException(f\"Spec file {args.spec} does not exist\")\n \n+ # Spec/YnlFamily will raise if both or neither spec and family are given\n if args.validate:\n+ # Force validation even for installed specs (schema=True), unless the\n+ # user explicitly picked a schema or opted out with --no-schema.\n+ schema = True if args.schema is None else args.schema\n try:\n- SpecFamily(spec, args.schema)\n+ SpecFamily(args.spec, schema_path=schema, family=args.family)\n except SpecException as error:\n print(error)\n sys.exit(1)\n return\n \n- if args.family: # set behaviour when using installed specs\n- if args.schema is None and spec.startswith(SYS_SCHEMA_DIR):\n- args.schema = '' # disable schema validation when installed\n- if args.process_unknown is None:\n- args.process_unknown = True\n-\n- ynl = YnlFamily(spec, args.schema, args.process_unknown,\n+ ynl = YnlFamily(args.spec, schema=args.schema, family=args.family,\n+ process_unknown=args.process_unknown,\n recv_size=args.dbg_small_recv)\n if args.dbg_small_recv:\n ynl.set_recv_dbg(True)\ndiff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py\nindex be741985ae4ee7..aa4263c8cba9ef 100644\n--- a/tools/net/ynl/pyynl/lib/__init__.py\n+++ b/tools/net/ynl/pyynl/lib/__init__.py\n@@ -5,12 +5,13 @@\n from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \\\n SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat, \\\n SpecException\n+from .specdir import list_families\n from .ynl import YnlFamily, Netlink, NlError, NlPolicy, YnlException\n \n from .doc_generator import YnlDocGenerator\n \n __all__ = [\"SpecAttr\", \"SpecAttrSet\", \"SpecEnumEntry\", \"SpecEnumSet\",\n \"SpecFamily\", \"SpecOperation\", \"SpecSubMessage\", \"SpecSubMessageFormat\",\n- \"SpecException\",\n+ \"SpecException\", \"list_families\",\n \"YnlFamily\", \"Netlink\", \"NlError\", \"NlPolicy\", \"YnlException\",\n \"YnlDocGenerator\"]\ndiff --git a/tools/net/ynl/pyynl/lib/nlspec.py b/tools/net/ynl/pyynl/lib/nlspec.py\nindex 0469a0e270d059..b4ec59814ab1bc 100644\n--- a/tools/net/ynl/pyynl/lib/nlspec.py\n+++ b/tools/net/ynl/pyynl/lib/nlspec.py\n@@ -12,6 +12,8 @@ import importlib\n import os\n import yaml as pyyaml\n \n+from .specdir import find_spec, SYS_SCHEMA_DIR\n+\n \n class SpecException(Exception):\n \"\"\"Netlink spec exception.\n@@ -444,7 +446,23 @@ class SpecFamily(SpecElement):\n except AttributeError:\n _yaml_loader = pyyaml.SafeLoader\n \n- def __init__(self, spec_path, schema_path=None, exclude_ops=None):\n+ def __init__(self, spec_path=None, schema_path=None, exclude_ops=None,\n+ family=None):\n+ # schema_path selects how the spec is validated:\n+ # None -- no preference: validate against the default schema,\n+ # but trust (skip) installed specs selected by family=\n+ # True -- always validate against the default schema\n+ # path -- validate against this schema\n+ # '' -- do not validate\n+ if (spec_path is None) == (family is None):\n+ raise ValueError(\"Specify exactly one of spec path or family name\")\n+ if family is not None:\n+ spec_path = find_spec(family)\n+ # Installed specs are assumed correct, so skip schema validation\n+ # to save cycles unless the caller asked to validate.\n+ if schema_path is None and spec_path.startswith(SYS_SCHEMA_DIR):\n+ schema_path = ''\n+\n with open(spec_path, \"r\", encoding='utf-8') as stream:\n prefix = '# SPDX-License-Identifier: '\n first = stream.readline().strip()\n@@ -465,7 +483,7 @@ class SpecFamily(SpecElement):\n self.proto = self.yaml.get('protocol', 'genetlink')\n self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')\n \n- if schema_path is None:\n+ if schema_path is None or schema_path is True:\n schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'\n if schema_path:\n with open(schema_path, \"r\", encoding='utf-8') as stream:\ndiff --git a/tools/net/ynl/pyynl/lib/specdir.py b/tools/net/ynl/pyynl/lib/specdir.py\nnew file mode 100644\nindex 00000000000000..fcea9b9fb7b043\n--- /dev/null\n+++ b/tools/net/ynl/pyynl/lib/specdir.py\n@@ -0,0 +1,51 @@\n+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+\"\"\"\n+Locating YNL spec and schema files on disk.\n+\n+Resolves the directory holding the YAML specs (preferring an in-tree copy\n+over the installed system path) and maps family names to spec files.\n+\"\"\"\n+\n+import os\n+\n+SYS_SCHEMA_DIR='/usr/share/ynl'\n+RELATIVE_SCHEMA_DIR='../../../../../Documentation/netlink'\n+\n+\n+def schema_dir():\n+ \"\"\"\n+ Return the effective schema directory, preferring in-tree before\n+ system schema directory.\n+ \"\"\"\n+ script_dir = os.path.dirname(os.path.abspath(__file__))\n+ schema_dir_ = os.path.abspath(f\"{script_dir}/{RELATIVE_SCHEMA_DIR}\")\n+ if not os.path.isdir(schema_dir_):\n+ schema_dir_ = SYS_SCHEMA_DIR\n+ if not os.path.isdir(schema_dir_):\n+ raise FileNotFoundError(f\"Schema directory {schema_dir_} does not exist\")\n+ return schema_dir_\n+\n+def spec_dir():\n+ \"\"\"\n+ Return the effective spec directory, relative to the effective\n+ schema directory.\n+ \"\"\"\n+ spec_dir_ = schema_dir() + '/specs'\n+ if not os.path.isdir(spec_dir_):\n+ raise FileNotFoundError(f\"Spec directory {spec_dir_} does not exist\")\n+ return spec_dir_\n+\n+\n+def find_spec(family):\n+ \"\"\" Return the path to the YAML spec file for a family by name. \"\"\"\n+ spec = f\"{spec_dir()}/{family}.yaml\"\n+ if not os.path.isfile(spec):\n+ raise FileNotFoundError(f\"Spec for family '{family}' not found at {spec}\")\n+ return spec\n+\n+\n+def list_families():\n+ \"\"\" Return the sorted names of all families with an installed spec. \"\"\"\n+ return sorted(f.removesuffix('.yaml')\n+ for f in os.listdir(spec_dir()) if f.endswith('.yaml'))\ndiff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py\nindex 092d132edec12f..8682bf588e1fa0 100644\n--- a/tools/net/ynl/pyynl/lib/ynl.py\n+++ b/tools/net/ynl/pyynl/lib/ynl.py\n@@ -661,6 +661,14 @@ class YnlFamily(SpecFamily):\n \"\"\"\n YNL family -- a Netlink interface built from a YAML spec.\n \n+ The spec can be selected either by file path (def_path=) or, when it\n+ ships in a well-known location, by family name (family=\"xyz\"); exactly\n+ one of the two must be given. For example:\n+\n+ from pyynl import YnlFamily\n+\n+ ynl = YnlFamily(family=\"netdev\")\n+\n Primary use of the class is to execute Netlink commands:\n \n ynl.\u003cop_name\u003e(attrs, ...)\n@@ -691,11 +699,16 @@ class YnlFamily(SpecFamily):\n \n ynl.get_policy(op_name, mode) -- query kernel policy for an op\n \"\"\"\n- def __init__(self, def_path, schema=None, process_unknown=False,\n- recv_size=0):\n- super().__init__(def_path, schema)\n+ def __init__(self, def_path=None, schema=None, process_unknown=None,\n+ recv_size=0, family=None):\n+ super().__init__(def_path, schema, family=family)\n \n self.include_raw = False\n+ # Specs from /usr (selected by family=) have a higher chance of being\n+ # stale, default to ignoring unknown attrs. In-tree users, and users\n+ # who bundle the spec need to make a conscious decision.\n+ if process_unknown is None:\n+ process_unknown = family is not None\n self.process_unknown = process_unknown\n \n try:\ndiff --git a/tools/net/ynl/tests/ethtool.py b/tools/net/ynl/tests/ethtool.py\nindex db3b62c652e77f..0ee0c8e87686be 100755\n--- a/tools/net/ynl/tests/ethtool.py\n+++ b/tools/net/ynl/tests/ethtool.py\n@@ -11,12 +11,10 @@ import pathlib\n import pprint\n import sys\n import re\n-import os\n \n # pylint: disable=no-name-in-module,wrong-import-position\n sys.path.append(pathlib.Path(__file__).resolve().parent.parent.joinpath('pyynl').as_posix())\n # pylint: disable=import-error\n-from cli import schema_dir, spec_dir\n from lib import YnlFamily\n \n \n@@ -173,10 +171,7 @@ def main():\n \n args = parser.parse_args()\n \n- spec = os.path.join(spec_dir(), 'ethtool.yaml')\n- schema = os.path.join(schema_dir(), 'genetlink-legacy.yaml')\n-\n- ynl = YnlFamily(spec, schema, recv_size=args.dbg_small_recv)\n+ ynl = YnlFamily(family='ethtool', recv_size=args.dbg_small_recv)\n if args.dbg_small_recv:\n ynl.set_recv_dbg(True)\n \n"
} |
| 2/1 | 2026/07/01 07:29 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies user-space Python tooling for Netlink (YNL) in tools/net/ynl/ and does not change any kernel code.",
"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). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 e7a3780e43d6f76f45093b9cd790caf690114a8d
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 1 07:29:27 2026 +0000
syz-cluster: applied patch under review
diff --git a/tools/net/ynl/pyynl/__init__.py b/tools/net/ynl/pyynl/__init__.py
index e69de29bb2d1d6..d8f59c132ab7ee 100644
--- a/tools/net/ynl/pyynl/__init__.py
+++ b/tools/net/ynl/pyynl/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+""" Python YNL (YAML Netlink) library. """
+
+# Re-export the public library API so it can be imported straight from the
+# package, e.g. `from pyynl import YnlFamily`.
+# pylint: disable=wildcard-import,unused-wildcard-import
+from .lib import *
+from .lib import __all__
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index 8275a806cf73d6..b6a6ce12b4a7f8 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -17,9 +17,7 @@ import textwrap
# pylint: disable=no-name-in-module,wrong-import-position
sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())
from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException
-
-SYS_SCHEMA_DIR='/usr/share/ynl'
-RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
+from lib import list_families
# pylint: disable=too-few-public-methods,too-many-locals
class Colors:
@@ -48,30 +46,6 @@ def term_width():
""" Get terminal width in columns (80 if stdout is not a terminal) """
return shutil.get_terminal_size().columns
-def schema_dir():
- """
- Return the effective schema directory, preferring in-tree before
- system schema directory.
- """
- script_dir = os.path.dirname(os.path.abspath(__file__))
- schema_dir_ = os.path.abspath(f"{script_dir}/{RELATIVE_SCHEMA_DIR}")
- if not os.path.isdir(schema_dir_):
- schema_dir_ = SYS_SCHEMA_DIR
- if not os.path.isdir(schema_dir_):
- raise YnlException(f"Schema directory {schema_dir_} does not exist")
- return schema_dir_
-
-def spec_dir():
- """
- Return the effective spec directory, relative to the effective
- schema directory.
- """
- spec_dir_ = schema_dir() + '/specs'
- if not os.path.isdir(spec_dir_):
- raise YnlException(f"Spec directory {spec_dir_} does not exist")
- return spec_dir_
-
-
class YnlEncoder(json.JSONEncoder):
"""A custom encoder for emitting JSON with ynl-specific instance types"""
def default(self, o):
@@ -272,9 +246,8 @@ def main():
pprint.pprint(msg, width=term_width(), compact=True)
if args.list_families:
- for filename in sorted(os.listdir(spec_dir())):
- if filename.endswith('.yaml'):
- print(filename.removesuffix('.yaml'))
+ for family in list_families():
+ print(family)
return
if args.no_schema:
@@ -284,28 +257,23 @@ def main():
if args.json_text:
attrs = json.loads(args.json_text)
- if args.family:
- spec = f"{spec_dir()}/{args.family}.yaml"
- else:
- spec = args.spec
- if not os.path.isfile(spec):
- raise YnlException(f"Spec file {spec} does not exist")
+ if args.spec and not os.path.isfile(args.spec):
+ raise YnlException(f"Spec file {args.spec} does not exist")
+ # Spec/YnlFamily will raise if both or neither spec and family are given
if args.validate:
+ # Force validation even for installed specs (schema=True), unless the
+ # user explicitly picked a schema or opted out with --no-schema.
+ schema = True if args.schema is None else args.schema
try:
- SpecFamily(spec, args.schema)
+ SpecFamily(args.spec, schema_path=schema, family=args.family)
except SpecException as error:
print(error)
sys.exit(1)
return
- if args.family: # set behaviour when using installed specs
- if args.schema is None and spec.startswith(SYS_SCHEMA_DIR):
- args.schema = '' # disable schema validation when installed
- if args.process_unknown is None:
- args.process_unknown = True
-
- ynl = YnlFamily(spec, args.schema, args.process_unknown,
+ ynl = YnlFamily(args.spec, schema=args.schema, family=args.family,
+ process_unknown=args.process_unknown,
recv_size=args.dbg_small_recv)
if args.dbg_small_recv:
ynl.set_recv_dbg(True)
diff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py
index be741985ae4ee7..aa4263c8cba9ef 100644
--- a/tools/net/ynl/pyynl/lib/__init__.py
+++ b/tools/net/ynl/pyynl/lib/__init__.py
@@ -5,12 +5,13 @@
from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat, \
SpecException
+from .specdir import list_families
from .ynl import YnlFamily, Netlink, NlError, NlPolicy, YnlException
from .doc_generator import YnlDocGenerator
__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
"SpecFamily", "SpecOperation", "SpecSubMessage", "SpecSubMessageFormat",
- "SpecException",
+ "SpecException", "list_families",
"YnlFamily", "Netlink", "NlError", "NlPolicy", "YnlException",
"YnlDocGenerator"]
diff --git a/tools/net/ynl/pyynl/lib/nlspec.py b/tools/net/ynl/pyynl/lib/nlspec.py
index 0469a0e270d059..b4ec59814ab1bc 100644
--- a/tools/net/ynl/pyynl/lib/nlspec.py
+++ b/tools/net/ynl/pyynl/lib/nlspec.py
@@ -12,6 +12,8 @@ import importlib
import os
import yaml as pyyaml
+from .specdir import find_spec, SYS_SCHEMA_DIR
+
class SpecException(Exception):
"""Netlink spec exception.
@@ -444,7 +446,23 @@ class SpecFamily(SpecElement):
except AttributeError:
_yaml_loader = pyyaml.SafeLoader
- def __init__(self, spec_path, schema_path=None, exclude_ops=None):
+ def __init__(self, spec_path=None, schema_path=None, exclude_ops=None,
+ family=None):
+ # schema_path selects how the spec is validated:
+ # None -- no preference: validate against the default schema,
+ # but trust (skip) installed specs selected by family=
+ # True -- always validate against the default schema
+ # path -- validate against this schema
+ # '' -- do not validate
+ if (spec_path is None) == (family is None):
+ raise ValueError("Specify exactly one of spec path or family name")
+ if family is not None:
+ spec_path = find_spec(family)
+ # Installed specs are assumed correct, so skip schema validation
+ # to save cycles unless the caller asked to validate.
+ if schema_path is None and spec_path.startswith(SYS_SCHEMA_DIR):
+ schema_path = ''
+
with open(spec_path, "r", encoding='utf-8') as stream:
prefix = '# SPDX-License-Identifier: '
first = stream.readline().strip()
@@ -465,7 +483,7 @@ class SpecFamily(SpecElement):
self.proto = self.yaml.get('protocol', 'genetlink')
self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')
- if schema_path is None:
+ if schema_path is None or schema_path is True:
schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'
if schema_path:
with open(schema_path, "r", encoding='utf-8') as stream:
diff --git a/tools/net/ynl/pyynl/lib/specdir.py b/tools/net/ynl/pyynl/lib/specdir.py
new file mode 100644
index 00000000000000..fcea9b9fb7b043
--- /dev/null
+++ b/tools/net/ynl/pyynl/lib/specdir.py
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+"""
+Locating YNL spec and schema files on disk.
+
+Resolves the directory holding the YAML specs (preferring an in-tree copy
+over the installed system path) and maps family names to spec files.
+"""
+
+import os
+
+SYS_SCHEMA_DIR='/usr/share/ynl'
+RELATIVE_SCHEMA_DIR='../../../../../Documentation/netlink'
+
+
+def schema_dir():
+ """
+ Return the effective schema directory, preferring in-tree before
+ system schema directory.
+ """
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ schema_dir_ = os.path.abspath(f"{script_dir}/{RELATIVE_SCHEMA_DIR}")
+ if not os.path.isdir(schema_dir_):
+ schema_dir_ = SYS_SCHEMA_DIR
+ if not os.path.isdir(schema_dir_):
+ raise FileNotFoundError(f"Schema directory {schema_dir_} does not exist")
+ return schema_dir_
+
+def spec_dir():
+ """
+ Return the effective spec directory, relative to the effective
+ schema directory.
+ """
+ spec_dir_ = schema_dir() + '/specs'
+ if not os.path.isdir(spec_dir_):
+ raise FileNotFoundError(f"Spec directory {spec_dir_} does not exist")
+ return spec_dir_
+
+
+def find_spec(family):
+ """ Return the path to the YAML spec file for a family by name. """
+ spec = f"{spec_dir()}/{family}.yaml"
+ if not os.path.isfile(spec):
+ raise FileNotFoundError(f"Spec for family '{family}' not found at {spec}")
+ return spec
+
+
+def list_families():
+ """ Return the sorted names of all families with an installed spec. """
+ return sorted(f.removesuffix('.yaml')
+ for f in os.listdir(spec_dir()) if f.endswith('.yaml'))
diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py
index 092d132edec12f..8682bf588e1fa0 100644
--- a/tools/net/ynl/pyynl/lib/ynl.py
+++ b/tools/net/ynl/pyynl/lib/ynl.py
@@ -661,6 +661,14 @@ class YnlFamily(SpecFamily):
"""
YNL family -- a Netlink interface built from a YAML spec.
+ The spec can be selected either by file path (def_path=) or, when it
+ ships in a well-known location, by family name (family="xyz"); exactly
+ one of the two must be given. For example:
+
+ from pyynl import YnlFamily
+
+ ynl = YnlFamily(family="netdev")
+
Primary use of the class is to execute Netlink commands:
ynl.<op_name>(attrs, ...)
@@ -691,11 +699,16 @@ class YnlFamily(SpecFamily):
ynl.get_policy(op_name, mode) -- query kernel policy for an op
"""
- def __init__(self, def_path, schema=None, process_unknown=False,
- recv_size=0):
- super().__init__(def_path, schema)
+ def __init__(self, def_path=None, schema=None, process_unknown=None,
+ recv_size=0, family=None):
+ super().__init__(def_path, schema, family=family)
self.include_raw = False
+ # Specs from /usr (selected by family=) have a higher chance of being
+ # stale, default to ignoring unknown attrs. In-tree users, and users
+ # who bundle the spec need to make a conscious decision.
+ if process_unknown is None:
+ process_unknown = family is not None
self.process_unknown = process_unknown
try:
diff --git a/tools/net/ynl/tests/ethtool.py b/tools/net/ynl/tests/ethtool.py
index db3b62c652e77f..0ee0c8e87686be 100755
--- a/tools/net/ynl/tests/ethtool.py
+++ b/tools/net/ynl/tests/ethtool.py
@@ -11,12 +11,10 @@ import pathlib
import pprint
import sys
import re
-import os
# pylint: disable=no-name-in-module,wrong-import-position
sys.path.append(pathlib.Path(__file__).resolve().parent.parent.joinpath('pyynl').as_posix())
# pylint: disable=import-error
-from cli import schema_dir, spec_dir
from lib import YnlFamily
@@ -173,10 +171,7 @@ def main():
args = parser.parse_args()
- spec = os.path.join(spec_dir(), 'ethtool.yaml')
- schema = os.path.join(schema_dir(), 'genetlink-legacy.yaml')
-
- ynl = YnlFamily(spec, schema, recv_size=args.dbg_small_recv)
+ ynl = YnlFamily(family='ethtool', recv_size=args.dbg_small_recv)
if args.dbg_small_recv:
ynl.set_recv_dbg(True)
|
| 3/2 | 2026/07/01 07:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5610 output: 63 thoughts: 339 Thoughts: **WorthFuzzing Assessment: Negative** Okay, let's break this down. This patch is touching Python code within `tools/net/ynl/pyynl/`. I recognize this as the user-space YAML Netlink library (YNL), which makes it clear this is a tooling layer for interacting with Netlink. Good, good. It's confined to `tools/`, which means this is **not** modifying any core kernel code, drivers, or architectures. The code itself is python scripts, specifically within a tool directory. Let me double check the files that have been modified: `__init__.py`, `cli.py`, the `lib` directory's `__init__.py`, `nlspec.py`, `specdir.py`, `ynl.py`, and finally a test file, `ethtool.py`. All are within that specified directory structure. No kernel headers, no kernel modules, nothing that directly impacts kernel execution. Therefore, because this is all within the user-space `tools/` directory and it is only modifications to Python scripts, there are no changes here that would necessitate fuzzing the kernel. It's a non-issue from a functional standpoint. So, WorthFuzzing is definitively `false`. No reason to even look at the focus symbols or enable configurations. The reasoning is clear: pure user-space tooling. |
| 4/2 | 2026/07/01 07:29 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies user-space Python tooling for Netlink (YNL) in tools/net/ynl/ and does not change any kernel code.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies user-space Python tooling for Netlink (YNL) in tools/net/ynl/ and does not change any kernel code.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|