Re: [PATCH] usb: gadget: ncm: validate the NDP chain before parsing
From: Krishna Kurapati
Date: Tue Sep 15 2026 - 00:55:22 EST
On 9/15/2026 9:41 AM, Aldo Ariel Panzardo wrote:
The next-NDP pointers form a chain supplied by the USB host. A cyclic
chain therefore makes the receive path loop indefinitely and repeatedly
allocate datagram skbs.
Is this an issue with NTB creation on host side ? (seems like it). If so, can you update commit message with an example packet details indicating the same (or a walkthrough of a packet with such headers indicating a cyclic chain).
Regards,
Krishna,
Prewalk the chain using only bounded header reads before parsing any
NDP. NDP offsets must be four-byte aligned, so following more than
block_len / 4 valid offsets proves that the chain contains a cycle.
This terminates cyclic chains without imposing an arbitrary limit on
valid NTBs or allocating skbs before the chain is known to terminate.
Fixes: 370af734dfaf ("usb: gadget: NCM: RX function support multiple NDPs")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@xxxxxxxxx>
---
drivers/usb/gadget/function/f_ncm.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 64eabda2f5..085aea142f 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1175,6 +1175,7 @@ static int ncm_unwrap_ntb(struct gether *port,
unsigned dg_len, dg_len2;
unsigned ndp_len;
unsigned block_len;
+ unsigned int ndp_count, next_ndp_index;
struct sk_buff *skb2;
int ret = -EINVAL;
unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
@@ -1224,6 +1225,30 @@ static int ncm_unwrap_ntb(struct gether *port,
}
ndp_index = get_ncm(&tmp, opts->ndp_index);
+ next_ndp_index = ndp_index;
+ ndp_count = 0;
+
+ /* Validate the NDP chain before allocating datagram skbs. */
+ while (next_ndp_index) {
+ if (next_ndp_index % 4 ||
+ next_ndp_index < opts->nth_size ||
+ next_ndp_index > block_len - opts->ndp_size) {
+ INFO(port->func.config->cdev, "Bad index: %#X\n",
+ next_ndp_index);
+ goto err;
+ }
+
+ /* More aligned offsets than fit in the NTB imply a cycle. */
+ if (++ndp_count > block_len / 4) {
+ INFO(port->func.config->cdev, "NDP chain cycle\n");
+ goto err;
+ }
+
+ tmp = (__le16 *)(ntb_ptr + next_ndp_index);
+ tmp += 3; /* skip the signature and length */
+ tmp += opts->reserved1;
+ next_ndp_index = get_ncm(&tmp, opts->next_ndp_index);
+ }
/* Run through all the NDP's in the NTB */
do {