Commit 9a5d353908db44d639108dee9fe05c0a8803b572

Authored by Sara Sharon
Committed by Greg Kroah-Hartman
1 parent 79448960e3

mac80211: avoid kernel panic when building AMSDU from non-linear SKB

[ Upstream commit 166ac9d55b0ab70b644e429be1f217fe8393cbd7 ]

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 <sara.sharon@intel.com>
Reviewed-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 1 changed file with 21 additions and 17 deletions Side-by-side Diff

... ... @@ -3022,27 +3022,18 @@
3022 3022 }
3023 3023  
3024 3024 static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
3025   - struct sk_buff *skb, int headroom,
3026   - int *subframe_len)
  3025 + struct sk_buff *skb, int headroom)
3027 3026 {
3028   - int amsdu_len = *subframe_len + sizeof(struct ethhdr);
3029   - int padding = (4 - amsdu_len) & 3;
3030   -
3031   - if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
  3027 + if (skb_headroom(skb) < headroom) {
3032 3028 I802_DEBUG_INC(local->tx_expand_skb_head);
3033 3029  
3034   - if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
  3030 + if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
3035 3031 wiphy_debug(local->hw.wiphy,
3036 3032 "failed to reallocate TX buffer\n");
3037 3033 return false;
3038 3034 }
3039 3035 }
3040 3036  
3041   - if (padding) {
3042   - *subframe_len += padding;
3043   - skb_put_zero(skb, padding);
3044   - }
3045   -
3046 3037 return true;
3047 3038 }
3048 3039  
... ... @@ -3066,8 +3057,7 @@
3066 3057 if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
3067 3058 return true;
3068 3059  
3069   - if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr),
3070   - &subframe_len))
  3060 + if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr)))
3071 3061 return false;
3072 3062  
3073 3063 data = skb_push(skb, sizeof(*amsdu_hdr));
... ... @@ -3133,7 +3123,8 @@
3133 3123 void *data;
3134 3124 bool ret = false;
3135 3125 unsigned int orig_len;
3136   - int n = 1, nfrags;
  3126 + int n = 1, nfrags, pad = 0;
  3127 + u16 hdrlen;
3137 3128  
3138 3129 if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
3139 3130 return false;
... ... @@ -3184,8 +3175,19 @@
3184 3175 if (max_frags && nfrags > max_frags)
3185 3176 goto out;
3186 3177  
3187   - if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
3188   - &subframe_len))
  3178 + /*
  3179 + * Pad out the previous subframe to a multiple of 4 by adding the
  3180 + * padding to the next one, that's being added. Note that head->len
  3181 + * is the length of the full A-MSDU, but that works since each time
  3182 + * we add a new subframe we pad out the previous one to a multiple
  3183 + * of 4 and thus it no longer matters in the next round.
  3184 + */
  3185 + hdrlen = fast_tx->hdr_len - sizeof(rfc1042_header);
  3186 + if ((head->len - hdrlen) & 3)
  3187 + pad = 4 - ((head->len - hdrlen) & 3);
  3188 +
  3189 + if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) +
  3190 + 2 + pad))
3189 3191 goto out;
3190 3192  
3191 3193 ret = true;
... ... @@ -3196,6 +3198,8 @@
3196 3198 len = cpu_to_be16(subframe_len);
3197 3199 memcpy(data, &len, 2);
3198 3200 memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
  3201 +
  3202 + memset(skb_push(skb, pad), 0, pad);
3199 3203  
3200 3204 head->len += skb->len;
3201 3205 head->data_len += skb->len;