]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: 6lowpan: avoid untracked enable work
authorCen Zhang <zzzccc427@gmail.com>
Tue, 23 Jun 2026 16:12:29 +0000 (00:12 +0800)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 6 Jul 2026 14:46:57 +0000 (10:46 -0400)
lowpan_enable_set() allocates a temporary work item and schedules
do_enable_set() on system_wq, then returns to debugfs. The debugfs active
operation has ended at that point, but the worker still executes module
text and manipulates enable_6lowpan and listen_chan.

bt_6lowpan_exit() removes the debugfs files and immediately closes and
puts listen_chan. It has no pointer to the queued work item, so it cannot
cancel or flush it before tearing down the state that the worker uses.

The buggy scenario involves two paths, with each column showing the order
within that path:

debugfs enable write              module exit
1. lowpan_enable_set() allocates  1. bt_6lowpan_exit() removes
   set_enable work                   the debugfs file
2. schedule_work() queues         2. bt_6lowpan_exit() closes
   do_enable_set()                   and puts listen_chan
3. the write operation returns    3. module teardown can continue
4. do_enable_set() later runs
   against stale state

Run the enable state transition synchronously in lowpan_enable_set()
instead. The simple debugfs setter can sleep, and this file already handles
the 6LoWPAN control write synchronously under the same set_lock. Once the
setter returns, debugfs removal covers the whole operation and exit can no
longer race with an untracked work item.

Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in do_enable_set+0x113/0x2e0
Workqueue: events do_enable_set [bluetooth_6lowpan]
The buggy address belongs to the object at ffff888109cb8000

Fixes: 90305829635d ("Bluetooth: 6lowpan: Converting rwlocks to use RCU")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/6lowpan.c

index 962e0e885105fdaad83ea8866314c7f9628c3f36..c4b0a4048be2267aafdfdb43502b9c99d55455d0 100644 (file)
@@ -1081,23 +1081,15 @@ done:
        } while (nchans);
 }
 
-struct set_enable {
-       struct work_struct work;
-       bool flag;
-};
-
-static void do_enable_set(struct work_struct *work)
+static void do_enable_set(bool flag)
 {
-       struct set_enable *set_enable = container_of(work,
-                                                    struct set_enable, work);
-
-       if (!set_enable->flag || enable_6lowpan != set_enable->flag)
+       if (!flag || enable_6lowpan != flag)
                /* Disconnect existing connections if 6lowpan is
                 * disabled
                 */
                disconnect_all_peers();
 
-       enable_6lowpan = set_enable->flag;
+       enable_6lowpan = flag;
 
        mutex_lock(&set_lock);
        if (listen_chan) {
@@ -1109,22 +1101,11 @@ static void do_enable_set(struct work_struct *work)
 
        listen_chan = bt_6lowpan_listen();
        mutex_unlock(&set_lock);
-
-       kfree(set_enable);
 }
 
 static int lowpan_enable_set(void *data, u64 val)
 {
-       struct set_enable *set_enable;
-
-       set_enable = kzalloc_obj(*set_enable);
-       if (!set_enable)
-               return -ENOMEM;
-
-       set_enable->flag = !!val;
-       INIT_WORK(&set_enable->work, do_enable_set);
-
-       schedule_work(&set_enable->work);
+       do_enable_set(!!val);
 
        return 0;
 }