[PATCH net v3] mac802154: hold an interface reference across the scan worker
From: Ibrahim Hashimov <hidden>
Date: 2026-07-21 21:13:10
Also in:
lkml, stable
Subsystem:
ieee 802.15.4 subsystem, networking [general], the rest · Maintainers:
Alexander Aring, Stefan Schmidt, Miquel Raynal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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 <redacted>
Assisted-by: AuditCode-AI:2026.07
---
v3: use netdev_hold()/netdev_put() with a stack tracker instead of
dev_hold()/dev_put(), per Jakub Kicinski. Posted standalone.
v2: trim the in-worker comment, per Miquel Raynal.
net/mac802154/scan.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 300d4584533e..d452b8d8fbb6 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c@@ -180,6 +180,7 @@ void mac802154_scan_worker(struct work_struct *work) struct ieee802154_sub_if_data *sdata; unsigned int scan_duration = 0; struct wpan_phy *wpan_phy; + netdevice_tracker dev_tracker; u8 scan_req_duration; u8 page, channel; int ret;
@@ -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,
--
2.50.1 (Apple Git-155)