]> git.baikalelectronics.ru Git - kernel.git/commitdiff
wifi: cfg80211: fix BSS refcounting bugs
authorJohannes Berg <johannes.berg@intel.com>
Fri, 30 Sep 2022 21:44:23 +0000 (23:44 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 15 Oct 2022 05:54:40 +0000 (07:54 +0200)
commit 71ce40dc9fe1dbc0a8c4c7fe85f630c493429e81 upstream.

There are multiple refcounting bugs related to multi-BSSID:
 - In bss_ref_get(), if the BSS has a hidden_beacon_bss, then
   the bss pointer is overwritten before checking for the
   transmitted BSS, which is clearly wrong. Fix this by using
   the bss_from_pub() macro.

 - In cfg80211_bss_update() we copy the transmitted_bss pointer
   from tmp into new, but then if we release new, we'll unref
   it erroneously. We already set the pointer and ref it, but
   need to NULL it since it was copied from the tmp data.

 - In cfg80211_inform_single_bss_data(), if adding to the non-
   transmitted list fails, we unlink the BSS and yet still we
   return it, but this results in returning an entry without
   a reference. We shouldn't return it anyway if it was broken
   enough to not get added there.

This fixes CVE-2022-42720.

Reported-by: Sönke Huster <shuster@seemoo.tu-darmstadt.de>
Tested-by: Sönke Huster <shuster@seemoo.tu-darmstadt.de>
Fixes: 12020910d0ee ("cfg80211: Properly track transmitting and non-transmitting BSS")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/wireless/scan.c

index 32d6532ce922c30ca38a21e37c788927e6a88aa2..30077be03192c14605b8563caa34580930b7981b 100644 (file)
@@ -104,18 +104,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
        lockdep_assert_held(&rdev->bss_lock);
 
        bss->refcount++;
-       if (bss->pub.hidden_beacon_bss) {
-               bss = container_of(bss->pub.hidden_beacon_bss,
-                                  struct cfg80211_internal_bss,
-                                  pub);
-               bss->refcount++;
-       }
-       if (bss->pub.transmitted_bss) {
-               bss = container_of(bss->pub.transmitted_bss,
-                                  struct cfg80211_internal_bss,
-                                  pub);
-               bss->refcount++;
-       }
+
+       if (bss->pub.hidden_beacon_bss)
+               bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++;
+
+       if (bss->pub.transmitted_bss)
+               bss_from_pub(bss->pub.transmitted_bss)->refcount++;
 }
 
 static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
@@ -1233,6 +1227,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
                new->refcount = 1;
                INIT_LIST_HEAD(&new->hidden_list);
                INIT_LIST_HEAD(&new->pub.nontrans_list);
+               /* we'll set this later if it was non-NULL */
+               new->pub.transmitted_bss = NULL;
 
                if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
                        hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
@@ -1462,10 +1458,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
                spin_lock_bh(&rdev->bss_lock);
                if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
                                               &res->pub)) {
-                       if (__cfg80211_unlink_bss(rdev, res))
+                       if (__cfg80211_unlink_bss(rdev, res)) {
                                rdev->bss_generation++;
+                               res = NULL;
+                       }
                }
                spin_unlock_bh(&rdev->bss_lock);
+
+               if (!res)
+                       return NULL;
        }
 
        trace_cfg80211_return_bss(&res->pub);