AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/03 00:58 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_dev_xmit",
    "hsr_handle_frame",
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation bug in HSR/PRP by adding `skb_cow` before modifying cloned skbs in `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. These are core packet forwarding functions in the HSR driver, reachable via `hsr_dev_xmit` (master device TX) and `hsr_handle_frame` (slave device RX). The code is fully functional and reachable in a virtualized environment.",
  "WorthFuzzing": true
}

1/1 2026/08/03 00:58 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 2580951a96725dca0df8dd2339bf6eff7d627f7c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 3 00:57:59 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c\nindex 0774981a65c16..974b55f248821 100644\n--- a/net/hsr/hsr_forward.c\n+++ b/net/hsr/hsr_forward.c\n@@ -336,12 +336,24 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,\n \tint movelen;\n \n \tif (frame-\u003eskb_hsr) {\n-\t\tstruct hsr_ethhdr *hsr_ethhdr =\n-\t\t\t(struct hsr_ethhdr *)skb_mac_header(frame-\u003eskb_hsr);\n+\t\tstruct hsr_ethhdr *hsr_ethhdr;\n+\n+\t\t/* The original skb data may be shared with another egress\n+\t\t * clone. Make the clone data private before updating the\n+\t\t * path id so the update cannot corrupt the other copy.\n+\t\t */\n+\t\tskb = skb_clone(frame-\u003eskb_hsr, GFP_ATOMIC);\n+\t\tif (!skb)\n+\t\t\treturn NULL;\n+\t\tif (skb_cow(skb, 0)) {\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn NULL;\n+\t\t}\n \n \t\t/* set the lane id properly */\n+\t\thsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb);\n \t\thsr_set_path_id(frame, hsr_ethhdr, port);\n-\t\treturn skb_clone(frame-\u003eskb_hsr, GFP_ATOMIC);\n+\t\treturn skb;\n \t} else if (port-\u003edev-\u003efeatures \u0026 NETIF_F_HW_HSR_TAG_INS) {\n \t\treturn skb_clone(frame-\u003eskb_std, GFP_ATOMIC);\n \t}\n@@ -377,15 +389,28 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\n \tstruct sk_buff *skb;\n \n \tif (frame-\u003eskb_prp) {\n-\t\tstruct prp_rct *trailer = skb_get_PRP_rct(frame-\u003eskb_prp);\n+\t\tstruct prp_rct *trailer;\n \n+\t\t/* Same sharing hazard as above: privatize the clone data\n+\t\t * before updating the LAN id.\n+\t\t */\n+\t\tskb = skb_clone(frame-\u003eskb_prp, GFP_ATOMIC);\n+\t\tif (!skb)\n+\t\t\treturn NULL;\n+\t\tif (skb_cow(skb, 0)) {\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\ttrailer = skb_get_PRP_rct(skb);\n \t\tif (trailer) {\n \t\t\tprp_set_lan_id(trailer, port);\n \t\t} else {\n \t\t\tWARN_ONCE(!trailer, \"errored PRP skb\");\n+\t\t\tkfree_skb(skb);\n \t\t\treturn NULL;\n \t\t}\n-\t\treturn skb_clone(frame-\u003eskb_prp, GFP_ATOMIC);\n+\t\treturn skb;\n \t} else if (port-\u003edev-\u003efeatures \u0026 NETIF_F_HW_HSR_TAG_INS) {\n \t\treturn skb_clone(frame-\u003eskb_std, GFP_ATOMIC);\n \t}\n@@ -420,6 +445,22 @@ static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,\n static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,\n \t\t    struct hsr_frame_info *frame)\n {\n+\t/* An interlink-bound skb from get_untagged_frame() can still alias\n+\t * another live consumer: for master-originated frames the clone\n+\t * shares the original TX skb (which taps or the TX path may still\n+\t * hold); for ring frames the master also consumes them when they\n+\t * are destined to the local node without being exclusive to it.\n+\t * Privatize before any address mutation.\n+\t */\n+\tif (port-\u003etype == HSR_PT_INTERLINK \u0026\u0026\n+\t    (frame-\u003eport_rcv-\u003etype == HSR_PT_MASTER ||\n+\t     (frame-\u003eis_local_dest \u0026\u0026 !frame-\u003eis_local_exclusive)) \u0026\u0026\n+\t    skb_cow(skb, 0)) {\n+\t\tframe-\u003eport_rcv-\u003edev-\u003estats.rx_dropped++;\n+\t\tkfree_skb(skb);\n+\t\treturn NET_XMIT_DROP;\n+\t}\n+\n \tif (frame-\u003eport_rcv-\u003etype == HSR_PT_MASTER) {\n \t\thsr_addr_subst_dest(frame-\u003enode_src, skb, port);\n \ndiff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile\nindex 31fb9326cf533..87fe34951b8b0 100644\n--- a/tools/testing/selftests/net/hsr/Makefile\n+++ b/tools/testing/selftests/net/hsr/Makefile\n@@ -5,6 +5,7 @@ top_srcdir = ../../../../..\n TEST_PROGS := \\\n \thsr_ping.sh \\\n \thsr_redbox.sh \\\n+\thsr_shared_mutation.sh \\\n \tlink_faults.sh \\\n \tprp_ping.sh \\\n # end of TEST_PROGS\ndiff --git a/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh\nnew file mode 100755\nindex 0000000000000..05e7e803d2867\n--- /dev/null\n+++ b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh\n@@ -0,0 +1,223 @@\n+#!/bin/bash\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# Verify that per-egress mutations of shared skb data are private:\n+#\n+# F2 (path/LAN ID): on an affected kernel the second slave's LAN-ID write\n+# lands in the first slave's still-queued clone; with a netem delay on\n+# slave A, injected frames leave A carrying B's LAN ID.\n+#\n+# F1 (RedBox source MAC): on an affected kernel an HSR-tagged multicast\n+# frame received on a RedBox slave is cloned for master and interlink,\n+# and the interlink's RedBox-MAC rewrite lands in the master clone's\n+# buffer, so the local stack receives the RedBox MAC instead of the\n+# originating node's MAC.\n+\n+ipv6=false\n+\n+source ./hsr_common.sh\n+\n+DUR=5\n+\n+require()\n+{\n+\tcommand -v \"$1\" \u003e/dev/null 2\u003e\u00261 \u0026\u0026 return 0\n+\techo \"SKIP: $1 not available\"\n+\texit $ksft_skip\n+}\n+\n+require ip\n+require tc\n+require python3\n+\n+trap cleanup_all_ns EXIT\n+\n+# ------------------------------------------------------- F2: LAN-ID isolation\n+# PRP DANP (proto 1), AF_PACKET pre-tagged injection, netem on slave A.\n+run_f2()\n+{\n+\tsetup_ns ns 2\u003e/dev/null || return $ksft_skip\n+\tnsx() { ip netns exec \"$ns\" \"$@\"; }\n+\n+\t# Probe sch_netem inside the disposable namespace only.\n+\tif ! nsx tc qdisc add dev lo root netem delay 1ms 2\u003e/dev/null; then\n+\t\techo \"SKIP: sch_netem not available\"\n+\t\treturn $ksft_skip\n+\tfi\n+\tnsx tc qdisc del dev lo root 2\u003e/dev/null\n+\n+\t# Capability probes end here; setup or runtime failure below is FAIL.\n+\tnsx ip link add vA type veth peer name vAp ||\n+\t\t{ echo \"FAIL: veth A\"; return 1; }\n+\tnsx ip link add vB type veth peer name vBp ||\n+\t\t{ echo \"FAIL: veth B\"; return 1; }\n+\tfor i in vA vB vAp vBp; do\n+\t\tnsx ip link set \"$i\" up || { echo \"FAIL: $i up\"; return 1; }\n+\tdone\n+\tif ! nsx ip link add name prp0 type hsr slave1 vA slave2 vB \\\n+\t\tsupervision 45 proto 1 2\u003e/dev/null; then\n+\t\techo \"SKIP: HSR/PRP not supported by this kernel\"\n+\t\treturn $ksft_skip\n+\tfi\n+\tnsx ip link set prp0 up || { echo \"FAIL: prp0 up\"; return 1; }\n+\tnsx tc qdisc add dev vA root netem delay 200ms ||\n+\t\t{ echo \"FAIL: netem\"; return 1; }\n+\n+\tnsx python3 /dev/stdin \"$DUR\" \u003c\u003c'PYF2'\n+import socket, struct, select, sys, time\n+\n+dur = int(sys.argv[1])\n+def lanid(pkt):\n+    if len(pkt) \u003c 20 or pkt[-2:] != b\"\\x88\\xfb\":\n+        return None\n+    return (pkt[-4] \u003e\u003e 4) \u0026 0xF\n+\n+SRC = bytes.fromhex(open(\"/sys/class/net/prp0/address\").read().replace(\":\", \"\"))\n+DST = bytes.fromhex(\"02aabbccdd01\")\n+PAY = bytes(range(46))\n+rct0 = struct.pack(\"\u003eH\", 0) + struct.pack(\"\u003eH\", 52 \u0026 0x0FFF) + b\"\\x88\\xfb\"\n+frame = DST + SRC + b\"\\x08\\x00\" + PAY + rct0\n+\n+tx = socket.socket(socket.AF_PACKET, socket.SOCK_RAW); tx.bind((\"prp0\", 0))\n+sA = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,\n+        socket.ntohs(0x0003))\n+sA.bind((\"vAp\", 0))\n+sB = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,\n+        socket.ntohs(0x0003))\n+sB.bind((\"vBp\", 0))\n+sA.setblocking(False); sB.setblocking(False)\n+for _ in range(200):\n+    tx.send(frame); time.sleep(0.001)\n+\n+a, b = [], []\n+end = time.time() + dur\n+while time.time() \u003c end:\n+    r, _, _ = select.select([sA, sB], [], [], 0.3)\n+    for s in r:\n+        pkt = s.recv(65535)\n+        if (pkt[:6] != DST or pkt[6:12] != SRC or pkt[12:14] != b\"\\x08\\x00\"\n+                or pkt[14:14 + len(PAY)] != PAY):\n+            continue\n+        lid = lanid(pkt)\n+        if lid is not None:\n+            (a if s is sA else b).append(lid)\n+\n+print(\"A-side count=%d lan ids=%s\" % (len(a), sorted(set(a))))\n+print(\"B-side count=%d lan ids=%s\" % (len(b), sorted(set(b))))\n+if len(a) \u003c 150 or len(b) \u003c 150:\n+    print(\"FAIL: too few injected frames captured (A=%d B=%d, sent 200)\"\n+          % (len(a), len(b)))\n+    sys.exit(1)\n+bad_a = [x for x in a if (x \u0026 1) != 0]\n+bad_b = [x for x in b if (x \u0026 1) != 1]\n+if bad_a or bad_b:\n+    print(\"FAIL: shared-mutation corruption - A: %d/%d wrong-lan,\"\n+          \" B: %d/%d wrong-lan\"\n+          % (len(bad_a), len(a), len(bad_b), len(b)))\n+    sys.exit(1)\n+print(\"PASS: per-egress LAN IDs isolated (A all bit0=0, B all bit0=1)\")\n+sys.exit(0)\n+PYF2\n+}\n+\n+# --------------------------------------------- F1: RedBox source-MAC privacy\n+# HSR RedBox (proto 0), tagged multicast from a slave: master must keep\n+# the node MAC, interlink must carry the RedBox MAC.\n+run_f1()\n+{\n+\tsetup_ns ns 2\u003e/dev/null || return $ksft_skip\n+\tnsx() { ip netns exec \"$ns\" \"$@\"; }\n+\n+\tnsx ip link add vA type veth peer name vAp ||\n+\t\t{ echo \"FAIL: veth A\"; return 1; }\n+\tnsx ip link add vB type veth peer name vBp ||\n+\t\t{ echo \"FAIL: veth B\"; return 1; }\n+\tnsx ip link add vI type veth peer name vIp ||\n+\t\t{ echo \"FAIL: veth I\"; return 1; }\n+\tfor i in vA vB vI vAp vBp vIp; do\n+\t\tnsx ip link set \"$i\" up || { echo \"FAIL: $i up\"; return 1; }\n+\tdone\n+\tif ! nsx ip link add name hsr0 type hsr slave1 vA slave2 vB \\\n+\t\tinterlink vI supervision 45 proto 0 2\u003e/dev/null; then\n+\t\techo \"SKIP: HSR RedBox not supported by this kernel\"\n+\t\treturn $ksft_skip\n+\tfi\n+\tnsx ip link set hsr0 up || { echo \"FAIL: hsr0 up\"; return 1; }\n+\n+\tnsx python3 /dev/stdin \u003c\u003c'PYF1'\n+import socket, select, sys, time\n+\n+NODE  = bytes.fromhex(\"021122334455\")\n+MCAST = bytes.fromhex(\"01005e000001\")\n+RB    = bytes.fromhex(open(\"/sys/class/net/vI/address\").read().replace(\":\", \"\"))\n+PAY   = bytes(range(46))\n+\n+def frame(seq):\n+    tag = (((1 \u003c\u003c 12) | len(PAY)).to_bytes(2, \"big\")\n+           + seq.to_bytes(2, \"big\") + b\"\\x08\\x00\")\n+    return MCAST + NODE + b\"\\x89\\x2f\" + tag + PAY\n+\n+tx = socket.socket(socket.AF_PACKET, socket.SOCK_RAW); tx.bind((\"vAp\", 0))\n+sm = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,\n+        socket.ntohs(0x0003))\n+sm.bind((\"hsr0\", 0))\n+si = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,\n+        socket.ntohs(0x0003))\n+si.bind((\"vIp\", 0))\n+sm.setblocking(False); si.setblocking(False)\n+\n+for i in range(3):\n+    tx.send(frame(i + 1)); time.sleep(0.05)\n+\n+m_src = i_src = None\n+end = time.time() + 4\n+while time.time() \u003c end and (m_src is None or i_src is None):\n+    r, _, _ = select.select([sm, si], [], [], 0.3)\n+    for s in r:\n+        pkt = s.recv(65535)\n+        # exact flow: dst, post-strip EtherType, exact payload, min length;\n+        # h_source is the asserted value and must NOT be filtered on\n+        if (len(pkt) \u003c 60 or pkt[:6] != MCAST or pkt[12:14] != b\"\\x08\\x00\"\n+                or pkt[14:14 + len(PAY)] != PAY):\n+            continue\n+        if s is sm and m_src is None:\n+            m_src = pkt[6:12]\n+        elif s is si and i_src is None:\n+            i_src = pkt[6:12]\n+\n+print(\"master h_source    =\", m_src.hex() if m_src else None)\n+print(\"node MAC           =\", NODE.hex())\n+print(\"interlink h_source =\", i_src.hex() if i_src else None)\n+print(\"redbox MAC         =\", RB.hex())\n+if i_src != RB:\n+    print(\"FAIL: interlink did not carry the RedBox MAC\")\n+    sys.exit(1)\n+if m_src != NODE:\n+    print(\"FAIL: master received %s instead of the node MAC \"\n+          \"(shared-mutation corruption)\"\n+          % (m_src.hex() if m_src else \"nothing\"))\n+    sys.exit(1)\n+print(\"PASS: master kept node MAC, interlink kept RedBox MAC\")\n+sys.exit(0)\n+PYF1\n+}\n+\n+rc=0\n+\n+run_f2\n+ret=$?\n+[ \"$ret\" -eq \"$ksft_skip\" ] \u0026\u0026 exit \"$ksft_skip\"\n+[ \"$ret\" -eq 0 ] || rc=1\n+\n+run_f1\n+ret=$?\n+[ \"$ret\" -eq \"$ksft_skip\" ] \u0026\u0026 exit \"$ksft_skip\"\n+[ \"$ret\" -eq 0 ] || rc=1\n+\n+if [ $rc -eq 0 ]; then\n+\techo \"hsr_shared_mutation: per-egress mutation isolation (F1+F2) [ OK ]\"\n+else\n+\techo \"hsr_shared_mutation: per-egress mutation isolation [ FAIL ]\" \\\n+\t\t\"rc=$rc\" 1\u003e\u00262\n+fi\n+exit $rc\n"
}

2/1 2026/08/03 00:58 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_dev_xmit",
    "hsr_handle_frame",
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation bug in HSR/PRP by adding `skb_cow` before modifying cloned skbs in `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. These are core packet forwarding functions in the HSR driver, reachable via `hsr_dev_xmit` (master device TX) and `hsr_handle_frame` (slave device RX). The code is fully functional and reachable in a virtualized environment.",
  "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 2580951a96725dca0df8dd2339bf6eff7d627f7c
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 3 00:57:59 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 0774981a65c16..974b55f248821 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -336,12 +336,24 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
 	int movelen;
 
 	if (frame->skb_hsr) {
-		struct hsr_ethhdr *hsr_ethhdr =
-			(struct hsr_ethhdr *)skb_mac_header(frame->skb_hsr);
+		struct hsr_ethhdr *hsr_ethhdr;
+
+		/* The original skb data may be shared with another egress
+		 * clone. Make the clone data private before updating the
+		 * path id so the update cannot corrupt the other copy.
+		 */
+		skb = skb_clone(frame->skb_hsr, GFP_ATOMIC);
+		if (!skb)
+			return NULL;
+		if (skb_cow(skb, 0)) {
+			kfree_skb(skb);
+			return NULL;
+		}
 
 		/* set the lane id properly */
+		hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb);
 		hsr_set_path_id(frame, hsr_ethhdr, port);
