mac80211: fix use of skb payload instead of header

When ieee80211_skb_resize() is called from ieee80211_build_hdr()
the skb has no 802.11 header yet, in fact it consist only of the
payload as the ethernet frame is removed. As such, we're using
the payload data for ieee80211_is_mgmt(), which is of course
completely wrong. This didn't really hurt us because these are
always data frames, so we could only have added more tailroom
than we needed if we determined it was a management frame and
sdata->crypto_tx_tailroom_needed_cnt was false.

However, syzbot found that of course there need not be any payload,
so we're using at best uninitialized memory for the check.

Fix this to pass explicitly the kind of frame that we have instead
of checking there, by replacing the "bool may_encrypt" argument
with an argument that can carry the three possible states - it's
not going to be encrypted, it's a management frame, or it's a data
frame (and then we check sdata->crypto_tx_tailroom_needed_cnt).

Reported-by: syzbot+32fd1a1bfe355e93f1e2@syzkaller.appspotmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Link: https://lore.kernel.org/r/20201009132538.e1fd7f802947.I799b288466ea2815f9d4c84349fae697dca2f189@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2020-10-09 13:25:41 +02:00
parent 804fc6a293
commit 14f46c1e51

View File

@ -1942,19 +1942,24 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
/* device xmit handlers */
enum ieee80211_encrypt {
ENCRYPT_NO,
ENCRYPT_MGMT,
ENCRYPT_DATA,
};
static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
int head_need, bool may_encrypt)
int head_need,
enum ieee80211_encrypt encrypt)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *hdr;
bool enc_tailroom;
int tail_need = 0;
hdr = (struct ieee80211_hdr *) skb->data;
enc_tailroom = may_encrypt &&
(sdata->crypto_tx_tailroom_needed_cnt ||
ieee80211_is_mgmt(hdr->frame_control));
enc_tailroom = encrypt == ENCRYPT_MGMT ||
(encrypt == ENCRYPT_DATA &&
sdata->crypto_tx_tailroom_needed_cnt);
if (enc_tailroom) {
tail_need = IEEE80211_ENCRYPT_TAILROOM;
@ -1985,23 +1990,29 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
int headroom;
bool may_encrypt;
enum ieee80211_encrypt encrypt;
may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
if (info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)
encrypt = ENCRYPT_NO;
else if (ieee80211_is_mgmt(hdr->frame_control))
encrypt = ENCRYPT_MGMT;
else
encrypt = ENCRYPT_DATA;
headroom = local->tx_headroom;
if (may_encrypt)
if (encrypt != ENCRYPT_NO)
headroom += sdata->encrypt_headroom;
headroom -= skb_headroom(skb);
headroom = max_t(int, 0, headroom);
if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
if (ieee80211_skb_resize(sdata, skb, headroom, encrypt)) {
ieee80211_free_txskb(&local->hw, skb);
return;
}
/* reload after potential resize */
hdr = (struct ieee80211_hdr *) skb->data;
info->control.vif = &sdata->vif;
@ -2828,7 +2839,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
head_need += sdata->encrypt_headroom;
head_need += local->tx_headroom;
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
return ERR_PTR(-ENOMEM);
@ -3502,7 +3513,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
if (unlikely(ieee80211_skb_resize(sdata, skb,
max_t(int, extra_head + hw_headroom -
skb_headroom(skb), 0),
false))) {
ENCRYPT_NO))) {
kfree_skb(skb);
return true;
}