Commit 166ac9d5 authored by Sara Sharon's avatar Sara Sharon Committed by Johannes Berg

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: 6e0456b5 ("mac80211: add A-MSDU tx support")
Signed-off-by: default avatarSara Sharon <sara.sharon@intel.com>
Reviewed-by: default avatarLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1f631c32
...@@ -3073,27 +3073,18 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta) ...@@ -3073,27 +3073,18 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta)
} }
static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local, static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
struct sk_buff *skb, int headroom, struct sk_buff *skb, int headroom)
int *subframe_len)
{ {
int amsdu_len = *subframe_len + sizeof(struct ethhdr); if (skb_headroom(skb) < headroom) {
int padding = (4 - amsdu_len) & 3;
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
I802_DEBUG_INC(local->tx_expand_skb_head); 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, wiphy_debug(local->hw.wiphy,
"failed to reallocate TX buffer\n"); "failed to reallocate TX buffer\n");
return false; return false;
} }
} }
if (padding) {
*subframe_len += padding;
skb_put_zero(skb, padding);
}
return true; return true;
} }
...@@ -3117,8 +3108,7 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, ...@@ -3117,8 +3108,7 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
if (info->control.flags & IEEE80211_TX_CTRL_AMSDU) if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
return true; return true;
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr), if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr)))
&subframe_len))
return false; return false;
data = skb_push(skb, sizeof(*amsdu_hdr)); data = skb_push(skb, sizeof(*amsdu_hdr));
...@@ -3184,7 +3174,8 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, ...@@ -3184,7 +3174,8 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
void *data; void *data;
bool ret = false; bool ret = false;
unsigned int orig_len; unsigned int orig_len;
int n = 1, nfrags; int n = 1, nfrags, pad = 0;
u16 hdrlen;
if (!ieee80211_hw_check(&local->hw, TX_AMSDU)) if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
return false; return false;
...@@ -3235,8 +3226,19 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, ...@@ -3235,8 +3226,19 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
if (max_frags && nfrags > max_frags) if (max_frags && nfrags > max_frags)
goto out; 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; goto out;
ret = true; ret = true;
...@@ -3248,6 +3250,8 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, ...@@ -3248,6 +3250,8 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
memcpy(data, &len, 2); memcpy(data, &len, 2);
memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header)); memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
memset(skb_push(skb, pad), 0, pad);
head->len += skb->len; head->len += skb->len;
head->data_len += skb->len; head->data_len += skb->len;
*frag_tail = skb; *frag_tail = skb;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment