diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index e58f1d2c26bd..eddd8406867f 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -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);