Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: wwan: t7xx: fix potential skb->frags overflow in RX path

When receiving data in the DPMAIF RX path,
the t7xx_dpmaif_set_frag_to_skb() function adds
page fragments to an skb without checking if the number of
fragments has exceeded MAX_SKB_FRAGS. This could lead to a buffer overflow
in skb_shinfo(skb)->frags[] array, corrupting adjacent memory and
potentially causing kernel crashes or other undefined behavior.

This issue was identified through static code analysis by comparing with a
similar vulnerability fixed in the mt76 driver commit b102f0c522cf ("mt76:
fix array overflow on receiving too many fragments for a packet").

The vulnerability could be triggered if the modem firmware sends packets
with excessive fragments. While under normal protocol conditions (MTU 3080
bytes, BAT buffer 3584 bytes),
a single packet should not require additional
fragments, the kernel should not blindly trust firmware behavior.
Malicious, buggy, or compromised firmware could potentially craft packets
with more fragments than the kernel expects.

Fix this by adding a bounds check before calling skb_add_rx_frag() to
ensure nr_frags does not exceed MAX_SKB_FRAGS.

The check must be performed before unmapping to avoid a page leak
and double DMA unmap during device teardown.

Fixes: d642b012df70a ("net: wwan: t7xx: Add data path interface")
Signed-off-by: Kery Qi <qikeyu2017@gmail.com>
Link: https://patch.msgid.link/20260122170401.1986-2-qikeyu2017@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kery Qi and committed by
Jakub Kicinski
f0813bcd 1742272b

+7 -2
+7 -2
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
··· 395 395 struct sk_buff *skb) 396 396 { 397 397 unsigned long long data_bus_addr, data_base_addr; 398 + struct skb_shared_info *shinfo = skb_shinfo(skb); 398 399 struct device *dev = rxq->dpmaif_ctrl->dev; 399 400 struct dpmaif_bat_page *page_info; 400 401 unsigned int data_len; ··· 403 402 404 403 page_info = rxq->bat_frag->bat_skb; 405 404 page_info += t7xx_normal_pit_bid(pkt_info); 406 - dma_unmap_page(dev, page_info->data_bus_addr, page_info->data_len, DMA_FROM_DEVICE); 407 405 408 406 if (!page_info->page) 409 407 return -EINVAL; 408 + 409 + if (shinfo->nr_frags >= MAX_SKB_FRAGS) 410 + return -EINVAL; 411 + 412 + dma_unmap_page(dev, page_info->data_bus_addr, page_info->data_len, DMA_FROM_DEVICE); 410 413 411 414 data_bus_addr = le32_to_cpu(pkt_info->pd.data_addr_h); 412 415 data_bus_addr = (data_bus_addr << 32) + le32_to_cpu(pkt_info->pd.data_addr_l); ··· 418 413 data_offset = data_bus_addr - data_base_addr; 419 414 data_offset += page_info->offset; 420 415 data_len = FIELD_GET(PD_PIT_DATA_LEN, le32_to_cpu(pkt_info->header)); 421 - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page, 416 + skb_add_rx_frag(skb, shinfo->nr_frags, page_info->page, 422 417 data_offset, data_len, page_info->data_len); 423 418 424 419 page_info->page = NULL;