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

Commit d5ddb4a5 authored by Alexey Orishko's avatar Alexey Orishko Committed by David S. Miller
Browse files

cdc_ncm: avoid discarding datagrams in rx path



Changes:
- removed a limit for amount of datagrams for IN NTB
- using pointer to traverse NTB in rx_fixup()
- renamed "temp" to "len" in rx_fixup()
- do NTB sequence number check in rx path
Tested on Intel/ARM.

Reviewed-by: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Tested-by: default avatarDmitry Tarnyagin <Dmitry.Tarnyagin@stericsson.com>
Signed-off-by: default avatarAlexey Orishko <alexey.orishko@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3f658cde
Loading
Loading
Loading
Loading
+47 −55
Original line number Original line Diff line number Diff line
@@ -71,13 +71,10 @@


/*
/*
 * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting
 * Maximum amount of datagrams in NCM Datagram Pointer Table, not counting
 * the last NULL entry. Any additional datagrams in NTB would be discarded.
 * the last NULL entry.
 */
 */
#define	CDC_NCM_DPT_DATAGRAMS_MAX		40
#define	CDC_NCM_DPT_DATAGRAMS_MAX		40


/* Maximum amount of IN datagrams in NTB */
#define	CDC_NCM_DPT_DATAGRAMS_IN_MAX		0 /* unlimited */

/* Restart the timer, if amount of datagrams is less than given value */
/* Restart the timer, if amount of datagrams is less than given value */
#define	CDC_NCM_RESTART_TIMER_DATAGRAM_CNT	3
#define	CDC_NCM_RESTART_TIMER_DATAGRAM_CNT	3
#define	CDC_NCM_TIMER_PENDING_CNT		2
#define	CDC_NCM_TIMER_PENDING_CNT		2
@@ -95,7 +92,6 @@ struct cdc_ncm_data {
};
};


