AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "ETHTOOL_NETLINK"
  ],
  "FocusSymbols": [
    "ethnl_act_phy_test"
  ],
  "KMSANReasoning": "The patch introduces new ethtool netlink commands (`INTF_CAPS_GET`, `PHY_TEST_GET`, `PHY_TEST_ACT`) to query interface capabilities and configure PHY tests. The data structures used to pass information between the ethtool core and the drivers (`struct ethtool_intf_caps` and `struct ethtool_phy_test`) are allocated dynamically.\n\nHowever, the ethtool netlink framework explicitly zero-initializes the reply data structures via `ethnl_init_reply_data()` (which calls `memset(reply_data, 0, ops-\u003ereply_data_size)`) before passing them to the driver callbacks (e.g., `get_intf_caps`). Similarly, `phy_test_prepare_data()` explicitly calls `memset(\u0026data-\u003etest, 0, sizeof(data-\u003etest))`, and `ethnl_act_phy_test()` initializes its local struct with `= {}`.\n\nBecause all structures are fully zero-initialized before use, there is no risk of leaking uninitialized kernel stack or heap memory to user space, even if a driver fails to populate all fields. Furthermore, the netlink response is constructed by serializing individual fields using `nla_put_u32`, `nla_put_string`, etc., rather than copying raw structs, which inherently avoids leaking struct padding.\n\nAny potential issues, such as a driver writing past the end of the `blocks` array or returning an invalid `num_blocks` leading to out-of-bounds reads, would be reliably detected by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch adds new ethtool netlink messages (ETHTOOL_MSG_INTF_CAPS_GET, ETHTOOL_MSG_PHY_TEST_GET, ETHTOOL_MSG_PHY_TEST_ACT) and their corresponding netlink attribute policies. Even though no drivers currently implement the underlying ethtool operations, the netlink message parsing, policy validation, and initial handler functions (like ethnl_act_phy_test) are reachable from user-space and should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/08/25 05:06 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit d506dd108638e5d77d23f30efdf2cf7fa86dee9c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Aug 25 05:06:14 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml\nindex 5dd4d1b5d94bb..361eb3c665770 100644\n--- a/Documentation/netlink/specs/ethtool.yaml\n+++ b/Documentation/netlink/specs/ethtool.yaml\n@@ -210,6 +210,55 @@ definitions:\n       -\n         name: discard\n         value: 31\n+  -\n+    name: intf-component\n+    type: enum\n+    doc: Type of hardware component in the interface.\n+    entries:\n+      - mac\n+      - phy\n+      - module\n+  -\n+    name: intf-sublayer\n+    type: enum\n+    doc: 802.3 sublayer within a component.\n+    entries:\n+      - none\n+      - rs\n+      - pcs\n+      - fec\n+      - pma\n+      - pmd\n+  -\n+    name: phy-test-pattern\n+    type: enum\n+    doc: PHY test pattern types for PRBS generation/checking.\n+    entries:\n+      - off\n+      - prbs7\n+      - prbs9\n+      - prbs11\n+      - prbs13\n+      - prbs15\n+      - prbs23\n+      - prbs31\n+      - ssprq\n+      - prbs13q\n+      - prbs31q\n+      - square-nrz\n+      - square-pam4\n+      - tx-linearity\n+      - scrambled-idle\n+      - k28-5\n+      - k28-7\n+  -\n+    name: phy-test-action\n+    type: enum\n+    doc: BERT control actions.\n+    entries:\n+      - none\n+      - start\n+      - stop\n \n attribute-sets:\n   -\n@@ -1905,6 +1954,115 @@ attribute-sets:\n         name: link\n         type: nest\n         nested-attributes: mse-snapshot\n+  -\n+    name: intf-block\n+    doc: A single functional block in the interface.\n+    attributes:\n+      -\n+        name: id\n+        type: u32\n+      -\n+        name: component\n+        type: u32\n+        enum: intf-component\n+      -\n+        name: sublayer\n+        type: u32\n+        enum: intf-sublayer\n+      -\n+        name: instance\n+        type: u32\n+        doc: |\n+          Identifies the hardware entity when multiple instances of\n+          the same component exist in the interface. The driver\n+          assigns instance numbers sequentially based on position in\n+          the physical path. Defaults to 0 when only one instance of\n+          a given component exists.\n+          For PHY: instance 0 is the internal/host-side PHY,\n+          instance 1 is an external PHY further along the path\n+          toward the line side.\n+          For MAC: instance 0 is the host-facing MAC, instance 1 is\n+          a secondary MAC further along the data path (e.g., behind\n+          an embedded switch).\n+      -\n+        name: name\n+        type: string\n+      -\n+        name: depth\n+        type: u8\n+      -\n+        name: lanes\n+        type: u32\n+      -\n+        name: loopback-supported\n+        type: u32\n+      -\n+        name: supported-tx-patterns\n+        type: u32\n+      -\n+        name: supported-rx-patterns\n+        type: u32\n+      -\n+        name: error-inject-supported\n+        type: flag\n+      -\n+        name: bert-supported\n+        type: flag\n+  -\n+    name: intf-caps\n+    doc: Interface capabilities message.\n+    attributes:\n+      -\n+        name: header\n+        type: nest\n+        nested-attributes: header\n+      -\n+        name: blocks\n+        type: nest\n+        nested-attributes: intf-block\n+        multi-attr: true\n+  -\n+    name: phy-test\n+    doc: PHY test configuration and status.\n+    attributes:\n+      -\n+        name: header\n+        type: nest\n+        nested-attributes: header\n+      -\n+        name: block-id\n+        type: u32\n+      -\n+        name: lane\n+        type: u32\n+      -\n+        name: tx-pattern\n+        type: u32\n+        enum: phy-test-pattern\n+      -\n+        name: rx-pattern\n+        type: u32\n+        enum: phy-test-pattern\n+      -\n+        name: bert-action\n+        type: u32\n+        enum: phy-test-action\n+      -\n+        name: inject-error-count\n+        type: u32\n+      -\n+        name: active-tests\n+        type: u32\n+        doc: Bitmask of currently running tests (bit 0 = BERT).\n+      -\n+        name: checker-lock\n+        type: u8\n+      -\n+        name: error-count\n+        type: u64\n+      -\n+        name: total-bits-sent\n+        type: u64\n \n operations:\n   enum-model: directional\n@@ -2859,6 +3017,54 @@ operations:\n             - worst-channel\n             - link\n       dump: *mse-get-op\n+    -\n+      name: intf-caps-get\n+      doc: Get interface capabilities (functional block layout).\n+      attribute-set: intf-caps\n+      do: \u0026intf-caps-get-op\n+        request:\n+          attributes:\n+            - header\n+        reply:\n+          attributes:\n+            - header\n+            - blocks\n+      dump: *intf-caps-get-op\n+    -\n+      name: phy-test-get\n+      doc: Get PHY test status (patterns, BERT counters).\n+      attribute-set: phy-test\n+      do:\n+        request:\n+          attributes:\n+            - header\n+            - block-id\n+            - lane\n+        reply:\n+          attributes:\n+            - header\n+            - block-id\n+            - lane\n+            - tx-pattern\n+            - rx-pattern\n+            - active-tests\n+            - checker-lock\n+            - error-count\n+            - total-bits-sent\n+    -\n+      name: phy-test-act\n+      doc: Configure PHY test (set patterns, start/stop BERT, inject errors).\n+      attribute-set: phy-test\n+      do:\n+        request:\n+          attributes:\n+            - header\n+            - block-id\n+            - lane\n+            - tx-pattern\n+            - rx-pattern\n+            - bert-action\n+            - inject-error-count\n \n mcast-groups:\n   list:\ndiff --git a/include/linux/ethtool.h b/include/linux/ethtool.h\nindex 12683b5d125e4..f9748e739f03e 100644\n--- a/include/linux/ethtool.h\n+++ b/include/linux/ethtool.h\n@@ -1197,6 +1197,92 @@ struct kernel_ethtool_ts_info {\n  * See \u0026struct net_device and \u0026struct net_device_ops for documentation\n  * of the generic netdev features interface.\n  */\n+\n+#define INTF_CAPS_MAX_BLOCKS\t16\n+#define INTF_BLOCK_NAME_LEN\t32\n+\n+#define LOOPBACK_SUPPORT_LOCAL\tBIT(0)\n+#define LOOPBACK_SUPPORT_REMOTE\tBIT(1)\n+\n+/**\n+ * struct ethtool_intf_block - Single functional block in the interface\n+ * @id: Unique block identifier\n+ * @component: Hardware component (MAC, PHY, MODULE)\n+ * @sublayer: 802.3 sublayer (RS, PCS, FEC, PMA, PMD, or NONE)\n+ * @instance: Index when multiple instances of same component exist\n+ * @name: Driver-chosen label\n+ * @depth: Ordering within same (component, sublayer) tuple\n+ * @lanes: Number of lanes\n+ * @loopback_supported: Bitmask of supported loopback directions\n+ * @supported_tx_patterns: Bitmask of patterns this block can generate\n+ * @supported_rx_patterns: Bitmask of patterns this block can check\n+ * @error_inject_supported: Block supports bit error injection\n+ * @bert_supported: Block supports BERT counters\n+ */\n+struct ethtool_intf_block {\n+\tu32 id;\n+\tenum intf_component component;\n+\tenum intf_sublayer sublayer;\n+\tu32 instance;\n+\tchar name[INTF_BLOCK_NAME_LEN];\n+\tu8 depth;\n+\tu32 lanes;\n+\tu32 loopback_supported;\n+\tu32 supported_tx_patterns;\n+\tu32 supported_rx_patterns;\n+\tbool error_inject_supported;\n+\tbool bert_supported;\n+};\n+\n+/**\n+ * struct ethtool_intf_caps - Interface capabilities (all blocks)\n+ * @num_blocks: Number of valid entries in blocks[]\n+ * @blocks: Array of functional blocks\n+ */\n+struct ethtool_intf_caps {\n+\tu32 num_blocks;\n+\tstruct ethtool_intf_block blocks[INTF_CAPS_MAX_BLOCKS];\n+};\n+\n+/* Bitmask of which ethtool_phy_test fields were explicitly specified */\n+#define PHY_TEST_CMD_TX_PATTERN\t\tBIT(0)\n+#define PHY_TEST_CMD_RX_PATTERN\t\tBIT(1)\n+#define PHY_TEST_CMD_BERT_ACTION\t\tBIT(2)\n+#define PHY_TEST_CMD_INJECT_COUNT\t\tBIT(3)\n+#define PHY_TEST_CMD_LANE\t\t\tBIT(4)\n+#define PHY_TEST_CMD_BLOCK_ID\t\t\tBIT(5)\n+\n+/* Bitmask of currently active tests (read-only) */\n+#define PHY_TEST_ACTIVE_BERT\t\tBIT(0)\n+\n+/**\n+ * struct ethtool_phy_test - PHY test configuration and status\n+ * @cmd: Bitmask of which fields are valid (PHY_TEST_CMD_*)\n+ * @block_id: Block to operate on (from intf-caps-get)\n+ * @lane: Lane number (0-based)\n+ * @tx_pattern: TX pattern generator setting\n+ * @rx_pattern: RX pattern checker setting\n+ * @bert_action: BERT start/stop control\n+ * @inject_error_count: Number of errors to inject\n+ * @active_tests: Bitmask of running tests (PHY_TEST_ACTIVE_*)\n+ * @checker_lock: RX checker lock status (read-only)\n+ * @error_count: BERT error counter (read-only)\n+ * @total_bits_sent: BERT total bits counter (read-only)\n+ */\n+struct ethtool_phy_test {\n+\tu32 cmd;\n+\tu32 block_id;\n+\tu32 lane;\n+\tenum phy_test_pattern tx_pattern;\n+\tenum phy_test_pattern rx_pattern;\n+\tenum phy_test_action bert_action;\n+\tu32 inject_error_count;\n+\tu32 active_tests;\n+\tu8 checker_lock;\n+\tu64 error_count;\n+\tu64 total_bits_sent;\n+};\n+\n struct ethtool_ops {\n \tu32     supported_input_xfrm:8;\n \tu32     cap_link_lanes_supported:1;\n@@ -1354,6 +1440,12 @@ struct ethtool_ops {\n \tint\t(*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,\n \t\t\t  struct netlink_ext_ack *extack);\n \tvoid\t(*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);\n+\tint\t(*get_intf_caps)(struct net_device *dev,\n+\t\t\t\t struct ethtool_intf_caps *caps);\n+\tint\t(*get_phy_test)(struct net_device *dev,\n+\t\t\t\tstruct ethtool_phy_test *test);\n+\tint\t(*set_phy_test)(struct net_device *dev,\n+\t\t\t\tstruct ethtool_phy_test *test);\n };\n \n int ethtool_check_ops(const struct ethtool_ops *ops);\ndiff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h\nindex 8134baf7860f3..3102bb7ba337f 100644\n--- a/include/uapi/linux/ethtool_netlink_generated.h\n+++ b/include/uapi/linux/ethtool_netlink_generated.h\n@@ -893,6 +893,9 @@ enum {\n \tETHTOOL_MSG_RSS_CREATE_ACT,\n \tETHTOOL_MSG_RSS_DELETE_ACT,\n \tETHTOOL_MSG_MSE_GET,\n+\tETHTOOL_MSG_INTF_CAPS_GET,\n+\tETHTOOL_MSG_PHY_TEST_GET,\n+\tETHTOOL_MSG_PHY_TEST_ACT,\n \n \t__ETHTOOL_MSG_USER_CNT,\n \tETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)\n@@ -954,6 +957,8 @@ enum {\n \tETHTOOL_MSG_RSS_CREATE_NTF,\n \tETHTOOL_MSG_RSS_DELETE_NTF,\n \tETHTOOL_MSG_MSE_GET_REPLY,\n+\tETHTOOL_MSG_INTF_CAPS_GET_REPLY,\n+\tETHTOOL_MSG_PHY_TEST_GET_REPLY,\n \n \t__ETHTOOL_MSG_KERNEL_CNT,\n \tETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)\n@@ -961,4 +966,103 @@ enum {\n \n #define ETHTOOL_MCGRP_MONITOR_NAME\t\"monitor\"\n \n+/* Interface component types */\n+enum intf_component {\n+\tINTF_COMPONENT_MAC,\n+\tINTF_COMPONENT_PHY,\n+\tINTF_COMPONENT_MODULE,\n+};\n+\n+/* Interface sublayer types */\n+enum intf_sublayer {\n+\tINTF_SUBLAYER_NONE,\n+\tINTF_SUBLAYER_RS,\n+\tINTF_SUBLAYER_PCS,\n+\tINTF_SUBLAYER_FEC,\n+\tINTF_SUBLAYER_PMA,\n+\tINTF_SUBLAYER_PMD,\n+};\n+\n+/* Attributes of a single interface block */\n+enum {\n+\tETHTOOL_A_INTF_BLOCK_ID,\n+\tETHTOOL_A_INTF_BLOCK_COMPONENT,\n+\tETHTOOL_A_INTF_BLOCK_SUBLAYER,\n+\tETHTOOL_A_INTF_BLOCK_INSTANCE,\n+\tETHTOOL_A_INTF_BLOCK_NAME,\n+\tETHTOOL_A_INTF_BLOCK_DEPTH,\n+\tETHTOOL_A_INTF_BLOCK_LANES,\n+\tETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,\n+\tETHTOOL_A_INTF_BLOCK_TX_PATTERNS,\n+\tETHTOOL_A_INTF_BLOCK_RX_PATTERNS,\n+\tETHTOOL_A_INTF_BLOCK_ERROR_INJECT,\n+\tETHTOOL_A_INTF_BLOCK_BERT,\n+\n+\t__ETHTOOL_A_INTF_BLOCK_CNT,\n+\tETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)\n+};\n+\n+/* Attributes of intf-caps-get message */\n+enum {\n+\tETHTOOL_A_INTF_CAPS_UNSPEC,\n+\tETHTOOL_A_INTF_CAPS_HEADER,\n+\tETHTOOL_A_INTF_CAPS_BLOCKS,\n+\n+\t__ETHTOOL_A_INTF_CAPS_CNT,\n+\tETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)\n+};\n+\n+/* PHY test pattern types */\n+enum phy_test_pattern {\n+\tPHY_TEST_PATTERN_OFF,\n+\tPHY_TEST_PATTERN_PRBS7,\n+\tPHY_TEST_PATTERN_PRBS9,\n+\tPHY_TEST_PATTERN_PRBS11,\n+\tPHY_TEST_PATTERN_PRBS13,\n+\tPHY_TEST_PATTERN_PRBS15,\n+\tPHY_TEST_PATTERN_PRBS23,\n+\tPHY_TEST_PATTERN_PRBS31,\n+\tPHY_TEST_PATTERN_SSPRQ,\n+\tPHY_TEST_PATTERN_PRBS13Q,\n+\tPHY_TEST_PATTERN_PRBS31Q,\n+\tPHY_TEST_PATTERN_SQUARE_NRZ,\n+\tPHY_TEST_PATTERN_SQUARE_PAM4,\n+\tPHY_TEST_PATTERN_TX_LINEARITY,\n+\tPHY_TEST_PATTERN_SCRAMBLED_IDLE,\n+\tPHY_TEST_PATTERN_K28_5,\n+\tPHY_TEST_PATTERN_K28_7,\n+\n+\t__PHY_TEST_PATTERN_COUNT,\n+\tPHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)\n+};\n+\n+/* PHY test BERT actions */\n+enum phy_test_action {\n+\tPHY_TEST_ACTION_NONE,\n+\tPHY_TEST_ACTION_START,\n+\tPHY_TEST_ACTION_STOP,\n+\n+\t__PHY_TEST_ACTION_COUNT,\n+\tPHY_TEST_ACTION_MAX = (__PHY_TEST_ACTION_COUNT - 1)\n+};\n+\n+/* Attributes of phy-test-get / phy-test-act messages */\n+enum {\n+\tETHTOOL_A_PHY_TEST_UNSPEC,\n+\tETHTOOL_A_PHY_TEST_HEADER,\n+\tETHTOOL_A_PHY_TEST_BLOCK_ID,\n+\tETHTOOL_A_PHY_TEST_LANE,\n+\tETHTOOL_A_PHY_TEST_TX_PATTERN,\n+\tETHTOOL_A_PHY_TEST_RX_PATTERN,\n+\tETHTOOL_A_PHY_TEST_BERT_ACTION,\n+\tETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,\n+\tETHTOOL_A_PHY_TEST_ACTIVE_TESTS,\n+\tETHTOOL_A_PHY_TEST_CHECKER_LOCK,\n+\tETHTOOL_A_PHY_TEST_ERROR_COUNT,\n+\tETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,\n+\n+\t__ETHTOOL_A_PHY_TEST_CNT,\n+\tETHTOOL_A_PHY_TEST_MAX = (__ETHTOOL_A_PHY_TEST_CNT - 1)\n+};\n+\n #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */\ndiff --git a/net/ethtool/Makefile b/net/ethtool/Makefile\nindex 629c10916670e..7dd2c5624cedf 100644\n--- a/net/ethtool/Makefile\n+++ b/net/ethtool/Makefile\n@@ -9,4 +9,4 @@ ethtool_nl-y\t:= netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \\\n \t\t   channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \\\n \t\t   tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \\\n \t\t   module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \\\n-\t\t   phy.o tsconfig.o mse.o\n+\t\t   phy.o tsconfig.o mse.o intf_caps.o phytest.o\ndiff --git a/net/ethtool/intf_caps.c b/net/ethtool/intf_caps.c\nnew file mode 100644\nindex 0000000000000..e45ccc4b88c9f\n--- /dev/null\n+++ b/net/ethtool/intf_caps.c\n@@ -0,0 +1,130 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+#include \"netlink.h\"\n+#include \"common.h\"\n+\n+struct intf_caps_req_info {\n+\tstruct ethnl_req_info base;\n+};\n+\n+struct intf_caps_reply_data {\n+\tstruct ethnl_reply_data base;\n+\tstruct ethtool_intf_caps caps;\n+};\n+\n+#define INTF_CAPS_REPDATA(__reply_base) \\\n+\tcontainer_of(__reply_base, struct intf_caps_reply_data, base)\n+\n+const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1] = {\n+\t[ETHTOOL_A_INTF_CAPS_HEADER] =\n+\t\tNLA_POLICY_NESTED(ethnl_header_policy),\n+};\n+\n+static int intf_caps_reply_size(const struct ethnl_req_info *req_base,\n+\t\t\t\tconst struct ethnl_reply_data *reply_base)\n+{\n+\tconst struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);\n+\tint len = 0;\n+\tu32 i;\n+\n+\tfor (i = 0; i \u003c data-\u003ecaps.num_blocks; i++) {\n+\t\tlen += nla_total_size(0);\t\t/* nested block */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* id */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* component */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* sublayer */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* instance */\n+\t\tlen += nla_total_size(INTF_BLOCK_NAME_LEN); /* name */\n+\t\tlen += nla_total_size(sizeof(u8));\t/* depth */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* lanes */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* loopback_supported */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* tx_patterns */\n+\t\tlen += nla_total_size(sizeof(u32));\t/* rx_patterns */\n+\t\tlen += nla_total_size(0);\t\t/* error_inject (flag) */\n+\t\tlen += nla_total_size(0);\t\t/* bert (flag) */\n+\t}\n+\n+\t/* outer BLOCKS nest */\n+\tlen += nla_total_size(0);\n+\n+\treturn len;\n+}\n+\n+static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,\n+\t\t\t\t  struct ethnl_reply_data *reply_base,\n+\t\t\t\t  const struct genl_info *info)\n+{\n+\tstruct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);\n+\tstruct net_device *dev = reply_base-\u003edev;\n+\n+\tif (!dev-\u003eethtool_ops-\u003eget_intf_caps)\n+\t\treturn -EOPNOTSUPP;\n+\n+\treturn dev-\u003eethtool_ops-\u003eget_intf_caps(dev, \u0026data-\u003ecaps);\n+}\n+\n+static int intf_caps_fill_reply(struct sk_buff *skb,\n+\t\t\t\tconst struct ethnl_req_info *req_base,\n+\t\t\t\tconst struct ethnl_reply_data *reply_base)\n+{\n+\tconst struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);\n+\tstruct nlattr *blocks_attr;\n+\tu32 i;\n+\n+\tblocks_attr = nla_nest_start(skb, ETHTOOL_A_INTF_CAPS_BLOCKS);\n+\tif (!blocks_attr)\n+\t\treturn -EMSGSIZE;\n+\n+\tfor (i = 0; i \u003c data-\u003ecaps.num_blocks; i++) {\n+\t\tconst struct ethtool_intf_block *b = \u0026data-\u003ecaps.blocks[i];\n+\t\tstruct nlattr *block_attr;\n+\n+\t\tblock_attr = nla_nest_start(skb, 0);\n+\t\tif (!block_attr)\n+\t\t\tgoto nla_put_failure;\n+\n+\t\tif (nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_ID, b-\u003eid) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_COMPONENT,\n+\t\t\t\tb-\u003ecomponent) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_SUBLAYER,\n+\t\t\t\tb-\u003esublayer) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_INSTANCE,\n+\t\t\t\tb-\u003einstance) ||\n+\t\t    nla_put_string(skb, ETHTOOL_A_INTF_BLOCK_NAME, b-\u003ename) ||\n+\t\t    nla_put_u8(skb, ETHTOOL_A_INTF_BLOCK_DEPTH, b-\u003edepth) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LANES, b-\u003elanes) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,\n+\t\t\t\tb-\u003eloopback_supported) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,\n+\t\t\t\tb-\u003esupported_tx_patterns) ||\n+\t\t    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,\n+\t\t\t\tb-\u003esupported_rx_patterns))\n+\t\t\tgoto nla_put_failure;\n+\t\tif (b-\u003eerror_inject_supported \u0026\u0026\n+\t\t    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_ERROR_INJECT))\n+\t\t\tgoto nla_put_failure;\n+\t\tif (b-\u003ebert_supported \u0026\u0026\n+\t\t    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_BERT))\n+\t\t\tgoto nla_put_failure;\n+\t\t\tgoto nla_put_failure;\n+\n+\t\tnla_nest_end(skb, block_attr);\n+\t}\n+\n+\tnla_nest_end(skb, blocks_attr);\n+\treturn 0;\n+\n+nla_put_failure:\n+\tnla_nest_cancel(skb, blocks_attr);\n+\treturn -EMSGSIZE;\n+}\n+\n+const struct ethnl_request_ops ethnl_intf_caps_request_ops = {\n+\t.request_cmd\t= ETHTOOL_MSG_INTF_CAPS_GET,\n+\t.reply_cmd\t= ETHTOOL_MSG_INTF_CAPS_GET_REPLY,\n+\t.hdr_attr\t= ETHTOOL_A_INTF_CAPS_HEADER,\n+\t.req_info_size\t= sizeof(struct intf_caps_req_info),\n+\t.reply_data_size = sizeof(struct intf_caps_reply_data),\n+\t.prepare_data\t= intf_caps_prepare_data,\n+\t.reply_size\t= intf_caps_reply_size,\n+\t.fill_reply\t= intf_caps_fill_reply,\n+};\ndiff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c\nindex 1af395b54330e..56e5c811454bb 100644\n--- a/net/ethtool/netlink.c\n+++ b/net/ethtool/netlink.c\n@@ -431,6 +431,8 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {\n \t[ETHTOOL_MSG_TSCONFIG_SET]\t= \u0026ethnl_tsconfig_request_ops,\n \t[ETHTOOL_MSG_PHY_GET]\t\t= \u0026ethnl_phy_request_ops,\n \t[ETHTOOL_MSG_MSE_GET]\t\t= \u0026ethnl_mse_request_ops,\n+\t[ETHTOOL_MSG_INTF_CAPS_GET]\t= \u0026ethnl_intf_caps_request_ops,\n+\t[ETHTOOL_MSG_PHY_TEST_GET]\t= \u0026ethnl_phy_test_request_ops,\n };\n \n static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)\n@@ -1572,6 +1574,28 @@ static const struct genl_ops ethtool_genl_ops[] = {\n \t\t.policy = ethnl_mse_get_policy,\n \t\t.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,\n \t},\n+\t{\n+\t\t.cmd\t= ETHTOOL_MSG_INTF_CAPS_GET,\n+\t\t.doit\t= ethnl_default_doit,\n+\t\t.start\t= ethnl_default_start,\n+\t\t.dumpit\t= ethnl_default_dumpit,\n+\t\t.done\t= ethnl_default_done,\n+\t\t.policy = ethnl_intf_caps_get_policy,\n+\t\t.maxattr = ARRAY_SIZE(ethnl_intf_caps_get_policy) - 1,\n+\t},\n+\t{\n+\t\t.cmd\t= ETHTOOL_MSG_PHY_TEST_GET,\n+\t\t.doit\t= ethnl_default_doit,\n+\t\t.policy = ethnl_phy_test_get_policy,\n+\t\t.maxattr = ARRAY_SIZE(ethnl_phy_test_get_policy) - 1,\n+\t},\n+\t{\n+\t\t.cmd\t= ETHTOOL_MSG_PHY_TEST_ACT,\n+\t\t.flags\t= GENL_UNS_ADMIN_PERM,\n+\t\t.doit\t= ethnl_act_phy_test,\n+\t\t.policy = ethnl_phy_test_act_policy,\n+\t\t.maxattr = ARRAY_SIZE(ethnl_phy_test_act_policy) - 1,\n+\t},\n };\n \n static const struct genl_multicast_group ethtool_nl_mcgrps[] = {\ndiff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h\nindex 3e969a070f9fe..1caba60d83c43 100644\n--- a/net/ethtool/netlink.h\n+++ b/net/ethtool/netlink.h\n@@ -474,6 +474,8 @@ extern const struct ethnl_request_ops ethnl_mm_request_ops;\n extern const struct ethnl_request_ops ethnl_phy_request_ops;\n extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;\n extern const struct ethnl_request_ops ethnl_mse_request_ops;\n+extern const struct ethnl_request_ops ethnl_intf_caps_request_ops;\n+extern const struct ethnl_request_ops ethnl_phy_test_request_ops;\n \n extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];\n extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];\n@@ -530,6 +532,11 @@ extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];\n extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];\n extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];\n extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];\n+extern const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];\n+extern const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1];\n+extern const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1];\n+\n+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info);\n \n int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);\n int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);\ndiff --git a/net/ethtool/phytest.c b/net/ethtool/phytest.c\nnew file mode 100644\nindex 0000000000000..dedcd036b7c2e\n--- /dev/null\n+++ b/net/ethtool/phytest.c\n@@ -0,0 +1,171 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+#include \"netlink.h\"\n+#include \"common.h\"\n+\n+struct phy_test_req_info {\n+\tstruct ethnl_req_info base;\n+};\n+\n+struct phy_test_reply_data {\n+\tstruct ethnl_reply_data base;\n+\tstruct ethtool_phy_test test;\n+};\n+\n+#define PHY_TEST_REPDATA(__reply_base) \\\n+\tcontainer_of(__reply_base, struct phy_test_reply_data, base)\n+\n+/* PHY_TEST_GET */\n+\n+const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1] = {\n+\t[ETHTOOL_A_PHY_TEST_HEADER]\t= NLA_POLICY_NESTED(ethnl_header_policy),\n+\t[ETHTOOL_A_PHY_TEST_BLOCK_ID]\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_LANE]\t= { .type = NLA_U32 },\n+};\n+\n+static int phy_test_reply_size(const struct ethnl_req_info *req_base,\n+\t\t\t       const struct ethnl_reply_data *reply_base)\n+{\n+\treturn nla_total_size(sizeof(u32)) +\t/* block_id */\n+\t       nla_total_size(sizeof(u32)) +\t/* lane */\n+\t       nla_total_size(sizeof(u32)) +\t/* tx_pattern */\n+\t       nla_total_size(sizeof(u32)) +\t/* rx_pattern */\n+\t       nla_total_size(sizeof(u32)) +\t/* active_tests */\n+\t       nla_total_size(sizeof(u8))  +\t/* checker_lock */\n+\t       nla_total_size(sizeof(u64)) +\t/* error_count */\n+\t       nla_total_size(sizeof(u64));\t/* total_bits_sent */\n+}\n+\n+static int phy_test_prepare_data(const struct ethnl_req_info *req_base,\n+\t\t\t\t struct ethnl_reply_data *reply_base,\n+\t\t\t\t const struct genl_info *info)\n+{\n+\tstruct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);\n+\tstruct net_device *dev = reply_base-\u003edev;\n+\tstruct nlattr **tb = info-\u003eattrs;\n+\n+\tif (!dev-\u003eethtool_ops-\u003eget_phy_test)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tmemset(\u0026data-\u003etest, 0, sizeof(data-\u003etest));\n+\n+\tif (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {\n+\t\tdata-\u003etest.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);\n+\t\tdata-\u003etest.cmd |= PHY_TEST_CMD_BLOCK_ID;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_LANE]) {\n+\t\tdata-\u003etest.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);\n+\t\tdata-\u003etest.cmd |= PHY_TEST_CMD_LANE;\n+\t}\n+\n+\treturn dev-\u003eethtool_ops-\u003eget_phy_test(dev, \u0026data-\u003etest);\n+}\n+\n+static int phy_test_fill_reply(struct sk_buff *skb,\n+\t\t\t       const struct ethnl_req_info *req_base,\n+\t\t\t       const struct ethnl_reply_data *reply_base)\n+{\n+\tconst struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);\n+\tconst struct ethtool_phy_test *t = \u0026data-\u003etest;\n+\n+\tif (nla_put_u32(skb, ETHTOOL_A_PHY_TEST_BLOCK_ID, t-\u003eblock_id) ||\n+\t    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_LANE, t-\u003elane) ||\n+\t    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_TX_PATTERN, t-\u003etx_pattern) ||\n+\t    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_RX_PATTERN, t-\u003erx_pattern) ||\n+\t    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,\n+\t\t\tt-\u003eactive_tests) ||\n+\t    nla_put_u8(skb, ETHTOOL_A_PHY_TEST_CHECKER_LOCK, t-\u003echecker_lock) ||\n+\t    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_ERROR_COUNT,\n+\t\t\t      t-\u003eerror_count, ETHTOOL_A_PHY_TEST_UNSPEC) ||\n+\t    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,\n+\t\t\t      t-\u003etotal_bits_sent, ETHTOOL_A_PHY_TEST_UNSPEC))\n+\t\treturn -EMSGSIZE;\n+\n+\treturn 0;\n+}\n+\n+const struct ethnl_request_ops ethnl_phy_test_request_ops = {\n+\t.request_cmd\t= ETHTOOL_MSG_PHY_TEST_GET,\n+\t.reply_cmd\t= ETHTOOL_MSG_PHY_TEST_GET_REPLY,\n+\t.hdr_attr\t= ETHTOOL_A_PHY_TEST_HEADER,\n+\t.req_info_size\t= sizeof(struct phy_test_req_info),\n+\t.reply_data_size = sizeof(struct phy_test_reply_data),\n+\t.prepare_data\t= phy_test_prepare_data,\n+\t.reply_size\t= phy_test_reply_size,\n+\t.fill_reply\t= phy_test_fill_reply,\n+};\n+\n+/* PHY_TEST_ACT */\n+\n+const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1] = {\n+\t[ETHTOOL_A_PHY_TEST_HEADER]\t\t= NLA_POLICY_NESTED(ethnl_header_policy),\n+\t[ETHTOOL_A_PHY_TEST_BLOCK_ID]\t\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_LANE]\t\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_TX_PATTERN]\t\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_RX_PATTERN]\t\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_BERT_ACTION]\t= { .type = NLA_U32 },\n+\t[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]\t= { .type = NLA_U32 },\n+};\n+\n+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)\n+{\n+\tstruct ethnl_req_info req_info = {};\n+\tstruct nlattr **tb = info-\u003eattrs;\n+\tstruct ethtool_phy_test test = {};\n+\tstruct net_device *dev;\n+\tint ret;\n+\n+\tret = ethnl_parse_header_dev_get(\u0026req_info,\n+\t\t\t\t\t tb[ETHTOOL_A_PHY_TEST_HEADER],\n+\t\t\t\t\t genl_info_net(info), info-\u003eextack,\n+\t\t\t\t\t true);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tdev = req_info.dev;\n+\n+\tif (!dev-\u003eethtool_ops-\u003eset_phy_test) {\n+\t\tret = -EOPNOTSUPP;\n+\t\tgoto out_dev;\n+\t}\n+\n+\tif (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {\n+\t\ttest.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);\n+\t\ttest.cmd |= PHY_TEST_CMD_BLOCK_ID;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_LANE]) {\n+\t\ttest.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);\n+\t\ttest.cmd |= PHY_TEST_CMD_LANE;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {\n+\t\ttest.tx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]);\n+\t\ttest.cmd |= PHY_TEST_CMD_TX_PATTERN;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {\n+\t\ttest.rx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]);\n+\t\ttest.cmd |= PHY_TEST_CMD_RX_PATTERN;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]) {\n+\t\ttest.bert_action = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]);\n+\t\ttest.cmd |= PHY_TEST_CMD_BERT_ACTION;\n+\t}\n+\tif (tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]) {\n+\t\ttest.inject_error_count =\n+\t\t\tnla_get_u32(tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]);\n+\t\ttest.cmd |= PHY_TEST_CMD_INJECT_COUNT;\n+\t}\n+\n+\trtnl_lock();\n+\tret = ethnl_ops_begin(dev);\n+\tif (ret \u003c 0)\n+\t\tgoto out_rtnl;\n+\n+\tret = dev-\u003eethtool_ops-\u003eset_phy_test(dev, \u0026test);\n+\tethnl_ops_complete(dev);\n+\n+out_rtnl:\n+\trtnl_unlock();\n+out_dev:\n+\tethnl_parse_header_dev_put(\u0026req_info);\n+\treturn ret;\n+}\n"
}

2/1 2026/08/25 05:06 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "ETHTOOL_NETLINK"
  ],
  "FocusSymbols": [
    "ethnl_act_phy_test"
  ],
  "Reasoning": "The patch adds new ethtool netlink messages (ETHTOOL_MSG_INTF_CAPS_GET, ETHTOOL_MSG_PHY_TEST_GET, ETHTOOL_MSG_PHY_TEST_ACT) and their corresponding netlink attribute policies. Even though no drivers currently implement the underlying ethtool operations, the netlink message parsing, policy validation, and initial handler functions (like ethnl_act_phy_test) are reachable from user-space and should be fuzzed.",
  "WorthFuzzing": true
}

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 d506dd108638e5d77d23f30efdf2cf7fa86dee9c
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Aug 25 05:06:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 5dd4d1b5d94bb..361eb3c665770 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -210,6 +210,55 @@ definitions:
       -
         name: discard
         value: 31
+  -
+    name: intf-component
+    type: enum
+    doc: Type of hardware component in the interface.
+    entries:
+      - mac
+      - phy
+      - module
+  -
+    name: intf-sublayer
+    type: enum
+    doc: 802.3 sublayer within a component.
+    entries:
+      - none
+      - rs
+      - pcs
+      - fec
+      - pma
+      - pmd
+  -
+    name: phy-test-pattern
+    type: enum
+    doc: PHY test pattern types for PRBS generation/checking.
+    entries:
+      - off
+      - prbs7
+      - prbs9
+      - prbs11
+      - prbs13
+      - prbs15
+      - prbs23
+      - prbs31
+      - ssprq
+      - prbs13q
+      - prbs31q
+      - square-nrz
+      - square-pam4
+      - tx-linearity
+      - scrambled-idle
+      - k28-5
+      - k28-7
+  -
+    name: phy-test-action
+    type: enum
+    doc: BERT control actions.
+    entries:
+      - none
+      - start
+      - stop
 
 attribute-sets:
   -
@@ -1905,6 +1954,115 @@ attribute-sets:
         name: link
         type: nest
         nested-attributes: mse-snapshot
+  -
+    name: intf-block
+    doc: A single functional block in the interface.
+    attributes:
+      -
+        name: id
+        type: u32
+      -
+        name: component
+        type: u32
+        enum: intf-component
+      -
+        name: sublayer
+        type: u32
+        enum: intf-sublayer
+      -
+        name: instance
+        type: u32
+        doc: |
+          Identifies the hardware entity when multiple instances of
+          the same component exist in the interface. The driver
+          assigns instance numbers sequentially based on position in
+          the physical path. Defaults to 0 when only one instance of
+          a given component exists.
+          For PHY: instance 0 is the internal/host-side PHY,
+          instance 1 is an external PHY further along the path
+          toward the line side.
+          For MAC: instance 0 is the host-facing MAC, instance 1 is
+          a secondary MAC further along the data path (e.g., behind
+          an embedded switch).
+      -
+        name: name
+        type: string
+      -
+        name: depth
+        type: u8
+      -
+        name: lanes
+        type: u32
+      -
+        name: loopback-supported
+        type: u32
+      -
+        name: supported-tx-patterns
+        type: u32
+      -
+        name: supported-rx-patterns
+        type: u32
+      -
+        name: error-inject-supported
+        type: flag
+      -
+        name: bert-supported
+        type: flag
+  -
+    name: intf-caps
+    doc: Interface capabilities message.
+    attributes:
+      -
+        name: header
+        type: nest
+        nested-attributes: header
+      -
+        name: blocks
+        type: nest
+        nested-attributes: intf-block
+        multi-attr: true
+  -
+    name: phy-test
+    doc: PHY test configuration and status.
+    attributes:
+      -
+        name: header
+        type: nest
+        nested-attributes: header
+      -
+        name: block-id
+        type: u32
+      -
+        name: lane
+        type: u32
+      -
+        name: tx-pattern
+        type: u32
+        enum: phy-test-pattern
+      -
+        name: rx-pattern
+        type: u32
+        enum: phy-test-pattern
+      -
+        name: bert-action
+        type: u32
+        enum: phy-test-action
+      -
+        name: inject-error-count
+        type: u32
+      -
+        name: active-tests
+        type: u32
+        doc: Bitmask of currently running tests (bit 0 = BERT).
+      -
+        name: checker-lock
+        type: u8
+      -
+        name: error-count
+        type: u64
+      -
+        name: total-bits-sent
+        type: u64
 
 operations:
   enum-model: directional
@@ -2859,6 +3017,54 @@ operations:
             - worst-channel
             - link
       dump: *mse-get-op
+    -
+      name: intf-caps-get
+      doc: Get interface capabilities (functional block layout).
+      attribute-set: intf-caps
+      do: &intf-caps-get-op
+        request:
+          attributes:
+            - header
+        reply:
+          attributes:
+            - header
+            - blocks
+      dump: *intf-caps-get-op
+    -
+      name: phy-test-get
+      doc: Get PHY test status (patterns, BERT counters).
+      attribute-set: phy-test
+      do:
+        request:
+          attributes:
+            - header
+            - block-id
+            - lane
+        reply:
+          attributes:
+            - header
+            - block-id
+            - lane
+            - tx-pattern
+            - rx-pattern
+            - active-tests
+            - checker-lock
+            - error-count
+            - total-bits-sent
+    -
+      name: phy-test-act
+      doc: Configure PHY test (set patterns, start/stop BERT, inject errors).
+      attribute-set: phy-test
+      do:
+        request:
+          attributes:
+            - header
+            - block-id
+            - lane
+            - tx-pattern
+            - rx-pattern
+            - bert-action
+            - inject-error-count
 
 mcast-groups:
   list:
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 12683b5d125e4..f9748e739f03e 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -1197,6 +1197,92 @@ struct kernel_ethtool_ts_info {
  * See &struct net_device and &struct net_device_ops for documentation
  * of the generic netdev features interface.
  */
+
+#define INTF_CAPS_MAX_BLOCKS	16
+#define INTF_BLOCK_NAME_LEN	32
+
+#define LOOPBACK_SUPPORT_LOCAL	BIT(0)
+#define LOOPBACK_SUPPORT_REMOTE	BIT(1)
+
+/**
+ * struct ethtool_intf_block - Single functional block in the interface
+ * @id: Unique block identifier
+ * @component: Hardware component (MAC, PHY, MODULE)
+ * @sublayer: 802.3 sublayer (RS, PCS, FEC, PMA, PMD, or NONE)
+ * @instance: Index when multiple instances of same component exist
+ * @name: Driver-chosen label
+ * @depth: Ordering within same (component, sublayer) tuple
+ * @lanes: Number of lanes
+ * @loopback_supported: Bitmask of supported loopback directions
+ * @supported_tx_patterns: Bitmask of patterns this block can generate
+ * @supported_rx_patterns: Bitmask of patterns this block can check
+ * @error_inject_supported: Block supports bit error injection
+ * @bert_supported: Block supports BERT counters
+ */
+struct ethtool_intf_block {
+	u32 id;
+	enum intf_component component;
+	enum intf_sublayer sublayer;
+	u32 instance;
+	char name[INTF_BLOCK_NAME_LEN];
+	u8 depth;
+	u32 lanes;
+	u32 loopback_supported;
+	u32 supported_tx_patterns;
+	u32 supported_rx_patterns;
+	bool error_inject_supported;
+	bool bert_supported;
+};
+
+/**
+ * struct ethtool_intf_caps - Interface capabilities (all blocks)
+ * @num_blocks: Number of valid entries in blocks[]
+ * @blocks: Array of functional blocks
+ */
+struct ethtool_intf_caps {
+	u32 num_blocks;
+	struct ethtool_intf_block blocks[INTF_CAPS_MAX_BLOCKS];
+};
+
+/* Bitmask of which ethtool_phy_test fields were explicitly specified */
+#define PHY_TEST_CMD_TX_PATTERN		BIT(0)
+#define PHY_TEST_CMD_RX_PATTERN		BIT(1)
+#define PHY_TEST_CMD_BERT_ACTION		BIT(2)
+#define PHY_TEST_CMD_INJECT_COUNT		BIT(3)
+#define PHY_TEST_CMD_LANE			BIT(4)
+#define PHY_TEST_CMD_BLOCK_ID			BIT(5)
+
+/* Bitmask of currently active tests (read-only) */
+#define PHY_TEST_ACTIVE_BERT		BIT(0)
+
+/**
+ * struct ethtool_phy_test - PHY test configuration and status
+ * @cmd: Bitmask of which fields are valid (PHY_TEST_CMD_*)
+ * @block_id: Block to operate on (from intf-caps-get)
+ * @lane: Lane number (0-based)
+ * @tx_pattern: TX pattern generator setting
+ * @rx_pattern: RX pattern checker setting
+ * @bert_action: BERT start/stop control
+ * @inject_error_count: Number of errors to inject
+ * @active_tests: Bitmask of running tests (PHY_TEST_ACTIVE_*)
+ * @checker_lock: RX checker lock status (read-only)
+ * @error_count: BERT error counter (read-only)
+ * @total_bits_sent: BERT total bits counter (read-only)
+ */
+struct ethtool_phy_test {
+	u32 cmd;
+	u32 block_id;
+	u32 lane;
+	enum phy_test_pattern tx_pattern;
+	enum phy_test_pattern rx_pattern;
+	enum phy_test_action bert_action;
+	u32 inject_error_count;
+	u32 active_tests;
+	u8 checker_lock;
+	u64 error_count;
+	u64 total_bits_sent;
+};
+
 struct ethtool_ops {
 	u32     supported_input_xfrm:8;
 	u32     cap_link_lanes_supported:1;
@@ -1354,6 +1440,12 @@ struct ethtool_ops {
 	int	(*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,
 			  struct netlink_ext_ack *extack);
 	void	(*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);
+	int	(*get_intf_caps)(struct net_device *dev,
+				 struct ethtool_intf_caps *caps);
+	int	(*get_phy_test)(struct net_device *dev,
+				struct ethtool_phy_test *test);
+	int	(*set_phy_test)(struct net_device *dev,
+				struct ethtool_phy_test *test);
 };
 
 int ethtool_check_ops(const struct ethtool_ops *ops);
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 8134baf7860f3..3102bb7ba337f 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -893,6 +893,9 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_ACT,
 	ETHTOOL_MSG_RSS_DELETE_ACT,
 	ETHTOOL_MSG_MSE_GET,
+	ETHTOOL_MSG_INTF_CAPS_GET,
+	ETHTOOL_MSG_PHY_TEST_GET,
+	ETHTOOL_MSG_PHY_TEST_ACT,
 
 	__ETHTOOL_MSG_USER_CNT,
 	ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -954,6 +957,8 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_NTF,
 	ETHTOOL_MSG_RSS_DELETE_NTF,
 	ETHTOOL_MSG_MSE_GET_REPLY,
+	ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+	ETHTOOL_MSG_PHY_TEST_GET_REPLY,
 
 	__ETHTOOL_MSG_KERNEL_CNT,
 	ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
@@ -961,4 +966,103 @@ enum {
 
 #define ETHTOOL_MCGRP_MONITOR_NAME	"monitor"
 
+/* Interface component types */
+enum intf_component {
+	INTF_COMPONENT_MAC,
+	INTF_COMPONENT_PHY,
+	INTF_COMPONENT_MODULE,
+};
+
+/* Interface sublayer types */
+enum intf_sublayer {
+	INTF_SUBLAYER_NONE,
+	INTF_SUBLAYER_RS,
+	INTF_SUBLAYER_PCS,
+	INTF_SUBLAYER_FEC,
+	INTF_SUBLAYER_PMA,
+	INTF_SUBLAYER_PMD,
+};
+
+/* Attributes of a single interface block */
+enum {
+	ETHTOOL_A_INTF_BLOCK_ID,
+	ETHTOOL_A_INTF_BLOCK_COMPONENT,
+	ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+	ETHTOOL_A_INTF_BLOCK_INSTANCE,
+	ETHTOOL_A_INTF_BLOCK_NAME,
+	ETHTOOL_A_INTF_BLOCK_DEPTH,
+	ETHTOOL_A_INTF_BLOCK_LANES,
+	ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+	ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_ERROR_INJECT,
+	ETHTOOL_A_INTF_BLOCK_BERT,
+
+	__ETHTOOL_A_INTF_BLOCK_CNT,
+	ETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)
+};
+
+/* Attributes of intf-caps-get message */
+enum {
+	ETHTOOL_A_INTF_CAPS_UNSPEC,
+	ETHTOOL_A_INTF_CAPS_HEADER,
+	ETHTOOL_A_INTF_CAPS_BLOCKS,
+
+	__ETHTOOL_A_INTF_CAPS_CNT,
+	ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
+};
+
+/* PHY test pattern types */
+enum phy_test_pattern {
+	PHY_TEST_PATTERN_OFF,
+	PHY_TEST_PATTERN_PRBS7,
+	PHY_TEST_PATTERN_PRBS9,
+	PHY_TEST_PATTERN_PRBS11,
+	PHY_TEST_PATTERN_PRBS13,
+	PHY_TEST_PATTERN_PRBS15,
+	PHY_TEST_PATTERN_PRBS23,
+	PHY_TEST_PATTERN_PRBS31,
+	PHY_TEST_PATTERN_SSPRQ,
+	PHY_TEST_PATTERN_PRBS13Q,
+	PHY_TEST_PATTERN_PRBS31Q,
+	PHY_TEST_PATTERN_SQUARE_NRZ,
+	PHY_TEST_PATTERN_SQUARE_PAM4,
+	PHY_TEST_PATTERN_TX_LINEARITY,
+	PHY_TEST_PATTERN_SCRAMBLED_IDLE,
+	PHY_TEST_PATTERN_K28_5,
+	PHY_TEST_PATTERN_K28_7,
+
+	__PHY_TEST_PATTERN_COUNT,
+	PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
+};
+
+/* PHY test BERT actions */
+enum phy_test_action {
+	PHY_TEST_ACTION_NONE,
+	PHY_TEST_ACTION_START,
+	PHY_TEST_ACTION_STOP,
+
+	__PHY_TEST_ACTION_COUNT,
+	PHY_TEST_ACTION_MAX = (__PHY_TEST_ACTION_COUNT - 1)
+};
+
+/* Attributes of phy-test-get / phy-test-act messages */
+enum {
+	ETHTOOL_A_PHY_TEST_UNSPEC,
+	ETHTOOL_A_PHY_TEST_HEADER,
+	ETHTOOL_A_PHY_TEST_BLOCK_ID,
+	ETHTOOL_A_PHY_TEST_LANE,
+	ETHTOOL_A_PHY_TEST_TX_PATTERN,
+	ETHTOOL_A_PHY_TEST_RX_PATTERN,
+	ETHTOOL_A_PHY_TEST_BERT_ACTION,
+	ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,
+	ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+	ETHTOOL_A_PHY_TEST_CHECKER_LOCK,
+	ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+	ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+
+	__ETHTOOL_A_PHY_TEST_CNT,
+	ETHTOOL_A_PHY_TEST_MAX = (__ETHTOOL_A_PHY_TEST_CNT - 1)
+};
+
 #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 629c10916670e..7dd2c5624cedf 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -9,4 +9,4 @@ ethtool_nl-y	:= netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
 		   channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
 		   tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
 		   module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \
-		   phy.o tsconfig.o mse.o
+		   phy.o tsconfig.o mse.o intf_caps.o phytest.o
diff --git a/net/ethtool/intf_caps.c b/net/ethtool/intf_caps.c
new file mode 100644
index 0000000000000..e45ccc4b88c9f
--- /dev/null
+++ b/net/ethtool/intf_caps.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct intf_caps_req_info {
+	struct ethnl_req_info base;
+};
+
+struct intf_caps_reply_data {
+	struct ethnl_reply_data base;
+	struct ethtool_intf_caps caps;
+};
+
+#define INTF_CAPS_REPDATA(__reply_base) \
+	container_of(__reply_base, struct intf_caps_reply_data, base)
+
+const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1] = {
+	[ETHTOOL_A_INTF_CAPS_HEADER] =
+		NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+static int intf_caps_reply_size(const struct ethnl_req_info *req_base,
+				const struct ethnl_reply_data *reply_base)
+{
+	const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	int len = 0;
+	u32 i;
+
+	for (i = 0; i < data->caps.num_blocks; i++) {
+		len += nla_total_size(0);		/* nested block */
+		len += nla_total_size(sizeof(u32));	/* id */
+		len += nla_total_size(sizeof(u32));	/* component */
+		len += nla_total_size(sizeof(u32));	/* sublayer */
+		len += nla_total_size(sizeof(u32));	/* instance */
+		len += nla_total_size(INTF_BLOCK_NAME_LEN); /* name */
+		len += nla_total_size(sizeof(u8));	/* depth */
+		len += nla_total_size(sizeof(u32));	/* lanes */
+		len += nla_total_size(sizeof(u32));	/* loopback_supported */
+		len += nla_total_size(sizeof(u32));	/* tx_patterns */
+		len += nla_total_size(sizeof(u32));	/* rx_patterns */
+		len += nla_total_size(0);		/* error_inject (flag) */
+		len += nla_total_size(0);		/* bert (flag) */
+	}
+
+	/* outer BLOCKS nest */
+	len += nla_total_size(0);
+
+	return len;
+}
+
+static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,
+				  struct ethnl_reply_data *reply_base,
+				  const struct genl_info *info)
+{
+	struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	struct net_device *dev = reply_base->dev;
+
+	if (!dev->ethtool_ops->get_intf_caps)
+		return -EOPNOTSUPP;
+
+	return dev->ethtool_ops->get_intf_caps(dev, &data->caps);
+}
+
+static int intf_caps_fill_reply(struct sk_buff *skb,
+				const struct ethnl_req_info *req_base,
+				const struct ethnl_reply_data *reply_base)
+{
+	const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	struct nlattr *blocks_attr;
+	u32 i;
+
+	blocks_attr = nla_nest_start(skb, ETHTOOL_A_INTF_CAPS_BLOCKS);
+	if (!blocks_attr)
+		return -EMSGSIZE;
+
+	for (i = 0; i < data->caps.num_blocks; i++) {
+		const struct ethtool_intf_block *b = &data->caps.blocks[i];
+		struct nlattr *block_attr;
+
+		block_attr = nla_nest_start(skb, 0);
+		if (!block_attr)
+			goto nla_put_failure;
+
+		if (nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_ID, b->id) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_COMPONENT,
+				b->component) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+				b->sublayer) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_INSTANCE,
+				b->instance) ||
+		    nla_put_string(skb, ETHTOOL_A_INTF_BLOCK_NAME, b->name) ||
+		    nla_put_u8(skb, ETHTOOL_A_INTF_BLOCK_DEPTH, b->depth) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LANES, b->lanes) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+				b->loopback_supported) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+				b->supported_tx_patterns) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+				b->supported_rx_patterns))
+			goto nla_put_failure;
+		if (b->error_inject_supported &&
+		    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_ERROR_INJECT))
+			goto nla_put_failure;
+		if (b->bert_supported &&
+		    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_BERT))
+			goto nla_put_failure;
+			goto nla_put_failure;
+
+		nla_nest_end(skb, block_attr);
+	}
+
+	nla_nest_end(skb, blocks_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, blocks_attr);
+	return -EMSGSIZE;
+}
+
+const struct ethnl_request_ops ethnl_intf_caps_request_ops = {
+	.request_cmd	= ETHTOOL_MSG_INTF_CAPS_GET,
+	.reply_cmd	= ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+	.hdr_attr	= ETHTOOL_A_INTF_CAPS_HEADER,
+	.req_info_size	= sizeof(struct intf_caps_req_info),
+	.reply_data_size = sizeof(struct intf_caps_reply_data),
+	.prepare_data	= intf_caps_prepare_data,
+	.reply_size	= intf_caps_reply_size,
+	.fill_reply	= intf_caps_fill_reply,
+};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 1af395b54330e..56e5c811454bb 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -431,6 +431,8 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
 	[ETHTOOL_MSG_TSCONFIG_SET]	= &ethnl_tsconfig_request_ops,
 	[ETHTOOL_MSG_PHY_GET]		= &ethnl_phy_request_ops,
 	[ETHTOOL_MSG_MSE_GET]		= &ethnl_mse_request_ops,
+	[ETHTOOL_MSG_INTF_CAPS_GET]	= &ethnl_intf_caps_request_ops,
+	[ETHTOOL_MSG_PHY_TEST_GET]	= &ethnl_phy_test_request_ops,
 };
 
 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1572,6 +1574,28 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.policy = ethnl_mse_get_policy,
 		.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
 	},
+	{
+		.cmd	= ETHTOOL_MSG_INTF_CAPS_GET,
+		.doit	= ethnl_default_doit,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
+		.policy = ethnl_intf_caps_get_policy,
+		.maxattr = ARRAY_SIZE(ethnl_intf_caps_get_policy) - 1,
+	},
+	{
+		.cmd	= ETHTOOL_MSG_PHY_TEST_GET,
+		.doit	= ethnl_default_doit,
+		.policy = ethnl_phy_test_get_policy,
+		.maxattr = ARRAY_SIZE(ethnl_phy_test_get_policy) - 1,
+	},
+	{
+		.cmd	= ETHTOOL_MSG_PHY_TEST_ACT,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= ethnl_act_phy_test,
+		.policy = ethnl_phy_test_act_policy,
+		.maxattr = ARRAY_SIZE(ethnl_phy_test_act_policy) - 1,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 3e969a070f9fe..1caba60d83c43 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -474,6 +474,8 @@ extern const struct ethnl_request_ops ethnl_mm_request_ops;
 extern const struct ethnl_request_ops ethnl_phy_request_ops;
 extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;
 extern const struct ethnl_request_ops ethnl_mse_request_ops;
+extern const struct ethnl_request_ops ethnl_intf_caps_request_ops;
+extern const struct ethnl_request_ops ethnl_phy_test_request_ops;
 
 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
 extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -530,6 +532,11 @@ extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];
 extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];
 extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];
 extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
+extern const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];
+extern const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1];
+extern const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1];
+
+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info);
 
 int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
diff --git a/net/ethtool/phytest.c b/net/ethtool/phytest.c
new file mode 100644
index 0000000000000..dedcd036b7c2e
--- /dev/null
+++ b/net/ethtool/phytest.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct phy_test_req_info {
+	struct ethnl_req_info base;
+};
+
+struct phy_test_reply_data {
+	struct ethnl_reply_data base;
+	struct ethtool_phy_test test;
+};
+
+#define PHY_TEST_REPDATA(__reply_base) \
+	container_of(__reply_base, struct phy_test_reply_data, base)
+
+/* PHY_TEST_GET */
+
+const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1] = {
+	[ETHTOOL_A_PHY_TEST_HEADER]	= NLA_POLICY_NESTED(ethnl_header_policy),
+	[ETHTOOL_A_PHY_TEST_BLOCK_ID]	= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_LANE]	= { .type = NLA_U32 },
+};
+
+static int phy_test_reply_size(const struct ethnl_req_info *req_base,
+			       const struct ethnl_reply_data *reply_base)
+{
+	return nla_total_size(sizeof(u32)) +	/* block_id */
+	       nla_total_size(sizeof(u32)) +	/* lane */
+	       nla_total_size(sizeof(u32)) +	/* tx_pattern */
+	       nla_total_size(sizeof(u32)) +	/* rx_pattern */
+	       nla_total_size(sizeof(u32)) +	/* active_tests */
+	       nla_total_size(sizeof(u8))  +	/* checker_lock */
+	       nla_total_size(sizeof(u64)) +	/* error_count */
+	       nla_total_size(sizeof(u64));	/* total_bits_sent */
+}
+
+static int phy_test_prepare_data(const struct ethnl_req_info *req_base,
+				 struct ethnl_reply_data *reply_base,
+				 const struct genl_info *info)
+{
+	struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+	struct net_device *dev = reply_base->dev;
+	struct nlattr **tb = info->attrs;
+
+	if (!dev->ethtool_ops->get_phy_test)
+		return -EOPNOTSUPP;
+
+	memset(&data->test, 0, sizeof(data->test));
+
+	if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+		data->test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
+		data->test.cmd |= PHY_TEST_CMD_BLOCK_ID;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+		data->test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+		data->test.cmd |= PHY_TEST_CMD_LANE;
+	}
+
+	return dev->ethtool_ops->get_phy_test(dev, &data->test);
+}
+
+static int phy_test_fill_reply(struct sk_buff *skb,
+			       const struct ethnl_req_info *req_base,
+			       const struct ethnl_reply_data *reply_base)
+{
+	const struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+	const struct ethtool_phy_test *t = &data->test;
+
+	if (nla_put_u32(skb, ETHTOOL_A_PHY_TEST_BLOCK_ID, t->block_id) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_LANE, t->lane) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_TX_PATTERN, t->tx_pattern) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_RX_PATTERN, t->rx_pattern) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+			t->active_tests) ||
+	    nla_put_u8(skb, ETHTOOL_A_PHY_TEST_CHECKER_LOCK, t->checker_lock) ||
+	    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+			      t->error_count, ETHTOOL_A_PHY_TEST_UNSPEC) ||
+	    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+			      t->total_bits_sent, ETHTOOL_A_PHY_TEST_UNSPEC))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+const struct ethnl_request_ops ethnl_phy_test_request_ops = {
+	.request_cmd	= ETHTOOL_MSG_PHY_TEST_GET,
+	.reply_cmd	= ETHTOOL_MSG_PHY_TEST_GET_REPLY,
+	.hdr_attr	= ETHTOOL_A_PHY_TEST_HEADER,
+	.req_info_size	= sizeof(struct phy_test_req_info),
+	.reply_data_size = sizeof(struct phy_test_reply_data),
+	.prepare_data	= phy_test_prepare_data,
+	.reply_size	= phy_test_reply_size,
+	.fill_reply	= phy_test_fill_reply,
+};
+
+/* PHY_TEST_ACT */
+
+const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1] = {
+	[ETHTOOL_A_PHY_TEST_HEADER]		= NLA_POLICY_NESTED(ethnl_header_policy),
+	[ETHTOOL_A_PHY_TEST_BLOCK_ID]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_LANE]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_TX_PATTERN]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_RX_PATTERN]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_BERT_ACTION]	= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]	= { .type = NLA_U32 },
+};
+
+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ethnl_req_info req_info = {};
+	struct nlattr **tb = info->attrs;
+	struct ethtool_phy_test test = {};
+	struct net_device *dev;
+	int ret;
+
+	ret = ethnl_parse_header_dev_get(&req_info,
+					 tb[ETHTOOL_A_PHY_TEST_HEADER],
+					 genl_info_net(info), info->extack,
+					 true);
+	if (ret < 0)
+		return ret;
+
+	dev = req_info.dev;
+
+	if (!dev->ethtool_ops->set_phy_test) {
+		ret = -EOPNOTSUPP;
+		goto out_dev;
+	}
+
+	if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+		test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
+		test.cmd |= PHY_TEST_CMD_BLOCK_ID;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+		test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+		test.cmd |= PHY_TEST_CMD_LANE;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {
+		test.tx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]);
+		test.cmd |= PHY_TEST_CMD_TX_PATTERN;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {
+		test.rx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]);
+		test.cmd |= PHY_TEST_CMD_RX_PATTERN;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]) {
+		test.bert_action = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]);
+		test.cmd |= PHY_TEST_CMD_BERT_ACTION;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]) {
+		test.inject_error_count =
+			nla_get_u32(tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]);
+		test.cmd |= PHY_TEST_CMD_INJECT_COUNT;
+	}
+
+	rtnl_lock();
+	ret = ethnl_ops_begin(dev);
+	if (ret < 0)
+		goto out_rtnl;
+
+	ret = dev->ethtool_ops->set_phy_test(dev, &test);
+	ethnl_ops_complete(dev);
+
+out_rtnl:
+	rtnl_unlock();
+out_dev:
+	ethnl_parse_header_dev_put(&req_info);
+	return ret;
+}

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

Args:
null

Results:
null

Tokens:
							input: 13013
							output: 21
							thoughts: 453
						

Thoughts:
**My Assessment of the Ethtool Netlink Patch**

Okay, so this patch introduces a few new ethtool netlink messages: `ETHTOOL_MSG_INTF_CAPS_GET`, `ETHTOOL_MSG_PHY_TEST_GET`, and `ETHTOOL_MSG_PHY_TEST_ACT`. These are specifically aimed at retrieving interface capabilities – sounds like a functional block diagram – and getting/setting PHY test status, which includes things like pattern configurations and BERT counters. Seems reasonable, given the direction we've been going with ethtool.

The changes are fairly concentrated: the core `net/ethtool/netlink.c`, the header in `net/ethtool/netlink.h`, the `Makefile`, and then new source files `net/ethtool/intf_caps.c` and `net/ethtool/phytest.c`. Of course, we have the corresponding updates to the ethtool kernel headers in `include/linux/ethtool.h` and the user-space API in `include/uapi/linux/ethtool_netlink_generated.h`. Standard stuff.

Let's make sure these new messages are actually being handled. Okay, they're added to `ethtool_genl_ops` within `net/ethtool/netlink.c`, that seems right. Looking at the implementation, `ETHTOOL_MSG_INTF_CAPS_GET` and `ETHTOOL_MSG_PHY_TEST_GET` are using `ethnl_default_doit` – which likely implies some generic netlink handling – while `ETHTOOL_MSG_PHY_TEST_ACT` specifically uses `ethnl_act_phy_test`.

Alright, let's track down the actual implementation. I'll need to look for `ethnl_act_phy_test` and also `ethnl_intf_caps_request_ops` and `ethnl_phy_test_request_ops`. Aha! They're defined within `net/ethtool/phytest.c` and `net/ethtool/intf_caps.c`, as expected.

One critical check: what about the config? I need to verify that ethtool netlink is actually enabled. `CONFIG_ETHTOOL_NETLINK` should do it. Wait, hold on. `CONFIG_ETHTOOL_NETLINK` is usually auto-enabled or depends on `CONFIG_NET`. I should quickly double-check that this dependency is as I expect. Let's look at `net/ethtool/Kconfig` or `net/Kconfig` to be sure. Nothing too tricky, hopefully.



4/2 2026/08/25 05:06 tool grepper
0m Args:
{
  "Expression": "config ETHTOOL_NETLINK"
}

Results:
{
  "Output": "net/Kconfig=507=config FAILOVER\n--\nnet/Kconfig-519-\nnet/Kconfig:520:config ETHTOOL_NETLINK\nnet/Kconfig-521-\tbool \"Netlink interface for ethtool\"\n"
}

5/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14146
							output: 32
							thoughts: 10
						

Thoughts:
Here's a summary of my thought process, as requested:

**Kconfig Dive**

Alright, time to dive in. My initial thought is to navigate directly to `net/Kconfig`. This is where the core network configuration options reside, so it’s the logical starting point for understanding and modifying network-related settings. I need to get a handle on the current configuration landscape. This is where the initial work happens.



6/2 2026/08/25 05:06 tool read-file
0m Args:
{
  "File": "net/Kconfig",
  "FirstLine": 515,
  "LineCount": 20
}

Results:
{
  "Contents": " 515:\t\t  failover netdev. This enables paravirtual drivers to use a\n 516:\t\t  VF as an accelerated low latency datapath. It also allows live\n 517:\t\t  migration of VMs with direct attached VFs by failing over to the\n 518:\t\t  paravirtual datapath when the VF is unplugged.\n 519:\t\n 520:\tconfig ETHTOOL_NETLINK\n 521:\t\tbool \"Netlink interface for ethtool\"\n 522:\t\tselect DIMLIB\n 523:\t\tdefault y\n 524:\t\thelp\n 525:\t\t  An alternative userspace interface for ethtool based on generic\n 526:\t\t  netlink. It provides better extensibility and some new features,\n 527:\t\t  e.g. notification messages.\n 528:\t\n 529:\tconfig NETDEV_ADDR_LIST_TEST\n 530:\t\ttristate \"Unit tests for device address list\"\n 531:\t\tdefault KUNIT_ALL_TESTS\n 532:\t\tdepends on KUNIT\n 533:\t\n 534:\tconfig NET_TEST\n"
}

7/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14580
							output: 21
							thoughts: 100
						

Thoughts:
**My Initial Assessment**

Okay, so this feature is on by default – good to know. Now, my immediate focus is on understanding the core functionality. I need to zero in on the relevant function calls. I see these as key: `ethnl_act_phy_test`, `intf_caps_prepare_data`, `intf_caps_fill_reply`, `phy_test_prepare_data`, and `phy_test_fill_reply`. These seem to be the primary building blocks related to either interface capabilities or the PHY testing itself. My next step, and it's a critical one, is to determine which drivers are actually *using* these functions. I need to locate the implementations of `get_intf_caps`, `get_phy_test`, and `set_phy_test`. That will reveal the specific hardware these features are impacting and how they're being used within that context. From there, I can understand the bigger picture and potential interaction with existing drivers.



8/2 2026/08/25 05:06 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "\\.get_intf_caps"
}

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

9/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14925
							output: 20
							thoughts: 19
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Analyzing `get_phy_test` and `set_phy_test`**

Okay, so I need to quickly refresh my memory on these two functions, `get_phy_test` and `set_phy_test`. My first instinct is to double-check their implementations. I want to see how these functions interface with the PHY layer and, more specifically, how they handle test mode configuration. Are they reading/writing directly to registers, or are they using some abstraction layer like a driver? If it's the latter, I need to understand the driver's underlying mechanisms.

What's the overall architecture? Are these functions responsible for configuring specific PHY test modes, or are they more general, providing a wrapper for different test scenarios? I need to look at any documentation or comments associated with them to get a clear understanding of their intended purpose and scope.

Furthermore, I need to consider any potential race conditions or synchronization issues, especially if these functions are being accessed concurrently, or from different contexts. I want to look closely at what these functions do, from the point of view of thread safety, and concurrency. I want to see if any mutexes, semaphores, or other synchronization primitives are being used to protect shared resources.

Finally, I need to check the error handling. How robust are these functions? Do they handle different types of PHY initialization failures, or invalid input parameters, or communication errors gracefully? Are there proper error codes or exception handling? Any unit tests for these functions? What are they testing?



10/2 2026/08/25 05:06 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "\\.get_phy_test"
}

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

11/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15311
							output: 38
							thoughts: 526
						

Thoughts:
**My Analysis of the ETHTOOL Netlink PHY Test Code**

Okay, so I'm looking at this ETHTOOL netlink code, specifically around the PHY test functionality. The initial thought is that since no current drivers implement these features yet, this area is a prime target for fuzzing. The netlink core itself is reachable, so that's where I need to focus.

The core parses the netlink messages and then calls driver operations if they exist. If the driver doesn't support the operation, it returns `-EOPNOTSUPP`, which is as expected. However, even if the driver ops are missing, some parsing logic in `ethnl_act_phy_test` and `phy_test_prepare_data` might be executed before the check.

I need to confirm that. Looking deeper, I see that `ethnl_default_doit` calls `prepare_data` which calls `dev->ethtool_ops->get_intf_caps`. And if this is NULL, `-EOPNOTSUPP` is returned. So it makes sense that `intf_caps_fill_reply` won't be reached.

But wait, `ethnl_act_phy_test` does some attribute parsing *before* the check for `dev->ethtool_ops->set_phy_test`. Okay, let's examine `ethnl_act_phy_test`.

```c
	ret = ethnl_parse_header_dev_get(&req_info,
					 tb[ETHTOOL_A_PHY_TEST_HEADER],
					 genl_info_net(info), info->extack,
					 true);
	if (ret < 0)
		return ret;

	dev = req_info.dev;

	if (!dev->ethtool_ops->set_phy_test) {
		ret = -EOPNOTSUPP;
		goto out_dev;
	}
```

Okay, I see. `ethnl_act_phy_test` checks for `set_phy_test` *immediately* after retrieving the device.

```c
	if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
		test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
		test.cmd |= PHY_TEST_CMD_BLOCK_ID;
	}
```

And this attribute parsing is done **after** the check:

```c
	if (!dev->ethtool_ops->set_phy_test) {
		ret = -EOPNOTSUPP;
		goto out_dev;
	}
```

So it looks like if no driver implements `set_phy_test`, that parsing logic isn't even reached. Hmm. I better double-check `ethnl_act_phy_test` to be sure I haven't missed something crucial. This is important to get right before I start fuzzing.



12/2 2026/08/25 05:06 tool read-file
0m Args:
{
  "File": "net/ethtool/phytest.c",
  "FirstLine": 109,
  "LineCount": 40
}

Results:
{
  "Contents": " 109:\t\n 110:\tint ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)\n 111:\t{\n 112:\t\tstruct ethnl_req_info req_info = {};\n 113:\t\tstruct nlattr **tb = info-\u003eattrs;\n 114:\t\tstruct ethtool_phy_test test = {};\n 115:\t\tstruct net_device *dev;\n 116:\t\tint ret;\n 117:\t\n 118:\t\tret = ethnl_parse_header_dev_get(\u0026req_info,\n 119:\t\t\t\t\t\t tb[ETHTOOL_A_PHY_TEST_HEADER],\n 120:\t\t\t\t\t\t genl_info_net(info), info-\u003eextack,\n 121:\t\t\t\t\t\t true);\n 122:\t\tif (ret \u003c 0)\n 123:\t\t\treturn ret;\n 124:\t\n 125:\t\tdev = req_info.dev;\n 126:\t\n 127:\t\tif (!dev-\u003eethtool_ops-\u003eset_phy_test) {\n 128:\t\t\tret = -EOPNOTSUPP;\n 129:\t\t\tgoto out_dev;\n 130:\t\t}\n 131:\t\n 132:\t\tif (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {\n 133:\t\t\ttest.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);\n 134:\t\t\ttest.cmd |= PHY_TEST_CMD_BLOCK_ID;\n 135:\t\t}\n 136:\t\tif (tb[ETHTOOL_A_PHY_TEST_LANE]) {\n 137:\t\t\ttest.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);\n 138:\t\t\ttest.cmd |= PHY_TEST_CMD_LANE;\n 139:\t\t}\n 140:\t\tif (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {\n 141:\t\t\ttest.tx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]);\n 142:\t\t\ttest.cmd |= PHY_TEST_CMD_TX_PATTERN;\n 143:\t\t}\n 144:\t\tif (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {\n 145:\t\t\ttest.rx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]);\n 146:\t\t\ttest.cmd |= PHY_TEST_CMD_RX_PATTERN;\n 147:\t\t}\n 148:\t\tif (tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]) {\n"
}

13/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17268
							output: 19
							thoughts: 196
						

Thoughts:
**Fuzzing Potential: Ethtool Netlink Interface**

Okay, so the parsing itself happens *after* the initial checks, which is good. But wait, the Netlink policy parsing, handled by the generic Netlink core, is definitely reachable. I know the policies are defined in `ethnl_phy_test_act_policy`, `ethnl_phy_test_get_policy`, and `ethnl_intf_caps_get_policy`, and `ethnl_act_phy_test` gets us that far.

So, is this worth fuzzing? Absolutely, yes! These are new Netlink messages and policies, perfect for Syzkaller. It can poke at the message formats. Even if there's no driver actually *using* this functionality yet, the parsing and initial handlers are definitely there and reachable.

Hmm, on second thought, if no driver implements these functions, the coverage from fuzzing will be very limited. It would essentially just be testing the parsing logic itself. But, it is still new kernel code, and any coverage is valuable. Let's not write this off completely. I should quickly check the current kernel tree to see if any driver was added to implement this feature. Perhaps a related commit in the same series snuck a driver in. Gotta check it out!



14/2 2026/08/25 05:06 tool grepper
0m Args:
{
  "Expression": "set_phy_test"
}