-		return skb_clone(frame->skb_hsr, GFP_ATOMIC);
+		return skb;
 	} else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) {
 		return skb_clone(frame->skb_std, GFP_ATOMIC);
 	}
@@ -377,15 +389,28 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
 	struct sk_buff *skb;
 
 	if (frame->skb_prp) {
-		struct prp_rct *trailer = skb_get_PRP_rct(frame->skb_prp);
+		struct prp_rct *trailer;
 
+		/* Same sharing hazard as above: privatize the clone data
+		 * before updating the LAN id.
+		 */
+		skb = skb_clone(frame->skb_prp, GFP_ATOMIC);
+		if (!skb)
+			return NULL;
+		if (skb_cow(skb, 0)) {
+			kfree_skb(skb);
+			return NULL;
+		}
+
+		trailer = skb_get_PRP_rct(skb);
 		if (trailer) {
 			prp_set_lan_id(trailer, port);
 		} else {
 			WARN_ONCE(!trailer, "errored PRP skb");
+			kfree_skb(skb);
 			return NULL;
 		}
-		return skb_clone(frame->skb_prp, GFP_ATOMIC);
+		return skb;
 	} else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) {
 		return skb_clone(frame->skb_std, GFP_ATOMIC);
 	}
@@ -420,6 +445,22 @@ static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,
 static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,
 		    struct hsr_frame_info *frame)
 {
+	/* An interlink-bound skb from get_untagged_frame() can still alias
+	 * another live consumer: for master-originated frames the clone
+	 * shares the original TX skb (which taps or the TX path may still
+	 * hold); for ring frames the master also consumes them when they
+	 * are destined to the local node without being exclusive to it.
+	 * Privatize before any address mutation.
+	 */
+	if (port->type == HSR_PT_INTERLINK &&
+	    (frame->port_rcv->type == HSR_PT_MASTER ||
+	     (frame->is_local_dest && !frame->is_local_exclusive)) &&
+	    skb_cow(skb, 0)) {
+		frame->port_rcv->dev->stats.rx_dropped++;
+		kfree_skb(skb);
+		return NET_XMIT_DROP;
+	}
+
 	if (frame->port_rcv->type == HSR_PT_MASTER) {
 		hsr_addr_subst_dest(frame->node_src, skb, port);
 
diff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile
index 31fb9326cf533..87fe34951b8b0 100644
--- a/tools/testing/selftests/net/hsr/Makefile
+++ b/tools/testing/selftests/net/hsr/Makefile
@@ -5,6 +5,7 @@ top_srcdir = ../../../../..
 TEST_PROGS := \
 	hsr_ping.sh \
 	hsr_redbox.sh \
+	hsr_shared_mutation.sh \
 	link_faults.sh \
 	prp_ping.sh \
 # end of TEST_PROGS
diff --git a/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh
new file mode 100755
index 0000000000000..05e7e803d2867
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh
@@ -0,0 +1,223 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Verify that per-egress mutations of shared skb data are private:
+#
+# F2 (path/LAN ID): on an affected kernel the second slave's LAN-ID write
+# lands in the first slave's still-queued clone; with a netem delay on
+# slave A, injected frames leave A carrying B's LAN ID.
+#
+# F1 (RedBox source MAC): on an affected kernel an HSR-tagged multicast
+# frame received on a RedBox slave is cloned for master and interlink,
+# and the interlink's RedBox-MAC rewrite lands in the master clone's
+# buffer, so the local stack receives the RedBox MAC instead of the
+# originating node's MAC.
+
+ipv6=false
+
+source ./hsr_common.sh
+
+DUR=5
+
+require()
+{
+	command -v "$1" >/dev/null 2>&1 && return 0
+	echo "SKIP: $1 not available"
+	exit $ksft_skip
+}
+
+require ip
+require tc
+require python3
+
+trap cleanup_all_ns EXIT
+
+# ------------------------------------------------------- F2: LAN-ID isolation
+# PRP DANP (proto 1), AF_PACKET pre-tagged injection, netem on slave A.
+run_f2()
+{
+	setup_ns ns 2>/dev/null || return $ksft_skip
+	nsx() { ip netns exec "$ns" "$@"; }
+
+	# Probe sch_netem inside the disposable namespace only.
+	if ! nsx tc qdisc add dev lo root netem delay 1ms 2>/dev/null; then
+		echo "SKIP: sch_netem not available"
+		return $ksft_skip
+	fi
+	nsx tc qdisc del dev lo root 2>/dev/null
+
+	# Capability probes end here; setup or runtime failure below is FAIL.
+	nsx ip link add vA type veth peer name vAp ||
+		{ echo "FAIL: veth A"; return 1; }
+	nsx ip link add vB type veth peer name vBp ||
+		{ echo "FAIL: veth B"; return 1; }
+	for i in vA vB vAp vBp; do
+		nsx ip link set "$i" up || { echo "FAIL: $i up"; return 1; }
+	done
+	if ! nsx ip link add name prp0 type hsr slave1 vA slave2 vB \
+		supervision 45 proto 1 2>/dev/null; then
+		echo "SKIP: HSR/PRP not supported by this kernel"
+		return $ksft_skip
+	fi
+	nsx ip link set prp0 up || { echo "FAIL: prp0 up"; return 1; }
+	nsx tc qdisc add dev vA root netem delay 200ms ||
+		{ echo "FAIL: netem"; return 1; }
+
+	nsx python3 /dev/stdin "$DUR" <<'PYF2'
+import socket, struct, select, sys, time
+
+dur = int(sys.argv[1])
+def lanid(pkt):
+    if len(pkt) < 20 or pkt[-2:] != b"\x88\xfb":
+        return None
+    return (pkt[-4] >> 4) & 0xF
+
+SRC = bytes.fromhex(open("/sys/class/net/prp0/address").read().replace(":", ""))
+DST = bytes.fromhex("02aabbccdd01")
+PAY = bytes(range(46))
+rct0 = struct.pack(">H", 0) + struct.pack(">H", 52 & 0x0FFF) + b"\x88\xfb"
+frame = DST + SRC + b"\x08\x00" + PAY + rct0
+
+tx = socket.socket(socket.AF_PACKET, socket.SOCK_RAW); tx.bind(("prp0", 0))
+sA = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+        socket.ntohs(0x0003))
+sA.bind(("vAp", 0))
+sB = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+        socket.ntohs(0x0003))
+sB.bind(("vBp", 0))
+sA.setblocking(False); sB.setblocking(False)
+for _ in range(200):
+    tx.send(frame); time.sleep(0.001)
+
+a, b = [], []
+end = time.time() + dur
+while time.time() < end:
+    r, _, _ = select.select([sA, sB], [], [], 0.3)
+    for s in r:
+        pkt = s.recv(65535)
+        if (pkt[:6] != DST or pkt[6:12] != SRC or pkt[12:14] != b"\x08\x00"
+                or pkt[14:14 + len(PAY)] != PAY):
+            continue
+        lid = lanid(pkt)
+        if lid is not None:
+            (a if s is sA else b).append(lid)
+
+print("A-side count=%d lan ids=%s" % (len(a), sorted(set(a))))
+print("B-side count=%d lan ids=%s" % (len(b), sorted(set(b))))
+if len(a) < 150 or len(b) < 150:
+    print("FAIL: too few injected frames captured (A=%d B=%d, sent 200)"
+          % (len(a), len(b)))
+    sys.exit(1)
+bad_a = [x for x in a if (x & 1) != 0]
+bad_b = [x for x in b if (x & 1) != 1]
+if bad_a or bad_b:
+    print("FAIL: shared-mutation corruption - A: %d/%d wrong-lan,"
+          " B: %d/%d wrong-lan"
+          % (len(bad_a), len(a), len(bad_b), len(b)))
+    sys.exit(1)
+print("PASS: per-egress LAN IDs isolated (A all bit0=0, B all bit0=1)")
+sys.exit(0)
+PYF2
+}
+
+# --------------------------------------------- F1: RedBox source-MAC privacy
+# HSR RedBox (proto 0), tagged multicast from a slave: master must keep
+# the node MAC, interlink must carry the RedBox MAC.
+run_f1()
+{
+	setup_ns ns 2>/dev/null || return $ksft_skip
+	nsx() { ip netns exec "$ns" "$@"; }
+
+	nsx ip link add vA type veth peer name vAp ||
+		{ echo "FAIL: veth A"; return 1; }
+	nsx ip link add vB type veth peer name vBp ||
+		{ echo "FAIL: veth B"; return 1; }
+	nsx ip link add vI type veth peer name vIp ||
+		{ echo "FAIL: veth I"; return 1; }
+	for i in vA vB vI vAp vBp vIp; do
+		nsx ip link set "$i" up || { echo "FAIL: $i up"; return 1; }
+	done
+	if ! nsx ip link add name hsr0 type hsr slave1 vA slave2 vB \
+		interlink vI supervision 45 proto 0 2>/dev/null; then
+		echo "SKIP: HSR RedBox not supported by this kernel"
+		return $ksft_skip
+	fi
+	nsx ip link set hsr0 up || { echo "FAIL: hsr0 up"; return 1; }
+
+	nsx python3 /dev/stdin <<'PYF1'
+import socket, select, sys, time
+
+NODE  = bytes.fromhex("021122334455")
+MCAST = bytes.fromhex("01005e000001")
+RB    = bytes.fromhex(open("/sys/class/net/vI/address").read().replace(":", ""))
+PAY   = bytes(range(46))
+
+def frame(seq):
+    tag = (((1 << 12) | len(PAY)).to_bytes(2, "big")
+           + seq.to_bytes(2, "big") + b"\x08\x00")
+    return MCAST + NODE + b"\x89\x2f" + tag + PAY
+
+tx = socket.socket(socket.AF_PACKET, socket.SOCK_RAW); tx.bind(("vAp", 0))
+sm = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+        socket.ntohs(0x0003))
+sm.bind(("hsr0", 0))
+si = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+        socket.ntohs(0x0003))
+si.bind(("vIp", 0))
+sm.setblocking(False); si.setblocking(False)
+
+for i in range(3):
+    tx.send(frame(i + 1)); time.sleep(0.05)
+
+m_src = i_src = None
+end = time.time() + 4
+while time.time() < end and (m_src is None or i_src is None):
+    r, _, _ = select.select([sm, si], [], [], 0.3)
+    for s in r:
+        pkt = s.recv(65535)
+        # exact flow: dst, post-strip EtherType, exact payload, min length;
+        # h_source is the asserted value and must NOT be filtered on
+        if (len(pkt) < 60 or pkt[:6] != MCAST or pkt[12:14] != b"\x08\x00"
+                or pkt[14:14 + len(PAY)] != PAY):
+            continue
+        if s is sm and m_src is None:
+            m_src = pkt[6:12]
+        elif s is si and i_src is None:
+            i_src = pkt[6:12]
+
+print("master h_source    =", m_src.hex() if m_src else None)
+print("node MAC           =", NODE.hex())
+print("interlink h_source =", i_src.hex() if i_src else None)
+print("redbox MAC         =", RB.hex())
+if i_src != RB:
+    print("FAIL: interlink did not carry the RedBox MAC")
+    sys.exit(1)
+if m_src != NODE:
+    print("FAIL: master received %s instead of the node MAC "
+          "(shared-mutation corruption)"
+          % (m_src.hex() if m_src else "nothing"))
+    sys.exit(1)
+print("PASS: master kept node MAC, interlink kept RedBox MAC")
+sys.exit(0)
+PYF1
+}
+
+rc=0
+
+run_f2
+ret=$?
+[ "$ret" -eq "$ksft_skip" ] && exit "$ksft_skip"
+[ "$ret" -eq 0 ] || rc=1
+
+run_f1
+ret=$?
+[ "$ret" -eq "$ksft_skip" ] && exit "$ksft_skip"
+[ "$ret" -eq 0 ] || rc=1
+
+if [ $rc -eq 0 ]; then
+	echo "hsr_shared_mutation: per-egress mutation isolation (F1+F2) [ OK ]"
+else
+	echo "hsr_shared_mutation: per-egress mutation isolation [ FAIL ]" \
+		"rc=$rc" 1>&2
+fi
+exit $rc

