AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/29 18:55 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation issue in HSR and PRP by cloning the skb and making it private before modifying it. This is a functional change in the core HSR logic that is reachable in virtualized environments.",
  "WorthFuzzing": true
}

1/1 2026/07/29 18:55 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 3a0ab0593951704e25d2b8958ded8dcecd359f05\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Jul 29 18:55:45 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..aeae7f3d6dd14 100644\n--- a/tools/testing/selftests/net/hsr/Makefile\n+++ b/tools/testing/selftests/net/hsr/Makefile\n@@ -7,6 +7,7 @@ TEST_PROGS := \\\n \thsr_redbox.sh \\\n \tlink_faults.sh \\\n \tprp_ping.sh \\\n+\thsr_shared_mutation.sh \\\n # end of TEST_PROGS\n \n TEST_FILES += hsr_common.sh\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..27103541cd78c\n--- /dev/null\n+++ b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh\n@@ -0,0 +1,213 @@\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 || { echo \"FAIL: veth A\"; return 1; }\n+\tnsx ip link add vB type veth peer name vBp || { 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+\tnsx ip link add name prp0 type hsr slave1 vA slave2 vB supervision 45 proto 1 2\u003e/dev/null\n+\tif [ $? -ne 0 ]; 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 || { 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)\" % (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, 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 || { echo \"FAIL: veth A\"; return 1; }\n+\tnsx ip link add vB type veth peer name vBp || { echo \"FAIL: veth B\"; return 1; }\n+\tnsx ip link add vI type veth peer name vIp || { 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+\tnsx ip link add name hsr0 type hsr slave1 vA slave2 vB interlink vI \\\n+\t\tsupervision 45 proto 0 2\u003e/dev/null\n+\tif [ $? -ne 0 ]; 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\") + 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)\" % (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 ] rc=$rc\" 1\u003e\u00262\n+fi\n+exit $rc\n"
}

2/1 2026/07/29 18:55 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation issue in HSR and PRP by cloning the skb and making it private before modifying it. This is a functional change in the core HSR logic that is reachable in virtualized environments.",
  "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 3a0ab0593951704e25d2b8958ded8dcecd359f05
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Jul 29 18:55:45 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..aeae7f3d6dd14 100644
--- a/tools/testing/selftests/net/hsr/Makefile
+++ b/tools/testing/selftests/net/hsr/Makefile
@@ -7,6 +7,7 @@ TEST_PROGS := \
 	hsr_redbox.sh \
 	link_faults.sh \
 	prp_ping.sh \
+	hsr_shared_mutation.sh \
 # end of TEST_PROGS
 
 TEST_FILES += hsr_common.sh
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..27103541cd78c
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/hsr_shared_mutation.sh
@@ -0,0 +1,213 @@
+#!/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
+	nsx ip link add name prp0 type hsr slave1 vA slave2 vB supervision 45 proto 1 2>/dev/null
+	if [ $? -ne 0 ]; 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
+	nsx ip link add name hsr0 type hsr slave1 vA slave2 vB interlink vI \
+		supervision 45 proto 0 2>/dev/null
+	if [ $? -ne 0 ]; 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/07/29 18:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6404
							output: 30
							thoughts: 312
						

Thoughts:
**Analyzing the HSR Patch and its Implications**

Okay, so I've got this patch – seems like a fix for a shared-mutation issue in HSR and PRP, specifically within the `net/hsr/hsr_forward.c` file. Interesting. The patch is also adding a new test, `tools/testing/selftests/net/hsr/hsr_shared_mutation.sh`, which should be helpful for validating the fix.

The core of the problem, as I understand it, lies in how frames are cloned when sent out to multiple egress ports. The fix involves using `skb_clone` and `skb_cow` in the `hsr_create_tagged_frame` and `prp_create_tagged_frame` functions to ensure that modifying the path ID or LAN ID on a cloned frame doesn't corrupt the original data. This makes perfect sense; shared data corruption is a nasty bug. The addition of an `skb_cow` check in `hsr_xmit` for interlink-bound skbs seems like a necessary precaution to catch anything that may have been missed.