Results:
{
  "Output": "drivers/gpu/drm/display/drm_dp_helper.c=3299=EXPORT_SYMBOL(drm_dp_get_phy_test_pattern);\n--\ndrivers/gpu/drm/display/drm_dp_helper.c-3301-/**\ndrivers/gpu/drm/display/drm_dp_helper.c:3302: * drm_dp_set_phy_test_pattern() - set the pattern to the sink.\ndrivers/gpu/drm/display/drm_dp_helper.c-3303- * @aux: DisplayPort AUX channel\n--\ndrivers/gpu/drm/display/drm_dp_helper.c-3308- */\ndrivers/gpu/drm/display/drm_dp_helper.c:3309:int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,\ndrivers/gpu/drm/display/drm_dp_helper.c-3310-\t\t\t\tstruct drm_dp_phy_test_params *data, u8 dp_rev)\n--\ndrivers/gpu/drm/display/drm_dp_helper.c-3334-}\ndrivers/gpu/drm/display/drm_dp_helper.c:3335:EXPORT_SYMBOL(drm_dp_set_phy_test_pattern);\ndrivers/gpu/drm/display/drm_dp_helper.c-3336-\n--\ndrivers/gpu/drm/i915/display/intel_dp_test.c=306=static void intel_dp_process_phy_request(struct intel_dp *intel_dp,\n--\ndrivers/gpu/drm/i915/display/intel_dp_test.c-330-\ndrivers/gpu/drm/i915/display/intel_dp_test.c:331:\tdrm_dp_set_phy_test_pattern(\u0026intel_dp-\u003eaux, data,\ndrivers/gpu/drm/i915/display/intel_dp_test.c-332-\t\t\t\t    intel_dp-\u003edpcd[DP_DPCD_REV]);\n--\ninclude/drm/display/drm_dp_helper.h=989=int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,\ninclude/drm/display/drm_dp_helper.h-990-\t\t\t\tstruct drm_dp_phy_test_params *data);\ninclude/drm/display/drm_dp_helper.h:991:int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,\ninclude/drm/display/drm_dp_helper.h-992-\t\t\t\tstruct drm_dp_phy_test_params *data, u8 dp_rev);\n--\ninclude/linux/ethtool.h=1286=struct ethtool_ops {\n--\ninclude/linux/ethtool.h-1446-\t\t\t\tstruct ethtool_phy_test *test);\ninclude/linux/ethtool.h:1447:\tint\t(*set_phy_test)(struct net_device *dev,\ninclude/linux/ethtool.h-1448-\t\t\t\tstruct ethtool_phy_test *test);\n--\nnet/ethtool/phytest.c=110=int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)\n--\nnet/ethtool/phytest.c-126-\nnet/ethtool/phytest.c:127:\tif (!dev-\u003eethtool_ops-\u003eset_phy_test) {\nnet/ethtool/phytest.c-128-\t\tret = -EOPNOTSUPP;\n--\nnet/ethtool/phytest.c-162-\nnet/ethtool/phytest.c:163:\tret = dev-\u003eethtool_ops-\u003eset_phy_test(dev, \u0026test);\nnet/ethtool/phytest.c-164-\tethnl_ops_complete(dev);\n"
}

15/2 2026/08/25 05:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18742
							output: 20
							thoughts: 43
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Initial Assessment: Missing Implementations**

Alright, so I'm taking a look at this code and, as I suspected, none of the drivers are actually implementing `set_phy_test`, `get_phy_test`, or `get_intf_caps` at this point. That's a bit of a red flag, but not necessarily unexpected given the stage of development. We might be relying on default or placeholder behavior for now. I'd better confirm that the interface capabilities are correctly established. Let me jump in and have a quick look at `get_intf_caps`.



16/2 2026/08/25 05:07 tool grepper
0m Args:
{
  "Expression": "get_intf_caps"
}

Results:
{
  "Output": "include/linux/ethtool.h=1286=struct ethtool_ops {\n--\ninclude/linux/ethtool.h-1442-\tvoid\t(*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);\ninclude/linux/ethtool.h:1443:\tint\t(*get_intf_caps)(struct net_device *dev,\ninclude/linux/ethtool.h-1444-\t\t\t\t struct ethtool_intf_caps *caps);\n--\nnet/ethtool/intf_caps.c=52=static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,\n--\nnet/ethtool/intf_caps.c-58-\nnet/ethtool/intf_caps.c:59:\tif (!dev-\u003eethtool_ops-\u003eget_intf_caps)\nnet/ethtool/intf_caps.c-60-\t\treturn -EOPNOTSUPP;\nnet/ethtool/intf_caps.c-61-\nnet/ethtool/intf_caps.c:62:\treturn dev-\u003eethtool_ops-\u003eget_intf_caps(dev, \u0026data-\u003ecaps);\nnet/ethtool/intf_caps.c-63-}\n"
}

17/2 2026/08/25 05:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19289
							output: 156
							thoughts: 1048
						

Thoughts:
**Analysis of the Proposed Ethtool Netlink Additions for Fuzzing**

Okay, so this is about the new ethtool netlink additions. The core of it. My initial thought is, is this even worth fuzzing? But wait, this introduces new netlink message types and policies, which *syzkaller* will definitely go after. That's a good sign. The parsing of these netlink messages is reachable. I mean, we're talking about `ethnl_act_phy_test` specifically – the function call that parses the header, retrieves the device, and then does a check of `set_phy_test` and related netlink policies. I am thinking that `ethnl_parse_header_dev_get` is reachable which gives me confidence.

The reachable netlink policies are `ethnl_phy_test_act_policy`, `ethnl_phy_test_get_policy`, and `ethnl_intf_caps_get_policy`. These will be used by the netlink core to validate attributes. This *is* new netlink API, and that's usually good for fuzzing. So yeah, I'm thinking, "WorthFuzzing = true".

Now, let's nail down the focus symbols. The obvious ones are `ethnl_act_phy_test`, `intf_caps_prepare_data`, and `phy_test_prepare_data`. But hold on, `intf_caps_prepare_data` and `phy_test_prepare_data` are actually called by `ethnl_default_doit`. Let me double-check for other relevant functions... Ah, no, the others, like `intf_caps_reply_size`, `intf_caps_fill_reply`, `phy_test_reply_size`, and `phy_test_fill_reply`, won't be reached because `prepare_data` returns `-EOPNOTSUPP`. So, back to focusing on `ethnl_act_phy_test`, `intf_caps_prepare_data`, and `phy_test_prepare_data`.

Hang on, `ethnl_act_phy_test` itself might just be returning `-EOPNOTSUPP` but this needs fuzzing! The netlink policy parsing is happening *before* the `doit` function is called. The kernel is going to parse those attributes according to the new policies, and *syzkaller* can definitely fuzz those netlink messages. Okay, definitely "WorthFuzzing = true".

Let's list the focus symbols:
`ethnl_act_phy_test`
`intf_caps_prepare_data`
`phy_test_prepare_data`

But wait, `intf_caps_prepare_data` and `phy_test_prepare_data` are *static* and the instructions say to prioritize non-static entry points. Okay, `ethnl_act_phy_test` is the one to focus on. It's non-static, so, that's perfect. The others are static, but they're function pointers within the `ethnl_intf_caps_request_ops` and `ethnl_phy_test_request_ops` structures. So, I can just use `ethnl_act_phy_test` as the focus symbol.

Let me verify if there are any other non-static functions... nope, just `ethnl_act_phy_test`.

Regarding configuration, `ETHTOOL_NETLINK` is enabled by default, but I'll add it to be safe. It's a bool, so no harm in being explicit.

I should double-check that `ethnl_act_phy_test` is indeed non-static, just to be sure... Yes, it's defined in `net/ethtool/phytest.c` and declared in `net/ethtool/netlink.h`.

Are there any other functions? No.

Alright, let's review the instructions to make sure I'm not missing anything. I need to make sure this isn't just a documentation or comment change or something that's unreachable. No, this adds new netlink messages to the *core* ethtool netlink interface. It's definitely reachable. So: `WorthFuzzing = true`.

**Focus symbol:**
`ethnl_act_phy_test`

**Configs:**
`ETHTOOL_NETLINK`

**Reasoning:** The patch introduces new ethtool netlink messages (`ETHTOOL_MSG_INTF_CAPS_GET`, `ETHTOOL_MSG_PHY_TEST_GET`, `ETHTOOL_MSG_PHY_TEST_ACT`) and their corresponding attribute policies


18/2 2026/08/25 05:07 tool set-results
0m Args:
{
  "EnableConfigs": [
    "ETHTOOL_NETLINK"
  ],
  "FocusSymbols": [
    "ethnl_act_phy_test"
  ],
  "Reasoning": "The patch adds new ethtool netlink messages (ETHTOOL_MSG_INTF_CAPS_GET, ETHTOOL_MSG_PHY_TEST_GET, ETHTOOL_MSG_PHY_TEST_ACT) and their corresponding netlink attribute policies. Even though no drivers currently implement the underlying ethtool operations, the netlink message parsing, policy validation, and initial handler functions (like ethnl_act_phy_test) are reachable from user-space and should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "ETHTOOL_NETLINK"
  ],
  "FocusSymbols": [
    "ethnl_act_phy_test"
  ],
  "Reasoning": "The patch adds new ethtool netlink messages (ETHTOOL_MSG_INTF_CAPS_GET, ETHTOOL_MSG_PHY_TEST_GET, ETHTOOL_MSG_PHY_TEST_ACT) and their corresponding netlink attribute policies. Even though no drivers currently implement the underlying ethtool operations, the netlink message parsing, policy validation, and initial handler functions (like ethnl_act_phy_test) are reachable from user-space and should be fuzzed.",
  "WorthFuzzing": true
}

19/1 2026/08/25 05:07 action If
2m Args:
{
  "WorthFuzzing": true
}

Results:
null

20/2 2026/08/25 05:07 agent kmsan-evaluator
2m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces new ethtool netlink commands (`INTF_CAPS_GET`, `PHY_TEST_GET`, `PHY_TEST_ACT`) to query interface capabilities and configure PHY tests. The data structures used to pass information between the ethtool core and the drivers (`struct ethtool_intf_caps` and `struct ethtool_phy_test`) are allocated dynamically.\n\nHowever, the ethtool netlink framework explicitly zero-initializes the reply data structures via `ethnl_init_reply_data()` (which calls `memset(reply_data, 0, ops-\u003ereply_data_size)`) before passing them to the driver callbacks (e.g., `get_intf_caps`). Similarly, `phy_test_prepare_data()` explicitly calls `memset(\u0026data-\u003etest, 0, sizeof(data-\u003etest))`, and `ethnl_act_phy_test()` initializes its local struct with `= {}`.\n\nBecause all structures are fully zero-initialized before use, there is no risk of leaking uninitialized kernel stack or heap memory to user space, even if a driver fails to populate all fields. Furthermore, the netlink response is constructed by serializing individual fields using `nla_put_u32`, `nla_put_string`, etc., rather than copying raw structs, which inherently avoids leaking struct padding.\n\nAny potential issues, such as a driver writing past the end of the `blocks` array or returning an invalid `num_blocks` leading to out-of-bounds reads, would be reliably detected by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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 d506dd108638e5d77d23f30efdf2cf7fa86dee9c
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Aug 25 05:06:14 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 5dd4d1b5d94bb..361eb3c665770 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -210,6 +210,55 @@ definitions:
       -
         name: discard
         value: 31
+  -
+    name: intf-component
+    type: enum
+    doc: Type of hardware component in the interface.
+    entries:
+      - mac
+      - phy
+      - module
+  -
+    name: intf-sublayer
+    type: enum
+    doc: 802.3 sublayer within a component.
+    entries:
+      - none
+      - rs
+      - pcs
+      - fec
+      - pma
+      - pmd
+  -
+    name: phy-test-pattern
+    type: enum
+    doc: PHY test pattern types for PRBS generation/checking.
+    entries:
+      - off
+      - prbs7
+      - prbs9
+      - prbs11
+      - prbs13
+      - prbs15
+      - prbs23
+      - prbs31
+      - ssprq
+      - prbs13q
+      - prbs31q
+      - square-nrz
+      - square-pam4
+      - tx-linearity
+      - scrambled-idle
+      - k28-5
+      - k28-7
+  -
+    name: phy-test-action
+    type: enum
+    doc: BERT control actions.
+    entries:
+      - none
+      - start
+      - stop
 
 attribute-sets:
   -
@@ -1905,6 +1954,115 @@ attribute-sets:
         name: link
         type: nest
         nested-attributes: mse-snapshot
+  -
+    name: intf-block
+    doc: A single functional block in the interface.
+    attributes:
+      -
+        name: id
+        type: u32
+      -
+        name: component
+        type: u32
+        enum: intf-component
+      -
+        name: sublayer
+        type: u32
+        enum: intf-sublayer
+      -
+        name: instance
+        type: u32
+        doc: |
+          Identifies the hardware entity when multiple instances of
+          the same component exist in the interface. The driver
+          assigns instance numbers sequentially based on position in
+          the physical path. Defaults to 0 when only one instance of
+          a given component exists.
+          For PHY: instance 0 is the internal/host-side PHY,
+          instance 1 is an external PHY further along the path
+          toward the line side.
+          For MAC: instance 0 is the host-facing MAC, instance 1 is
+          a secondary MAC further along the data path (e.g., behind
+          an embedded switch).
+      -
+        name: name
+        type: string
+      -
+        name: depth
+        type: u8
+      -
+        name: lanes
+        type: u32
+      -
+        name: loopback-supported
+        type: u32
+      -
+        name: supported-tx-patterns
+        type: u32
+      -
+        name: supported-rx-patterns
+        type: u32
+      -
+        name: error-inject-supported
+        type: flag
+      -
+        name: bert-supported
+        type: flag
+  -
+    name: intf-caps
+    doc: Interface capabilities message.
+    attributes:
+      -
+        name: header
+        type: nest
+        nested-attributes: header
+      -
+        name: blocks
+        type: nest
+        nested-attributes: intf-block
+        multi-attr: true
+  -
+    name: phy-test
+    doc: PHY test configuration and status.
+    attributes:
+      -
+        name: header
+        type: nest
+        nested-attributes: header
+      -
+        name: block-id
+        type: u32
+      -
+        name: lane
+        type: u32
+      -
+        name: tx-pattern
+        type: u32
+        enum: phy-test-pattern
+      -
+        name: rx-pattern
+        type: u32
+        enum: phy-test-pattern
+      -
+        name: bert-action
+        type: u32
+        enum: phy-test-action
+      -
+        name: inject-error-count
+        type: u32
+      -
+        name: active-tests
+        type: u32
+        doc: Bitmask of currently running tests (bit 0 = BERT).
+      -
+        name: checker-lock
+        type: u8
+      -
+        name: error-count
+        type: u64
+      -
+        name: total-bits-sent
+        type: u64
 
 operations:
   enum-model: directional
@@ -2859,6 +3017,54 @@ operations:
             - worst-channel
             - link
       dump: *mse-get-op
+    -
+      name: intf-caps-get
+      doc: Get interface capabilities (functional block layout).
+      attribute-set: intf-caps
+      do: &intf-caps-get-op
+        request:
+          attributes:
+            - header
+        reply:
+          attributes:
+            - header
+            - blocks
+      dump: *intf-caps-get-op
+    -
+      name: phy-test-get
+      doc: Get PHY test status (patterns, BERT counters).
+      attribute-set: phy-test
+      do:
+        request:
+          attributes:
+            - header
+            - block-id
+            - lane
+        reply:
+          attributes:
+            - header
+            - block-id
+            - lane
+            - tx-pattern
+            - rx-pattern
+            - active-tests
+            - checker-lock
+            - error-count
+            - total-bits-sent
+    -
+      name: phy-test-act
+      doc: Configure PHY test (set patterns, start/stop BERT, inject errors).
+      attribute-set: phy-test
+      do:
+        request:
+          attributes:
+            - header
+            - block-id
+            - lane
+            - tx-pattern
+            - rx-pattern
+            - bert-action
+            - inject-error-count
 
 mcast-groups:
   list:
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 12683b5d125e4..f9748e739f03e 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -1197,6 +1197,92 @@ struct kernel_ethtool_ts_info {
  * See &struct net_device and &struct net_device_ops for documentation
  * of the generic netdev features interface.
  */
+
+#define INTF_CAPS_MAX_BLOCKS	16
+#define INTF_BLOCK_NAME_LEN	32
+
+#define LOOPBACK_SUPPORT_LOCAL	BIT(0)
+#define LOOPBACK_SUPPORT_REMOTE	BIT(1)
+
+/**
+ * struct ethtool_intf_block - Single functional block in the interface
+ * @id: Unique block identifier
+ * @component: Hardware component (MAC, PHY, MODULE)
+ * @sublayer: 802.3 sublayer (RS, PCS, FEC, PMA, PMD, or NONE)
+ * @instance: Index when multiple instances of same component exist
+ * @name: Driver-chosen label
+ * @depth: Ordering within same (component, sublayer) tuple
+ * @lanes: Number of lanes
+ * @loopback_supported: Bitmask of supported loopback directions
+ * @supported_tx_patterns: Bitmask of patterns this block can generate
+ * @supported_rx_patterns: Bitmask of patterns this block can check
+ * @error_inject_supported: Block supports bit error injection
+ * @bert_supported: Block supports BERT counters
+ */
+struct ethtool_intf_block {
+	u32 id;
+	enum intf_component component;
+	enum intf_sublayer sublayer;
+	u32 instance;
+	char name[INTF_BLOCK_NAME_LEN];
+	u8 depth;
+	u32 lanes;
+	u32 loopback_supported;
+	u32 supported_tx_patterns;
+	u32 supported_rx_patterns;
+	bool error_inject_supported;
+	bool bert_supported;
+};
+
+/**
+ * struct ethtool_intf_caps - Interface capabilities (all blocks)
+ * @num_blocks: Number of valid entries in blocks[]
+ * @blocks: Array of functional blocks
+ */
+struct ethtool_intf_caps {
+	u32 num_blocks;
+	struct ethtool_intf_block blocks[INTF_CAPS_MAX_BLOCKS];
+};
+
+/* Bitmask of which ethtool_phy_test fields were explicitly specified */
+#define PHY_TEST_CMD_TX_PATTERN		BIT(0)
+#define PHY_TEST_CMD_RX_PATTERN		BIT(1)
+#define PHY_TEST_CMD_BERT_ACTION		BIT(2)
+#define PHY_TEST_CMD_INJECT_COUNT		BIT(3)
+#define PHY_TEST_CMD_LANE			BIT(4)
+#define PHY_TEST_CMD_BLOCK_ID			BIT(5)
+
+/* Bitmask of currently active tests (read-only) */
+#define PHY_TEST_ACTIVE_BERT		BIT(0)
+
+/**
+ * struct ethtool_phy_test - PHY test configuration and status
+ * @cmd: Bitmask of which fields are valid (PHY_TEST_CMD_*)
+ * @block_id: Block to operate on (from intf-caps-get)
+ * @lane: Lane number (0-based)
+ * @tx_pattern: TX pattern generator setting
+ * @rx_pattern: RX pattern checker setting
+ * @bert_action: BERT start/stop control
+ * @inject_error_count: Number of errors to inject
+ * @active_tests: Bitmask of running tests (PHY_TEST_ACTIVE_*)
+ * @checker_lock: RX checker lock status (read-only)
+ * @error_count: BERT error counter (read-only)
+ * @total_bits_sent: BERT total bits counter (read-only)
+ */
+struct ethtool_phy_test {
+	u32 cmd;
+	u32 block_id;
+	u32 lane;
+	enum phy_test_pattern tx_pattern;
+	enum phy_test_pattern rx_pattern;
+	enum phy_test_action bert_action;
+	u32 inject_error_count;
+	u32 active_tests;
+	u8 checker_lock;
+	u64 error_count;
+	u64 total_bits_sent;
+};
+
 struct ethtool_ops {
 	u32     supported_input_xfrm:8;
 	u32     cap_link_lanes_supported:1;
@@ -1354,6 +1440,12 @@ struct ethtool_ops {
 	int	(*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,
 			  struct netlink_ext_ack *extack);
 	void	(*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);
+	int	(*get_intf_caps)(struct net_device *dev,
+				 struct ethtool_intf_caps *caps);
+	int	(*get_phy_test)(struct net_device *dev,
+				struct ethtool_phy_test *test);
+	int	(*set_phy_test)(struct net_device *dev,
+				struct ethtool_phy_test *test);
 };
 
 int ethtool_check_ops(const struct ethtool_ops *ops);
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 8134baf7860f3..3102bb7ba337f 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -893,6 +893,9 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_ACT,
 	ETHTOOL_MSG_RSS_DELETE_ACT,
 	ETHTOOL_MSG_MSE_GET,
+	ETHTOOL_MSG_INTF_CAPS_GET,
+	ETHTOOL_MSG_PHY_TEST_GET,
+	ETHTOOL_MSG_PHY_TEST_ACT,
 
 	__ETHTOOL_MSG_USER_CNT,
 	ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -954,6 +957,8 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_NTF,
 	ETHTOOL_MSG_RSS_DELETE_NTF,
 	ETHTOOL_MSG_MSE_GET_REPLY,
+	ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+	ETHTOOL_MSG_PHY_TEST_GET_REPLY,
 
 	__ETHTOOL_MSG_KERNEL_CNT,
 	ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
@@ -961,4 +966,103 @@ enum {
 
 #define ETHTOOL_MCGRP_MONITOR_NAME	"monitor"
 
+/* Interface component types */
+enum intf_component {
+	INTF_COMPONENT_MAC,
+	INTF_COMPONENT_PHY,
+	INTF_COMPONENT_MODULE,
+};
+
+/* Interface sublayer types */
+enum intf_sublayer {
+	INTF_SUBLAYER_NONE,
+	INTF_SUBLAYER_RS,
+	INTF_SUBLAYER_PCS,
+	INTF_SUBLAYER_FEC,
+	INTF_SUBLAYER_PMA,
+	INTF_SUBLAYER_PMD,
+};
+
+/* Attributes of a single interface block */
+enum {
+	ETHTOOL_A_INTF_BLOCK_ID,
+	ETHTOOL_A_INTF_BLOCK_COMPONENT,
+	ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+	ETHTOOL_A_INTF_BLOCK_INSTANCE,
+	ETHTOOL_A_INTF_BLOCK_NAME,
+	ETHTOOL_A_INTF_BLOCK_DEPTH,
+	ETHTOOL_A_INTF_BLOCK_LANES,
+	ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+	ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_ERROR_INJECT,
+	ETHTOOL_A_INTF_BLOCK_BERT,
+
+	__ETHTOOL_A_INTF_BLOCK_CNT,
+	ETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)
+};
+
+/* Attributes of intf-caps-get message */
+enum {
+	ETHTOOL_A_INTF_CAPS_UNSPEC,
+	ETHTOOL_A_INTF_CAPS_HEADER,
+	ETHTOOL_A_INTF_CAPS_BLOCKS,
+
+	__ETHTOOL_A_INTF_CAPS_CNT,
+	ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
+};
+
+/* PHY test pattern types */
+enum phy_test_pattern {
+	PHY_TEST_PATTERN_OFF,
+	PHY_TEST_PATTERN_PRBS7,
+	PHY_TEST_PATTERN_PRBS9,
+	PHY_TEST_PATTERN_PRBS11,
+	PHY_TEST_PATTERN_PRBS13,
+	PHY_TEST_PATTERN_PRBS15,
+	PHY_TEST_PATTERN_PRBS23,
+	PHY_TEST_PATTERN_PRBS31,
+	PHY_TEST_PATTERN_SSPRQ,
+	PHY_TEST_PATTERN_PRBS13Q,
+	PHY_TEST_PATTERN_PRBS31Q,
+	PHY_TEST_PATTERN_SQUARE_NRZ,
+	PHY_TEST_PATTERN_SQUARE_PAM4,
+	PHY_TEST_PATTERN_TX_LINEARITY,
+	PHY_TEST_PATTERN_SCRAMBLED_IDLE,
+	PHY_TEST_PATTERN_K28_5,
+	PHY_TEST_PATTERN_K28_7,
+
+	__PHY_TEST_PATTERN_COUNT,
+	PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
+};
+
+/* PHY test BERT actions */
+enum phy_test_action {
+	PHY_TEST_ACTION_NONE,
+	PHY_TEST_ACTION_START,
+	PHY_TEST_ACTION_STOP,
+
+	__PHY_TEST_ACTION_COUNT,
+	PHY_TEST_ACTION_MAX = (__PHY_TEST_ACTION_COUNT - 1)
+};
+
+/* Attributes of phy-test-get / phy-test-act messages */
+enum {
+	ETHTOOL_A_PHY_TEST_UNSPEC,
+	ETHTOOL_A_PHY_TEST_HEADER,
+	ETHTOOL_A_PHY_TEST_BLOCK_ID,
+	ETHTOOL_A_PHY_TEST_LANE,
+	ETHTOOL_A_PHY_TEST_TX_PATTERN,
+	ETHTOOL_A_PHY_TEST_RX_PATTERN,
+	ETHTOOL_A_PHY_TEST_BERT_ACTION,
+	ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,
+	ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+	ETHTOOL_A_PHY_TEST_CHECKER_LOCK,
+	ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+	ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+
+	__ETHTOOL_A_PHY_TEST_CNT,
+	ETHTOOL_A_PHY_TEST_MAX = (__ETHTOOL_A_PHY_TEST_CNT - 1)
+};
+
 #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 629c10916670e..7dd2c5624cedf 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -9,4 +9,4 @@ ethtool_nl-y	:= netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
 		   channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
 		   tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
 		   module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \
-		   phy.o tsconfig.o mse.o
+		   phy.o tsconfig.o mse.o intf_caps.o phytest.o
diff --git a/net/ethtool/intf_caps.c b/net/ethtool/intf_caps.c
new file mode 100644
index 0000000000000..e45ccc4b88c9f
--- /dev/null
+++ b/net/ethtool/intf_caps.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct intf_caps_req_info {
+	struct ethnl_req_info base;
+};
+
+struct intf_caps_reply_data {
+	struct ethnl_reply_data base;
+	struct ethtool_intf_caps caps;
+};
+
+#define INTF_CAPS_REPDATA(__reply_base) \
+	container_of(__reply_base, struct intf_caps_reply_data, base)
+
+const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1] = {
+	[ETHTOOL_A_INTF_CAPS_HEADER] =
+		NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+static int intf_caps_reply_size(const struct ethnl_req_info *req_base,
+				const struct ethnl_reply_data *reply_base)
+{
+	const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	int len = 0;
+	u32 i;
+
+	for (i = 0; i < data->caps.num_blocks; i++) {
+		len += nla_total_size(0);		/* nested block */
+		len += nla_total_size(sizeof(u32));	/* id */
+		len += nla_total_size(sizeof(u32));	/* component */
+		len += nla_total_size(sizeof(u32));	/* sublayer */
+		len += nla_total_size(sizeof(u32));	/* instance */
+		len += nla_total_size(INTF_BLOCK_NAME_LEN); /* name */
+		len += nla_total_size(sizeof(u8));	/* depth */
+		len += nla_total_size(sizeof(u32));	/* lanes */
+		len += nla_total_size(sizeof(u32));	/* loopback_supported */
+		len += nla_total_size(sizeof(u32));	/* tx_patterns */
+		len += nla_total_size(sizeof(u32));	/* rx_patterns */
+		len += nla_total_size(0);		/* error_inject (flag) */
+		len += nla_total_size(0);		/* bert (flag) */
+	}
+
+	/* outer BLOCKS nest */
+	len += nla_total_size(0);
+
+	return len;
+}
+
+static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,
+				  struct ethnl_reply_data *reply_base,
+				  const struct genl_info *info)
+{
+	struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	struct net_device *dev = reply_base->dev;
+
+	if (!dev->ethtool_ops->get_intf_caps)
+		return -EOPNOTSUPP;
+
+	return dev->ethtool_ops->get_intf_caps(dev, &data->caps);
+}
+
+static int intf_caps_fill_reply(struct sk_buff *skb,
+				const struct ethnl_req_info *req_base,
+				const struct ethnl_reply_data *reply_base)
+{
+	const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+	struct nlattr *blocks_attr;
+	u32 i;
+
+	blocks_attr = nla_nest_start(skb, ETHTOOL_A_INTF_CAPS_BLOCKS);
+	if (!blocks_attr)
+		return -EMSGSIZE;
+
+	for (i = 0; i < data->caps.num_blocks; i++) {
+		const struct ethtool_intf_block *b = &data->caps.blocks[i];
+		struct nlattr *block_attr;
+
+		block_attr = nla_nest_start(skb, 0);
+		if (!block_attr)
+			goto nla_put_failure;
+
+		if (nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_ID, b->id) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_COMPONENT,
+				b->component) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+				b->sublayer) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_INSTANCE,
+				b->instance) ||
+		    nla_put_string(skb, ETHTOOL_A_INTF_BLOCK_NAME, b->name) ||
+		    nla_put_u8(skb, ETHTOOL_A_INTF_BLOCK_DEPTH, b->depth) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LANES, b->lanes) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+				b->loopback_supported) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+				b->supported_tx_patterns) ||
+		    nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+				b->supported_rx_patterns))
+			goto nla_put_failure;
+		if (b->error_inject_supported &&
+		    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_ERROR_INJECT))
+			goto nla_put_failure;
+		if (b->bert_supported &&
+		    nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_BERT))
+			goto nla_put_failure;
+			goto nla_put_failure;
+
+		nla_nest_end(skb, block_attr);
+	}
+
+	nla_nest_end(skb, blocks_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, blocks_attr);
+	return -EMSGSIZE;
+}
+
+const struct ethnl_request_ops ethnl_intf_caps_request_ops = {
+	.request_cmd	= ETHTOOL_MSG_INTF_CAPS_GET,
+	.reply_cmd	= ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+	.hdr_attr	= ETHTOOL_A_INTF_CAPS_HEADER,
+	.req_info_size	= sizeof(struct intf_caps_req_info),
+	.reply_data_size = sizeof(struct intf_caps_reply_data),
+	.prepare_data	= intf_caps_prepare_data,
+	.reply_size	= intf_caps_reply_size,
+	.fill_reply	= intf_caps_fill_reply,
+};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 1af395b54330e..56e5c811454bb 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -431,6 +431,8 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
 	[ETHTOOL_MSG_TSCONFIG_SET]	= &ethnl_tsconfig_request_ops,
 	[ETHTOOL_MSG_PHY_GET]		= &ethnl_phy_request_ops,
 	[ETHTOOL_MSG_MSE_GET]		= &ethnl_mse_request_ops,
+	[ETHTOOL_MSG_INTF_CAPS_GET]	= &ethnl_intf_caps_request_ops,
+	[ETHTOOL_MSG_PHY_TEST_GET]	= &ethnl_phy_test_request_ops,
 };
 
 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1572,6 +1574,28 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.policy = ethnl_mse_get_policy,
 		.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
 	},
+	{
+		.cmd	= ETHTOOL_MSG_INTF_CAPS_GET,
+		.doit	= ethnl_default_doit,
+		.start	= ethnl_default_start,
+		.dumpit	= ethnl_default_dumpit,
+		.done	= ethnl_default_done,
+		.policy = ethnl_intf_caps_get_policy,
+		.maxattr = ARRAY_SIZE(ethnl_intf_caps_get_policy) - 1,
+	},
+	{
+		.cmd	= ETHTOOL_MSG_PHY_TEST_GET,
+		.doit	= ethnl_default_doit,
+		.policy = ethnl_phy_test_get_policy,
+		.maxattr = ARRAY_SIZE(ethnl_phy_test_get_policy) - 1,
+	},
+	{
+		.cmd	= ETHTOOL_MSG_PHY_TEST_ACT,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= ethnl_act_phy_test,
+		.policy = ethnl_phy_test_act_policy,
+		.maxattr = ARRAY_SIZE(ethnl_phy_test_act_policy) - 1,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 3e969a070f9fe..1caba60d83c43 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -474,6 +474,8 @@ extern const struct ethnl_request_ops ethnl_mm_request_ops;
 extern const struct ethnl_request_ops ethnl_phy_request_ops;
 extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;
 extern const struct ethnl_request_ops ethnl_mse_request_ops;
+extern const struct ethnl_request_ops ethnl_intf_caps_request_ops;
+extern const struct ethnl_request_ops ethnl_phy_test_request_ops;
 
 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
 extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -530,6 +532,11 @@ extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];
 extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];
 extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];
 extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
+extern const struct nla_policy ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];
+extern const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1];
+extern const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1];
+
+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info);
 
 int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
diff --git a/net/ethtool/phytest.c b/net/ethtool/phytest.c
new file mode 100644
index 0000000000000..dedcd036b7c2e
--- /dev/null
+++ b/net/ethtool/phytest.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct phy_test_req_info {
+	struct ethnl_req_info base;
+};
+
+struct phy_test_reply_data {
+	struct ethnl_reply_data base;
+	struct ethtool_phy_test test;
+};
+
+#define PHY_TEST_REPDATA(__reply_base) \
+	container_of(__reply_base, struct phy_test_reply_data, base)
+
+/* PHY_TEST_GET */
+
+const struct nla_policy ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1] = {
+	[ETHTOOL_A_PHY_TEST_HEADER]	= NLA_POLICY_NESTED(ethnl_header_policy),
+	[ETHTOOL_A_PHY_TEST_BLOCK_ID]	= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_LANE]	= { .type = NLA_U32 },
+};
+
+static int phy_test_reply_size(const struct ethnl_req_info *req_base,
+			       const struct ethnl_reply_data *reply_base)
+{
+	return nla_total_size(sizeof(u32)) +	/* block_id */
+	       nla_total_size(sizeof(u32)) +	/* lane */
+	       nla_total_size(sizeof(u32)) +	/* tx_pattern */
+	       nla_total_size(sizeof(u32)) +	/* rx_pattern */
+	       nla_total_size(sizeof(u32)) +	/* active_tests */
+	       nla_total_size(sizeof(u8))  +	/* checker_lock */
+	       nla_total_size(sizeof(u64)) +	/* error_count */
+	       nla_total_size(sizeof(u64));	/* total_bits_sent */
+}
+
+static int phy_test_prepare_data(const struct ethnl_req_info *req_base,
+				 struct ethnl_reply_data *reply_base,
+				 const struct genl_info *info)
+{
+	struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+	struct net_device *dev = reply_base->dev;
+	struct nlattr **tb = info->attrs;
+
+	if (!dev->ethtool_ops->get_phy_test)
+		return -EOPNOTSUPP;
+
+	memset(&data->test, 0, sizeof(data->test));
+
+	if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+		data->test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
+		data->test.cmd |= PHY_TEST_CMD_BLOCK_ID;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+		data->test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+		data->test.cmd |= PHY_TEST_CMD_LANE;
+	}
+
+	return dev->ethtool_ops->get_phy_test(dev, &data->test);
+}
+
+static int phy_test_fill_reply(struct sk_buff *skb,
+			       const struct ethnl_req_info *req_base,
+			       const struct ethnl_reply_data *reply_base)
+{
+	const struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+	const struct ethtool_phy_test *t = &data->test;
+
+	if (nla_put_u32(skb, ETHTOOL_A_PHY_TEST_BLOCK_ID, t->block_id) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_LANE, t->lane) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_TX_PATTERN, t->tx_pattern) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_RX_PATTERN, t->rx_pattern) ||
+	    nla_put_u32(skb, ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+			t->active_tests) ||
+	    nla_put_u8(skb, ETHTOOL_A_PHY_TEST_CHECKER_LOCK, t->checker_lock) ||
+	    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+			      t->error_count, ETHTOOL_A_PHY_TEST_UNSPEC) ||
+	    nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+			      t->total_bits_sent, ETHTOOL_A_PHY_TEST_UNSPEC))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+const struct ethnl_request_ops ethnl_phy_test_request_ops = {
+	.request_cmd	= ETHTOOL_MSG_PHY_TEST_GET,
+	.reply_cmd	= ETHTOOL_MSG_PHY_TEST_GET_REPLY,
+	.hdr_attr	= ETHTOOL_A_PHY_TEST_HEADER,
+	.req_info_size	= sizeof(struct phy_test_req_info),
+	.reply_data_size = sizeof(struct phy_test_reply_data),
+	.prepare_data	= phy_test_prepare_data,
+	.reply_size	= phy_test_reply_size,
+	.fill_reply	= phy_test_fill_reply,
+};
+
+/* PHY_TEST_ACT */
+
+const struct nla_policy ethnl_phy_test_act_policy[ETHTOOL_A_PHY_TEST_MAX + 1] = {
+	[ETHTOOL_A_PHY_TEST_HEADER]		= NLA_POLICY_NESTED(ethnl_header_policy),
+	[ETHTOOL_A_PHY_TEST_BLOCK_ID]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_LANE]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_TX_PATTERN]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_RX_PATTERN]		= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_BERT_ACTION]	= { .type = NLA_U32 },
+	[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]	= { .type = NLA_U32 },
+};
+
+int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ethnl_req_info req_info = {};
+	struct nlattr **tb = info->attrs;
+	struct ethtool_phy_test test = {};
+	struct net_device *dev;
+	int ret;
+
+	ret = ethnl_parse_header_dev_get(&req_info,
+					 tb[ETHTOOL_A_PHY_TEST_HEADER],
+					 genl_info_net(info), info->extack,
+					 true);
+	if (ret < 0)
+		return ret;
+
+	dev = req_info.dev;
+
+	if (!dev->ethtool_ops->set_phy_test) {
+		ret = -EOPNOTSUPP;
+		goto out_dev;
+	}
+
+	if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+		test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
+		test.cmd |= PHY_TEST_CMD_BLOCK_ID;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+		test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+		test.cmd |= PHY_TEST_CMD_LANE;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {
+		test.tx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]);
+		test.cmd |= PHY_TEST_CMD_TX_PATTERN;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {
+		test.rx_pattern = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]);
+		test.cmd |= PHY_TEST_CMD_RX_PATTERN;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]) {
+		test.bert_action = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]);
+		test.cmd |= PHY_TEST_CMD_BERT_ACTION;
+	}
+	if (tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]) {
+		test.inject_error_count =
+			nla_get_u32(tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]);
+		test.cmd |= PHY_TEST_CMD_INJECT_COUNT;
+	}
+
+	rtnl_lock();
+	ret = ethnl_ops_begin(dev);
+	if (ret < 0)
+		goto out_rtnl;
+
+	ret = dev->ethtool_ops->set_phy_test(dev, &test);
+	ethnl_ops_complete(dev);
+
+out_rtnl:
+	rtnl_unlock();
+out_dev:
+	ethnl_parse_header_dev_put(&req_info);
+	return ret;
+}

21/3 2026/08/25 05:07 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13010
							output: 31
							thoughts: 405
						

Thoughts:
**Analyzing the New ethtool Netlink Commands**

Okay, so we've got this new patch series adding a few ethtool netlink commands: `ETHTOOL_MSG_INTF_CAPS_GET`, `ETHTOOL_MSG_PHY_TEST_GET`, and `ETHTOOL_MSG_PHY_TEST_ACT`. Seems straightforward enough, using `struct ethtool_intf_caps` and `struct ethtool_phy_test` to handle communication with the kernel drivers. I'm taking a look at `intf_caps_prepare_data` in `net/ethtool/intf_caps.c` to see how it works.

Specifically, this snippet:

```c
static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,
				  struct ethnl_reply_data *reply_base,
				  const struct genl_info *info)
{
	struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
	struct net_device *dev = reply_base->dev;

	if (!dev->ethtool_ops->get_intf_caps)
		return -EOPNOTSUPP;

	return dev->ethtool_ops->get_intf_caps(dev, &data->caps);
}
```

Wait a second… `data->caps` is *directly* passed to `dev->ethtool_ops->get_intf_caps(dev, &data->caps)`. The driver's `get_intf_caps` function is going to write into that `data->caps` struct. But is that `data->caps` *initialized* before the call? That's what I need to know. Where is it getting allocated?

Okay, so `data` is part of `struct intf_caps_reply_data`, which is allocated by the ethtool netlink framework. Right, let me check how the `reply_data` itself is allocated within the framework in `net/ethtool/netlink.c`. I should probably use `default_api:grepper` to find out where `reply_data` is allocated.



22/3 2026/08/25 05:07 tool grepper
0m Args:
{
  "Expression": "alloc.*reply_data",
  "PathPrefix": "net/ethtool/"
}

Results:
{
  "Output": "net/ethtool/netlink.c=509=static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)\n--\nnet/ethtool/netlink.c-529-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:530:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-531-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=672=static int ethnl_default_start(struct netlink_callback *cb)\n--\nnet/ethtool/netlink.c-690-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:691:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-692-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=725=static int ethnl_perphy_start(struct netlink_callback *cb)\n--\nnet/ethtool/netlink.c-744-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:745:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-746-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=998=static void ethnl_default_notify(struct net_device *dev, unsigned int cmd,\n--\nnet/ethtool/netlink.c-1019-\t\treturn;\nnet/ethtool/netlink.c:1020:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-1021-\tif (!reply_data) {\n--\nnet/ethtool/rss.c=118=static int\nnet/ethtool/rss.c:119:rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data)\nnet/ethtool/rss.c-120-{\n--\nnet/ethtool/tsconfig.c=199=static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info)\n--\nnet/ethtool/tsconfig.c-210-\t\treturn -ENOMEM;\nnet/ethtool/tsconfig.c:211:\treply_data = kmalloc_obj(*reply_data);\nnet/ethtool/tsconfig.c-212-\tif (!reply_data) {\n--\nnet/ethtool/tsinfo.c=500=int ethnl_tsinfo_start(struct netlink_callback *cb)\n--\nnet/ethtool/tsinfo.c-513-\t\treturn -ENOMEM;\nnet/ethtool/tsinfo.c:514:\treply_data = kzalloc_obj(*reply_data);\nnet/ethtool/tsinfo.c-515-\tif (!reply_data) {\n"
}

23/3 2026/08/25 05:07 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14749
							output: 32
							thoughts: 1627
						

Thoughts:
**Uninitialized Memory Vulnerability in ethtool's Interface Capabilities Handling**

Okay, so I'm looking at how `ethtool` handles interface capabilities, specifically within the `ethnl_default_doit` and `ethnl_default_start` functions. I see that `reply_data` is allocated using `kmalloc` without the `__GFP_ZERO` flag. This immediately flags a potential issue: the allocated memory isn't zeroed, meaning it contains whatever garbage happened to be there previously on the heap.

Then, I focused on the `intf_caps_prepare_data` function. This function calls the driver's `get_intf_caps` function, passing it a pointer to a `struct intf_caps_reply_data`. The critical point is that `intf_caps_prepare_data` *doesn't* zero-initialize the `data->caps` structure before calling the driver. This is where things get interesting. The driver is responsible for populating this structure.

The vulnerability stems from what happens if the driver's `get_intf_caps` function doesn't completely initialize the `data->caps` structure, perhaps due to an error condition or a lack of complete initialization. If it doesn't initialize all the fields, including, importantly, `num_blocks`, the values within `data->caps` are essentially garbage from the heap. These uninitialized values can then get passed to user space.

Specifically, the `intf_caps_fill_reply` function iterates through a `blocks` array based on `data->caps.num_blocks`. If `num_blocks` is uninitialized, it could be a large, random value, leading to an out-of-bounds read when accessing the `blocks` array and sending that to the user.

However, even if the driver correctly sets `num_blocks`, it might not fully initialize all the fields within the `blocks` array. This means that if `get_intf_caps` doesn't fully initialize the data, the contents of the `blocks` array (including the `name` field, which is used with `nla_put_string`) could contain uninitialized data. This might cause an out-of-bounds read if the name is not null terminated and then user space may also leak stack or heap memory.

I confirmed that `intf_caps_prepare_data` does *not* zero the `data->caps` structure before passing it to the driver, in contrast to what happens in `phy_test_prepare_data`, which *does* initialize its data.

This is a classic uninitialized memory info-leak situation, where kernel memory ends up being leaked to user space, potentially containing sensitive data, or causing crashes. I should file a KMSAN report for this!



24/3 2026/08/25 05:07 tool grepper
0m Args:
{
  "Expression": "reply_data = kmalloc",
  "PathPrefix": "net/ethtool/"
}

Results:
{
  "Output": "net/ethtool/netlink.c=509=static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)\n--\nnet/ethtool/netlink.c-529-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:530:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-531-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=672=static int ethnl_default_start(struct netlink_callback *cb)\n--\nnet/ethtool/netlink.c-690-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:691:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-692-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=725=static int ethnl_perphy_start(struct netlink_callback *cb)\n--\nnet/ethtool/netlink.c-744-\t\treturn -ENOMEM;\nnet/ethtool/netlink.c:745:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-746-\tif (!reply_data) {\n--\nnet/ethtool/netlink.c=998=static void ethnl_default_notify(struct net_device *dev, unsigned int cmd,\n--\nnet/ethtool/netlink.c-1019-\t\treturn;\nnet/ethtool/netlink.c:1020:\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\nnet/ethtool/netlink.c-1021-\tif (!reply_data) {\n--\nnet/ethtool/tsconfig.c=199=static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info)\n--\nnet/ethtool/tsconfig.c-210-\t\treturn -ENOMEM;\nnet/ethtool/tsconfig.c:211:\treply_data = kmalloc_obj(*reply_data);\nnet/ethtool/tsconfig.c-212-\tif (!reply_data) {\n"
}

25/3 2026/08/25 05:07 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17625
							output: 38
							thoughts: 798
						

Thoughts:
**Uninitialized Memory Vulnerability in netlink Attributes**

Okay, so I've been looking at this code and, as expected, `reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);` is used. This means that the memory allocated for `reply_data` is just uninitialized heap memory, a classic source of problems.

The `intf_caps_prepare_data` function is where my attention is drawn. Specifically, it passes `data->caps` to the driver's `get_intf_caps` callback. My concern is that if the driver doesn't initialize *all* the fields of `data->caps`, say `num_blocks` or even just the individual fields within `blocks[]`, then `intf_caps_fill_reply` will read uninitialized memory when it goes to construct the netlink attributes. Functions like `nla_put_u32`, `nla_put_string`, etc., will effectively leak this uninitialized data to user space via the skb. KMSAN is detecting this, as it should, because KASAN wouldn't catch it since the memory is both allocated and within bounds.

I needed to check if there are other uninitialized memory issues. So I then examined `ethnl_act_phy_test`.

```c
int ethnl_act_phy_test(struct sk_buff *skb, struct genl_info *info)
{
	struct ethnl_req_info req_info = {};
	struct nlattr **tb = info->attrs;
	struct ethtool_phy_test test = {};
```

Fortunately, `test` is initialized to zero (`= {}`), so that one is safe.

Then on to `phy_test_prepare_data`?
```c
static int phy_test_prepare_data(const struct ethnl_req_info *req_base,
				 struct ethnl_reply_data *reply_base,
				 const struct genl_info *info)
{
	struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
	struct net_device *dev = reply_base->dev;
	struct nlattr **tb = info->attrs;

	if (!dev->ethtool_ops->get_phy_test)
		return -EOPNOTSUPP;

	memset(&data->test, 0, sizeof(data->test));
```
It explicitly does `memset(&data->test, 0, sizeof(data->test));`, so that looks safe too. Good.

But wait, `intf_caps_prepare_data` is the one to focus on!

```c
static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,
				  struct ethnl_reply_data *reply_base,
				  const struct genl_info *info)
{
	struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
	struct net_device *dev = reply_base->dev;

	if (!dev->ethtool_ops->get_intf_caps)
		return -EOPNOTSUPP;

	return dev->ethtool_ops->get_intf_caps(dev, &data->caps);
}
```

There's no `memset` here! That means the `data` struct, allocated using `kmalloc` in `ethnl_default_doit`, is passed to `get_intf_caps`. If the driver *doesn't* fully initialize `data->caps`, we *will* leak uninitialized heap memory to user space via `intf_caps_fill_reply`. This is a definite vulnerability.

Time to double-check `ethnl_default_doit` to make sure it's not somehow zeroing the memory that's getting allocated.



26/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 500,
  "LineCount": 50
}

Results:
{
  "Contents": " 500:\tstatic void ethnl_init_reply_data(struct ethnl_reply_data *reply_data,\n 501:\t\t\t\t\t  const struct ethnl_request_ops *ops,\n 502:\t\t\t\t\t  struct net_device *dev)\n 503:\t{\n 504:\t\tmemset(reply_data, 0, ops-\u003ereply_data_size);\n 505:\t\treply_data-\u003edev = dev;\n 506:\t}\n 507:\t\n 508:\t/* default -\u003edoit() handler for GET type requests */\n 509:\tstatic int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)\n 510:\t{\n 511:\t\tstruct ethnl_reply_data *reply_data = NULL;\n 512:\t\tstruct ethnl_req_info *req_info = NULL;\n 513:\t\tconst u8 cmd = info-\u003egenlhdr-\u003ecmd;\n 514:\t\tconst struct ethnl_request_ops *ops;\n 515:\t\tbool need_rtnl = false;\n 516:\t\tint hdr_len, reply_len;\n 517:\t\tstruct sk_buff *rskb;\n 518:\t\tvoid *reply_payload;\n 519:\t\tint ret;\n 520:\t\n 521:\t\tops = ethnl_default_requests[cmd];\n 522:\t\tif (WARN_ONCE(!ops, \"cmd %u has no ethnl_request_ops\\n\", cmd))\n 523:\t\t\treturn -EOPNOTSUPP;\n 524:\t\tif (GENL_REQ_ATTR_CHECK(info, ops-\u003ehdr_attr))\n 525:\t\t\treturn -EINVAL;\n 526:\t\n 527:\t\treq_info = kzalloc(ops-\u003ereq_info_size, GFP_KERNEL);\n 528:\t\tif (!req_info)\n 529:\t\t\treturn -ENOMEM;\n 530:\t\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\n 531:\t\tif (!reply_data) {\n 532:\t\t\tkfree(req_info);\n 533:\t\t\treturn -ENOMEM;\n 534:\t\t}\n 535:\t\n 536:\t\tret = ethnl_default_parse(req_info, info, ops, !ops-\u003eallow_nodev_do);\n 537:\t\tif (ret \u003c 0)\n 538:\t\t\tgoto err_free;\n 539:\t\tethnl_init_reply_data(reply_data, ops, req_info-\u003edev);\n 540:\t\n 541:\t\tif (req_info-\u003edev) {\n 542:\t\t\tneed_rtnl = !netdev_need_ops_lock(req_info-\u003edev) ||\n 543:\t\t\t\t    ethtool_nl_msg_needs_rtnl(req_info-\u003edev, cmd);\n 544:\t\t\tif (need_rtnl)\n 545:\t\t\t\trtnl_lock();\n 546:\t\t\tnetdev_lock_ops(req_info-\u003edev);\n 547:\t\t}\n 548:\t\tret = ops-\u003eprepare_data(req_info, reply_data, info);\n 549:\t\tif (req_info-\u003edev) {\n"
}

27/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20197
							output: 38
							thoughts: 161
						

Thoughts:
**My Initial Thoughts on Data Initialization**

Okay, so `ethnl_init_reply_data` is calling `memset` to zero out the `reply_data` buffer. Let's confirm that with a quick check of line 504: Yup, there it is: `memset(reply_data, 0, ops->reply_data_size);`. And it's called at line 539, so `ethnl_init_reply_data(reply_data, ops, req_info->dev);` is definitely initializing that `reply_data` buffer.

Aha! If `reply_data` is being zeroed out right there, then the `data->caps` field in `intf_caps_prepare_data` should *already* be zeroed as well. This changes things; the explicit zeroing I was expecting might be redundant. Let's make sure that nothing else overwrites this as well, and double-check `ethnl_default_start` to be thorough.



28/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 672,
  "LineCount": 50
}

Results:
{
  "Contents": " 672:\tstatic int ethnl_default_start(struct netlink_callback *cb)\n 673:\t{\n 674:\t\tconst struct genl_dumpit_info *info = genl_dumpit_info(cb);\n 675:\t\tstruct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);\n 676:\t\tstruct ethnl_reply_data *reply_data;\n 677:\t\tconst struct ethnl_request_ops *ops;\n 678:\t\tstruct ethnl_req_info *req_info;\n 679:\t\tstruct genlmsghdr *ghdr;\n 680:\t\tint ret;\n 681:\t\n 682:\t\tBUILD_BUG_ON(sizeof(*ctx) \u003e sizeof(cb-\u003ectx));\n 683:\t\n 684:\t\tghdr = nlmsg_data(cb-\u003enlh);\n 685:\t\tops = ethnl_default_requests[ghdr-\u003ecmd];\n 686:\t\tif (WARN_ONCE(!ops, \"cmd %u has no ethnl_request_ops\\n\", ghdr-\u003ecmd))\n 687:\t\t\treturn -EOPNOTSUPP;\n 688:\t\treq_info = kzalloc(ops-\u003ereq_info_size, GFP_KERNEL);\n 689:\t\tif (!req_info)\n 690:\t\t\treturn -ENOMEM;\n 691:\t\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\n 692:\t\tif (!reply_data) {\n 693:\t\t\tret = -ENOMEM;\n 694:\t\t\tgoto free_req_info;\n 695:\t\t}\n 696:\t\n 697:\t\tret = ethnl_default_parse(req_info, \u0026info-\u003einfo, ops, false);\n 698:\t\tif (ret \u003c 0)\n 699:\t\t\tgoto free_reply_data;\n 700:\t\tif (req_info-\u003edev) {\n 701:\t\t\t/* We ignore device specification in dump requests but as the\n 702:\t\t\t * same parser as for non-dump (doit) requests is used, it\n 703:\t\t\t * would take reference to the device if it finds one\n 704:\t\t\t */\n 705:\t\t\tnetdev_put(req_info-\u003edev, \u0026req_info-\u003edev_tracker);\n 706:\t\t\treq_info-\u003edev = NULL;\n 707:\t\t}\n 708:\t\n 709:\t\tctx-\u003eops = ops;\n 710:\t\tctx-\u003ereq_info = req_info;\n 711:\t\tctx-\u003ereply_data = reply_data;\n 712:\t\tctx-\u003epos_ifindex = 0;\n 713:\t\n 714:\t\treturn 0;\n 715:\t\n 716:\tfree_reply_data:\n 717:\t\tkfree(reply_data);\n 718:\tfree_req_info:\n 719:\t\tkfree(req_info);\n 720:\t\n 721:\t\treturn ret;\n"
}

29/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21421
							output: 38
							thoughts: 59
						

Thoughts:
**Analyzing the `ethnl_default_start` Function**

Okay, so I'm looking at this `ethnl_default_start` function, and the first thing that jumps out is that `reply_data` is allocated using `kmalloc` but it *isn't* zeroed out. That's a potential vulnerability or at least an area for debugging later, as that memory might contain garbage. Wait a minute... this is the dumpit module, isn't it? Okay, so `ethnl_default_start` is specific to the dumpit functionality. Alright, let's trace this `reply_data` variable through the rest of the dumpit code and see exactly how it's being utilized, and where and whether it gets cleaned up. I need to be sure that the information in there does not go where it isn't supposed to, and that the function does not suffer from any potential information leaks.



30/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 722,
  "LineCount": 50
}

Results:
{
  "Contents": " 722:\t}\n 723:\t\n 724:\t/* per-PHY -\u003estart() handler for GET requests */\n 725:\tstatic int ethnl_perphy_start(struct netlink_callback *cb)\n 726:\t{\n 727:\t\tstruct ethnl_perphy_dump_ctx *phy_ctx = ethnl_perphy_dump_context(cb);\n 728:\t\tconst struct genl_dumpit_info *info = genl_dumpit_info(cb);\n 729:\t\tstruct ethnl_dump_ctx *ctx = \u0026phy_ctx-\u003eethnl_ctx;\n 730:\t\tstruct ethnl_reply_data *reply_data;\n 731:\t\tconst struct ethnl_request_ops *ops;\n 732:\t\tstruct ethnl_req_info *req_info;\n 733:\t\tstruct genlmsghdr *ghdr;\n 734:\t\tint ret;\n 735:\t\n 736:\t\tBUILD_BUG_ON(sizeof(*ctx) \u003e sizeof(cb-\u003ectx));\n 737:\t\n 738:\t\tghdr = nlmsg_data(cb-\u003enlh);\n 739:\t\tops = ethnl_default_requests[ghdr-\u003ecmd];\n 740:\t\tif (WARN_ONCE(!ops, \"cmd %u has no ethnl_request_ops\\n\", ghdr-\u003ecmd))\n 741:\t\t\treturn -EOPNOTSUPP;\n 742:\t\treq_info = kzalloc(ops-\u003ereq_info_size, GFP_KERNEL);\n 743:\t\tif (!req_info)\n 744:\t\t\treturn -ENOMEM;\n 745:\t\treply_data = kmalloc(ops-\u003ereply_data_size, GFP_KERNEL);\n 746:\t\tif (!reply_data) {\n 747:\t\t\tret = -ENOMEM;\n 748:\t\t\tgoto free_req_info;\n 749:\t\t}\n 750:\t\n 751:\t\t/* Unlike per-dev dump, don't ignore dev. The dump handler\n 752:\t\t * will notice it and dump PHYs from given dev. We only keep track of\n 753:\t\t * the dev's ifindex, .dumpit() will grab and release the netdev itself.\n 754:\t\t */\n 755:\t\tret = ethnl_default_parse(req_info, \u0026info-\u003einfo, ops, false);\n 756:\t\tif (ret \u003c 0)\n 757:\t\t\tgoto free_reply_data;\n 758:\t\tif (req_info-\u003edev) {\n 759:\t\t\tphy_ctx-\u003eifindex = req_info-\u003edev-\u003eifindex;\n 760:\t\t\tnetdev_put(req_info-\u003edev, \u0026req_info-\u003edev_tracker);\n 761:\t\t\treq_info-\u003edev = NULL;\n 762:\t\t}\n 763:\t\n 764:\t\tctx-\u003eops = ops;\n 765:\t\tctx-\u003ereq_info = req_info;\n 766:\t\tctx-\u003ereply_data = reply_data;\n 767:\t\tctx-\u003epos_ifindex = 0;\n 768:\t\n 769:\t\treturn 0;\n 770:\t\n 771:\tfree_reply_data:\n"
}

31/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22571
							output: 38
							thoughts: 13
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:

**Investigating `ethnl_default_dumpit`**

Alright, let's dive into this. The mental checklist starts with `ethnl_default_dumpit`. I need to figure out what this specific entity is, probably a function, perhaps a variable, or potentially even a configuration file. I can infer from the name that this has to do with some default settings that likely get "dumped" somewhere. My expertise tells me it's likely related to either network configuration or low-level Ethernet driver settings. Let me start by seeing where it's called and defined, perhaps a quick `grep` or `find` will get me started. From there I'll investigate how it's used and what context it's applied in, in this system. I want to understand its impact on the overall process before I investigate the intricacies.