3/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6440
							output: 28
							thoughts: 254
						

Thoughts:
**Analyzing the HSR/PRP Shared-Mutation Bug Fix**

Okay, so I've got a patch here that's hitting `net/hsr/hsr_forward.c` and introducing a new selftest in `tools/testing/selftests/net/hsr/hsr_shared_mutation.sh`. My initial scan tells me this is addressing a shared-mutation bug within the HSR/PRP protocols. Specifically, the issue revolves around `skb_clone`. It looks like we were cloning SKBs but then directly modifying the *cloned* SKB without using `skb_cow` or `skb_copy` first. That's a classic mistake – modifying a clone without ensuring the original data is protected. This would lead to data corruption in the original SKB, shared with other clones, a significant vulnerability.

The fix seems straightforward enough. It adds `skb_cow(skb, 0)` before the modification steps. Let me make sure I've got the context correct. Looks like this affects `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit` within the HSR network driver. Quick reminder, these are all part of the HSR stack, and that functionality is configured with `CONFIG_HSR`.

Right, time to dig in and verify the logic around those functions. I'll need to confirm that `skb_cow` is correctly implemented now in all the listed functions. I'll also need to see if the new selftest appropriately covers the bug scenario. We can't have this again.



4/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "config HSR",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/Kconfig-5-\nnet/hsr/Kconfig:6:config HSR\nnet/hsr/Kconfig-7-\ttristate \"High-availability Seamless Redundancy (HSR \u0026 PRP)\"\n"
}

5/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7136
							output: 33
							thoughts: 17
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Analyzing Dependencies in the `net/hsr/Kconfig` File**

Alright, before I dive in too deep, I need to understand the architecture, which is a good habit. My initial instinct is to go directly to the `net/hsr/Kconfig` file. I'm going to navigate to that Kconfig file. I want to see a clear map of the dependencies and configuration options within the HSR (High-availability Seamless Redundancy) module. I know that file will contain the crucial `depends on` statements and conditionals that govern which features are enabled, and which aren't, based on the overall kernel configuration. I need to quickly identify the crucial variables and conditions that drive the HSR module's build process. Essentially, I'm aiming for a quick scan of the configuration dependencies.



6/2 2026/08/03 00:58 tool read-file
0m Args:
{
  "File": "net/hsr/Kconfig",
  "FirstLine": 1,
  "LineCount": 20
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\t#\n   3:\t# IEC 62439-3 High-availability Seamless Redundancy\n   4:\t#\n   5:\t\n   6:\tconfig HSR\n   7:\t\ttristate \"High-availability Seamless Redundancy (HSR \u0026 PRP)\"\n   8:\t\thelp\n   9:\t\t  This enables IEC 62439 defined High-availability Seamless\n  10:\t\t  Redundancy (HSR) and Parallel Redundancy Protocol (PRP).\n  11:\t\n  12:\t\t  If you say Y here, then your Linux box will be able to act as a\n  13:\t\t  DANH (\"Doubly attached node implementing HSR\") or DANP (\"Doubly\n  14:\t\t  attached node implementing PRP\"). For this to work, your Linux box\n  15:\t\t  needs (at least) two physical Ethernet interfaces.\n  16:\t\n  17:\t\t  For DANH, it must be connected as a node in a ring network together\n  18:\t\t  with other HSR capable nodes. All Ethernet frames sent over the HSR\n  19:\t\t  device will be sent in both directions on the ring (over both slave\n  20:\t\t  ports), giving a redundant, instant fail-over network. Each HSR node\n"
}

