diff mbox series

[v3,1/8] ath10k: adjust skb length in ath10k_sdio_mbox_rx_packet

Message ID 1566998177-2658-2-git-send-email-wgong@codeaurora.org (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show
Series ath10k: improve throughout of tcp/udp TX/RX of sdio | expand

Commit Message

Wen Gong Aug. 28, 2019, 1:16 p.m. UTC
When the FW bundles multiple packets, pkt->act_len may be incorrect
as it refers to the first packet only (however, the FW will only
bundle packets that fit into the same pkt->alloc_len).

Before this patch, the skb length would be set (incorrectly) to
pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
adjusted in ath10k_sdio_mbox_rx_process_packet.

The first problem is that ath10k_sdio_mbox_rx_process_packet does not
use proper skb_put commands to adjust the length (it directly changes
skb->len), so we end up with a mismatch between skb->head + skb->tail
and skb->data + skb->len. This is quite serious, and causes corruptions
in the TCP stack, as the stack tries to coalesce packets, and relies
on skb->tail being correct (that is, skb_tail_pointer must point to
the first byte_after_ the data).

Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
bounds check, as skb_put would crash the kernel if not enough space is
available.

Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.

Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
v2:no this patch
v2:new added
 drivers/net/wireless/ath/ath10k/sdio.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

Comments

Nicolas Boichat Aug. 30, 2019, 2:53 a.m. UTC | #1
On Thu, Aug 29, 2019 at 12:18 AM Wen Gong <wgong@codeaurora.org> wrote:
>
> When the FW bundles multiple packets, pkt->act_len may be incorrect
> as it refers to the first packet only (however, the FW will only
> bundle packets that fit into the same pkt->alloc_len).
>
> Before this patch, the skb length would be set (incorrectly) to
> pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
> adjusted in ath10k_sdio_mbox_rx_process_packet.
>
> The first problem is that ath10k_sdio_mbox_rx_process_packet does not
> use proper skb_put commands to adjust the length (it directly changes
> skb->len), so we end up with a mismatch between skb->head + skb->tail
> and skb->data + skb->len. This is quite serious, and causes corruptions
> in the TCP stack, as the stack tries to coalesce packets, and relies
> on skb->tail being correct (that is, skb_tail_pointer must point to
> the first byte_after_ the data).
>
> Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
> this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
> bounds check, as skb_put would crash the kernel if not enough space is
> available.
>
> Tested with QCA6174 SDIO with firmware
> WLAN.RMH.4.4.1-00007-QCARMSWP-1.
>
> Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
> Signed-off-by: Wen Gong <wgong@codeaurora.org>

You are not the author of this patch, please reset the author in your
git tree and resend.

Once you do that, the first line should say:
From: Nicolas Boichat <drinkcat@chromium.org>

Without this fix:
NACKed-by: Nicolas Boichat <drinkcat@chromium.org>

> ---
> v2:no this patch
> v2:new added
>  drivers/net/wireless/ath/ath10k/sdio.c | 27 +++++++++++++++++++--------
>  1 file changed, 19 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
> index 8ed4fbd..1127e44 100644
> --- a/drivers/net/wireless/ath/ath10k/sdio.c
> +++ b/drivers/net/wireless/ath/ath10k/sdio.c
> @@ -381,16 +381,11 @@ static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
>         struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
>         bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
>         enum ath10k_htc_ep_id eid;
> -       u16 payload_len;
>         u8 *trailer;
>         int ret;
>
> -       payload_len = le16_to_cpu(htc_hdr->len);
> -       skb->len = payload_len + sizeof(struct ath10k_htc_hdr);
> -
>         if (trailer_present) {
> -               trailer = skb->data + sizeof(*htc_hdr) +
> -                         payload_len - htc_hdr->trailer_len;
> +               trailer = skb->data + skb->len - htc_hdr->trailer_len;
>
>                 eid = pipe_id_to_eid(htc_hdr->eid);
>
> @@ -632,13 +627,29 @@ static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
>  {
>         struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
>         struct sk_buff *skb = pkt->skb;
> +       struct ath10k_htc_hdr *htc_hdr;
>         int ret;
>
>         ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
>                                  skb->data, pkt->alloc_len);
> +
> +       if (!ret) {
> +               /* Update actual length. The original length may be incorrect,
> +                * as the FW will bundle multiple packets as long as their sizes
> +                * fit within the same aligned length (pkt->alloc_len).
> +                */
> +               htc_hdr = (struct ath10k_htc_hdr *)skb->data;
> +               pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
> +               if (pkt->act_len <= pkt->alloc_len) {
> +                       skb_put(skb, pkt->act_len);
> +               } else {
> +                       ath10k_warn(ar, "rx packet too large (%zu > %zu)\n",
> +                                   pkt->act_len, pkt->alloc_len);
> +                       ret = -EMSGSIZE;
> +               }
> +       }
> +
>         pkt->status = ret;
> -       if (!ret)
> -               skb_put(skb, pkt->act_len);
>
>         return ret;
>  }
> --
> 1.9.1
>
Nicolas Boichat Aug. 30, 2019, 2:58 a.m. UTC | #2
On Fri, Aug 30, 2019 at 10:53 AM Nicolas Boichat <drinkcat@chromium.org> wrote:
>
> On Thu, Aug 29, 2019 at 12:18 AM Wen Gong <wgong@codeaurora.org> wrote:
> >
> > When the FW bundles multiple packets, pkt->act_len may be incorrect
> > as it refers to the first packet only (however, the FW will only
> > bundle packets that fit into the same pkt->alloc_len).
> >
> > Before this patch, the skb length would be set (incorrectly) to
> > pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
> > adjusted in ath10k_sdio_mbox_rx_process_packet.
> >
> > The first problem is that ath10k_sdio_mbox_rx_process_packet does not
> > use proper skb_put commands to adjust the length (it directly changes
> > skb->len), so we end up with a mismatch between skb->head + skb->tail
> > and skb->data + skb->len. This is quite serious, and causes corruptions
> > in the TCP stack, as the stack tries to coalesce packets, and relies
> > on skb->tail being correct (that is, skb_tail_pointer must point to
> > the first byte_after_ the data).
> >
> > Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
> > this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
> > bounds check, as skb_put would crash the kernel if not enough space is
> > available.
> >
> > Tested with QCA6174 SDIO with firmware
> > WLAN.RMH.4.4.1-00007-QCARMSWP-1.
> >
> > Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
> > Signed-off-by: Wen Gong <wgong@codeaurora.org>
>
> You are not the author of this patch, please reset the author in your
> git tree and resend.
>
> Once you do that, the first line should say:
> From: Nicolas Boichat <drinkcat@chromium.org>
>
> Without this fix:
> NACKed-by: Nicolas Boichat <drinkcat@chromium.org>
>

Also you dropped the Fixes line:
Fixes: 8530b4e7b22bc3b ("ath10k: sdio: set skb len for all rx packets")

> > ---
> > v2:no this patch
> > v2:new added
> >  drivers/net/wireless/ath/ath10k/sdio.c | 27 +++++++++++++++++++--------
> >  1 file changed, 19 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
> > index 8ed4fbd..1127e44 100644
> > --- a/drivers/net/wireless/ath/ath10k/sdio.c
> > +++ b/drivers/net/wireless/ath/ath10k/sdio.c
> > @@ -381,16 +381,11 @@ static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
> >         struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
> >         bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
> >         enum ath10k_htc_ep_id eid;
> > -       u16 payload_len;
> >         u8 *trailer;
> >         int ret;
> >
> > -       payload_len = le16_to_cpu(htc_hdr->len);
> > -       skb->len = payload_len + sizeof(struct ath10k_htc_hdr);
> > -
> >         if (trailer_present) {
> > -               trailer = skb->data + sizeof(*htc_hdr) +
> > -                         payload_len - htc_hdr->trailer_len;
> > +               trailer = skb->data + skb->len - htc_hdr->trailer_len;
> >
> >                 eid = pipe_id_to_eid(htc_hdr->eid);
> >
> > @@ -632,13 +627,29 @@ static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
> >  {
> >         struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
> >         struct sk_buff *skb = pkt->skb;
> > +       struct ath10k_htc_hdr *htc_hdr;
> >         int ret;
> >
> >         ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
> >                                  skb->data, pkt->alloc_len);
> > +
> > +       if (!ret) {
> > +               /* Update actual length. The original length may be incorrect,
> > +                * as the FW will bundle multiple packets as long as their sizes
> > +                * fit within the same aligned length (pkt->alloc_len).
> > +                */
> > +               htc_hdr = (struct ath10k_htc_hdr *)skb->data;
> > +               pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
> > +               if (pkt->act_len <= pkt->alloc_len) {
> > +                       skb_put(skb, pkt->act_len);
> > +               } else {
> > +                       ath10k_warn(ar, "rx packet too large (%zu > %zu)\n",
> > +                                   pkt->act_len, pkt->alloc_len);
> > +                       ret = -EMSGSIZE;
> > +               }
> > +       }
> > +
> >         pkt->status = ret;
> > -       if (!ret)
> > -               skb_put(skb, pkt->act_len);
> >
> >         return ret;
> >  }
> > --
> > 1.9.1
> >
Wen Gong Sept. 4, 2019, 2:42 a.m. UTC | #3
> -----Original Message-----
> From: ath10k <ath10k-bounces@lists.infradead.org> On Behalf Of Wen Gong
> Sent: Wednesday, August 28, 2019 9:16 PM
> To: ath10k@lists.infradead.org
> Cc: linux-wireless@vger.kernel.org
> Subject: [EXT] [PATCH v3 1/8] ath10k: adjust skb length in
> ath10k_sdio_mbox_rx_packet
> 
Patch v4 sent, https://patchwork.kernel.org/patch/11126221/
> 
> 
> _______________________________________________
> ath10k mailing list
> ath10k@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/ath10k
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
index 8ed4fbd..1127e44 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.c
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -381,16 +381,11 @@  static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
 	struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
 	bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
 	enum ath10k_htc_ep_id eid;
-	u16 payload_len;
 	u8 *trailer;
 	int ret;
 
-	payload_len = le16_to_cpu(htc_hdr->len);
-	skb->len = payload_len + sizeof(struct ath10k_htc_hdr);
-
 	if (trailer_present) {
-		trailer = skb->data + sizeof(*htc_hdr) +
-			  payload_len - htc_hdr->trailer_len;
+		trailer = skb->data + skb->len - htc_hdr->trailer_len;
 
 		eid = pipe_id_to_eid(htc_hdr->eid);
 
@@ -632,13 +627,29 @@  static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
 {
 	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
 	struct sk_buff *skb = pkt->skb;
+	struct ath10k_htc_hdr *htc_hdr;
 	int ret;
 
 	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
 				 skb->data, pkt->alloc_len);
+
+	if (!ret) {
+		/* Update actual length. The original length may be incorrect,
+		 * as the FW will bundle multiple packets as long as their sizes
+		 * fit within the same aligned length (pkt->alloc_len).
+		 */
+		htc_hdr = (struct ath10k_htc_hdr *)skb->data;
+		pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
+		if (pkt->act_len <= pkt->alloc_len) {
+			skb_put(skb, pkt->act_len);
+		} else {
+			ath10k_warn(ar, "rx packet too large (%zu > %zu)\n",
+				    pkt->act_len, pkt->alloc_len);
+			ret = -EMSGSIZE;
+		}
+	}
+
 	pkt->status = ret;
-	if (!ret)
-		skb_put(skb, pkt->act_len);
 
 	return ret;
 }