]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mac80211: validate skb->dev in the tx status path
authorFelix Fietkau <nbd@openwrt.org>
Sat, 8 Sep 2012 09:58:30 +0000 (11:58 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 10 Sep 2012 16:44:58 +0000 (18:44 +0200)
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>
net/mac80211/status.c

index b0801b7d572dfd71ad2f50b1b6dba4429aaa0894..2ce89732d0f21755939699b55e3382ac98baeaca 100644 (file)
@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
                u64 cookie = (unsigned long)skb;
+               bool found = false;
+
                acked = info->flags & IEEE80211_TX_STAT_ACK;
 
-               if (ieee80211_is_nullfunc(hdr->frame_control) ||
-                   ieee80211_is_qos_nullfunc(hdr->frame_control)) {
-                       cfg80211_probe_status(skb->dev, hdr->addr1,
-                                             cookie, acked, GFP_ATOMIC);
-               } else if (skb->dev) {
-                       cfg80211_mgmt_tx_status(
-                               skb->dev->ieee80211_ptr, cookie, skb->data,
-                               skb->len, acked, GFP_ATOMIC);
-               } else {
-                       struct ieee80211_sub_if_data *p2p_sdata;
+               rcu_read_lock();
+
+               list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+                       if (!sdata->dev)
+                               continue;
 
-                       rcu_read_lock();
+                       if (skb->dev != sdata->dev)
+                               continue;
 
-                       p2p_sdata = rcu_dereference(local->p2p_sdata);
-                       if (p2p_sdata) {
-                               cfg80211_mgmt_tx_status(
-                                       &p2p_sdata->wdev, cookie, skb->data,
-                                       skb->len, acked, GFP_ATOMIC);
-                       }
-                       rcu_read_unlock();
+                       found = true;
+                       break;
+               }
+
+               if (!skb->dev) {
+                       sdata = rcu_dereference(local->p2p_sdata);
+                       if (sdata)
+                               found = true;
+               }
+
+               if (!found)
+                       skb->dev = NULL;
+               else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+                        ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+                       cfg80211_probe_status(sdata->dev, hdr->addr1,
+                                             cookie, acked, GFP_ATOMIC);
+               } else {
+                       cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+                                               skb->len, acked, GFP_ATOMIC);
                }
+
+               rcu_read_unlock();
        }
 
        if (unlikely(info->ack_frame_id)) {