From: Xin Xie <xiexinet@gmail.com>
To: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com
Cc: edumazet@google.com, horms@kernel.org, shuah@kernel.org,
lukma@denx.de, m-karicheri2@ti.com, netdev@vger.kernel.org,
linux-kselftest@vger.kernel.org, Xin Xie <xiexinet@gmail.com>
Subject: [PATCH net 2/3] net: hsr: clone before updating path and LAN IDs in tagged frames
Date: Tue, 28 Jul 2026 16:36:03 +0200 [thread overview]
Message-ID: <20260728143604.26-3-xiexinet@gmail.com> (raw)
In-Reply-To: <20260728143604.26-1-xiexinet@gmail.com>
hsr_create_tagged_frame() and prp_create_tagged_frame() update the
path/LAN ID in the received skb data buffer and then skb_clone() it
for the egress port. When the same frame is forwarded to both slave
ports, the second port ID update lands in the same shared buffer the
first port clone still points at; if that clone is still queued
(qdisc backlog, NETEM, BQL), it is transmitted with the second port
ID - a silent on-wire corruption.
One always-available trigger is the master transmit path: a
pre-tagged frame transmitted on the master (for example injected
locally via AF_PACKET with a valid RCT) passes prp_fill_frame_info()
with frame->skb_prp set, the master-origin gate lets it through to
both slaves, and both slaves run prp_set_lan_id() + skb_clone() on
the same buffer. Tagged frames arriving on an HSR RedBox interlink
take the analogous path through hsr_create_tagged_frame(). Normal
traffic tests do not expose the race: the window between the first
dev_queue_xmit() and the second ID write is only a few instructions
and opens only under egress backpressure. With a 200 ms netem delay
on one slave, all 200 injected frames left that slave carrying the
other slave LAN ID in testing.
Reorder both helpers: clone first, privatize the clone with
skb_cow(), reacquire the header/trailer pointer, then update the ID.
skb_cow() is used rather than skb_cow_head() because privacy is
needed for the whole linear area, not only the header: the HSR tag
sits in the head, but the PRP RCT sits at the linear tail.
skb_get_PRP_rct() computes the trailer from skb_tail_pointer(), so
the returned pointer is always inside the linear area that
pskb_expand_head() copies; frames with a nonlinear tail are
mis-parsed by the existing helper regardless and are out of scope
here. (Both wrappers currently reach pskb_expand_head(); they differ
in the cloned-data predicate, and correctness here needs full data
privacy, not only header privacy.) This adds one linear-head copy
per tagged egress; untagged master traffic keeps the existing
__pskb_copy() path and pays nothing from this patch.
Fixes: 451d8123f897 ("net: prp: add packet handling support")
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
net/hsr/hsr_forward.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 67aaf5a862..974b55f248 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);
}
--
2.43.0
next prev parent reply other threads:[~2026-07-28 14:36 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-28 14:36 [PATCH net 0/3] net: hsr: fix shared-skb mutations in the forwarding path Xin Xie
2026-07-28 14:36 ` [PATCH net 1/3] net: hsr: privatize interlink-bound skbs before address mutation Xin Xie
2026-07-28 14:36 ` Xin Xie [this message]
2026-07-28 14:36 ` [PATCH net 3/3] selftests: net: hsr: add shared-mutation regression test Xin Xie
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260728143604.26-3-xiexinet@gmail.com \
--to=xiexinet@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=lukma@denx.de \
--cc=m-karicheri2@ti.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox