* [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() @ 2026-05-13 6:45 w15303746062 2026-05-13 9:04 ` Paul Menzel 2026-05-15 12:37 ` [PATCH] " Luiz Augusto von Dentz 0 siblings, 2 replies; 13+ messages in thread From: w15303746062 @ 2026-05-13 6:45 UTC (permalink / raw) To: marcel, luiz.dentz Cc: linux-bluetooth, linux-serial, linux-kernel, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> A Use-After-Free (UAF) vulnerability and a subsequent General Protection Fault (GPF) were observed in h5_recv() due to a race condition between the initialization of the HCI UART line discipline and concurrent TTY hangup via TIOCVHANGUP. The issue arises because the workqueues (init_ready and write_work) are only cancelled if the HCI_UART_PROTO_READY flag is set. However, during the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying protocol (e.g., H5) may schedule work (such as sending sync/config packets). If a hangup occurs before the setup completes and the READY flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls and proceeds to free the `hu` struct. When the delayed workqueue finally executes, it blindly dereferences the freed `hu` struct, causing ODEBUG warnings and kernel panics. Fix this by moving the cancel_work_sync() calls outside the HCI_UART_PROTO_READY check, ensuring that any pending works are unconditionally cancelled before the hci_uart structure is freed. Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- drivers/bluetooth/hci_ldisc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..566e1c525ee2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hdev) hci_uart_close(hdev); + /* + * Always cancel workqueues unconditionally before freeing the hu + * struct, as they might be active during the PROTO_INIT phase. + */ + cancel_work_sync(&hu->init_ready); + cancel_work_sync(&hu->write_work); + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-13 6:45 [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() w15303746062 @ 2026-05-13 9:04 ` Paul Menzel 2026-05-14 15:17 ` [PATCH v2] " w15303746062 2026-05-15 6:50 ` [PATCH v3] " w15303746062 2026-05-15 12:37 ` [PATCH] " Luiz Augusto von Dentz 1 sibling, 2 replies; 13+ messages in thread From: Paul Menzel @ 2026-05-13 9:04 UTC (permalink / raw) To: Mingyu Wang, Mingyu Wang Cc: marcel, luiz.dentz, linux-bluetooth, linux-serial, linux-kernel Dear Mingyu, Thank you for the patch, and your work on the Linux kernel. Am 13.05.26 um 08:45 schrieb w15303746062@163.com: > From: Mingyu Wang <25181214217@stu.xidian.edu.cn> > > A Use-After-Free (UAF) vulnerability and a subsequent General Protection > Fault (GPF) were observed in h5_recv() due to a race condition between > the initialization of the HCI UART line discipline and concurrent TTY > hangup via TIOCVHANGUP. Please elaborate, in what setup it was observed, and please add an excerpt of the trace. > The issue arises because the workqueues (init_ready and write_work) are > only cancelled if the HCI_UART_PROTO_READY flag is set. However, during > the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying > protocol (e.g., H5) may schedule work (such as sending sync/config > packets). If a hangup occurs before the setup completes and the READY > flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls > and proceeds to free the `hu` struct. > > When the delayed workqueue finally executes, it blindly dereferences > the freed `hu` struct, causing ODEBUG warnings and kernel panics. > > Fix this by moving the cancel_work_sync() calls outside the > HCI_UART_PROTO_READY check, ensuring that any pending works are > unconditionally cancelled before the hci_uart structure is freed. Please add a Fixes: tag, so it gets backported. Also, please add a Link: tag with a URL to the test case, or include it in the commit message. > Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> > --- > drivers/bluetooth/hci_ldisc.c | 10 +++++++--- > 1 file changed, 7 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 275ea865bc29..566e1c525ee2 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) > if (hdev) > hci_uart_close(hdev); > > + /* > + * Always cancel workqueues unconditionally before freeing the hu > + * struct, as they might be active during the PROTO_INIT phase. > + */ > + cancel_work_sync(&hu->init_ready); > + cancel_work_sync(&hu->write_work); > + > if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { > percpu_down_write(&hu->proto_lock); > clear_bit(HCI_UART_PROTO_READY, &hu->flags); > percpu_up_write(&hu->proto_lock); > > - cancel_work_sync(&hu->init_ready); > - cancel_work_sync(&hu->write_work); > - > if (hdev) { > if (test_bit(HCI_UART_REGISTERED, &hu->flags)) > hci_unregister_dev(hdev); Kind regards, Paul PS: If you resend, and don’t know yet (you have commits in the Linux kernel already), please add v2 to the tag. (`git format-patch -2 …` or an equivalent option to your tooling. PPS: sashiko.dev did not pick this patch up yet [1]. [1]: https://sashiko.dev/#/?list=org.kernel.vger.linux-bluetooth ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-13 9:04 ` Paul Menzel @ 2026-05-14 15:17 ` w15303746062 2026-05-15 6:10 ` Greg KH 2026-05-15 6:50 ` [PATCH v3] " w15303746062 1 sibling, 1 reply; 13+ messages in thread From: w15303746062 @ 2026-05-14 15:17 UTC (permalink / raw) To: pmenzel, marcel, luiz.dentz, linux-bluetooth Cc: linux-serial, linux-kernel, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were observed in hci_uart_write_work() due to a race condition between the initialization of the HCI UART line discipline and concurrent TTY hangup. This issue was triggered by our custom device emulation and fuzzing framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent nature of this race condition (requiring a precise interleaving of TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable standalone C reproducer (reproducer is too unreliable: 0.00). The crash trace is as follows: ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 ... Call Trace: <TASK> debug_check_no_obj_freed+0x3ec/0x520 kfree+0x3f0/0x6c0 hci_uart_tty_close+0x127/0x2a0 tty_ldisc_close+0x113/0x1a0 tty_ldisc_kill+0x8e/0x150 tty_ldisc_hangup+0x3c1/0x730 __tty_hangup.part.0+0x3fd/0x8a0 tty_ioctl+0x120f/0x1690 __x64_sys_ioctl+0x18f/0x210 do_syscall_64+0xcb/0xfa0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> The issue arises because the workqueues (init_ready and write_work) are only cancelled if the HCI_UART_PROTO_READY flag is set. However, during the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying protocol may schedule work. If a hangup occurs before the setup completes and the READY flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls and proceeds to free the `hu` struct. When the delayed workqueue executes, it blindly dereferences the freed `hu` struct. Fix this by moving the cancel_work_sync() calls outside the HCI_UART_PROTO_READY check, ensuring that any pending works are unconditionally cancelled before the hci_uart structure is freed. Note that hu->init_ready and hu->write_work are initialized in hci_uart_tty_open(), so it is always safe to call cancel_work_sync() on them in hci_uart_tty_close(), even if the protocol was never fully attached. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v2: - Added KASAN/ODEBUG crash trace. - Added explanation for the absence of a standalone reproducer (highly timing-dependent race condition). - Added Fixes tag pointing to commit 3b799254cf6f. drivers/bluetooth/hci_ldisc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..566e1c525ee2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hdev) hci_uart_close(hdev); + /* + * Always cancel workqueues unconditionally before freeing the hu + * struct, as they might be active during the PROTO_INIT phase. + */ + cancel_work_sync(&hu->init_ready); + cancel_work_sync(&hu->write_work); + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-14 15:17 ` [PATCH v2] " w15303746062 @ 2026-05-15 6:10 ` Greg KH 0 siblings, 0 replies; 13+ messages in thread From: Greg KH @ 2026-05-15 6:10 UTC (permalink / raw) To: w15303746062 Cc: pmenzel, marcel, luiz.dentz, linux-bluetooth, linux-serial, linux-kernel, Mingyu Wang On Thu, May 14, 2026 at 11:17:22PM +0800, w15303746062@163.com wrote: > From: Mingyu Wang <25181214217@stu.xidian.edu.cn> > > A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were > observed in hci_uart_write_work() due to a race condition between the > initialization of the HCI UART line discipline and concurrent TTY hangup. > > This issue was triggered by our custom device emulation and fuzzing > framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent > nature of this race condition (requiring a precise interleaving of > TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable > standalone C reproducer (reproducer is too unreliable: 0.00). > > The crash trace is as follows: > ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 > WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 > ... > Call Trace: > <TASK> > debug_check_no_obj_freed+0x3ec/0x520 > kfree+0x3f0/0x6c0 > hci_uart_tty_close+0x127/0x2a0 > tty_ldisc_close+0x113/0x1a0 > tty_ldisc_kill+0x8e/0x150 > tty_ldisc_hangup+0x3c1/0x730 > __tty_hangup.part.0+0x3fd/0x8a0 > tty_ioctl+0x120f/0x1690 > __x64_sys_ioctl+0x18f/0x210 > do_syscall_64+0xcb/0xfa0 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > </TASK> > > The issue arises because the workqueues (init_ready and write_work) are > only cancelled if the HCI_UART_PROTO_READY flag is set. However, during > the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying > protocol may schedule work. If a hangup occurs before the setup completes > and the READY flag is set, hci_uart_tty_close() skips the cancel_work_sync() > calls and proceeds to free the `hu` struct. When the delayed workqueue > executes, it blindly dereferences the freed `hu` struct. > > Fix this by moving the cancel_work_sync() calls outside the > HCI_UART_PROTO_READY check, ensuring that any pending works are > unconditionally cancelled before the hci_uart structure is freed. > Note that hu->init_ready and hu->write_work are initialized in > hci_uart_tty_open(), so it is always safe to call cancel_work_sync() > on them in hci_uart_tty_close(), even if the protocol was never > fully attached. > > Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") > > Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> > --- > Changes in v2: > - Added KASAN/ODEBUG crash trace. > - Added explanation for the absence of a standalone reproducer (highly timing-dependent race condition). > - Added Fixes tag pointing to commit 3b799254cf6f. > > drivers/bluetooth/hci_ldisc.c | 10 +++++++--- > 1 file changed, 7 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 275ea865bc29..566e1c525ee2 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) > if (hdev) > hci_uart_close(hdev); > > + /* > + * Always cancel workqueues unconditionally before freeing the hu > + * struct, as they might be active during the PROTO_INIT phase. > + */ > + cancel_work_sync(&hu->init_ready); > + cancel_work_sync(&hu->write_work); > + > if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { > percpu_down_write(&hu->proto_lock); > clear_bit(HCI_UART_PROTO_READY, &hu->flags); > percpu_up_write(&hu->proto_lock); > > - cancel_work_sync(&hu->init_ready); > - cancel_work_sync(&hu->write_work); > - > if (hdev) { > if (test_bit(HCI_UART_REGISTERED, &hu->flags)) > hci_unregister_dev(hdev); > -- > 2.34.1 > > Hi, This is the friendly patch-bot of Greg Kroah-Hartman. You have sent him a patch that has triggered this response. He used to manually respond to these common problems, but in order to save his sanity (he kept writing the same thing over and over, yet to different people), I was created. Hopefully you will not take offence and will fix the problem in your patch and resubmit it so that it can be accepted into the Linux kernel tree. You are receiving this message because of the following common error(s) as indicated below: - You have marked a patch with a "Fixes:" tag for a commit that is in an older released kernel, yet you do not have a cc: stable line in the signed-off-by area at all, which means that the patch will not be applied to any older kernel releases. To properly fix this, please follow the documented rules in the Documentation/process/stable-kernel-rules.rst file for how to resolve this. If you wish to discuss this problem further, or you have questions about how to resolve this issue, please feel free to respond to this email and Greg will reply once he has dug out from the pending patches received from other developers. thanks, greg k-h's patch email bot ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-13 9:04 ` Paul Menzel 2026-05-14 15:17 ` [PATCH v2] " w15303746062 @ 2026-05-15 6:50 ` w15303746062 1 sibling, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-15 6:50 UTC (permalink / raw) To: pmenzel, marcel, luiz.dentz, linux-bluetooth Cc: linux-serial, linux-kernel, greg, stable, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were observed in hci_uart_write_work() due to a race condition between the initialization of the HCI UART line discipline and concurrent TTY hangup. This issue was triggered by our custom device emulation and fuzzing framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent nature of this race condition (requiring a precise interleaving of TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable standalone C reproducer (reproducer is too unreliable: 0.00). The crash trace is as follows: ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 ... Call Trace: <TASK> debug_check_no_obj_freed+0x3ec/0x520 kfree+0x3f0/0x6c0 hci_uart_tty_close+0x127/0x2a0 tty_ldisc_close+0x113/0x1a0 tty_ldisc_kill+0x8e/0x150 tty_ldisc_hangup+0x3c1/0x730 __tty_hangup.part.0+0x3fd/0x8a0 tty_ioctl+0x120f/0x1690 __x64_sys_ioctl+0x18f/0x210 do_syscall_64+0xcb/0xfa0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> The issue arises because the workqueues (init_ready and write_work) are only cancelled if the HCI_UART_PROTO_READY flag is set. However, during the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying protocol may schedule work. If a hangup occurs before the setup completes and the READY flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls and proceeds to free the `hu` struct. When the delayed workqueue executes, it blindly dereferences the freed `hu` struct. Fix this by moving the cancel_work_sync() calls outside the HCI_UART_PROTO_READY check, ensuring that any pending works are unconditionally cancelled before the hci_uart structure is freed. Note that hu->init_ready and hu->write_work are initialized in hci_uart_tty_open(), so it is always safe to call cancel_work_sync() on them in hci_uart_tty_close(), even if the protocol was never fully attached. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v3: - Added 'Cc: stable' tag as requested by the stable bot. Changes in v2: - Added KASAN/ODEBUG crash trace. drivers/bluetooth/hci_ldisc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..566e1c525ee2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hdev) hci_uart_close(hdev); + /* + * Always cancel workqueues unconditionally before freeing the hu + * struct, as they might be active during the PROTO_INIT phase. + */ + cancel_work_sync(&hu->init_ready); + cancel_work_sync(&hu->write_work); + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-13 6:45 [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() w15303746062 2026-05-13 9:04 ` Paul Menzel @ 2026-05-15 12:37 ` Luiz Augusto von Dentz 2026-05-15 13:39 ` w15303746062 2026-05-15 14:05 ` [PATCH v4] " w15303746062 1 sibling, 2 replies; 13+ messages in thread From: Luiz Augusto von Dentz @ 2026-05-15 12:37 UTC (permalink / raw) To: w15303746062 Cc: marcel, linux-bluetooth, linux-serial, linux-kernel, Mingyu Wang Hi, On Wed, May 13, 2026 at 2:46 AM <w15303746062@163.com> wrote: > > From: Mingyu Wang <25181214217@stu.xidian.edu.cn> > > A Use-After-Free (UAF) vulnerability and a subsequent General Protection > Fault (GPF) were observed in h5_recv() due to a race condition between > the initialization of the HCI UART line discipline and concurrent TTY > hangup via TIOCVHANGUP. > > The issue arises because the workqueues (init_ready and write_work) are > only cancelled if the HCI_UART_PROTO_READY flag is set. However, during > the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying > protocol (e.g., H5) may schedule work (such as sending sync/config > packets). If a hangup occurs before the setup completes and the READY > flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls > and proceeds to free the `hu` struct. > > When the delayed workqueue finally executes, it blindly dereferences > the freed `hu` struct, causing ODEBUG warnings and kernel panics. > > Fix this by moving the cancel_work_sync() calls outside the > HCI_UART_PROTO_READY check, ensuring that any pending works are > unconditionally cancelled before the hci_uart structure is freed. > > Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> > --- > drivers/bluetooth/hci_ldisc.c | 10 +++++++--- > 1 file changed, 7 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 275ea865bc29..566e1c525ee2 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) > if (hdev) > hci_uart_close(hdev); > > + /* > + * Always cancel workqueues unconditionally before freeing the hu > + * struct, as they might be active during the PROTO_INIT phase. > + */ > + cancel_work_sync(&hu->init_ready); > + cancel_work_sync(&hu->write_work); Can't we use disable_work_sync? If it frees up at the end, it's probably best to disable it so it doesn't allow new submissions. > if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { > percpu_down_write(&hu->proto_lock); > clear_bit(HCI_UART_PROTO_READY, &hu->flags); > percpu_up_write(&hu->proto_lock); > > - cancel_work_sync(&hu->init_ready); > - cancel_work_sync(&hu->write_work); > - > if (hdev) { > if (test_bit(HCI_UART_REGISTERED, &hu->flags)) > hci_unregister_dev(hdev); > -- > 2.34.1 > > -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re:Re: [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-15 12:37 ` [PATCH] " Luiz Augusto von Dentz @ 2026-05-15 13:39 ` w15303746062 2026-05-15 14:05 ` [PATCH v4] " w15303746062 1 sibling, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-15 13:39 UTC (permalink / raw) To: Luiz Augusto von Dentz Cc: marcel, linux-bluetooth, linux-serial, linux-kernel, Mingyu Wang Hi Luiz, Thank you for the review. That is an excellent suggestion. You are absolutely right. Since the `hu` structure is being torn down and freed immediately afterward, using `disable_work_sync()` provides a much stronger guarantee by preventing any concurrent threads from re-queuing the works, thus eliminating the risk of a lingering UAF. Both `init_ready` and `write_work` are standard `struct work_struct`, so `disable_work_sync()` applies perfectly here. I will send out a v4 patch shortly adopting this change. Thank you for pointing this out! Best regards, Mingyu At 2026-05-15 20:37:57, "Luiz Augusto von Dentz" <luiz.dentz@gmail.com> wrote: >Hi, > >On Wed, May 13, 2026 at 2:46 AM <w15303746062@163.com> wrote: >> >> From: Mingyu Wang <25181214217@stu.xidian.edu.cn> >> >> A Use-After-Free (UAF) vulnerability and a subsequent General Protection >> Fault (GPF) were observed in h5_recv() due to a race condition between >> the initialization of the HCI UART line discipline and concurrent TTY >> hangup via TIOCVHANGUP. >> >> The issue arises because the workqueues (init_ready and write_work) are >> only cancelled if the HCI_UART_PROTO_READY flag is set. However, during >> the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying >> protocol (e.g., H5) may schedule work (such as sending sync/config >> packets). If a hangup occurs before the setup completes and the READY >> flag is set, hci_uart_tty_close() skips the cancel_work_sync() calls >> and proceeds to free the `hu` struct. >> >> When the delayed workqueue finally executes, it blindly dereferences >> the freed `hu` struct, causing ODEBUG warnings and kernel panics. >> >> Fix this by moving the cancel_work_sync() calls outside the >> HCI_UART_PROTO_READY check, ensuring that any pending works are >> unconditionally cancelled before the hci_uart structure is freed. >> >> Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> >> --- >> drivers/bluetooth/hci_ldisc.c | 10 +++++++--- >> 1 file changed, 7 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c >> index 275ea865bc29..566e1c525ee2 100644 >> --- a/drivers/bluetooth/hci_ldisc.c >> +++ b/drivers/bluetooth/hci_ldisc.c >> @@ -544,14 +544,18 @@ static void hci_uart_tty_close(struct tty_struct *tty) >> if (hdev) >> hci_uart_close(hdev); >> >> + /* >> + * Always cancel workqueues unconditionally before freeing the hu >> + * struct, as they might be active during the PROTO_INIT phase. >> + */ >> + cancel_work_sync(&hu->init_ready); >> + cancel_work_sync(&hu->write_work); > >Can't we use disable_work_sync? If it frees up at the end, it's >probably best to disable it so it doesn't allow new submissions. > >> if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { >> percpu_down_write(&hu->proto_lock); >> clear_bit(HCI_UART_PROTO_READY, &hu->flags); >> percpu_up_write(&hu->proto_lock); >> >> - cancel_work_sync(&hu->init_ready); >> - cancel_work_sync(&hu->write_work); >> - >> if (hdev) { >> if (test_bit(HCI_UART_REGISTERED, &hu->flags)) >> hci_unregister_dev(hdev); >> -- >> 2.34.1 >> >> > > >-- >Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-15 12:37 ` [PATCH] " Luiz Augusto von Dentz 2026-05-15 13:39 ` w15303746062 @ 2026-05-15 14:05 ` w15303746062 2026-05-15 16:08 ` Luiz Augusto von Dentz 1 sibling, 1 reply; 13+ messages in thread From: w15303746062 @ 2026-05-15 14:05 UTC (permalink / raw) To: luiz.dentz, pmenzel, marcel, linux-bluetooth Cc: linux-serial, linux-kernel, greg, stable, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were observed in hci_uart_write_work() due to a race condition between the initialization of the HCI UART line discipline and concurrent TTY hangup. This issue was triggered by our custom device emulation and fuzzing framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent nature of this race condition (requiring a precise interleaving of TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable standalone C reproducer (reproducer is too unreliable: 0.00). The crash trace is as follows: ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 ... Call Trace: <TASK> debug_check_no_obj_freed+0x3ec/0x520 kfree+0x3f0/0x6c0 hci_uart_tty_close+0x127/0x2a0 tty_ldisc_close+0x113/0x1a0 tty_ldisc_kill+0x8e/0x150 tty_ldisc_hangup+0x3c1/0x730 __tty_hangup.part.0+0x3fd/0x8a0 tty_ioctl+0x120f/0x1690 __x64_sys_ioctl+0x18f/0x210 do_syscall_64+0xcb/0xfa0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> The issue arises because the workqueues (init_ready and write_work) are only flushed/cancelled if the HCI_UART_PROTO_READY flag is set. However, during the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying protocol may schedule work. If a hangup occurs before the setup completes and the READY flag is set, hci_uart_tty_close() skips the teardown of these workqueues and proceeds to free the `hu` struct. When the scheduled work executes later, it blindly dereferences the freed `hu` struct. Fix this by moving the workqueue teardown outside the HCI_UART_PROTO_READY check. Furthermore, use disable_work_sync() instead of cancel_work_sync() to unconditionally disable the works. This ensures that any pending works are cancelled and no new submissions can occur before the hci_uart structure is freed. Note that hu->init_ready and hu->write_work are initialized in hci_uart_tty_open(), so it is always safe to call disable_work_sync() on them in hci_uart_tty_close(), even if the protocol was never fully attached. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v4: - Adopted Luiz's suggestion to use disable_work_sync() instead of cancel_work_sync() to prevent new work submissions during teardown. Changes in v3: - Added 'Cc: stable' tag as requested by the stable bot. Changes in v2: - Added KASAN/ODEBUG crash trace. drivers/bluetooth/hci_ldisc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..333c1e1503e8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -544,14 +544,20 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hdev) hci_uart_close(hdev); + /* + * Disable workqueues unconditionally before freeing the hu + * struct, as they might be active during the PROTO_INIT phase. + * Using disable_work_sync() instead of cancel_work_sync() + * ensures no new submissions can occur. + */ + disable_work_sync(&hu->init_ready); + disable_work_sync(&hu->write_work); + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v4] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-15 14:05 ` [PATCH v4] " w15303746062 @ 2026-05-15 16:08 ` Luiz Augusto von Dentz 2026-05-16 1:41 ` w15303746062 ` (3 more replies) 0 siblings, 4 replies; 13+ messages in thread From: Luiz Augusto von Dentz @ 2026-05-15 16:08 UTC (permalink / raw) To: w15303746062 Cc: pmenzel, marcel, linux-bluetooth, linux-serial, linux-kernel, greg, stable, Mingyu Wang Hi, On Fri, May 15, 2026 at 10:06 AM <w15303746062@163.com> wrote: > > From: Mingyu Wang <25181214217@stu.xidian.edu.cn> > > A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were > observed in hci_uart_write_work() due to a race condition between the > initialization of the HCI UART line discipline and concurrent TTY hangup. > > This issue was triggered by our custom device emulation and fuzzing > framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent > nature of this race condition (requiring a precise interleaving of > TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable > standalone C reproducer (reproducer is too unreliable: 0.00). > > The crash trace is as follows: > ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 > WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 > ... > Call Trace: > <TASK> > debug_check_no_obj_freed+0x3ec/0x520 > kfree+0x3f0/0x6c0 > hci_uart_tty_close+0x127/0x2a0 > tty_ldisc_close+0x113/0x1a0 > tty_ldisc_kill+0x8e/0x150 > tty_ldisc_hangup+0x3c1/0x730 > __tty_hangup.part.0+0x3fd/0x8a0 > tty_ioctl+0x120f/0x1690 > __x64_sys_ioctl+0x18f/0x210 > do_syscall_64+0xcb/0xfa0 > entry_SYSCALL_64_after_hwframe+0x77/0x7f > </TASK> > > The issue arises because the workqueues (init_ready and write_work) are > only flushed/cancelled if the HCI_UART_PROTO_READY flag is set. However, > during the protocol initialization phase (HCI_UART_PROTO_INIT), the > underlying protocol may schedule work. If a hangup occurs before the setup > completes and the READY flag is set, hci_uart_tty_close() skips the > teardown of these workqueues and proceeds to free the `hu` struct. When > the scheduled work executes later, it blindly dereferences the freed `hu` > struct. > > Fix this by moving the workqueue teardown outside the HCI_UART_PROTO_READY > check. Furthermore, use disable_work_sync() instead of cancel_work_sync() > to unconditionally disable the works. This ensures that any pending works > are cancelled and no new submissions can occur before the hci_uart > structure is freed. Note that hu->init_ready and hu->write_work are > initialized in hci_uart_tty_open(), so it is always safe to call > disable_work_sync() on them in hci_uart_tty_close(), even if the protocol > was never fully attached. > > Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") > Cc: stable@vger.kernel.org > Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> > --- > Changes in v4: > - Adopted Luiz's suggestion to use disable_work_sync() instead of > cancel_work_sync() to prevent new work submissions during teardown. > > Changes in v3: > - Added 'Cc: stable' tag as requested by the stable bot. > > Changes in v2: > - Added KASAN/ODEBUG crash trace. > > drivers/bluetooth/hci_ldisc.c | 12 +++++++++--- > 1 file changed, 9 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c > index 275ea865bc29..333c1e1503e8 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -544,14 +544,20 @@ static void hci_uart_tty_close(struct tty_struct *tty) > if (hdev) > hci_uart_close(hdev); > > + /* > + * Disable workqueues unconditionally before freeing the hu > + * struct, as they might be active during the PROTO_INIT phase. > + * Using disable_work_sync() instead of cancel_work_sync() > + * ensures no new submissions can occur. > + */ > + disable_work_sync(&hu->init_ready); > + disable_work_sync(&hu->write_work); Looks like sashiko has a problem with these being after hci_uart_close: https://sashiko.dev/#/patchset/20260515140548.393865-1-w15303746062%40163.com > if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { > percpu_down_write(&hu->proto_lock); > clear_bit(HCI_UART_PROTO_READY, &hu->flags); > percpu_up_write(&hu->proto_lock); > > - cancel_work_sync(&hu->init_ready); > - cancel_work_sync(&hu->write_work); > - > if (hdev) { > if (test_bit(HCI_UART_REGISTERED, &hu->flags)) > hci_unregister_dev(hdev); > -- > 2.34.1 > -- Luiz Augusto von Dentz ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re:Re: [PATCH v4] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-15 16:08 ` Luiz Augusto von Dentz @ 2026-05-16 1:41 ` w15303746062 2026-05-16 2:22 ` [PATCH v5] " w15303746062 ` (2 subsequent siblings) 3 siblings, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-16 1:41 UTC (permalink / raw) To: Luiz Augusto von Dentz Cc: pmenzel, marcel, linux-bluetooth, linux-serial, linux-kernel, greg, stable, Mingyu Wang Hi Luiz, Thank you for checking it with Sashiko. At 2026-05-16 00:08:05, "Luiz Augusto von Dentz" <luiz.dentz@gmail.com> wrote: >Hi, > >On Fri, May 15, 2026 at 10:06 AM <w15303746062@163.com> wrote: >> >> From: Mingyu Wang <25181214217@stu.xidian.edu.cn> >> >> A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were >> observed in hci_uart_write_work() due to a race condition between the >> initialization of the HCI UART line discipline and concurrent TTY hangup. >> >> This issue was triggered by our custom device emulation and fuzzing >> framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent >> nature of this race condition (requiring a precise interleaving of >> TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable >> standalone C reproducer (reproducer is too unreliable: 0.00). >> >> The crash trace is as follows: >> ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 >> WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 >> ... >> Call Trace: >> <TASK> >> debug_check_no_obj_freed+0x3ec/0x520 >> kfree+0x3f0/0x6c0 >> hci_uart_tty_close+0x127/0x2a0 >> tty_ldisc_close+0x113/0x1a0 >> tty_ldisc_kill+0x8e/0x150 >> tty_ldisc_hangup+0x3c1/0x730 >> __tty_hangup.part.0+0x3fd/0x8a0 >> tty_ioctl+0x120f/0x1690 >> __x64_sys_ioctl+0x18f/0x210 >> do_syscall_64+0xcb/0xfa0 >> entry_SYSCALL_64_after_hwframe+0x77/0x7f >> </TASK> >> >> The issue arises because the workqueues (init_ready and write_work) are >> only flushed/cancelled if the HCI_UART_PROTO_READY flag is set. However, >> during the protocol initialization phase (HCI_UART_PROTO_INIT), the >> underlying protocol may schedule work. If a hangup occurs before the setup >> completes and the READY flag is set, hci_uart_tty_close() skips the >> teardown of these workqueues and proceeds to free the `hu` struct. When >> the scheduled work executes later, it blindly dereferences the freed `hu` >> struct. >> >> Fix this by moving the workqueue teardown outside the HCI_UART_PROTO_READY >> check. Furthermore, use disable_work_sync() instead of cancel_work_sync() >> to unconditionally disable the works. This ensures that any pending works >> are cancelled and no new submissions can occur before the hci_uart >> structure is freed. Note that hu->init_ready and hu->write_work are >> initialized in hci_uart_tty_open(), so it is always safe to call >> disable_work_sync() on them in hci_uart_tty_close(), even if the protocol >> was never fully attached. >> >> Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") >> Cc: stable@vger.kernel.org >> Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> >> --- >> Changes in v4: >> - Adopted Luiz's suggestion to use disable_work_sync() instead of >> cancel_work_sync() to prevent new work submissions during teardown. >> >> Changes in v3: >> - Added 'Cc: stable' tag as requested by the stable bot. >> >> Changes in v2: >> - Added KASAN/ODEBUG crash trace. >> >> drivers/bluetooth/hci_ldisc.c | 12 +++++++++--- >> 1 file changed, 9 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c >> index 275ea865bc29..333c1e1503e8 100644 >> --- a/drivers/bluetooth/hci_ldisc.c >> +++ b/drivers/bluetooth/hci_ldisc.c >> @@ -544,14 +544,20 @@ static void hci_uart_tty_close(struct tty_struct *tty) >> if (hdev) >> hci_uart_close(hdev); >> >> + /* >> + * Disable workqueues unconditionally before freeing the hu >> + * struct, as they might be active during the PROTO_INIT phase. >> + * Using disable_work_sync() instead of cancel_work_sync() >> + * ensures no new submissions can occur. >> + */ >> + disable_work_sync(&hu->init_ready); >> + disable_work_sync(&hu->write_work); > >Looks like sashiko has a problem with these being after hci_uart_close: I see the issue now. Placing `disable_work_sync()` after `hci_uart_close()` could still leave a tiny window where the workqueues might race with the teardown of the `hdev` structure. The safest and most logical approach is to pull the `disable_work_sync()` calls to the very top of `hci_uart_tty_close()`, before `hci_uart_close()` or any other teardown logic begins. This will completely choke off any asynchronous operations before we touch the connection state or hardware. I will update the patch and send out v5 immediately. > >https://sashiko.dev/#/patchset/20260515140548.393865-1-w15303746062%40163.com > >> if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { >> percpu_down_write(&hu->proto_lock); >> clear_bit(HCI_UART_PROTO_READY, &hu->flags); >> percpu_up_write(&hu->proto_lock); >> >> - cancel_work_sync(&hu->init_ready); >> - cancel_work_sync(&hu->write_work); >> - >> if (hdev) { >> if (test_bit(HCI_UART_REGISTERED, &hu->flags)) >> hci_unregister_dev(hdev); >> -- >> 2.34.1 >> > > >-- >Luiz Augusto von Dentz Best regards, Mingyu ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() 2026-05-15 16:08 ` Luiz Augusto von Dentz 2026-05-16 1:41 ` w15303746062 @ 2026-05-16 2:22 ` w15303746062 2026-05-16 5:30 ` [PATCH v6] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths w15303746062 2026-05-16 8:47 ` [PATCH v7] " w15303746062 3 siblings, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-16 2:22 UTC (permalink / raw) To: luiz.dentz, pmenzel, marcel, linux-bluetooth Cc: linux-serial, linux-kernel, greg, stable, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> A Use-After-Free (UAF) vulnerability and a subsequent kernel panic were observed in hci_uart_write_work() due to a race condition between the initialization of the HCI UART line discipline and concurrent TTY hangup. This issue was triggered by our custom device emulation and fuzzing framework (DevGen) on the v6.18 kernel. Due to the highly timing-dependent nature of this race condition (requiring a precise interleaving of TIOCVHANGUP and protocol setup), Syzkaller failed to extract a reliable standalone C reproducer (reproducer is too unreliable: 0.00). The crash trace is as follows: ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 ... Call Trace: <TASK> debug_check_no_obj_freed+0x3ec/0x520 kfree+0x3f0/0x6c0 hci_uart_tty_close+0x127/0x2a0 k_ldisc_close+0x113/0x1a0 tty_ldisc_kill+0x8e/0x150 tty_ldisc_hangup+0x3c1/0x730 __tty_hangup.part.0+0x3fd/0x8a0 tty_ioctl+0x120f/0x1690 __x64_sys_ioctl+0x18f/0x210 do_syscall_64+0xcb/0xfa0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> The issue arises because the workqueues (init_ready and write_work) are only flushed/cancelled if the HCI_UART_PROTO_READY flag is set. However, during the protocol initialization phase (HCI_UART_PROTO_INIT), the underlying protocol may schedule work. If a hangup occurs before the setup completes and the READY flag is set, hci_uart_tty_close() skips the teardown of these workqueues and proceeds to free the `hu` struct. When the scheduled work executes later, it blindly dereferences the freed `hu` struct. Fix this by moving the workqueue teardown to the very beginning of hci_uart_tty_close(), outside the HCI_UART_PROTO_READY check and prior to calling hci_uart_close(). Furthermore, use disable_work_sync() instead of cancel_work_sync() to unconditionally disable the works. This ensures that any pending works are cancelled and no new submissions can occur before or during the teardown of the device connection. Note that hu->init_ready and hu->write_work are initialized in hci_uart_tty_open(), so it is always safe to call disable_work_sync() on them in hci_uart_tty_close(), even if the protocol was never fully attached. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v5: - Moved disable_work_sync() to the very top of hci_uart_tty_close(), before hci_uart_close(), to prevent any concurrent re-queuing during device shutdown and resolve Sashiko static analysis warnings. Changes in v4: - Adopted Luiz's suggestion to use disable_work_sync() instead of cancel_work_sync() to prevent new work submissions during teardown. Changes in v3: - Added 'Cc: stable' tag as requested by the stable bot. Changes in v2: - Added KASAN/ODEBUG crash trace. drivers/bluetooth/hci_ldisc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..ebdbcd567cd2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -540,6 +540,15 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (!hu) return; + /* + * Disable workqueues unconditionally before tearing down the + * connection, as they might be active during the PROTO_INIT phase. + * Using disable_work_sync() ensures no new submissions can occur + * during or after hci_uart_close(). + */ + disable_work_sync(&hu->init_ready); + disable_work_sync(&hu->write_work); + hdev = hu->hdev; if (hdev) hci_uart_close(hdev); @@ -549,9 +558,6 @@ static void hci_uart_tty_close(struct tty_struct *tty) clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v6] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths 2026-05-15 16:08 ` Luiz Augusto von Dentz 2026-05-16 1:41 ` w15303746062 2026-05-16 2:22 ` [PATCH v5] " w15303746062 @ 2026-05-16 5:30 ` w15303746062 2026-05-16 8:47 ` [PATCH v7] " w15303746062 3 siblings, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-16 5:30 UTC (permalink / raw) To: luiz.dentz, pmenzel, marcel, linux-bluetooth Cc: linux-serial, linux-kernel, greg, stable, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Vulnerabilities leading to Use-After-Free (UAF) and Null Pointer Dereference (NPD) conditions were observed in the lifecycle management of hci_uart. The primary issue arises because the workqueues (init_ready and write_work) are only flushed/cancelled if the HCI_UART_PROTO_READY flag is set during TTY close. If a hangup occurs before setup completes, hci_uart_tty_close() skips the teardown of these workqueues and proceeds to free the `hu` struct. When the scheduled work executes later, it blindly dereferences the freed `hu` struct. This issue was triggered by our custom device emulation and fuzzing framework (DevGen) on the v6.18 kernel. The crash trace is as follows: ODEBUG: free active (active state 0) object: ffff88804024e870 object type: work_struct hint: hci_uart_write_work+0x0/0x940 WARNING: CPU: 0 PID: 338273 at lib/debugobjects.c:612 debug_print_object+0x1a2/0x2b0 ... Call Trace: <TASK> debug_check_no_obj_freed+0x3ec/0x520 kfree+0x3f0/0x6c0 hci_uart_tty_close+0x127/0x2a0 k_ldisc_close+0x113/0x1a0 tty_ldisc_kill+0x8e/0x150 tty_ldisc_hangup+0x3c1/0x730 __tty_hangup.part.0+0x3fd/0x8a0 tty_ioctl+0x120f/0x1690 __x64_sys_ioctl+0x18f/0x210 do_syscall_64+0xcb/0xfa0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Additionally, sister bugs exist in the device registration error paths within hci_uart_init_work() and hci_uart_register_dev(). If hci_register_dev() fails, the code frees hdev and sets hu->hdev to NULL, but leaves hu->write_work active if it was already scheduled during early protocol setup phase. When the work executes later, it blindly fetches and dereferences the NULL hu->hdev pointer, triggering an unconditioned kernel panic. Furthermore, the error path in hci_uart_init_work() clears the HCI_UART_PROTO_READY flag and invokes hu->proto->close(hu) without acquiring the hu->proto_lock write lock. Since concurrent callbacks like hci_uart_tty_receive() execute under the read lock, this missing synchronization allows the protocol state to be destroyed while active readers are accessing it. Fix these synchronization and lifecycle issues by: 1. Moving the workqueue teardown to the very beginning of hci_uart_tty_close() and using disable_work_sync() to unconditionally prevent new work submissions. 2. Wrapping the clearance of HCI_UART_PROTO_READY in hci_uart_init_work() with percpu_down_write/percpu_up_write of hu->proto_lock. 3. Explicitly invoking disable_work_sync(&hu->write_work) before resetting hu->hdev to NULL in both initialization error paths to safely prevent any concurrent re-queuing issues. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v6: - Fixed missing `hu->proto_lock` write lock in hci_uart_init_work() error path to prevent race with readers (reported by Sashiko). - Added disable_work_sync() instead of cancel_work_sync() for `hu->write_work` in hci_uart_init_work() and hci_uart_register_dev() error paths to completely block any concurrent re-queuing window before hdev is freed (reported by Sashiko). Changes in v5: - Relocated disable_work_sync() to the very top of hci_uart_tty_close(), before hci_uart_close(), to ensure no new work is submitted during device teardown. Changes in v4: - Adopted Luiz's suggestion to use disable_work_sync() instead of cancel_work_sync() in close path to prevent new work submissions. Changes in v3: - Added 'Cc: stable' tag as requested by the stable bot. Changes in v2: - Added KASAN/ODEBUG crash trace. drivers/bluetooth/hci_ldisc.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..612fa2890cec 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -194,7 +194,22 @@ void hci_uart_init_work(struct work_struct *work) err = hci_register_dev(hu->hdev); if (err < 0) { BT_ERR("Can't register HCI device"); + + /* + * Acquire proto_lock before clearing PROTO_READY and closing + * the protocol to synchronize with active readers. + */ + percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); + percpu_up_write(&hu->proto_lock); + + /* + * Disable any pending write_work that may have been queued + * during the PROTO_INIT phase to prevent UAF/NPD when hdev + * is freed. + */ + disable_work_sync(&hu->write_work); + hu->proto->close(hu); hdev = hu->hdev; hu->hdev = NULL; @@ -540,6 +555,15 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (!hu) return; + /* + * Disable workqueues unconditionally before tearing down the + * connection, as they might be active during the PROTO_INIT phase. + * Using disable_work_sync() ensures no new submissions can occur + * during or after hci_uart_close(). + */ + disable_work_sync(&hu->init_ready); + disable_work_sync(&hu->write_work); + hdev = hu->hdev; if (hdev) hci_uart_close(hdev); @@ -549,9 +573,6 @@ static void hci_uart_tty_close(struct tty_struct *tty) clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); - if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); @@ -692,6 +713,14 @@ static int hci_uart_register_dev(struct hci_uart *hu) if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); + + /* + * Disable any pending write_work that may have been queued + * during the proto->open() phase to prevent UAF/NPD when + * hdev is freed. + */ + disable_work_sync(&hu->write_work); + percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_INIT, &hu->flags); percpu_up_write(&hu->proto_lock); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v7] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths 2026-05-15 16:08 ` Luiz Augusto von Dentz ` (2 preceding siblings ...) 2026-05-16 5:30 ` [PATCH v6] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths w15303746062 @ 2026-05-16 8:47 ` w15303746062 3 siblings, 0 replies; 13+ messages in thread From: w15303746062 @ 2026-05-16 8:47 UTC (permalink / raw) To: luiz.dentz, pmenzel, marcel, linux-bluetooth Cc: linux-serial, linux-kernel, greg, stable, Mingyu Wang From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Vulnerabilities leading to Use-After-Free (UAF) and Null Pointer Dereference (NPD) conditions were observed in the lifecycle management of hci_uart. The primary issue arises because the workqueues (init_ready and write_work) are only flushed/cancelled if the HCI_UART_PROTO_READY flag is set during TTY close. If a hangup occurs before setup completes, hci_uart_tty_close() skips the teardown of these workqueues and proceeds to free the `hu` struct. When the scheduled work executes later, it blindly dereferences the freed `hu` struct. Furthermore, several data races and UAFs were identified in the teardown sequence: 1. Calling hci_uart_close(hdev) before cancel_work_sync(&hu->write_work) causes a race condition where hci_uart_flush() and write_work can concurrently double-free hu->tx_skb. 2. Calling hci_free_dev(hdev) before hu->proto->close(hu) causes a UAF when vendor specific protocol close callbacks dereference hu->hdev. 3. In the initialization error paths, failing to take the proto_lock write lock before clearing PROTO_READY leads to races with active readers (e.g., hci_uart_tty_receive). Fix these synchronization and lifecycle issues by: 1. Re-ordering hci_uart_tty_close() to unconditionally cancel init_ready first, then atomically clear PROTO_READY under proto_lock, and safely cancel write_work before touching hdev. 2. Relocating hu->proto->close(hu) strictly prior to hci_free_dev(hdev) across all close and error paths to prevent vendor-level UAFs. 3. Utilizing cancel_work_sync() instead of disable_work_sync() after flags are cleared to safely flush workqueues without permanently breaking user-space retry capabilities. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> --- Changes in v7: - Reverted disable_work_sync() back to cancel_work_sync() across all error and close paths to preserve user-space retry capabilities, addressing the regression introduced in v4/v6 where work items were permanently disabled. - Synchronized workqueue teardown safely by atomically clearing PROTO_READY / PROTO_INIT under proto_lock prior to calling cancel_work_sync(), preventing any concurrent work requeuing. - Fixed a Use-After-Free (UAF) vulnerability in the teardown sequence by relocating hu->proto->close(hu) strictly prior to hci_free_dev(hdev) in all close and error paths, ensuring vendor specific callbacks safely access hu->hdev. - Added cancel_work_sync(&hu->init_ready) at the very beginning of hci_uart_tty_close() to serialize teardown against active asynchronous registration, eliminating race-induced double-frees. Changes in v6: - Fixed missing `hu->proto_lock` write lock in hci_uart_init_work() error path to prevent race with readers (reported by Sashiko). - Added disable_work_sync() instead of cancel_work_sync() for `hu->write_work` in hci_uart_init_work() and hci_uart_register_dev() error paths to completely block any concurrent re-queuing window before hdev is freed (reported by Sashiko). Changes in v5: - Relocated disable_work_sync() to the very top of hci_uart_tty_close(), before hci_uart_close(), to ensure no new work is submitted during device teardown. Changes in v4: - Adopted Luiz's suggestion to use disable_work_sync() instead of cancel_work_sync() in close path to prevent new work submissions. Changes in v3: - Added 'Cc: stable' tag as requested by the stable bot. Changes in v2: - Added KASAN/ODEBUG crash trace. drivers/bluetooth/hci_ldisc.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 275ea865bc29..46a080f77cb1 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -194,7 +194,15 @@ void hci_uart_init_work(struct work_struct *work) err = hci_register_dev(hu->hdev); if (err < 0) { BT_ERR("Can't register HCI device"); + + percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); + percpu_up_write(&hu->proto_lock); + + /* Safely cancel work after clearing flags */ + cancel_work_sync(&hu->write_work); + + /* Close protocol before freeing hdev */ hu->proto->close(hu); hdev = hu->hdev; hu->hdev = NULL; @@ -531,6 +539,7 @@ static void hci_uart_tty_close(struct tty_struct *tty) { struct hci_uart *hu = tty->disc_data; struct hci_dev *hdev; + bool proto_ready; BT_DBG("tty %p", tty); @@ -540,24 +549,32 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (!hu) return; - hdev = hu->hdev; - if (hdev) - hci_uart_close(hdev); + /* Wait for init_ready to finish to prevent registration races */ + cancel_work_sync(&hu->init_ready); - if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { + proto_ready = test_bit(HCI_UART_PROTO_READY, &hu->flags); + if (proto_ready) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); + } + /* Unconditionally cancel write_work after clearing flags */ + cancel_work_sync(&hu->write_work); - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); + hdev = hu->hdev; + if (hdev) + hci_uart_close(hdev); + if (proto_ready) { if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); - hci_free_dev(hdev); } + /* Close protocol before freeing hdev */ hu->proto->close(hu); + + if (hdev) + hci_free_dev(hdev); } clear_bit(HCI_UART_PROTO_SET, &hu->flags); @@ -695,6 +712,10 @@ static int hci_uart_register_dev(struct hci_uart *hu) percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_INIT, &hu->flags); percpu_up_write(&hu->proto_lock); + /* Cancel work after clearing flags */ + cancel_work_sync(&hu->write_work); + + /* Close protocol before freeing hdev */ hu->proto->close(hu); hu->hdev = NULL; hci_free_dev(hdev); -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-05-16 8:48 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-13 6:45 [PATCH] Bluetooth: hci_uart: fix UAF in hci_uart_tty_close() w15303746062 2026-05-13 9:04 ` Paul Menzel 2026-05-14 15:17 ` [PATCH v2] " w15303746062 2026-05-15 6:10 ` Greg KH 2026-05-15 6:50 ` [PATCH v3] " w15303746062 2026-05-15 12:37 ` [PATCH] " Luiz Augusto von Dentz 2026-05-15 13:39 ` w15303746062 2026-05-15 14:05 ` [PATCH v4] " w15303746062 2026-05-15 16:08 ` Luiz Augusto von Dentz 2026-05-16 1:41 ` w15303746062 2026-05-16 2:22 ` [PATCH v5] " w15303746062 2026-05-16 5:30 ` [PATCH v6] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths w15303746062 2026-05-16 8:47 ` [PATCH v7] " w15303746062
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox