From: Xuanqiang Luo <[email protected]>

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: [email protected]
Signed-off-by: Xuanqiang Luo <[email protected]>
---
 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

Reply via email to