32/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 772,
  "LineCount": 50
}

Results:
{
  "Contents": " 772:\t\tkfree(reply_data);\n 773:\tfree_req_info:\n 774:\t\tkfree(req_info);\n 775:\t\n 776:\t\treturn ret;\n 777:\t}\n 778:\t\n 779:\tstatic int ethnl_perphy_dump_one_dev(struct sk_buff *skb,\n 780:\t\t\t\t\t     struct ethnl_perphy_dump_ctx *ctx,\n 781:\t\t\t\t\t     const struct genl_info *info)\n 782:\t{\n 783:\t\tstruct ethnl_dump_ctx *ethnl_ctx = \u0026ctx-\u003eethnl_ctx;\n 784:\t\tstruct net_device *dev = ethnl_ctx-\u003ereq_info-\u003edev;\n 785:\t\tstruct phy_device_node *pdn;\n 786:\t\tint ret;\n 787:\t\n 788:\t\tif (!dev-\u003elink_topo)\n 789:\t\t\treturn 0;\n 790:\t\n 791:\t\txa_for_each_start(\u0026dev-\u003elink_topo-\u003ephys, ctx-\u003epos_phyindex, pdn,\n 792:\t\t\t\t  ctx-\u003epos_phyindex) {\n 793:\t\t\tethnl_ctx-\u003ereq_info-\u003ephy_index = ctx-\u003epos_phyindex;\n 794:\t\n 795:\t\t\t/* We can re-use the original dump_one as -\u003eprepare_data in\n 796:\t\t\t * commands use ethnl_req_get_phydev(), which gets the PHY from\n 797:\t\t\t * the req_info-\u003ephy_index\n 798:\t\t\t */\n 799:\t\t\tret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info);\n 800:\t\t\tif (ret)\n 801:\t\t\t\treturn ret;\n 802:\t\t}\n 803:\t\n 804:\t\tctx-\u003epos_phyindex = 0;\n 805:\t\n 806:\t\treturn 0;\n 807:\t}\n 808:\t\n 809:\tstatic int ethnl_perphy_dump_all_dev(struct sk_buff *skb,\n 810:\t\t\t\t\t     struct ethnl_perphy_dump_ctx *ctx,\n 811:\t\t\t\t\t     const struct genl_info *info)\n 812:\t{\n 813:\t\tstruct ethnl_dump_ctx *ethnl_ctx = \u0026ctx-\u003eethnl_ctx;\n 814:\t\tstruct net *net = sock_net(skb-\u003esk);\n 815:\t\tnetdevice_tracker dev_tracker;\n 816:\t\tstruct net_device *dev;\n 817:\t\tint ret = 0;\n 818:\t\n 819:\t\trcu_read_lock();\n 820:\t\tfor_each_netdev_dump(net, dev, ethnl_ctx-\u003epos_ifindex) {\n 821:\t\t\tnetdev_hold(dev, \u0026dev_tracker, GFP_ATOMIC);\n"
}

33/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23620
							output: 38
							thoughts: 29
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Analyzing the Call Stack**

Okay, so I see that `ethnl_default_dump_one` is being invoked. Right away, my brain jumps to where I would expect to find that function defined, and what its likely purpose might be in this context. I need to take a closer look at `ethnl_default_dump_one` itself to understand exactly what the code is doing within this particular call stack. I need to understand its behavior fully to evaluate the implications of the invocation, and where the values being processed come from. I need to examine the details.



34/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 822,
  "LineCount": 50
}

Results:
{
  "Contents": " 822:\t\t\trcu_read_unlock();\n 823:\t\n 824:\t\t\t/* per-PHY commands use ethnl_req_get_phydev(), which needs the\n 825:\t\t\t * net_device in the req_info\n 826:\t\t\t */\n 827:\t\t\tethnl_ctx-\u003ereq_info-\u003edev = dev;\n 828:\t\t\tret = ethnl_perphy_dump_one_dev(skb, ctx, info);\n 829:\t\n 830:\t\t\trcu_read_lock();\n 831:\t\t\tnetdev_put(dev, \u0026dev_tracker);\n 832:\t\t\tethnl_ctx-\u003ereq_info-\u003edev = NULL;\n 833:\t\n 834:\t\t\tif (ret \u003c 0 \u0026\u0026 ret != -EOPNOTSUPP) {\n 835:\t\t\t\tif (likely(skb-\u003elen))\n 836:\t\t\t\t\tret = skb-\u003elen;\n 837:\t\t\t\tbreak;\n 838:\t\t\t}\n 839:\t\t\tret = 0;\n 840:\t\t}\n 841:\t\trcu_read_unlock();\n 842:\t\n 843:\t\treturn ret;\n 844:\t}\n 845:\t\n 846:\t/* per-PHY -\u003edumpit() handler for GET requests. */\n 847:\tstatic int ethnl_perphy_dumpit(struct sk_buff *skb,\n 848:\t\t\t\t       struct netlink_callback *cb)\n 849:\t{\n 850:\t\tstruct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb);\n 851:\t\tconst struct genl_dumpit_info *info = genl_dumpit_info(cb);\n 852:\t\tstruct ethnl_dump_ctx *ethnl_ctx = \u0026ctx-\u003eethnl_ctx;\n 853:\t\tint ret = 0;\n 854:\t\n 855:\t\tif (ctx-\u003eifindex) {\n 856:\t\t\tnetdevice_tracker dev_tracker;\n 857:\t\t\tstruct net_device *dev;\n 858:\t\n 859:\t\t\tdev = netdev_get_by_index(genl_info_net(\u0026info-\u003einfo),\n 860:\t\t\t\t\t\t  ctx-\u003eifindex, \u0026dev_tracker,\n 861:\t\t\t\t\t\t  GFP_KERNEL);\n 862:\t\t\tif (!dev)\n 863:\t\t\t\treturn -ENODEV;\n 864:\t\n 865:\t\t\tethnl_ctx-\u003ereq_info-\u003edev = dev;\n 866:\t\t\tret = ethnl_perphy_dump_one_dev(skb, ctx, genl_info_dump(cb));\n 867:\t\n 868:\t\t\tif (ret \u003c 0 \u0026\u0026 ret != -EOPNOTSUPP \u0026\u0026 likely(skb-\u003elen))\n 869:\t\t\t\tret = skb-\u003elen;\n 870:\t\n 871:\t\t\tnetdev_put(dev, \u0026dev_tracker);\n"
}

35/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24614
							output: 34
							thoughts: 14
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Hunting for `ethnl_default_dump_one`**

Okay, so the hunt begins. I'm looking for `ethnl_default_dump_one`. My initial thought is to figure out the context. Is this a function, a variable, a macro? Its naming convention suggests a function or potentially a macro. The `ethnl` prefix is likely some sort of module or namespace; perhaps related to Ethernet or network configuration, given the `nl` for "netlink". The "default" probably hints at a default or fallback mechanism. And "dump\_one"... definitely sounds like some kind of data retrieval, likely related to dumping or outputting a single configuration parameter or state.

My mind immediately starts forming a hypothesis. Is this related to a specific Ethernet interface's default configuration? Or perhaps related to an overall system default for network interfaces?

I'll quickly check the relevant codebase; I can use grep, ack, ripgrep, or similar tools, depending on the scale and complexity of the project. I'd start with a broad search, looking for the exact name, and variations like "ethnl" and "dump". I'll also consider searching for related terms like "netlink", "ethernet", "default configuration", "interface dump", or similar.

I'm also considering the language or framework here. For example, if it's kernel space, I might expect to see a lot of C code. If it is userspace, it could be C, C++, Python, or whatever the project is using. So I have to keep that in mind when doing the search. Based on the language and framework the search might differ as well.

Once I have a list of possible locations or usages, I'll then drill down and read the surrounding code. I need to understand its purpose, what it does, and how it interacts with other components, in order to figure out what it does in detail. I'm looking for clues about where the "dump" occurs, when it's called, and who calls it. This will help me connect the dots about its functionality. I'm expecting this will involve network configuration structures, socket calls, netlink messages, and potentially some form of state management.



36/3 2026/08/25 05:08 tool grepper
0m Args:
{
  "Expression": "ethnl_default_dump_one",
  "PathPrefix": "net/ethtool/"
}

Results:
{
  "Output": "net/ethtool/netlink.c=509=static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)\n--\nnet/ethtool/netlink.c-595-\nnet/ethtool/netlink.c:596:static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev,\nnet/ethtool/netlink.c-597-\t\t\t\t  const struct ethnl_dump_ctx *ctx,\n--\nnet/ethtool/netlink.c=640=static int ethnl_default_dumpit(struct sk_buff *skb,\n--\nnet/ethtool/netlink.c-653-\nnet/ethtool/netlink.c:654:\t\tret = ethnl_default_dump_one(skb, dev, ctx, genl_info_dump(cb));\nnet/ethtool/netlink.c-655-\n--\nnet/ethtool/netlink.c=779=static int ethnl_perphy_dump_one_dev(struct sk_buff *skb,\n--\nnet/ethtool/netlink.c-798-\t\t */\nnet/ethtool/netlink.c:799:\t\tret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info);\nnet/ethtool/netlink.c-800-\t\tif (ret)\n"
}

37/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25524
							output: 38
							thoughts: 14
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Initial Assessment: Diving into `ethnl_default_dump_one`**

Alright, let's get down to business. My first thought is to dive straight into `ethnl_default_dump_one`. I'm assuming from the naming convention that this is likely a default dump, potentially related to ethnic network (ethnl) configurations or data. The "one" at the end suggests a primary or base configuration. Given my experience, I'm already mentally running through the various potential contexts: is this a specific network parameter dump? A representation of the initial state of a particular device or system related to ethnl? Or perhaps it's a snapshot of the default settings for the ethnl interface itself?

I'm mentally preparing for a file that could contain anything from basic MAC address assignments and MTU sizes to more complex routing information, VLAN configurations, or even security settings related to the ethnl protocols. My focus will be on quickly identifying the key elements and assessing their implications, as well as looking for any obvious deviations from standard practice. I'll be particularly interested in anything that could suggest underlying assumptions or hardcoded values. My goal is to rapidly understand the scope and the likely purpose of this dump. Now, let's get to it.



38/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 596,
  "LineCount": 40
}

Results:
{
  "Contents": " 596:\tstatic int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev,\n 597:\t\t\t\t\t  const struct ethnl_dump_ctx *ctx,\n 598:\t\t\t\t\t  const struct genl_info *info)\n 599:\t{\n 600:\t\tbool need_rtnl;\n 601:\t\tvoid *ehdr;\n 602:\t\tint ret;\n 603:\t\n 604:\t\tehdr = genlmsg_put(skb, info-\u003esnd_portid, info-\u003esnd_seq,\n 605:\t\t\t\t   \u0026ethtool_genl_family, NLM_F_MULTI,\n 606:\t\t\t\t   ctx-\u003eops-\u003ereply_cmd);\n 607:\t\tif (!ehdr)\n 608:\t\t\treturn -EMSGSIZE;\n 609:\t\n 610:\t\tethnl_init_reply_data(ctx-\u003ereply_data, ctx-\u003eops, dev);\n 611:\t\tneed_rtnl = !netdev_need_ops_lock(dev) ||\n 612:\t\t\t    ethtool_nl_msg_needs_rtnl(dev, ctx-\u003eops-\u003erequest_cmd);\n 613:\t\tif (need_rtnl)\n 614:\t\t\trtnl_lock();\n 615:\t\tnetdev_lock_ops(dev);\n 616:\t\tret = ctx-\u003eops-\u003eprepare_data(ctx-\u003ereq_info, ctx-\u003ereply_data, info);\n 617:\t\tnetdev_unlock_ops(dev);\n 618:\t\tif (need_rtnl)\n 619:\t\t\trtnl_unlock();\n 620:\t\tif (ret \u003c 0)\n 621:\t\t\tgoto out_cancel;\n 622:\t\tret = ethnl_fill_reply_header(skb, dev, ctx-\u003eops-\u003ehdr_attr);\n 623:\t\tif (ret \u003c 0)\n 624:\t\t\tgoto out;\n 625:\t\tret = ctx-\u003eops-\u003efill_reply(skb, ctx-\u003ereq_info, ctx-\u003ereply_data);\n 626:\t\n 627:\tout:\n 628:\t\tif (ctx-\u003eops-\u003ecleanup_data)\n 629:\t\t\tctx-\u003eops-\u003ecleanup_data(ctx-\u003ereply_data);\n 630:\tout_cancel:\n 631:\t\tctx-\u003ereply_data-\u003edev = NULL;\n 632:\t\tif (ret \u003c 0)\n 633:\t\t\tgenlmsg_cancel(skb, ehdr);\n 634:\t\telse\n 635:\t\t\tgenlmsg_end(skb, ehdr);\n"
}

39/3 2026/08/25 05:08 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26554
							output: 38
							thoughts: 125
						

Thoughts:
**Double-Checking the Data Initialization**

Okay, so I'm noticing something interesting. The call to `ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev);` pops up in `ethnl_default_dump_one` too, not just in the `doit` path. Right, and earlier we verified that `ethnl_init_reply_data` includes a `memset(reply_data, 0, ops->reply_data_size);`. That `memset` is critical!

So, the `reply_data` buffer is definitely getting zeroed out *before* `prepare_data` gets called. This holds true for both the `doit` and `dumpit` execution paths. It's good that we're covering our bases. Just to be absolutely sure, let me take a quick look back at the `ethnl_init_reply_data` function itself. Always worth a double-check on initialization.



40/3 2026/08/25 05:08 tool read-file
0m Args:
{
  "File": "net/ethtool/netlink.c",
  "FirstLine": 500,
  "LineCount": 10
}

Results:
{
  "Contents": " 500:\tstatic void ethnl_init_reply_data(struct ethnl_reply_data *reply_data,\n 501:\t\t\t\t\t  const struct ethnl_request_ops *ops,\n 502:\t\t\t\t\t  struct net_device *dev)\n 503:\t{\n 504:\t\tmemset(reply_data, 0, ops-\u003ereply_data_size);\n 505:\t\treply_data-\u003edev = dev;\n 506:\t}\n 507:\t\n 508:\t/* default -\u003edoit() handler for GET type requests */\n 509:\tstatic int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)\n"
}

41/3 2026/08/25 05:08 llm kmsan-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27118
							output: 377
							thoughts: 7868
						

Thoughts:
**Summary of the Code Review**

Okay, so I've gone through this patch with a fine-tooth comb, focusing on the potential for uninitialized memory usage, particularly related to the new ethtool netlink functionality. My initial concern was about potential memory leaks, especially given the new data structures. However, after carefully tracing the code execution and initialization paths, I'm confident that there are no uninitialized memory leaks.

Here's my reasoning. First, I confirmed that the primary `reply_data` structure used for both `INTF_CAPS_GET` and `PHY_TEST_GET` is always zeroed out before the driver's `get_intf_caps` and `get_phy_test` callbacks are invoked. The `ethnl_init_reply_data` function, which is called in multiple places, explicitly uses `memset` to zero the entire allocated memory region (`ops->reply_data_size`). This includes all fields within structures like `struct intf_caps_reply_data` and `struct ethtool_intf_caps`. This means that even if the driver *fails* to initialize a specific field, it will default to zero.

I then revisited `phy_test_prepare_data` which I found has an extra `memset` call, and which can be redundant, but doesn't cause any issues as it guarantees the struct is indeed zeroed. I also reviewed `ethnl_act_phy_test` which defines `test = {}`, which also guarantees the struct is zeroed. Also any local or statically defined structs are initialized to zero.

The code uses individual `nla_put_*` functions (e.g., `nla_put_u32`, `nla_put_string`, `nla_put_flag`, and `nla_put_u64_64bit`) to serialize the data into netlink messages. These functions deal with copying the value of individual fields, not the entire struct. This is important: even if there are uninitialized padding bytes within a struct, these padding bytes would not be copied into the netlink message. The functions copy the actual data fields only.

I also looked at potential out-of-bounds reads or writes. This can occur, for instance, if the driver sets an invalid `num_blocks` value, so that a loop in a serialization function goes past the valid block range. That would be caught by KASAN.

I did identify a logic bug within `intf_caps_fill_reply`, where an unconditional `goto nla_put_failure;` is present. However, this is a code bug that will cause the function to always fail. But, crucially, it's not a memory safety issue. The netlink message would be canceled, so no uninitialized memory would be exposed.

Therefore, since the data structures are explicitly zeroed before use, and data fields are individually serialized into netlink messages, and any potential out-of-bounds access is caught by KASAN, there is no risk of leaking uninitialized memory. Therefore, I conclude that `NeedsKMSAN` is `false` for this patch.



42/3 2026/08/25 05:10 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces new ethtool netlink commands (`INTF_CAPS_GET`, `PHY_TEST_GET`, `PHY_TEST_ACT`) to query interface capabilities and configure PHY tests. The data structures used to pass information between the ethtool core and the drivers (`struct ethtool_intf_caps` and `struct ethtool_phy_test`) are allocated dynamically.\n\nHowever, the ethtool netlink framework explicitly zero-initializes the reply data structures via `ethnl_init_reply_data()` (which calls `memset(reply_data, 0, ops-\u003ereply_data_size)`) before passing them to the driver callbacks (e.g., `get_intf_caps`). Similarly, `phy_test_prepare_data()` explicitly calls `memset(\u0026data-\u003etest, 0, sizeof(data-\u003etest))`, and `ethnl_act_phy_test()` initializes its local struct with `= {}`.\n\nBecause all structures are fully zero-initialized before use, there is no risk of leaking uninitialized kernel stack or heap memory to user space, even if a driver fails to populate all fields. Furthermore, the netlink response is constructed by serializing individual fields using `nla_put_u32`, `nla_put_string`, etc., rather than copying raw structs, which inherently avoids leaking struct padding.\n\nAny potential issues, such as a driver writing past the end of the `blocks` array or returning an invalid `num_blocks` leading to out-of-bounds reads, would be reliably detected by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch introduces new ethtool netlink commands (`INTF_CAPS_GET`, `PHY_TEST_GET`, `PHY_TEST_ACT`) to query interface capabilities and configure PHY tests. The data structures used to pass information between the ethtool core and the drivers (`struct ethtool_intf_caps` and `struct ethtool_phy_test`) are allocated dynamically.\n\nHowever, the ethtool netlink framework explicitly zero-initializes the reply data structures via `ethnl_init_reply_data()` (which calls `memset(reply_data, 0, ops-\u003ereply_data_size)`) before passing them to the driver callbacks (e.g., `get_intf_caps`). Similarly, `phy_test_prepare_data()` explicitly calls `memset(\u0026data-\u003etest, 0, sizeof(data-\u003etest))`, and `ethnl_act_phy_test()` initializes its local struct with `= {}`.\n\nBecause all structures are fully zero-initialized before use, there is no risk of leaking uninitialized kernel stack or heap memory to user space, even if a driver fails to populate all fields. Furthermore, the netlink response is constructed by serializing individual fields using `nla_put_u32`, `nla_put_string`, etc., rather than copying raw structs, which inherently avoids leaking struct padding.\n\nAny potential issues, such as a driver writing past the end of the `blocks` array or returning an invalid `num_blocks` leading to out-of-bounds reads, would be reliably detected by standard KASAN. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

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)