Netdev List
 help / color / mirror / Atom feed
* [PATCH net v4] rtase: Workaround for TX hang caused by short UDP packets entering hardware PTP parsing
@ 2026-06-26  4:45 Justin Lai
  2026-06-30  2:01 ` Jakub Kicinski
  0 siblings, 1 reply; 2+ messages in thread
From: Justin Lai @ 2026-06-26  4:45 UTC (permalink / raw)
  To: kuba
  Cc: davem, edumazet, pabeni, andrew+netdev, linux-kernel, netdev,
	stable, horms, richardcochran, david.laight.linux,
	aleksander.lobakin, pkshih, larry.chiu, Justin Lai

The hardware performs additional PTP parsing on UDP packets identified
by destination ports 319/320 at the expected UDP destination port
offset.

If such a packet has transport data smaller than RTASE_MIN_PAD_LEN,
the parser may access data beyond the end of the packet and trigger a
TX hang.

For IPv4 fragmented packets, a non-initial fragment does not contain a
UDP header. However, if the payload contains values matching PTP
destination ports (319/320) at the expected UDP destination port
offset, the hardware incorrectly classifies the fragment as a PTP
packet and performs further parsing.

IPv6 fragmented packets are not affected because the hardware only
enters this parsing path when the IPv6 Next Header field directly
indicates UDP. Packets carrying a Fragment Header do not enter this
path.

Pad affected packets so the transport data reaches
RTASE_MIN_PAD_LEN before transmission to avoid triggering the
hardware issue.

Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function")
Cc: stable@vger.kernel.org
Signed-off-by: Justin Lai <justinlai0215@realtek.com>
---
v3 -> v4:
- Derive the L3 protocol and network offset from Ethernet/VLAN
  headers instead of relying on vlan_get_protocol().
- Reject malformed packets when the computed UDP offset exceeds
  skb->len.
 
v2 -> v3:
- Remove dependency on skb_transport_header_was_set().
- Determine UDP header offset from IPv4/IPv6 headers.
- Use skb_header_pointer() for UDP header access.
- Add non-linear skb handling.
 
v1 -> v2:
- Remove RTASE_SHORT_PKT_THRESH and the packet length check.
- Check transport data length before parsing the UDP header.
- Add Fixes tag.
- Add Cc: stable@vger.kernel.org.
- Target net tree.
---
 drivers/net/ethernet/realtek/rtase/rtase.h    |   2 +
 .../net/ethernet/realtek/rtase/rtase_main.c   | 113 ++++++++++++++++++
 2 files changed, 115 insertions(+)

diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h
index b9209eb6ea73..d489d20177ac 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase.h
+++ b/drivers/net/ethernet/realtek/rtase/rtase.h
@@ -359,4 +359,6 @@ struct rtase_private {
 
 #define RTASE_MSS_MASK GENMASK(28, 18)
 
+#define RTASE_MIN_PAD_LEN 47
+
 #endif /* RTASE_H */
diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 55105d34bc79..7dabf0004068 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -61,6 +61,7 @@
 #include <linux/pci.h>
 #include <linux/pm_runtime.h>
 #include <linux/prefetch.h>
+#include <linux/ptp_classify.h>
 #include <linux/rtnetlink.h>
 #include <linux/tcp.h>
 #include <asm/irq.h>
@@ -1249,6 +1250,115 @@ static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
 	return csum_cmd;
 }
 
+static bool rtase_get_l3_proto(struct sk_buff *skb, __be16 *proto,
+			       u32 *network_offset)
+{
+	struct vlan_hdr *vh, _vh;
+	struct ethhdr *eh, _eh;
+	u32 offset = ETH_HLEN;
+
+	eh = skb_header_pointer(skb, 0, sizeof(_eh), &_eh);
+	if (!eh)
+		return false;
+
+	*proto = eh->h_proto;
+
+	while (eth_type_vlan(*proto)) {
+		vh = skb_header_pointer(skb, offset, sizeof(_vh), &_vh);
+		if (!vh)
+			return false;
+
+		*proto = vh->h_vlan_encapsulated_proto;
+		offset += VLAN_HLEN;
+	}
+
+	*network_offset = offset;
+
+	return true;
+}
+
+static bool rtase_get_udp_offset(struct sk_buff *skb, u32 *udp_offset)
+{
+	struct ipv6hdr *i6h, _i6h;
+	struct iphdr *ih, _ih;
+	__be16 proto;
+	u32 no;
+
+	if (!rtase_get_l3_proto(skb, &proto, &no))
+		return false;
+
+	switch (proto) {
+	case htons(ETH_P_IP):
+		ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih);
+		if (!ih)
+			return false;
+
+		if (ih->ihl < 5)
+			return false;
+
+		if (ih->protocol != IPPROTO_UDP)
+			return false;
+
+		*udp_offset = no + ih->ihl * 4;
+
+		return true;
+	case htons(ETH_P_IPV6):
+		i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h);
+		if (!i6h)
+			return false;
+
+		if (i6h->nexthdr != IPPROTO_UDP)
+			return false;
+
+		*udp_offset = no + sizeof(*i6h);
+
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rtase_skb_pad(struct sk_buff *skb)
+{
+	__be16 *dest, _dest;
+	u32 trans_data_len;
+	u32 udp_offset;
+	u16 dest_port;
+	u32 pad_len;
+
+	if (!rtase_get_udp_offset(skb, &udp_offset))
+		return true;
+
+	if (udp_offset > skb->len)
+		return false;
+
+	trans_data_len = skb->len - udp_offset;
+	if (trans_data_len < offsetof(struct udphdr, len) ||
+	    trans_data_len >= RTASE_MIN_PAD_LEN)
+		return true;
+
+	dest = skb_header_pointer(skb,
+				  udp_offset + offsetof(struct udphdr, dest),
+				  sizeof(_dest), &_dest);
+	if (!dest)
+		return true;
+
+	dest_port = ntohs(*dest);
+	if (dest_port != PTP_EV_PORT && dest_port != PTP_GEN_PORT)
+		return true;
+
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb))
+			return false;
+	}
+
+	pad_len = RTASE_MIN_PAD_LEN - trans_data_len;
+	if (__skb_put_padto(skb, skb->len + pad_len, false))
+		return false;
+
+	return true;
+}
+
 static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
 			    u32 opts1, u32 opts2)
 {
@@ -1362,6 +1472,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
 		opts2 |= rtase_tx_csum(skb, dev);
 	}
 
+	if (!rtase_skb_pad(skb))
+		goto err_dma_0;
+
 	frags = rtase_xmit_frags(ring, skb, opts1, opts2);
 	if (unlikely(frags < 0))
 		goto err_dma_0;
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH net v4] rtase: Workaround for TX hang caused by short UDP packets entering hardware PTP parsing
  2026-06-26  4:45 [PATCH net v4] rtase: Workaround for TX hang caused by short UDP packets entering hardware PTP parsing Justin Lai
@ 2026-06-30  2:01 ` Jakub Kicinski
  0 siblings, 0 replies; 2+ messages in thread
