[PATCH net v2 2/2] mac802154: pin netdevs for queued RX descriptors

From: Xuanqiang Luo

Date: Thu Sep 03 2026 - 08:41:57 EST


From: Xuanqiang Luo <luoxuanqiang@xxxxxxxxxx>

The RX path keeps the receiving sub-interface in queued beacon and MAC
command descriptors after leaving its RCU read-side critical section. An
interface can therefore be unregistered and freed before mac_wq consumes
the descriptor, leading to a use-after-free such as:

BUG: KASAN: slab-use-after-free in mac802154_rx_mac_cmd_worker+0xc0/0x498 [mac802154]
Read of size 8 at addr ffff0000c6db0ba8 by task kworker/u16:3/61
...
Call trace:
show_stack+0x20/0x38 (C)
dump_stack_lvl+0x78/0x90
print_address_description.constprop.0+0x88/0x398
print_report+0xa8/0x278
kasan_report+0xa8/0xf8
__asan_load8+0x9c/0xc0
mac802154_rx_mac_cmd_worker+0xc0/0x498 [mac802154]
process_one_work+0x334/0x8b8
...
Allocated by task 630:
kasan_save_stack+0x2c/0x58
kasan_save_track+0x20/0x40
kasan_save_alloc_info+0x40/0x58
__kasan_kmalloc+0xa0/0xb8
__kvmalloc_node_noprof+0x1e8/0x588
alloc_netdev_mqs+0x74/0x7f0
ieee802154_if_add+0xac/0x630 [mac802154]
ieee802154_register_hw+0x31c/0x3d0 [mac802154]
fakelb_add_one+0x250/0x318 [fakelb]
...
Freed by task 652:
kasan_save_stack+0x2c/0x58
kasan_save_track+0x20/0x40
kasan_save_free_info+0x4c/0x78
__kasan_slab_free+0x60/0x90
kfree+0x194/0x478
kvfree+0x44/0x60
netdev_release+0x4c/0x68
device_release+0xac/0x130
kobject_cleanup+0x84/0x248
kobject_put+0x98/0xf8
netdev_run_todo+0x3a0/0x5e0
rtnl_unlock+0x18/0x30
ieee802154_unregister_hw+0x48/0x90 [mac802154]
fakelb_remove+0xe8/0x148 [fakelb]

Hold the receiving netdev from before a descriptor is queued until the
descriptor is released. Since sdata is the netdev's private data, this also
keeps sdata valid while a worker or scan cleanup accesses the descriptor.

Fixes: 57588c71177f ("mac802154: Handle passive scanning")
Fixes: d021d218f6d9 ("mac802154: Handle received BEACON_REQ")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Xuanqiang Luo <luoxuanqiang@xxxxxxxxxx>
---
include/net/cfg802154.h | 2 ++
net/mac802154/rx.c | 4 ++++
net/mac802154/scan.c | 1 +
3 files changed, 7 insertions(+)

diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h
index 76d2cd2e2b309..57343762d7f0b 100644
--- a/include/net/cfg802154.h
+++ b/include/net/cfg802154.h
@@ -369,6 +369,7 @@ struct cfg802154_beacon_request {
* @node: MAC packets to process list member
* @skb: the received sk_buff
* @sdata: the interface on which @skb was received
+ * @dev_tracker: netdev reference held until the descriptor is released
* @page: page configuration when @skb was received
* @channel: channel configuration when @skb was received
*/
@@ -376,6 +377,7 @@ struct cfg802154_mac_pkt {
struct list_head node;
struct sk_buff *skb;
struct ieee802154_sub_if_data *sdata;
+ netdevice_tracker dev_tracker;
u8 page;
u8 channel;
};
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 26da20ea470ef..df2f6128ddd4a 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -62,6 +62,7 @@ void mac802154_rx_beacon_worker(struct work_struct *work)
mac802154_process_beacon(local, mac_pkt->skb, mac_pkt->page, mac_pkt->channel);

kfree_skb(mac_pkt->skb);
+ netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker);
kfree(mac_pkt);
}

@@ -141,6 +142,7 @@ void mac802154_rx_mac_cmd_worker(struct work_struct *work)

out:
kfree_skb(mac_pkt->skb);
+ netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker);
kfree(mac_pkt);
}

@@ -237,6 +239,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
mac_pkt->sdata = sdata;
mac_pkt->page = sdata->local->scan_page;
mac_pkt->channel = sdata->local->scan_channel;
+ netdev_hold(sdata->dev, &mac_pkt->dev_tracker, GFP_ATOMIC);
spin_lock_bh(&sdata->local->rx_lists_lock);
list_add_tail(&mac_pkt->node, &sdata->local->rx_beacon_list);
queue_work(sdata->local->mac_wq, &sdata->local->rx_beacon_work);
@@ -251,6 +254,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,

mac_pkt->skb = skb_get(skb);
mac_pkt->sdata = sdata;
+ netdev_hold(sdata->dev, &mac_pkt->dev_tracker, GFP_ATOMIC);
spin_lock_bh(&sdata->local->rx_lists_lock);
list_add_tail(&mac_pkt->node, &sdata->local->rx_mac_cmd_list);
queue_work(sdata->local->mac_wq, &sdata->local->rx_mac_cmd_work);
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 5fa98e001a8fb..c3c61ebde1568 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -114,6 +114,7 @@ static void mac802154_flush_queued_beacons(struct ieee802154_local *local)
list_for_each_entry_safe(mac_pkt, tmp, &mac_pkt_list, node) {
list_del(&mac_pkt->node);
kfree_skb(mac_pkt->skb);
+ netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker);
kfree(mac_pkt);
}
}
--
2.43.0