[PATCH net] net: usb: cdc_ncm: don't underflow the NDP room check
From: xujiahao
Date: Mon Nov 09 2026 - 01:41:19 EST
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;
tx_max is only clamped to max_datagram_size + max_ndp_size + the NTH
header size, so tx_curr_size is just big enough for one datagram and
one NDP. A second datagram in the same NTB is therefore exactly the
case this check has to reject, and that is also the case where
skb->len + reserve exceeds tx_curr_size: 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 buffer:
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 end is 0x140 = 320 = 140 + 172 + 8, i.e. the one-datagram minimum.
The room check in the aggregation loop cannot catch this, because it is
done after the NDP has already been pushed.
Rewrite the test as an addition, which cannot underflow and gives the
same answer whenever the original subtraction did not wrap.
Reported-by: syzbot+1994e2ecf323ed90f255@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=1994e2ecf323ed90f255
Signed-off-by: xujiahao <xujiahao@xxxxxxxxxxxxxxxxxxxx>
---
drivers/net/usb/cdc_ncm.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 19db5a668..e92dc7f01 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 */
--
2.43.0