From: Tjerk Kusters When Rx hardware timestamping is enabled (e.g. ptp4l, which configures HWTSTAMP_FILTER_ALL), the NIC prepends a 16-byte timestamp header to the first Rx buffer of every received frame. igb_clean_rx_irq() strips this header inside its per-buffer loop: if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { ts_hdr_len = igb_ptp_rx_pktstamp(rx_ring->q_vector, pktbuf, ×tamp); pkt_offset += ts_hdr_len; size -= ts_hdr_len; } For a frame that spans more than one Rx buffer (e.g. a jumbo frame), this block runs once per buffer. The timestamp header only exists at the start of the first buffer, but igb_ptp_rx_pktstamp() is called for every buffer. On a continuation buffer the data is packet payload, not a timestamp header. igb_ptp_rx_pktstamp() already has two guards against acting on a non-header buffer: it returns 0 if PTP is disabled, and returns 0 if the reserved dwords (the first 8 bytes) are non-zero. Neither is sufficient here: PTP is enabled, and a continuation buffer whose payload happens to begin with 8 zero bytes passes the reserved-dword check. In that case the payload is mistaken for a valid timestamp header and igb_ptp_rx_pktstamp() returns IGB_TS_HDR_LEN, and the caller reduces that buffer's size by 16. igb_add_rx_frag() adds a continuation buffer from its original offset, so the buffer loses its last 16 bytes and the data of the next buffer follows directly. A frame spanning N buffers loses 16 bytes per affected continuation buffer, in the middle of the payload when N > 2. If the last buffer of a frame holds 8 to 15 bytes, all zero, the unsigned size wraps and a fragment of about 4 GiB is added to the skb. The sender chooses the frame length and content, so this can be triggered remotely. This is easily triggered by a GigE Vision camera streaming dark frames (mostly 0x00 pixel data) over jumbo UDP with PTP active on the receiver: the all-zero frames are dropped as truncated while frames with non-zero content are fine. The driver reports no error; only the IP layer counts the frames in IpExtInTruncatedPkts. No content-based check can reliably tell a continuation buffer that begins with zero bytes from a real timestamp header, because both are all zero. Fix it structurally instead: only attempt the strip on the first buffer of a frame, which is the only buffer that can contain a timestamp header. In igb_clean_rx_irq() skb is NULL until the first buffer has been processed, so guarding the strip with !skb restricts it to the first buffer regardless of payload content. This relies on the previous patch. Without it, an XDP program that does not return XDP_PASS for the first buffer of a multi-buffer frame leaves skb NULL for the rest of that frame. With the receive length limited while XDP is attached, such frames no longer reach the XDP path. igc has the same bug; Paul Moses' igc series, linked below, fixes it the same way. Link: https://lore.kernel.org/all/20261028083344.3047350-2-p@1g4.org/ Fixes: 5379260852b0 ("igb: Fix XDP with PTP enabled") Cc: stable@vger.kernel.org Reviewed-by: Piotr Kwapulinski Reviewed-by: Aleksandr Loktionov Reviewed-by: Kurt Kanzenbach Tested-by: Alexander Nowlin Signed-off-by: Tjerk Kusters --- drivers/net/ethernet/intel/igb/igb_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 711a67f0bd80..e787095d3cdb 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -9125,8 +9125,11 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) rx_buffer = igb_get_rx_buffer(rx_ring, size, &rx_buf_pgcnt); pktbuf = page_address(rx_buffer->page) + rx_buffer->page_offset; - /* pull rx packet timestamp if available and valid */ - if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { + /* pull rx packet timestamp if available and valid; it is only + * present on the first buffer of a frame + */ + if (!skb && + igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { int ts_hdr_len; ts_hdr_len = igb_ptp_rx_pktstamp(rx_ring->q_vector, -- 2.27.0