Bluetooth: hci_uart: Fix another race during initialization
authorArseniy Krasnov <avkrasnov@salutedevices.com>
Wed, 12 Feb 2025 15:59:46 +0000 (18:59 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 25 Apr 2025 08:45:37 +0000 (10:45 +0200)
commit 5df5dafc171b90d0b8d51547a82657cd5a1986c7 upstream.

Do not set 'HCI_UART_PROTO_READY' before call 'hci_uart_register_dev()'.
Possible race is when someone calls 'hci_tty_uart_close()' after this bit
is set, but 'hci_uart_register_dev()' wasn't done. This leads to access
to uninitialized fields. To fix it let's set this bit after device was
registered (as before patch c411c62cc133) and to fix previous problem let's
add one more bit in addition to 'HCI_UART_PROTO_READY' which allows to
perform power up without original bit set (pls see commit c411c62cc133).

Crash backtrace from syzbot report:

RIP: 0010:skb_queue_empty_lockless include/linux/skbuff.h:1887 [inline]
RIP: 0010:skb_queue_purge_reason+0x6d/0x140 net/core/skbuff.c:3936

Call Trace:
 <TASK>
 skb_queue_purge include/linux/skbuff.h:3364 [inline]
 mrvl_close+0x2f/0x90 drivers/bluetooth/hci_mrvl.c:100
 hci_uart_tty_close+0xb6/0x120 drivers/bluetooth/hci_ldisc.c:557
 tty_ldisc_close drivers/tty/tty_ldisc.c:455 [inline]
 tty_ldisc_kill+0x66/0xc0 drivers/tty/tty_ldisc.c:613
 tty_ldisc_release+0xc9/0x120 drivers/tty/tty_ldisc.c:781
 tty_release_struct+0x10/0x80 drivers/tty/tty_io.c:1690
 tty_release+0x4ef/0x640 drivers/tty/tty_io.c:1861
 __fput+0x86/0x2a0 fs/file_table.c:450
 task_work_run+0x82/0xb0 kernel/task_work.c:239
 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
 exit_to_user_mode_loop kernel/entry/common.c:114 [inline]
 exit_to_user_mode_prepare include/linux/entry-common.h:329 [inline]
 __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline]
 syscall_exit_to_user_mode+0xa3/0x1b0 kernel/entry/common.c:218
 do_syscall_64+0x9a/0x190 arch/x86/entry/common.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com>
Reported-by: syzbot+683f8cb11b94b1824c77@syzkaller.appspotmail.com
Tested-by: syzbot+683f8cb11b94b1824c77@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-bluetooth/d159c57f-8490-4c26-79da-6ad3612c4a14@salutedevices.com/
Fixes: 366ceff495f9 ("Bluetooth: hci_uart: fix race during initialization")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_uart.h

index b67023d69e03ddb1eeb3439ce442289439a534b2..70320b8f1aa1c043276a5d4656468d0d1b8e1956 100644 (file)
@@ -102,7 +102,8 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
        if (!skb) {
                percpu_down_read(&hu->proto_lock);
 
-               if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+               if (test_bit(HCI_UART_PROTO_READY, &hu->flags) ||
+                   test_bit(HCI_UART_PROTO_INIT, &hu->flags))
                        skb = hu->proto->dequeue(hu);
 
                percpu_up_read(&hu->proto_lock);
@@ -124,7 +125,8 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
        if (!percpu_down_read_trylock(&hu->proto_lock))
                return 0;
 
-       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+           !test_bit(HCI_UART_PROTO_INIT, &hu->flags))
                goto no_schedule;
 
        set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
@@ -278,7 +280,8 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        percpu_down_read(&hu->proto_lock);
 
-       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+           !test_bit(HCI_UART_PROTO_INIT, &hu->flags)) {
                percpu_up_read(&hu->proto_lock);
                return -EUNATCH;
        }
@@ -582,7 +585,8 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
        if (tty != hu->tty)
                return;
 
-       if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+       if (test_bit(HCI_UART_PROTO_READY, &hu->flags) ||
+           test_bit(HCI_UART_PROTO_INIT, &hu->flags))
                hci_uart_tx_wakeup(hu);
 }
 
@@ -608,7 +612,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 
        percpu_down_read(&hu->proto_lock);
 
-       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+       if (!test_bit(HCI_UART_PROTO_READY, &hu->flags) &&
+           !test_bit(HCI_UART_PROTO_INIT, &hu->flags)) {
                percpu_up_read(&hu->proto_lock);
                return;
        }
@@ -704,13 +709,16 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
 
        hu->proto = p;
 
-       set_bit(HCI_UART_PROTO_READY, &hu->flags);
+       set_bit(HCI_UART_PROTO_INIT, &hu->flags);
 
        err = hci_uart_register_dev(hu);
        if (err) {
                return err;
        }
 
+       set_bit(HCI_UART_PROTO_READY, &hu->flags);
+       clear_bit(HCI_UART_PROTO_INIT, &hu->flags);
+
        return 0;
 }
 
index 00bf7ae82c5b721366474e5b3af9597e39682273..39f39704be41fd9ddfacae07ce03c54e612856d0 100644 (file)
@@ -89,6 +89,7 @@ struct hci_uart {
 #define HCI_UART_REGISTERED            1
 #define HCI_UART_PROTO_READY           2
 #define HCI_UART_NO_SUSPEND_NOTIFIER   3
+#define HCI_UART_PROTO_INIT            4
 
 /* TX states  */
 #define HCI_UART_SENDING       1