Now, let's take a closer look at the actual code in `net/hsr/hsr_forward.c`. This is where the core HSR logic resides, so understanding it thoroughly is crucial. Given that HSR is a network protocol, I need to make sure this code path is actually reachable within my standard QEMU setup. Since HSR is implemented as a software network device, similar to a bridge or VLAN, then I would expect it to be.

The test script itself confirms this, using `ip link add name prp0 type hsr slave1 vA slave2 vB supervision 45 proto 1` which suggests that it's a virtual network device. This confirms my understanding.

Next, I need to determine how HSR is configured within the kernel. I'll search for the relevant Kconfig options – likely looking for "hsr" in `net/hsr/Kconfig` or a similar file. This will help me understand how this functionality is enabled and how it interacts with other parts of the network stack. It's important to know the configuration options for a complete picture.



4/2 2026/07/29 18:55 tool grepper
0m Args:
{
  "Expression": "config HSR",
  "PathPrefix": "net/hsr/Kconfig"
}

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/07/29 18:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7289
							output: 33
							thoughts: 63
						

Thoughts:
**Analyzing the Impact of CONFIG_HSR**

Okay, so `CONFIG_HSR` is the key configuration option here. My task is to understand its ramifications. Let's dig in and see how it's affecting things.

First, I need to look at the functions that have been modified as a result of enabling/disabling `CONFIG_HSR`. It seems like `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit` are the areas of interest, the specific details of these function calls are very important. Now, to get a clear picture of their role and accessibility I have to determine whether these functions are internal (static) or visible outside the module (exported). That will give me the information I require to determine how this configuration change affects the larger system.



6/2 2026/07/29 18:55 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"
}

7/2 2026/07/29 18:55 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7972
							output: 30
							thoughts: 40
						

Thoughts:
Here's my take on the thought process, as if it were my own:

**Analyzing Function Attributes**

Okay, so I'm looking at these functions: `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_xmit`. Right away, the critical distinction jumps out. I've got to determine which of these are static and which aren't. Clearly, `hsr_create_tagged_frame` is *not* a static function. Also, `prp_create_tagged_frame` is *also* not a static function. On the other hand, `hsr_xmit` is, in fact, static. This is the key piece of information, and where my analysis will likely diverge, depending on the larger context of what is being examined.



8/2 2026/07/29 18:56 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"
}

9/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8489
							output: 31
							thoughts: 38
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Analyzing the HSR Packet Forwarding Path**

Alright, let's take a closer look at this HSR packet transmission process. My initial focus is on `hsr_xmit`. I see it's declared `static`, which immediately raises a flag. Static means it's limited in scope, likely internal to its compilation unit. Now, given that `hsr_xmit` is being called by `hsr_forward_do`, I'm curious about the calling function's scope. Is `hsr_forward_do` also static? If so, this suggests a tight, localized handling of packet forwarding within a specific module or component. The limited visibility implies a well-defined interaction point, but I need to confirm `hsr_forward_do`'s static nature to get the full picture and potential implications for debugging and modification. I'll check its declaration now to confirm my hypothesis.



10/2 2026/07/29 18:56 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"
}

11/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9041
							output: 32
							thoughts: 27
						

Thoughts:
Here's my take on the situation:

**Investigating the `hsr_forward_skb` Function**

Okay, so I'm taking a look at this `hsr_forward_skb` function, and the first thing I notice is that it's *not* static, which immediately piques my interest. I need to understand its scope and potential interactions more clearly. This means I'll need to dig deeper into the code to figure out why it's structured this way. I will start by carefully examining the code to check exactly what it does.



12/2 2026/07/29 18:56 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"
}

13/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10054
							output: 34
							thoughts: 76
						

Thoughts:
Okay, let's see. My initial hunch is that `hsr_forward_skb` is a pretty central function here, a good place to start digging. And, yeah, `hsr_create_tagged_frame` and `prp_create_tagged_frame` also look like promising focal points. These functions are likely handling the core frame manipulation.

Now, I'm going to shift gears slightly. I'll need to check the broader configuration landscape. Besides the obvious `CONFIG_HSR`, are there any other dependencies I need to be aware of? Are there related features that have their own config options? I want to make sure I get the full picture.



