Re: [PATCH] Bluetooth: HCI: fix slab-use-after-free in cmd_sync_work

Marek Szyprowski
2024-04-26T11:10:52Z

Dear All,

On 25.04.2024 06:11, Sungwoo Kim wrote:
> Hello, could you review the UAF bug and its fix?
> The stack trace is at the bottom.
>
> mgmt sync cmd could be used after freed in this scenario:
>
> set_local_name() ... cmd is allocated, set_name_complete() is
> queued in cmd_sync_work.
> hci_error_reset() ... hci device reset.
> hci_dev_close_sync() ... close hdev, at this point, cmd is freed.
> set_name_complete() ... callback from cmd_sync_work. cmd->param causes UAF.
>
> To fix this, this patch makes hci_dev_close_sync() call hci_cmd_sync_clear() to clear the cmd_sync_work.
>
> Thanks,
> Sungwoo Kim.

This patch landed in today's linux-next as commit 37dd04e4d594
("Bluetooth: HCI: fix slab-use-after-free in cmd_sync_work"). I believe
it correctly fixes the mentioned problem, but on the other hand it
introduces the following kernel's lock dependency checker warning:

============================================
WARNING: possible recursive locking detected
6.9.0-rc4-01047-g37dd04e4d594 #14949 Not tainted
--------------------------------------------
kworker/u9:0/56 is trying to acquire lock:
c3aba78c ((work_completion)(&hdev->cmd_sync_work)){+.+.}-{0:0}, at:
__flush_work+0x1e0/0x538

but task is already holding lock:
f0eb5f18 ((work_completion)(&hdev->cmd_sync_work)){+.+.}-{0:0}, at:
process_scheduled_works+0x328/0x7a8

other info that might help us debug this:
Possible unsafe locking scenario:

CPU0
----
lock((work_completion)(&hdev->cmd_sync_work));
lock((work_completion)(&hdev->cmd_sync_work));

*** DEADLOCK ***

May be due to missing lock nesting notation

4 locks held by kworker/u9:0/56:
#0: c3a4e8b4 ((wq_completion)hci0){+.+.}-{0:0}, at:
process_scheduled_works+0x2fc/0x7a8
#1: f0eb5f18 ((work_completion)(&hdev->cmd_sync_work)){+.+.}-{0:0},
at: process_scheduled_works+0x328/0x7a8
#2: c3abab6c (&hdev->req_lock){+.+.}-{3:3}, at:
hci_cmd_sync_work+0xa0/0x154 [bluetooth]
#3: c137bdfc (rcu_read_lock){....}-{1:2}, at: __flush_work+0x40/0x538

stack backtrace:
CPU: 0 PID: 56 Comm: kworker/u9:0 Not tainted
6.9.0-rc4-01047-g37dd04e4d594 #14949
Hardware name: Samsung Exynos (Flattened Device Tree)
Workqueue: hci0 hci_cmd_sync_work [bluetooth]
Call trace:.
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x68/0x88
dump_stack_lvl from __lock_acquire+0x152c/0x1744
__lock_acquire from lock_acquire+0x21c/0x394
lock_acquire from __flush_work+0x20c/0x538
__flush_work from __cancel_work_sync+0x12c/0x20c
__cancel_work_sync from hci_cmd_sync_clear+0x1c/0x6c [bluetooth]
hci_cmd_sync_clear [bluetooth] from hci_dev_close_sync+0x31c/0x5f4
[bluetooth]
hci_dev_close_sync [bluetooth] from hci_set_powered_sync+0x27c/0x288
[bluetooth]
hci_set_powered_sync [bluetooth] from hci_cmd_sync_work+0xb0/0x154
[bluetooth]
hci_cmd_sync_work [bluetooth] from process_scheduled_works+0x390/0x7a8
process_scheduled_works from worker_thread+0x150/0x3bc
worker_thread from kthread+0x108/0x140
kthread from ret_from_fork+0x14/0x28
Exception stack(0xf0eb5fb0 to 0xf0eb5ff8)
...

