mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
wifi: mt76: fix scan work requeue race with spinlock
Prevent mt76_scan_rx_beacon() in softirq from requeuing scan_work after cancel_delayed_work_sync() returns in mt76_abort_scan(). Without synchronization, the beacon handler can observe beacon_wait as true and requeue scan_work between the cancel and the memset in mt76_scan_complete(), leading to a NULL pointer dereference when scan_work runs against zeroed scan state. Introduce scan_lock (placed outside the scan struct to survive the memset) to make the beacon handler's check-and-queue atomic with respect to abort_scan's clear of beacon_wait. This eliminates the race with a single cancel_delayed_work_sync() call. Signed-off-by: Felix Fietkau <[email protected]>
This commit is contained in:
@@ -728,6 +728,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
|
|||||||
INIT_LIST_HEAD(&dev->rxwi_cache);
|
INIT_LIST_HEAD(&dev->rxwi_cache);
|
||||||
dev->token_size = dev->drv->token_size;
|
dev->token_size = dev->drv->token_size;
|
||||||
INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work);
|
INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work);
|
||||||
|
spin_lock_init(&dev->scan_lock);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
|
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
|
||||||
skb_queue_head_init(&dev->rx_skb[i]);
|
skb_queue_head_init(&dev->rx_skb[i]);
|
||||||
|
|||||||
@@ -1021,6 +1021,7 @@ struct mt76_dev {
|
|||||||
u32 rxfilter;
|
u32 rxfilter;
|
||||||
|
|
||||||
struct delayed_work scan_work;
|
struct delayed_work scan_work;
|
||||||
|
spinlock_t scan_lock;
|
||||||
struct {
|
struct {
|
||||||
struct cfg80211_scan_request *req;
|
struct cfg80211_scan_request *req;
|
||||||
struct ieee80211_channel *chan;
|
struct ieee80211_channel *chan;
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
|
|||||||
|
|
||||||
void mt76_abort_scan(struct mt76_dev *dev)
|
void mt76_abort_scan(struct mt76_dev *dev)
|
||||||
{
|
{
|
||||||
|
spin_lock_bh(&dev->scan_lock);
|
||||||
|
dev->scan.beacon_wait = false;
|
||||||
|
spin_unlock_bh(&dev->scan_lock);
|
||||||
|
|
||||||
cancel_delayed_work_sync(&dev->scan_work);
|
cancel_delayed_work_sync(&dev->scan_work);
|
||||||
mt76_scan_complete(dev, true);
|
mt76_scan_complete(dev, true);
|
||||||
}
|
}
|
||||||
@@ -85,16 +89,21 @@ void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan)
|
|||||||
{
|
{
|
||||||
struct mt76_phy *phy;
|
struct mt76_phy *phy;
|
||||||
|
|
||||||
|
spin_lock(&dev->scan_lock);
|
||||||
|
|
||||||
if (!dev->scan.beacon_wait || dev->scan.beacon_received ||
|
if (!dev->scan.beacon_wait || dev->scan.beacon_received ||
|
||||||
dev->scan.chan != chan)
|
dev->scan.chan != chan)
|
||||||
return;
|
goto out;
|
||||||
|
|
||||||
phy = dev->scan.phy;
|
phy = dev->scan.phy;
|
||||||
if (!phy)
|
if (!phy)
|
||||||
return;
|
goto out;
|
||||||
|
|
||||||
dev->scan.beacon_received = true;
|
dev->scan.beacon_received = true;
|
||||||
ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0);
|
ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock(&dev->scan_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mt76_scan_work(struct work_struct *work)
|
void mt76_scan_work(struct work_struct *work)
|
||||||
@@ -111,8 +120,10 @@ void mt76_scan_work(struct work_struct *work)
|
|||||||
if (!phy || !req)
|
if (!phy || !req)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
spin_lock_bh(&dev->scan_lock);
|
||||||
beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received;
|
beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received;
|
||||||
dev->scan.beacon_wait = false;
|
dev->scan.beacon_wait = false;
|
||||||
|
spin_unlock_bh(&dev->scan_lock);
|
||||||
|
|
||||||
if (beacon_rx)
|
if (beacon_rx)
|
||||||
goto probe;
|
goto probe;
|
||||||
@@ -140,8 +151,10 @@ void mt76_scan_work(struct work_struct *work)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) {
|
if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) {
|
||||||
|
spin_lock_bh(&dev->scan_lock);
|
||||||
dev->scan.beacon_received = false;
|
dev->scan.beacon_received = false;
|
||||||
dev->scan.beacon_wait = true;
|
dev->scan.beacon_wait = true;
|
||||||
|
spin_unlock_bh(&dev->scan_lock);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user