From 4bc0a34c94094d9e896c5a2f45b54d8be6c0fca7 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Thu, 7 May 2026 21:19:49 +0200 Subject: [PATCH 1/3] bes2600: replace a set of atomic_add() Backport of cw1200 mainline commit 07f995ca1951 ("cw1200: replace a set of atomic_add()", 2020-11-10). atomic_inc() reads more naturally than atomic_add(1, &x). Mechanical change, no functional impact. 7 sites: 6 in bh.c (bh_term, bh_rx x2, bh_tx x3) and 1 in itp.c (awaiting_confirm). Two of the bh_rx and three of the bh_tx sites are inside the cw1200-ancestor #if 0 block; replaced anyway to keep the file consistent with cw1200 mainline source style. Cherry-picked from upstream Linux: 07f995ca1951 cw1200: replace a set of atomic_add() Author: Yejune Deng Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1604991491-27908-1-git-send-email-yejune.deng@gmail.com --- bes2600/bh.c | 12 ++++++------ bes2600/itp.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bes2600/bh.c b/bes2600/bh.c index 6385312..1d2773c 100644 --- a/bes2600/bh.c +++ b/bes2600/bh.c @@ -101,7 +101,7 @@ void bes2600_unregister_bh(struct bes2600_common *hw_priv) coex_deinit_mode(hw_priv); #endif - atomic_add(1, &hw_priv->bh_term); + atomic_inc(&hw_priv->bh_term); wake_up(&hw_priv->bh_wq); flush_workqueue(hw_priv->bh_workqueue); @@ -590,7 +590,7 @@ static int bes2600_bh(void *arg) bes_devel("[BH] Device resume.\n"); atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); wake_up(&hw_priv->bh_evt_wq); - atomic_add(1, &hw_priv->bh_rx); + atomic_inc(&hw_priv->bh_rx); continue; } @@ -758,9 +758,9 @@ tx: #if 0 /* count is not implemented */ if (ret > 1) - atomic_add(1, &hw_priv->bh_tx); + atomic_inc(&hw_priv->bh_tx); #else - atomic_add(1, &hw_priv->bh_tx); + atomic_inc(&hw_priv->bh_tx); #endif #if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) @@ -1134,7 +1134,7 @@ static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv, tx_len += 4; #endif - atomic_add(1, &hw_priv->bh_tx); + atomic_inc(&hw_priv->bh_tx); tx_len = hw_priv->sbus_ops->align_size( hw_priv->sbus_priv, tx_len); @@ -1435,7 +1435,7 @@ static int bes2600_bh(void *arg) bes_devel("[BH] Device resume.\n"); atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); wake_up(&hw_priv->bh_evt_wq); - atomic_add(1, &hw_priv->bh_rx); + atomic_inc(&hw_priv->bh_rx); goto done; } diff --git a/bes2600/itp.c b/bes2600/itp.c index e5c2958..c50b29c 100644 --- a/bes2600/itp.c +++ b/bes2600/itp.c @@ -570,7 +570,7 @@ int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, *burst = 2; atomic_set(&priv->bh_tx, 1); ktime_get_ts(&itp->last_sent); - atomic_add(1, &itp->awaiting_confirm); + atomic_inc(&itp->awaiting_confirm); spin_unlock_bh(&itp->tx_lock); return 1; From 65a4c39914f07bcb0fc01ea78b974e6901d3377d Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Thu, 7 May 2026 21:20:46 +0200 Subject: [PATCH 2/3] bes2600: fix missing destroy_workqueue() on error in init_common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two error paths between create_singlethread_workqueue() (~main.c:489) and the success-path destroy_workqueue() in unregister_common (~609) return without cleaning up the workqueue, leaking it on probe failure: 1. bes2600_queue_stats_init() failure 2. bes2600_queue_init() failure (any of the 4 TID queues) Both call ieee80211_free_hw(hw); return NULL — without first destroy_workqueue(hw_priv->workqueue). Add it. Backport of cw1200 mainline commit 7ec8a926188e ("cw1200: fix missing destroy_workqueue() on error in cw1200_init_common", 2020-11-19), which fixed the identical bug in the same code shape we inherited. Reported on cw1200 by Hulk Robot. Cherry-picked from upstream Linux: 7ec8a926188e cw1200: fix missing destroy_workqueue() on error Author: Qinglang Miao Reported-by: Hulk Robot Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20201119070842.1011-1-miaoqinglang@huawei.com Fixes: a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets") --- bes2600/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bes2600/main.c b/bes2600/main.c index d6da84a..90a8ff8 100644 --- a/bes2600/main.c +++ b/bes2600/main.c @@ -497,6 +497,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) WLAN_LINK_ID_MAX, bes2600_skb_dtor, hw_priv))) { + destroy_workqueue(hw_priv->workqueue); ieee80211_free_hw(hw); return NULL; } @@ -508,6 +509,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) for (; i > 0; i--) bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]); bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); + destroy_workqueue(hw_priv->workqueue); ieee80211_free_hw(hw); return NULL; } From b717251598c95bb7ce7822ffa103216598f19b67 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Thu, 7 May 2026 21:24:01 +0200 Subject: [PATCH 3/3] bes2600: fix concurrency UAF in bes2600_hw_scan and sched_scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Link: https://lore.kernel.org/r/20181214035521.7575-1-baijiaju1990@gmail.com --- bes2600/scan.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/bes2600/scan.c b/bes2600/scan.c index ad5033b..16b5d0f 100644 --- a/bes2600/scan.c +++ b/bes2600/scan.c @@ -257,18 +257,21 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, 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() */ down(&hw_priv->scan.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) { int ret; //if (priv->if_id == 0) @@ -286,9 +289,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, } #endif if (ret) { + dev_kfree_skb(frame.skb); up(&hw_priv->conf_lock); up(&hw_priv->scan.lock); - dev_kfree_skb(frame.skb); return ret; } } @@ -318,10 +321,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, ++hw_priv->scan.n_ssids; } - up(&hw_priv->conf_lock); - if (frame.skb) dev_kfree_skb(frame.skb); + + up(&hw_priv->conf_lock); #ifdef WIFI_BT_COEXIST_EPTA_ENABLE bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING); #endif @@ -362,14 +365,18 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, if (req->n_ssids > hw->wiphy->max_scan_ssids) 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() */ down(&hw_priv->scan.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) { int ret; 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); } if (ret) { + dev_kfree_skb(frame.skb); up(&hw_priv->conf_lock); up(&hw_priv->scan.lock); - dev_kfree_skb(frame.skb); return ret; } } @@ -414,10 +421,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, } } - up(&hw_priv->conf_lock); - if (frame.skb) dev_kfree_skb(frame.skb); + + up(&hw_priv->conf_lock); queue_work(hw_priv->workqueue, &hw_priv->scan.swork); wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n"); return 0;