From: Jakub Kicinski @ 2026-06-30  2:01 UTC (permalink / raw)
  To: justinlai0215
  Cc: Jakub Kicinski, davem, edumazet, pabeni, andrew+netdev,
	linux-kernel, netdev, stable, horms, richardcochran,
	david.laight.linux, aleksander.lobakin, pkshih, larry.chiu

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
    rtase: Workaround for TX hang caused by short UDP packets entering hardware PTP parsing

This commit attempts to work around a hardware TX hang caused by out-of-bounds
reads during PTP parsing. It introduces logic to pad short UDP packets to a
minimum transport data length before transmission.

> diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
> index 255667775f0e8..944ffe71b2d07 100644
> --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
> +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
[ ... ]
> +static bool rtase_get_udp_offset(struct sk_buff *skb, u32 *udp_offset)
> +{
> +	struct ipv6hdr *i6h, _i6h;
> +	struct iphdr *ih, _ih;
> +	__be16 proto;
> +	u32 no;
> +
> +	if (!rtase_get_l3_proto(skb, &proto, &no))
> +		return false;
> +
> +	switch (proto) {
> +	case htons(ETH_P_IP):
> +		ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih);
> +		if (!ih)
> +			return false;

[Severity: High]
Does this skip the padding workaround if the IP header itself is truncated?

If the packet is shorter than the IP header (e.g., less than 34 bytes),
skb_header_pointer() will return false. This causes rtase_skb_pad() to
return true, allowing the packet to skip padding. Could this small unpadded
packet still trigger the hardware parser and cause the TX hang?

[ ... ]
> +static bool rtase_skb_pad(struct sk_buff *skb)
> +{
> +	__be16 *dest, _dest;
> +	u32 trans_data_len;
> +	u32 udp_offset;
> +	u16 dest_port;
> +	u32 pad_len;
> +
> +	if (!rtase_get_udp_offset(skb, &udp_offset))
> +		return true;
> +
> +	if (udp_offset > skb->len)
> +		return false;
> +
> +	trans_data_len = skb->len - udp_offset;
> +	if (trans_data_len < offsetof(struct udphdr, len) ||
> +	    trans_data_len >= RTASE_MIN_PAD_LEN)
> +		return true;

[Severity: High]
Can an attacker bypass the workaround if the transport payload is very small?

By returning true when trans_data_len is less than
offsetof(struct udphdr, len) (which is 4 bytes), the packet is passed to
the hardware unpadded.

If an attacker sends an IPv4 non-initial fragment with a very small payload,
the hardware parser might identify the fragment as UDP and attempt to read the
destination port at offset 2 from the payload. If this out-of-bounds read
hits residual uninitialized memory in the TX FIFO containing PTP port values
(319 or 320), the hardware could incorrectly classify it as a PTP packet,
continue parsing out of bounds, and trigger the TX hang.

> +
> +	dest = skb_header_pointer(skb,
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-06-30  2:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26  4:45 [PATCH net v4] rtase: Workaround for TX hang caused by short UDP packets entering hardware PTP parsing Justin Lai
2026-06-30  2:01 ` Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox