]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
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, 2 May 2025 05:39:18 +0000 (07:39 +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 8e4a23cd40218ab83437ef63c9b1af03652d1f76..3facfc53f31b5c77eba1d08a4d6ea795dafa0bab 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;
        }
@@ -576,7 +579,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);
 }
 
@@ -602,7 +606,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;
        }
@@ -703,13 +708,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 6ab631101019c4198d24266936c179c262501c03..a07f77555026d8b9ddf836a1b8c24e473359090e 100644 (file)
@@ -89,6 +89,7 @@ struct hci_uart {
 #define HCI_UART_PROTO_SET     0
 #define HCI_UART_REGISTERED    1
 #define HCI_UART_PROTO_READY   2
+#define HCI_UART_PROTO_INIT    4
 
 /* TX states  */
 #define HCI_UART_SENDING       1