VYPR
Unrated severityNVD Advisory· Published Jun 8, 2026

CVE-2026-46275

CVE-2026-46275

Description

Linux kernel Bluetooth driver has Use-After-Free and Null Pointer Dereference flaws in its lifecycle management, potentially leading to crashes or code execution.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Linux kernel Bluetooth driver has Use-After-Free and Null Pointer Dereference flaws in its lifecycle management, potentially leading to crashes or code execution.

Vulnerability

Use-After-Free (UAF) and Null Pointer Dereference (NPD) vulnerabilities exist in the lifecycle management of the hci_uart component within the Linux kernel's Bluetooth subsystem. These issues occur when workqueues (init_ready and write_work) are not properly flushed or cancelled during the TTY close path, especially if a hangup happens before setup is complete. This can lead to the hu struct being freed while scheduled work still attempts to access it.

Exploitation

An attacker could trigger these vulnerabilities by exploiting race conditions during the device teardown sequence. For instance, calling hci_uart_flush() from hci_uart_close() without properly disabling write_work can lead to a double-free of hu->tx_skb. Additionally, freeing the device (hci_free_dev) before protocol-specific cleanup (hu->proto->close) can result in a UAF when callbacks dereference the freed device. Initialization error paths also present race conditions if locks are not acquired correctly.

Impact

Successful exploitation of these vulnerabilities could lead to system instability, crashes, and potentially arbitrary code execution within the kernel. The specific impact depends on the exact race condition exploited, but it generally involves accessing freed memory, which can corrupt data or allow an attacker to gain control of kernel-level operations.

Mitigation

These vulnerabilities have been addressed in the Linux kernel. The specific commits that fix these issues are referenced as [1], [2], [3], and [4]. Users are advised to update to a patched version of the Linux kernel as soon as possible. No workarounds are available, and the affected versions are not specified beyond being prior to the fixes in the referenced commits.

AI Insight generated on Jun 8, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

16
c1bb9336ae6b

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 7.1-rc5via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 275ea865bc297..47f4902b40b47 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
c85cff648a2b

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 6.1.175via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index f86ac94d53a4e..00e03f77ae9a0 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -528,6 +540,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);
     
    @@ -537,24 +550,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -622,11 +649,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -697,6 +725,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);
    -- 
    cgit 1.3-korg
    
    
    
7338031946bd

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 7.0.11via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 275ea865bc297..47f4902b40b47 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
78aad93e938f

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 5.10.258via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index b1e036bb682f8..2c9e6e5a4d182 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -525,6 +537,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);
     
    @@ -534,24 +547,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -619,11 +646,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -694,6 +722,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);
    -- 
    cgit 1.3-korg
    
    
    
e2d19969c8d9

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 5.15.209via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index dfd1d4a4d9fc2..f0fb84352fcde 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -525,6 +537,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);
     
    @@ -534,24 +547,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -619,11 +646,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -694,6 +722,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);
    -- 
    cgit 1.3-korg
    
    
    
9d20d48be2c4

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 6.6.142via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 85baba91aaa97..573be8c3b4dca 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -528,6 +540,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);
     
    @@ -537,24 +550,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -622,11 +649,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -692,6 +720,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);
    -- 
    cgit 1.3-korg
    
    
    
81c7a3c22a0f

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 6.12.92via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 0d06b83816d1c..05deb52380189 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
192cb0f1ca70

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMingyu WangMay 18, 2026Fixed in 6.18.34via kernel-cna
1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 5455990ab211e..10e936b87cc8e 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
9d20d48be2c4

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 85baba91aaa97..573be8c3b4dca 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -528,6 +540,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);
     
    @@ -537,24 +550,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -622,11 +649,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -692,6 +720,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);
    -- 
    cgit 1.3-korg
    
    
    
c1bb9336ae6b

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 275ea865bc297..47f4902b40b47 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
c85cff648a2b

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index f86ac94d53a4e..00e03f77ae9a0 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -528,6 +540,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);
     
    @@ -537,24 +550,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -622,11 +649,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -697,6 +725,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);
    -- 
    cgit 1.3-korg
    
    
    
192cb0f1ca70

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 5455990ab211e..10e936b87cc8e 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
7338031946bd

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 275ea865bc297..47f4902b40b47 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    
e2d19969c8d9

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index dfd1d4a4d9fc2..f0fb84352fcde 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -525,6 +537,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);
     
    @@ -534,24 +547,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -619,11 +646,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -694,6 +722,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);
    -- 
    cgit 1.3-korg
    
    
    
78aad93e938f

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index b1e036bb682f8..2c9e6e5a4d182 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -525,6 +537,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);
     
    @@ -534,24 +547,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -619,11 +646,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -694,6 +722,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);
    -- 
    cgit 1.3-korg
    
    
    
81c7a3c22a0f

Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths

1 file changed · +40 9
  • drivers/bluetooth/hci_ldisc.c+40 9 modified
    diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
    index 0d06b83816d1c..05deb52380189 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;
    @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev)
     /* Close device */
     static int hci_uart_close(struct hci_dev *hdev)
     {
    +	struct hci_uart *hu = hci_get_drvdata(hdev);
    +
     	BT_DBG("hdev %p", hdev);
     
    +	cancel_work_sync(&hu->write_work);
    +
     	hci_uart_flush(hdev);
     	hdev->flush = NULL;
     	return 0;
    @@ -531,6 +543,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 +553,38 @@ 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);
    +	}
     
    -		cancel_work_sync(&hu->init_ready);
    -		cancel_work_sync(&hu->write_work);
    +	/*
    +	 * Unconditionally cancel write_work AFTER clearing PROTO_READY.
    +	 * This ensures that concurrent protocol timers cannot requeue
    +	 * write_work via hci_uart_tx_wakeup(), permanently preventing
    +	 * double-free races and UAFs.
    +	 */
    +	cancel_work_sync(&hu->write_work);
    +
    +	hdev = hu->hdev;
    +	if (hdev)
    +		hci_uart_close(hdev); /* proto->flush is safely skipped */
     
    +	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 (intrinsically purges queues) */
     		hu->proto->close(hu);
    +
    +		if (hdev)
    +			hci_free_dev(hdev);
     	}
     	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
     
    @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
     	 * tty caller
     	 */
     	hu->proto->recv(hu, data, count);
    -	percpu_up_read(&hu->proto_lock);
     
     	if (hu->hdev)
     		hu->hdev->stat.byte_rx += count;
     
    +	percpu_up_read(&hu->proto_lock);
    +
     	tty_unthrottle(tty);
     }
     
    @@ -695,6 +723,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);
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

8

News mentions

0

No linked articles in our index yet.