From 6f543d37d984e1ec7ffad0d9bd0123f6a4debb0b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 Jun 2023 20:28:45 +0200 Subject: [PATCH] 4.14-stable patches added patches: cdc_ncm-fix-the-build-warning.patch cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch --- .../cdc_ncm-fix-the-build-warning.patch | 31 + ...32-bit-version-of-ncm-transfer-block.patch | 690 ++++++++++++++++++ ...th-too-low-values-of-dwntboutmaxsize.patch | 128 ++++ queue-4.14/series | 3 + 4 files changed, 852 insertions(+) create mode 100644 queue-4.14/cdc_ncm-fix-the-build-warning.patch create mode 100644 queue-4.14/cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch create mode 100644 queue-4.14/net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch diff --git a/queue-4.14/cdc_ncm-fix-the-build-warning.patch b/queue-4.14/cdc_ncm-fix-the-build-warning.patch new file mode 100644 index 00000000000..8b06dd50df3 --- /dev/null +++ b/queue-4.14/cdc_ncm-fix-the-build-warning.patch @@ -0,0 +1,31 @@ +From 5d0ab06b63fc9c727a7bb72c81321c0114be540b Mon Sep 17 00:00:00 2001 +From: Alexander Bersenev +Date: Sat, 14 Mar 2020 10:33:24 +0500 +Subject: cdc_ncm: Fix the build warning + +From: Alexander Bersenev + +commit 5d0ab06b63fc9c727a7bb72c81321c0114be540b upstream. + +The ndp32->wLength is two bytes long, so replace cpu_to_le32 with cpu_to_le16. + +Fixes: 0fa81b304a79 ("cdc_ncm: Implement the 32-bit version of NCM Transfer Block") +Signed-off-by: Alexander Bersenev +Signed-off-by: David S. Miller +Signed-off-by: Tudor Ambarus +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/usb/cdc_ncm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/usb/cdc_ncm.c ++++ b/drivers/net/usb/cdc_ncm.c +@@ -1176,7 +1176,7 @@ static struct usb_cdc_ncm_ndp32 *cdc_ncm + ndp32 = ctx->delayed_ndp32; + + ndp32->dwSignature = sign; +- ndp32->wLength = cpu_to_le32(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32)); ++ ndp32->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32)); + return ndp32; + } + diff --git a/queue-4.14/cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch b/queue-4.14/cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch new file mode 100644 index 00000000000..9eb376d05c4 --- /dev/null +++ b/queue-4.14/cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch @@ -0,0 +1,690 @@ +From 0fa81b304a7973a499f844176ca031109487dd31 Mon Sep 17 00:00:00 2001 +From: Alexander Bersenev +Date: Fri, 6 Mar 2020 01:33:16 +0500 +Subject: cdc_ncm: Implement the 32-bit version of NCM Transfer Block + +From: Alexander Bersenev + +commit 0fa81b304a7973a499f844176ca031109487dd31 upstream. + +The NCM specification defines two formats of transfer blocks: with 16-bit +fields (NTB-16) and with 32-bit fields (NTB-32). Currently only NTB-16 is +implemented. + +This patch adds the support of NTB-32. The motivation behind this is that +some devices such as E5785 or E5885 from the current generation of Huawei +LTE routers do not support NTB-16. The previous generations of Huawei +devices are also use NTB-32 by default. + +Also this patch enables NTB-32 by default for Huawei devices. + +During the 2019 ValdikSS made five attempts to contact Huawei to add the +NTB-16 support to their router firmware, but they were unsuccessful. + +Signed-off-by: Alexander Bersenev +Signed-off-by: David S. Miller +Stable-dep-of: 7e01c7f7046e ("net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize") +Signed-off-by: Tudor Ambarus +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/usb/cdc_ncm.c | 415 ++++++++++++++++++++++++++++++--------- + drivers/net/usb/huawei_cdc_ncm.c | 8 + include/linux/usb/cdc_ncm.h | 15 + + 3 files changed, 342 insertions(+), 96 deletions(-) + +--- a/drivers/net/usb/cdc_ncm.c ++++ b/drivers/net/usb/cdc_ncm.c +@@ -175,7 +175,11 @@ static u32 cdc_ncm_check_tx_max(struct u + u32 val, max, min; + + /* clamp new_tx to sane values */ +- min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); ++ if (ctx->is_ndp16) ++ min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); ++ else ++ min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32); ++ + max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); + if (max == 0) + max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */ +@@ -309,10 +313,17 @@ static ssize_t ndp_to_end_store(struct d + if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) + return len; + +- if (enable && !ctx->delayed_ndp16) { +- ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); +- if (!ctx->delayed_ndp16) +- return -ENOMEM; ++ if (enable) { ++ if (ctx->is_ndp16 && !ctx->delayed_ndp16) { ++ ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); ++ if (!ctx->delayed_ndp16) ++ return -ENOMEM; ++ } ++ if (!ctx->is_ndp16 && !ctx->delayed_ndp32) { ++ ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); ++ if (!ctx->delayed_ndp32) ++ return -ENOMEM; ++ } + } + + /* flush pending data before changing flag */ +@@ -514,6 +525,9 @@ static int cdc_ncm_init(struct usbnet *d + dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n"); + } + ++ /* use ndp16 by default */ ++ ctx->is_ndp16 = 1; ++ + /* set NTB format, if both formats are supported. + * + * "The host shall only send this command while the NCM Data +@@ -521,14 +535,27 @@ static int cdc_ncm_init(struct usbnet *d + */ + if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) & + USB_CDC_NCM_NTB32_SUPPORTED) { +- dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); +- err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, +- USB_TYPE_CLASS | USB_DIR_OUT +- | USB_RECIP_INTERFACE, +- USB_CDC_NCM_NTB16_FORMAT, +- iface_no, NULL, 0); +- if (err < 0) ++ if (ctx->drvflags & CDC_NCM_FLAG_PREFER_NTB32) { ++ ctx->is_ndp16 = 0; ++ dev_dbg(&dev->intf->dev, "Setting NTB format to 32-bit\n"); ++ err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, ++ USB_TYPE_CLASS | USB_DIR_OUT ++ | USB_RECIP_INTERFACE, ++ USB_CDC_NCM_NTB32_FORMAT, ++ iface_no, NULL, 0); ++ } else { ++ ctx->is_ndp16 = 1; ++ dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); ++ err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, ++ USB_TYPE_CLASS | USB_DIR_OUT ++ | USB_RECIP_INTERFACE, ++ USB_CDC_NCM_NTB16_FORMAT, ++ iface_no, NULL, 0); ++ } ++ if (err < 0) { ++ ctx->is_ndp16 = 1; + dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n"); ++ } + } + + /* set initial device values */ +@@ -551,7 +578,10 @@ static int cdc_ncm_init(struct usbnet *d + ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; + + /* set up maximum NDP size */ +- ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); ++ if (ctx->is_ndp16) ++ ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); ++ else ++ ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp32) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe32); + + /* initial coalescing timer interval */ + ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; +@@ -736,7 +766,10 @@ static void cdc_ncm_free(struct cdc_ncm_ + ctx->tx_curr_skb = NULL; + } + +- kfree(ctx->delayed_ndp16); ++ if (ctx->is_ndp16) ++ kfree(ctx->delayed_ndp16); ++ else ++ kfree(ctx->delayed_ndp32); + + kfree(ctx); + } +@@ -774,10 +807,8 @@ int cdc_ncm_bind_common(struct usbnet *d + u8 *buf; + int len; + int temp; +- int err; + u8 iface_no; + struct usb_cdc_parsed_header hdr; +- __le16 curr_ntb_format; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) +@@ -882,32 +913,6 @@ int cdc_ncm_bind_common(struct usbnet *d + goto error2; + } + +- /* +- * Some Huawei devices have been observed to come out of reset in NDP32 mode. +- * Let's check if this is the case, and set the device to NDP16 mode again if +- * needed. +- */ +- if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) { +- err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT, +- USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, +- 0, iface_no, &curr_ntb_format, 2); +- if (err < 0) { +- goto error2; +- } +- +- if (curr_ntb_format == cpu_to_le16(USB_CDC_NCM_NTB32_FORMAT)) { +- dev_info(&intf->dev, "resetting NTB format to 16-bit"); +- err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, +- USB_TYPE_CLASS | USB_DIR_OUT +- | USB_RECIP_INTERFACE, +- USB_CDC_NCM_NTB16_FORMAT, +- iface_no, NULL, 0); +- +- if (err < 0) +- goto error2; +- } +- } +- + cdc_ncm_find_endpoints(dev, ctx->data); + cdc_ncm_find_endpoints(dev, ctx->control); + if (!dev->in || !dev->out || !dev->status) { +@@ -932,9 +937,15 @@ int cdc_ncm_bind_common(struct usbnet *d + + /* Allocate the delayed NDP if needed. */ + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { +- ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); +- if (!ctx->delayed_ndp16) +- goto error2; ++ if (ctx->is_ndp16) { ++ ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); ++ if (!ctx->delayed_ndp16) ++ goto error2; ++ } else { ++ ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); ++ if (!ctx->delayed_ndp32) ++ goto error2; ++ } + dev_info(&intf->dev, "NDP will be placed at end of frame for this device."); + } + +@@ -1059,7 +1070,7 @@ static void cdc_ncm_align_tail(struct sk + /* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly + * allocating a new one within skb + */ +-static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) ++static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) + { + struct usb_cdc_ncm_ndp16 *ndp16 = NULL; + struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data; +@@ -1114,12 +1125,73 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm + return ndp16; + } + ++static struct usb_cdc_ncm_ndp32 *cdc_ncm_ndp32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) ++{ ++ struct usb_cdc_ncm_ndp32 *ndp32 = NULL; ++ struct usb_cdc_ncm_nth32 *nth32 = (void *)skb->data; ++ size_t ndpoffset = le32_to_cpu(nth32->dwNdpIndex); ++ ++ /* If NDP should be moved to the end of the NCM package, we can't follow the ++ * NTH32 header as we would normally do. NDP isn't written to the SKB yet, and ++ * the wNdpIndex field in the header is actually not consistent with reality. It will be later. ++ */ ++ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { ++ if (ctx->delayed_ndp32->dwSignature == sign) ++ return ctx->delayed_ndp32; ++ ++ /* We can only push a single NDP to the end. Return ++ * NULL to send what we've already got and queue this ++ * skb for later. ++ */ ++ else if (ctx->delayed_ndp32->dwSignature) ++ return NULL; ++ } ++ ++ /* follow the chain of NDPs, looking for a match */ ++ while (ndpoffset) { ++ ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb->data + ndpoffset); ++ if (ndp32->dwSignature == sign) ++ return ndp32; ++ ndpoffset = le32_to_cpu(ndp32->dwNextNdpIndex); ++ } ++ ++ /* align new NDP */ ++ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) ++ cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size); ++ ++ /* verify that there is room for the NDP and the datagram (reserve) */ ++ if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size) ++ return NULL; ++ ++ /* link to it */ ++ if (ndp32) ++ ndp32->dwNextNdpIndex = cpu_to_le32(skb->len); ++ else ++ nth32->dwNdpIndex = cpu_to_le32(skb->len); ++ ++ /* push a new empty NDP */ ++ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) ++ ndp32 = skb_put_zero(skb, ctx->max_ndp_size); ++ else ++ ndp32 = ctx->delayed_ndp32; ++ ++ ndp32->dwSignature = sign; ++ ndp32->wLength = cpu_to_le32(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32)); ++ return ndp32; ++} ++ + struct sk_buff * + cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) + { + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; +- struct usb_cdc_ncm_nth16 *nth16; +- struct usb_cdc_ncm_ndp16 *ndp16; ++ union { ++ struct usb_cdc_ncm_nth16 *nth16; ++ struct usb_cdc_ncm_nth32 *nth32; ++ } nth; ++ union { ++ struct usb_cdc_ncm_ndp16 *ndp16; ++ struct usb_cdc_ncm_ndp32 *ndp32; ++ } ndp; + struct sk_buff *skb_out; + u16 n = 0, index, ndplen; + u8 ready2send = 0; +@@ -1186,11 +1258,19 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + } + ctx->tx_low_mem_val--; + } +- /* fill out the initial 16-bit NTB header */ +- nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16)); +- nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); +- nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); +- nth16->wSequence = cpu_to_le16(ctx->tx_seq++); ++ if (ctx->is_ndp16) { ++ /* fill out the initial 16-bit NTB header */ ++ nth.nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16)); ++ nth.nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); ++ nth.nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); ++ nth.nth16->wSequence = cpu_to_le16(ctx->tx_seq++); ++ } else { ++ /* fill out the initial 32-bit NTB header */ ++ nth.nth32 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth32)); ++ nth.nth32->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH32_SIGN); ++ nth.nth32->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth32)); ++ nth.nth32->wSequence = cpu_to_le16(ctx->tx_seq++); ++ } + + /* count total number of frames in this NTB */ + ctx->tx_curr_frame_num = 0; +@@ -1212,13 +1292,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + } + + /* get the appropriate NDP for this skb */ +- ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); ++ if (ctx->is_ndp16) ++ ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); ++ else ++ ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); + + /* align beginning of next frame */ + cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size); + + /* check if we had enough room left for both NDP and frame */ +- if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { ++ if ((ctx->is_ndp16 && !ndp.ndp16) || (!ctx->is_ndp16 && !ndp.ndp32) || ++ skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { + if (n == 0) { + /* won't fit, MTU problem? */ + dev_kfree_skb_any(skb); +@@ -1240,13 +1324,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + } + + /* calculate frame number withing this NDP */ +- ndplen = le16_to_cpu(ndp16->wLength); +- index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; ++ if (ctx->is_ndp16) { ++ ndplen = le16_to_cpu(ndp.ndp16->wLength); ++ index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; ++ ++ /* OK, add this skb */ ++ ndp.ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); ++ ndp.ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); ++ ndp.ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); ++ } else { ++ ndplen = le16_to_cpu(ndp.ndp32->wLength); ++ index = (ndplen - sizeof(struct usb_cdc_ncm_ndp32)) / sizeof(struct usb_cdc_ncm_dpe32) - 1; + +- /* OK, add this skb */ +- ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); +- ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); +- ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); ++ ndp.ndp32->dpe32[index].dwDatagramLength = cpu_to_le32(skb->len); ++ ndp.ndp32->dpe32[index].dwDatagramIndex = cpu_to_le32(skb_out->len); ++ ndp.ndp32->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe32)); ++ } + skb_put_data(skb_out, skb->data, skb->len); + ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */ + dev_kfree_skb_any(skb); +@@ -1293,13 +1386,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + + /* If requested, put NDP at end of frame. */ + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { +- nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; +- cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); +- nth16->wNdpIndex = cpu_to_le16(skb_out->len); +- skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size); ++ if (ctx->is_ndp16) { ++ nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; ++ cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); ++ nth.nth16->wNdpIndex = cpu_to_le16(skb_out->len); ++ skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size); + +- /* Zero out delayed NDP - signature checking will naturally fail. */ +- ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size); ++ /* Zero out delayed NDP - signature checking will naturally fail. */ ++ ndp.ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size); ++ } else { ++ nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data; ++ cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); ++ nth.nth32->dwNdpIndex = cpu_to_le32(skb_out->len); ++ skb_put_data(skb_out, ctx->delayed_ndp32, ctx->max_ndp_size); ++ ++ ndp.ndp32 = memset(ctx->delayed_ndp32, 0, ctx->max_ndp_size); ++ } + } + + /* If collected data size is less or equal ctx->min_tx_pkt +@@ -1322,8 +1424,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + } + + /* set final frame length */ +- nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; +- nth16->wBlockLength = cpu_to_le16(skb_out->len); ++ if (ctx->is_ndp16) { ++ nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; ++ nth.nth16->wBlockLength = cpu_to_le16(skb_out->len); ++ } else { ++ nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data; ++ nth.nth32->dwBlockLength = cpu_to_le32(skb_out->len); ++ } + + /* return skb */ + ctx->tx_curr_skb = NULL; +@@ -1406,7 +1513,12 @@ cdc_ncm_tx_fixup(struct usbnet *dev, str + goto error; + + spin_lock_bh(&ctx->mtx); +- skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); ++ ++ if (ctx->is_ndp16) ++ skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); ++ else ++ skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)); ++ + spin_unlock_bh(&ctx->mtx); + return skb_out; + +@@ -1467,6 +1579,54 @@ error: + } + EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16); + ++int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) ++{ ++ struct usbnet *dev = netdev_priv(skb_in->dev); ++ struct usb_cdc_ncm_nth32 *nth32; ++ int len; ++ int ret = -EINVAL; ++ ++ if (ctx == NULL) ++ goto error; ++ ++ if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth32) + ++ sizeof(struct usb_cdc_ncm_ndp32))) { ++ netif_dbg(dev, rx_err, dev->net, "frame too short\n"); ++ goto error; ++ } ++ ++ nth32 = (struct usb_cdc_ncm_nth32 *)skb_in->data; ++ ++ if (nth32->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH32_SIGN)) { ++ netif_dbg(dev, rx_err, dev->net, ++ "invalid NTH32 signature <%#010x>\n", ++ le32_to_cpu(nth32->dwSignature)); ++ goto error; ++ } ++ ++ len = le32_to_cpu(nth32->dwBlockLength); ++ if (len > ctx->rx_max) { ++ netif_dbg(dev, rx_err, dev->net, ++ "unsupported NTB block length %u/%u\n", len, ++ ctx->rx_max); ++ goto error; ++ } ++ ++ if ((ctx->rx_seq + 1) != le16_to_cpu(nth32->wSequence) && ++ (ctx->rx_seq || le16_to_cpu(nth32->wSequence)) && ++ !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth32->wSequence))) { ++ netif_dbg(dev, rx_err, dev->net, ++ "sequence number glitch prev=%d curr=%d\n", ++ ctx->rx_seq, le16_to_cpu(nth32->wSequence)); ++ } ++ ctx->rx_seq = le16_to_cpu(nth32->wSequence); ++ ++ ret = le32_to_cpu(nth32->dwNdpIndex); ++error: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth32); ++ + /* verify NDP header and return number of datagrams, or negative error */ + int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) + { +@@ -1503,6 +1663,42 @@ error: + } + EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); + ++/* verify NDP header and return number of datagrams, or negative error */ ++int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset) ++{ ++ struct usbnet *dev = netdev_priv(skb_in->dev); ++ struct usb_cdc_ncm_ndp32 *ndp32; ++ int ret = -EINVAL; ++ ++ if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp32)) > skb_in->len) { ++ netif_dbg(dev, rx_err, dev->net, "invalid NDP offset <%u>\n", ++ ndpoffset); ++ goto error; ++ } ++ ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset); ++ ++ if (le16_to_cpu(ndp32->wLength) < USB_CDC_NCM_NDP32_LENGTH_MIN) { ++ netif_dbg(dev, rx_err, dev->net, "invalid DPT32 length <%u>\n", ++ le16_to_cpu(ndp32->wLength)); ++ goto error; ++ } ++ ++ ret = ((le16_to_cpu(ndp32->wLength) - ++ sizeof(struct usb_cdc_ncm_ndp32)) / ++ sizeof(struct usb_cdc_ncm_dpe32)); ++ ret--; /* we process NDP entries except for the last one */ ++ ++ if ((sizeof(struct usb_cdc_ncm_ndp32) + ++ ret * (sizeof(struct usb_cdc_ncm_dpe32))) > skb_in->len) { ++ netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret); ++ ret = -EINVAL; ++ } ++ ++error: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp32); ++ + int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) + { + struct sk_buff *skb; +@@ -1511,34 +1707,66 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, + int nframes; + int x; + int offset; +- struct usb_cdc_ncm_ndp16 *ndp16; +- struct usb_cdc_ncm_dpe16 *dpe16; ++ union { ++ struct usb_cdc_ncm_ndp16 *ndp16; ++ struct usb_cdc_ncm_ndp32 *ndp32; ++ } ndp; ++ union { ++ struct usb_cdc_ncm_dpe16 *dpe16; ++ struct usb_cdc_ncm_dpe32 *dpe32; ++ } dpe; ++ + int ndpoffset; + int loopcount = 50; /* arbitrary max preventing infinite loop */ + u32 payload = 0; + +- ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); ++ if (ctx->is_ndp16) ++ ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); ++ else ++ ndpoffset = cdc_ncm_rx_verify_nth32(ctx, skb_in); ++ + if (ndpoffset < 0) + goto error; + + next_ndp: +- nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); +- if (nframes < 0) +- goto error; ++ if (ctx->is_ndp16) { ++ nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); ++ if (nframes < 0) ++ goto error; + +- ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); ++ ndp.ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); + +- if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { +- netif_dbg(dev, rx_err, dev->net, +- "invalid DPT16 signature <%#010x>\n", +- le32_to_cpu(ndp16->dwSignature)); +- goto err_ndp; +- } +- dpe16 = ndp16->dpe16; +- +- for (x = 0; x < nframes; x++, dpe16++) { +- offset = le16_to_cpu(dpe16->wDatagramIndex); +- len = le16_to_cpu(dpe16->wDatagramLength); ++ if (ndp.ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { ++ netif_dbg(dev, rx_err, dev->net, ++ "invalid DPT16 signature <%#010x>\n", ++ le32_to_cpu(ndp.ndp16->dwSignature)); ++ goto err_ndp; ++ } ++ dpe.dpe16 = ndp.ndp16->dpe16; ++ } else { ++ nframes = cdc_ncm_rx_verify_ndp32(skb_in, ndpoffset); ++ if (nframes < 0) ++ goto error; ++ ++ ndp.ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset); ++ ++ if (ndp.ndp32->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)) { ++ netif_dbg(dev, rx_err, dev->net, ++ "invalid DPT32 signature <%#010x>\n", ++ le32_to_cpu(ndp.ndp32->dwSignature)); ++ goto err_ndp; ++ } ++ dpe.dpe32 = ndp.ndp32->dpe32; ++ } ++ ++ for (x = 0; x < nframes; x++) { ++ if (ctx->is_ndp16) { ++ offset = le16_to_cpu(dpe.dpe16->wDatagramIndex); ++ len = le16_to_cpu(dpe.dpe16->wDatagramLength); ++ } else { ++ offset = le32_to_cpu(dpe.dpe32->dwDatagramIndex); ++ len = le32_to_cpu(dpe.dpe32->dwDatagramLength); ++ } + + /* + * CDC NCM ch. 3.7 +@@ -1569,10 +1797,19 @@ next_ndp: + usbnet_skb_return(dev, skb); + payload += len; /* count payload bytes in this NTB */ + } ++ ++ if (ctx->is_ndp16) ++ dpe.dpe16++; ++ else ++ dpe.dpe32++; + } + err_ndp: + /* are there more NDPs to process? */ +- ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); ++ if (ctx->is_ndp16) ++ ndpoffset = le16_to_cpu(ndp.ndp16->wNextNdpIndex); ++ else ++ ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex); ++ + if (ndpoffset && loopcount--) + goto next_ndp; + +--- a/drivers/net/usb/huawei_cdc_ncm.c ++++ b/drivers/net/usb/huawei_cdc_ncm.c +@@ -81,11 +81,11 @@ static int huawei_cdc_ncm_bind(struct us + */ + drvflags |= CDC_NCM_FLAG_NDP_TO_END; + +- /* Additionally, it has been reported that some Huawei E3372H devices, with +- * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence +- * needing to be set to the NTB16 one again. ++ /* For many Huawei devices the NTB32 mode is the default and the best mode ++ * they work with. Huawei E5785 and E5885 devices refuse to work in NTB16 mode at all. + */ +- drvflags |= CDC_NCM_FLAG_RESET_NTB16; ++ drvflags |= CDC_NCM_FLAG_PREFER_NTB32; ++ + ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags); + if (ret) + goto err; +--- a/include/linux/usb/cdc_ncm.h ++++ b/include/linux/usb/cdc_ncm.h +@@ -45,9 +45,12 @@ + #define CDC_NCM_DATA_ALTSETTING_NCM 1 + #define CDC_NCM_DATA_ALTSETTING_MBIM 2 + +-/* CDC NCM subclass 3.2.1 */ ++/* CDC NCM subclass 3.3.1 */ + #define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10 + ++/* CDC NCM subclass 3.3.2 */ ++#define USB_CDC_NCM_NDP32_LENGTH_MIN 0x20 ++ + /* Maximum NTB length */ + #define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */ + #define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */ +@@ -83,7 +86,7 @@ + /* Driver flags */ + #define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */ + #define CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE 0x04 /* Avoid altsetting toggle during init */ +-#define CDC_NCM_FLAG_RESET_NTB16 0x08 /* set NDP16 one more time after altsetting switch */ ++#define CDC_NCM_FLAG_PREFER_NTB32 0x08 /* prefer NDP32 over NDP16 */ + + #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ + (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) +@@ -112,7 +115,11 @@ struct cdc_ncm_ctx { + + u32 timer_interval; + u32 max_ndp_size; +- struct usb_cdc_ncm_ndp16 *delayed_ndp16; ++ u8 is_ndp16; ++ union { ++ struct usb_cdc_ncm_ndp16 *delayed_ndp16; ++ struct usb_cdc_ncm_ndp32 *delayed_ndp32; ++ }; + + u32 tx_timer_pending; + u32 tx_curr_frame_num; +@@ -149,6 +156,8 @@ void cdc_ncm_unbind(struct usbnet *dev, + struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); + int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); + int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset); ++int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); ++int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset); + struct sk_buff * + cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); + int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in); diff --git a/queue-4.14/net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch b/queue-4.14/net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch new file mode 100644 index 00000000000..ae7b540a1c4 --- /dev/null +++ b/queue-4.14/net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch @@ -0,0 +1,128 @@ +From 7e01c7f7046efc2c7c192c3619db43292b98e997 Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Wed, 17 May 2023 13:38:08 +0000 +Subject: net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize + +From: Tudor Ambarus + +commit 7e01c7f7046efc2c7c192c3619db43292b98e997 upstream. + +Currently in cdc_ncm_check_tx_max(), if dwNtbOutMaxSize is lower than +the calculated "min" value, but greater than zero, the logic sets +tx_max to dwNtbOutMaxSize. This is then used to allocate a new SKB in +cdc_ncm_fill_tx_frame() where all the data is handled. + +For small values of dwNtbOutMaxSize the memory allocated during +alloc_skb(dwNtbOutMaxSize, GFP_ATOMIC) will have the same size, due to +how size is aligned at alloc time: + size = SKB_DATA_ALIGN(size); + size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); +Thus we hit the same bug that we tried to squash with +commit 2be6d4d16a084 ("net: cdc_ncm: Allow for dwNtbOutMaxSize to be unset or zero") + +Low values of dwNtbOutMaxSize do not cause an issue presently because at +alloc_skb() time more memory (512b) is allocated than required for the +SKB headers alone (320b), leaving some space (512b - 320b = 192b) +for CDC data (172b). + +However, if more elements (for example 3 x u64 = [24b]) were added to +one of the SKB header structs, say 'struct skb_shared_info', +increasing its original size (320b [320b aligned]) to something larger +(344b [384b aligned]), then suddenly the CDC data (172b) no longer +fits in the spare SKB data area (512b - 384b = 128b). + +Consequently the SKB bounds checking semantics fails and panics: + +skbuff: skb_over_panic: text:ffffffff831f755b len:184 put:172 head:ffff88811f1c6c00 data:ffff88811f1c6c00 tail:0xb8 end:0x80 dev: +------------[ cut here ]------------ +kernel BUG at net/core/skbuff.c:113! +invalid opcode: 0000 [#1] PREEMPT SMP KASAN +CPU: 0 PID: 57 Comm: kworker/0:2 Not tainted 5.15.106-syzkaller-00249-g19c0ed55a470 #0 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/14/2023 +Workqueue: mld mld_ifc_work +RIP: 0010:skb_panic net/core/skbuff.c:113 [inline] +RIP: 0010:skb_over_panic+0x14c/0x150 net/core/skbuff.c:118 +[snip] +Call Trace: + + skb_put+0x151/0x210 net/core/skbuff.c:2047 + skb_put_zero include/linux/skbuff.h:2422 [inline] + cdc_ncm_ndp16 drivers/net/usb/cdc_ncm.c:1131 [inline] + cdc_ncm_fill_tx_frame+0x11ab/0x3da0 drivers/net/usb/cdc_ncm.c:1308 + cdc_ncm_tx_fixup+0xa3/0x100 + +Deal with too low values of dwNtbOutMaxSize, clamp it in the range +[USB_CDC_NCM_NTB_MIN_OUT_SIZE, CDC_NCM_NTB_MAX_SIZE_TX]. We ensure +enough data space is allocated to handle CDC data by making sure +dwNtbOutMaxSize is not smaller than USB_CDC_NCM_NTB_MIN_OUT_SIZE. + +Fixes: 289507d3364f ("net: cdc_ncm: use sysfs for rx/tx aggregation tuning") +Cc: stable@vger.kernel.org +Reported-by: syzbot+9f575a1f15fc0c01ed69@syzkaller.appspotmail.com +Link: https://syzkaller.appspot.com/bug?extid=b982f1059506db48409d +Link: https://lore.kernel.org/all/20211202143437.1411410-1-lee.jones@linaro.org/ +Signed-off-by: Tudor Ambarus +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/20230517133808.1873695-2-tudor.ambarus@linaro.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Tudor Ambarus +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/usb/cdc_ncm.c | 24 +++++++++++++++--------- + 1 file changed, 15 insertions(+), 9 deletions(-) + +--- a/drivers/net/usb/cdc_ncm.c ++++ b/drivers/net/usb/cdc_ncm.c +@@ -180,9 +180,12 @@ static u32 cdc_ncm_check_tx_max(struct u + else + min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32); + +- max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); +- if (max == 0) ++ if (le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) == 0) + max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */ ++ else ++ max = clamp_t(u32, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize), ++ USB_CDC_NCM_NTB_MIN_OUT_SIZE, ++ CDC_NCM_NTB_MAX_SIZE_TX); + + /* some devices set dwNtbOutMaxSize too low for the above default */ + min = min(min, max); +@@ -1231,6 +1234,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + * further. + */ + if (skb_out == NULL) { ++ /* If even the smallest allocation fails, abort. */ ++ if (ctx->tx_curr_size == USB_CDC_NCM_NTB_MIN_OUT_SIZE) ++ goto alloc_failed; + ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1, + (unsigned)CDC_NCM_LOW_MEM_MAX_CNT); + ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt; +@@ -1249,13 +1255,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); + + /* No allocation possible so we will abort */ +- if (skb_out == NULL) { +- if (skb != NULL) { +- dev_kfree_skb_any(skb); +- dev->net->stats.tx_dropped++; +- } +- goto exit_no_skb; +- } ++ if (!skb_out) ++ goto alloc_failed; + ctx->tx_low_mem_val--; + } + if (ctx->is_ndp16) { +@@ -1448,6 +1449,11 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev + + return skb_out; + ++alloc_failed: ++ if (skb) { ++ dev_kfree_skb_any(skb); ++ dev->net->stats.tx_dropped++; ++ } + exit_no_skb: + /* Start timer, if there is a remaining non-empty skb */ + if (ctx->tx_curr_skb != NULL && n > 0) diff --git a/queue-4.14/series b/queue-4.14/series index b1809bf722a..c0cba4d2ef9 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -54,3 +54,6 @@ ext4-add-lockdep-annotations-for-i_data_sem-for-ea_inode-s.patch fbcon-fix-null-ptr-deref-in-soft_cursor.patch scsi-dpt_i2o-remove-broken-pass-through-ioctl-i2ousercmd.patch scsi-dpt_i2o-do-not-process-completions-with-invalid-addresses.patch +cdc_ncm-implement-the-32-bit-version-of-ncm-transfer-block.patch +cdc_ncm-fix-the-build-warning.patch +net-cdc_ncm-deal-with-too-low-values-of-dwntboutmaxsize.patch -- 2.47.3