Commit 2b74b0a0 authored by Brooke Basile's avatar Brooke Basile Committed by Greg Kroah-Hartman

USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()

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: default avatarIlja Van Sprundel <ivansprundel@ioactive.com>
Signed-off-by: default avatarBrooke Basile <brookebasile@gmail.com>
Acked-by: default avatarFelipe Balbi <balbi@kernel.org>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b1cd1b65
...@@ -1181,12 +1181,15 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1181,12 +1181,15 @@ static int ncm_unwrap_ntb(struct gether *port,
int ndp_index; int ndp_index;
unsigned dg_len, dg_len2; unsigned dg_len, dg_len2;
unsigned ndp_len; unsigned ndp_len;
unsigned block_len;
struct sk_buff *skb2; struct sk_buff *skb2;
int ret = -EINVAL; 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; const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter; int dgram_counter;
bool ndp_after_header;
/* dwSignature */ /* dwSignature */
if (get_unaligned_le32(tmp) != opts->nth_sign) { if (get_unaligned_le32(tmp) != opts->nth_sign) {
...@@ -1205,25 +1208,37 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1205,25 +1208,37 @@ static int ncm_unwrap_ntb(struct gether *port,
} }
tmp++; /* skip wSequence */ tmp++; /* skip wSequence */
block_len = get_ncm(&tmp, opts->block_length);
/* (d)wBlockLength */ /* (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"); INFO(port->func.config->cdev, "OUT size exceeded\n");
goto err; goto err;
} }
ndp_index = get_ncm(&tmp, opts->ndp_index); ndp_index = get_ncm(&tmp, opts->ndp_index);
ndp_after_header = false;
/* Run through all the NDP's in the NTB */ /* Run through all the NDP's in the NTB */
do { do {
/* NCM 3.2 */ /*
if (((ndp_index % 4) != 0) && * NCM 3.2
(ndp_index < opts->nth_size)) { * 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", INFO(port->func.config->cdev, "Bad index: %#X\n",
ndp_index); ndp_index);
goto err; 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); tmp = (void *)(skb->data + ndp_index);
if (get_unaligned_le32(tmp) != ncm->ndp_sign) { if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
...@@ -1234,14 +1249,15 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1234,14 +1249,15 @@ static int ncm_unwrap_ntb(struct gether *port,
ndp_len = get_unaligned_le16(tmp++); ndp_len = get_unaligned_le16(tmp++);
/* /*
* NCM 3.3.1 * NCM 3.3.1
* wLength
* entry is 2 items * entry is 2 items
* item size is 16/32 bits, opts->dgram_item_len * 2 bytes * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
* minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
* Each entry is a dgram index and a dgram length. * Each entry is a dgram index and a dgram length.
*/ */
if ((ndp_len < opts->ndp_size if ((ndp_len < opts->ndp_size
+ 2 * 2 * (opts->dgram_item_len * 2)) + 2 * 2 * (opts->dgram_item_len * 2)) ||
|| (ndp_len % opts->ndplen_align != 0)) { (ndp_len % opts->ndplen_align != 0)) {
INFO(port->func.config->cdev, "Bad NDP length: %#X\n", INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
ndp_len); ndp_len);
goto err; goto err;
...@@ -1258,8 +1274,21 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1258,8 +1274,21 @@ static int ncm_unwrap_ntb(struct gether *port,
do { do {
index = index2; 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; 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, INFO(port->func.config->cdev,
"Bad dgram length: %#X\n", dg_len); "Bad dgram length: %#X\n", dg_len);
goto err; goto err;
...@@ -1283,6 +1312,37 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1283,6 +1312,37 @@ static int ncm_unwrap_ntb(struct gether *port,
index2 = get_ncm(&tmp, opts->dgram_item_len); index2 = get_ncm(&tmp, opts->dgram_item_len);
dg_len2 = 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. * Copy the data into a new skb.
* This ensures the truesize is correct * This ensures the truesize is correct
...@@ -1299,9 +1359,6 @@ static int ncm_unwrap_ntb(struct gether *port, ...@@ -1299,9 +1359,6 @@ static int ncm_unwrap_ntb(struct gether *port,
ndp_len -= 2 * (opts->dgram_item_len * 2); ndp_len -= 2 * (opts->dgram_item_len * 2);
dgram_counter++; dgram_counter++;
if (index2 == 0 || dg_len2 == 0)
break;
} while (ndp_len > 2 * (opts->dgram_item_len * 2)); } while (ndp_len > 2 * (opts->dgram_item_len * 2));
} while (ndp_index); } while (ndp_index);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment