mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: atl1c: drop oversized RX frames instead of panicking
atl1c sizes its receive buffers from the netdev MTU plus a fixed set of L2 overheads, which comes to 1522 bytes at the default MTU. A link partner that sends a longer frame - in practice a MikroTik CCR2004-1G-2XS-PCIe, whose RouterOS side defaults to l2mtu 1600 while the host stays at MTU 1500 - makes the length reported by the RX return descriptor exceed what the hardware wrote, and skb_put() then either includes bytes that were never received or trips skb_over_panic and takes the host down. Compare the descriptor length against buffer_info->length, the length the hardware was given, rather than against skb_tailroom(): the skb carries 64 bytes of slack for the DMA erratum, so tailroom is larger than the DMA buffer and a frame in between would pass the check and still be delivered with a tail the device never wrote. The same test rejects frames shorter than ETH_HLEN + ETH_FCS_LEN; before this change a reported length below ETH_FCS_LEN underflowed in skb_put(). The frame is dropped with kfree_skb_reason(SKB_DROP_REASON_PKT_TOO_BIG) so drop_monitor and the kfree_skb tracepoint see it, and counted through dev_core_stats_rx_dropped_inc(), which survives atl1c_get_stats() overwriting the hardware counters on every read. It also counts as work: atl1c_clean_rfd() has already recycled the RFD, and the refill at the end of the poll only runs when work_done is non-zero, so sustained oversized traffic would otherwise drain the ring and stop RX. Tested on a MikroTik CCR2004-1G-2XS-PCIe (4x AR8151) in a Proxmox VE host, with atl1c built from a DKMS tree carrying this change: the l2mtu-1600 traffic that panics the stock driver is dropped and counted with the patch applied, and received normally once the host MTU is raised to match. This complements commit36c2009d90("net: atl1c: fix soft lockup on out-of-range tpd_cons read"), which fixed the TX side of the same "host dies while the card resets" problem. Fixes:43250ddd75("atl1c: Atheros L1C Gigabit Ethernet driver") Cc: stable@vger.kernel.org Signed-off-by: Federico Musto <federico@rimpianto.com> Signed-off-by: Dave Miller <davem@davem-thinkpadp1gen8.ibmlond.csb>
This commit is contained in:
parent
cb284914e7
commit
c4ba171685
|
|
@ -1934,6 +1934,39 @@ static int atl1c_clean_rx(struct napi_struct *napi, int budget)
|
|||
break;
|
||||
}
|
||||
atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
|
||||
|
||||
/* A link partner whose l2mtu is larger than ours can hand us a
|
||||
* frame that does not fit the buffer the hardware was given:
|
||||
* the descriptor reports the untruncated length while at most
|
||||
* buffer_info->length bytes were written. skb_put() of such a
|
||||
* length would pull in whatever the tail of the buffer holds
|
||||
* and, past the end of the skb, trips skb_over_panic and takes
|
||||
* the host down. Bound it by what the device actually wrote
|
||||
* rather than by skb_tailroom(): the skb carries 64 bytes of
|
||||
* slack for the DMA erratum, so the two are not the same.
|
||||
*
|
||||
* Frames shorter than an Ethernet header plus its FCS are
|
||||
* rejected by the same test; before this, a length below
|
||||
* ETH_FCS_LEN underflowed in skb_put().
|
||||
*/
|
||||
if (unlikely(length < ETH_HLEN + ETH_FCS_LEN ||
|
||||
length > buffer_info->length)) {
|
||||
dev_core_stats_rx_dropped_inc(netdev);
|
||||
if (netif_msg_rx_err(adapter))
|
||||
dev_warn_ratelimited(&pdev->dev,
|
||||
"dropped rx frame: %u bytes, buffer %u\n",
|
||||
length, buffer_info->length);
|
||||
kfree_skb_reason(skb, SKB_DROP_REASON_PKT_TOO_BIG);
|
||||
/* atl1c_clean_rfd() has already recycled the RFD, so
|
||||
* count the drop as work: the refill at the end of the
|
||||
* poll only runs when work_done is non-zero, and
|
||||
* sustained oversized traffic would otherwise drain the
|
||||
* ring and stop RX for good.
|
||||
*/
|
||||
work_done++;
|
||||
continue;
|
||||
}
|
||||
|
||||
skb_put(skb, length - ETH_FCS_LEN);
|
||||
skb->protocol = eth_type_trans(skb, netdev);
|
||||
atl1c_rx_checksum(adapter, skb, rrs);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user