7/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7712
							output: 33
							thoughts: 68
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the HSR Configuration and Forwarding**

Okay, so I'm looking at this HSR configuration, and it seems pretty straightforward. The config itself, let's just call it `HSR`, doesn't look like it has any crazy dependencies – standard networking is all that's required, which is what I'd expect. Now, I want to see how these tagged frames are created and handled. My primary interest lies in the core forwarding logic.

I think I'll start by examining the functions `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit` within the `net/hsr/hsr_forward.c` file. Those three seem like the logical place to start digging into the details of the tagging and transmission processes. I'm especially interested in how the HSR and PRP tagging mechanisms interact. I want to see how the frames are constructed and how they get sent out.



8/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_create_tagged_frame",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_device.c=621=static const struct hsr_proto_ops hsr_ops = {\nnet/hsr/hsr_device.c-622-\t.send_sv_frame = send_hsr_supervision_frame,\nnet/hsr/hsr_device.c:623:\t.create_tagged_frame = hsr_create_tagged_frame,\nnet/hsr/hsr_device.c-624-\t.get_untagged_frame = hsr_get_untagged_frame,\n--\nnet/hsr/hsr_forward.c=288=static struct sk_buff *hsr_fill_tag(struct sk_buff *skb,\n--\nnet/hsr/hsr_forward.c-330- */\nnet/hsr/hsr_forward.c:331:struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.c-332-\t\t\t\t\tstruct hsr_port *port)\n--\nnet/hsr/hsr_forward.h=17=struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.h-18-\t\t\t\t\tstruct hsr_port *port);\nnet/hsr/hsr_forward.h:19:struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.h-20-\t\t\t\t\tstruct hsr_port *port);\n"
}

