diff options
author | Felix Fietkau <nbd@openwrt.org> | 2012-09-08 05:58:30 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-09-10 12:44:58 -0400 |
commit | 1bad53824305807bb5cf49d6b588dd9d867586c6 (patch) | |
tree | 55f046a4d3c0eec4f2f16b18b586c5e245921a03 /net | |
parent | b22cfcfcae5b2c1e9b43543b6a23e5ef517de8f8 (diff) |
mac80211: validate skb->dev in the tx status path
skb->dev might contain a stale reference to a device that was already
deleted, and using it unchecked can lead to invalid pointer accesses.
Since this is only used for nl80211 tx, iterate over active interfaces
to find a match for skb->dev, and discard the tx status if the device
is gone.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/status.c | 48 |
1 files changed, 30 insertions, 18 deletions
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b0801b7d572d..2ce89732d0f2 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
517 | 517 | ||
518 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { | 518 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { |
519 | u64 cookie = (unsigned long)skb; | 519 | u64 cookie = (unsigned long)skb; |
520 | bool found = false; | ||
521 | |||
520 | acked = info->flags & IEEE80211_TX_STAT_ACK; | 522 | acked = info->flags & IEEE80211_TX_STAT_ACK; |
521 | 523 | ||
522 | if (ieee80211_is_nullfunc(hdr->frame_control) || | 524 | rcu_read_lock(); |
523 | ieee80211_is_qos_nullfunc(hdr->frame_control)) { | 525 | |
524 | cfg80211_probe_status(skb->dev, hdr->addr1, | 526 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
525 | cookie, acked, GFP_ATOMIC); | 527 | if (!sdata->dev) |
526 | } else if (skb->dev) { | 528 | continue; |
527 | cfg80211_mgmt_tx_status( | ||
528 | skb->dev->ieee80211_ptr, cookie, skb->data, | ||
529 | skb->len, acked, GFP_ATOMIC); | ||
530 | } else { | ||
531 | struct ieee80211_sub_if_data *p2p_sdata; | ||
532 | 529 | ||
533 | rcu_read_lock(); | 530 | if (skb->dev != sdata->dev) |
531 | continue; | ||
534 | 532 | ||
535 | p2p_sdata = rcu_dereference(local->p2p_sdata); | 533 | found = true; |
536 | if (p2p_sdata) { | 534 | break; |
537 | cfg80211_mgmt_tx_status( | 535 | } |
538 | &p2p_sdata->wdev, cookie, skb->data, | 536 | |
539 | skb->len, acked, GFP_ATOMIC); | 537 | if (!skb->dev) { |
540 | } | 538 | sdata = rcu_dereference(local->p2p_sdata); |
541 | rcu_read_unlock(); | 539 | if (sdata) |
540 | found = true; | ||
541 | } | ||
542 | |||
543 | if (!found) | ||
544 | skb->dev = NULL; | ||
545 | else if (ieee80211_is_nullfunc(hdr->frame_control) || | ||
546 | ieee80211_is_qos_nullfunc(hdr->frame_control)) { | ||
547 | cfg80211_probe_status(sdata->dev, hdr->addr1, | ||
548 | cookie, acked, GFP_ATOMIC); | ||
549 | } else { | ||
550 | cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data, | ||
551 | skb->len, acked, GFP_ATOMIC); | ||
542 | } | 552 | } |
553 | |||
554 | rcu_read_unlock(); | ||
543 | } | 555 | } |
544 | 556 | ||
545 | if (unlikely(info->ack_frame_id)) { | 557 | if (unlikely(info->ack_frame_id)) { |