[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CABBYNZLL0uYpgqSpQ+75mkZ194UTx+ojv-WreBKX52+EyQJ+Hw@mail.gmail.com>
Date: Mon, 1 Jul 2024 16:06:04 -0400
From: Luiz Augusto von Dentz <luiz.dentz@...il.com>
To: Yunseong Kim <yskelg@...il.com>
Cc: Marcel Holtmann <marcel@...tmann.org>, Johan Hedberg <johan.hedberg@...il.com>,
Austin Kim <austindh.kim@...il.com>, Yeoreum Yun <ppbuk5246@...il.com>,
MichelleJin <shjy180909@...il.com>, linux-bluetooth@...r.kernel.org,
linux-kernel@...r.kernel.org, Levi Yun <yeoreum.yun@....com>
Subject: Re: [PATCH] hci: fix double free in hci_req_sync
Hi,
On Mon, Jul 1, 2024 at 3:45 PM Yunseong Kim <yskelg@...il.com> wrote:
>
> The approach taken to address the 'CVE-2024-35978' introduced another
> double-free vulnerability. commit 45d355a926ab
> ("Bluetooth: Fix memory leak in hci_req_sync_complete()")
>
> 'hdev->req_skb' double free scenario:
>
> cpu1 cpu2
> ==== ====
> sock_ioctl
> sock_do_ioctl
> hci_sock_ioctl
> hci_dev_cmd
> hci_req_sync
hci_req_sync is no longer called from hci_dev_cmd:
https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git/commit/?id=6851d11d389ceb00c1220b267b9a04f54dbc4573
And I'm in process of removing hci_request.c completely.
> __hci_req_sync hci_rx_work
> kfree_skb(hdev->req_skb) hci_event_packet
> (sleep) hci_req_sync_complete
> \__ Longer times, kfree_skb(hdev->req_skb)
> reproduce well hdev->req_skb = NULL
>
> The longer cpu1 sleep in '__hci_req_sync', the more reproducible it is.
> We've tested it by inserting the 'msleep()' function, and it's frequently
> at 1000ms, and It has been consistently reproducible at 2000ms.
>
> We confirmed the detection with various workloads that cause CPU1 to sleep.
> The call trace below is one of the KASAN has seen.
>
> Bluetooth: hci0: unexpected cc 0x0c38 length: 249 > 2
> ==================================================================
> BUG: KASAN: slab-use-after-free in skb_release_data+0x7d8/0x8a0 home/paran/linux/net/core/skbuff.c:1119
> Write of size 1 at addr ffff00000947d3fe by task syz-executor.2/2010
>
> CPU: 1 PID: 2010 Comm: syz-executor.2 Not tainted
> 6.10.0-rc4-00217-g35bb670d65fc-dirty #22 Hardware name: linux,dummy-virt (DT)
> Call trace:
> dump_backtrace+0x318/0x348 home/paran/linux/arch/arm64/kernel/stacktrace.c:317
> show_stack+0x4c/0x80 home/paran/linux/arch/arm64/kernel/stacktrace.c:324
> __dump_stack home/paran/linux/lib/dump_stack.c:88 [inline]
> dump_stack_lvl+0x214/0x328 home/paran/linux/lib/dump_stack.c:114
> print_address_description home/paran/linux/mm/kasan/report.c:377 [inline]
> print_report+0x2ac/0x948 home/paran/linux/mm/kasan/report.c:488
> kasan_report+0xc8/0x148 home/paran/linux/mm/kasan/report.c:601
> __asan_report_store1_noabort+0x44/0x60 home/paran/linux/mm/kasan/report_generic.c:383
> skb_release_data+0x7d8/0x8a0 home/paran/linux/net/core/skbuff.c:1119
> skb_release_all+0x80/0xe0 home/paran/linux/net/core/skbuff.c:1173
> __kfree_skb home/paran/linux/net/core/skbuff.c:1187 [inline]
> kfree_skb_reason+0x138/0x3a8 home/paran/linux/net/core/skbuff.c:1223
> __hci_req_sync+0x404/0x948 [bluetooth]
> hci_req_sync+0xc0/0x138 [bluetooth]
> hci_dev_cmd+0x33c/0xc18 [bluetooth]
> hci_sock_ioctl+0x800/0xb68 [bluetooth]
> sock_do_ioctl+0xfc/0x2e0 home/paran/linux/net/socket.c:1222
> sock_ioctl+0x62c/0xab0 home/paran/linux/net/socket.c:1341
> vfs_ioctl+0x90/0x140 home/paran/linux/fs/ioctl.c:51
> __do_sys_ioctl home/paran/linux/fs/ioctl.c:907 [inline]
> __se_sys_ioctl home/paran/linux/fs/ioctl.c:893 [inline]
> __arm64_sys_ioctl+0x218/0x268 home/paran/linux/fs/ioctl.c:893
> __invoke_syscall home/paran/linux/arch/arm64/kernel/syscall.c:34 [inline]
> invoke_syscall+0xdc/0x460 home/paran/linux/arch/arm64/kernel/syscall.c:48
> el0_svc_common.constprop.0+0x2d4/0x3e8 home/paran/linux/arch/arm64/kernel/syscall.c:133
> do_el0_svc+0x60/0x98 home/paran/linux/arch/arm64/kernel/syscall.c:152
> el0_svc+0xc4/0x240 home/paran/linux/arch/arm64/kernel/entry-common.c:712
> el0t_64_sync_handler+0x120/0x130 home/paran/linux/arch/arm64/kernel/entry-common.c:730
> el0t_64_sync+0x190/0x198 home/paran/linux/arch/arm64/kernel/entry.S:598
>
> Allocated by task 577:
> kasan_save_stack+0x48/0x90 home/paran/linux/mm/kasan/common.c:47
> kasan_save_track+0x38/0x60 home/paran/linux/mm/kasan/common.c:68
> kasan_save_alloc_info+0x64/0xc0 home/paran/linux/mm/kasan/generic.c:565
> unpoison_slab_object home/paran/linux/mm/kasan/common.c:312 [inline]
> __kasan_slab_alloc+0x100/0x110 home/paran/linux/mm/kasan/common.c:338
> kasan_slab_alloc home/paran/linux/./include/linux/kasan.h:201 [inline]
> slab_post_alloc_hook home/paran/linux/mm/slub.c:3941 [inline]
> slab_alloc_node home/paran/linux/mm/slub.c:4001 [inline]
> kmem_cache_alloc_noprof+0x2e8/0x630 home/paran/linux/mm/slub.c:4008
> skb_clone+0x1a4/0x4d0 home/paran/linux/net/core/skbuff.c:2052
> hci_cmd_work+0x78c/0x868 [bluetooth]
> process_one_work home/paran/linux/kernel/workqueue.c:3231 [inline]
> process_scheduled_works+0x9fc/0x1d98 home/paran/linux/kernel/workqueue.c:3312
> worker_thread+0x57c/0xf98 home/paran/linux/kernel/workqueue.c:3393
> kthread+0x3c8/0x478 home/paran/linux/kernel/kthread.c:389
> ret_from_fork+0x10/0x20 home/paran/linux/arch/arm64/kernel/entry.S:860
>
> Freed by task 577:
> kasan_save_stack+0x48/0x90 home/paran/linux/mm/kasan/common.c:47
> kasan_save_track+0x38/0x60 home/paran/linux/mm/kasan/common.c:68
> kasan_save_free_info+0x64/0xf0 home/paran/linux/mm/kasan/generic.c:579
> poison_slab_object+0x168/0x270 home/paran/linux/mm/kasan/common.c:240
> __kasan_slab_free+0x34/0xa0 home/paran/linux/mm/kasan/common.c:256
> kasan_slab_free home/paran/linux/./include/linux/kasan.h:184 [inline]
> slab_free_hook home/paran/linux/mm/slub.c:2196 [inline]
> slab_free home/paran/linux/mm/slub.c:4437 [inline]
> kmem_cache_free+0x20c/0x670 home/paran/linux/mm/slub.c:4512
> kfree_skbmem+0x2b0/0x390 home/paran/linux/net/core/skbuff.c:1131
> __kfree_skb home/paran/linux/net/core/skbuff.c:1188 [inline]
> kfree_skb_reason+0x14c/0x3a8 home/paran/linux/net/core/skbuff.c:1223
> hci_req_sync_complete+0x114/0x308 [bluetooth]
> hci_event_packet+0xa10/0x12a0 [bluetooth]
> hci_rx_work+0x4d8/0xa80 [bluetooth]
> process_one_work home/paran/linux/kernel/workqueue.c:3231 [inline]
> process_scheduled_works+0x9fc/0x1d98 home/paran/linux/kernel/workqueue.c:3312
> worker_thread+0x57c/0xf98 home/paran/linux/kernel/workqueue.c:3393
> kthread+0x3c8/0x478 home/paran/linux/kernel/kthread.c:389
> ret_from_fork+0x10/0x20 home/paran/linux/arch/arm64/kernel/entry.S:860
>
> The buggy address belongs to the object at ffff00000947d380
> which belongs to the cache skbuff_head_cache of size 232
> The buggy address is located 126 bytes inside of
> freed 232-byte region [ffff00000947d380, ffff00000947d468)
>
> The buggy address belongs to the physical page:
> page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4947c
> head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
> memcg:ffff0000222a73b1
> flags: 0x3fffe0000000040(head|node=0|zone=0|lastcpupid=0x1ffff)
> page_type: 0xffffefff(slab)
> raw: 03fffe0000000040 ffff0000133a41c0 fffffdffc0e3a090 fffffdffc1069290
> raw: 0000000000000000 0000000000120012 00000001ffffefff ffff0000222a73b1
> head: 03fffe0000000040 ffff0000133a41c0 fffffdffc0e3a090 fffffdffc1069290
> head: 0000000000000000 0000000000120012 00000001ffffefff ffff0000222a73b1
> head: 03fffe0000000001 fffffdffc0251f01 ffffffffffffffff 0000000000000000
> head: 0000000000000002 0000000000000000 00000000ffffffff 0000000000000000
> page dumped because: kasan: bad access detected
>
> Memory state around the buggy address:
> ffff00000947d280: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc
> ffff00000947d300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> >ffff00000947d380: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ^
> ffff00000947d400: fb fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc
> ffff00000947d480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> ==================================================================
>
> We considered using the "hci_req_sync_lock" mutex, but concluded it
> wasn't a good solution due to the increased sleep intervals causing
> this issue. Instead, we introduced a spinlock member on 'struct hci_dev'.
>
> Since applying our patch, we have repeatedly run the same tests in
> the syzkaller without encountering any issues.
>
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=89a32741f4217856066c198a4a7267bcdd1edd67
> Fixes: 45d355a926ab ("Bluetooth: Fix memory leak in hci_req_sync_complete()")
> Signed-off-by: Levi Yun <yeoreum.yun@....com>
> Signed-off-by: Yunseong Kim <yskelg@...il.com>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> net/bluetooth/hci_core.c | 1 +
> net/bluetooth/hci_request.c | 6 ++----
> net/bluetooth/hci_request.h | 9 +++++++++
> net/bluetooth/hci_sync.c | 13 +++----------
> 5 files changed, 16 insertions(+), 14 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index c43716edf205..8b95061f063b 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -519,6 +519,7 @@ struct hci_dev {
> struct sk_buff *recv_event;
>
> struct mutex req_lock;
> + spinlock_t req_skb_lock;
> wait_queue_head_t req_wait_q;
> __u32 req_status;
> __u32 req_result;
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index dd3b0f501018..138a6b19894d 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2572,6 +2572,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
>
> mutex_init(&hdev->lock);
> mutex_init(&hdev->req_lock);
> + spin_lock_init(&hdev->req_skb_lock);
>
> ida_init(&hdev->unset_handle_ida);
>
> diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
> index efea25eb56ce..6a109c1ad359 100644
> --- a/net/bluetooth/hci_request.c
> +++ b/net/bluetooth/hci_request.c
> @@ -106,8 +106,7 @@ void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
> hdev->req_result = result;
> hdev->req_status = HCI_REQ_DONE;
> if (skb) {
> - kfree_skb(hdev->req_skb);
> - hdev->req_skb = skb_get(skb);
> + hci_req_skb_release_and_set(hdev, skb_get(skb));
> }
> wake_up_interruptible(&hdev->req_wait_q);
> }
> @@ -181,8 +180,7 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
> break;
> }
>
> - kfree_skb(hdev->req_skb);
> - hdev->req_skb = NULL;
> + hci_req_skb_release_and_set(hdev, NULL);
> hdev->req_status = hdev->req_result = 0;
>
> bt_dev_dbg(hdev, "end: err %d", err);
> diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
> index c91f2838f542..6526c78443bc 100644
> --- a/net/bluetooth/hci_request.h
> +++ b/net/bluetooth/hci_request.h
> @@ -28,6 +28,15 @@
>
> #define hci_req_sync_lock(hdev) mutex_lock(&hdev->req_lock)
> #define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
> +#define hci_req_skb_release_and_set(hdev, val) \
> +({ \
> + if (hdev->req_skb) { \
> + spin_lock(&hdev->req_skb_lock); \
> + kfree_skb(hdev->req_skb); \
> + hdev->req_skb = val; \
> + spin_unlock(&hdev->req_skb_lock); \
> + } \
> +})
>
> struct hci_request {
> struct hci_dev *hdev;
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index a8a7d2b36870..25c8d858c82e 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -33,8 +33,7 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
> hdev->req_status = HCI_REQ_DONE;
>
> /* Free the request command so it is not used as response */
> - kfree_skb(hdev->req_skb);
> - hdev->req_skb = NULL;
This doesn't even apply upstream
> + hci_req_skb_release_and_set(hdev, NULL);
>
> if (skb) {
> struct sock *sk = hci_skb_sk(skb);
> @@ -4935,10 +4934,7 @@ int hci_dev_open_sync(struct hci_dev *hdev)
> hdev->sent_cmd = NULL;
> }
>
> - if (hdev->req_skb) {
> - kfree_skb(hdev->req_skb);
> - hdev->req_skb = NULL;
> - }
> + hci_req_skb_release_and_set(hdev, NULL);
>
> clear_bit(HCI_RUNNING, &hdev->flags);
> hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
> @@ -5100,10 +5096,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
> }
>
> /* Drop last request */
> - if (hdev->req_skb) {
> - kfree_skb(hdev->req_skb);
> - hdev->req_skb = NULL;
> - }
> + hci_req_skb_release_and_set(hdev, NULL);
>
> clear_bit(HCI_RUNNING, &hdev->flags);
> hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
> --
> 2.45.2
>
--
Luiz Augusto von Dentz
Powered by blists - more mailing lists