Takashi Iwai ab97d8
From: Johannes Berg <johannes.berg@intel.com>
Takashi Iwai ab97d8
Date: Fri, 30 Sep 2022 23:44:23 +0200
Takashi Iwai ab97d8
Subject: [PATCH] wifi: cfg80211: fix BSS refcounting bugs
Takashi Iwai ab97d8
Mime-version: 1.0
Takashi Iwai ab97d8
Content-type: text/plain; charset=UTF-8
Takashi Iwai ab97d8
Content-transfer-encoding: 8bit
Takashi Iwai ab97d8
Patch-mainline: Not yet, embargoed
Takashi Iwai ab97d8
References: CVE-2022-42720 bsc#1204059
Takashi Iwai ab97d8
Takashi Iwai ab97d8
There are multiple refcounting bugs related to multi-BSSID:
Takashi Iwai ab97d8
 - In bss_ref_get(), if the BSS has a hidden_beacon_bss, then
Takashi Iwai ab97d8
   the bss pointer is overwritten before checking for the
Takashi Iwai ab97d8
   transmitted BSS, which is clearly wrong. Fix this by using
Takashi Iwai ab97d8
   the bss_from_pub() macro.
Takashi Iwai ab97d8
Takashi Iwai ab97d8
 - In cfg80211_bss_update() we copy the transmitted_bss pointer
Takashi Iwai ab97d8
   from tmp into new, but then if we release new, we'll unref
Takashi Iwai ab97d8
   it erroneously. We already set the pointer and ref it, but
Takashi Iwai ab97d8
   need to NULL it since it was copied from the tmp data.
Takashi Iwai ab97d8
Takashi Iwai ab97d8
 - In cfg80211_inform_single_bss_data(), if adding to the non-
Takashi Iwai ab97d8
   transmitted list fails, we unlink the BSS and yet still we
Takashi Iwai ab97d8
   return it, but this results in returning an entry without
Takashi Iwai ab97d8
   a reference. We shouldn't return it anyway if it was broken
Takashi Iwai ab97d8
   enough to not get added there.
Takashi Iwai ab97d8
Takashi Iwai ab97d8
This fixes CVE-2022-42720.
Takashi Iwai ab97d8
Takashi Iwai ab97d8
Reported-by: Sönke Huster <shuster@seemoo.tu-darmstadt.de>
Takashi Iwai ab97d8
Tested-by: Sönke Huster <shuster@seemoo.tu-darmstadt.de>
Takashi Iwai ab97d8
Fixes: a3584f56de1c ("cfg80211: Properly track transmitting and non-transmitting BSS")
Takashi Iwai ab97d8
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Takashi Iwai ab97d8
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai ab97d8
Takashi Iwai ab97d8
---
Takashi Iwai ab97d8
 net/wireless/scan.c | 27 ++++++++++++++-------------
Takashi Iwai ab97d8
 1 file changed, 14 insertions(+), 13 deletions(-)
Takashi Iwai ab97d8
Takashi Iwai ab97d8
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
Takashi Iwai ab97d8
index 5382fc2003db..94a8679ca388 100644
Takashi Iwai ab97d8
--- a/net/wireless/scan.c
Takashi Iwai ab97d8
+++ b/net/wireless/scan.c
Takashi Iwai ab97d8
@@ -143,18 +143,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
Takashi Iwai ab97d8
 	lockdep_assert_held(&rdev->bss_lock);
Takashi Iwai ab97d8
 
Takashi Iwai ab97d8
 	bss->refcount++;
Takashi Iwai ab97d8
-	if (bss->pub.hidden_beacon_bss) {
Takashi Iwai ab97d8
-		bss = container_of(bss->pub.hidden_beacon_bss,
Takashi Iwai ab97d8
-				   struct cfg80211_internal_bss,
Takashi Iwai ab97d8
-				   pub);
Takashi Iwai ab97d8
-		bss->refcount++;
Takashi Iwai ab97d8
-	}
Takashi Iwai ab97d8
-	if (bss->pub.transmitted_bss) {
Takashi Iwai ab97d8
-		bss = container_of(bss->pub.transmitted_bss,
Takashi Iwai ab97d8
-				   struct cfg80211_internal_bss,
Takashi Iwai ab97d8
-				   pub);
Takashi Iwai ab97d8
-		bss->refcount++;
Takashi Iwai ab97d8
-	}
Takashi Iwai ab97d8
+
Takashi Iwai ab97d8
+	if (bss->pub.hidden_beacon_bss)
Takashi Iwai ab97d8
+		bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++;
Takashi Iwai ab97d8
+
Takashi Iwai ab97d8
+	if (bss->pub.transmitted_bss)
Takashi Iwai ab97d8
+		bss_from_pub(bss->pub.transmitted_bss)->refcount++;
Takashi Iwai ab97d8
 }
Takashi Iwai ab97d8
 
Takashi Iwai ab97d8
 static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
Takashi Iwai ab97d8
@@ -1739,6 +1733,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
Takashi Iwai ab97d8
 		new->refcount = 1;
Takashi Iwai ab97d8
 		INIT_LIST_HEAD(&new->hidden_list);
Takashi Iwai ab97d8
 		INIT_LIST_HEAD(&new->pub.nontrans_list);
Takashi Iwai ab97d8
+		/* we'll set this later if it was non-NULL */
Takashi Iwai ab97d8
+		new->pub.transmitted_bss = NULL;
Takashi Iwai ab97d8
 
Takashi Iwai ab97d8
 		if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
Takashi Iwai ab97d8
 			hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
Takashi Iwai ab97d8
@@ -2021,10 +2017,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
Takashi Iwai ab97d8
 		spin_lock_bh(&rdev->bss_lock);
Takashi Iwai ab97d8
 		if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
Takashi Iwai ab97d8
 					       &res->pub)) {
Takashi Iwai ab97d8
-			if (__cfg80211_unlink_bss(rdev, res))
Takashi Iwai ab97d8
+			if (__cfg80211_unlink_bss(rdev, res)) {
Takashi Iwai ab97d8
 				rdev->bss_generation++;
Takashi Iwai ab97d8
+				res = NULL;
Takashi Iwai ab97d8
+			}
Takashi Iwai ab97d8
 		}
Takashi Iwai ab97d8
 		spin_unlock_bh(&rdev->bss_lock);
Takashi Iwai ab97d8
+
Takashi Iwai ab97d8
+		if (!res)
Takashi Iwai ab97d8
+			return NULL;
Takashi Iwai ab97d8
 	}
Takashi Iwai ab97d8
 
Takashi Iwai ab97d8
 	trace_cfg80211_return_bss(&res->pub);
Takashi Iwai ab97d8
-- 
Takashi Iwai ab97d8
2.35.3
Takashi Iwai ab97d8