Skip to content

Commit

Permalink
USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Brooke Basile <[email protected]>
Acked-by: Felipe Balbi <[email protected]>
Cc: stable <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
brookebasile authored and gregkh committed Aug 25, 2020
1 parent b1cd1b6 commit 2b74b0a
Showing 1 changed file with 69 additions and 12 deletions.
81 changes: 69 additions & 12 deletions drivers/usb/gadget/function/f_ncm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1181,12 +1181,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) {
Expand All @@ -1205,25 +1208,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");
Expand All @@ -1234,14 +1249,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;
Expand All @@ -1258,8 +1274,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;
Expand All @@ -1283,6 +1312,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
Expand All @@ -1299,9 +1359,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);

Expand Down

0 comments on commit 2b74b0a

Please sign in to comment.