RX descriptor lengths are used to adjust the page offset and populate skb fragments without checking that the resulting range remains inside the posted free queue fragment. A zero-length descriptor is logged but is still appended. The backing page is larger than the default 2 KiB DMA fragment, so checking only the page boundary would allow a malformed descriptor for the first half of a page to consume data from the sibling fragment. That fragment may still be owned by the device. Reject a short initial fragment before applying NET_IP_ALIGN, reject frame length underflow and ranges extending beyond either the DMA fragment or the page. Derive the final fragment length from the total frame byte count rather than treating the descriptor buffer size as an EOF payload length. Drop zero-length non-final descriptors. If an EOF descriptor carries no new data after earlier descriptors completed the frame, release its unused page reference and pass the assembled skb to GRO. Check this case before the fragment limit so an empty EOF can terminate a full fragment array. Correct the copied buffer_size comment to match the Gemini manual, which defines it as the transfer buffer size associated with the descriptor. Require the final frame length to cover an Ethernet frame so a malformed chain cannot pass a short skb to GRO. Account descriptor inconsistencies as drops rather than wire length errors. The issue was found by inspecting the descriptor arithmetic and validated against the Gemini receive descriptor definitions. The series was tested on a D-Link DIR-685 with media playback and ksmbd transfers under ping load. Assisted-by: LLM Signed-off-by: Linus Walleij --- drivers/net/ethernet/cortina/gemini.c | 42 +++++++++++++++++++++++++++-------- drivers/net/ethernet/cortina/gemini.h | 2 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index e42eeefd4d19..70aa418d7037 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -1745,6 +1745,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, struct gemini_ethernet_port *port = netdev_priv(netdev); unsigned short m = (1 << port->rxq_order) - 1; struct gemini_ethernet *geth = port->geth; + unsigned int freeq_frag_len = 1 << geth->freeq_frag_order; void __iomem *ptr_reg = port->rxq_rwptr; unsigned int frag_nr = port->rx_frag_nr; struct sk_buff *skb = port->rx_skb; @@ -1819,6 +1820,9 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, if (!skb) goto err_drop; + if (frag_len < NET_IP_ALIGN) + goto err_drop; + page_offs += NET_IP_ALIGN; frag_len -= NET_IP_ALIGN; frag_nr = 0; @@ -1827,15 +1831,33 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, goto err_drop; } - if (word3.bits32 & EOF_BIT) + if (word3.bits32 & EOF_BIT) { + if (frame_len < ETH_ZLEN || + frame_len < skb->len) + goto err_drop; frag_len = frame_len - skb->len; + } + + if (!frag_len) { + if (word3.bits32 & EOF_BIT) { + put_page(page); + page = NULL; + goto complete_frame; + } + + if (net_ratelimit()) + netdev_err(netdev, + "Received fragment with len = 0\n"); + goto err_drop; + } /* append page frag to skb */ if (frag_nr == MAX_SKB_FRAGS) goto err_drop; - - if (frag_len == 0 && net_ratelimit()) - netdev_err(netdev, "Received fragment with len = 0\n"); + if (frag_len > freeq_frag_len - + (page_offs & (freeq_frag_len - 1)) || + frag_len > PAGE_SIZE - page_offs) + goto err_drop; skb_fill_page_desc(skb, frag_nr, page, page_offs, frag_len); skb->len += frag_len; @@ -1843,11 +1865,13 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, skb->truesize += frag_len; frag_nr++; - if (word3.bits32 & EOF_BIT) { - napi_gro_frags(&port->napi); - skb = NULL; - frag_nr = 0; - } + if (!(word3.bits32 & EOF_BIT)) + goto next_desc; + +complete_frame: + napi_gro_frags(&port->napi); + skb = NULL; + frag_nr = 0; goto next_desc; err_drop: diff --git a/drivers/net/ethernet/cortina/gemini.h b/drivers/net/ethernet/cortina/gemini.h index d359c633c5de..43006fe44c9f 100644 --- a/drivers/net/ethernet/cortina/gemini.h +++ b/drivers/net/ethernet/cortina/gemini.h @@ -519,7 +519,7 @@ struct gmac_txdesc { union gmac_rxdesc_0 { unsigned int bits32; struct bit_8060 { - /* bit 15:0 number of descriptors used for the current frame */ + /* bit 15:0 transfer buffer size for this descriptor */ unsigned int buffer_size:16; /* bit 21:16 number of descriptors used for the current frame */ unsigned int desc_count:6; -- 2.56.0