This is an automated email from the ASF dual-hosted git repository.

acassis 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 779494e6cfa usbdev/cdcncm: validate host-provided NTB bounds
779494e6cfa is described below

commit 779494e6cfa8751aa0df573e723ecf1076c7c5d8
Author: chao an <[email protected]>
AuthorDate: Thu Oct 22 22:18:28 2026 +0800

    usbdev/cdcncm: validate host-provided NTB bounds
    
    cdcncm_receive() parsed CDC-NCM transfer blocks from the USB host without
    fully validating that the host-provided offsets and lengths stayed within 
the
    received request buffer.
    
    A malformed NTB could advertise a block length larger than the received 
data,
    make the NDP chain point back to itself, or provide datagram offsets and
    lengths outside the NTB.  That could make the receive path loop indefinitely
    or pass an out-of-bounds buffer range to cdcncm_packet_handler().
    
    Validate the received NTB size before parsing, bound the number of NDPs 
parsed
    from one NTB, ensure each NDP lies inside the block, and reject datagrams 
whose
    offset and length extend beyond the received NTB.
    
    Signed-off-by: chao an <[email protected]>
---
 drivers/usbdev/cdcncm.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/usbdev/cdcncm.c b/drivers/usbdev/cdcncm.c
index ede6cb00a25..2bd23edfe01 100644
--- a/drivers/usbdev/cdcncm.c
+++ b/drivers/usbdev/cdcncm.c
@@ -1029,10 +1029,17 @@ static void cdcncm_receive(FAR struct cdcncm_driver_s 
*self)
   FAR uint8_t *tmp = self->rdreq->buf;
   uint32_t ntbmax = g_ntbparameters.ntboutmaxsize;
   uint32_t blocklen;
+  uint32_t ndpcount;
+  uint32_t ndpindex;
   uint32_t ndplen;
-  int ndpindex;
   int dgramcounter;
 
+  if (self->rdreq->xfrd < opts->nthsize)
+    {
+      uerr("Short NTB header: %zu\n", self->rdreq->xfrd);
+      return;
+    }
+
   /* Get signature */
 
   if (GETUINT32(tmp) != opts->nthsign)
@@ -1057,13 +1064,16 @@ static void cdcncm_receive(FAR struct cdcncm_driver_s 
*self)
 
   /* Get block len */
 
-  if (blocklen > ntbmax)
+  if (blocklen < opts->nthsize || blocklen > ntbmax ||
+      blocklen > self->rdreq->xfrd)
     {
-      uerr("OUT size exceeded\n");
+      uerr("Bad NTB length: %" PRIu32 " / %zu\n",
+           blocklen, self->rdreq->xfrd);
       return;
     }
 
   ndpindex = cdcncm_get(&tmp, opts->ndpindex);
+  ndpcount = 0;
 
   do
     {
@@ -1071,9 +1081,10 @@ static void cdcncm_receive(FAR struct cdcncm_driver_s 
*self)
       uint32_t dglen;
 
       if (((ndpindex % 4) != 0) || (ndpindex < opts->nthsize) ||
-          (ndpindex > (blocklen - opts->ndpsize)))
+          (ndpindex > blocklen) || (opts->ndpsize > blocklen - ndpindex) ||
+          (++ndpcount > blocklen / opts->ndpsize))
         {
-          uerr("Bad index: %#X\n", ndpindex);
+          uerr("Bad index: %#" PRIx32 "\n", ndpindex);
           return;
         }
 
@@ -1089,7 +1100,8 @@ static void cdcncm_receive(FAR struct cdcncm_driver_s 
*self)
       ndplen = cdcncm_get(&tmp, 2);
 
       if ((ndplen < opts->ndpsize + 2 * (opts->dgramitemlen * 2)) ||
-          (ndplen % opts->ndpalign != 0))
+          (ndplen % opts->ndpalign != 0) ||
+          (ndplen > blocklen - ndpindex))
         {
           uerr("Bad NDP length: %04" PRIx32 " \n", ndplen);
           return;
@@ -1117,6 +1129,13 @@ static void cdcncm_receive(FAR struct cdcncm_driver_s 
*self)
               break;
             }
 
+          if (index > blocklen || dglen > blocklen - index)
+            {
+              uerr("Bad datagram: %" PRIu32 "+%" PRIu32 "\n",
+                   index, dglen);
+              return;
+            }
+
           dgramcounter++;
 
           /* Copy the data from the hardware to self->rx_queue. */

Reply via email to