| From 2b74b0a04d3e9f9f08ff026e5663dce88ff94e52 Mon Sep 17 00:00:00 2001 |
| From: Brooke Basile <brookebasile@gmail.com> |
| Date: Tue, 25 Aug 2020 09:07:27 -0400 |
| Subject: USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb() |
| |
| From: Brooke Basile <brookebasile@gmail.com> |
| |
| 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 | 81 ++++++++++++++++++++++++++++++------ |
| 1 file changed, 69 insertions(+), 12 deletions(-) |
| |
| --- a/drivers/usb/gadget/function/f_ncm.c |
| +++ b/drivers/usb/gadget/function/f_ncm.c |
| @@ -1209,12 +1209,15 @@ static int ncm_unwrap_ntb(struct gether |
| 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 |
| } |
| 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 |
| 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 |
| |
| 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 |
| 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 |
| 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); |
| |