Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7e0611e authored by Brooke Basile's avatar Brooke Basile Committed by Greg Kroah-Hartman
Browse files

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



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: 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 849d2b00
Loading
Loading
Loading
Loading
+69 −12
Original line number Diff line number Diff line
@@ -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);