Commit b507828c7b61 for kernel
commit b507828c7b6135ff7a4b69eba6e33502b4839fb9
Author: Jiahao Xu <xujiahao@cmss.chinamobile.com>
Date: Wed Nov 11 12:41:56 2026 +0800
net: usb: cdc_ncm: don't underflow the NDP room check
cdc_ncm_ndp16() and cdc_ncm_ndp32() verify that the new NDP and the
datagram fit into the NTB with an unsigned subtraction:
if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
return NULL;
Nothing guarantees that skb->len plus the reserve stays below
tx_curr_size, and when it does not the subtraction wraps around to a
huge value, the check passes, and the skb_put_zero() of max_ndp_size
bytes that follows overruns the NTB:
skbuff: skb_over_panic: text:ffff80000db33558 len:428 put:172
head:ffff0000c2c66100 tail:0x1ac end:0x140
kernel BUG at net/core/skbuff.c:200
skb_put_zero include/linux/skbuff.h:2595 [inline]
cdc_ncm_ndp16 drivers/net/usb/cdc_ncm.c:1132 [inline]
cdc_ncm_fill_tx_frame+0xdf0/0x30e0 drivers/net/usb/cdc_ncm.c:1309
Here tx_curr_size is 0x140 = 320, skb->len is already 256 and the
skb_put_zero() wants another 172 bytes.
Two device controlled settings make this reachable:
- dwNtbOutMaxSize = 320 caps tx_max at 320. The lower bound computed
in cdc_ncm_check_tx_max() is max_datagram_size + max_ndp_size + the
NTH header, but cdc_ncm_setup() clamps tx_max before
cdc_ncm_set_dgram_size() runs, so max_datagram_size is still 0 and
the bound is only max_ndp_size + the NTH header. It does not
reserve room for one datagram plus one NDP.
- wNdpOutAlignment = 256 is accepted by cdc_ncm_fix_modulus() as long
as it is smaller than tx_max, and cdc_ncm_align_tail() then pads the
12 byte NTH header up to 256 bytes right before this check.
An NDP can also be added to an NTB that already holds datagrams through
cdc_mbim_tx_fixup(), which uses a different signature per MBIM session.
On plain cdc_ncm the chain lookup in cdc_ncm_ndp16() returns the
existing NDP for the single signature, so there the create path is
normally reached for the first datagram of each NTB.
Rewrite the test as an addition, which cannot underflow and gives the
same answer whenever the original subtraction did not wrap.
Fixes: c78b7c586657 ("net: cdc_ncm: refactoring for tx multiplexing")
Fixes: 0fa81b304a79 ("cdc_ncm: Implement the 32-bit version of NCM Transfer Block")
Reported-by: syzbot+1994e2ecf323ed90f255@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1994e2ecf323ed90f255
Signed-off-by: Jiahao Xu <xujiahao@cmss.chinamobile.com>
Link: https://patch.msgid.link/20261111124156.637661-1-xujiahao@cmss.chinamobile.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 19db5a668dcb..e92dc7f011ea 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1125,7 +1125,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct s
cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
/* verify that there is room for the NDP and the datagram (reserve) */
- if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
+ if (skb->len + reserve + ctx->max_ndp_size > ctx->tx_curr_size)
return NULL;
/* link to it */
@@ -1180,7 +1180,7 @@ static struct usb_cdc_ncm_ndp32 *cdc_ncm_ndp32(struct cdc_ncm_ctx *ctx, struct s
cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
/* verify that there is room for the NDP and the datagram (reserve) */
- if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size)
+ if (skb->len + reserve + ctx->max_ndp_size > ctx->tx_curr_size)
return NULL;
/* link to it */