bes2600: fix concurrency UAF in bes2600_hw_scan and sched_scan
bes2600_bss_info_changed() and bes2600_hw_scan() can run concurrently.
The probe-request SKB allocated by ieee80211_probereq_get() before
scan.lock + conf_lock are taken can be touched by a concurrent
bss_info_changed (via wsm_set_template_frame's path) while we hold no
lock. Reorder to acquire both locks BEFORE the SKB allocation.
Also reorder cleanup paths so dev_kfree_skb() runs BEFORE up() —
otherwise a small window exists where the SKB has been touched but the
lock has been released, allowing concurrent code to also touch it.
Three sites fixed:
- bes2600_hw_scan: lock-take + ENOMEM cleanup + wsm_set_template_frame
error cleanup + success-path SKB free + lock release order
- bes2600_sched_scan_start (#ifdef ROAM_OFFLOAD): same three sub-fixes
(compiled-out at default build, fixed for consistency)
- All success/error paths: dev_kfree_skb before up()
Backport of cw1200 mainline commit 86760e0dfe36 ("cw1200: Fix
concurrency use-after-free bugs in cw1200_hw_scan()", 2018-12-14),
which fixed the identical bug in the same code shape we inherited.
That commit was merged from upstream 4f68ef64cd7f.
Cherry-picked from upstream Linux:
86760e0dfe36 cw1200: Fix concurrency use-after-free bugs in cw1200_hw_scan()
Author: Jia-Ju Bai <baijiaju1990@gmail.com>
Link: https://lore.kernel.org/r/20181214035521.7575-1-baijiaju1990@gmail.com
This commit is contained in:
+26
-19
@@ -257,18 +257,21 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
|||||||
|
|
||||||
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
|
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
|
||||||
|
|
||||||
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
|
|
||||||
req->ie_len);
|
|
||||||
if (!frame.skb)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (req->ie_len)
|
|
||||||
skb_put_data(frame.skb, req->ie, req->ie_len);
|
|
||||||
|
|
||||||
/* will be unlocked in bes2600_scan_work() */
|
/* will be unlocked in bes2600_scan_work() */
|
||||||
down(&hw_priv->scan.lock);
|
down(&hw_priv->scan.lock);
|
||||||
down(&hw_priv->conf_lock);
|
down(&hw_priv->conf_lock);
|
||||||
|
|
||||||
|
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
|
||||||
|
req->ie_len);
|
||||||
|
if (!frame.skb) {
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
up(&hw_priv->scan.lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->ie_len)
|
||||||
|
skb_put_data(frame.skb, req->ie, req->ie_len);
|
||||||
|
|
||||||
if (frame.skb) {
|
if (frame.skb) {
|
||||||
int ret;
|
int ret;
|
||||||
//if (priv->if_id == 0)
|
//if (priv->if_id == 0)
|
||||||
@@ -286,9 +289,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
dev_kfree_skb(frame.skb);
|
||||||
up(&hw_priv->conf_lock);
|
up(&hw_priv->conf_lock);
|
||||||
up(&hw_priv->scan.lock);
|
up(&hw_priv->scan.lock);
|
||||||
dev_kfree_skb(frame.skb);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,10 +321,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
|
|||||||
++hw_priv->scan.n_ssids;
|
++hw_priv->scan.n_ssids;
|
||||||
}
|
}
|
||||||
|
|
||||||
up(&hw_priv->conf_lock);
|
|
||||||
|
|
||||||
if (frame.skb)
|
if (frame.skb)
|
||||||
dev_kfree_skb(frame.skb);
|
dev_kfree_skb(frame.skb);
|
||||||
|
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
||||||
bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
|
bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
|
||||||
#endif
|
#endif
|
||||||
@@ -362,14 +365,18 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
|
|||||||
if (req->n_ssids > hw->wiphy->max_scan_ssids)
|
if (req->n_ssids > hw->wiphy->max_scan_ssids)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
|
|
||||||
req->ie_len);
|
|
||||||
if (!frame.skb)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* will be unlocked in bes2600_scan_work() */
|
/* will be unlocked in bes2600_scan_work() */
|
||||||
down(&hw_priv->scan.lock);
|
down(&hw_priv->scan.lock);
|
||||||
down(&hw_priv->conf_lock);
|
down(&hw_priv->conf_lock);
|
||||||
|
|
||||||
|
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
|
||||||
|
req->ie_len);
|
||||||
|
if (!frame.skb) {
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
up(&hw_priv->scan.lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
if (frame.skb) {
|
if (frame.skb) {
|
||||||
int ret;
|
int ret;
|
||||||
if (priv->if_id == 0)
|
if (priv->if_id == 0)
|
||||||
@@ -380,9 +387,9 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
|
|||||||
ret = wsm_set_probe_responder(priv, true);
|
ret = wsm_set_probe_responder(priv, true);
|
||||||
}
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
dev_kfree_skb(frame.skb);
|
||||||
up(&hw_priv->conf_lock);
|
up(&hw_priv->conf_lock);
|
||||||
up(&hw_priv->scan.lock);
|
up(&hw_priv->scan.lock);
|
||||||
dev_kfree_skb(frame.skb);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,10 +421,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
up(&hw_priv->conf_lock);
|
|
||||||
|
|
||||||
if (frame.skb)
|
if (frame.skb)
|
||||||
dev_kfree_skb(frame.skb);
|
dev_kfree_skb(frame.skb);
|
||||||
|
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
|
queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
|
||||||
wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
|
wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user