9/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8448
							output: 30
							thoughts: 101
						

Thoughts:
Okay, here's my understanding, considering I'm deep in this code:

**Analyzing Function Scope**

Right, so I'm looking at these functions: `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. The immediate question is always about scope – are these static, or are they global? The behavior will be very different. Alright, let's see. Looks like `hsr_create_tagged_frame` is global, and `prp_create_tagged_frame` is also globally accessible. Hmm, but then here's `hsr_xmit`, flagged as static. Okay, good. Let's make absolutely sure about the `hsr_xmit` scoping as that could be a potential point of divergence later.



10/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_xmit",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_forward.c=424=static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,\n--\nnet/hsr/hsr_forward.c-444-\nnet/hsr/hsr_forward.c:445:static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,\nnet/hsr/hsr_forward.c-446-\t\t    struct hsr_frame_info *frame)\n--\nnet/hsr/hsr_forward.c=560=static void hsr_forward_do(struct hsr_frame_info *frame)\n--\nnet/hsr/hsr_forward.c-621-\t\t} else {\nnet/hsr/hsr_forward.c:622:\t\t\tif (!hsr_xmit(skb, port, frame))\nnet/hsr/hsr_forward.c-623-\t\t\t\tif (port-\u003etype == HSR_PT_SLAVE_A ||\n"
}

11/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9024
							output: 31
							thoughts: 66
						

Thoughts:
Here's a breakdown of my thought process:

**Diving into the `hsr_xmit` Code**

Okay, so `hsr_xmit` is static, which tells me it's likely a core internal function within this HSR (High-availability Seamless Redundancy) module. That means it's probably not directly called from outside, but rather acts as the workhorse for sending frames. Given that, I need to figure out how *other* functions interact with it. The entry points, the functions that kick things off, are probably `hsr_forward_do`, `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_forward_skb`. Those functions are the front doors, the initiators of the process. Time to verify that hypothesis. Let's start with `hsr_forward_do` and see what it does. I'll focus my attention there and see what it tells me.



12/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_forward_do",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_forward.c=492=bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)\n--\nnet/hsr/hsr_forward.c-559- */\nnet/hsr/hsr_forward.c:560:static void hsr_forward_do(struct hsr_frame_info *frame)\nnet/hsr/hsr_forward.c-561-{\n--\nnet/hsr/hsr_forward.c=775=void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\n--\nnet/hsr/hsr_forward.c-783-\thsr_register_frame_in(frame.node_src, port, frame.sequence_nr);\nnet/hsr/hsr_forward.c:784:\thsr_forward_do(\u0026frame);\nnet/hsr/hsr_forward.c-785-\trcu_read_unlock();\n"
}

13/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9598
							output: 32
							thoughts: 12
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm an expert in network kernel internals, specifically focusing on packet processing.

**Analyzing the Significance of a Global Variable: `hsr_forward_skb`**

Alright, so `hsr_forward_skb` is global, huh? Immediately, my mind starts racing. A global variable in this context is rarely a good thing, unless its use is *very* carefully managed. My initial reaction is a mix of intrigue and concern. 

`hsr_forward_skb` suggests a structure, probably a `sk_buff`, is being used to forward packets in an HSR (High-availability Seamless Redundancy) context. The fact that it's global implies it’s probably accessible from multiple threads or interrupt contexts, which raises several red flags. Is it adequately protected? Are there potential race conditions lurking?

I need to quickly assess its scope. What modules access this variable? Where is it defined and initialized? How is it modified? Is there a spinlock, mutex, or RCU mechanism employed for synchronization? A global `skb` pointer screams for concurrent access scrutiny.

The use of a global `skb` is also a bit unusual. Usually, we'd pass the `sk_buff` around function arguments. Perhaps it’s a temporary buffer for re-encapsulation or frame construction in the HSR path. I'm wondering if it's being used for inter-CPU communication, or if the function is just sharing a buffer.

I need to dive deeper into the code using this variable, I need to know exactly how it is used. I am most interested in how the variable is shared and accessed, it all hinges on correct synchronization.



14/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_forward_skb",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_device.c=224=static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/hsr/hsr_device.c-235-\t\tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\nnet/hsr/hsr_device.c:236:\t\thsr_forward_skb(skb, master);\nnet/hsr/hsr_device.c-237-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n--\nnet/hsr/hsr_device.c=293=static void send_hsr_supervision_frame(struct hsr_port *port,\n--\nnet/hsr/hsr_device.c-363-\nnet/hsr/hsr_device.c:364:\thsr_forward_skb(skb, port);\nnet/hsr/hsr_device.c-365-\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n--\nnet/hsr/hsr_device.c=369=static void send_prp_supervision_frame(struct hsr_port *master,\n--\nnet/hsr/hsr_device.c-404-\nnet/hsr/hsr_device.c:405:\thsr_forward_skb(skb, master);\nnet/hsr/hsr_device.c-406-\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\n--\nnet/hsr/hsr_forward.c=717=static int fill_frame_info(struct hsr_frame_info *frame,\n--\nnet/hsr/hsr_forward.c-774-/* Must be called holding rcu read lock (because of the port parameter) */\nnet/hsr/hsr_forward.c:775:void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)\nnet/hsr/hsr_forward.c-776-{\n--\nnet/hsr/hsr_forward.h-15-\nnet/hsr/hsr_forward.h:16:void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);\nnet/hsr/hsr_forward.h-17-struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\n--\nnet/hsr/hsr_slave.c=24=static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)\n--\nnet/hsr/hsr_slave.c-80-\t\tspin_lock_bh(\u0026hsr-\u003eseqnr_lock);\nnet/hsr/hsr_slave.c:81:\t\thsr_forward_skb(skb, port);\nnet/hsr/hsr_slave.c-82-\t\tspin_unlock_bh(\u0026hsr-\u003eseqnr_lock);\nnet/hsr/hsr_slave.c-83-\t} else {\nnet/hsr/hsr_slave.c:84:\t\thsr_forward_skb(skb, port);\nnet/hsr/hsr_slave.c-85-\t}\n"
}

