]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mac802154: hold an interface reference across the scan worker
authorIbrahim Hashimov <security@auditcode.ai>
Tue, 21 Jul 2026 21:12:28 +0000 (23:12 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 23 Jul 2026 17:37:46 +0000 (10:37 -0700)
mac802154_scan_worker() captures the scanning sub-interface under RCU
and then keeps dereferencing sdata->dev after rcu_read_unlock() and
outside the rtnl -- in the failure traces, in
mac802154_transmit_beacon_req() (skb->dev = sdata->dev), and in the
end_scan cleanup. Nothing keeps that netdev alive across the worker
iteration.

A concurrent DEL_INTERFACE or PHY removal can unregister the interface
once the worker drops the rtnl between its two drv_set_channel()
sections. unregister_netdevice() frees the netdev asynchronously from
netdev_run_todo() with the rtnl already dropped, so neither holding the
rtnl nor the per-PHY IEEE802154_IS_SCANNING flag prevents a stale worker
iteration from dereferencing the freed netdev -- a KASAN
slab-use-after-free, reachable by racing TRIGGER_SCAN against
DEL_INTERFACE (both CAP_NET_ADMIN).

Pin the netdev with netdev_hold() while the RCU read lock is still held,
and release it at every worker exit.

Fixes: 57588c71177f ("mac802154: Handle passive scanning")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Link: https://patch.msgid.link/20260721211228.34578-1-security@auditcode.ai
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/mac802154/scan.c

index 300d4584533e6f1cc501a9169d4e7e4fbceb9638..65089826ff59725766fa5237872a80029eb566f7 100644 (file)
@@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work)
        enum nl802154_scan_types scan_req_type;
        struct ieee802154_sub_if_data *sdata;
        unsigned int scan_duration = 0;
+       netdevice_tracker dev_tracker;
        struct wpan_phy *wpan_phy;
        u8 scan_req_duration;
        u8 page, channel;
@@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work)
                return;
        }
 
+       /*
+        * sdata->dev is dereferenced below after rcu_read_unlock() and outside
+        * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it
+        * asynchronously from netdev_run_todo(). Pin it with a reference taken
+        * while the RCU read lock is still held, and drop it at every exit.
+        */
+       netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
+
        wpan_phy = scan_req->wpan_phy;
        scan_req_type = scan_req->type;
        scan_req_duration = scan_req->duration;
@@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work)
                "Scan page %u channel %u for %ums\n",
                page, channel, jiffies_to_msecs(scan_duration));
        queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
+       netdev_put(sdata->dev, &dev_tracker);
        return;
 
 end_scan:
        rtnl_lock();
        mac802154_scan_cleanup_locked(local, sdata, false);
        rtnl_unlock();
+       netdev_put(sdata->dev, &dev_tracker);
 }
 
 int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,