All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v2] Bluetooth: hci_sync: Fix tx_work queuing race during device close
@ 2026-08-13 12:12 syzbot
  2026-08-13 12:47 ` Krystian Kaniewski
  0 siblings, 1 reply; 2+ messages in thread
From: syzbot @ 2026-08-13 12:12 UTC (permalink / raw)
  To: syzkaller-upstream-moderation; +Cc: krystianmkaniewski, syzbot

During the shutdown process of a Bluetooth device in hci_dev_close_sync(),
tx_work can still be queued by concurrent transmission paths or raw HCI
socket operations after HCI_UP has been checked or while workqueues are
being flushed. This causes __queue_work() to trigger a warning when
attempting to queue work on a draining workqueue:

workqueue: cannot queue hci_tx_work on wq hci0
WARNING: kernel/workqueue.c:2306 at __queue_work+0xd4a/0x1090
Call Trace:
 <TASK>
 queue_work_on+0x106/0x1c0 kernel/workqueue.c:2452
 l2cap_chan_send+0x168a/0x22f0 net/bluetooth/l2cap_core.c:-1
 l2cap_sock_sendmsg+0x33a/0x4d0 net/bluetooth/l2cap_sock.c:1180
 ____sys_sendmsg+0x54e/0x850 net/socket.c:2684
 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
 __sys_sendmmsg+0x273/0x4d0 net/socket.c:2827
 </TASK>

To fix this, replace flush_work() with disable_work_sync() and re-enable it
with enable_work() in hci_dev_close_sync() to establish a work exclusion
interval for hdev->tx_work while the device is closing. Additionally,
ensure raw HCI socket producers in hci_sock_sendmsg() are quiesced by
holding rcu_read_lock() while checking HCI_UP, paired with
synchronize_rcu() in hci_dev_close_sync() right after disabling tx_work.

Fixes: 76727c02c1e1 ("Bluetooth: Call drain_workqueue() before resetting state")
Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+b6919040d9958e2fc1ae@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b6919040d9958e2fc1ae
Link: https://syzkaller.appspot.com/ai_job?id=bb132695-7f6d-4df7-8464-0dd1708b1661
To: <linux-bluetooth@vger.kernel.org>
To: "Luiz Augusto von Dentz" <luiz.dentz@gmail.com>
To: "Marcel Holtmann" <marcel@holtmann.org>
To: "Johan Hedberg" <johan.hedberg@intel.com>
Cc: <linux-kernel@vger.kernel.org>

---
v2:
- Replaced per-send HCI_UP checks in hci_core.c with disable_work_sync() and enable_work() on hdev->tx_work in hci_dev_close_sync() to establish a work exclusion interval during shutdown.
- Added rcu_read_lock() protection around the HCI_UP check in hci_sock_sendmsg() and synchronize_rcu() in hci_dev_close_sync() to quiesce raw HCI producers.

v1:
https://lore.kernel.org/all/2c2febf2-fc12-4407-9c81-d3d9713f09a4@mail.kernel.org/T/
---
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 070ca388f..8345ebbbd 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -1850,9 +1850,10 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 		goto drop;
 	}
 
+	rcu_read_lock();
 	if (!test_bit(HCI_UP, &hdev->flags)) {
 		err = -ENETDOWN;
-		goto drop;
+		goto drop_rcu;
 	}
 
 	hci_skb_pkt_type(skb) = skb->data[0];
@@ -1870,7 +1871,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 		    hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
 		    hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
 			err = -EINVAL;
-			goto drop;
+			goto drop_rcu;
 		}
 
 		skb_queue_tail(&hdev->raw_q, skb);
@@ -1885,7 +1886,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 				   &hci_sec_filter.ocf_mask[ogf])) &&
 		    !capable(CAP_NET_RAW)) {
 			err = -EPERM;
-			goto drop;
+			goto drop_rcu;
 		}
 
 		/* Since the opcode has already been extracted here, store
@@ -1908,14 +1909,14 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 	} else {
 		if (!capable(CAP_NET_RAW)) {
 			err = -EPERM;
-			goto drop;
+			goto drop_rcu;
 		}
 
 		if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
 		    hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
 		    hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
 			err = -EINVAL;
-			goto drop;
+			goto drop_rcu;
 		}
 
 		skb_queue_tail(&hdev->raw_q, skb);
@@ -1923,14 +1924,16 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 	}
 
 	err = len;
+	rcu_read_unlock();
+	goto done;
 
+drop_rcu:
+	rcu_read_unlock();
+drop:
+	kfree_skb(skb);
 done:
 	release_sock(sk);
 	return err;
-
-drop:
-	kfree_skb(skb);
-	goto done;
 }
 
 static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c8d14128c..bad83313b 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5472,9 +5472,11 @@ int hci_dev_close_sync(struct hci_dev *hdev)
 	hci_leds_update_powered(hdev, false);
 
 	/* Flush RX and TX works */
-	flush_work(&hdev->tx_work);
+	disable_work_sync(&hdev->tx_work);
 	flush_work(&hdev->rx_work);
 
+	synchronize_rcu();
+
 	if (hdev->discov_timeout > 0) {
 		hdev->discov_timeout = 0;
 		hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
@@ -5576,6 +5578,8 @@ int hci_dev_close_sync(struct hci_dev *hdev)
 	bacpy(&hdev->random_addr, BDADDR_ANY);
 	hci_codec_list_clear(&hdev->local_codecs);
 
+	enable_work(&hdev->tx_work);
+
 	hci_dev_put(hdev);
 	return err;
 }


base-commit: 075b74841bd0065a3bda3440873c747938e69b68
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at syzkaller@googlegroups.com.

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH RFC v2] Bluetooth: hci_sync: Fix tx_work queuing race during device close
  2026-08-13 12:12 [PATCH RFC v2] Bluetooth: hci_sync: Fix tx_work queuing race during device close syzbot
@ 2026-08-13 12:47 ` Krystian Kaniewski
  0 siblings, 0 replies; 2+ messages in thread
From: Krystian Kaniewski @ 2026-08-13 12:47 UTC (permalink / raw)
  To: syzbot, syzkaller-upstream-moderation; +Cc: syzbot

Preserve the existing flush_work(&hdev->tx_work) in hci_dev_close_sync() and
call disable_work_sync(&hdev->tx_work) immediately after that flush. The
current patch replaces the flush with a canceling operation. This can 
discard
packets accepted before normal close without the transmit attempt 
performed by
the existing code, and cancellation is not required to close the scheduling
race.

Keep tx_work disabled through drain_workqueue(), connection teardown, final
queue cleanup, HCI_RUNNING clearing, the driver close callback, and volatile
flag cleanup. Keep the final balanced enable_work(), including the nested
disable behavior under hci_unregister_dev().

Keep the RCU read-side section in hci_sock_sendmsg() from the HCI_UP check
through validation, queue insertion, and scheduling. Keep the matching
synchronize_rcu() after HCI_UP is clear and tx_work is disabled. 
Preserve all
socket errors, skb ownership, UAPI, Fixes, Reported-by, Closes, Link, and
Gemini provenance tags. Update the close comment and patch description 
so they
state that pending TX work is flushed before later scheduling is disabled.

On 8/13/2026 2:12 PM, syzbot wrote:
> During the shutdown process of a Bluetooth device in hci_dev_close_sync(),
> tx_work can still be queued by concurrent transmission paths or raw HCI
> socket operations after HCI_UP has been checked or while workqueues are
> being flushed. This causes __queue_work() to trigger a warning when
> attempting to queue work on a draining workqueue:
>
> workqueue: cannot queue hci_tx_work on wq hci0
> WARNING: kernel/workqueue.c:2306 at __queue_work+0xd4a/0x1090
> Call Trace:
>   <TASK>
>   queue_work_on+0x106/0x1c0 kernel/workqueue.c:2452
>   l2cap_chan_send+0x168a/0x22f0 net/bluetooth/l2cap_core.c:-1
>   l2cap_sock_sendmsg+0x33a/0x4d0 net/bluetooth/l2cap_sock.c:1180
>   ____sys_sendmsg+0x54e/0x850 net/socket.c:2684
>   ___sys_sendmsg+0x2a5/0x360 net/socket.c:2738
>   __sys_sendmmsg+0x273/0x4d0 net/socket.c:2827
>   </TASK>
>
> To fix this, replace flush_work() with disable_work_sync() and re-enable it
> with enable_work() in hci_dev_close_sync() to establish a work exclusion
> interval for hdev->tx_work while the device is closing. Additionally,
> ensure raw HCI socket producers in hci_sock_sendmsg() are quiesced by
> holding rcu_read_lock() while checking HCI_UP, paired with
> synchronize_rcu() in hci_dev_close_sync() right after disabling tx_work.
>
> Fixes: 76727c02c1e1 ("Bluetooth: Call drain_workqueue() before resetting state")
> Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot
> Reported-by: syzbot+b6919040d9958e2fc1ae@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=b6919040d9958e2fc1ae
> Link: https://syzkaller.appspot.com/ai_job?id=bb132695-7f6d-4df7-8464-0dd1708b1661
> To: <linux-bluetooth@vger.kernel.org>
> To: "Luiz Augusto von Dentz" <luiz.dentz@gmail.com>
> To: "Marcel Holtmann" <marcel@holtmann.org>
> To: "Johan Hedberg" <johan.hedberg@intel.com>
> Cc: <linux-kernel@vger.kernel.org>
>
> ---
> v2:
> - Replaced per-send HCI_UP checks in hci_core.c with disable_work_sync() and enable_work() on hdev->tx_work in hci_dev_close_sync() to establish a work exclusion interval during shutdown.
> - Added rcu_read_lock() protection around the HCI_UP check in hci_sock_sendmsg() and synchronize_rcu() in hci_dev_close_sync() to quiesce raw HCI producers.
>
> v1:
> https://lore.kernel.org/all/2c2febf2-fc12-4407-9c81-d3d9713f09a4@mail.kernel.org/T/
> ---
> diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
> index 070ca388f..8345ebbbd 100644
> --- a/net/bluetooth/hci_sock.c
> +++ b/net/bluetooth/hci_sock.c
> @@ -1850,9 +1850,10 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
>   		goto drop;
>   	}
>   
> +	rcu_read_lock();
>   	if (!test_bit(HCI_UP, &hdev->flags)) {
>   		err = -ENETDOWN;
> -		goto drop;
> +		goto drop_rcu;
>   	}
>   
>   	hci_skb_pkt_type(skb) = skb->data[0];
> @@ -1870,7 +1871,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
>   		    hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
>   		    hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
>   			err = -EINVAL;
> -			goto drop;
> +			goto drop_rcu;
>   		}
>   
>   		skb_queue_tail(&hdev->raw_q, skb);
> @@ -1885,7 +1886,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
>   				   &hci_sec_filter.ocf_mask[ogf])) &&
>   		    !capable(CAP_NET_RAW)) {
>   			err = -EPERM;
> -			goto drop;
> +			goto drop_rcu;
>   		}
>   
>   		/* Since the opcode has already been extracted here, store
> @@ -1908,14 +1909,14 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
>   	} else {
>   		if (!capable(CAP_NET_RAW)) {
>   			err = -EPERM;
> -			goto drop;
> +			goto drop_rcu;
>   		}
>   
>   		if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
>   		    hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
>   		    hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
>   			err = -EINVAL;
> -			goto drop;
> +			goto drop_rcu;
>   		}
>   
>   		skb_queue_tail(&hdev->raw_q, skb);
> @@ -1923,14 +1924,16 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
>   	}
>   
>   	err = len;
> +	rcu_read_unlock();
> +	goto done;
>   
> +drop_rcu:
> +	rcu_read_unlock();
> +drop:
> +	kfree_skb(skb);
>   done:
>   	release_sock(sk);
>   	return err;
> -
> -drop:
> -	kfree_skb(skb);
> -	goto done;
>   }
>   
>   static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index c8d14128c..bad83313b 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -5472,9 +5472,11 @@ int hci_dev_close_sync(struct hci_dev *hdev)
>   	hci_leds_update_powered(hdev, false);
>   
>   	/* Flush RX and TX works */
> -	flush_work(&hdev->tx_work);
> +	disable_work_sync(&hdev->tx_work);
>   	flush_work(&hdev->rx_work);
>   
> +	synchronize_rcu();
> +
>   	if (hdev->discov_timeout > 0) {
>   		hdev->discov_timeout = 0;
>   		hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
> @@ -5576,6 +5578,8 @@ int hci_dev_close_sync(struct hci_dev *hdev)
>   	bacpy(&hdev->random_addr, BDADDR_ANY);
>   	hci_codec_list_clear(&hdev->local_codecs);
>   
> +	enable_work(&hdev->tx_work);
> +
>   	hci_dev_put(hdev);
>   	return err;
>   }
>
>
> base-commit: 075b74841bd0065a3bda3440873c747938e69b68

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-13 12:47 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 12:12 [PATCH RFC v2] Bluetooth: hci_sync: Fix tx_work queuing race during device close syzbot
2026-08-13 12:47 ` Krystian Kaniewski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.