15/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10842
							output: 30
							thoughts: 75
						

Thoughts:
Okay, let's break this down.

**Diving into the HSR Forwarding Mechanism**

Right, so `hsr_forward_skb` is the prime directive, the main entry point for the packet forwarding process. That's where it all begins. Now, I see `hsr_create_tagged_frame` and `prp_create_tagged_frame` are used by the forwarding functions, and I suspect these are the functions that actually build the tagged frames. Given that, I need to understand the relationship and, specifically, how the tagging is kicked off and controlled. I'll need to go deeper into `hsr_forward_do` to figure out the operational details and how the call-tree works. I'll see where those frame creation functions fit into the puzzle. Specifically, I need to see *how* they're called from within `hsr_forward_do`, likely through some sort of function pointer or operation structure. Let's get to it.



16/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "create_tagged_frame",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_device.c=621=static const struct hsr_proto_ops hsr_ops = {\nnet/hsr/hsr_device.c-622-\t.send_sv_frame = send_hsr_supervision_frame,\nnet/hsr/hsr_device.c:623:\t.create_tagged_frame = hsr_create_tagged_frame,\nnet/hsr/hsr_device.c-624-\t.get_untagged_frame = hsr_get_untagged_frame,\n--\nnet/hsr/hsr_device.c=631=static const struct hsr_proto_ops prp_ops = {\nnet/hsr/hsr_device.c-632-\t.send_sv_frame = send_prp_supervision_frame,\nnet/hsr/hsr_device.c:633:\t.create_tagged_frame = prp_create_tagged_frame,\nnet/hsr/hsr_device.c-634-\t.get_untagged_frame = prp_get_untagged_frame,\n--\nnet/hsr/hsr_forward.c=288=static struct sk_buff *hsr_fill_tag(struct sk_buff *skb,\n--\nnet/hsr/hsr_forward.c-330- */\nnet/hsr/hsr_forward.c:331:struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.c-332-\t\t\t\t\tstruct hsr_port *port)\n--\nnet/hsr/hsr_forward.c-385-\nnet/hsr/hsr_forward.c:386:struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.c-387-\t\t\t\t\tstruct hsr_port *port)\n--\nnet/hsr/hsr_forward.c=560=static void hsr_forward_do(struct hsr_frame_info *frame)\n--\nnet/hsr/hsr_forward.c-608-\t\t    port-\u003etype == HSR_PT_SLAVE_B)\nnet/hsr/hsr_forward.c:609:\t\t\tskb = hsr-\u003eproto_ops-\u003ecreate_tagged_frame(frame, port);\nnet/hsr/hsr_forward.c-610-\t\telse\n--\nnet/hsr/hsr_forward.h=16=void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);\nnet/hsr/hsr_forward.h:17:struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.h-18-\t\t\t\t\tstruct hsr_port *port);\nnet/hsr/hsr_forward.h:19:struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,\nnet/hsr/hsr_forward.h-20-\t\t\t\t\tstruct hsr_port *port);\n--\nnet/hsr/hsr_main.h=164=struct hsr_proto_ops {\n--\nnet/hsr/hsr_main.h-172-\t\t\t\t\t       struct hsr_port *port);\nnet/hsr/hsr_main.h:173:\tstruct sk_buff * (*create_tagged_frame)(struct hsr_frame_info *frame,\nnet/hsr/hsr_main.h-174-\t\t\t\t\t\tstruct hsr_port *port);\n"
}

17/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12088
							output: 36
							thoughts: 712
						

Thoughts:
**Analyzing the HSR Patch and Potential Fuzzing Targets**

Okay, so I've been handed this patch and need to figure out the attack surface and how to approach fuzzing it. Let's break this down. From what I see, `hsr_forward_skb` is the central function here. It's the point where packets from both the slave and master devices converge. `hsr_dev_xmit` is the `ndo_start_xmit` for the master, and `hsr_handle_frame` is the `rx_handler` for the slaves. These are all linked together: `hsr_forward_skb` calls `hsr_dev_xmit` and `hsr_handle_frame` to forward packets.

The patch itself touches `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`, all reachable through the core of `hsr_forward_skb`. This is all definitely reachable in a virtualized environment. That's a good starting point. I can set up an HSR device and start sending packets to test this. The relevant config is `CONFIG_HSR`. Good, it's a tristate, and that's the only one of interest, it seems.

Wait, I need to check `hsr_xmit` more carefully. There's a new check inside it. Specifically, inside `hsr_xmit`, there's a new check where if `port->type == HSR_PT_INTERLINK` and a set of other conditions are true, the packet is dropped with `skb_cow`. This could be interesting, and definitely something I should focus on. This seems to be related to interlink-bound SKBs. We want to test this codepath. 

I need to make sure I understand the relationship between these functions, particularly the various calling relationships. I'll need to figure out if `hsr_forward_skb` is exported or if it's just global.

I will need to figure out which packets can trigger that codepath for the `hsr_xmit` changes and craft interesting test cases to exploit potential issues.



18/2 2026/08/03 00:58 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "EXPORT_SYMBOL.*hsr_forward_skb",
  "PathPrefix": "net/hsr/"
}

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

19/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13325
							output: 32
							thoughts: 170
						

Thoughts:
**Analyzing HSR Module Entry Points**

Okay, so I'm looking at this HSR module and just trying to get a handle on its internal structure. From what I can see, a few things are immediately clear about scope: `hsr_create_tagged_frame` and `prp_create_tagged_frame` seem to be globally accessible within the module, while the other functions mentioned are scoped within the module: `hsr`

The more interesting case involves `hsr_dev_xmit` and `hsr_handle_frame`, and since those are static, they have to be important. I need to understand what's going on at the entry points for the HSR driver. They look like prime candidates for deeper investigation.

Ah, right, `hsr_dev_xmit` is the `ndo_start_xmit` function, the likely transmission entry point. And `hsr_handle_frame` is the `rx_handler`, so that's the receive path. Yep, these are definitely good focus symbols for my analysis.

