This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 3db851bb85d usbhost/cdcmbim: validate device-provided NTB bounds
3db851bb85d is described below
commit 3db851bb85de5eca56e549f208abc5e7fd934844
Author: chao an <[email protected]>
AuthorDate: Thu Oct 22 22:24:55 2026 +0800
usbhost/cdcmbim: validate device-provided NTB bounds
The CDC-MBIM host receive path trusted NDP offsets, NDP lengths,
and DPE entries from the attached device. A malicious device could
create an NDP chain that loops forever or make the DPE walk read past
the received NTB.
Validate the NTB header and block length, bound the number of NDPs
processed per NTB, validate each current and next NDP offset, restrict
DPE iteration to the advertised NDP length, and ensure datagram ranges
are contained within the received NTB before passing them to the
network stack.
Signed-off-by: chao an <[email protected]>
---
drivers/usbhost/usbhost_cdcmbim.c | 72 +++++++++++++++++++++++++++++++++++----
1 file changed, 65 insertions(+), 7 deletions(-)
diff --git a/drivers/usbhost/usbhost_cdcmbim.c
b/drivers/usbhost/usbhost_cdcmbim.c
index 499915905d3..15ef7baf471 100644
--- a/drivers/usbhost/usbhost_cdcmbim.c
+++ b/drivers/usbhost/usbhost_cdcmbim.c
@@ -755,9 +755,11 @@ static void usbhost_bulkin_work(FAR void *arg)
FAR struct usbhost_hubport_s *hport;
FAR struct usb_cdc_ncm_nth16_s *nth;
uint16_t ndpoffset;
+ uint16_t ndpcount;
uint16_t dgram_len;
uint16_t dgram_off;
uint16_t block_len;
+ uint16_t rxlen;
priv = (FAR struct usbhost_cdcmbim_s *)arg;
DEBUGASSERT(priv);
@@ -778,6 +780,8 @@ static void usbhost_bulkin_work(FAR void *arg)
goto out;
}
+ rxlen = priv->bulkinbytes;
+
/* Parse the NTB header */
nth = (FAR struct usb_cdc_ncm_nth16_s *)priv->rxnetbuf;
@@ -788,18 +792,34 @@ static void usbhost_bulkin_work(FAR void *arg)
goto out;
}
+ if (usbhost_getle16(nth->length) != sizeof(struct usb_cdc_ncm_nth16_s))
+ {
+ uerr("Invalid NTH length\n");
+ goto out;
+ }
+
block_len = usbhost_getle16(nth->block_length);
- if (block_len > priv->bulkinbytes)
+ if (block_len < sizeof(struct usb_cdc_ncm_nth16_s) || block_len > rxlen)
{
- uerr("Block length larger than rx buffer\n");
+ uerr("Invalid block length: %u\n", block_len);
goto out;
}
ndpoffset = usbhost_getle16(nth->ndp_index);
- if (ndpoffset > priv->bulkinbytes)
+ if (ndpoffset == 0)
+ {
+ goto out;
+ }
+
+ ndpcount = 0;
+
+ if (((ndpoffset % 4) != 0) ||
+ (ndpoffset < sizeof(struct usb_cdc_ncm_nth16_s)) ||
+ (ndpoffset > block_len) ||
+ (sizeof(struct usb_cdc_ncm_ndp16_s) > block_len - ndpoffset))
{
- uerr("NDP offset too far %u > %u\n", ndpoffset, priv->bulkinbytes);
+ uerr("Invalid NDP offset: %u\n", ndpoffset);
goto out;
}
@@ -808,22 +828,60 @@ static void usbhost_bulkin_work(FAR void *arg)
do
{
FAR struct usb_cdc_ncm_dpe16_s *dpe;
+ FAR struct usb_cdc_ncm_dpe16_s *dpe_end;
FAR struct usb_cdc_ncm_ndp16_s *ndp
= (FAR struct usb_cdc_ncm_ndp16_s *)(priv->rxnetbuf + ndpoffset);
+ uint16_t curroffset;
+ uint16_t ndplen;
+
+ curroffset = ndpoffset;
+ ndplen = usbhost_getle16(ndp->length);
+
+ if (++ndpcount > block_len / sizeof(struct usb_cdc_ncm_ndp16_s))
+ {
+ uerr("Too many NDPs\n");
+ goto out;
+ }
+
+ if (ndplen < sizeof(struct usb_cdc_ncm_ndp16_s) +
+ sizeof(struct usb_cdc_ncm_dpe16_s) ||
+ ndplen > block_len - ndpoffset ||
+ (ndplen - sizeof(struct usb_cdc_ncm_ndp16_s)) %
+ sizeof(struct usb_cdc_ncm_dpe16_s) != 0)
+ {
+ uerr("Invalid NDP length: %u\n", ndplen);
+ goto out;
+ }
ndpoffset = usbhost_getle16(ndp->next_ndp_index);
+ if (ndpoffset != 0 &&
+ (((ndpoffset % 4) != 0) ||
+ (ndpoffset < sizeof(struct usb_cdc_ncm_nth16_s)) ||
+ (ndpoffset > block_len) ||
+ (sizeof(struct usb_cdc_ncm_ndp16_s) > block_len - ndpoffset)))
+ {
+ uerr("Invalid next NDP offset: %u\n", ndpoffset);
+ goto out;
+ }
/* Parse each DPE */
- for (dpe = ndp->dpe16; usbhost_getle16(dpe->index); dpe++)
+ dpe_end = (FAR struct usb_cdc_ncm_dpe16_s *)
+ (priv->rxnetbuf + curroffset + ndplen);
+ for (dpe = ndp->dpe16; dpe < dpe_end && usbhost_getle16(dpe->index);
+ dpe++)
{
dgram_off = usbhost_getle16(dpe->index);
dgram_len = usbhost_getle16(dpe->length);
- if (dgram_off + dgram_len <= priv->bulkinbytes)
+ if (dgram_len == 0 || dgram_off > block_len ||
+ dgram_len > block_len - dgram_off)
{
- cdcmbim_receive(priv, priv->rxnetbuf + dgram_off, dgram_len);
+ uerr("Invalid datagram: %u+%u\n", dgram_off, dgram_len);
+ goto out;
}
+
+ cdcmbim_receive(priv, priv->rxnetbuf + dgram_off, dgram_len);
}
}
while (ndpoffset);