Please check if the locking is really correct and if so, add the needed
lockdep annotation to the bluetooth code to silence the above warning.

> ==================================================================
> BUG: KASAN: slab-use-after-free in set_name_complete+0x4a/0x330 net/bluetooth/mgmt.c:3815
> Read of size 8 at addr ffff888107259098 by task kworker/u3:0/66
>
> CPU: 0 PID: 66 Comm: kworker/u3:0 Not tainted 6.8.0+ #61
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
> Workqueue: hci0 hci_cmd_sync_work
> Call Trace:
> <TASK>
> __dump_stack lib/dump_stack.c:88 [inline]
> dump_stack_lvl+0x85/0xb0 lib/dump_stack.c:106
> print_address_description mm/kasan/report.c:377 [inline]
> print_report+0x18f/0x560 mm/kasan/report.c:488
> kasan_report+0xd7/0x110 mm/kasan/report.c:601
> __asan_report_load8_noabort+0x18/0x20 mm/kasan/report_generic.c:381
> set_name_complete+0x4a/0x330 net/bluetooth/mgmt.c:3815
> hci_cmd_sync_work+0x269/0x3e0 net/bluetooth/hci_sync.c:308
> process_one_work kernel/workqueue.c:2633 [inline]
> process_scheduled_works+0x6b9/0xdc0 kernel/workqueue.c:2706
> worker_thread+0xb2b/0x13d0 kernel/workqueue.c:2787
> kthread+0x2a9/0x340 kernel/kthread.c:388
> ret_from_fork+0x5c/0x90 arch/x86/kernel/process.c:147
> ret_from_fork_asm+0x1b/0x30 arch/x86/entry/entry_64.S:243
> </TASK>
>
> Allocated by task 308:
> kasan_save_stack mm/kasan/common.c:47 [inline]
> kasan_save_track+0x30/0x70 mm/kasan/common.c:68
> kasan_save_alloc_info+0x3c/0x50 mm/kasan/generic.c:575
> poison_kmalloc_redzone mm/kasan/common.c:370 [inline]
> __kasan_kmalloc+0xa2/0xc0 mm/kasan/common.c:387
> kasan_kmalloc include/linux/kasan.h:211 [inline]
> kmalloc_trace+0x1c9/0x390 mm/slub.c:4012
> kmalloc include/linux/slab.h:590 [inline]
> kzalloc include/linux/slab.h:711 [inline]
> mgmt_pending_new+0x6f/0x230 net/bluetooth/mgmt_util.c:269
> mgmt_pending_add+0x3f/0x120 net/bluetooth/mgmt_util.c:296
> set_local_name+0x15a/0x4c0 net/bluetooth/mgmt.c:3892
> hci_mgmt_cmd+0xb79/0x1190 net/bluetooth/hci_sock.c:1715
> hci_sock_sendmsg+0x63a/0xf00 net/bluetooth/hci_sock.c:1835
> sock_sendmsg_nosec net/socket.c:730 [inline]
> __sock_sendmsg+0x227/0x270 net/socket.c:745
> sock_write_iter+0x28d/0x3d0 net/socket.c:1160
> do_iter_readv_writev+0x331/0x4c0
> vfs_writev+0x2e6/0xa40 fs/read_write.c:971
> do_writev+0xfd/0x250 fs/read_write.c:1018
> __do_sys_writev fs/read_write.c:1091 [inline]
> __se_sys_writev fs/read_write.c:1088 [inline]
> __x64_sys_writev+0x86/0xa0 fs/read_write.c:1088
> do_syscall_x64 arch/x86/entry/common.c:52 [inline]
> do_syscall_64+0x84/0x120 arch/x86/entry/common.c:83
> entry_SYSCALL_64_after_hwframe+0x6e/0x76
>
> Freed by task 66:
> kasan_save_stack mm/kasan/common.c:47 [inline]
> kasan_save_track+0x30/0x70 mm/kasan/common.c:68
> kasan_save_free_info+0x44/0x50 mm/kasan/generic.c:589
> poison_slab_object+0x11a/0x190 mm/kasan/common.c:240
> __kasan_slab_free+0x3b/0x60 mm/kasan/common.c:256
> kasan_slab_free include/linux/kasan.h:184 [inline]
> slab_free_hook mm/slub.c:2121 [inline]
> slab_free mm/slub.c:4299 [inline]
> kfree+0x106/0x2e0 mm/slub.c:4409
> mgmt_pending_free net/bluetooth/mgmt_util.c:309 [inline]
> mgmt_pending_remove+0x19e/0x1d0 net/bluetooth/mgmt_util.c:315
> cmd_complete_rsp+0x104/0x1a0
> mgmt_pending_foreach+0xc7/0x120 net/bluetooth/mgmt_util.c:259
> __mgmt_power_off+0x137/0x370 net/bluetooth/mgmt.c:9496
> hci_dev_close_sync+0x4ab/0xe80 net/bluetooth/hci_sync.c:4953
> hci_dev_do_close net/bluetooth/hci_core.c:554 [inline]
> hci_error_reset+0x150/0x410 net/bluetooth/hci_core.c:1060
> process_one_work kernel/workqueue.c:2633 [inline]
> process_scheduled_works+0x6b9/0xdc0 kernel/workqueue.c:2706
> worker_thread+0xb2b/0x13d0 kernel/workqueue.c:2787
> kthread+0x2a9/0x340 kernel/kthread.c:388
> ret_from_fork+0x5c/0x90 arch/x86/kernel/process.c:147
> ret_from_fork_asm+0x1b/0x30 arch/x86/entry/entry_64.S:243
>
> The buggy address belongs to the object at ffff888107259080
> which belongs to the cache kmalloc-96 of size 96
> The buggy address is located 24 bytes inside of
> freed 96-byte region [ffff888107259080, ffff8881072590e0)
>
> The buggy address belongs to the physical page:
> page:000000006bdb81a5 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff888107259280 pfn:0x107259
> flags: 0x17ffffc0000a00(workingset|slab|node=0|zone=2|lastcpupid=0x1fffff)
> page_type: 0xffffffff()
> raw: 0017ffffc0000a00 ffff888100041780 ffffea0004145510 ffffea0004240190
> raw: ffff888107259280 000000000020000f 00000001ffffffff 0000000000000000
> page dumped because: kasan: bad access detected
>
> Memory state around the buggy address:
> ffff888107258f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> ffff888107259000: 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc
>> ffff888107259080: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
> ^
> ffff888107259100: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
> ffff888107259180: 00 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc
> ==================================================================
>
> Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
> ---
> net/bluetooth/hci_core.c | 2 --
> net/bluetooth/hci_sync.c | 5 ++++-
> 2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index a7028d38c..c347efc4f 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2764,8 +2764,6 @@ void hci_unregister_dev(struct hci_dev *hdev)
>
> cancel_work_sync(&hdev->power_on);
>
> - hci_cmd_sync_clear(hdev);
> -
> hci_unregister_suspend_notifier(hdev);
>
> msft_unregister(hdev);
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index c5d879904..aa8e0c33c 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -5181,9 +5181,12 @@ int hci_dev_close_sync(struct hci_dev *hdev)
> clear_bit(HCI_INIT, &hdev->flags);
> }
>
> - /* flush cmd work */
> + /* flush cmd work */
> flush_work(&hdev->cmd_work);
>
> + /* flush cmd sync work */
> + hci_cmd_sync_clear(hdev);
> +
> /* Drop queues */
> skb_queue_purge(&hdev->rx_q);
> skb_queue_purge(&hdev->cmd_q);

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland