USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()
authorBrooke Basile <brookebasile@gmail.com>
Tue, 25 Aug 2020 13:07:27 +0000 (09:07 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Sep 2020 09:21:23 +0000 (11:21 +0200)
commit 2b74b0a04d3e9f9f08ff026e5663dce88ff94e52 upstream.

Some values extracted by ncm_unwrap_ntb() could possibly lead to several
different out of bounds reads of memory.  Specifically the values passed
to netdev_alloc_skb_ip_align() need to be checked so that memory is not
overflowed.

Resolve this by applying bounds checking to a number of different
indexes and lengths of the structure parsing logic.

Reported-by: Ilja Van Sprundel <ivansprundel@ioactive.com>
Signed-off-by: Brooke Basile <brookebasile@gmail.com>
Acked-by: Felipe Balbi <balbi@kernel.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_ncm.c

index 6399923239e78108a4b7386d58196aa840a9ef6c..b69747367bd7597206a2143d981d7e75f51c5418 100644 (file)
@@ -1209,12 +1209,15 @@ static int ncm_unwrap_ntb(struct gether *port,
        int             ndp_index;
        unsigned        dg_len, dg_len2;
        unsigned        ndp_len;
+       unsigned        block_len;
        struct sk_buff  *skb2;
        int             ret = -EINVAL;
-       unsigned        max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+       unsigned        ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+       unsigned        frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize);
        const struct ndp_parser_opts *opts = ncm->parser_opts;
        unsigned        crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
        int             dgram_counter;
+       bool            ndp_after_header;
 
        /* dwSignature */
        if (get_unaligned_le32(tmp) != opts->nth_sign) {
@@ -1233,25 +1236,37 @@ static int ncm_unwrap_ntb(struct gether *port,
        }
        tmp++; /* skip wSequence */
 
+       block_len = get_ncm(&tmp, opts->block_length);
        /* (d)wBlockLength */
-       if (get_ncm(&tmp, opts->block_length) > max_size) {
+       if (block_len > ntb_max) {
                INFO(port->func.config->cdev, "OUT size exceeded\n");
                goto err;
        }
 
        ndp_index = get_ncm(&tmp, opts->ndp_index);
+       ndp_after_header = false;
 
        /* Run through all the NDP's in the NTB */
        do {
-               /* NCM 3.2 */
-               if (((ndp_index % 4) != 0) &&
-                               (ndp_index < opts->nth_size)) {
+               /*
+                * NCM 3.2
+                * dwNdpIndex
+                */
+               if (((ndp_index % 4) != 0) ||
+                               (ndp_index < opts->nth_size) ||
+                               (ndp_index > (block_len -
+                                             opts->ndp_size))) {
                        INFO(port->func.config->cdev, "Bad index: %#X\n",
                             ndp_index);
                        goto err;
                }
+               if (ndp_index == opts->nth_size)
+                       ndp_after_header = true;
 
-               /* walk through NDP */
+               /*
+                * walk through NDP
+                * dwSignature
+                */
                tmp = (void *)(skb->data + ndp_index);
                if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
                        INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
@@ -1262,14 +1277,15 @@ static int ncm_unwrap_ntb(struct gether *port,
                ndp_len = get_unaligned_le16(tmp++);
                /*
                 * NCM 3.3.1
+                * wLength
                 * entry is 2 items
                 * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
                 * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
                 * Each entry is a dgram index and a dgram length.
                 */
                if ((ndp_len < opts->ndp_size
-                               + 2 * 2 * (opts->dgram_item_len * 2))
-                               || (ndp_len % opts->ndplen_align != 0)) {
+                               + 2 * 2 * (opts->dgram_item_len * 2)) ||
+                               (ndp_len % opts->ndplen_align != 0)) {
                        INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
                             ndp_len);
                        goto err;
@@ -1286,8 +1302,21 @@ static int ncm_unwrap_ntb(struct gether *port,
 
                do {
                        index = index2;
+                       /* wDatagramIndex[0] */
+                       if ((index < opts->nth_size) ||
+                                       (index > block_len - opts->dpe_size)) {
+                               INFO(port->func.config->cdev,
+                                    "Bad index: %#X\n", index);
+                               goto err;
+                       }
+
                        dg_len = dg_len2;
-                       if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
+                       /*
+                        * wDatagramLength[0]
+                        * ethernet hdr + crc or larger than max frame size
+                        */
+                       if ((dg_len < 14 + crc_len) ||
+                                       (dg_len > frame_max)) {
                                INFO(port->func.config->cdev,
                                     "Bad dgram length: %#X\n", dg_len);
                                goto err;
@@ -1311,6 +1340,37 @@ static int ncm_unwrap_ntb(struct gether *port,
                        index2 = get_ncm(&tmp, opts->dgram_item_len);
                        dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
 
+                       if (index2 == 0 || dg_len2 == 0)
+                               break;
+
+                       /* wDatagramIndex[1] */
+                       if (ndp_after_header) {
+                               if (index2 < opts->nth_size + opts->ndp_size) {
+                                       INFO(port->func.config->cdev,
+                                            "Bad index: %#X\n", index2);
+                                       goto err;
+                               }
+                       } else {
+                               if (index2 < opts->nth_size + opts->dpe_size) {
+                                       INFO(port->func.config->cdev,
+                                            "Bad index: %#X\n", index2);
+                                       goto err;
+                               }
+                       }
+                       if (index2 > block_len - opts->dpe_size) {
+                               INFO(port->func.config->cdev,
+                                    "Bad index: %#X\n", index2);
+                               goto err;
+                       }
+
+                       /* wDatagramLength[1] */
+                       if ((dg_len2 < 14 + crc_len) ||
+                                       (dg_len2 > frame_max)) {
+                               INFO(port->func.config->cdev,
+                                    "Bad dgram length: %#X\n", dg_len);
+                               goto err;
+                       }
+
                        /*
                         * Copy the data into a new skb.
                         * This ensures the truesize is correct
@@ -1327,9 +1387,6 @@ static int ncm_unwrap_ntb(struct gether *port,
                        ndp_len -= 2 * (opts->dgram_item_len * 2);
 
                        dgram_counter++;
-
-                       if (index2 == 0 || dg_len2 == 0)
-                               break;
                } while (ndp_len > 2 * (opts->dgram_item_len * 2));
        } while (ndp_index);