|
@@ -0,0 +1,102 @@
|
|
|
+From: Sara Sharon <[email protected]>
|
|
|
+Date: Wed, 29 Aug 2018 08:57:02 +0200
|
|
|
+Subject: [PATCH] mac80211: avoid kernel panic when building AMSDU from
|
|
|
+ non-linear SKB
|
|
|
+
|
|
|
+When building building AMSDU from non-linear SKB, we hit a
|
|
|
+kernel panic when trying to push the padding to the tail.
|
|
|
+Instead, put the padding at the head of the next subframe.
|
|
|
+This also fixes the A-MSDU subframes to not have the padding
|
|
|
+accounted in the length field and not have pad at all for
|
|
|
+the last subframe, both required by the spec.
|
|
|
+
|
|
|
+Fixes: 6e0456b54545 ("mac80211: add A-MSDU tx support")
|
|
|
+Signed-off-by: Sara Sharon <[email protected]>
|
|
|
+Reviewed-by: Lorenzo Bianconi <[email protected]>
|
|
|
+Signed-off-by: Johannes Berg <[email protected]>
|
|
|
+---
|
|
|
+
|
|
|
+--- a/net/mac80211/tx.c
|
|
|
++++ b/net/mac80211/tx.c
|
|
|
+@@ -3064,27 +3064,18 @@ void ieee80211_clear_fast_xmit(struct st
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
|
|
|
+- struct sk_buff *skb, int headroom,
|
|
|
+- int *subframe_len)
|
|
|
++ struct sk_buff *skb, int headroom)
|
|
|
+ {
|
|
|
+- int amsdu_len = *subframe_len + sizeof(struct ethhdr);
|
|
|
+- int padding = (4 - amsdu_len) & 3;
|
|
|
+-
|
|
|
+- if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
|
|
|
++ if (skb_headroom(skb) < headroom) {
|
|
|
+ I802_DEBUG_INC(local->tx_expand_skb_head);
|
|
|
+
|
|
|
+- if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
|
|
|
++ if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
|
|
|
+ wiphy_debug(local->hw.wiphy,
|
|
|
+ "failed to reallocate TX buffer\n");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- if (padding) {
|
|
|
+- *subframe_len += padding;
|
|
|
+- skb_put_zero(skb, padding);
|
|
|
+- }
|
|
|
+-
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -3108,8 +3099,7 @@ static bool ieee80211_amsdu_prepare_head
|
|
|
+ if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
|
|
|
+ return true;
|
|
|
+
|
|
|
+- if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr),
|
|
|
+- &subframe_len))
|
|
|
++ if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr)))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ data = skb_push(skb, sizeof(*amsdu_hdr));
|
|
|
+@@ -3176,7 +3166,8 @@ static bool ieee80211_amsdu_aggregate(st
|
|
|
+ void *data;
|
|
|
+ bool ret = false;
|
|
|
+ unsigned int orig_len;
|
|
|
+- int n = 1, nfrags;
|
|
|
++ int n = 1, nfrags, pad = 0;
|
|
|
++ u16 hdrlen;
|
|
|
+
|
|
|
+ if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
|
|
|
+ return false;
|
|
|
+@@ -3228,8 +3219,19 @@ static bool ieee80211_amsdu_aggregate(st
|
|
|
+ if (max_frags && nfrags > max_frags)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+- if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
|
|
|
+- &subframe_len))
|
|
|
++ /*
|
|
|
++ * Pad out the previous subframe to a multiple of 4 by adding the
|
|
|
++ * padding to the next one, that's being added. Note that head->len
|
|
|
++ * is the length of the full A-MSDU, but that works since each time
|
|
|
++ * we add a new subframe we pad out the previous one to a multiple
|
|
|
++ * of 4 and thus it no longer matters in the next round.
|
|
|
++ */
|
|
|
++ hdrlen = fast_tx->hdr_len - sizeof(rfc1042_header);
|
|
|
++ if ((head->len - hdrlen) & 3)
|
|
|
++ pad = 4 - ((head->len - hdrlen) & 3);
|
|
|
++
|
|
|
++ if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) +
|
|
|
++ 2 + pad))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = true;
|
|
|
+@@ -3241,6 +3243,8 @@ static bool ieee80211_amsdu_aggregate(st
|
|
|
+ memcpy(data, &len, 2);
|
|
|
+ memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
|
|
|
+
|
|
|
++ memset(skb_push(skb, pad), 0, pad);
|
|
|
++
|
|
|
+ head->len += skb->len;
|
|
|
+ head->data_len += skb->len;
|
|
|
+ *frag_tail = skb;
|