14/2 2026/07/29 18:56 tool read-file
0m Args:
{
  "File": "net/hsr/Kconfig",
  "FirstLine": 1,
  "LineCount": 100
}

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  21:\t\t  in the ring acts like a bridge for HSR frames, but filters frames\n  22:\t\t  that have been forwarded earlier.\n  23:\t\n  24:\t\t  For DANP, it must be connected as a node connecting to two\n  25:\t\t  separate networks over the two slave interfaces. Like HSR, Ethernet\n  26:\t\t  frames sent over the PRP device will be sent to both networks giving\n  27:\t\t  a redundant, instant fail-over network. Unlike HSR, PRP networks\n  28:\t\t  can have Singly Attached Nodes (SAN) such as PC, printer, bridges\n  29:\t\t  etc and will be able to communicate with DANP nodes.\n  30:\t\n  31:\t\t  This code is a \"best effort\" to comply with the HSR standard as\n  32:\t\t  described in IEC 62439-3:2010 (HSRv0) and IEC 62439-3:2012 (HSRv1),\n  33:\t\t  and PRP standard described in IEC 62439-4:2012 (PRP), but no\n  34:\t\t  compliancy tests have been made. Use iproute2 to select the protocol\n  35:\t\t  you would like to use.\n  36:\t\n  37:\t\t  You need to perform any and all necessary tests yourself before\n  38:\t\t  relying on this code in a safety critical system!\n  39:\t\n  40:\t\t  If unsure, say N.\n  41:\t\n  42:\tif HSR\n  43:\t\n  44:\tconfig PRP_DUP_DISCARD_KUNIT_TEST\n  45:\t\ttristate \"PRP duplicate discard KUnit tests\" if !KUNIT_ALL_TESTS\n  46:\t\tdepends on KUNIT\n  47:\t\tdefault KUNIT_ALL_TESTS\n  48:\t\thelp\n  49:\t\t  Covers the PRP duplicate discard algorithm.\n  50:\t\t  Only useful for kernel devs running KUnit test harness and are not\n  51:\t\t  for inclusion into a production build.\n  52:\t\n  53:\t\t  For more information on KUnit and unit tests in general please refer\n  54:\t\t  to the KUnit documentation in Documentation/dev-tools/kunit/.\n  55:\t\n  56:\t\t  If unsure, say N.\n  57:\t\n  58:\tendif\n"
}

15/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11245
							output: 25
							thoughts: 62
						

Thoughts:
Okay, let's see what we have here.

**Navigating Dependencies in the Network Stack**

Hmm, `CONFIG_HSR` doesn't seem to have any explicit `depends on` statements within *this* specific configuration file. That's a little unusual. It's in the `net/hsr/` directory though, which usually implies some relationship with the broader network subsystem. Let me check... Ah, right, that points to `NET`.

Okay, so I suspect it *implicitly* depends on `NET`. To be certain, I should check the `net/Makefile` to see how the `hsr` directory and its associated source files are actually incorporated into the build process. That should show any direct or indirect dependencies. Let's see what's what.



16/2 2026/07/29 18:56 tool grepper
0m Args:
{
  "Expression": "hsr",
  "PathPrefix": "net/Makefile"
}

Results:
{
  "Output": "net/Makefile=67=obj-$(CONFIG_NET_NSH)\t\t+= nsh/\nnet/Makefile:68:obj-$(CONFIG_HSR)\t\t+= hsr/\nnet/Makefile-69-obj-$(CONFIG_NET_SWITCHDEV)\t+= switchdev/\n"
}

17/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11578
							output: 72
							thoughts: 220
						

Thoughts:
**Analyzing the HSR Patch**

Okay, so it looks like `CONFIG_HSR` is the only config option I need to be concerned with here. Let's go over these changes again. It seems we have a patch that's modifying `net/hsr/hsr_forward.c`, focusing on a shared-mutation issue. The fix involves cloning the skb before modifying it, which makes sense to ensure isolation and prevent unexpected side effects. This is a pretty fundamental functional change in the HSR logic.

Given that HSR is a software network device, it should be easily reachable in a QEMU environment, which is good for testing. The inclusion of a selftest, `hsr_shared_mutation.sh`, is a solid addition. The script uses `ip link add` to create an HSR interface with the typical PRP configuration, which tells me this can be effectively tested in a virtual environment. Specifically `prp0` `slave1` `slave2`. Nice, good to see they're explicitly defining the virtual network device's nature.