struct cdc_ncm_ctx {
struct cdc_ncm_ctx {
	struct cdc_ncm_data rx_ncm;
	struct cdc_ncm_data tx_ncm;
	struct cdc_ncm_data tx_ncm;
	struct usb_cdc_ncm_ntb_parameters ncm_parm;
	struct usb_cdc_ncm_ntb_parameters ncm_parm;
	struct hrtimer tx_timer;
	struct hrtimer tx_timer;
@@ -135,6 +131,7 @@ struct cdc_ncm_ctx {
	u16 tx_modulus;
	u16 tx_modulus;
	u16 tx_ndp_modulus;
	u16 tx_ndp_modulus;
	u16 tx_seq;
	u16 tx_seq;
	u16 rx_seq;
	u16 connected;
	u16 connected;
};
};


@@ -956,108 +953,103 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
{
{
	struct sk_buff *skb;
	struct sk_buff *skb;
	struct cdc_ncm_ctx *ctx;
	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
	int sumlen;
	int len;
	int actlen;
	int temp;
	int nframes;
	int nframes;
	int x;
	int x;
	int offset;
	int offset;
	struct usb_cdc_ncm_nth16 *nth16;
	struct usb_cdc_ncm_ndp16 *ndp16;
	struct usb_cdc_ncm_dpe16 *dpe16;


	ctx = (struct cdc_ncm_ctx *)dev->data[0];
	if (ctx == NULL)
	if (ctx == NULL)
		goto error;
		goto error;


	actlen = skb_in->len;
	if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) +
	sumlen = CDC_NCM_NTB_MAX_SIZE_RX;
					sizeof(struct usb_cdc_ncm_ndp16))) {

	if (actlen < (sizeof(ctx->rx_ncm.nth16) + sizeof(ctx->rx_ncm.ndp16))) {
		pr_debug("frame too short\n");
		pr_debug("frame too short\n");
		goto error;
		goto error;
	}
	}


	memcpy(&(ctx->rx_ncm.nth16), ((u8 *)skb_in->data),
	nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data;
						sizeof(ctx->rx_ncm.nth16));


	if (le32_to_cpu(ctx->rx_ncm.nth16.dwSignature) !=
	if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) {
	    USB_CDC_NCM_NTH16_SIGN) {
		pr_debug("invalid NTH16 signature <%u>\n",
		pr_debug("invalid NTH16 signature <%u>\n",
			 le32_to_cpu(ctx->rx_ncm.nth16.dwSignature));
					le32_to_cpu(nth16->dwSignature));
		goto error;
		goto error;
	}
	}


	temp = le16_to_cpu(ctx->rx_ncm.nth16.wBlockLength);
	len = le16_to_cpu(nth16->wBlockLength);
	if (temp > sumlen) {
	if (len > ctx->rx_max) {
		pr_debug("unsupported NTB block length %u/%u\n", temp, sumlen);
		pr_debug("unsupported NTB block length %u/%u\n", len,
								ctx->rx_max);
		goto error;
		goto error;
	}
	}


	temp = le16_to_cpu(ctx->rx_ncm.nth16.wNdpIndex);
	if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) &&
	if ((temp + sizeof(ctx->rx_ncm.ndp16)) > actlen) {
		(ctx->rx_seq || le16_to_cpu(nth16->wSequence)) &&
		pr_debug("invalid DPT16 index\n");
		!((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) {
		pr_debug("sequence number glitch prev=%d curr=%d\n",
				ctx->rx_seq, le16_to_cpu(nth16->wSequence));
	}
	ctx->rx_seq = le16_to_cpu(nth16->wSequence);

	len = le16_to_cpu(nth16->wNdpIndex);
	if ((len + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) {
		pr_debug("invalid DPT16 index <%u>\n",
					le16_to_cpu(nth16->wNdpIndex));
		goto error;
		goto error;
	}
	}


	memcpy(&(ctx->rx_ncm.ndp16), ((u8 *)skb_in->data) + temp,
	ndp16 = (struct usb_cdc_ncm_ndp16 *)(((u8 *)skb_in->data) + len);
						sizeof(ctx->rx_ncm.ndp16));


	if (le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature) !=
	if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) {
	    USB_CDC_NCM_NDP16_NOCRC_SIGN) {
		pr_debug("invalid DPT16 signature <%u>\n",
		pr_debug("invalid DPT16 signature <%u>\n",
			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
					le32_to_cpu(ndp16->dwSignature));
		goto error;
		goto error;
	}
	}


	if (le16_to_cpu(ctx->rx_ncm.ndp16.wLength) <
	if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
	    USB_CDC_NCM_NDP16_LENGTH_MIN) {
		pr_debug("invalid DPT16 length <%u>\n",
		pr_debug("invalid DPT16 length <%u>\n",
			 le32_to_cpu(ctx->rx_ncm.ndp16.dwSignature));
					le32_to_cpu(ndp16->dwSignature));
		goto error;
		goto error;
	}
	}


	nframes = ((le16_to_cpu(ctx->rx_ncm.ndp16.wLength) -
	nframes = ((le16_to_cpu(ndp16->wLength) -
					sizeof(struct usb_cdc_ncm_ndp16)) /
					sizeof(struct usb_cdc_ncm_ndp16)) /
					sizeof(struct usb_cdc_ncm_dpe16));
					sizeof(struct usb_cdc_ncm_dpe16));
	nframes--; /* we process NDP entries except for the last one */
	nframes--; /* we process NDP entries except for the last one */


	pr_debug("nframes = %u\n", nframes);
	len += sizeof(struct usb_cdc_ncm_ndp16);


	temp += sizeof(ctx->rx_ncm.ndp16);
	if ((len + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) >

								skb_in->len) {
	if ((temp + nframes * (sizeof(struct usb_cdc_ncm_dpe16))) > actlen) {
		pr_debug("Invalid nframes = %d\n", nframes);
		pr_debug("Invalid nframes = %d\n", nframes);
		goto error;
		goto error;
	}
	}


	if (nframes > CDC_NCM_DPT_DATAGRAMS_MAX) {
	dpe16 = (struct usb_cdc_ncm_dpe16 *)(((u8 *)skb_in->data) + len);
		pr_debug("Truncating number of frames from %u to %u\n",
					nframes, CDC_NCM_DPT_DATAGRAMS_MAX);
		nframes = CDC_NCM_DPT_DATAGRAMS_MAX;
	}

	memcpy(&(ctx->rx_ncm.dpe16), ((u8 *)skb_in->data) + temp,
				nframes * (sizeof(struct usb_cdc_ncm_dpe16)));


	for (x = 0; x < nframes; x++) {
	for (x = 0; x < nframes; x++, dpe16++) {
		offset = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramIndex);
		offset = le16_to_cpu(dpe16->wDatagramIndex);
		temp = le16_to_cpu(ctx->rx_ncm.dpe16[x].wDatagramLength);
		len = le16_to_cpu(dpe16->wDatagramLength);


		/*
		/*
		 * CDC NCM ch. 3.7
		 * CDC NCM ch. 3.7
		 * All entries after first NULL entry are to be ignored
		 * All entries after first NULL entry are to be ignored
		 */
		 */
		if ((offset == 0) || (temp == 0)) {
		if ((offset == 0) || (len == 0)) {
			if (!x)
			if (!x)
				goto error; /* empty NTB */
				goto error; /* empty NTB */
			break;
			break;
		}
		}


		/* sanity checking */
		/* sanity checking */
		if (((offset + temp) > actlen) ||
		if (((offset + len) > skb_in->len) ||
		    (temp > CDC_NCM_MAX_DATAGRAM_SIZE) || (temp < ETH_HLEN)) {
				(len > ctx->rx_max) || (len < ETH_HLEN)) {
			pr_debug("invalid frame detected (ignored)"
			pr_debug("invalid frame detected (ignored)"
					"offset[%u]=%u, length=%u, skb=%p\n",
					"offset[%u]=%u, length=%u, skb=%p\n",
					x, offset, temp, skb_in);
					x, offset, len, skb_in);
			if (!x)
			if (!x)
				goto error;
				goto error;
			break;
			break;
@@ -1066,9 +1058,9 @@ static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
			skb = skb_clone(skb_in, GFP_ATOMIC);
			skb = skb_clone(skb_in, GFP_ATOMIC);
			if (!skb)
			if (!skb)
				goto error;
				goto error;
			skb->len = temp;
			skb->len = len;
			skb->data = ((u8 *)skb_in->data) + offset;
			skb->data = ((u8 *)skb_in->data) + offset;
			skb_set_tail_pointer(skb, temp);
			skb_set_tail_pointer(skb, len);
			usbnet_skb_return(dev, skb);
			usbnet_skb_return(dev, skb);
		}
		}
	}
	}