Now I should probably quickly check if I've missed any other entry points. Gotta be thorough.



20/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_dev_xmit",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_device.c=216=static netdev_features_t hsr_fix_features(struct net_device *dev,\n--\nnet/hsr/hsr_device.c-223-\nnet/hsr/hsr_device.c:224:static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)\nnet/hsr/hsr_device.c-225-{\n--\nnet/hsr/hsr_device.c=605=static const struct net_device_ops hsr_device_ops = {\n--\nnet/hsr/hsr_device.c-608-\t.ndo_stop = hsr_dev_close,\nnet/hsr/hsr_device.c:609:\t.ndo_start_xmit = hsr_dev_xmit,\nnet/hsr/hsr_device.c-610-\t.ndo_change_rx_flags = hsr_change_rx_flags,\n--\nnet/hsr/hsr_device.c=671=bool is_hsr_master(struct net_device *dev)\nnet/hsr/hsr_device.c-672-{\nnet/hsr/hsr_device.c:673:\treturn (dev-\u003enetdev_ops-\u003endo_start_xmit == hsr_dev_xmit);\nnet/hsr/hsr_device.c-674-}\n"
}

21/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14154
							output: 31
							thoughts: 40
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Checking the HSR Device Transmission**

Alright, so `hsr_dev_xmit`... yep, that's it. That's the function the HSR device uses as its `ndo_start_xmit`. Now, I need to understand how it handles individual frames. To do that, I should take a look at the `hsr_handle_frame` function. I want to see how the device processes and manages the frames. This will really help me understand the overall transmission behavior.



22/2 2026/08/03 00:58 tool grepper
0m Args:
{
  "Expression": "hsr_handle_frame",
  "PathPrefix": "net/hsr/"
}

Results:
{
  "Output": "net/hsr/hsr_device.c=720=int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],\n--\nnet/hsr/hsr_device.c-746-\nnet/hsr/hsr_device.c:747:\t/* Make sure we recognize frames from ourselves in hsr_handle_frame() */\nnet/hsr/hsr_device.c-748-\tres = hsr_create_self_node(hsr, hsr_dev-\u003edev_addr,\n--\nnet/hsr/hsr_framereg.c=66=bool hsr_is_node_in_db(struct list_head *node_db,\n--\nnet/hsr/hsr_framereg.c-71-\nnet/hsr/hsr_framereg.c:72:/* Helper for device init; the self_node is used in hsr_handle_frame() to\nnet/hsr/hsr_framereg.c-73- * recognize frames from self that's been looped over the HSR ring.\n--\nnet/hsr/hsr_main.c=31=static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,\n--\nnet/hsr/hsr_main.c-91-\nnet/hsr/hsr_main.c:92:\t\t/* Make sure we recognize frames from ourselves in hsr_handle_frame() */\nnet/hsr/hsr_main.c-93-\t\tport = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);\n--\nnet/hsr/hsr_slave.c=19=bool hsr_invalid_dan_ingress_frame(__be16 protocol)\n--\nnet/hsr/hsr_slave.c-23-\nnet/hsr/hsr_slave.c:24:static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)\nnet/hsr/hsr_slave.c-25-{\n--\nnet/hsr/hsr_slave.c=94=bool hsr_port_exists(const struct net_device *dev)\nnet/hsr/hsr_slave.c-95-{\nnet/hsr/hsr_slave.c:96:\treturn rcu_access_pointer(dev-\u003erx_handler) == hsr_handle_frame;\nnet/hsr/hsr_slave.c-97-}\n--\nnet/hsr/hsr_slave.c=141=static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,\n--\nnet/hsr/hsr_slave.c-168-\nnet/hsr/hsr_slave.c:169:\tres = netdev_rx_handler_register(dev, hsr_handle_frame, port);\nnet/hsr/hsr_slave.c-170-\tif (res)\n"
}

23/2 2026/08/03 00:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15106
							output: 196
							thoughts: 556
						

Thoughts:
**Fuzzing Analysis: HSR Shared-Mutation Bug Fix**

Okay, so I've been looking at this patch and, yes, `hsr_handle_frame` is the receive handler for the slave HSR devices. That's a key function. Given the nature of the fix, my focus needs to be on these symbols: `hsr_dev_xmit`, `hsr_handle_frame`, `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_forward_skb`. These functions seem central to the problem.

Let me just quickly confirm the necessary configuration. It looks like it just requires `HSR`. Good. I'll make sure to note that.

Aha! There's also a modification to a selftest script (`tools/testing/selftests/net/hsr/hsr_shared_mutation.sh`). That's not the target. While it's good the self test is included it does not need to be fuzzed. The real kernel changes are in `net/hsr/hsr_forward.c`, and those are what I'm interested in fuzzing.

Now, let's think about *why* this is fuzzable. The patch addresses a shared-mutation bug in HSR/PRP where cloned `skb`s were being directly modified without proper copy-on-write mechanisms (`skb_cow` or `skb_copy`). This would corrupt the original shared data. The fix involves adding `skb_cow(skb, 0)` before modifying the cloned `skb` in functions like `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. These functions, crucially, are reachable from `hsr_forward_skb`, which in turn is reachable through `hsr_dev_xmit` and `hsr_handle_frame`. I can definitely see this being triggered in a virtualized environment. Setting up an HSR device and sending packets through it seems like a straightforward way to test this.

Alright, let's document my findings concisely:

*   **WorthFuzzing:** True
*   **FocusSymbols:** `hsr_dev_xmit`, `hsr_handle_frame`, `hsr_create_tagged_frame`, `prp_create_tagged_frame`, `hsr_forward_skb`
*   **EnableConfigs:** `HSR`
*   **Reasoning:** The patch adds `skb_cow` before mutating cloned skbs to fix a shared-mutation bug in HSR/PRP. Core packet forwarding functions in HSR driver reachable via `hsr_dev_xmit` (master TX) and `hsr_handle_frame` (slave RX). Functional and reachable in a virtualized environment.



24/2 2026/08/03 00:59 tool set-results
0m Args:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_dev_xmit",
    "hsr_handle_frame",
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation bug in HSR/PRP by adding `skb_cow` before modifying cloned skbs in `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. These are core packet forwarding functions in the HSR driver, reachable via `hsr_dev_xmit` (master device TX) and `hsr_handle_frame` (slave device RX). The code is fully functional and reachable in a virtualized environment.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_dev_xmit",
    "hsr_handle_frame",
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation bug in HSR/PRP by adding `skb_cow` before modifying cloned skbs in `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. These are core packet forwarding functions in the HSR driver, reachable via `hsr_dev_xmit` (master device TX) and `hsr_handle_frame` (slave device RX). The code is fully functional and reachable in a virtualized environment.",
  "WorthFuzzing": true
}

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)