I'll want to pay close attention to `hsr_create_tagged_frame`, `prp_create_tagged_frame`, and `hsr_forward_skb` within the code. Those are the key symbols to understand the flow and logic of the fix.

Before I dig into the details, I should quickly check if there are any other non-static functions within `net/hsr/hsr_forward.c`. I need to ensure there are no other points of potential interaction that might be relevant.



18/2 2026/07/29 18:56 tool grepper
0m Args:
{
  "Expression": "^[a-zA-Z_][a-zA-Z0-9_]*\\s+\\*?[a-zA-Z_][a-zA-Z0-9_]*\\(",
  "PathPrefix": "net/hsr/hsr_forward.c"
}

Results:
{
  "Output": "net/hsr/hsr_forward.c=445=static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,\n--\nnet/hsr/hsr_forward.c-483-\nnet/hsr/hsr_forward.c:484:bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)\nnet/hsr/hsr_forward.c-485-{\n--\nnet/hsr/hsr_forward.c-491-\nnet/hsr/hsr_forward.c:492:bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)\nnet/hsr/hsr_forward.c-493-{\n--\nnet/hsr/hsr_forward.c=649=static void handle_std_frame(struct sk_buff *skb,\n--\nnet/hsr/hsr_forward.c-670-\nnet/hsr/hsr_forward.c:671:int hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,\nnet/hsr/hsr_forward.c-672-\t\t\tstruct hsr_frame_info *frame)\n--\nnet/hsr/hsr_forward.c-697-\nnet/hsr/hsr_forward.c:698:int prp_fill_frame_info(__be16 proto, struct sk_buff *skb,\nnet/hsr/hsr_forward.c-699-\t\t\tstruct hsr_frame_info *frame)\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"
}

19/2 2026/07/29 18:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12759
							output: 111
							thoughts: 413
						

Thoughts:
**My Analysis of the HSR Patch**

Okay, so I've got this patch for HSR and PRP, and I need to figure out the best way to approach fuzzing it. The main entry point for forwarding an skb in HSR is `hsr_forward_skb`, so that's definitely a key function to keep in mind. The patch seems to be modifying `hsr_create_tagged_frame` and `prp_create_tagged_frame` – those are crucial because they're directly impacted. Let's make absolutely sure I've got the names right: `hsr_create_tagged_frame`, and `prp_create_tagged_frame`. Yup, all good.

Now, a quick check… I saw `hsr_xmit` mentioned, but that's static, so it’s not ideal as a top-level focus. However, `hsr_forward_skb` does call `hsr_forward_do`, which in turn calls `hsr_xmit`, meaning `hsr_forward_skb` is still a relevant focal point and likely more reachable.

Checking for any configuration dependencies, it's just `CONFIG_HSR`. Simple enough.

Alright, what else have we got? The patch modifies `net/hsr/hsr_forward.c` and adds a test case, so it's a functional change with good test coverage, which makes fuzzing it even more valuable. It addresses a shared-mutation issue by cloning the skb before modifying it, which is the kind of vulnerability fuzzing can uncover. This change is absolutely reachable in virtualized environments.

So, here's my plan:

**Focus Symbols:**

*   `hsr_create_tagged_frame`
*   `prp_create_tagged_frame`
*   `hsr_forward_skb`

**Enable Configs:**

*   `CONFIG_HSR`

**Reasoning:**

The patch addresses a shared-mutation vulnerability by cloning the skb before modification, directly impacting HSR and PRP. It's a functional and reachable change with test coverage, making it ideal for fuzzing.
Time to call `set-results`.



20/2 2026/07/29 18:56 tool set-results
0m Args:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation issue in HSR and PRP by cloning the skb and making it private before modifying it. This is a functional change in the core HSR logic that is reachable in virtualized environments.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "HSR"
  ],
  "FocusSymbols": [
    "hsr_create_tagged_frame",
    "prp_create_tagged_frame",
    "hsr_forward_skb"
  ],
  "Reasoning": "The patch fixes a shared-mutation issue in HSR and PRP by cloning the skb and making it private before modifying it. This is a functional change in the core HSR logic that is reachable in virtualized environments.",
  "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)