| 0/0 |
2026/05/21 02:00 |
flow |
assessment-security |
42m
Results: map[DenialOfService:true Explanation:Based on the analysis of the provided kernel bug report and source code, here is
the assessment of the vulnerability:
**Root Cause Analysis:**
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered when
`queue_work()` is called on a workqueue that is currently being drained (i.e.,
has the `__WQ_DRAINING` flag set).
In the Bluetooth subsystem, `drain_workqueue(hdev->workqueue)` is called during
the device shutdown sequence in `hci_dev_close_sync()`. Concurrently, if a
process sends data over an L2CAP socket, the network stack calls
`l2cap_chan_send()` -> `hci_send_acl()`, which attempts to queue `hdev->tx_work`
on `hdev->workqueue`. If this queueing attempt happens exactly while the
workqueue is being drained, the kernel emits the reported `WARN_ONCE` and drops
the work.
**Security Impact (Exploitability & Denial of Service):**
While the warning itself is benign (unless `panic_on_warn` is enabled, causing a
DoS), the underlying race condition can lead to a severe **Use-After-Free
(UAF)**.
If the L2CAP data is sent slightly laterβafter `drain_workqueue()` finishes but
before `hci_dev_close_sync()` calls `hci_conn_hash_flush()` to destroy active
connectionsβthe `tx_work` will be successfully queued. This allows
`hci_tx_work()` to run concurrently with `hci_conn_hash_flush()`.
During this race, `hci_tx_work()` calls `hci_sched_acl_pkt()`, which retrieves
an `hci_chan` pointer via `hci_chan_sent()`. However, `hci_chan_sent()` releases
the `rcu_read_lock()` *before* returning the pointer. Concurrently,
`hci_conn_hash_flush()` deletes the connection and frees the `hci_chan`
structure via `kfree()`. The `tx_work` thread will then access the freed
`chan->data_q` (a linked list), leading to an exploitable Use-After-Free that
can be leveraged for arbitrary memory corruption and privilege escalation.
**Trigger Vectors & Accessibility:**
To trigger this vulnerability, an attacker must be able to initiate a Bluetooth
device shutdown (`hci_dev_close_sync()`) concurrently with sending L2CAP
traffic.
1. **Unprivileged / User Namespaces:** The device shutdown can be triggered via
the `HCIDEVDOWN` or `HCIDEVRESET` ioctls. However, the `hci_sock_ioctl()`
handler explicitly checks for global `CAP_NET_ADMIN` using
`capable(CAP_NET_ADMIN)` rather than `ns_capable()`. Therefore, unprivileged
users **cannot** trigger this bug, even from within a user namespace.
2. **Peripheral Trigger:** The shutdown sequence is also automatically invoked
when a Bluetooth controller is physically removed from the system. An attacker
with physical access could trigger the race condition by rapidly unplugging a
USB Bluetooth dongle while a background process floods the device with L2CAP
traffic.
In conclusion, this is an exploitable Use-After-Free vulnerability, but its
attack surface is restricted to privileged users (root/CAP_NET_ADMIN) or
attackers with physical access to plug/unplug peripheral devices. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/21 02:00 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/21 02:00 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/2afd6c0879e2db462e2b6d005e868f11ace61250]
|
| 3/1 |
2026/05/21 02:01 |
action |
kernel-builder |
18m
Results: map[KernelObj:/app/workdir/cache/build/b8cdaa558a1c3061870677ca490703542de04669]
|
| 4/1 |
2026/05/21 02:20 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/21 02:32 |
agent |
expert |
9m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction:
You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by greeping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Don't make assumptions; verify them with source code access. Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completly wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control tranfers, that's also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitabability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidetial Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
workqueue: cannot queue hci_tx_work on wq hci0
WARNING: CPU: 0 PID: 18997 at kernel/workqueue.c:2255 __queue_work+0xd03/0x1160 kernel/workqueue.c:2254
Modules linked in:
CPU: 0 UID: 0 PID: 18997 Comm: syz.1.3296 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025
RIP: 0010:__queue_work+0xd03/0x1160 kernel/workqueue.c:2254
Code: 00 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 4b 04 00 00 48 8b 75 18 4c 89 fa 48 c7 c7 a0 03 ac 8b e8 be 1e f7 ff 90 <0f> 0b 90 90 e9 2f f7 ff ff e8 8f 7b 38 00 90 0f 0b 90 e9 b4 f5 ff
RSP: 0018:ffffc90004a97710 EFLAGS: 00010086
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffc90020733000
RDX: 0000000000080000 RSI: ffffffff817a4395 RDI: 0000000000000001
RBP: ffff88805dd4cb50 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000000 R11: fffffffffffce858 R12: 0000000000000008
R13: ffff88807f0db000 R14: 0000000000000000 R15: ffff88807f0db178
FS: 00007f7326cdc6c0(0000) GS:ffff8881246b4000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000003940 CR3: 0000000050120000 CR4: 00000000003526f0
Call Trace:
<TASK>
queue_work_on+0x1a4/0x1f0 kernel/workqueue.c:2390
l2cap_chan_send+0xce5/0x2c50 net/bluetooth/l2cap_core.c:2610
l2cap_sock_sendmsg+0x3c7/0x490 net/bluetooth/l2cap_sock.c:1139
sock_sendmsg_nosec net/socket.c:714 [inline]
__sock_sendmsg net/socket.c:729 [inline]
____sys_sendmsg+0xa95/0xc70 net/socket.c:2614
___sys_sendmsg+0x134/0x1d0 net/socket.c:2668
__sys_sendmmsg+0x200/0x420 net/socket.c:2757
__do_sys_sendmmsg net/socket.c:2784 [inline]
__se_sys_sendmmsg net/socket.c:2781 [inline]
__x64_sys_sendmmsg+0x9c/0x100 net/socket.c:2781
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcd/0x4e0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f7325d8eba9
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f7326cdc038 EFLAGS: 00000246 ORIG_RAX: 0000000000000133
RAX: ffffffffffffffda RBX: 00007f7325fd6090 RCX: 00007f7325d8eba9
RDX: 000000000000ff80 RSI: 0000200000003940 RDI: 0000000000000006
RBP: 00007f7325e11e19 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000024004810 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f7325fd6128 R14: 00007f7325fd6090 R15: 00007ffc32846ad8
</TASK>
Reply: Based on the analysis of the provided kernel bug report and source code, here is the assessment of the vulnerability:
**Root Cause Analysis:**
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered when `queue_work()` is called on a workqueue that is currently being drained (i.e., has the `__WQ_DRAINING` flag set).
In the Bluetooth subsystem, `drain_workqueue(hdev->workqueue)` is called during the device shutdown sequence in `hci_dev_close_sync()`. Concurrently, if a process sends data over an L2CAP socket, the network stack calls `l2cap_chan_send()` -> `hci_send_acl()`, which attempts to queue `hdev->tx_work` on `hdev->workqueue`. If this queueing attempt happens exactly while the workqueue is being drained, the kernel emits the reported `WARN_ONCE` and drops the work.
**Security Impact (Exploitability & Denial of Service):**
While the warning itself is benign (unless `panic_on_warn` is enabled, causing a DoS), the underlying race condition can lead to a severe **Use-After-Free (UAF)**.
If the L2CAP data is sent slightly laterβafter `drain_workqueue()` finishes but before `hci_dev_close_sync()` calls `hci_conn_hash_flush()` to destroy active connectionsβthe `tx_work` will be successfully queued. This allows `hci_tx_work()` to run concurrently with `hci_conn_hash_flush()`.
During this race, `hci_tx_work()` calls `hci_sched_acl_pkt()`, which retrieves an `hci_chan` pointer via `hci_chan_sent()`. However, `hci_chan_sent()` releases the `rcu_read_lock()` *before* returning the pointer. Concurrently, `hci_conn_hash_flush()` deletes the connection and frees the `hci_chan` structure via `kfree()`. The `tx_work` thread will then access the freed `chan->data_q` (a linked list), leading to an exploitable Use-After-Free that can be leveraged for arbitrary memory corruption and privilege escalation.
**Trigger Vectors & Accessibility:**
To trigger this vulnerability, an attacker must be able to initiate a Bluetooth device shutdown (`hci_dev_close_sync()`) concurrently with sending L2CAP traffic.
1. **Unprivileged / User Namespaces:** The device shutdown can be triggered via the `HCIDEVDOWN` or `HCIDEVRESET` ioctls. However, the `hci_sock_ioctl()` handler explicitly checks for global `CAP_NET_ADMIN` using `capable(CAP_NET_ADMIN)` rather than `ns_capable()`. Therefore, unprivileged users **cannot** trigger this bug, even from within a user namespace.
2. **Peripheral Trigger:** The shutdown sequence is also automatically invoked when a Bluetooth controller is physically removed from the system. An attacker with physical access could trigger the race condition by rapidly unplugging a USB Bluetooth dongle while a background process floods the device with L2CAP traffic.
In conclusion, this is an exploitable Use-After-Free vulnerability, but its attack surface is restricted to privileged users (root/CAP_NET_ADMIN) or attackers with physical access to plug/unplug peripheral devices.
|
| 6/2 |
2026/05/21 02:32 |
llm |
expert |
0m
Model:
Tokens:
input: 6143
output: 20
thoughts: 453
Thoughts: **Analyzing a Syzkaller Workqueue Bug**
Okay, so I've got this syzkaller bug report: "workqueue: cannot queue hci_tx_work on wq hci0". The report includes a warning: `WARNING: CPU: 0 PID: 18997 at kernel/workqueue.c:2255 __queue_work+0xd03/0x1160 kernel/workqueue.c:2254`. Immediately, my brain goes to "destroyed workqueue" territory. These workqueue warnings are usually a good indicator of something amiss, probably a race condition.
Looking at the provided call trace, I see `queue_work_on`, which is then invoked by `l2cap_chan_send`, `l2cap_sock_sendmsg`, and eventually, a `sendmmsg` system call path. This suggests a Bluetooth L2CAP socket operation is where things are going wrong. The core issue appears to be originating from the network stack (net/bluetooth), specifically around L2CAP.
I need to understand what's happening around `kernel/workqueue.c:2254`. This is where the warning is triggered. Time to check `__queue_work` in `kernel/workqueue.c` to see the logic. It's likely that we have a workqueue being destroyed or accessed after freeing. Or, maybe there's a reference counter or locking issue preventing the work from being queued. I'm going to look for any potential for a workqueue to be freed or stopped concurrently with a send operation or workqueue enqueue attempt. The warning message should give me a good hint to the problem.
|
| 7/2 |
2026/05/21 02:33 |
tool |
grepper |
0m
Args: map[Expression:cannot queue .* on wq]
Results: map[Output:kernel/workqueue.c=2233=static void __queue_work(int cpu, struct workqueue_struct *wq,
--
kernel/workqueue.c-2254- if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
kernel/workqueue.c:2255: WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n",
kernel/workqueue.c-2256- work->func, wq->name))) {
]
|
| 8/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 7125
output: 21
thoughts: 172
Thoughts: **Analyzing a Workqueue Issue**
Okay, so I'm seeing a warning in the kernel: a work item is being queued onto a workqueue that's in the process of being destroyed or drained. The specific check that's triggering this is the `unlikely()` condition, and it seems to be flagging a problem with how work items are being handled when the workqueue is shutting down. The code snippet shows this check: `if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) && WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n", work->func, wq->name)))`.
The warning identifies the work as `hci_tx_work` and the workqueue as `hci0`. My initial hunch is this is Bluetooth related. Now, I need to trace back where `hci_tx_work` is being submitted. I'll start by checking `l2cap_chan_send` in `net/bluetooth/l2cap_core.c:2610` to see if the work item is created there. It's a prime suspect for an L2CAP channel sending, and it's highly likely to queue something onto a workqueue.
|
| 9/2 |
2026/05/21 02:33 |
tool |
grepper |
0m
Args: map[Expression:l2cap_chan_send]
Results: map[Output:include/net/bluetooth/l2cap.h=957=int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu);
include/net/bluetooth/l2cap.h:958:int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
include/net/bluetooth/l2cap.h-959- const struct sockcm_cookie *sockc);
--
net/bluetooth/6lowpan.c=429=static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
--
net/bluetooth/6lowpan.c-446-
net/bluetooth/6lowpan.c:447: err = l2cap_chan_send(chan, &msg, skb->len, NULL);
net/bluetooth/6lowpan.c-448- if (err > 0) {
--
net/bluetooth/l2cap_core.c=2531=static void l2cap_tx_timestamp_seg(struct sk_buff_head *queue,
--
net/bluetooth/l2cap_core.c-2543-
net/bluetooth/l2cap_core.c:2544:int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
net/bluetooth/l2cap_core.c-2545- const struct sockcm_cookie *sockc)
--
net/bluetooth/l2cap_core.c-2655-}
net/bluetooth/l2cap_core.c:2656:EXPORT_SYMBOL_GPL(l2cap_chan_send);
net/bluetooth/l2cap_core.c-2657-
--
net/bluetooth/l2cap_sock.c=1104=static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg,
--
net/bluetooth/l2cap_sock.c-1138- l2cap_chan_lock(chan);
net/bluetooth/l2cap_sock.c:1139: err = l2cap_chan_send(chan, msg, len, &sockc);
net/bluetooth/l2cap_sock.c-1140- l2cap_chan_unlock(chan);
--
net/bluetooth/smp.c=589=static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
--
net/bluetooth/smp.c-610-
net/bluetooth/smp.c:611: l2cap_chan_send(chan, &msg, 1 + len, NULL);
net/bluetooth/smp.c-612-
]
|
| 10/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 8307
output: 47
thoughts: 15
Thoughts: Okay, let's dive into this `l2cap_chan_send` implementation.
|
| 11/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_chan_send]
Results: map[SourceCode:2544: int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
2545: const struct sockcm_cookie *sockc)
2546: {
2547: struct sk_buff *skb;
2548: int err;
2549: struct sk_buff_head seg_queue;
2550:
2551: if (!chan->conn)
2552: return -ENOTCONN;
2553:
2554: /* Connectionless channel */
2555: if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
2556: skb = l2cap_create_connless_pdu(chan, msg, len);
2557: if (IS_ERR(skb))
2558: return PTR_ERR(skb);
2559:
2560: l2cap_tx_timestamp(skb, sockc, len);
2561:
2562: l2cap_do_send(chan, skb);
2563: return len;
2564: }
2565:
2566: switch (chan->mode) {
2567: case L2CAP_MODE_LE_FLOWCTL:
2568: case L2CAP_MODE_EXT_FLOWCTL:
2569: /* Check outgoing MTU */
2570: if (len > chan->omtu)
2571: return -EMSGSIZE;
2572:
2573: __skb_queue_head_init(&seg_queue);
2574:
2575: err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
2576:
2577: if (chan->state != BT_CONNECTED) {
2578: __skb_queue_purge(&seg_queue);
2579: err = -ENOTCONN;
2580: }
2581:
2582: if (err)
2583: return err;
2584:
2585: l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
2586:
2587: skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
2588:
2589: l2cap_le_flowctl_send(chan);
2590:
2591: if (!chan->tx_credits)
2592: chan->ops->suspend(chan);
2593:
2594: err = len;
2595:
2596: break;
2597:
2598: case L2CAP_MODE_BASIC:
2599: /* Check outgoing MTU */
2600: if (len > chan->omtu)
2601: return -EMSGSIZE;
2602:
2603: /* Create a basic PDU */
2604: skb = l2cap_create_basic_pdu(chan, msg, len);
2605: if (IS_ERR(skb))
2606: return PTR_ERR(skb);
2607:
2608: l2cap_tx_timestamp(skb, sockc, len);
2609:
2610: l2cap_do_send(chan, skb);
2611: err = len;
2612: break;
2613:
2614: case L2CAP_MODE_ERTM:
2615: case L2CAP_MODE_STREAMING:
2616: /* Check outgoing MTU */
2617: if (len > chan->omtu) {
2618: err = -EMSGSIZE;
2619: break;
2620: }
2621:
2622: __skb_queue_head_init(&seg_queue);
2623:
2624: /* Do segmentation before calling in to the state machine,
2625: * since it's possible to block while waiting for memory
2626: * allocation.
2627: */
2628: err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
2629:
2630: if (err)
2631: break;
2632:
2633: if (chan->mode == L2CAP_MODE_ERTM) {
2634: /* TODO: ERTM mode timestamping */
2635: l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST);
2636: } else {
2637: l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
2638: l2cap_streaming_send(chan, &seg_queue);
2639: }
2640:
2641: err = len;
2642:
2643: /* If the skbs were not queued for sending, they'll still be in
2644: * seg_queue and need to be purged.
2645: */
2646: __skb_queue_purge(&seg_queue);
2647: break;
2648:
2649: default:
2650: BT_DBG("bad state %1.1x", chan->mode);
2651: err = -EBADFD;
2652: }
2653:
2654: return err;
2655: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 12/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 9970
output: 47
thoughts: 63
Thoughts: Okay, here's my interpretation of that.
**Analyzing the Bluetooth Stack's L2CAP Layer**
Right, so the call trace points me directly to `l2cap_chan_send` calling `queue_work_on` at line 2610. But wait a minute... line 2610, that's actually inside `l2cap_do_send`. I've got to check `l2cap_do_send` and understand its role here. That's where the real action's happening. I need to figure out *why* `queue_work_on` is being called from within `l2cap_do_send`. Is this related to flow control, retransmissions, or something else entirely? Time to dig into the details of that function. I have to see what's being queued and under what conditions.
|
| 13/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_do_send]
Results: map[SourceCode: 983: static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
984: {
985: struct hci_conn *hcon = chan->conn->hcon;
986: u16 flags;
987:
988: BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
989: skb->priority);
990:
991: /* Use NO_FLUSH for LE links (where this is the only option) or
992: * if the BR/EDR link supports it and flushing has not been
993: * explicitly requested (through FLAG_FLUSHABLE).
994: */
995: if (hcon->type == LE_LINK ||
996: (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
997: lmp_no_flush_capable(hcon->hdev)))
998: flags = ACL_START_NO_FLUSH;
999: else
1000: flags = ACL_START;
1001:
1002: bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
1003: hci_send_acl(chan->conn->hchan, skb, flags);
1004: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 14/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 10690
output: 45
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing `hci_send_acl`**
Alright, time to dig into this. "Let's check `hci_send_acl`." That means I'm immediately homing in on the function responsible for transmitting ACL (Asynchronous Connection-Less) data over the HCI (Host Controller Interface) in this Bluetooth stack. I'm likely either debugging a potential transmission issue, optimizing data flow, or perhaps reverse engineering a specific HCI command. This isn't just a casual glance; I'm going to want to understand its implementation details β how it handles the ACL data buffer, how it interacts with the underlying hardware, the error checking involved, and, crucially, how it manages concurrency and potential race conditions. I'll need to look at buffer allocations, DMA (Direct Memory Access) setups if applicable, and interrupt handling. The goal is pinpointing any bottlenecks, inefficiencies, or possible sources of data corruption or packet loss within the transmission process. This initial thought signifies that I have a very specific set of questions I need answered about this function, and I'm ready to get down to details.
|
| 15/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:hci_send_acl]
Results: map[SourceCode:3272: void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
3273: {
3274: struct hci_dev *hdev = chan->conn->hdev;
3275:
3276: BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
3277:
3278: hci_queue_acl(chan, &chan->data_q, skb, flags);
3279:
3280: queue_work(hdev->workqueue, &hdev->tx_work);
3281: }
SourceFile:net/bluetooth/hci_core.c]
|
| 16/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 11212
output: 43
thoughts: 152
Thoughts: **Debugging a Kernel Workqueue Issue**
Okay, so the warning message is pointing me directly to `queue_work(hdev->workqueue, &hdev->tx_work);` and it's calling `queue_work_on`. Right, that's where the work item is being enqueued. The problem is, that warning indicates that `hdev->workqueue` is likely in the process of being destroyed or drained while we're trying to add `hdev->tx_work` to it. It seems we have a race condition. This means `hci_send_acl` is being called at a bad time, specifically, when the host device, `hdev`, is in the process of unregistration or destruction, and therefore, its workqueue is also being taken down. Let me think...if the host device is being unregistered, the workqueue `hdev->workqueue` should be getting destroyed within `hci_unregister_dev`. I need to confirm that. Let's go check the implementation of `hci_unregister_dev` in `net/bluetooth/hci_core.c`.
|
| 17/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_unregister_dev]
Results: map[SourceCode:2689: /* Unregister HCI device */
2690: void hci_unregister_dev(struct hci_dev *hdev)
2691: {
2692: BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
2693:
2694: mutex_lock(&hdev->unregister_lock);
2695: hci_dev_set_flag(hdev, HCI_UNREGISTER);
2696: mutex_unlock(&hdev->unregister_lock);
2697:
2698: write_lock(&hci_dev_list_lock);
2699: list_del(&hdev->list);
2700: write_unlock(&hci_dev_list_lock);
2701:
2702: synchronize_srcu(&hdev->srcu);
2703: cleanup_srcu_struct(&hdev->srcu);
2704:
2705: disable_work_sync(&hdev->rx_work);
2706: disable_work_sync(&hdev->cmd_work);
2707: disable_work_sync(&hdev->tx_work);
2708: disable_work_sync(&hdev->power_on);
2709: disable_work_sync(&hdev->error_reset);
2710:
2711: hci_cmd_sync_clear(hdev);
2712:
2713: hci_unregister_suspend_notifier(hdev);
2714:
2715: hci_dev_do_close(hdev);
2716:
2717: if (!test_bit(HCI_INIT, &hdev->flags) &&
2718: !hci_dev_test_flag(hdev, HCI_SETUP) &&
2719: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
2720: hci_dev_lock(hdev);
2721: mgmt_index_removed(hdev);
2722: hci_dev_unlock(hdev);
2723: }
2724:
2725: /* mgmt_index_removed should take care of emptying the
2726: * pending list */
2727: BUG_ON(!list_empty(&hdev->mgmt_pending));
2728:
2729: hci_sock_dev_event(hdev, HCI_DEV_UNREG);
2730:
2731: if (hdev->rfkill) {
2732: rfkill_unregister(hdev->rfkill);
2733: rfkill_destroy(hdev->rfkill);
2734: }
2735:
2736: device_del(&hdev->dev);
2737: /* Actual cleanup is deferred until hci_release_dev(). */
2738: hci_dev_put(hdev);
2739: }
SourceFile:net/bluetooth/hci_core.c]
|
| 18/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 12469
output: 43
thoughts: 19
Thoughts: Here's a summary of my understanding, framed as my own thoughts:
**The Implications of `hci_release_dev`**
Okay, so I'm processing this code and I've hit a critical point. I'm focusing on the Bluetooth host controller driver and I see this function, `hci_release_dev`, and it just clicked: `hdev->workqueue` is destroyed *in* `hci_release_dev`. That means the workqueue associated with the HCI device gets nuked when the device is released. I need to keep this in mind. It has some serious implications for anything that might try to access the workqueue after the release process starts. I need to scrutinize all the places where the workqueue is used to be absolutely sure there are no race conditions or use-after-free situations. I need to double-check the timing of all other related functions too. This has implications for all pending and scheduled tasks. I need to make sure I understand the locking and synchronization surrounding the workqueue usage, particularly in the context of device detachment and module unloading. This is a potential source of some nasty bugs if I don't handle it with extreme care.
|
| 19/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_release_dev]
Results: map[SourceCode:2742: /* Release HCI device */
2743: void hci_release_dev(struct hci_dev *hdev)
2744: {
2745: debugfs_remove_recursive(hdev->debugfs);
2746: kfree_const(hdev->hw_info);
2747: kfree_const(hdev->fw_info);
2748:
2749: destroy_workqueue(hdev->workqueue);
2750: destroy_workqueue(hdev->req_workqueue);
2751:
2752: hci_dev_lock(hdev);
2753: hci_bdaddr_list_clear(&hdev->reject_list);
2754: hci_bdaddr_list_clear(&hdev->accept_list);
2755: hci_uuids_clear(hdev);
2756: hci_link_keys_clear(hdev);
2757: hci_smp_ltks_clear(hdev);
2758: hci_smp_irks_clear(hdev);
2759: hci_remote_oob_data_clear(hdev);
2760: hci_adv_instances_clear(hdev);
2761: hci_adv_monitors_clear(hdev);
2762: hci_bdaddr_list_clear(&hdev->le_accept_list);
2763: hci_bdaddr_list_clear(&hdev->le_resolv_list);
2764: hci_conn_params_clear_all(hdev);
2765: hci_discovery_filter_clear(hdev);
2766: hci_blocked_keys_clear(hdev);
2767: hci_codec_list_clear(&hdev->local_codecs);
2768: msft_release(hdev);
2769: hci_dev_unlock(hdev);
2770:
2771: ida_destroy(&hdev->unset_handle_ida);
2772: ida_free(&hci_index_ida, hdev->id);
2773: kfree_skb(hdev->sent_cmd);
2774: kfree_skb(hdev->req_skb);
2775: kfree_skb(hdev->recv_event);
2776: kfree(hdev);
2777: }
SourceFile:net/bluetooth/hci_core.c]
|
| 20/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 13449
output: 43
thoughts: 176
Thoughts: **Debugging a Kernel Workqueue Issue**
Okay, so I'm tracking down this pesky workqueue warning. The backtrace points to `destroy_workqueue(hdev->workqueue)` being called within `hci_release_dev`. The core problem seems to be a race condition: `queue_work` is called while `destroy_workqueue` is either in progress or has already started, leading to the warning. Now, I have to trace how this can even happen.
`hci_release_dev` is triggered when the reference count of `hdev` drops to zero, which makes sense. I see `l2cap_chan_send` is calling `hci_send_acl` in the backtrace. It *looks* like if `l2cap_chan_send` is still active, it implies that the `l2cap_chan` still holds a reference to `l2cap_conn`. The `l2cap_conn` in turn must have a reference to the `hci_conn`, which itself would have a reference to the `hci_dev`. This suggests an indirect dependency chain.
But wait a second... does `hci_conn` actually *hold* a reference to `hci_dev`? This is the crucial question. I need to verify how `hci_conn` is managed. I'll need to check the logic in `hci_conn_add` or a similar function to confirm if this reference is explicitly held and how that reference is managed. That is the next logical step.
|
| 21/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_add]
Results: map[SourceCode:1064: struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
1065: u8 role, u16 handle)
1066: {
1067: if (handle > HCI_CONN_HANDLE_MAX)
1068: return ERR_PTR(-EINVAL);
1069:
1070: return __hci_conn_add(hdev, type, dst, role, handle);
1071: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 22/2 |
2026/05/21 02:33 |
llm |
expert |
0m
Model:
Tokens:
input: 14158
output: 44
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deeply familiar with this kind of system:
**Diving into the Connection Add Function**
Alright, the next step is to examine `__hci_conn_add`. That's where connections get established at the HCI layer. I need to refresh my memory on the internal workings here β think through how that function is implemented, all the different checks it does, the data structures it manipulates, and how it interacts with the lower layers of the Bluetooth stack, the link control (LC) and link manager (LM) layers. I want to see if this is where the root cause might be. I need to quickly visualize the data flow; is it properly handling the new connection parameters? Any subtle error conditions that might be overlooked during normal operation? Need to confirm the sequence numbers and timeouts and whether it has properly handled the allocation of buffers for the connection context. Let's see how the connection handle is assigned and whether there might be a conflict. This is definitely a critical function, so thorough scrutiny is warranted. Let's dig in.
|
| 23/2 |
2026/05/21 02:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:__hci_conn_add]
Results: map[SourceCode: 914: static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
915: u8 role, u16 handle)
916: {
917: struct hci_conn *conn;
918:
919: switch (type) {
920: case ACL_LINK:
921: if (!hdev->acl_mtu)
922: return ERR_PTR(-ECONNREFUSED);
923: break;
924: case CIS_LINK:
925: case BIS_LINK:
926: case PA_LINK:
927: if (hdev->iso_mtu)
928: /* Dedicated ISO Buffer exists */
929: break;
930: fallthrough;
931: case LE_LINK:
932: if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
933: return ERR_PTR(-ECONNREFUSED);
934: if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
935: return ERR_PTR(-ECONNREFUSED);
936: break;
937: case SCO_LINK:
938: case ESCO_LINK:
939: if (!hdev->sco_pkts)
940: /* Controller does not support SCO or eSCO over HCI */
941: return ERR_PTR(-ECONNREFUSED);
942: break;
943: default:
944: return ERR_PTR(-ECONNREFUSED);
945: }
946:
947: bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
948:
949: conn = kzalloc(sizeof(*conn), GFP_KERNEL);
950: if (!conn)
951: return ERR_PTR(-ENOMEM);
952:
953: bacpy(&conn->dst, dst);
954: bacpy(&conn->src, &hdev->bdaddr);
955: conn->handle = handle;
956: conn->hdev = hdev;
957: conn->type = type;
958: conn->role = role;
959: conn->mode = HCI_CM_ACTIVE;
960: conn->state = BT_OPEN;
961: conn->auth_type = HCI_AT_GENERAL_BONDING;
962: conn->io_capability = hdev->io_capability;
963: conn->remote_auth = 0xff;
964: conn->key_type = 0xff;
965: conn->rssi = HCI_RSSI_INVALID;
966: conn->tx_power = HCI_TX_POWER_INVALID;
967: conn->max_tx_power = HCI_TX_POWER_INVALID;
968: conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
969: conn->sid = HCI_SID_INVALID;
970:
971: set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
972: conn->disc_timeout = HCI_DISCONN_TIMEOUT;
973:
974: /* Set Default Authenticated payload timeout to 30s */
975: conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
976:
977: if (conn->role == HCI_ROLE_MASTER)
978: conn->out = true;
979:
980: switch (type) {
981: case ACL_LINK:
982: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
983: conn->mtu = hdev->acl_mtu;
984: break;
985: case LE_LINK:
986: /* conn->src should reflect the local identity address */
987: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
988: conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
989: break;
990: case CIS_LINK:
991: case BIS_LINK:
992: case PA_LINK:
993: /* conn->src should reflect the local identity address */
994: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
995:
996: /* set proper cleanup function */
997: if (!bacmp(dst, BDADDR_ANY))
998: conn->cleanup = bis_cleanup;
999: else if (conn->role == HCI_ROLE_MASTER)
1000: conn->cleanup = cis_cleanup;
1001:
1002: conn->mtu = hdev->iso_mtu ? hdev->iso_mtu :
1003: hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
1004: break;
1005: case SCO_LINK:
1006: if (lmp_esco_capable(hdev))
1007: conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
1008: (hdev->esco_type & EDR_ESCO_MASK);
1009: else
1010: conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
1011:
1012: conn->mtu = hdev->sco_mtu;
1013: break;
1014: case ESCO_LINK:
1015: conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
1016: conn->mtu = hdev->sco_mtu;
1017: break;
1018: }
1019:
1020: skb_queue_head_init(&conn->data_q);
1021: skb_queue_head_init(&conn->tx_q.queue);
1022:
1023: INIT_LIST_HEAD(&conn->chan_list);
1024: INIT_LIST_HEAD(&conn->link_list);
1025:
1026: INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
1027: INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
1028: INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
1029: INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
1030:
1031: atomic_set(&conn->refcnt, 0);
1032:
1033: hci_dev_hold(hdev);
1034:
1035: hci_conn_hash_add(hdev, conn);
1036:
1037: /* The SCO and eSCO connections will only be notified when their
1038: * setup has been completed. This is different to ACL links which
1039: * can be notified right away.
1040: */
1041: if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
1042: if (hdev->notify)
1043: hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
1044: }
1045:
1046: hci_conn_init_sysfs(conn);
1047: return conn;
1048: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 24/2 |
2026/05/21 02:33 |
llm |
expert |
1m
Model:
Tokens:
input: 16650
output: 45
thoughts: 106
Thoughts: **Reflecting on Bluetooth Device Management**
Okay, so I'm tracing the lifecycle of a Bluetooth device (`hdev`) within this connection management system. I see that `hci_dev_hold(hdev)` gets called within `__hci_conn_add`. That makes sense; we're establishing a connection, so we need to ensure the device remains available. Then, on the other side, `hci_dev_put(hdev)` is called within `hci_conn_del`, when the connection is terminated, which is consistent.
Therefore, as long as an `hci_conn` struct exists, the reference count of the corresponding `hdev` should be greater than zero. Now, this is important. If the reference count is greater than zero, then `hci_release_dev` should *not* be able to execute. This is exactly what I'd expect; we don't want to release a device while it's still actively managing a connection. Let me quickly double-check the `hci_dev_put` implementation to confirm that this logic holds.
|
| 25/2 |
2026/05/21 02:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/net/bluetooth/hci_core.h IncludeLines:true Name:hci_dev_put]
Results: map[SourceCode:1680: /* ----- HCI Devices ----- */
1681: static inline void hci_dev_put(struct hci_dev *d)
1682: {
1683: BT_DBG("%s orig refcnt %d", d->name,
1684: kref_read(&d->dev.kobj.kref));
1685:
1686: put_device(&d->dev);
1687: }
SourceFile:include/net/bluetooth/hci_core.h]
|
| 26/2 |
2026/05/21 02:34 |
llm |
expert |
1m
Model:
Tokens:
input: 17182
output: 19
thoughts: 19
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating a Release Function**
Alright, so I'm looking at this `hci_release_dev` function, and it's got me thinking. My first impression is, is this *the* release function we're using for `d->dev`? It's a key question, and I'm immediately trying to connect the dots between the function's name and its potential purpose within the device structure. My primary focus is on how resources associated with `d->dev` are managed after they are no longer needed. Is this the right one? Let's check the code and find out!
|
| 27/2 |
2026/05/21 02:35 |
tool |
grepper |
0m
Args: map[Expression:hci_release_dev]
Results: map[Output:include/net/bluetooth/hci_core.h=1731=void hci_unregister_dev(struct hci_dev *hdev);
include/net/bluetooth/hci_core.h:1732:void hci_release_dev(struct hci_dev *hdev);
include/net/bluetooth/hci_core.h-1733-int hci_register_suspend_notifier(struct hci_dev *hdev);
--
net/bluetooth/hci_core.c=2690=void hci_unregister_dev(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-2736- device_del(&hdev->dev);
net/bluetooth/hci_core.c:2737: /* Actual cleanup is deferred until hci_release_dev(). */
net/bluetooth/hci_core.c-2738- hci_dev_put(hdev);
--
net/bluetooth/hci_core.c=2740=EXPORT_SYMBOL(hci_unregister_dev);
--
net/bluetooth/hci_core.c-2742-/* Release HCI device */
net/bluetooth/hci_core.c:2743:void hci_release_dev(struct hci_dev *hdev)
net/bluetooth/hci_core.c-2744-{
--
net/bluetooth/hci_core.c-2777-}
net/bluetooth/hci_core.c:2778:EXPORT_SYMBOL(hci_release_dev);
net/bluetooth/hci_core.c-2779-
--
net/bluetooth/hci_sysfs.c=82=static void bt_host_release(struct device *dev)
--
net/bluetooth/hci_sysfs.c-86- if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
net/bluetooth/hci_sysfs.c:87: hci_release_dev(hdev);
net/bluetooth/hci_sysfs.c-88- else
]
|
| 28/2 |
2026/05/21 02:35 |
llm |
expert |
0m
Model:
Tokens:
input: 17872
output: 21
thoughts: 186
Thoughts: **Debugging a Bluetooth Device Release**
Okay, so I'm tracing the release process for this Bluetooth device, let's call it `hdev`. It looks like `hci_release_dev` is the crucial function, and it's called from `bt_host_release`, which is the release function for `hdev->dev`. Makes sense β when the underlying device's reference count hits zero, we clean up the HCI-specific bits.
Now, a key point: if `hci_conn` (presumably a connection object) holds a reference to `hdev`, then `hdev`'s reference count *shouldn't* drop to zero while that connection is active. Therefore, `hci_release_dev` shouldn't be called while an active connection exists. This is fundamental for resource management, right?
But then I see this warning: "workqueue: cannot queue hci_tx_work on wq hci0". This is a red flag. It implies `hdev->workqueue` is being destroyed. Wait a second, isn't `destroy_workqueue` called *inside* `hci_release_dev`? That's what I was assuming. Is it possible that `hdev->workqueue` is being destroyed somewhere else, before `hci_release_dev` is even called? This would be a significant concurrency issue. This is where I need to dig deeper. I'm going to have to review the surrounding code more carefully.
|
| 29/2 |
2026/05/21 02:35 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue.*workqueue]
Results: map[Output:
Full output is too long, showing 500 out of 1101 lines.
Use more precise expression if possible.
[block/bio.c=1753=void bioset_exit(struct bio_set *bs)
--
block/bio.c-1756- if (bs->rescue_workqueue)
block/bio.c:1757: destroy_workqueue(bs->rescue_workqueue);
block/bio.c-1758- bs->rescue_workqueue = NULL;
--
drivers/auxdisplay/cfag12864b.c=354=static void __exit cfag12864b_exit(void)
--
drivers/auxdisplay/cfag12864b.c-357- cfag12864b_off();
drivers/auxdisplay/cfag12864b.c:358: destroy_workqueue(cfag12864b_workqueue);
drivers/auxdisplay/cfag12864b.c-359- kfree(cfag12864b_cache);
--
drivers/block/loop.c=1752=static void lo_free_disk(struct gendisk *disk)
--
drivers/block/loop.c-1756- if (lo->workqueue)
drivers/block/loop.c:1757: destroy_workqueue(lo->workqueue);
drivers/block/loop.c-1758- loop_free_idle_workers(lo, true);
--
drivers/block/zloop.c=698=static void zloop_free_disk(struct gendisk *disk)
--
drivers/block/zloop.c-713- fput(zlo->data_dir);
drivers/block/zloop.c:714: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-715- kfree(zlo->base_dir);
--
drivers/block/zloop.c=883=static int zloop_ctl_add(struct zloop_options *opts)
--
drivers/block/zloop.c-1040-out_destroy_workqueue:
drivers/block/zloop.c:1041: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-1042-out_free_idr:
--
drivers/bluetooth/btintel_pcie.c=2529=static void btintel_pcie_remove(struct pci_dev *pdev)
--
drivers/bluetooth/btintel_pcie.c-2552-
drivers/bluetooth/btintel_pcie.c:2553: destroy_workqueue(data->workqueue);
drivers/bluetooth/btintel_pcie.c-2554-
--
drivers/bluetooth/hci_qca.c=718=static int qca_close(struct hci_uart *hu)
--
drivers/bluetooth/hci_qca.c-736- timer_shutdown_sync(&qca->wake_retrans_timer);
drivers/bluetooth/hci_qca.c:737: destroy_workqueue(qca->workqueue);
drivers/bluetooth/hci_qca.c-738- qca->hu = NULL;
--
drivers/cdx/controller/mcdi.c=132=void cdx_mcdi_finish(struct cdx_mcdi *cdx)
--
drivers/cdx/controller/mcdi.c-141-
drivers/cdx/controller/mcdi.c:142: destroy_workqueue(mcdi->workqueue);
drivers/cdx/controller/mcdi.c-143- kfree(cdx->mcdi);
--
drivers/char/tpm/tpm_vtpm_proxy.c=687=static int __init vtpm_module_init(void)
--
drivers/char/tpm/tpm_vtpm_proxy.c-699- pr_err("couldn't create vtpmx device\n");
drivers/char/tpm/tpm_vtpm_proxy.c:700: destroy_workqueue(workqueue);
drivers/char/tpm/tpm_vtpm_proxy.c-701- }
--
drivers/char/tpm/tpm_vtpm_proxy.c=706=static void __exit vtpm_module_exit(void)
drivers/char/tpm/tpm_vtpm_proxy.c-707-{
drivers/char/tpm/tpm_vtpm_proxy.c:708: destroy_workqueue(workqueue);
drivers/char/tpm/tpm_vtpm_proxy.c-709- misc_deregister(&vtpmx_miscdev);
--
drivers/crypto/inside-secure/safexcel.c=1389=static int safexcel_probe_generic(void *pdev,
--
drivers/crypto/inside-secure/safexcel.c-1704- if (priv->ring[i].workqueue)
drivers/crypto/inside-secure/safexcel.c:1705: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1706- }
--
drivers/crypto/inside-secure/safexcel.c=1799=static void safexcel_remove(struct platform_device *pdev)
--
drivers/crypto/inside-secure/safexcel.c-1811- irq_set_affinity_hint(priv->ring[i].irq, NULL);
drivers/crypto/inside-secure/safexcel.c:1812: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1813- }
--
drivers/crypto/inside-secure/safexcel.c=1967=static void safexcel_pci_remove(struct pci_dev *pdev)
--
drivers/crypto/inside-secure/safexcel.c-1974- for (i = 0; i < priv->config.rings; i++)
drivers/crypto/inside-secure/safexcel.c:1975: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1976-
--
drivers/firewire/core-transaction.c=1398=static int __init fw_core_init(void)
--
drivers/firewire/core-transaction.c-1407- if (ret < 0) {
drivers/firewire/core-transaction.c:1408: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1409- return ret;
--
drivers/firewire/core-transaction.c-1414- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1415: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1416- return fw_cdev_major;
--
drivers/firewire/core-transaction.c=1428=static void __exit fw_core_cleanup(void)
--
drivers/firewire/core-transaction.c-1431- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1432: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1433- xa_destroy(&fw_device_xa);
--
drivers/firewire/ohci.c=3959=static void __exit fw_ohci_cleanup(void)
--
drivers/firewire/ohci.c-3961- pci_unregister_driver(&fw_ohci_pci_driver);
drivers/firewire/ohci.c:3962: destroy_workqueue(selfid_workqueue);
drivers/firewire/ohci.c-3963-}
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c=2177=static void amdgpu_dm_fini(struct amdgpu_device *adev)
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2181- if (adev->dm.vblank_control_workqueue) {
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:2182: destroy_workqueue(adev->dm.vblank_control_workqueue);
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2183- adev->dm.vblank_control_workqueue = NULL;
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2619=static int anx7625_i2c_probe(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2760- if (platform->workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2761: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2762-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2764- if (platform->hdcp_workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2765: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2766-
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2770=static void anx7625_i2c_remove(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2776- if (platform->pdata.intp_irq)
drivers/gpu/drm/bridge/analogix/anx7625.c:2777: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2778-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2780- cancel_delayed_work(&platform->hdcp_work);
drivers/gpu/drm/bridge/analogix/anx7625.c:2781: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2782- }
--
drivers/gpu/drm/msm/dsi/dsi_host.c=2035=void msm_dsi_host_destroy(struct mipi_dsi_host *host)
--
drivers/gpu/drm/msm/dsi/dsi_host.c-2040- if (msm_host->workqueue) {
drivers/gpu/drm/msm/dsi/dsi_host.c:2041: destroy_workqueue(msm_host->workqueue);
drivers/gpu/drm/msm/dsi/dsi_host.c-2042- msm_host->workqueue = NULL;
--
drivers/infiniband/hw/hfi1/init.c=737=static int create_workqueues(struct hfi1_devdata *dd)
--
drivers/infiniband/hw/hfi1/init.c-787-/**
drivers/infiniband/hw/hfi1/init.c:788: * destroy_workqueues - destroy per port workqueues
drivers/infiniband/hw/hfi1/init.c-789- * @dd: the hfi1_ib device
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2696=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2755- ib_sa_unregister_client(&ipoib_sa_client);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2756: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2757-
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2764=static void __exit ipoib_cleanup_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2772- ipoib_unregister_debugfs();
drivers/infiniband/ulp/ipoib/ipoib_main.c:2773: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2774-}
--
drivers/input/rmi4/rmi_f54.c=659=static int rmi_f54_probe(struct rmi_function *fn)
--
drivers/input/rmi4/rmi_f54.c-733- cancel_delayed_work_sync(&f54->work);
drivers/input/rmi4/rmi_f54.c:734: destroy_workqueue(f54->workqueue);
drivers/input/rmi4/rmi_f54.c-735- return ret;
--
drivers/input/rmi4/rmi_f54.c=738=static void rmi_f54_remove(struct rmi_function *fn)
--
drivers/input/rmi4/rmi_f54.c-743- v4l2_device_unregister(&f54->v4l2);
drivers/input/rmi4/rmi_f54.c:744: destroy_workqueue(f54->workqueue);
drivers/input/rmi4/rmi_f54.c-745-}
--
drivers/md/dm.c=203=static int __init local_init(void)
--
drivers/md/dm.c-227-out_free_workqueue:
drivers/md/dm.c:228: destroy_workqueue(deferred_remove_workqueue);
drivers/md/dm.c-229-out_uevent_exit:
--
drivers/md/dm.c=235=static void local_exit(void)
drivers/md/dm.c-236-{
drivers/md/dm.c:237: destroy_workqueue(deferred_remove_workqueue);
drivers/md/dm.c-238-
--
drivers/media/platform/amphion/vpu_core.c=250=static int vpu_core_register(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-289- if (core->workqueue) {
drivers/media/platform/amphion/vpu_core.c:290: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-291- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_core.c=305=static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-316- cancel_delayed_work_sync(&core->msg_delayed_work);
drivers/media/platform/amphion/vpu_core.c:317: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-318- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=706=static int vpu_v4l2_release(struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-714- cancel_work_sync(&inst->msg_work);
drivers/media/platform/amphion/vpu_v4l2.c:715: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-716- inst->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=729=int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-781- if (ret) {
drivers/media/platform/amphion/vpu_v4l2.c:782: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-783- inst->workqueue = NULL;
--
drivers/media/platform/chips-media/coda/coda-common.c=3145=static int coda_probe(struct platform_device *pdev)
--
drivers/media/platform/chips-media/coda/coda-common.c-3294- pm_runtime_put_noidle(&pdev->dev);
drivers/media/platform/chips-media/coda/coda-common.c:3295: destroy_workqueue(dev->workqueue);
drivers/media/platform/chips-media/coda/coda-common.c-3296-err_v4l2_register:
--
drivers/media/platform/chips-media/coda/coda-common.c=3301=static void coda_remove(struct platform_device *pdev)
--
drivers/media/platform/chips-media/coda/coda-common.c-3313- v4l2_device_unregister(&dev->v4l2_dev);
drivers/media/platform/chips-media/coda/coda-common.c:3314: destroy_workqueue(dev->workqueue);
drivers/media/platform/chips-media/coda/coda-common.c-3315- if (dev->iram.vaddr)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c=353=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-520- media_device_cleanup(&dev->mdev_dec);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:521: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-522-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-527- if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch))
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:528: destroy_workqueue(dev->core_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-529-err_res:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c=567=static void mtk_vcodec_dec_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-570-
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:571: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-572-
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=234=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-364-err_enc_reg:
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:365: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-366-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=457=static void mtk_vcodec_enc_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-460-
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:461: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-462- if (dev->m2m_dev_enc)
--
drivers/media/test-drivers/vivid/vivid-core.c=2246=static int __init vivid_init(void)
--
drivers/media/test-drivers/vivid/vivid-core.c-2311-destroy_hdmi_wq:
drivers/media/test-drivers/vivid/vivid-core.c:2312: destroy_workqueue(update_hdmi_ctrls_workqueue);
drivers/media/test-drivers/vivid/vivid-core.c-2313-unreg_driver:
--
drivers/media/test-drivers/vivid/vivid-core.c=2325=static void __exit vivid_exit(void)
--
drivers/media/test-drivers/vivid/vivid-core.c-2330- }
drivers/media/test-drivers/vivid/vivid-core.c:2331: destroy_workqueue(update_svid_ctrls_workqueue);
drivers/media/test-drivers/vivid/vivid-core.c:2332: destroy_workqueue(update_hdmi_ctrls_workqueue);
drivers/media/test-drivers/vivid/vivid-core.c-2333- platform_driver_unregister(&vivid_pdrv);
--
drivers/memstick/core/memstick.c=629=static int __init memstick_init(void)
--
drivers/memstick/core/memstick.c-649-error_destroy_workqueue:
drivers/memstick/core/memstick.c:650: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-651-
--
drivers/memstick/core/memstick.c=655=static void __exit memstick_exit(void)
--
drivers/memstick/core/memstick.c-658- bus_unregister(&memstick_bus_type);
drivers/memstick/core/memstick.c:659: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-660- idr_destroy(&memstick_host_idr);
--
drivers/mfd/ezx-pcap.c=360=static void ezx_pcap_remove(struct spi_device *spi)
--
drivers/mfd/ezx-pcap.c-378-
drivers/mfd/ezx-pcap.c:379: destroy_workqueue(pcap->workqueue);
drivers/mfd/ezx-pcap.c-380-}
--
drivers/mfd/ezx-pcap.c=382=static int ezx_pcap_probe(struct spi_device *spi)
--
drivers/mfd/ezx-pcap.c-471-/* destroy_workqueue: */
drivers/mfd/ezx-pcap.c:472: destroy_workqueue(pcap->workqueue);
drivers/mfd/ezx-pcap.c-473-ret:
--
drivers/misc/tifm_core.c=327=static int __init tifm_init(void)
--
drivers/misc/tifm_core.c-346-err_out_wq:
drivers/misc/tifm_core.c:347: destroy_workqueue(workqueue);
drivers/misc/tifm_core.c-348-
--
drivers/misc/tifm_core.c=352=static void __exit tifm_exit(void)
--
drivers/misc/tifm_core.c-355- bus_unregister(&tifm_bus_type);
drivers/misc/tifm_core.c:356: destroy_workqueue(workqueue);
drivers/misc/tifm_core.c-357-}
--
drivers/mmc/host/vub300.c=2419=static int __init vub300_init(void)
--
drivers/mmc/host/vub300.c-2449-out4:
drivers/mmc/host/vub300.c:2450: destroy_workqueue(deadworkqueue);
drivers/mmc/host/vub300.c-2451-out3:
drivers/mmc/host/vub300.c:2452: destroy_workqueue(pollworkqueue);
drivers/mmc/host/vub300.c-2453-out2:
drivers/mmc/host/vub300.c:2454: destroy_workqueue(cmndworkqueue);
drivers/mmc/host/vub300.c-2455-out1:
--
drivers/mmc/host/vub300.c=2459=static void __exit vub300_exit(void)
--
drivers/mmc/host/vub300.c-2464- flush_workqueue(deadworkqueue);
drivers/mmc/host/vub300.c:2465: destroy_workqueue(cmndworkqueue);
drivers/mmc/host/vub300.c:2466: destroy_workqueue(pollworkqueue);
drivers/mmc/host/vub300.c:2467: destroy_workqueue(deadworkqueue);
drivers/mmc/host/vub300.c-2468-}
--
drivers/mtd/nand/raw/r852.c=844=static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
--
drivers/mtd/nand/raw/r852.c-954-error10:
drivers/mtd/nand/raw/r852.c:955: destroy_workqueue(dev->card_workqueue);
drivers/mtd/nand/raw/r852.c-956-error9:
--
drivers/mtd/nand/raw/r852.c=976=static void r852_remove(struct pci_dev *pci_dev)
--
drivers/mtd/nand/raw/r852.c-982- cancel_delayed_work_sync(&dev->card_detect_work);
drivers/mtd/nand/raw/r852.c:983: destroy_workqueue(dev->card_workqueue);
drivers/mtd/nand/raw/r852.c-984-
--
drivers/mtd/sm_ftl.c=1273=static __init int sm_module_init(void)
--
drivers/mtd/sm_ftl.c-1282- if (error)
drivers/mtd/sm_ftl.c:1283: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1284- return error;
--
drivers/mtd/sm_ftl.c=1288=static void __exit sm_module_exit(void)
drivers/mtd/sm_ftl.c-1289-{
drivers/mtd/sm_ftl.c:1290: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1291- deregister_mtd_blktrans(&sm_ftl_ops);
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1532=static int xgbe_open(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1610-err_an_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1611: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1612-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1613-err_dev_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1614: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1615-
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1619=static int xgbe_close(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1631-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1632: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1633-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1634: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1635-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3002=static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3128-err_destroy_ordered_workqueue:
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3129: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3130-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3248=static void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3255-
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3256: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3257-
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=919=static int enetc4_pf_netdev_create(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-973-err_reg_netdev:
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:974: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-975-err_wq_init:
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=987=static void enetc4_pf_netdev_destroy(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-993- cancel_work(&si->rx_mode_task);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:994: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-995- enetc4_link_deinit(priv);
--
drivers/net/ethernet/intel/fm10k/fm10k_main.c=32=static int __init fm10k_init_module(void)
--
drivers/net/ethernet/intel/fm10k/fm10k_main.c-49- fm10k_dbg_exit();
drivers/net/ethernet/intel/fm10k/fm10k_main.c:50: destroy_workqueue(fm10k_workqueue);
drivers/net/ethernet/intel/fm10k/fm10k_main.c-51- }
--
drivers/net/ethernet/intel/fm10k/fm10k_main.c=63=static void __exit fm10k_exit_module(void)
--
drivers/net/ethernet/intel/fm10k/fm10k_main.c-69- /* destroy driver workqueue */
drivers/net/ethernet/intel/fm10k/fm10k_main.c:70: destroy_workqueue(fm10k_workqueue);
drivers/net/ethernet/intel/fm10k/fm10k_main.c-71-}
--
drivers/net/ethernet/mellanox/mlx4/en_main.c=237=static void mlx4_en_remove(struct auxiliary_device *adev)
--
drivers/net/ethernet/mellanox/mlx4/en_main.c-253-
drivers/net/ethernet/mellanox/mlx4/en_main.c:254: destroy_workqueue(mdev->workqueue);
drivers/net/ethernet/mellanox/mlx4/en_main.c-255- (void) mlx4_mr_free(dev, &mdev->mr);
--
drivers/net/ethernet/qlogic/qla3xxx.c=3901=static void ql3xxx_remove(struct pci_dev *pdev)
--
drivers/net/ethernet/qlogic/qla3xxx.c-3912- cancel_delayed_work(&qdev->tx_timeout_work);
drivers/net/ethernet/qlogic/qla3xxx.c:3913: destroy_workqueue(qdev->workqueue);
drivers/net/ethernet/qlogic/qla3xxx.c-3914- qdev->workqueue = NULL;
--
drivers/net/ethernet/sfc/efx_common.c=130=void efx_destroy_reset_workqueue(void)
--
drivers/net/ethernet/sfc/efx_common.c-132- if (reset_workqueue) {
drivers/net/ethernet/sfc/efx_common.c:133: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/efx_common.c-134- reset_workqueue = NULL;
--
drivers/net/ethernet/sfc/efx_common.c=1034=void efx_fini_struct(struct efx_nic *efx)
--
drivers/net/ethernet/sfc/efx_common.c-1044- if (efx->workqueue) {
drivers/net/ethernet/sfc/efx_common.c:1045: destroy_workqueue(efx->workqueue);
drivers/net/ethernet/sfc/efx_common.c-1046- efx->workqueue = NULL;
--
drivers/net/ethernet/sfc/falcon/efx.c=2677=static void ef4_fini_struct(struct ef4_nic *efx)
--
drivers/net/ethernet/sfc/falcon/efx.c-2686- if (efx->workqueue) {
drivers/net/ethernet/sfc/falcon/efx.c:2687: destroy_workqueue(efx->workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-2688- efx->workqueue = NULL;
--
drivers/net/ethernet/sfc/falcon/efx.c=3160=static int __init ef4_init_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3182- err_pci:
drivers/net/ethernet/sfc/falcon/efx.c:3183: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3184- err_reset:
--
drivers/net/ethernet/sfc/falcon/efx.c=3190=static void __exit ef4_exit_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3194- pci_unregister_driver(&ef4_pci_driver);
drivers/net/ethernet/sfc/falcon/efx.c:3195: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3196- unregister_netdevice_notifier(&ef4_netdev_notifier);
--
drivers/net/ethernet/sfc/siena/efx_common.c=134=void efx_siena_destroy_reset_workqueue(void)
--
drivers/net/ethernet/sfc/siena/efx_common.c-136- if (reset_workqueue) {
drivers/net/ethernet/sfc/siena/efx_common.c:137: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/siena/efx_common.c-138- reset_workqueue = NULL;
--
drivers/net/ethernet/sfc/siena/efx_common.c=1059=void efx_siena_fini_struct(struct efx_nic *efx)
--
drivers/net/ethernet/sfc/siena/efx_common.c-1069- if (efx->workqueue) {
drivers/net/ethernet/sfc/siena/efx_common.c:1070: destroy_workqueue(efx->workqueue);
drivers/net/ethernet/sfc/siena/efx_common.c-1071- efx->workqueue = NULL;
--
drivers/net/ethernet/sfc/siena/siena_sriov.c=1590=void efx_fini_sriov(void)
drivers/net/ethernet/sfc/siena/siena_sriov.c-1591-{
drivers/net/ethernet/sfc/siena/siena_sriov.c:1592: destroy_workqueue(vfdi_workqueue);
drivers/net/ethernet/sfc/siena/siena_sriov.c-1593-}
--
drivers/net/ieee802154/ca8210.c=2842=static int ca8210_dev_com_init(struct ca8210_priv *priv)
--
drivers/net/ieee802154/ca8210.c-2852- dev_crit(&priv->spi->dev, "alloc of irq_workqueue failed!\n");
drivers/net/ieee802154/ca8210.c:2853: destroy_workqueue(priv->mlme_workqueue);
drivers/net/ieee802154/ca8210.c-2854- return -ENOMEM;
--
drivers/net/ieee802154/ca8210.c=2864=static void ca8210_dev_com_clear(struct ca8210_priv *priv)
drivers/net/ieee802154/ca8210.c-2865-{
drivers/net/ieee802154/ca8210.c:2866: destroy_workqueue(priv->mlme_workqueue);
drivers/net/ieee802154/ca8210.c:2867: destroy_workqueue(priv->irq_workqueue);
drivers/net/ieee802154/ca8210.c-2868-}
--
drivers/net/wireless/ath/ath10k/core.c=3624=struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
--
drivers/net/wireless/ath/ath10k/core.c-3758-err_free_tx_complete:
drivers/net/wireless/ath/ath10k/core.c:3759: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3760-err_free_aux_wq:
drivers/net/wireless/ath/ath10k/core.c:3761: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3762-err_free_wq:
drivers/net/wireless/ath/ath10k/core.c:3763: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3764-err_free_mac:
--
drivers/net/wireless/ath/ath10k/core.c=3771=void ath10k_core_destroy(struct ath10k *ar)
drivers/net/wireless/ath/ath10k/core.c-3772-{
drivers/net/wireless/ath/ath10k/core.c:3773: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3774-
drivers/net/wireless/ath/ath10k/core.c:3775: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3776-
drivers/net/wireless/ath/ath10k/core.c:3777: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3778-
--
drivers/net/wireless/ath/ath10k/sdio.c=2511=static int ath10k_sdio_probe(struct sdio_func *func,
--
drivers/net/wireless/ath/ath10k/sdio.c-2631-err_free_wq:
drivers/net/wireless/ath/ath10k/sdio.c:2632: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2633-err_core_destroy:
--
drivers/net/wireless/ath/ath10k/sdio.c=2639=static void ath10k_sdio_remove(struct sdio_func *func)
--
drivers/net/wireless/ath/ath10k/sdio.c-2651-
drivers/net/wireless/ath/ath10k/sdio.c:2652: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2653-
--
drivers/net/wireless/ath/ath11k/core.c=2614=void ath11k_core_free(struct ath11k_base *ab)
drivers/net/wireless/ath/ath11k/core.c-2615-{
drivers/net/wireless/ath/ath11k/core.c:2616: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath11k/core.c:2617: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2618-
--
drivers/net/wireless/ath/ath11k/core.c=2623=struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
--
drivers/net/wireless/ath/ath11k/core.c-2668-err_free_wq:
drivers/net/wireless/ath/ath11k/core.c:2669: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2670-err_sc_free:
--
drivers/net/wireless/ath/ath12k/core.c=2215=void ath12k_core_free(struct ath12k_base *ab)
--
drivers/net/wireless/ath/ath12k/core.c-2217- timer_delete_sync(&ab->rx_replenish_retry);
drivers/net/wireless/ath/ath12k/core.c:2218: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath12k/core.c:2219: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath12k/core.c-2220- kfree(ab);
--
]
]
|
| 30/2 |
2026/05/21 02:35 |
llm |
expert |
0m
Model:
Tokens:
input: 30250
output: 31
thoughts: 22
Thoughts: Here's my analysis of the provided thought process, tailored for an expert audience:
**Analyzing `destroy_workqueue` in `net/bluetooth/hci_core.c`**
Okay, so the mental focus here is on `destroy_workqueue` within the Bluetooth HCI core code. My initial thought process would likely involve several parallel tracks of inquiry, given the context. First, Iβd be mentally running through the typical uses of workqueues in this subsystem. I'd be trying to recall the asynchronous operations commonly found in the HCI core: handling incoming events, managing command responses, device initialization/shutdown, and possibly error recovery. Each of these would immediately suggest potential pitfalls if the workqueue isn't handled correctly during destruction.
Next, I'd immediately jump to thinking about potential race conditions. I'm imagining scenarios where a work item might be queued just as the `destroy_workqueue` call is issued. Is there a graceful shutdown path? Does the code properly drain the queue, or does it risk leaving incomplete work? Are there any associated locks or synchronization primitives that might cause deadlocks or unexpected behavior during the destruction process? I'd be expecting to see a combination of `flush_workqueue` and potentially a more aggressive `cancel_work_sync` (or something similar) to ensure all pending work is completed, canceled, or properly handled before the queue is deallocated.
Furthermore, I'd probably be thinking about how `destroy_workqueue` interacts with any associated device or module initialization/uninitialization routines. Is this called as part of a Bluetooth adapter's removal, or is it triggered by some other system event? The timing and context are crucial. If it's part of device removal, I'd expect careful consideration of the dependency on other HCI core structures. If it's triggered elsewhere, I'd be thinking about how the system might be managing references to the workqueue, and if there are any potential double-free scenarios or memory leaks that could be introduced. I'd mentally start sketching out the control flow, ensuring it handles error conditions gracefully (e.g., if the workqueue is already destroyed). Iβd be focusing on confirming all pointers are valid and that there are no unexpected side effects. My primary concern here is safety.
|
| 31/2 |
2026/05/21 02:36 |
tool |
grepper |
0m
Error:
Args: map[Expression:destroy_workqueue.*workqueue.*net/bluetooth/hci_core.c]
Results:
|
| 32/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 30794
output: 18
thoughts: 19
Thoughts: Here's my thought process, as if I were analyzing that prompt:
**Initial Assessment: Bluetooth Module Debug**
Okay, so I'm looking at something related to `destroy_workqueue` within the Bluetooth networking subsystem. My initial thought is, "Where's the problem? What's going on that I'm looking for a destruction function?". I need to figure out what's breaking here.
Then, I realize I need to get to the core issue quickly and efficiently. Time is of the essence. My expert knowledge kicks in: I know the `net/bluetooth/` directory structure pretty well and how to navigate it, so my next thought is: Why waste time with any deep analysis, let's go straight for the jugular. A simple `grep` is all that's needed to identify the exact location of `destroy_workqueue` within the specific Bluetooth code base. This will save a lot of debugging time. I'll get the context I need immediately. A targeted search is way more efficient than any broad approach.
|
| 33/2 |
2026/05/21 02:36 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue]
Results: map[Output:
Full output is too long, showing 500 out of 6646 lines.
Use more precise expression if possible.
[block/bio.c=1753=void bioset_exit(struct bio_set *bs)
--
block/bio.c-1756- if (bs->rescue_workqueue)
block/bio.c:1757: destroy_workqueue(bs->rescue_workqueue);
block/bio.c-1758- bs->rescue_workqueue = NULL;
--
block/blk-crypto-fallback.c=535=static int blk_crypto_fallback_init(void)
--
block/blk-crypto-fallback.c-608-fail_free_wq:
block/blk-crypto-fallback.c:609: destroy_workqueue(blk_crypto_wq);
block/blk-crypto-fallback.c-610-fail_destroy_profile:
--
block/blk-zoned.c=1438=void disk_free_zone_resources(struct gendisk *disk)
--
block/blk-zoned.c-1443- if (disk->zone_wplugs_wq) {
block/blk-zoned.c:1444: destroy_workqueue(disk->zone_wplugs_wq);
block/blk-zoned.c-1445- disk->zone_wplugs_wq = NULL;
--
crypto/cryptd.c=1114=static int __init cryptd_init(void)
--
crypto/cryptd.c-1135-err_destroy_wq:
crypto/cryptd.c:1136: destroy_workqueue(cryptd_wq);
crypto/cryptd.c-1137- return err;
--
crypto/cryptd.c=1140=static void __exit cryptd_exit(void)
crypto/cryptd.c-1141-{
crypto/cryptd.c:1142: destroy_workqueue(cryptd_wq);
crypto/cryptd.c-1143- cryptd_fini_queue(&queue);
--
drivers/accel/amdxdna/aie2_error.c=290=void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
--
drivers/accel/amdxdna/aie2_error.c-297- mutex_unlock(&xdna->dev_lock);
drivers/accel/amdxdna/aie2_error.c:298: destroy_workqueue(events->wq);
drivers/accel/amdxdna/aie2_error.c-299- mutex_lock(&xdna->dev_lock);
--
drivers/accel/amdxdna/amdxdna_mailbox.c=473=xdna_mailbox_create_channel(struct mailbox *mb,
--
drivers/accel/amdxdna/amdxdna_mailbox.c-520-destroy_wq:
drivers/accel/amdxdna/amdxdna_mailbox.c:521: destroy_workqueue(mb_chann->work_q);
drivers/accel/amdxdna/amdxdna_mailbox.c-522-free_and_out:
--
drivers/accel/amdxdna/amdxdna_mailbox.c=527=int xdna_mailbox_destroy_channel(struct mailbox_channel *mb_chann)
--
drivers/accel/amdxdna/amdxdna_mailbox.c-533- free_irq(mb_chann->msix_irq, mb_chann);
drivers/accel/amdxdna/amdxdna_mailbox.c:534: destroy_workqueue(mb_chann->work_q);
drivers/accel/amdxdna/amdxdna_mailbox.c-535- /* We can clean up and release resources */
--
drivers/accel/amdxdna/amdxdna_pci_drv.c=245=static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
--
drivers/accel/amdxdna/amdxdna_pci_drv.c-309-destroy_notifier_wq:
drivers/accel/amdxdna/amdxdna_pci_drv.c:310: destroy_workqueue(xdna->notifier_wq);
drivers/accel/amdxdna/amdxdna_pci_drv.c-311- return ret;
--
drivers/accel/amdxdna/amdxdna_pci_drv.c=314=static void amdxdna_remove(struct pci_dev *pdev)
--
drivers/accel/amdxdna/amdxdna_pci_drv.c-319-
drivers/accel/amdxdna/amdxdna_pci_drv.c:320: destroy_workqueue(xdna->notifier_wq);
drivers/accel/amdxdna/amdxdna_pci_drv.c-321-
--
drivers/accel/habanalabs/common/device.c=846=static int device_early_init(struct hl_device *hdev)
--
drivers/accel/habanalabs/common/device.c-996-free_prefetch_wq:
drivers/accel/habanalabs/common/device.c:997: destroy_workqueue(hdev->prefetch_wq);
drivers/accel/habanalabs/common/device.c-998-free_ts_free_wq:
drivers/accel/habanalabs/common/device.c:999: destroy_workqueue(hdev->ts_free_obj_wq);
drivers/accel/habanalabs/common/device.c-1000-free_cs_cmplt_wq:
drivers/accel/habanalabs/common/device.c:1001: destroy_workqueue(hdev->cs_cmplt_wq);
drivers/accel/habanalabs/common/device.c-1002-free_eq_wq:
drivers/accel/habanalabs/common/device.c:1003: destroy_workqueue(hdev->eq_wq);
drivers/accel/habanalabs/common/device.c-1004-free_cq_wq:
--
drivers/accel/habanalabs/common/device.c-1006- if (hdev->cq_wq[i])
drivers/accel/habanalabs/common/device.c:1007: destroy_workqueue(hdev->cq_wq[i]);
drivers/accel/habanalabs/common/device.c-1008- kfree(hdev->cq_wq);
--
drivers/accel/habanalabs/common/device.c=1024=static void device_early_fini(struct hl_device *hdev)
--
drivers/accel/habanalabs/common/device.c-1040-
drivers/accel/habanalabs/common/device.c:1041: destroy_workqueue(hdev->prefetch_wq);
drivers/accel/habanalabs/common/device.c:1042: destroy_workqueue(hdev->ts_free_obj_wq);
drivers/accel/habanalabs/common/device.c:1043: destroy_workqueue(hdev->cs_cmplt_wq);
drivers/accel/habanalabs/common/device.c:1044: destroy_workqueue(hdev->eq_wq);
drivers/accel/habanalabs/common/device.c:1045: destroy_workqueue(hdev->reset_wq);
drivers/accel/habanalabs/common/device.c-1046-
drivers/accel/habanalabs/common/device.c-1047- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
drivers/accel/habanalabs/common/device.c:1048: destroy_workqueue(hdev->cq_wq[i]);
drivers/accel/habanalabs/common/device.c-1049- kfree(hdev->cq_wq);
--
drivers/accel/qaic/qaic_debugfs.c=201=static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
--
drivers/accel/qaic/qaic_debugfs.c-214- if (ret)
drivers/accel/qaic/qaic_debugfs.c:215: goto destroy_workqueue;
drivers/accel/qaic/qaic_debugfs.c-216-
--
drivers/accel/qaic/qaic_debugfs.c-218- if (ret)
drivers/accel/qaic/qaic_debugfs.c:219: goto destroy_workqueue;
drivers/accel/qaic/qaic_debugfs.c-220-
--
drivers/accel/qaic/qaic_debugfs.c-241- mhi_unprepare_from_transfer(mhi_dev);
drivers/accel/qaic/qaic_debugfs.c:242:destroy_workqueue:
drivers/accel/qaic/qaic_debugfs.c:243: destroy_workqueue(qdev->bootlog_wq);
drivers/accel/qaic/qaic_debugfs.c-244-out:
--
drivers/accel/qaic/qaic_debugfs.c=248=static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev)
--
drivers/accel/qaic/qaic_debugfs.c-254- mhi_unprepare_from_transfer(qdev->bootlog_ch);
drivers/accel/qaic/qaic_debugfs.c:255: destroy_workqueue(qdev->bootlog_wq);
drivers/accel/qaic/qaic_debugfs.c-256- qdev->bootlog_ch = NULL;
--
drivers/accel/qaic/qaic_drv.c=84=static void qaicm_wq_release(struct drm_device *dev, void *res)
--
drivers/accel/qaic/qaic_drv.c-87-
drivers/accel/qaic/qaic_drv.c:88: destroy_workqueue(wq);
drivers/accel/qaic/qaic_drv.c-89-}
--
drivers/acpi/ec.c=2268=static struct acpi_driver acpi_ec_driver = {
--
drivers/acpi/ec.c-2278-
drivers/acpi/ec.c:2279:static void acpi_ec_destroy_workqueues(void)
drivers/acpi/ec.c-2280-{
drivers/acpi/ec.c-2281- if (ec_wq) {
drivers/acpi/ec.c:2282: destroy_workqueue(ec_wq);
drivers/acpi/ec.c-2283- ec_wq = NULL;
--
drivers/acpi/ec.c-2285- if (ec_query_wq) {
drivers/acpi/ec.c:2286: destroy_workqueue(ec_query_wq);
drivers/acpi/ec.c-2287- ec_query_wq = NULL;
--
drivers/acpi/ec.c=2291=static int acpi_ec_init_workqueues(void)
--
drivers/acpi/ec.c-2299- if (!ec_wq || !ec_query_wq) {
drivers/acpi/ec.c:2300: acpi_ec_destroy_workqueues();
drivers/acpi/ec.c-2301- return -ENODEV;
--
drivers/acpi/ec.c=2387=static void __exit acpi_ec_exit(void)
--
drivers/acpi/ec.c-2390- acpi_bus_unregister_driver(&acpi_ec_driver);
drivers/acpi/ec.c:2391: acpi_ec_destroy_workqueues();
drivers/acpi/ec.c-2392-}
--
drivers/acpi/nfit/core.c=3485=static __init int nfit_init(void)
--
drivers/acpi/nfit/core.c-3521- nfit_mce_unregister();
drivers/acpi/nfit/core.c:3522: destroy_workqueue(nfit_wq);
drivers/acpi/nfit/core.c-3523- }
--
drivers/acpi/nfit/core.c=3529=static __exit void nfit_exit(void)
--
drivers/acpi/nfit/core.c-3532- acpi_bus_unregister_driver(&acpi_nfit_driver);
drivers/acpi/nfit/core.c:3533: destroy_workqueue(nfit_wq);
drivers/acpi/nfit/core.c-3534- WARN_ON(!list_empty(&acpi_descs));
--
drivers/acpi/osl.c=1707=acpi_status acpi_os_terminate(void)
--
drivers/acpi/osl.c-1724-
drivers/acpi/osl.c:1725: destroy_workqueue(kacpid_wq);
drivers/acpi/osl.c:1726: destroy_workqueue(kacpi_notify_wq);
drivers/acpi/osl.c:1727: destroy_workqueue(kacpi_hotplug_wq);
drivers/acpi/osl.c-1728-
--
drivers/acpi/thermal.c=1053=static int __init acpi_thermal_init(void)
--
drivers/acpi/thermal.c-1070- if (result < 0) {
drivers/acpi/thermal.c:1071: destroy_workqueue(acpi_thermal_pm_queue);
drivers/acpi/thermal.c-1072- return -ENODEV;
--
drivers/acpi/thermal.c=1078=static void __exit acpi_thermal_exit(void)
--
drivers/acpi/thermal.c-1080- acpi_bus_unregister_driver(&acpi_thermal_driver);
drivers/acpi/thermal.c:1081: destroy_workqueue(acpi_thermal_pm_queue);
drivers/acpi/thermal.c-1082-}
--
drivers/ata/libata-sff.c=3201=void ata_sff_exit(void)
drivers/ata/libata-sff.c-3202-{
drivers/ata/libata-sff.c:3203: destroy_workqueue(ata_sff_wq);
drivers/ata/libata-sff.c-3204-}
--
drivers/auxdisplay/cfag12864b.c=354=static void __exit cfag12864b_exit(void)
--
drivers/auxdisplay/cfag12864b.c-357- cfag12864b_off();
drivers/auxdisplay/cfag12864b.c:358: destroy_workqueue(cfag12864b_workqueue);
drivers/auxdisplay/cfag12864b.c-359- kfree(cfag12864b_cache);
--
drivers/block/aoe/aoemain.c=29=aoe_exit(void)
--
drivers/block/aoe/aoemain.c-38- aoeblk_exit(); /* free cache after de-allocating bufs */
drivers/block/aoe/aoemain.c:39: destroy_workqueue(aoe_wq);
drivers/block/aoe/aoemain.c-40-}
--
drivers/block/aoe/aoemain.c=43=aoe_init(void)
--
drivers/block/aoe/aoemain.c-86- dev_fail:
drivers/block/aoe/aoemain.c:87: destroy_workqueue(aoe_wq);
drivers/block/aoe/aoemain.c-88-
--
drivers/block/drbd/drbd_main.c=2155=void drbd_destroy_device(struct kref *kref)
--
drivers/block/drbd/drbd_main.c-2194- if (device->submit.wq)
drivers/block/drbd/drbd_main.c:2195: destroy_workqueue(device->submit.wq);
drivers/block/drbd/drbd_main.c-2196- kfree(device);
--
drivers/block/drbd/drbd_main.c=2303=static void drbd_cleanup(void)
--
drivers/block/drbd/drbd_main.c-2320- if (retry.wq)
drivers/block/drbd/drbd_main.c:2321: destroy_workqueue(retry.wq);
drivers/block/drbd/drbd_main.c-2322-
--
drivers/block/drbd/drbd_main.c=2645=enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsigned int minor)
--
drivers/block/drbd/drbd_main.c-2756- if (err)
drivers/block/drbd/drbd_main.c:2757: goto out_destroy_workqueue;
drivers/block/drbd/drbd_main.c-2758-
--
drivers/block/drbd/drbd_main.c-2770-
drivers/block/drbd/drbd_main.c:2771:out_destroy_workqueue:
drivers/block/drbd/drbd_main.c:2772: destroy_workqueue(device->submit.wq);
drivers/block/drbd/drbd_main.c-2773-out_idr_remove_from_resource:
--
drivers/block/drbd/drbd_receiver.c=4921=static void conn_disconnect(struct drbd_connection *connection)
--
drivers/block/drbd/drbd_receiver.c-4939- if (connection->ack_sender) {
drivers/block/drbd/drbd_receiver.c:4940: destroy_workqueue(connection->ack_sender);
drivers/block/drbd/drbd_receiver.c-4941- connection->ack_sender = NULL;
--
drivers/block/floppy.c=4574=static int __init do_floppy_init(void)
--
drivers/block/floppy.c-4760-out_put_disk:
drivers/block/floppy.c:4761: destroy_workqueue(floppy_wq);
drivers/block/floppy.c-4762- for (drive = 0; drive < N_DRIVE; drive++) {
--
drivers/block/floppy.c=4976=static void __exit floppy_module_exit(void)
--
drivers/block/floppy.c-4982-
drivers/block/floppy.c:4983: destroy_workqueue(floppy_wq);
drivers/block/floppy.c-4984-
--
drivers/block/loop.c=1752=static void lo_free_disk(struct gendisk *disk)
--
drivers/block/loop.c-1756- if (lo->workqueue)
drivers/block/loop.c:1757: destroy_workqueue(lo->workqueue);
drivers/block/loop.c-1758- loop_free_idle_workers(lo, true);
--
drivers/block/mtip32xx/mtip32xx.c=3688=static int mtip_pci_probe(struct pci_dev *pdev,
--
drivers/block/mtip32xx/mtip32xx.c-3834- if (dd->isr_workq) {
drivers/block/mtip32xx/mtip32xx.c:3835: destroy_workqueue(dd->isr_workq);
drivers/block/mtip32xx/mtip32xx.c-3836- drop_cpu(dd->work[0].cpu_binding);
--
drivers/block/mtip32xx/mtip32xx.c=3856=static void mtip_pci_remove(struct pci_dev *pdev)
--
drivers/block/mtip32xx/mtip32xx.c-3906- if (dd->isr_workq) {
drivers/block/mtip32xx/mtip32xx.c:3907: destroy_workqueue(dd->isr_workq);
drivers/block/mtip32xx/mtip32xx.c-3908- drop_cpu(dd->work[0].cpu_binding);
--
drivers/block/nbd.c=264=static void nbd_dev_remove(struct nbd_device *nbd)
--
drivers/block/nbd.c-277- mutex_unlock(&nbd_index_mutex);
drivers/block/nbd.c:278: destroy_workqueue(nbd->recv_workq);
drivers/block/nbd.c-279- put_disk(disk);
--
drivers/block/nbd.c=1896=static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
--
drivers/block/nbd.c-1984-out_free_work:
drivers/block/nbd.c:1985: destroy_workqueue(nbd->recv_workq);
drivers/block/nbd.c-1986-out_err_disk:
--
drivers/block/nbd.c=2631=static int __init nbd_init(void)
--
drivers/block/nbd.c-2672- if (genl_register_family(&nbd_genl_family)) {
drivers/block/nbd.c:2673: destroy_workqueue(nbd_del_wq);
drivers/block/nbd.c-2674- unregister_blkdev(NBD_MAJOR, "nbd");
--
drivers/block/nbd.c=2696=static void __exit nbd_cleanup(void)
--
drivers/block/nbd.c-2724- /* Also wait for nbd_dev_remove_work() completes */
drivers/block/nbd.c:2725: destroy_workqueue(nbd_del_wq);
drivers/block/nbd.c-2726-
--
drivers/block/rbd.c=5321=static void rbd_dev_release(struct device *dev)
--
drivers/block/rbd.c-5326- if (need_put) {
drivers/block/rbd.c:5327: destroy_workqueue(rbd_dev->task_wq);
drivers/block/rbd.c-5328- ida_free(&rbd_dev_id_ida, rbd_dev->dev_id);
--
drivers/block/rbd.c=7375=static int __init rbd_init(void)
--
drivers/block/rbd.c-7420-err_out_wq:
drivers/block/rbd.c:7421: destroy_workqueue(rbd_wq);
drivers/block/rbd.c-7422-err_out_slab:
--
drivers/block/rbd.c=7427=static void __exit rbd_exit(void)
--
drivers/block/rbd.c-7432- unregister_blkdev(rbd_major, RBD_DRV_NAME);
drivers/block/rbd.c:7433: destroy_workqueue(rbd_wq);
drivers/block/rbd.c-7434- rbd_slab_exit();
--
drivers/block/rnbd/rnbd-clt.c=1823=static void __exit rnbd_client_exit(void)
--
drivers/block/rnbd/rnbd-clt.c-1827- ida_destroy(&index_ida);
drivers/block/rnbd/rnbd-clt.c:1828: destroy_workqueue(rnbd_clt_wq);
drivers/block/rnbd/rnbd-clt.c-1829-}
--
drivers/block/sunvdc.c=1216=static int __init vdc_init(void)
--
drivers/block/sunvdc.c-1240-out_free_wq:
drivers/block/sunvdc.c:1241: destroy_workqueue(sunvdc_wq);
drivers/block/sunvdc.c-1242- return err;
--
drivers/block/sunvdc.c=1245=static void __exit vdc_exit(void)
--
drivers/block/sunvdc.c-1248- unregister_blkdev(vdc_major, VDCBLK_NAME);
drivers/block/sunvdc.c:1249: destroy_workqueue(sunvdc_wq);
drivers/block/sunvdc.c-1250-}
--
drivers/block/virtio_blk.c=1681=static int __init virtio_blk_init(void)
--
drivers/block/virtio_blk.c-1691- error = major;
drivers/block/virtio_blk.c:1692: goto out_destroy_workqueue;
drivers/block/virtio_blk.c-1693- }
--
drivers/block/virtio_blk.c-1701- unregister_blkdev(major, "virtblk");
drivers/block/virtio_blk.c:1702:out_destroy_workqueue:
drivers/block/virtio_blk.c:1703: destroy_workqueue(virtblk_wq);
drivers/block/virtio_blk.c-1704- return error;
--
drivers/block/virtio_blk.c=1707=static void __exit virtio_blk_fini(void)
--
drivers/block/virtio_blk.c-1710- unregister_blkdev(major, "virtblk");
drivers/block/virtio_blk.c:1711: destroy_workqueue(virtblk_wq);
drivers/block/virtio_blk.c-1712-}
--
drivers/block/zloop.c=698=static void zloop_free_disk(struct gendisk *disk)
--
drivers/block/zloop.c-713- fput(zlo->data_dir);
drivers/block/zloop.c:714: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-715- kfree(zlo->base_dir);
--
drivers/block/zloop.c=883=static int zloop_ctl_add(struct zloop_options *opts)
--
drivers/block/zloop.c-952- ret = -ENOMEM;
drivers/block/zloop.c:953: goto out_destroy_workqueue;
drivers/block/zloop.c-954- }
--
drivers/block/zloop.c-1039- kfree(zlo->base_dir);
drivers/block/zloop.c:1040:out_destroy_workqueue:
drivers/block/zloop.c:1041: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-1042-out_free_idr:
--
drivers/bluetooth/btintel_pcie.c=2529=static void btintel_pcie_remove(struct pci_dev *pdev)
--
drivers/bluetooth/btintel_pcie.c-2552-
drivers/bluetooth/btintel_pcie.c:2553: destroy_workqueue(data->workqueue);
drivers/bluetooth/btintel_pcie.c-2554-
--
drivers/bluetooth/hci_qca.c=718=static int qca_close(struct hci_uart *hu)
--
drivers/bluetooth/hci_qca.c-730- * Shut the timers down so they can't be rearmed when
drivers/bluetooth/hci_qca.c:731: * destroy_workqueue() drains pending work which in turn might try
drivers/bluetooth/hci_qca.c-732- * to arm a timer. After shutdown rearm attempts are silently
--
drivers/bluetooth/hci_qca.c-736- timer_shutdown_sync(&qca->wake_retrans_timer);
drivers/bluetooth/hci_qca.c:737: destroy_workqueue(qca->workqueue);
drivers/bluetooth/hci_qca.c-738- qca->hu = NULL;
--
drivers/bus/mhi/ep/main.c=1459=int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
--
drivers/bus/mhi/ep/main.c-1570-err_destroy_wq:
drivers/bus/mhi/ep/main.c:1571: destroy_workqueue(mhi_cntrl->wq);
drivers/bus/mhi/ep/main.c-1572-err_destroy_ring_item_cache:
--
drivers/bus/mhi/ep/main.c=1591=void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl)
--
drivers/bus/mhi/ep/main.c-1594-
drivers/bus/mhi/ep/main.c:1595: destroy_workqueue(mhi_cntrl->wq);
drivers/bus/mhi/ep/main.c-1596-
--
drivers/bus/mhi/host/init.c=932=int mhi_register_controller(struct mhi_controller *mhi_cntrl,
--
drivers/bus/mhi/host/init.c-1061-err_destroy_wq:
drivers/bus/mhi/host/init.c:1062: destroy_workqueue(mhi_cntrl->hiprio_wq);
drivers/bus/mhi/host/init.c-1063-err_free_cmd:
--
drivers/bus/mhi/host/init.c=1073=void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
--
drivers/bus/mhi/host/init.c-1084-
drivers/bus/mhi/host/init.c:1085: destroy_workqueue(mhi_cntrl->hiprio_wq);
drivers/bus/mhi/host/init.c-1086- kfree(mhi_cntrl->mhi_cmd);
--
drivers/cdx/controller/mcdi.c=132=void cdx_mcdi_finish(struct cdx_mcdi *cdx)
--
drivers/cdx/controller/mcdi.c-141-
drivers/cdx/controller/mcdi.c:142: destroy_workqueue(mcdi->workqueue);
drivers/cdx/controller/mcdi.c-143- kfree(cdx->mcdi);
--
drivers/char/ipmi/ipmi_msghandler.c=5560=static void __exit cleanup_ipmi(void)
--
drivers/char/ipmi/ipmi_msghandler.c-5564- if (initialized) {
drivers/char/ipmi/ipmi_msghandler.c:5565: destroy_workqueue(bmc_remove_work_wq);
drivers/char/ipmi/ipmi_msghandler.c-5566-
--
drivers/char/tpm/tpm-dev-common.c=283=void __exit tpm_dev_common_exit(void)
--
drivers/char/tpm/tpm-dev-common.c-285- if (tpm_dev_wq) {
drivers/char/tpm/tpm-dev-common.c:286: destroy_workqueue(tpm_dev_wq);
drivers/char/tpm/tpm-dev-common.c-287- tpm_dev_wq = NULL;
--
drivers/char/tpm/tpm_vtpm_proxy.c=687=static int __init vtpm_module_init(void)
--
drivers/char/tpm/tpm_vtpm_proxy.c-699- pr_err("couldn't create vtpmx device\n");
drivers/char/tpm/tpm_vtpm_proxy.c:700: destroy_workqueue(workqueue);
drivers/char/tpm/tpm_vtpm_proxy.c-701- }
--
drivers/char/tpm/tpm_vtpm_proxy.c=706=static void __exit vtpm_module_exit(void)
drivers/char/tpm/tpm_vtpm_proxy.c-707-{
drivers/char/tpm/tpm_vtpm_proxy.c:708: destroy_workqueue(workqueue);
drivers/char/tpm/tpm_vtpm_proxy.c-709- misc_deregister(&vtpmx_miscdev);
--
drivers/char/xillybus/xillybus_core.c=1983=static void __exit xillybus_exit(void)
--
drivers/char/xillybus/xillybus_core.c-1985- /* flush_workqueue() was called for each endpoint released */
drivers/char/xillybus/xillybus_core.c:1986: destroy_workqueue(xillybus_wq);
drivers/char/xillybus/xillybus_core.c-1987-}
--
drivers/char/xillybus/xillyusb.c=547=static void cleanup_dev(struct kref *kref)
--
drivers/char/xillybus/xillyusb.c-558- if (xdev->workq)
drivers/char/xillybus/xillyusb.c:559: destroy_workqueue(xdev->workq);
drivers/char/xillybus/xillyusb.c-560-
--
drivers/char/xillybus/xillyusb.c=2274=static int __init xillyusb_init(void)
--
drivers/char/xillybus/xillyusb.c-2289- if (rc)
drivers/char/xillybus/xillyusb.c:2290: destroy_workqueue(wakeup_wq);
drivers/char/xillybus/xillyusb.c-2291-
--
drivers/char/xillybus/xillyusb.c=2295=static void __exit xillyusb_exit(void)
--
drivers/char/xillybus/xillyusb.c-2298-
drivers/char/xillybus/xillyusb.c:2299: destroy_workqueue(wakeup_wq);
drivers/char/xillybus/xillyusb.c-2300-}
--
drivers/cpufreq/tegra194-cpufreq.c=608=static void tegra194_cpufreq_free_resources(void)
drivers/cpufreq/tegra194-cpufreq.c-609-{
drivers/cpufreq/tegra194-cpufreq.c:610: destroy_workqueue(read_counters_wq);
drivers/cpufreq/tegra194-cpufreq.c-611-}
--
drivers/crypto/atmel-i2c.c=409=static void __exit atmel_i2c_exit(void)
drivers/crypto/atmel-i2c.c-410-{
drivers/crypto/atmel-i2c.c:411: destroy_workqueue(atmel_wq);
drivers/crypto/atmel-i2c.c-412-}
--
drivers/crypto/cavium/nitrox/nitrox_mbx.c=207=void nitrox_mbox_cleanup(struct nitrox_device *ndev)
--
drivers/crypto/cavium/nitrox/nitrox_mbx.c-212- if (ndev->iov.pf2vf_wq)
drivers/crypto/cavium/nitrox/nitrox_mbx.c:213: destroy_workqueue(ndev->iov.pf2vf_wq);
drivers/crypto/cavium/nitrox/nitrox_mbx.c-214-
--
drivers/crypto/ccree/cc_request_mgr.c=86=void cc_req_mgr_fini(struct cc_drvdata *drvdata)
--
drivers/crypto/ccree/cc_request_mgr.c-103-#ifdef COMP_IN_WQ
drivers/crypto/ccree/cc_request_mgr.c:104: destroy_workqueue(req_mgr_h->workq);
drivers/crypto/ccree/cc_request_mgr.c-105-#else
--
drivers/crypto/hisilicon/qm.c=3021=static void hisi_qm_unint_work(struct hisi_qm *qm)
drivers/crypto/hisilicon/qm.c-3022-{
drivers/crypto/hisilicon/qm.c:3023: destroy_workqueue(qm->wq);
drivers/crypto/hisilicon/qm.c-3024-}
--
drivers/crypto/inside-secure/safexcel.c=1389=static int safexcel_probe_generic(void *pdev,
--
drivers/crypto/inside-secure/safexcel.c-1704- if (priv->ring[i].workqueue)
drivers/crypto/inside-secure/safexcel.c:1705: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1706- }
--
drivers/crypto/inside-secure/safexcel.c=1799=static void safexcel_remove(struct platform_device *pdev)
--
drivers/crypto/inside-secure/safexcel.c-1811- irq_set_affinity_hint(priv->ring[i].irq, NULL);
drivers/crypto/inside-secure/safexcel.c:1812: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1813- }
--
drivers/crypto/inside-secure/safexcel.c=1967=static void safexcel_pci_remove(struct pci_dev *pdev)
--
drivers/crypto/inside-secure/safexcel.c-1974- for (i = 0; i < priv->config.rings; i++)
drivers/crypto/inside-secure/safexcel.c:1975: destroy_workqueue(priv->ring[i].workqueue);
drivers/crypto/inside-secure/safexcel.c-1976-
--
drivers/crypto/intel/qat/qat_common/adf_aer.c=276=int adf_init_aer(void)
--
drivers/crypto/intel/qat/qat_common/adf_aer.c-284- if (!device_sriov_wq) {
drivers/crypto/intel/qat/qat_common/adf_aer.c:285: destroy_workqueue(device_reset_wq);
drivers/crypto/intel/qat/qat_common/adf_aer.c-286- device_reset_wq = NULL;
--
drivers/crypto/intel/qat/qat_common/adf_aer.c=293=void adf_exit_aer(void)
--
drivers/crypto/intel/qat/qat_common/adf_aer.c-295- if (device_reset_wq)
drivers/crypto/intel/qat/qat_common/adf_aer.c:296: destroy_workqueue(device_reset_wq);
drivers/crypto/intel/qat/qat_common/adf_aer.c-297- device_reset_wq = NULL;
--
drivers/crypto/intel/qat/qat_common/adf_aer.c-299- if (device_sriov_wq)
drivers/crypto/intel/qat/qat_common/adf_aer.c:300: destroy_workqueue(device_sriov_wq);
]
]
|
| 34/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 41460
output: 47
thoughts: 161
Thoughts: **Bluetooth HCI Device Deallocation Conundrum**
Okay, so I'm diving into this Bluetooth HCI device cleanup, specifically looking at `destroy_workqueue`. Right, it's right there in `net/bluetooth/hci_core.c`, line 2749, easy enough to find. Now, the key is where that function is actually *called*. Aha! `destroy_workqueue(hdev->workqueue)` is invoked within `hci_release_dev`. And `hci_release_dev`, as expected, gets called when the reference count of the `hdev` structure hits zero.
But here's where it gets interesting, and what I *thought* was a potential issue initially. `hci_conn` structures maintain a reference to `hdev`. So, if an `hci_conn` exists, the `hdev` reference count should, theoretically, be greater than zero. Therefore, `hci_release_dev` shouldn't be called, and the workqueue should be safe from accidental destruction. So far, so good.
But wait a minute, I have a niggling doubt. I need to make sure that the `l2cap_chan` structure, which often interacts with `hci_conn`, doesn't hold a reference to `hci_conn`. I need to review the `l2cap_chan` structure's usage and make certain there's no subtle refcount linkage I'm missing. Time to check `l2cap_chan` more closely.
|
| 35/2 |
2026/05/21 02:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_chan_del]
Results: map[SourceCode: 646: void l2cap_chan_del(struct l2cap_chan *chan, int err)
647: {
648: struct l2cap_conn *conn = chan->conn;
649:
650: __clear_chan_timer(chan);
651:
652: BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
653: state_to_string(chan->state));
654:
655: chan->ops->teardown(chan, err);
656:
657: if (conn) {
658: /* Delete from channel list */
659: list_del(&chan->list);
660:
661: l2cap_chan_put(chan);
662:
663: chan->conn = NULL;
664:
665: /* Reference was only held for non-fixed channels or
666: * fixed channels that explicitly requested it using the
667: * FLAG_HOLD_HCI_CONN flag.
668: */
669: if (chan->chan_type != L2CAP_CHAN_FIXED ||
670: test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
671: hci_conn_drop(conn->hcon);
672: }
673:
674: if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
675: return;
676:
677: switch (chan->mode) {
678: case L2CAP_MODE_BASIC:
679: break;
680:
681: case L2CAP_MODE_LE_FLOWCTL:
682: case L2CAP_MODE_EXT_FLOWCTL:
683: skb_queue_purge(&chan->tx_q);
684: break;
685:
686: case L2CAP_MODE_ERTM:
687: __clear_retrans_timer(chan);
688: __clear_monitor_timer(chan);
689: __clear_ack_timer(chan);
690:
691: skb_queue_purge(&chan->srej_q);
692:
693: l2cap_seq_list_free(&chan->srej_list);
694: l2cap_seq_list_free(&chan->retrans_list);
695: fallthrough;
696:
697: case L2CAP_MODE_STREAMING:
698: skb_queue_purge(&chan->tx_q);
699: break;
700: }
701: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 36/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 42817
output: 45
thoughts: 230
Thoughts: **Analyzing Potential Race Conditions in L2CAP Channel Management**
Okay, so `l2cap_chan_del` is calling `hci_conn_drop` on the HCI connection handle. Now, I need to trace this a bit. I see that `l2cap_chan_send` is called from `l2cap_sock_sendmsg`, which is the critical path. My initial concern is whether there's a lock protecting `l2cap_chan` during this process. Aha, yes, `l2cap_chan_lock(chan)` is called before `l2cap_chan_send`. Good, that's expected.
But, a thought is creeping in: What if `chan->conn` somehow becomes NULL *before* `l2cap_chan_send` uses it? Let me check⦠Okay, `l2cap_chan_send` has a check on line 2551 for `if (!chan->conn) return -ENOTCONN;`. So, that specific race condition is handled. It *shouldn't* be NULL.
But now I'm thinking about *another* scenario: what if `chan->conn->hcon` is already in the process of being destroyed while all this is happening? We need to verify what happens on the HCI side during connection teardown. Remember, `hci_conn_drop` will eventually drop the reference to the `hci_conn` structure. If the reference count on `hci_conn` goes to zero, then `hci_conn_del` is going to be invoked. Okay, I need to dig into the details of `hci_conn_drop` to be certain.
|
| 37/2 |
2026/05/21 02:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/net/bluetooth/hci_core.h IncludeLines:true Name:hci_conn_drop]
Results: map[SourceCode:1649: static inline void hci_conn_drop(struct hci_conn *conn)
1650: {
1651: BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt));
1652:
1653: if (atomic_dec_and_test(&conn->refcnt)) {
1654: unsigned long timeo;
1655:
1656: switch (conn->type) {
1657: case ACL_LINK:
1658: case LE_LINK:
1659: cancel_delayed_work(&conn->idle_work);
1660: if (conn->state == BT_CONNECTED) {
1661: timeo = conn->disc_timeout;
1662: if (!conn->out)
1663: timeo *= 2;
1664: } else {
1665: timeo = 0;
1666: }
1667: break;
1668:
1669: default:
1670: timeo = 0;
1671: break;
1672: }
1673:
1674: cancel_delayed_work(&conn->disc_work);
1675: queue_delayed_work(conn->hdev->workqueue,
1676: &conn->disc_work, timeo);
1677: }
1678: }
SourceFile:include/net/bluetooth/hci_core.h]
|
| 38/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 43877
output: 43
thoughts: 123
Thoughts: **Debugging a Bluetooth Connection Drop**
Okay, so `hci_conn_drop` doesn't instantly nuke the `hci_conn` structure. It seems like it's scheduling `conn->disc_work` on `conn->hdev->workqueue`. That makes sense, keeps things orderly, asynchronous.
But hang on... isn't `hci_conn_del` usually triggered by something like `hci_conn_timeout`? Right, that implies that if `hci_conn` is still around, then the reference count on `hdev` has to still be greater than zero.
Which means we shouldn't be hitting a situation where `hci_release_dev` is called prematurely! This is starting to look very suspect. If the refcount is greater than zero, how can the workqueue get destroyed?
This is leading me to believe the problem is somewhere else. I need to dive into `hci_unregister_dev` next. Let's see what's happening there.
|
| 39/2 |
2026/05/21 02:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_unregister_dev]
Results: map[SourceCode:2689: /* Unregister HCI device */
2690: void hci_unregister_dev(struct hci_dev *hdev)
2691: {
2692: BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
2693:
2694: mutex_lock(&hdev->unregister_lock);
2695: hci_dev_set_flag(hdev, HCI_UNREGISTER);
2696: mutex_unlock(&hdev->unregister_lock);
2697:
2698: write_lock(&hci_dev_list_lock);
2699: list_del(&hdev->list);
2700: write_unlock(&hci_dev_list_lock);
2701:
2702: synchronize_srcu(&hdev->srcu);
2703: cleanup_srcu_struct(&hdev->srcu);
2704:
2705: disable_work_sync(&hdev->rx_work);
2706: disable_work_sync(&hdev->cmd_work);
2707: disable_work_sync(&hdev->tx_work);
2708: disable_work_sync(&hdev->power_on);
2709: disable_work_sync(&hdev->error_reset);
2710:
2711: hci_cmd_sync_clear(hdev);
2712:
2713: hci_unregister_suspend_notifier(hdev);
2714:
2715: hci_dev_do_close(hdev);
2716:
2717: if (!test_bit(HCI_INIT, &hdev->flags) &&
2718: !hci_dev_test_flag(hdev, HCI_SETUP) &&
2719: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
2720: hci_dev_lock(hdev);
2721: mgmt_index_removed(hdev);
2722: hci_dev_unlock(hdev);
2723: }
2724:
2725: /* mgmt_index_removed should take care of emptying the
2726: * pending list */
2727: BUG_ON(!list_empty(&hdev->mgmt_pending));
2728:
2729: hci_sock_dev_event(hdev, HCI_DEV_UNREG);
2730:
2731: if (hdev->rfkill) {
2732: rfkill_unregister(hdev->rfkill);
2733: rfkill_destroy(hdev->rfkill);
2734: }
2735:
2736: device_del(&hdev->dev);
2737: /* Actual cleanup is deferred until hci_release_dev(). */
2738: hci_dev_put(hdev);
2739: }
SourceFile:net/bluetooth/hci_core.c]
|
| 40/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 45084
output: 40
thoughts: 82
Thoughts: **Analyzing the Bluetooth Device Unregistration Process**
Okay, so I'm digging into this `hci_unregister_dev` function and something's bugging me. It calls `disable_work_sync(&hdev->tx_work)`. That seems straightforward enough, but then it hits me: `disable_work_sync` *doesn't* actually destroy the workqueue. So, the workqueue is disabled, but still exists! That's when alarm bells go off.
If we've disabled the workqueue via `disable_work_sync`, what happens if some other part of the system, maybe inadvertently or due to a race condition, tries to call `queue_work` on it *after* the disabling but *before* the device is completely unregistered? We could potentially have a big problem on our hands.
I need to get a really good look at the `disable_work_sync` function itself to understand exactly what happens in that disabled state and if there is any protection from concurrent calls. I need to understand this better.
|
| 41/2 |
2026/05/21 02:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c IncludeLines:true Name:disable_work_sync]
Results: map[SourceCode:4460: /**
4461: * disable_work_sync - Disable, cancel and drain a work item
4462: * @work: work item to disable
4463: *
4464: * Similar to disable_work() but also wait for @work to finish if currently
4465: * executing.
4466: *
4467: * Must be called from a sleepable context if @work was last queued on a non-BH
4468: * workqueue. Can also be called from non-hardirq atomic contexts including BH
4469: * if @work was last queued on a BH workqueue.
4470: *
4471: * Returns %true if @work was pending, %false otherwise.
4472: */
4473: bool disable_work_sync(struct work_struct *work)
4474: {
4475: return __cancel_work_sync(work, WORK_CANCEL_DISABLE);
4476: }
SourceFile:kernel/workqueue.c]
|
| 42/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 45720
output: 39
thoughts: 170
Thoughts: **My Analysis of Workqueue Destruction in the Kernel**
Okay, so I'm thinking through this `disable_work_sync` function. It seems I initially misinterpreted its impact. It doesn't actually disable the *workqueue* itself, but rather a *work item* within that queue. If a work item is disabled, then calling `queue_work` on it will just return false silently, which is an optimization, avoiding an error.
The important point is that this won't trigger any warning about the workqueue being destroyed. That warning, which I've seen before, is: `if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) && WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n", work->func, wq->name)))`.
So, the key condition is that `wq->flags` has either `__WQ_DESTROYING` or `__WQ_DRAINING` set. I need to be more precise: When is `__WQ_DESTROYING` actually set? Of course, it's set in `destroy_workqueue`.
|
| 43/2 |
2026/05/21 02:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c IncludeLines:true Name:destroy_workqueue]
Results: map[SourceCode:5835: /**
5836: * destroy_workqueue - safely terminate a workqueue
5837: * @wq: target workqueue
5838: *
5839: * Safely destroy a workqueue. All work currently pending will be done first.
5840: *
5841: * This function does NOT guarantee that non-pending work that has been
5842: * submitted with queue_delayed_work() and similar functions will be done
5843: * before destroying the workqueue. The fundamental problem is that, currently,
5844: * the workqueue has no way of accessing non-pending delayed_work. delayed_work
5845: * is only linked on the timer-side. All delayed_work must, therefore, be
5846: * canceled before calling this function.
5847: *
5848: * TODO: It would be better if the problem described above wouldn't exist and
5849: * destroy_workqueue() would cleanly cancel all pending and non-pending
5850: * delayed_work.
5851: */
5852: void destroy_workqueue(struct workqueue_struct *wq)
5853: {
5854: struct pool_workqueue *pwq;
5855: int cpu;
5856:
5857: /*
5858: * Remove it from sysfs first so that sanity check failure doesn't
5859: * lead to sysfs name conflicts.
5860: */
5861: workqueue_sysfs_unregister(wq);
5862:
5863: /* mark the workqueue destruction is in progress */
5864: mutex_lock(&wq->mutex);
5865: wq->flags |= __WQ_DESTROYING;
5866: mutex_unlock(&wq->mutex);
5867:
5868: /* drain it before proceeding with destruction */
5869: drain_workqueue(wq);
5870:
5871: /* kill rescuer, if sanity checks fail, leave it w/o rescuer */
5872: if (wq->rescuer) {
5873: struct worker *rescuer = wq->rescuer;
5874:
5875: /* this prevents new queueing */
5876: raw_spin_lock_irq(&wq_mayday_lock);
5877: wq->rescuer = NULL;
5878: raw_spin_unlock_irq(&wq_mayday_lock);
5879:
5880: /* rescuer will empty maydays list before exiting */
5881: kthread_stop(rescuer->task);
5882: kfree(rescuer);
5883: }
5884:
5885: /*
5886: * Sanity checks - grab all the locks so that we wait for all
5887: * in-flight operations which may do put_pwq().
5888: */
5889: mutex_lock(&wq_pool_mutex);
5890: mutex_lock(&wq->mutex);
5891: for_each_pwq(pwq, wq) {
5892: raw_spin_lock_irq(&pwq->pool->lock);
5893: if (WARN_ON(pwq_busy(pwq))) {
5894: pr_warn("%s: %s has the following busy pwq\n",
5895: __func__, wq->name);
5896: show_pwq(pwq);
5897: raw_spin_unlock_irq(&pwq->pool->lock);
5898: mutex_unlock(&wq->mutex);
5899: mutex_unlock(&wq_pool_mutex);
5900: show_one_workqueue(wq);
5901: return;
5902: }
5903: raw_spin_unlock_irq(&pwq->pool->lock);
5904: }
5905: mutex_unlock(&wq->mutex);
5906:
5907: /*
5908: * wq list is used to freeze wq, remove from list after
5909: * flushing is complete in case freeze races us.
5910: */
5911: list_del_rcu(&wq->list);
5912: mutex_unlock(&wq_pool_mutex);
5913:
5914: /*
5915: * We're the sole accessor of @wq. Directly access cpu_pwq and dfl_pwq
5916: * to put the base refs. @wq will be auto-destroyed from the last
5917: * pwq_put. RCU read lock prevents @wq from going away from under us.
5918: */
5919: rcu_read_lock();
5920:
5921: for_each_possible_cpu(cpu) {
5922: put_pwq_unlocked(unbound_pwq(wq, cpu));
5923: RCU_INIT_POINTER(*unbound_pwq_slot(wq, cpu), NULL);
5924: }
5925:
5926: put_pwq_unlocked(unbound_pwq(wq, -1));
5927: RCU_INIT_POINTER(*unbound_pwq_slot(wq, -1), NULL);
5928:
5929: rcu_read_unlock();
5930: }
SourceFile:kernel/workqueue.c]
|
| 44/2 |
2026/05/21 02:36 |
llm |
expert |
0m
Model:
Tokens:
input: 47694
output: 18
thoughts: 66
Thoughts: Okay, let me think this through.
**Workqueue Destruction and Draining: A Refresher**
Right, so I'm looking at the workqueue destruction process. I see that the `__WQ_DESTROYING` flag is set within the `destroy_workqueue` function. Makes sense, that's the point where things are starting to shut down. But, then I'm also seeing the `__WQ_DRAINING` flag being set in `drain_workqueue`. Wait a second⦠isn't `drain_workqueue` called *from* `destroy_workqueue`? That's the normal path. The question I need to consider - is `drain_workqueue` called from *anywhere else* in the code? If so, this could influence the design in a few ways.
|
| 45/2 |
2026/05/21 02:37 |
tool |
grepper |
0m
Args: map[Expression:drain_workqueue]
Results: map[Output:drivers/cxl/core/port.c=2102=void cxl_bus_drain(void)
drivers/cxl/core/port.c-2103-{
drivers/cxl/core/port.c:2104: drain_workqueue(cxl_bus_wq);
drivers/cxl/core/port.c-2105-}
--
drivers/dma/idxd/cdev.c=328=static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid)
--
drivers/dma/idxd/cdev.c-352- if (wq->wq)
drivers/dma/idxd/cdev.c:353: drain_workqueue(wq->wq);
drivers/dma/idxd/cdev.c-354-
--
drivers/firewire/core-card.c=746=void fw_core_remove_card(struct fw_card *card)
--
drivers/firewire/core-card.c-762- card->driver = &dummy_driver;
drivers/firewire/core-card.c:763: drain_workqueue(card->isoc_wq);
drivers/firewire/core-card.c:764: drain_workqueue(card->async_wq);
drivers/firewire/core-card.c-765-
--
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c=4876=void amdgpu_device_fini_hw(struct amdgpu_device *adev)
--
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c-4881- if (adev->mman.initialized)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c:4882: drain_workqueue(adev->mman.bdev.wq);
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c-4883- adev->shutdown = true;
--
drivers/gpu/drm/i915/i915_gem.c=1111=void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
--
drivers/gpu/drm/i915/i915_gem.c-1114- flush_work(&i915->mm.free_work);
drivers/gpu/drm/i915/i915_gem.c:1115: drain_workqueue(i915->bdev.wq);
drivers/gpu/drm/i915/i915_gem.c-1116- rcu_barrier();
--
drivers/gpu/drm/i915/i915_gem.c-1124- * waiting for the RCU grace period so that we catch work queued via RCU from
drivers/gpu/drm/i915/i915_gem.c:1125: * the first pass. As neither drain_workqueue() nor flush_workqueue() report a
drivers/gpu/drm/i915/i915_gem.c-1126- * result, we make an assumption that we only don't require more than 3 passes
--
drivers/gpu/drm/i915/i915_gem.c-1128- */
drivers/gpu/drm/i915/i915_gem.c:1129:void i915_gem_drain_workqueue(struct drm_i915_private *i915)
drivers/gpu/drm/i915/i915_gem.c-1130-{
--
drivers/gpu/drm/i915/i915_gem.c-1138-
drivers/gpu/drm/i915/i915_gem.c:1139: drain_workqueue(i915->wq);
drivers/gpu/drm/i915/i915_gem.c-1140-}
--
drivers/gpu/drm/i915/i915_gem.c=1142=int i915_gem_init(struct drm_i915_private *dev_priv)
--
drivers/gpu/drm/i915/i915_gem.c-1210-err_unlock:
drivers/gpu/drm/i915/i915_gem.c:1211: i915_gem_drain_workqueue(dev_priv);
drivers/gpu/drm/i915/i915_gem.c-1212-
--
drivers/gpu/drm/i915/i915_gem.c=1256=void i915_gem_driver_remove(struct drm_i915_private *dev_priv)
--
drivers/gpu/drm/i915/i915_gem.c-1266- /* Flush any outstanding unpin_work. */
drivers/gpu/drm/i915/i915_gem.c:1267: i915_gem_drain_workqueue(dev_priv);
drivers/gpu/drm/i915/i915_gem.c-1268-}
--
drivers/gpu/drm/i915/i915_gem.c=1270=void i915_gem_driver_release(struct drm_i915_private *dev_priv)
--
drivers/gpu/drm/i915/i915_gem.c-1280- /* Flush any outstanding work, including i915_gem_context.release_work. */
drivers/gpu/drm/i915/i915_gem.c:1281: i915_gem_drain_workqueue(dev_priv);
drivers/gpu/drm/i915/i915_gem.c-1282-
--
drivers/gpu/drm/i915/i915_gem.c=1304=void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
drivers/gpu/drm/i915/i915_gem.c-1305-{
drivers/gpu/drm/i915/i915_gem.c:1306: i915_gem_drain_workqueue(dev_priv);
drivers/gpu/drm/i915/i915_gem.c-1307- GEM_BUG_ON(!llist_empty(&dev_priv->mm.free_list));
--
drivers/gpu/drm/i915/i915_gem.h=52=void i915_gem_drain_freed_objects(struct drm_i915_private *i915);
drivers/gpu/drm/i915/i915_gem.h:53:void i915_gem_drain_workqueue(struct drm_i915_private *i915);
drivers/gpu/drm/i915/i915_gem.h-54-
--
drivers/gpu/drm/i915/intel_region_ttm.c=108=int intel_region_ttm_fini(struct intel_memory_region *mem)
--
drivers/gpu/drm/i915/intel_region_ttm.c-133- msleep(20);
drivers/gpu/drm/i915/intel_region_ttm.c:134: drain_workqueue(mem->i915->bdev.wq);
drivers/gpu/drm/i915/intel_region_ttm.c-135- }
--
drivers/gpu/drm/i915/selftests/mock_gem_device.c=61=static void mock_device_release(struct drm_device *dev)
--
drivers/gpu/drm/i915/selftests/mock_gem_device.c-70-
drivers/gpu/drm/i915/selftests/mock_gem_device.c:71: i915_gem_drain_workqueue(i915);
drivers/gpu/drm/i915/selftests/mock_gem_device.c-72-
--
drivers/gpu/drm/ttm/ttm_device.c=253=void ttm_device_fini(struct ttm_device *bdev)
--
drivers/gpu/drm/ttm/ttm_device.c-261-
drivers/gpu/drm/ttm/ttm_device.c:262: drain_workqueue(bdev->wq);
drivers/gpu/drm/ttm/ttm_device.c-263- destroy_workqueue(bdev->wq);
--
drivers/gpu/drm/xe/xe_ggtt.c=241=static void dev_fini_ggtt(void *arg)
--
drivers/gpu/drm/xe/xe_ggtt.c-244-
drivers/gpu/drm/xe/xe_ggtt.c:245: drain_workqueue(ggtt->wq);
drivers/gpu/drm/xe/xe_ggtt.c-246-}
--
drivers/gpu/drm/xe/xe_guc_submit.c=229=static void guc_submit_fini(struct drm_device *drm, void *arg)
--
drivers/gpu/drm/xe/xe_guc_submit.c-239-
drivers/gpu/drm/xe/xe_guc_submit.c:240: drain_workqueue(xe->destroy_wq);
drivers/gpu/drm/xe/xe_guc_submit.c-241-
--
drivers/hv/vmbus_drv.c=2593=static int vmbus_bus_suspend(struct device *dev)
--
drivers/hv/vmbus_drv.c-2604- /* Drain all the workqueues as we are in suspend */
drivers/hv/vmbus_drv.c:2605: drain_workqueue(vmbus_connection.rescind_work_queue);
drivers/hv/vmbus_drv.c:2606: drain_workqueue(vmbus_connection.work_queue);
drivers/hv/vmbus_drv.c:2607: drain_workqueue(vmbus_connection.handle_primary_chan_wq);
drivers/hv/vmbus_drv.c:2608: drain_workqueue(vmbus_connection.handle_sub_chan_wq);
drivers/hv/vmbus_drv.c-2609-
--
drivers/md/dm-cache-target.c=2792=static void cache_postsuspend(struct dm_target *ti)
--
drivers/md/dm-cache-target.c-2799- cancel_delayed_work_sync(&cache->waker);
drivers/md/dm-cache-target.c:2800: drain_workqueue(cache->wq);
drivers/md/dm-cache-target.c-2801- WARN_ON(cache->tracker.in_flight);
--
drivers/md/dm-era-target.c=1420=static void stop_worker(struct era *era)
--
drivers/md/dm-era-target.c-1422- atomic_set(&era->suspended, 1);
drivers/md/dm-era-target.c:1423: drain_workqueue(era->wq);
drivers/md/dm-era-target.c-1424-}
--
drivers/md/dm-integrity.c=3599=static void dm_integrity_postsuspend(struct dm_target *ti)
--
drivers/md/dm-integrity.c-3608- if (ic->recalc_wq)
drivers/md/dm-integrity.c:3609: drain_workqueue(ic->recalc_wq);
drivers/md/dm-integrity.c-3610-
--
drivers/md/dm-integrity.c-3614- queue_work(ic->commit_wq, &ic->commit_work);
drivers/md/dm-integrity.c:3615: drain_workqueue(ic->commit_wq);
drivers/md/dm-integrity.c-3616-
--
drivers/md/dm-integrity.c-3618- queue_work(ic->writer_wq, &ic->writer_work);
drivers/md/dm-integrity.c:3619: drain_workqueue(ic->writer_wq);
drivers/md/dm-integrity.c-3620- dm_integrity_flush_buffers(ic, true);
--
drivers/md/dm-writecache.c=924=static void writecache_suspend(struct dm_target *ti)
--
drivers/md/dm-writecache.c-941-
drivers/md/dm-writecache.c:942: drain_workqueue(wc->writeback_wq);
drivers/md/dm-writecache.c-943-
--
drivers/net/ethernet/cavium/thunder/nicvf_main.c=1324=int nicvf_stop(struct net_device *netdev)
--
drivers/net/ethernet/cavium/thunder/nicvf_main.c-1334- cancel_delayed_work_sync(&nic->link_change_work);
drivers/net/ethernet/cavium/thunder/nicvf_main.c:1335: drain_workqueue(nic->nicvf_rx_mode_wq);
drivers/net/ethernet/cavium/thunder/nicvf_main.c-1336- }
--
drivers/net/ethernet/cavium/thunder/nicvf_main.c=1449=int nicvf_open(struct net_device *netdev)
--
drivers/net/ethernet/cavium/thunder/nicvf_main.c-1457- if (nic->nicvf_rx_mode_wq)
drivers/net/ethernet/cavium/thunder/nicvf_main.c:1458: drain_workqueue(nic->nicvf_rx_mode_wq);
drivers/net/ethernet/cavium/thunder/nicvf_main.c-1459-
--
drivers/net/ethernet/marvell/prestera/prestera_main.c=44=void prestera_queue_drain(void)
drivers/net/ethernet/marvell/prestera/prestera_main.c-45-{
drivers/net/ethernet/marvell/prestera/prestera_main.c:46: drain_workqueue(prestera_wq);
drivers/net/ethernet/marvell/prestera/prestera_main.c:47: drain_workqueue(prestera_owq);
drivers/net/ethernet/marvell/prestera/prestera_main.c-48-}
--
drivers/net/ethernet/netronome/nfp/ccm_mbox.c=721=void nfp_ccm_mbox_clean(struct nfp_net *nn)
drivers/net/ethernet/netronome/nfp/ccm_mbox.c-722-{
drivers/net/ethernet/netronome/nfp/ccm_mbox.c:723: drain_workqueue(nn->mbox_cmsg.workq);
drivers/net/ethernet/netronome/nfp/ccm_mbox.c-724-}
--
drivers/s390/net/qeth_l2_main.c=271=static void qeth_l2_set_pnso_mode(struct qeth_card *card,
--
drivers/s390/net/qeth_l2_main.c-278- if (mode == QETH_PNSO_NONE)
drivers/s390/net/qeth_l2_main.c:279: drain_workqueue(card->event_wq);
drivers/s390/net/qeth_l2_main.c-280-}
--
drivers/s390/net/qeth_l2_main.c=1372=static void qeth_l2_dev2br_worker(struct work_struct *work)
--
drivers/s390/net/qeth_l2_main.c-1406- /* Card fdb and bridge fdb are out of sync, card has stopped
drivers/s390/net/qeth_l2_main.c:1407: * notifications (no need to drain_workqueue). Purge all
drivers/s390/net/qeth_l2_main.c-1408- * 'extern_learn' entries from the parent bridge and restart
--
drivers/scsi/libsas/sas_event.c=57=void __sas_drain_work(struct sas_ha_struct *ha)
--
drivers/scsi/libsas/sas_event.c-63-
drivers/scsi/libsas/sas_event.c:64: drain_workqueue(ha->event_q);
drivers/scsi/libsas/sas_event.c:65: drain_workqueue(ha->disco_q);
drivers/scsi/libsas/sas_event.c-66-
--
drivers/scsi/storvsc_drv.c=2159=static int storvsc_suspend(struct hv_device *hv_dev)
--
drivers/scsi/storvsc_drv.c-2166-
drivers/scsi/storvsc_drv.c:2167: drain_workqueue(host_dev->handle_error_wq);
drivers/scsi/storvsc_drv.c-2168-
--
drivers/usb/gadget/function/f_fs.c=4038=static void ffs_func_unbind(struct usb_configuration *c,
--
drivers/usb/gadget/function/f_fs.c-4054- /* Drain any pending AIO completions */
drivers/usb/gadget/function/f_fs.c:4055: drain_workqueue(ffs->io_completion_wq);
drivers/usb/gadget/function/f_fs.c-4056-
--
drivers/virt/acrn/ioreq.c=562=static void ioreq_pause(void)
--
drivers/virt/acrn/ioreq.c-565- acrn_remove_intr_handler();
drivers/virt/acrn/ioreq.c:566: drain_workqueue(ioreq_wq);
drivers/virt/acrn/ioreq.c-567-}
--
fs/xfs/xfs_icache.c=2031=xfs_inodegc_stop(
--
fs/xfs/xfs_icache.c-2044- * inodes, so we queue pending work items and flush the workqueue until
fs/xfs/xfs_icache.c:2045: * all inodegc lists are empty. IOWs, we cannot use drain_workqueue
fs/xfs/xfs_icache.c-2046- * here because it does not allow other unserialized mechanisms to
--
include/linux/workqueue.h=603=extern void __flush_workqueue(struct workqueue_struct *wq);
include/linux/workqueue.h:604:extern void drain_workqueue(struct workqueue_struct *wq);
include/linux/workqueue.h-605-
--
kernel/workqueue.c=4085=EXPORT_SYMBOL(__flush_workqueue);
--
kernel/workqueue.c-4087-/**
kernel/workqueue.c:4088: * drain_workqueue - drain a workqueue
kernel/workqueue.c-4089- * @wq: workqueue to drain
--
kernel/workqueue.c-4097- */
kernel/workqueue.c:4098:void drain_workqueue(struct workqueue_struct *wq)
kernel/workqueue.c-4099-{
--
kernel/workqueue.c-4104- * __queue_work() needs to test whether there are drainers, is much
kernel/workqueue.c:4105: * hotter than drain_workqueue() and already looks at @wq->flags.
kernel/workqueue.c-4106- * Use __WQ_DRAINING so that queue doesn't have to check nr_drainers.
--
kernel/workqueue.c-4139-}
kernel/workqueue.c:4140:EXPORT_SYMBOL_GPL(drain_workqueue);
kernel/workqueue.c-4141-
--
kernel/workqueue.c=5852=void destroy_workqueue(struct workqueue_struct *wq)
--
kernel/workqueue.c-5868- /* drain it before proceeding with destruction */
kernel/workqueue.c:5869: drain_workqueue(wq);
kernel/workqueue.c-5870-
--
net/bluetooth/hci_core.c=533=static int hci_dev_do_reset(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-561- */
net/bluetooth/hci_core.c:562: drain_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-563-
--
net/bluetooth/hci_sync.c=5220=int hci_dev_close_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5279- */
net/bluetooth/hci_sync.c:5280: drain_workqueue(hdev->workqueue);
net/bluetooth/hci_sync.c-5281-
]
|
| 46/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 53466
output: 45
thoughts: 81
Thoughts: Here's my analysis of the provided code snippet:
**Debugging the `drain_workqueue` Calls**
Okay, so I've identified that `drain_workqueue(hdev->workqueue)` is being called in two specific locations within the Bluetooth stack. My initial focus is to pinpoint *why* the function is being called, because I've seen some odd behaviours stemming from it. First, I'm noting the calls specifically occur in `net/bluetooth/hci_core.c` line 562, triggered in `hci_dev_do_reset` function and `net/bluetooth/hci_sync.c` line 5280, from the `hci_dev_close_sync` function. My instinct is to investigate the latter, `hci_dev_close_sync`, since it often relates to device teardown or closure, which can be a key source of unexpected interactions. I need to understand the control flow around that specific call. So, let me delve into the `hci_dev_close_sync` implementation, and the surrounding context, so I can analyze why `drain_workqueue` is considered a necessary part of the process. Is it for guaranteed order? Safety? What's going on there?
|
| 47/2 |
2026/05/21 02:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c IncludeLines:true Name:hci_dev_close_sync]
Results: map[SourceCode:5220: int hci_dev_close_sync(struct hci_dev *hdev)
5221: {
5222: bool auto_off;
5223: int err = 0;
5224:
5225: bt_dev_dbg(hdev, "");
5226:
5227: if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
5228: disable_delayed_work(&hdev->power_off);
5229: disable_delayed_work(&hdev->ncmd_timer);
5230: disable_delayed_work(&hdev->le_scan_disable);
5231: } else {
5232: cancel_delayed_work(&hdev->power_off);
5233: cancel_delayed_work(&hdev->ncmd_timer);
5234: cancel_delayed_work(&hdev->le_scan_disable);
5235: }
5236:
5237: hci_cmd_sync_cancel_sync(hdev, ENODEV);
5238:
5239: cancel_interleave_scan(hdev);
5240:
5241: if (hdev->adv_instance_timeout) {
5242: cancel_delayed_work_sync(&hdev->adv_instance_expire);
5243: hdev->adv_instance_timeout = 0;
5244: }
5245:
5246: err = hci_dev_shutdown(hdev);
5247:
5248: if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
5249: cancel_delayed_work_sync(&hdev->cmd_timer);
5250: return err;
5251: }
5252:
5253: hci_leds_update_powered(hdev, false);
5254:
5255: /* Flush RX and TX works */
5256: flush_work(&hdev->tx_work);
5257: flush_work(&hdev->rx_work);
5258:
5259: if (hdev->discov_timeout > 0) {
5260: hdev->discov_timeout = 0;
5261: hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
5262: hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
5263: }
5264:
5265: if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
5266: cancel_delayed_work(&hdev->service_cache);
5267:
5268: if (hci_dev_test_flag(hdev, HCI_MGMT)) {
5269: struct adv_info *adv_instance;
5270:
5271: cancel_delayed_work_sync(&hdev->rpa_expired);
5272:
5273: list_for_each_entry(adv_instance, &hdev->adv_instances, list)
5274: cancel_delayed_work_sync(&adv_instance->rpa_expired_cb);
5275: }
5276:
5277: /* Avoid potential lockdep warnings from the *_flush() calls by
5278: * ensuring the workqueue is empty up front.
5279: */
5280: drain_workqueue(hdev->workqueue);
5281:
5282: hci_dev_lock(hdev);
5283:
5284: hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
5285:
5286: auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
5287:
5288: if (!auto_off && !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
5289: hci_dev_test_flag(hdev, HCI_MGMT))
5290: __mgmt_power_off(hdev);
5291:
5292: hci_inquiry_cache_flush(hdev);
5293: hci_pend_le_actions_clear(hdev);
5294: hci_conn_hash_flush(hdev);
5295: /* Prevent data races on hdev->smp_data or hdev->smp_bredr_data */
5296: smp_unregister(hdev);
5297: hci_dev_unlock(hdev);
5298:
5299: hci_sock_dev_event(hdev, HCI_DEV_DOWN);
5300:
5301: if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
5302: aosp_do_close(hdev);
5303: msft_do_close(hdev);
5304: }
5305:
5306: if (hdev->flush)
5307: hdev->flush(hdev);
5308:
5309: /* Reset device */
5310: skb_queue_purge(&hdev->cmd_q);
5311: atomic_set(&hdev->cmd_cnt, 1);
5312: if (hci_test_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE) &&
5313: !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
5314: set_bit(HCI_INIT, &hdev->flags);
5315: hci_reset_sync(hdev);
5316: clear_bit(HCI_INIT, &hdev->flags);
5317: }
5318:
5319: /* flush cmd work */
5320: flush_work(&hdev->cmd_work);
5321:
5322: /* Drop queues */
5323: skb_queue_purge(&hdev->rx_q);
5324: skb_queue_purge(&hdev->cmd_q);
5325: skb_queue_purge(&hdev->raw_q);
5326:
5327: /* Drop last sent command */
5328: if (hdev->sent_cmd) {
5329: cancel_delayed_work_sync(&hdev->cmd_timer);
5330: kfree_skb(hdev->sent_cmd);
5331: hdev->sent_cmd = NULL;
5332: }
5333:
5334: /* Drop last request */
5335: if (hdev->req_skb) {
5336: kfree_skb(hdev->req_skb);
5337: hdev->req_skb = NULL;
5338: }
5339:
5340: clear_bit(HCI_RUNNING, &hdev->flags);
5341: hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
5342:
5343: /* After this point our queues are empty and no tasks are scheduled. */
5344: hdev->close(hdev);
5345:
5346: /* Clear flags */
5347: hdev->flags &= BIT(HCI_RAW);
5348: hci_dev_clear_volatile_flags(hdev);
5349:
5350: memset(hdev->eir, 0, sizeof(hdev->eir));
5351: memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
5352: bacpy(&hdev->random_addr, BDADDR_ANY);
5353: hci_codec_list_clear(&hdev->local_codecs);
5354:
5355: hci_dev_put(hdev);
5356: return err;
5357: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 48/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 56083
output: 43
thoughts: 1584
Thoughts: **Analyzing a Potential Race Condition in Bluetooth HCI Device Closure**
Okay, so I'm digging into this Bluetooth HCI device closure code, and I see something that could be problematic. `hci_dev_close_sync` is the function that gets called when a device is closed, like through an `HCIDEVDOWN` ioctl or device unregistration. Line 5280 calls `drain_workqueue(hdev->workqueue)`. That's where things get interesting. `drain_workqueue` sets the `__WQ_DRAINING` flag on the workqueue, which basically means, "hold your horses, we're shutting down." Now, any attempt to queue a *non-chained* work while `__WQ_DRAINING` is set will trigger a `WARN_ONCE`. The work won't get queued.
The sequence looks like this: `hci_dev_close_sync` runs, `drain_workqueue` gets called (sets the draining flag), and then concurrently, a user-space process sends data on an L2CAP socket. This `sendmsg` call goes through the L2CAP stack and eventually hits `hci_send_acl`, which tries to queue `hdev->tx_work` on the same workqueue using `queue_work`. But `queue_work` sees the draining flag and warns.
The real head-scratcher is why `l2cap_chan_send` is allowed to run concurrently with `hci_dev_close_sync`. `hci_dev_close_sync` *also* calls `hci_conn_hash_flush(hdev)` on line 5294, which deletes all connections. But it calls `drain_workqueue` *before* it flushes the connections. During this "drain" time, the connections are still alive, and `l2cap_chan_send` can still be called because the socket is still connected to the L2CAP channel, which is still connected to the HCI connection. `drain_workqueue` is supposed to wait for currently pending works, but it doesn't prevent new ones from being queued, which it *should* do, according to the documentation.
However, even if the connection flushing happened first, `l2cap_chan_send` might already be running and passed the connection check within the function.
So, is this a real problem, or just a warning? The code uses `WARN_ONCE`, so it's not a crash but just a warning. If `queue_work` fails, it returns false, and the work isn't queued. The data packet, though, is added to `chan->data_q` via `hci_queue_acl`, but the `tx_work` never gets scheduled. But if the `tx_work` is never scheduled, then the packet just sits in `chan->data_q` until the connection is deleted, at which point the queue is purged, and the packet is freed. No leak or corruption, it's just that the API isn't behaving as expected. The kernel API expects the caller to prevent new work from being queued.
Let's check the code for verification. The code shows that `flush_work` is called *before* `drain_workqueue`. However, `tx_work` can still be queued again after `flush_work` and before or even during `drain_workqueue`. If it is queued during `drain_workqueue`, `queue_work` will trigger the warning.
Could this be exploited? The warning itself is a Denial of Service if `panic_on_warn` is set, but syzkaller reports it as a WARNING. In production, `panic_on_warn` is usually not set, so it's not a crash. Does it lead to a use-after-free, though? The `queue_work` call accesses `hdev->workqueue`. Is the workqueue still valid? Yes, `hdev` is kept alive by `hci_conn`, which is kept alive by `l2cap_conn`, which is kept alive by `l2cap_chan`. `hdev->workqueue` is destroyed in `hci_release_dev`, which is called when `hdev` refcount goes to 0, but since the connection holds a reference, that won't happen. The workqueue is valid, it's just being drained.
What if `tx_work` is running while `hci_release_dev` is called? The `tx_work` is disabled via `disable_work_sync(&hdev->tx_work)` during `hci_unregister_dev`. `tx_work` cannot run after `hci_unregister_dev` returns, and `hci_release_dev` is called after unregistration, so there is no use
|
| 49/2 |
2026/05/21 02:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_del]
Results: map[SourceCode:1141: void hci_conn_del(struct hci_conn *conn)
1142: {
1143: struct hci_dev *hdev = conn->hdev;
1144:
1145: BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
1146:
1147: hci_conn_unlink(conn);
1148:
1149: disable_delayed_work_sync(&conn->disc_work);
1150: disable_delayed_work_sync(&conn->auto_accept_work);
1151: disable_delayed_work_sync(&conn->idle_work);
1152:
1153: /* Remove the connection from the list so unacked logic can detect when
1154: * a certain pool is not being utilized.
1155: */
1156: hci_conn_hash_del(hdev, conn);
1157:
1158: /* Handle unacked frames:
1159: *
1160: * - In case there are no connection, or if restoring the buffers
1161: * considered in transist would overflow, restore all buffers to the
1162: * pool.
1163: * - Otherwise restore just the buffers considered in transit for the
1164: * hci_conn
1165: */
1166: switch (conn->type) {
1167: case ACL_LINK:
1168: if (!hci_conn_num(hdev, ACL_LINK) ||
1169: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1170: hdev->acl_cnt = hdev->acl_pkts;
1171: else
1172: hdev->acl_cnt += conn->sent;
1173: break;
1174: case LE_LINK:
1175: cancel_delayed_work(&conn->le_conn_timeout);
1176:
1177: if (hdev->le_pkts) {
1178: if (!hci_conn_num(hdev, LE_LINK) ||
1179: hdev->le_cnt + conn->sent > hdev->le_pkts)
1180: hdev->le_cnt = hdev->le_pkts;
1181: else
1182: hdev->le_cnt += conn->sent;
1183: } else {
1184: if ((!hci_conn_num(hdev, LE_LINK) &&
1185: !hci_conn_num(hdev, ACL_LINK)) ||
1186: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1187: hdev->acl_cnt = hdev->acl_pkts;
1188: else
1189: hdev->acl_cnt += conn->sent;
1190: }
1191: break;
1192: case CIS_LINK:
1193: case BIS_LINK:
1194: case PA_LINK:
1195: if (!hci_iso_count(hdev) ||
1196: hdev->iso_cnt + conn->sent > hdev->iso_pkts)
1197: hdev->iso_cnt = hdev->iso_pkts;
1198: else
1199: hdev->iso_cnt += conn->sent;
1200: break;
1201: }
1202:
1203: skb_queue_purge(&conn->data_q);
1204: skb_queue_purge(&conn->tx_q.queue);
1205:
1206: /* Remove the connection from the list and cleanup its remaining
1207: * state. This is a separate function since for some cases like
1208: * BT_CONNECT_SCAN we *only* want the cleanup part without the
1209: * rest of hci_conn_del.
1210: */
1211: hci_conn_cleanup(conn);
1212:
1213: /* Dequeue callbacks using connection pointer as data */
1214: hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
1215: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 50/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 59937
output: 43
thoughts: 19
Thoughts: Here's my thought process, summarized as you requested:
**Digging into Bluetooth Connection Cleanup**
Okay, so I'm looking at the connection cleanup procedure, specifically the `hci_conn_cleanup` function. What's the critical step here? Ah, there it is! I see that `hci_conn_cleanup` directly calls `hci_chan_list_flush`. This is the important connection! So, my understanding is that cleaning up the connection involves ensuring that the associated channel list is also flushed. This must be the mechanism used to ensure no stale connections are left lingering. This implies that this `hci_chan_list_flush` function is essential for the integrity of the overall cleanup and state management. I need to understand what `hci_chan_list_flush` is actually doing in more depth. It is also interesting because it could be implemented in many different ways, depending on how this channel list is implemented.
|
| 51/2 |
2026/05/21 02:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_cleanup]
Results: map[SourceCode: 140: static void hci_conn_cleanup(struct hci_conn *conn)
141: {
142: struct hci_dev *hdev = conn->hdev;
143:
144: if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
145: hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
146:
147: if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
148: hci_remove_link_key(hdev, &conn->dst);
149:
150: hci_chan_list_flush(conn);
151:
152: if (HCI_CONN_HANDLE_UNSET(conn->handle))
153: ida_free(&hdev->unset_handle_ida, conn->handle);
154:
155: if (conn->cleanup)
156: conn->cleanup(conn);
157:
158: if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
159: switch (conn->setting & SCO_AIRMODE_MASK) {
160: case SCO_AIRMODE_CVSD:
161: case SCO_AIRMODE_TRANSP:
162: if (hdev->notify)
163: hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
164: break;
165: }
166: } else {
167: if (hdev->notify)
168: hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
169: }
170:
171: debugfs_remove_recursive(conn->debugfs);
172:
173: hci_conn_del_sysfs(conn);
174:
175: hci_dev_put(hdev);
176: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 52/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 60798
output: 22
thoughts: 103
Thoughts: **Delving into HCI Channel Flushing and L2CAP Connection Management**
Okay, so I'm revisiting this `hci_chan_list_flush` function... right, it *deletes* `hci_chan`. But hold on, what about the associated `l2cap_conn` structures? My initial thought was that `l2cap_conn` was notified somehow through `hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);`, but that doesn't quite feel right. There's a nagging feeling it's handled differently, possibly via the callback mechanisms.
I need to confirm this... I think `l2cap_disconn_cfm` or something similar might shed some light on the notification process. Time to dive into that function and trace the connection teardown logic to understand how we're really cleaning up those `l2cap_conn` structures after the `hci_chan` is flushed. Let's see how that connection state is managed.
|
| 53/2 |
2026/05/21 02:37 |
tool |
grepper |
0m
Args: map[Expression:l2cap_disconn_cfm]
Results: map[Output:net/bluetooth/l2cap_core.c=7307=int l2cap_disconn_ind(struct hci_conn *hcon)
--
net/bluetooth/l2cap_core.c-7317-
net/bluetooth/l2cap_core.c:7318:static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
net/bluetooth/l2cap_core.c-7319-{
--
net/bluetooth/l2cap_core.c=7647=static struct hci_cb l2cap_cb = {
--
net/bluetooth/l2cap_core.c-7649- .connect_cfm = l2cap_connect_cfm,
net/bluetooth/l2cap_core.c:7650: .disconn_cfm = l2cap_disconn_cfm,
net/bluetooth/l2cap_core.c-7651- .security_cfm = l2cap_security_cfm,
]
|
| 54/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 61394
output: 48
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Diving into `l2cap_disconn_cfm`**
Okay, let's zero in on this `l2cap_disconn_cfm` function. I need to refresh my understanding of how this confirmation plays out in the L2CAP disconnection sequence. I want to quickly evaluate the error handling, any crucial state transitions, and make sure there are no obvious missed race conditions. I'm looking for anything that jumps out immediately β perhaps a missed cleanup, an incorrect state reset, or a potential for a double-free scenario. This often points to the root cause. This is a crucial area and can easily be overlooked. Let's make sure it's tight and robust.
|
| 55/2 |
2026/05/21 02:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_disconn_cfm]
Results: map[SourceCode:7318: static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
7319: {
7320: if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
7321: return;
7322:
7323: BT_DBG("hcon %p reason %d", hcon, reason);
7324:
7325: l2cap_conn_del(hcon, bt_to_errno(reason));
7326: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 56/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 61805
output: 18
thoughts: 45
Thoughts: Okay, here's my interpretation of those thoughts, presented as my own mental processing as an expert in the field:
**L2CAP Connection Cleanup Sequence**
Alright, so I'm focusing on the L2CAP connection teardown here. I see that the `l2cap_conn_del` function is responsible for removing all associated `l2cap_chan` structures. This is the core cleanup routine for an L2CAP connection. That's as expected; it's the standard practice.
Now, a crucial piece is the link between the HCI and the L2CAP layers. It seems `l2cap_disconn_cfm` is triggered *from* `hci_disconn_cfm`. That implies that when the HCI layer confirms a disconnection at the lower (radio) level, that event ripples up to the L2CAP layer. This is how the L2CAP knows that the underlying physical link is gone and should perform its teardown actions, and notify any connected applications or protocols.
So, the sequence is: HCI disconnects --> HCI confirms disconnect --> L2CAP sees the confirmation --> L2CAP triggers its disconnect confirm process (`l2cap_disconn_cfm`) --> `l2cap_conn_del` is called to clean up channels and state.
|
| 57/2 |
2026/05/21 02:37 |
tool |
grepper |
0m
Args: map[Expression:disconn_cfm]
Results: map[Output:include/net/bluetooth/hci_core.h=676=struct hci_conn {
--
include/net/bluetooth/hci_core.h-779- void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
include/net/bluetooth/hci_core.h:780: void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
include/net/bluetooth/hci_core.h-781-
--
include/net/bluetooth/hci_core.h=2062=struct hci_cb {
--
include/net/bluetooth/hci_core.h-2067- void (*connect_cfm) (struct hci_conn *conn, __u8 status);
include/net/bluetooth/hci_core.h:2068: void (*disconn_cfm) (struct hci_conn *conn, __u8 status);
include/net/bluetooth/hci_core.h-2069- void (*security_cfm) (struct hci_conn *conn, __u8 status,
--
include/net/bluetooth/hci_core.h=2075=static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
--
include/net/bluetooth/hci_core.h-2089-
include/net/bluetooth/hci_core.h:2090:static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
include/net/bluetooth/hci_core.h-2091-{
--
include/net/bluetooth/hci_core.h-2095- list_for_each_entry(cb, &hci_cb_list, list) {
include/net/bluetooth/hci_core.h:2096: if (cb->disconn_cfm)
include/net/bluetooth/hci_core.h:2097: cb->disconn_cfm(conn, reason);
include/net/bluetooth/hci_core.h-2098- }
--
include/net/bluetooth/hci_core.h-2100-
include/net/bluetooth/hci_core.h:2101: if (conn->disconn_cfm_cb)
include/net/bluetooth/hci_core.h:2102: conn->disconn_cfm_cb(conn, reason);
include/net/bluetooth/hci_core.h-2103-}
--
net/bluetooth/hci_conn.c=2584=void hci_conn_hash_flush(struct hci_dev *hdev)
--
net/bluetooth/hci_conn.c-2598- conn->state = BT_CLOSED;
net/bluetooth/hci_conn.c:2599: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
net/bluetooth/hci_conn.c-2600- hci_conn_del(conn);
--
net/bluetooth/hci_event.c=2681=static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
--
net/bluetooth/hci_event.c-2715- /* Inform sockets conn is gone before we delete it */
net/bluetooth/hci_event.c:2716: hci_disconn_cfm(conn, HCI_ERROR_UNSPECIFIED);
net/bluetooth/hci_event.c-2717-
--
net/bluetooth/hci_event.c-2755-
net/bluetooth/hci_event.c:2756: hci_disconn_cfm(conn, cp->reason);
net/bluetooth/hci_event.c-2757-
--
net/bluetooth/hci_event.c=3354=static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
--
net/bluetooth/hci_event.c-3422-
net/bluetooth/hci_event.c:3423: hci_disconn_cfm(conn, ev->reason);
net/bluetooth/hci_event.c-3424-
--
net/bluetooth/hci_event.c=7022=static void hci_le_big_sync_lost_evt(struct hci_dev *hdev, void *data,
--
net/bluetooth/hci_event.c-7050- clear_bit(HCI_CONN_BIG_SYNC, &bis->flags);
net/bluetooth/hci_event.c:7051: hci_disconn_cfm(bis, ev->reason);
net/bluetooth/hci_event.c-7052- hci_conn_del(bis);
--
net/bluetooth/hci_sync.c=5644=int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
--
net/bluetooth/hci_sync.c-5685- conn->state = BT_CLOSED;
net/bluetooth/hci_sync.c:5686: hci_disconn_cfm(conn, reason);
net/bluetooth/hci_sync.c-5687- hci_conn_del(conn);
--
net/bluetooth/iso.c=2238=static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
--
net/bluetooth/iso.c-2279-
net/bluetooth/iso.c:2280:static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
net/bluetooth/iso.c-2281-{
--
net/bluetooth/iso.c=2434=static struct hci_cb iso_cb = {
--
net/bluetooth/iso.c-2436- .connect_cfm = iso_connect_cfm,
net/bluetooth/iso.c:2437: .disconn_cfm = iso_disconn_cfm,
net/bluetooth/iso.c-2438-};
--
net/bluetooth/l2cap_core.c=7307=int l2cap_disconn_ind(struct hci_conn *hcon)
--
net/bluetooth/l2cap_core.c-7317-
net/bluetooth/l2cap_core.c:7318:static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
net/bluetooth/l2cap_core.c-7319-{
--
net/bluetooth/l2cap_core.c=7647=static struct hci_cb l2cap_cb = {
--
net/bluetooth/l2cap_core.c-7649- .connect_cfm = l2cap_connect_cfm,
net/bluetooth/l2cap_core.c:7650: .disconn_cfm = l2cap_disconn_cfm,
net/bluetooth/l2cap_core.c-7651- .security_cfm = l2cap_security_cfm,
--
net/bluetooth/mgmt.c=3435=static int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status)
--
net/bluetooth/mgmt.c-3449- conn->security_cfm_cb = NULL;
net/bluetooth/mgmt.c:3450: conn->disconn_cfm_cb = NULL;
net/bluetooth/mgmt.c-3451-
--
net/bluetooth/mgmt.c=3511=static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
--
net/bluetooth/mgmt.c-3623- conn->security_cfm_cb = pairing_complete_cb;
net/bluetooth/mgmt.c:3624: conn->disconn_cfm_cb = pairing_complete_cb;
net/bluetooth/mgmt.c-3625- } else {
--
net/bluetooth/mgmt.c-3627- conn->security_cfm_cb = le_pairing_complete_cb;
net/bluetooth/mgmt.c:3628: conn->disconn_cfm_cb = le_pairing_complete_cb;
net/bluetooth/mgmt.c-3629- }
--
net/bluetooth/sco.c=1425=static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
--
net/bluetooth/sco.c-1443-
net/bluetooth/sco.c:1444:static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
net/bluetooth/sco.c-1445-{
--
net/bluetooth/sco.c=1472=static struct hci_cb sco_cb = {
--
net/bluetooth/sco.c-1474- .connect_cfm = sco_connect_cfm,
net/bluetooth/sco.c:1475: .disconn_cfm = sco_disconn_cfm,
net/bluetooth/sco.c-1476-};
]
|
| 58/2 |
2026/05/21 02:37 |
llm |
expert |
0m
Model:
Tokens:
input: 64383
output: 39
thoughts: 541
Thoughts: **Analyzing a Potential Race Condition in HCI Disconnection**
Okay, so here's what I've been thinking about this HCI disconnection scenario. Essentially, `hci_conn_hash_flush` first calls `hci_disconn_cfm`, and then `hci_conn_del`. `hci_disconn_cfm` initiates a cascading disconnect through L2CAP. Specifically, `l2cap_disconn_cfm` calls `l2cap_conn_del`, which, in turn, deletes all associated `l2cap_chan`s. A critical step is that when an `l2cap_chan` is deleted, `chan->conn` gets set to NULL. This means any subsequent call to `l2cap_chan_send` should immediately fail, returning `-ENOTCONN`, which is as expected.
But here's the catch: If `l2cap_chan_send` is already *in flight*, it might have *already* checked `chan->conn` and is about to call `hci_send_acl`. `hci_send_acl`, in turn, queues work to the device's workqueue via `queue_work(hdev->workqueue, &hdev->tx_work)`. Now, here's where the race comes in: If `hci_dev_close_sync` is also running concurrently, it could be in the process of draining the same workqueue using `drain_workqueue(hdev->workqueue)`.
So, `queue_work` might be called *while* `drain_workqueue` is running. This is what's generating the warning. The work is *not* actually queued because the workqueue is draining. However, the SKB is added to `chan->data_q` (in `hci_queue_acl`), and `tx_work` is not scheduled. This means that, when `hci_conn_del` is called subsequently, it will purge `conn->data_q`, and the SKB will be freed.
Right, so that's the warning. The important question is, can this be exploited? Based on my initial assessment, I'm thinking... no. There's no memory leak because the SKB is ultimately freed. There's also no use-after-free, because the `hdev` is kept alive by `hci_conn`, which is kept alive by `l2cap_conn`, which in turn is kept alive by `l2cap_chan`, which is held by the socket. The `hdev` is thus valid, and the workqueue itself is valid, just being drained. So it looks like a benign warning.
But now waitβ¦what if `drain_workqueue` finishes, and *then* `queue_work` is called? The critical thing is, if `drain_workqueue` finishes, then it clears `__WQ_DRAINING`, right? Hold on, does `drain_workqueue` *really* clear `__WQ_DRAINING`? Need to check thatβ¦
|
| 59/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c IncludeLines:true Name:drain_workqueue]
Results: map[SourceCode:4087: /**
4088: * drain_workqueue - drain a workqueue
4089: * @wq: workqueue to drain
4090: *
4091: * Wait until the workqueue becomes empty. While draining is in progress,
4092: * only chain queueing is allowed. IOW, only currently pending or running
4093: * work items on @wq can queue further work items on it. @wq is flushed
4094: * repeatedly until it becomes empty. The number of flushing is determined
4095: * by the depth of chaining and should be relatively short. Whine if it
4096: * takes too long.
4097: */
4098: void drain_workqueue(struct workqueue_struct *wq)
4099: {
4100: unsigned int flush_cnt = 0;
4101: struct pool_workqueue *pwq;
4102:
4103: /*
4104: * __queue_work() needs to test whether there are drainers, is much
4105: * hotter than drain_workqueue() and already looks at @wq->flags.
4106: * Use __WQ_DRAINING so that queue doesn't have to check nr_drainers.
4107: */
4108: mutex_lock(&wq->mutex);
4109: if (!wq->nr_drainers++)
4110: wq->flags |= __WQ_DRAINING;
4111: mutex_unlock(&wq->mutex);
4112: reflush:
4113: __flush_workqueue(wq);
4114:
4115: mutex_lock(&wq->mutex);
4116:
4117: for_each_pwq(pwq, wq) {
4118: bool drained;
4119:
4120: raw_spin_lock_irq(&pwq->pool->lock);
4121: drained = pwq_is_empty(pwq);
4122: raw_spin_unlock_irq(&pwq->pool->lock);
4123:
4124: if (drained)
4125: continue;
4126:
4127: if (++flush_cnt == 10 ||
4128: (flush_cnt % 100 == 0 && flush_cnt <= 1000))
4129: pr_warn("workqueue %s: %s() isn't complete after %u tries\n",
4130: wq->name, __func__, flush_cnt);
4131:
4132: mutex_unlock(&wq->mutex);
4133: goto reflush;
4134: }
4135:
4136: if (!--wq->nr_drainers)
4137: wq->flags &= ~__WQ_DRAINING;
4138: mutex_unlock(&wq->mutex);
4139: }
SourceFile:kernel/workqueue.c]
|
| 60/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 66442
output: 43
thoughts: 200
Thoughts: **Analyzing a Potential Race Condition in HCI Device Closure**
Okay, so `drain_workqueue` cleans up the `__WQ_DRAINING` flag once it's done. That means, logically, if `queue_work` gets called *after* `drain_workqueue` completes, everything should be fine, and the work item will be queued as expected. But hold on... if `queue_work` is called, in this context, it'll be queuing `tx_work`. And, if I'm remembering correctly, `hci_dev_close_sync` has *already* finished that `drain_workqueue` call.
Now, after `drain_workqueue` completes, `hci_dev_close_sync` goes on to call `hci_conn_hash_flush`, and that's where things get dicey. `hci_conn_hash_flush` specifically *deletes* the connections. So, if `tx_work` runs at this point... it's going to try to process `conn->data_q`. But `hci_conn_del` *also* purges `conn->data_q` and, importantly, deletes the connection!
Is there a race then? Am I thinking this through correctly? We could have a situation where `tx_work` gets scheduled *after* the connection has been freed, leading to a memory access on a freed object? That's definitely a problem. I need to dig into `hci_tx_work` to make sure I've identified the root cause here. I'll need to confirm what the work item is doing exactly.
|
| 61/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_tx_work]
Results: map[SourceCode:3777: static void hci_tx_work(struct work_struct *work)
3778: {
3779: struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work);
3780: struct sk_buff *skb;
3781:
3782: BT_DBG("%s acl %d sco %d le %d iso %d", hdev->name, hdev->acl_cnt,
3783: hdev->sco_cnt, hdev->le_cnt, hdev->iso_cnt);
3784:
3785: if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
3786: /* Schedule queues and send stuff to HCI driver */
3787: hci_sched_sco(hdev, SCO_LINK);
3788: hci_sched_sco(hdev, ESCO_LINK);
3789: hci_sched_iso(hdev, CIS_LINK);
3790: hci_sched_iso(hdev, BIS_LINK);
3791: hci_sched_iso(hdev, PA_LINK);
3792: hci_sched_acl(hdev);
3793: hci_sched_le(hdev);
3794: }
3795:
3796: /* Send next queued raw (unknown type) packet */
3797: while ((skb = skb_dequeue(&hdev->raw_q)))
3798: hci_send_frame(hdev, skb);
3799: }
SourceFile:net/bluetooth/hci_core.c]
|
| 62/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 67484
output: 43
thoughts: 19
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing HCI Transmission Scheduling**
Right, so `hci_tx_work` is kicking things off. I see it's calling `hci_sched_acl(hdev)`. Okay, that's pretty standard. The core of this transmission process relies on the HCI scheduler, specifically the ACL (Asynchronous Connection-Less) part, to actually manage the data flow. `hdev` likely refers to the HCI device handle, meaning we're targeting a specific Bluetooth controller. This implies that this particular work queue entry focuses on scheduling ACL data for transmission. We're not talking about SCO (Synchronous Connection-Oriented) traffic or any other types of HCI commands. This is ACL traffic only. The scheduler is the crucial part that interfaces with the hardware and makes the decisions about how and when data gets sent, likely based on priorities, available buffers, and link characteristics. Good, this is the expected workflow within a Bluetooth stack. It's a fundamental part of the HCI data transmission flow.
|
| 63/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_sched_acl]
Results: map[SourceCode:3694: static void hci_sched_acl(struct hci_dev *hdev)
3695: {
3696: BT_DBG("%s", hdev->name);
3697:
3698: /* No ACL link over BR/EDR controller */
3699: if (!hci_conn_num(hdev, ACL_LINK))
3700: return;
3701:
3702: hci_sched_acl_pkt(hdev);
3703: }
SourceFile:net/bluetooth/hci_core.c]
|
| 64/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 67949
output: 45
thoughts: 22
Thoughts: Here's my understanding of the thought process, from an expert's perspective:
**Diving into ACL Packet Scheduling**
Okay, so I'm digging into this `hci_sched_acl_pkt` function. Based on the name, it's pretty clear this is related to scheduling ACL (Asynchronous Connection-Less) packets within the Bluetooth HCI (Host Controller Interface) stack. The core of my question seems to be, what's it iterating over? My initial thought is it's handling `hdev->acl_q`. That sounds right because ACL packets typically have a dedicated queue to manage them. My guess is this queue is either associated with a specific host device handle (`hdev`) or part of a shared global resource, but more likely per-device given the `hdev` prefix. Understanding precisely how it's iterating, and what criteria it's using to select packets for transmission would be key to debugging or optimizing the scheduling. Is it FIFO? Priority-based? Weighted fair queuing? That's what I want to know next.
|
| 65/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_sched_acl_pkt]
Results: map[SourceCode:3652: static void hci_sched_acl_pkt(struct hci_dev *hdev)
3653: {
3654: unsigned int cnt = hdev->acl_cnt;
3655: struct hci_chan *chan;
3656: struct sk_buff *skb;
3657: int quote;
3658:
3659: __check_timeout(hdev, cnt, ACL_LINK);
3660:
3661: while (hdev->acl_cnt &&
3662: (chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
3663: u32 priority = (skb_peek(&chan->data_q))->priority;
3664: while (quote-- && (skb = skb_peek(&chan->data_q))) {
3665: BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
3666: skb->len, skb->priority);
3667:
3668: /* Stop if priority has changed */
3669: if (skb->priority < priority)
3670: break;
3671:
3672: skb = skb_dequeue(&chan->data_q);
3673:
3674: hci_conn_enter_active_mode(chan->conn,
3675: bt_cb(skb)->force_active);
3676:
3677: hci_send_conn_frame(hdev, chan->conn, skb);
3678: hdev->acl_last_tx = jiffies;
3679:
3680: hdev->acl_cnt--;
3681: chan->sent++;
3682: chan->conn->sent++;
3683:
3684: /* Send pending SCO packets right away */
3685: hci_sched_sco(hdev, SCO_LINK);
3686: hci_sched_sco(hdev, ESCO_LINK);
3687: }
3688: }
3689:
3690: if (cnt != hdev->acl_cnt)
3691: hci_prio_recalculate(hdev, ACL_LINK);
3692: }
SourceFile:net/bluetooth/hci_core.c]
|
| 66/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 68893
output: 43
thoughts: 37
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel-level Bluetooth HCI:
**Delving into `hci_chan_sent`**
Alright, I'm digging into `hci_chan_sent`. Seems pretty straightforward at first glance β it's designed to retrieve a `chan` structure, based on an `hdev` handle. I expect a lookup, probably based on some kind of identifier within the `hdev` context. But wait a minute... I see `rcu_read_lock` being used here. Now that's interesting.
The use of RCU implies that the `chan` data is potentially being accessed concurrently from different threads, or that it's sensitive to updates from other parts of the kernel. This adds a layer of complexity to how the data is handled. So this is not just a simple lookup function. I need to understand what this lock is protecting and why RCU is required here. Is `chan` being updated while the function is reading it? Is the data potentially reordered? It's time to dig deeper and see what data races could happen here.
|
| 67/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_chan_sent]
Results: map[SourceCode:3474: static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
3475: int *quote)
3476: {
3477: struct hci_conn_hash *h = &hdev->conn_hash;
3478: struct hci_chan *chan = NULL;
3479: unsigned int num = 0, min = ~0, cur_prio = 0;
3480: struct hci_conn *conn;
3481: int conn_num = 0;
3482:
3483: BT_DBG("%s", hdev->name);
3484:
3485: rcu_read_lock();
3486:
3487: list_for_each_entry_rcu(conn, &h->list, list) {
3488: struct hci_chan *tmp;
3489:
3490: if (conn->type != type)
3491: continue;
3492:
3493: if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
3494: continue;
3495:
3496: conn_num++;
3497:
3498: list_for_each_entry_rcu(tmp, &conn->chan_list, list) {
3499: struct sk_buff *skb;
3500:
3501: if (skb_queue_empty(&tmp->data_q))
3502: continue;
3503:
3504: skb = skb_peek(&tmp->data_q);
3505: if (skb->priority < cur_prio)
3506: continue;
3507:
3508: if (skb->priority > cur_prio) {
3509: num = 0;
3510: min = ~0;
3511: cur_prio = skb->priority;
3512: }
3513:
3514: num++;
3515:
3516: if (conn->sent < min) {
3517: min = conn->sent;
3518: chan = tmp;
3519: }
3520: }
3521:
3522: if (hci_conn_num(hdev, type) == conn_num)
3523: break;
3524: }
3525:
3526: rcu_read_unlock();
3527:
3528: if (!chan)
3529: return NULL;
3530:
3531: hci_quote_sent(chan->conn, num, quote);
3532:
3533: BT_DBG("chan %p quote %d", chan, *quote);
3534: return chan;
3535: }
SourceFile:net/bluetooth/hci_core.c]
|
| 68/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 70056
output: 45
thoughts: 241
Thoughts: **RCU Concerns in HCI Channel Handling**
Okay, so I'm looking at `hci_chan_sent` and immediately something jumps out at me. It's using `rcu_read_lock()` when iterating through the connection lists (`h->list` and `conn->chan_list`), which is standard practice for RCU-protected data structures. The function returns a `chan` pointer, which is the channel object that we're going to be working with. But here's the kicker: the `rcu_read_unlock()` happens *after* `chan` is returned. And then, we have `hci_sched_acl_pkt` which then uses `chan` *outside* of the RCU read lock's protection.
Specifically, I'm thinking about this loop:
```c
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
while (quote-- && (skb = skb_peek(&chan->data_q))) {
...
```
The question that immediately arises is: Is `chan` guaranteed to be alive and valid for the entirety of this code block? That's the critical point here.
Because, if there's concurrent deletion of connections via `hci_conn_del`, things get real interesting. `hci_conn_del` removes `conn` from `h->list`. More significantly, it calls `hci_chan_list_flush(conn)`. Wait, what does *that* do? Right, it deletes all the `chan`s belonging to that connection! And, from what I'm seeing, `hci_chan_list_flush` *frees* `chan`! So if `hci_conn_del` is running concurrently, we could be looking at a use-after-free scenario. This is a potential race condition and a pretty serious issue. I need to dig into this more.
|
| 69/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_chan_list_flush]
Results: map[SourceCode:2772: void hci_chan_list_flush(struct hci_conn *conn)
2773: {
2774: struct hci_chan *chan, *n;
2775:
2776: BT_DBG("hcon %p", conn);
2777:
2778: list_for_each_entry_safe(chan, n, &conn->chan_list, list)
2779: hci_chan_del(chan);
2780: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 70/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 70950
output: 43
thoughts: 11
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `hci_chan_del`**
Alright, time to dive in. My mind immediately goes to `hci_chan_del`. Seems like something critical is happening, likely related to channel deletion within the HCI subsystem. I need to understand what's going on with this specific function. I'm already thinking about its potential implications for resource management, connection handling, and overall system stability. I'm probably going to start by tracing the function's calls and dependencies to get a sense of its role within the broader architecture. Let's see what we can find.
|
| 71/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_chan_del]
Results: map[SourceCode:2752: void hci_chan_del(struct hci_chan *chan)
2753: {
2754: struct hci_conn *conn = chan->conn;
2755: struct hci_dev *hdev = conn->hdev;
2756:
2757: BT_DBG("%s hcon %p chan %p", hdev->name, conn, chan);
2758:
2759: list_del_rcu(&chan->list);
2760:
2761: synchronize_rcu();
2762:
2763: /* Prevent new hci_chan's to be created for this hci_conn */
2764: set_bit(HCI_CONN_DROP, &conn->flags);
2765:
2766: hci_conn_put(conn);
2767:
2768: skb_queue_purge(&chan->data_q);
2769: kfree(chan);
2770: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 72/2 |
2026/05/21 02:38 |
llm |
expert |
0m
Model:
Tokens:
input: 71454
output: 43
thoughts: 2032
Thoughts: **Use-After-Free Vulnerability in HCI Subsystem**
Okay, so here's the breakdown of what I'm seeing. It looks like we have a potential use-after-free issue in the Bluetooth HCI subsystem. The vulnerability stems from a race condition between channel deletion and packet scheduling during device shutdown.
My thinking is, `hci_chan_del` calls `synchronize_rcu()` *before* freeing the channel memory. This is standard RCU practice, ensuring that any readers holding a reference to the channel complete before freeing it. The potential problem occurs within `hci_chan_sent` and the concurrent execution. `hci_chan_sent` grabs the channel, *then* releases the `rcu_read_lock()` before returning a pointer to the channel. If `hci_chan_del` is running concurrently, the `synchronize_rcu` will not wait for `hci_chan_sent`. This looks highly problematic.
Let me spell it out: `hci_chan_sent` returns a pointer to the channel *without* holding a lock or reference. It essentially hands off the channel's ownership without any protection. The critical part is when `hci_sched_acl_pkt` (which is called by `hci_tx_work`) attempts to use the channel. If, during the brief window after `hci_chan_sent` returns and before `hci_sched_acl_pkt` uses it, `hci_chan_del` has run to completion, the memory for the channel has been freed. The subsequent access to `chan->data_q` in `hci_sched_acl_pkt` will be a use-after-free.
I've traced the execution flow to be certain. Specifically, it seems that there's no lock to prevent `tx_work` from running concurrently with the channel cleanup in `hci_conn_hash_flush`, which is called during device shutdown. The `drain_workqueue` call in `hci_dev_close_sync` *does* appear to prevent concurrent calls to `hci_conn_hash_flush`. However, it only applies to work queued *before* `drain_workqueue` is called.
Here's my logic:
1. Device shutdown begins: `drain_workqueue` is called.
2. `drain_workqueue` completes. `__WQ_DRAINING` is cleared.
3. A packet may be sent. `l2cap_chan_send` is called, enqueuing a new work item (`hci_tx_work`) *after* the drain has finished.
4. The new work item is executed.
5. Simultaneously, the shutdown process moves forward: `hci_dev_close_sync` calls `hci_conn_hash_flush`.
6. `hci_conn_hash_flush` calls `hci_conn_del`, which in turn calls `hci_chan_del`.
7. `hci_chan_del` runs, removing the channel from the list, synchronizing RCU, and freeing memory.
8. `hci_tx_work` is running concurrently in another thread, and the packet processing calls `hci_chan_sent` with `rcu_read_lock` protecting the `chan` in the first phase.
9. `hci_chan_sent` unlocks RCU and returns the pointer to a now-freed channel.
10. `hci_tx_work` calls `hci_sched_acl_pkt`, which accesses a freed pointer.
The crucial point is that since `hci_chan_sent` calls `rcu_read_unlock` before returning the channel, `synchronize_rcu` in `hci_chan_del` is not guaranteed to wait for the completion of `hci_sched_acl_pkt`'s use of the channel. This is because when RCU is unlocked, the thread accessing the resources in this phase could have started execution before the other finishes and deallocates the resource. The subsequent access to `chan->data_q` is then a use-after-free.
I'm checking that the `hci_dev_lock` is *not* held during `hci_tx_work`, confirming that there's no synchronization to prevent this concurrency. The lack of locking in the `hci_tx_work` routine, coupled with the RCU usage in `hci_chan_sent`, creates the vulnerability. I need to investigate if other scheduling functions, like `hci_sched_le`, also have this issue.
|
| 73/2 |
2026/05/21 02:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_sched_le]
Results: map[SourceCode:3705: static void hci_sched_le(struct hci_dev *hdev)
3706: {
3707: struct hci_chan *chan;
3708: struct sk_buff *skb;
3709: int quote, *cnt, tmp;
3710:
3711: BT_DBG("%s", hdev->name);
3712:
3713: if (!hci_conn_num(hdev, LE_LINK))
3714: return;
3715:
3716: cnt = hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
3717:
3718: __check_timeout(hdev, *cnt, LE_LINK);
3719:
3720: tmp = *cnt;
3721: while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) {
3722: u32 priority = (skb_peek(&chan->data_q))->priority;
3723: while (quote-- && (skb = skb_peek(&chan->data_q))) {
3724: BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
3725: skb->len, skb->priority);
3726:
3727: /* Stop if priority has changed */
3728: if (skb->priority < priority)
3729: break;
3730:
3731: skb = skb_dequeue(&chan->data_q);
3732:
3733: hci_send_conn_frame(hdev, chan->conn, skb);
3734: hdev->le_last_tx = jiffies;
3735:
3736: (*cnt)--;
3737: chan->sent++;
3738: chan->conn->sent++;
3739:
3740: /* Send pending SCO packets right away */
3741: hci_sched_sco(hdev, SCO_LINK);
3742: hci_sched_sco(hdev, ESCO_LINK);
3743: }
3744: }
3745:
3746: if (*cnt != tmp)
3747: hci_prio_recalculate(hdev, LE_LINK);
3748: }
SourceFile:net/bluetooth/hci_core.c]
|
| 74/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 75198
output: 21
thoughts: 415
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in HCI Scheduling**
Okay, so I've spotted a pattern similar to a previously identified vulnerability in `hci_sched_le`. The code calls `hci_chan_sent`, which helpfully releases the `rcu_read_lock()` and returns `chan`. That looks innocent enough, but then it proceeds to access `chan->data_q`. Uh oh, this is where it gets interesting. If `hci_chan_del` were to concurrently free `chan`, we'd have a classic use-after-free situation, right?
Right, it definitely *does* free `chan`. `hci_chan_del` calls `kfree(chan)`. That confirms my initial concern. But, is this exploitable?
Let's think through the exploit path. An attacker needs to somehow trigger `hci_dev_close_sync` at the same time as sending data via L2CAP. `HCIDEVDOWN` calls `hci_dev_close_sync`, which can be triggered by an ioctl, but that requires `CAP_NET_ADMIN`. So, not directly exploitable by an unprivileged user.
But wait, is there another way to trigger this? What about unplugging the device? If the Bluetooth USB dongle is removed, `hci_unregister_dev` is called, which then calls `hci_dev_do_close` which in turn calls `hci_dev_close_sync`. This means that someone could potentially unplug a device to cause the device to close, which in turn calls `hci_dev_close_sync` and exploits the vulnerability. The issue with this is the attacker will need physical access to the device or use a virtual device (like vhci).
Could rfkill somehow trigger this? No, usually requires root or a physical button press.
Ok, let's explore `hci_dev_do_reset`. That calls `drain_workqueue(hdev->workqueue)`. Is *that* reachable by an unprivileged user? We'll need to trace its call sites to be sure.
|
| 75/2 |
2026/05/21 02:39 |
tool |
grepper |
0m
Args: map[Expression:hci_dev_do_reset]
Results: map[Output:net/bluetooth/hci_core.c=508=int hci_dev_close(__u16 dev)
--
net/bluetooth/hci_core.c-532-
net/bluetooth/hci_core.c:533:static int hci_dev_do_reset(struct hci_dev *hdev)
net/bluetooth/hci_core.c-534-{
--
net/bluetooth/hci_core.c=586=int hci_dev_reset(__u16 dev)
--
net/bluetooth/hci_core.c-609-
net/bluetooth/hci_core.c:610: err = hci_dev_do_reset(hdev);
net/bluetooth/hci_core.c-611-
]
|
| 76/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 76273
output: 43
thoughts: 1493
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in Bluetooth HCI**
Okay, so I'm diving into this Bluetooth HCI code. My initial thought was about how a Use-After-Free (UAF) might be triggered. `hci_dev_reset` requires `CAP_NET_ADMIN`, triggered via `HCIDEVRESET` ioctl. Similarly, `HCIDEVDOWN` also needs those privileges. That's a restriction. What about the `/dev/vhci` device? If a user could create and destroy virtual HCI devices through `/dev/vhci`, that could be a vector. But, it is usually root-only access, so that doesn't seem likely.
Then, I saw this Syzkaller report. It mentioned a warning: `workqueue: cannot queue hci_tx_work on wq hci0`. Importantly, this isn't a crash. It's *just* a warning. While a warning by itself isn't a security issue, it's triggered because `queue_work` is called while `drain_workqueue` is running. This might be a problem if `panic_on_warn` is set, and while this will cause a DoS, we're not using that in production builds.
The warning got me thinking. Could this warning lead to *something* more? Could it lead to a UAF if `tx_work` runs concurrently with `hci_conn_hash_flush`? However, if `queue_work` returns false when called during `drain_workqueue`, then `tx_work` *won't* be queued. Therefore, that wouldn't cause a UAF.
Then I had to consider: what if `queue_work` is called *after* `drain_workqueue` finishes? Then `queue_work` would succeed, and `tx_work` *would* be queued. And now, the timing becomes critical. `tx_work` runs concurrently with `hci_conn_hash_flush`. Could this be possible? Yes, if `l2cap_chan_send` is called after `drain_workqueue` completes, but before `hci_conn_hash_flush` removes the connection.
But then, I reviewed that `hci_dev_close_sync` *does* take the `hci_dev_lock` *after* `drain_workqueue`. So `hci_conn_hash_flush` is called under that lock. Does `l2cap_chan_send` also hold that lock? No. Does `hci_send_acl` hold it? No. So `l2cap_chan_send` could indeed call `queue_work` *after* `drain_workqueue`, and `tx_work` can be queued. Then, `tx_work` could run concurrently with `hci_conn_hash_flush`. Crucially, `tx_work` itself doesn't take the `hci_dev_lock`.
I had to double-check that. I went back to `hci_tx_work` and looked carefully. Nope, no `hci_dev_lock`. Checked `hci_sched_acl` again. Still no `hci_dev_lock`. So, yes, the UAF is possible!
But the initial report was about the warning, not the UAF *directly*. This warning occurs if `queue_work` is called *during* `drain_workqueue`, not after. That's the key. This means that to trigger it, the user would need to call `sendmsg` on an L2CAP socket concurrently with `drain_workqueue`. `drain_workqueue` is called during `hci_dev_close_sync` or `hci_dev_do_reset`. Both of those require `CAP_NET_ADMIN` through an ioctl call, which is privileged.
Could an unprivileged user trigger this? That's when I thought about the USB dongle scenario. If a user physically unplugs a USB Bluetooth dongle, `hci_unregister_dev` is called, which calls `hci_dev_close_sync`. A user with physical access could unplug the dongle and concurrently run a program sending data to an L2CAP socket. This would trigger the warning, which is triggered via a peripheral trigger.
However, the Syzkaller report is what I'm focused on. Syzkaller usually runs as root, or with user namespaces. If it *uses* user namespaces, could it get `CAP_NET_ADMIN` in that user namespace, and call `HCIDEVDOWN`? That's the next check.
|
| 77/2 |
2026/05/21 02:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sock.c IncludeLines:true Name:hci_sock_ioctl]
Results: map[SourceCode:1043: static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
1044: unsigned long arg)
1045: {
1046: void __user *argp = (void __user *)arg;
1047: struct sock *sk = sock->sk;
1048: int err;
1049:
1050: BT_DBG("cmd %x arg %lx", cmd, arg);
1051:
1052: /* Make sure the cmd is valid before doing anything */
1053: switch (cmd) {
1054: case HCIGETDEVLIST:
1055: case HCIGETDEVINFO:
1056: case HCIGETCONNLIST:
1057: case HCIDEVUP:
1058: case HCIDEVDOWN:
1059: case HCIDEVRESET:
1060: case HCIDEVRESTAT:
1061: case HCISETSCAN:
1062: case HCISETAUTH:
1063: case HCISETENCRYPT:
1064: case HCISETPTYPE:
1065: case HCISETLINKPOL:
1066: case HCISETLINKMODE:
1067: case HCISETACLMTU:
1068: case HCISETSCOMTU:
1069: case HCIINQUIRY:
1070: case HCISETRAW:
1071: case HCIGETCONNINFO:
1072: case HCIGETAUTHINFO:
1073: case HCIBLOCKADDR:
1074: case HCIUNBLOCKADDR:
1075: break;
1076: default:
1077: return -ENOIOCTLCMD;
1078: }
1079:
1080: lock_sock(sk);
1081:
1082: if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
1083: err = -EBADFD;
1084: goto done;
1085: }
1086:
1087: /* When calling an ioctl on an unbound raw socket, then ensure
1088: * that the monitor gets informed. Ensure that the resulting event
1089: * is only send once by checking if the cookie exists or not. The
1090: * socket cookie will be only ever generated once for the lifetime
1091: * of a given socket.
1092: */
1093: if (hci_sock_gen_cookie(sk)) {
1094: struct sk_buff *skb;
1095:
1096: /* Perform careful checks before setting the HCI_SOCK_TRUSTED
1097: * flag. Make sure that not only the current task but also
1098: * the socket opener has the required capability, since
1099: * privileged programs can be tricked into making ioctl calls
1100: * on HCI sockets, and the socket should not be marked as
1101: * trusted simply because the ioctl caller is privileged.
1102: */
1103: if (sk_capable(sk, CAP_NET_ADMIN))
1104: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1105:
1106: /* Send event to monitor */
1107: skb = create_monitor_ctrl_open(sk);
1108: if (skb) {
1109: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1110: HCI_SOCK_TRUSTED, NULL);
1111: kfree_skb(skb);
1112: }
1113: }
1114:
1115: release_sock(sk);
1116:
1117: switch (cmd) {
1118: case HCIGETDEVLIST:
1119: return hci_get_dev_list(argp);
1120:
1121: case HCIGETDEVINFO:
1122: return hci_get_dev_info(argp);
1123:
1124: case HCIGETCONNLIST:
1125: return hci_get_conn_list(argp);
1126:
1127: case HCIDEVUP:
1128: if (!capable(CAP_NET_ADMIN))
1129: return -EPERM;
1130: return hci_dev_open(arg);
1131:
1132: case HCIDEVDOWN:
1133: if (!capable(CAP_NET_ADMIN))
1134: return -EPERM;
1135: return hci_dev_close(arg);
1136:
1137: case HCIDEVRESET:
1138: if (!capable(CAP_NET_ADMIN))
1139: return -EPERM;
1140: return hci_dev_reset(arg);
1141:
1142: case HCIDEVRESTAT:
1143: if (!capable(CAP_NET_ADMIN))
1144: return -EPERM;
1145: return hci_dev_reset_stat(arg);
1146:
1147: case HCISETSCAN:
1148: case HCISETAUTH:
1149: case HCISETENCRYPT:
1150: case HCISETPTYPE:
1151: case HCISETLINKPOL:
1152: case HCISETLINKMODE:
1153: case HCISETACLMTU:
1154: case HCISETSCOMTU:
1155: if (!capable(CAP_NET_ADMIN))
1156: return -EPERM;
1157: return hci_dev_cmd(cmd, argp);
1158:
1159: case HCIINQUIRY:
1160: return hci_inquiry(argp);
1161: }
1162:
1163: lock_sock(sk);
1164:
1165: err = hci_sock_bound_ioctl(sk, cmd, arg);
1166:
1167: done:
1168: release_sock(sk);
1169: return err;
1170: }
SourceFile:net/bluetooth/hci_sock.c]
|
| 78/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 80549
output: 16
thoughts: 96
Thoughts: **Analyzing the `HCIDEVDOWN` Vulnerability**
Okay, so we're looking at this `HCIDEVDOWN` trigger. The code uses `capable(CAP_NET_ADMIN)`, not `ns_capable()`. That's a key distinction. What that means is a global `CAP_NET_ADMIN` is needed. Right, that makes it immediately clear: an unprivileged user operating within a user namespace is effectively blocked from triggering this vulnerability directly. The security boundary seems pretty solid in that case. The attack surface is narrowed considerably.
So, the only straightforward path for an unprivileged user is physically yanking out a USB Bluetooth dongle. Okay, that's not ideal, but it's less concerning than a purely software-based exploit. Still, we need to think beyond the obvious.
Wait a second... what about `vhci`? Could `vhci`, the virtual HCI device, be leveraged here somehow? It's time to check the permissions associated with `vhci`. Let's see if that introduces another attack vector. We have to go deeper and check the specifics.
|
| 79/2 |
2026/05/21 02:39 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 2008 lines.
Use more precise expression if possible.
[Documentation/admin-guide/devices.txt-274- 135 = /dev/rtc Real Time Clock
Documentation/admin-guide/devices.txt:275: 137 = /dev/vhci Bluetooth virtual HCI driver
Documentation/admin-guide/devices.txt-276- 139 = /dev/openprom SPARC OpenBoot PROM
--
drivers/bluetooth/Kconfig=377=config BT_HCIVHCI
--
drivers/bluetooth/Kconfig-384- Say Y here to compile support for virtual HCI devices into the
drivers/bluetooth/Kconfig:385: kernel or say M to compile it as module (hci_vhci).
drivers/bluetooth/Kconfig-386-
--
drivers/bluetooth/Makefile-5-
drivers/bluetooth/Makefile:6:obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
drivers/bluetooth/Makefile-7-obj-$(CONFIG_BT_HCIUART) += hci_uart.o
--
drivers/bluetooth/hci_vhci.c=32=static bool amp;
drivers/bluetooth/hci_vhci.c-33-
drivers/bluetooth/hci_vhci.c:34:struct vhci_data {
drivers/bluetooth/hci_vhci.c-35- struct hci_dev *hdev;
--
drivers/bluetooth/hci_vhci.c-50-
drivers/bluetooth/hci_vhci.c:51:static int vhci_open_dev(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-52-{
--
drivers/bluetooth/hci_vhci.c-55-
drivers/bluetooth/hci_vhci.c:56:static int vhci_close_dev(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-57-{
drivers/bluetooth/hci_vhci.c:58: struct vhci_data *data = hci_get_drvdata(hdev);
drivers/bluetooth/hci_vhci.c-59-
--
drivers/bluetooth/hci_vhci.c-64-
drivers/bluetooth/hci_vhci.c:65:static int vhci_flush(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-66-{
drivers/bluetooth/hci_vhci.c:67: struct vhci_data *data = hci_get_drvdata(hdev);
drivers/bluetooth/hci_vhci.c-68-
--
drivers/bluetooth/hci_vhci.c-73-
drivers/bluetooth/hci_vhci.c:74:static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
drivers/bluetooth/hci_vhci.c-75-{
drivers/bluetooth/hci_vhci.c:76: struct vhci_data *data = hci_get_drvdata(hdev);
drivers/bluetooth/hci_vhci.c-77-
--
drivers/bluetooth/hci_vhci.c-86-
drivers/bluetooth/hci_vhci.c:87:static int vhci_get_data_path_id(struct hci_dev *hdev, u8 *data_path_id)
drivers/bluetooth/hci_vhci.c-88-{
--
drivers/bluetooth/hci_vhci.c-92-
drivers/bluetooth/hci_vhci.c:93:static int vhci_get_codec_config_data(struct hci_dev *hdev, __u8 type,
drivers/bluetooth/hci_vhci.c-94- struct bt_codec *codec, __u8 *vnd_len,
--
drivers/bluetooth/hci_vhci.c-104-
drivers/bluetooth/hci_vhci.c:105:static bool vhci_wakeup(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-106-{
drivers/bluetooth/hci_vhci.c:107: struct vhci_data *data = hci_get_drvdata(hdev);
drivers/bluetooth/hci_vhci.c-108-
--
drivers/bluetooth/hci_vhci.c=112=static ssize_t force_suspend_read(struct file *file, char __user *user_buf,
--
drivers/bluetooth/hci_vhci.c-114-{
drivers/bluetooth/hci_vhci.c:115: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-116- char buf[3];
--
drivers/bluetooth/hci_vhci.c-123-
drivers/bluetooth/hci_vhci.c:124:static void vhci_suspend_work(struct work_struct *work)
drivers/bluetooth/hci_vhci.c-125-{
drivers/bluetooth/hci_vhci.c:126: struct vhci_data *data = container_of(work, struct vhci_data,
drivers/bluetooth/hci_vhci.c-127- suspend_work);
--
drivers/bluetooth/hci_vhci.c=135=static ssize_t force_suspend_write(struct file *file,
--
drivers/bluetooth/hci_vhci.c-138-{
drivers/bluetooth/hci_vhci.c:139: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-140- bool enable;
--
drivers/bluetooth/hci_vhci.c=164=static ssize_t force_wakeup_read(struct file *file, char __user *user_buf,
--
drivers/bluetooth/hci_vhci.c-166-{
drivers/bluetooth/hci_vhci.c:167: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-168- char buf[3];
--
drivers/bluetooth/hci_vhci.c=176=static ssize_t force_wakeup_write(struct file *file,
--
drivers/bluetooth/hci_vhci.c-179-{
drivers/bluetooth/hci_vhci.c:180: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-181- bool enable;
--
drivers/bluetooth/hci_vhci.c=203=static int msft_opcode_set(void *data, u64 val)
drivers/bluetooth/hci_vhci.c-204-{
drivers/bluetooth/hci_vhci.c:205: struct vhci_data *vhci = data;
drivers/bluetooth/hci_vhci.c-206-
--
drivers/bluetooth/hci_vhci.c-209-
drivers/bluetooth/hci_vhci.c:210: if (vhci->msft_opcode)
drivers/bluetooth/hci_vhci.c-211- return -EALREADY;
drivers/bluetooth/hci_vhci.c-212-
drivers/bluetooth/hci_vhci.c:213: vhci->msft_opcode = val;
drivers/bluetooth/hci_vhci.c-214-
--
drivers/bluetooth/hci_vhci.c=218=static int msft_opcode_get(void *data, u64 *val)
drivers/bluetooth/hci_vhci.c-219-{
drivers/bluetooth/hci_vhci.c:220: struct vhci_data *vhci = data;
drivers/bluetooth/hci_vhci.c-221-
drivers/bluetooth/hci_vhci.c:222: *val = vhci->msft_opcode;
drivers/bluetooth/hci_vhci.c-223-
--
drivers/bluetooth/hci_vhci.c=230=static ssize_t aosp_capable_read(struct file *file, char __user *user_buf,
--
drivers/bluetooth/hci_vhci.c-232-{
drivers/bluetooth/hci_vhci.c:233: struct vhci_data *vhci = file->private_data;
drivers/bluetooth/hci_vhci.c-234- char buf[3];
drivers/bluetooth/hci_vhci.c-235-
drivers/bluetooth/hci_vhci.c:236: buf[0] = vhci->aosp_capable ? 'Y' : 'N';
drivers/bluetooth/hci_vhci.c-237- buf[1] = '\n';
--
drivers/bluetooth/hci_vhci.c=242=static ssize_t aosp_capable_write(struct file *file,
--
drivers/bluetooth/hci_vhci.c-245-{
drivers/bluetooth/hci_vhci.c:246: struct vhci_data *vhci = file->private_data;
drivers/bluetooth/hci_vhci.c-247- bool enable;
--
drivers/bluetooth/hci_vhci.c-256-
drivers/bluetooth/hci_vhci.c:257: if (vhci->aosp_capable)
drivers/bluetooth/hci_vhci.c-258- return -EALREADY;
drivers/bluetooth/hci_vhci.c-259-
drivers/bluetooth/hci_vhci.c:260: vhci->aosp_capable = enable;
drivers/bluetooth/hci_vhci.c-261-
--
drivers/bluetooth/hci_vhci.c=265=static const struct file_operations aosp_capable_fops = {
--
drivers/bluetooth/hci_vhci.c-271-
drivers/bluetooth/hci_vhci.c:272:static int vhci_setup(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-273-{
drivers/bluetooth/hci_vhci.c:274: struct vhci_data *vhci = hci_get_drvdata(hdev);
drivers/bluetooth/hci_vhci.c-275-
drivers/bluetooth/hci_vhci.c:276: if (vhci->msft_opcode)
drivers/bluetooth/hci_vhci.c:277: hci_set_msft_opcode(hdev, vhci->msft_opcode);
drivers/bluetooth/hci_vhci.c-278-
drivers/bluetooth/hci_vhci.c:279: if (vhci->aosp_capable)
drivers/bluetooth/hci_vhci.c-280- hci_set_aosp_capable(hdev);
--
drivers/bluetooth/hci_vhci.c-284-
drivers/bluetooth/hci_vhci.c:285:static void vhci_coredump(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-286-{
--
drivers/bluetooth/hci_vhci.c-289-
drivers/bluetooth/hci_vhci.c:290:static void vhci_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
drivers/bluetooth/hci_vhci.c-291-{
--
drivers/bluetooth/hci_vhci.c-293-
drivers/bluetooth/hci_vhci.c:294: buf = "Controller Name: vhci_ctrl\n";
drivers/bluetooth/hci_vhci.c-295- skb_put_data(skb, buf, strlen(buf));
drivers/bluetooth/hci_vhci.c-296-
drivers/bluetooth/hci_vhci.c:297: buf = "Firmware Version: vhci_fw\n";
drivers/bluetooth/hci_vhci.c-298- skb_put_data(skb, buf, strlen(buf));
drivers/bluetooth/hci_vhci.c-299-
drivers/bluetooth/hci_vhci.c:300: buf = "Driver: vhci_drv\n";
drivers/bluetooth/hci_vhci.c-301- skb_put_data(skb, buf, strlen(buf));
drivers/bluetooth/hci_vhci.c-302-
drivers/bluetooth/hci_vhci.c:303: buf = "Vendor: vhci\n";
drivers/bluetooth/hci_vhci.c-304- skb_put_data(skb, buf, strlen(buf));
--
drivers/bluetooth/hci_vhci.c=323=static ssize_t force_devcd_write(struct file *file, const char __user *user_buf,
--
drivers/bluetooth/hci_vhci.c-325-{
drivers/bluetooth/hci_vhci.c:326: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-327- struct hci_dev *hdev = data->hdev;
--
drivers/bluetooth/hci_vhci.c-345-
drivers/bluetooth/hci_vhci.c:346: hci_devcd_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL);
drivers/bluetooth/hci_vhci.c-347-
--
drivers/bluetooth/hci_vhci.c=378=static const struct file_operations force_devcoredump_fops = {
--
drivers/bluetooth/hci_vhci.c-382-
drivers/bluetooth/hci_vhci.c:383:static void vhci_debugfs_init(struct vhci_data *data)
drivers/bluetooth/hci_vhci.c-384-{
--
drivers/bluetooth/hci_vhci.c-404-
drivers/bluetooth/hci_vhci.c:405:static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
drivers/bluetooth/hci_vhci.c-406-{
--
drivers/bluetooth/hci_vhci.c-431-
drivers/bluetooth/hci_vhci.c:432: hdev->open = vhci_open_dev;
drivers/bluetooth/hci_vhci.c:433: hdev->close = vhci_close_dev;
drivers/bluetooth/hci_vhci.c:434: hdev->flush = vhci_flush;
drivers/bluetooth/hci_vhci.c:435: hdev->send = vhci_send_frame;
drivers/bluetooth/hci_vhci.c:436: hdev->get_data_path_id = vhci_get_data_path_id;
drivers/bluetooth/hci_vhci.c:437: hdev->get_codec_config_data = vhci_get_codec_config_data;
drivers/bluetooth/hci_vhci.c:438: hdev->wakeup = vhci_wakeup;
drivers/bluetooth/hci_vhci.c:439: hdev->setup = vhci_setup;
drivers/bluetooth/hci_vhci.c-440- hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
--
drivers/bluetooth/hci_vhci.c-459- if (!IS_ERR_OR_NULL(hdev->debugfs))
drivers/bluetooth/hci_vhci.c:460: vhci_debugfs_init(data);
drivers/bluetooth/hci_vhci.c-461-
--
drivers/bluetooth/hci_vhci.c-473-
drivers/bluetooth/hci_vhci.c:474:static int vhci_create_device(struct vhci_data *data, __u8 opcode)
drivers/bluetooth/hci_vhci.c-475-{
--
drivers/bluetooth/hci_vhci.c-478- mutex_lock(&data->open_mutex);
drivers/bluetooth/hci_vhci.c:479: err = __vhci_create_device(data, opcode);
drivers/bluetooth/hci_vhci.c-480- mutex_unlock(&data->open_mutex);
--
drivers/bluetooth/hci_vhci.c-484-
drivers/bluetooth/hci_vhci.c:485:static inline ssize_t vhci_get_user(struct vhci_data *data,
drivers/bluetooth/hci_vhci.c-486- struct iov_iter *from)
--
drivers/bluetooth/hci_vhci.c-535-
drivers/bluetooth/hci_vhci.c:536: ret = vhci_create_device(data, opcode);
drivers/bluetooth/hci_vhci.c-537- break;
--
drivers/bluetooth/hci_vhci.c-546-
drivers/bluetooth/hci_vhci.c:547:static inline ssize_t vhci_put_user(struct vhci_data *data,
drivers/bluetooth/hci_vhci.c-548- struct sk_buff *skb,
--
drivers/bluetooth/hci_vhci.c-578-
drivers/bluetooth/hci_vhci.c:579:static ssize_t vhci_read(struct file *file,
drivers/bluetooth/hci_vhci.c-580- char __user *buf, size_t count, loff_t *pos)
drivers/bluetooth/hci_vhci.c-581-{
drivers/bluetooth/hci_vhci.c:582: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-583- struct sk_buff *skb;
--
drivers/bluetooth/hci_vhci.c-588- if (skb) {
drivers/bluetooth/hci_vhci.c:589: ret = vhci_put_user(data, skb, buf, count);
drivers/bluetooth/hci_vhci.c-590- if (ret < 0)
--
drivers/bluetooth/hci_vhci.c-610-
drivers/bluetooth/hci_vhci.c:611:static ssize_t vhci_write(struct kiocb *iocb, struct iov_iter *from)
drivers/bluetooth/hci_vhci.c-612-{
drivers/bluetooth/hci_vhci.c-613- struct file *file = iocb->ki_filp;
drivers/bluetooth/hci_vhci.c:614: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-615-
drivers/bluetooth/hci_vhci.c:616: return vhci_get_user(data, from);
drivers/bluetooth/hci_vhci.c-617-}
drivers/bluetooth/hci_vhci.c-618-
drivers/bluetooth/hci_vhci.c:619:static __poll_t vhci_poll(struct file *file, poll_table *wait)
drivers/bluetooth/hci_vhci.c-620-{
drivers/bluetooth/hci_vhci.c:621: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-622-
--
drivers/bluetooth/hci_vhci.c-630-
drivers/bluetooth/hci_vhci.c:631:static void vhci_open_timeout(struct work_struct *work)
drivers/bluetooth/hci_vhci.c-632-{
drivers/bluetooth/hci_vhci.c:633: struct vhci_data *data = container_of(work, struct vhci_data,
drivers/bluetooth/hci_vhci.c-634- open_timeout.work);
drivers/bluetooth/hci_vhci.c-635-
drivers/bluetooth/hci_vhci.c:636: vhci_create_device(data, 0x00);
drivers/bluetooth/hci_vhci.c-637-}
drivers/bluetooth/hci_vhci.c-638-
drivers/bluetooth/hci_vhci.c:639:static int vhci_open(struct inode *inode, struct file *file)
drivers/bluetooth/hci_vhci.c-640-{
drivers/bluetooth/hci_vhci.c:641: struct vhci_data *data;
drivers/bluetooth/hci_vhci.c-642-
--
drivers/bluetooth/hci_vhci.c-650- mutex_init(&data->open_mutex);
drivers/bluetooth/hci_vhci.c:651: INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
drivers/bluetooth/hci_vhci.c:652: INIT_WORK(&data->suspend_work, vhci_suspend_work);
drivers/bluetooth/hci_vhci.c-653-
--
drivers/bluetooth/hci_vhci.c-661-
drivers/bluetooth/hci_vhci.c:662:static void vhci_debugfs_remove(struct hci_dev *hdev)
drivers/bluetooth/hci_vhci.c-663-{
--
drivers/bluetooth/hci_vhci.c-676-
drivers/bluetooth/hci_vhci.c:677:static int vhci_release(struct inode *inode, struct file *file)
drivers/bluetooth/hci_vhci.c-678-{
drivers/bluetooth/hci_vhci.c:679: struct vhci_data *data = file->private_data;
drivers/bluetooth/hci_vhci.c-680- struct hci_dev *hdev;
--
drivers/bluetooth/hci_vhci.c-688- if (!IS_ERR_OR_NULL(hdev->debugfs))
drivers/bluetooth/hci_vhci.c:689: vhci_debugfs_remove(hdev);
drivers/bluetooth/hci_vhci.c-690- hci_unregister_dev(hdev);
--
drivers/bluetooth/hci_vhci.c-700-
drivers/bluetooth/hci_vhci.c:701:static const struct file_operations vhci_fops = {
drivers/bluetooth/hci_vhci.c-702- .owner = THIS_MODULE,
drivers/bluetooth/hci_vhci.c:703: .read = vhci_read,
drivers/bluetooth/hci_vhci.c:704: .write_iter = vhci_write,
drivers/bluetooth/hci_vhci.c:705: .poll = vhci_poll,
drivers/bluetooth/hci_vhci.c:706: .open = vhci_open,
drivers/bluetooth/hci_vhci.c:707: .release = vhci_release,
drivers/bluetooth/hci_vhci.c-708-};
drivers/bluetooth/hci_vhci.c-709-
drivers/bluetooth/hci_vhci.c:710:static struct miscdevice vhci_miscdev = {
drivers/bluetooth/hci_vhci.c:711: .name = "vhci",
drivers/bluetooth/hci_vhci.c:712: .fops = &vhci_fops,
drivers/bluetooth/hci_vhci.c-713- .minor = VHCI_MINOR,
drivers/bluetooth/hci_vhci.c-714-};
drivers/bluetooth/hci_vhci.c:715:module_misc_device(vhci_miscdev);
drivers/bluetooth/hci_vhci.c-716-
--
drivers/bluetooth/hci_vhci.c=723=MODULE_LICENSE("GPL");
drivers/bluetooth/hci_vhci.c:724:MODULE_ALIAS("devname:vhci");
drivers/bluetooth/hci_vhci.c-725-MODULE_ALIAS_MISCDEV(VHCI_MINOR);
--
drivers/usb/usbip/Kconfig=21=config USBIP_VHCI_HCD
--
drivers/usb/usbip/Kconfig-28- To compile this driver as a module, choose M here: the
drivers/usb/usbip/Kconfig:29: module will be called vhci-hcd.
drivers/usb/usbip/Kconfig-30-
--
drivers/usb/usbip/Makefile=5=usbip-core-y := usbip_common.o usbip_event.o
drivers/usb/usbip/Makefile-6-
drivers/usb/usbip/Makefile:7:obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o
drivers/usb/usbip/Makefile:8:vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
drivers/usb/usbip/Makefile-9-
--
drivers/usb/usbip/stub_dev.c=310=static int stub_probe(struct usb_device *udev)
--
drivers/usb/usbip/stub_dev.c-353-
drivers/usb/usbip/stub_dev.c:354: if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
drivers/usb/usbip/stub_dev.c-355- dev_dbg(&udev->dev,
drivers/usb/usbip/stub_dev.c:356: "%s is attached on vhci_hcd... skip!\n",
drivers/usb/usbip/stub_dev.c-357- udev_busid);
--
drivers/usb/usbip/stub_rx.c=173=static int tweak_special_requests(struct urb *urb)
--
drivers/usb/usbip/stub_rx.c-210- *
drivers/usb/usbip/stub_rx.c:211: * See also comments about unlinking strategy in vhci_hcd.c.
drivers/usb/usbip/stub_rx.c-212- */
drivers/usb/usbip/stub_rx.c=213=static int stub_recv_cmd_unlink(struct stub_device *sdev,
--
drivers/usb/usbip/stub_rx.c-278- * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only
drivers/usb/usbip/stub_rx.c:279: * return the completeness of this unlink request to vhci_hcd.
drivers/usb/usbip/stub_rx.c-280- */
--
drivers/usb/usbip/stub_tx.c=40=void stub_complete(struct urb *urb)
--
drivers/usb/usbip/stub_tx.c-75- * If the server breaks single SG request into the several URBs, the
drivers/usb/usbip/stub_tx.c:76: * URBs must be reassembled before sending completed URB to the vhci.
drivers/usb/usbip/stub_tx.c-77- * Don't wake up the tx thread until all the URBs are completed.
--
drivers/usb/usbip/stub_tx.c=417=int stub_tx_loop(void *data)
--
drivers/usb/usbip/stub_tx.c-430- * priv_tx queue and stub_rx does not find the target priv. In
drivers/usb/usbip/stub_tx.c:431: * this case, vhci_rx receives the result of the submit request
drivers/usb/usbip/stub_tx.c-432- * and then receives the result of the unlink request. The
--
drivers/usb/usbip/usbip_common.c=342=static unsigned int tweak_transfer_flags(unsigned int flags)
--
drivers/usb/usbip/usbip_common.c-349- * USBIP driver packs URB transfer flags in PDUs that are exchanged
drivers/usb/usbip/usbip_common.c:350: * between Server (usbip_host) and Client (vhci_hcd). URB_* flags
drivers/usb/usbip/usbip_common.c-351- * are internal to kernel and could change. Where as USBIP URB flags
--
drivers/usb/usbip/usbip_common.h=32=enum {
--
drivers/usb/usbip/usbip_common.h-42-
drivers/usb/usbip/usbip_common.h:43: usbip_debug_vhci_rh = (1 << 8),
drivers/usb/usbip/usbip_common.h:44: usbip_debug_vhci_hc = (1 << 9),
drivers/usb/usbip/usbip_common.h:45: usbip_debug_vhci_rx = (1 << 10),
drivers/usb/usbip/usbip_common.h:46: usbip_debug_vhci_tx = (1 << 11),
drivers/usb/usbip/usbip_common.h:47: usbip_debug_vhci_sysfs = (1 << 12)
drivers/usb/usbip/usbip_common.h-48-};
--
drivers/usb/usbip/usbip_common.h-50-#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit)
drivers/usb/usbip/usbip_common.h:51:#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh)
drivers/usb/usbip/usbip_common.h:52:#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc)
drivers/usb/usbip/usbip_common.h:53:#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx)
drivers/usb/usbip/usbip_common.h:54:#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx)
drivers/usb/usbip/usbip_common.h-55-#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx)
drivers/usb/usbip/usbip_common.h-56-#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx)
drivers/usb/usbip/usbip_common.h:57:#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs)
drivers/usb/usbip/usbip_common.h-58-
--
drivers/usb/usbip/usbip_common.h=60=extern struct device_attribute dev_attr_usbip_debug;
--
drivers/usb/usbip/usbip_common.h-76-
drivers/usb/usbip/usbip_common.h:77:#define usbip_dbg_vhci_rh(fmt, args...) \
drivers/usb/usbip/usbip_common.h:78: usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args)
drivers/usb/usbip/usbip_common.h:79:#define usbip_dbg_vhci_hc(fmt, args...) \
drivers/usb/usbip/usbip_common.h:80: usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args)
drivers/usb/usbip/usbip_common.h:81:#define usbip_dbg_vhci_rx(fmt, args...) \
drivers/usb/usbip/usbip_common.h:82: usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args)
drivers/usb/usbip/usbip_common.h:83:#define usbip_dbg_vhci_tx(fmt, args...) \
drivers/usb/usbip/usbip_common.h:84: usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args)
drivers/usb/usbip/usbip_common.h:85:#define usbip_dbg_vhci_sysfs(fmt, args...) \
drivers/usb/usbip/usbip_common.h:86: usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args)
drivers/usb/usbip/usbip_common.h-87-
--
drivers/usb/usbip/usbip_common.h=229=enum usbip_side {
--
drivers/usb/usbip/usbip_common.h-258-
drivers/usb/usbip/usbip_common.h:259:/* a common structure for stub_device and vhci_device */
drivers/usb/usbip/usbip_common.h-260-struct usbip_device {
--
drivers/usb/usbip/vhci.h-18-
drivers/usb/usbip/vhci.h:19:struct vhci_device {
drivers/usb/usbip/vhci.h-20- struct usb_device *udev;
--
drivers/usb/usbip/vhci.h-30-
drivers/usb/usbip/vhci.h:31: /* vhci root-hub port to which this device is attached */
drivers/usb/usbip/vhci.h-32- __u32 rhport;
--
drivers/usb/usbip/vhci.h-38-
drivers/usb/usbip/vhci.h:39: /* vhci_priv is linked to one of them. */
drivers/usb/usbip/vhci.h-40- struct list_head priv_tx;
--
drivers/usb/usbip/vhci.h-42-
drivers/usb/usbip/vhci.h:43: /* vhci_unlink is linked to one of them */
drivers/usb/usbip/vhci.h-44- struct list_head unlink_tx;
--
drivers/usb/usbip/vhci.h-46-
drivers/usb/usbip/vhci.h:47: /* vhci_tx thread sleeps for this queue */
drivers/usb/usbip/vhci.h-48- wait_queue_head_t waitq_tx;
--
drivers/usb/usbip/vhci.h-51-/* urb->hcpriv, use container_of() */
drivers/usb/usbip/vhci.h:52:struct vhci_priv {
drivers/usb/usbip/vhci.h-53- unsigned long seqnum;
--
drivers/usb/usbip/vhci.h-55-
drivers/usb/usbip/vhci.h:56: struct vhci_device *vdev;
drivers/usb/usbip/vhci.h-57- struct urb *urb;
--
drivers/usb/usbip/vhci.h-59-
drivers/usb/usbip/vhci.h:60:struct vhci_unlink {
drivers/usb/usbip/vhci.h-61- /* seqnum of this request */
--
drivers/usb/usbip/vhci.h=70=enum hub_speed {
--
drivers/usb/usbip/vhci.h-92-
drivers/usb/usbip/vhci.h:93:struct vhci {
drivers/usb/usbip/vhci.h-94- spinlock_t lock;
--
drivers/usb/usbip/vhci.h-97-
drivers/usb/usbip/vhci.h:98: struct vhci_hcd *vhci_hcd_hs;
drivers/usb/usbip/vhci.h:99: struct vhci_hcd *vhci_hcd_ss;
drivers/usb/usbip/vhci.h-100-};
--
drivers/usb/usbip/vhci.h-102-/* for usb_hcd.hcd_priv[0] */
drivers/usb/usbip/vhci.h:103:struct vhci_hcd {
drivers/usb/usbip/vhci.h:104: struct vhci *vhci;
drivers/usb/usbip/vhci.h-105-
--
drivers/usb/usbip/vhci.h-117- */
drivers/usb/usbip/vhci.h:118: struct vhci_device vdev[VHCI_HC_PORTS];
drivers/usb/usbip/vhci.h-119-};
drivers/usb/usbip/vhci.h-120-
drivers/usb/usbip/vhci.h:121:extern int vhci_num_controllers;
drivers/usb/usbip/vhci.h:122:extern struct vhci *vhcis;
drivers/usb/usbip/vhci.h:123:extern struct attribute_group vhci_attr_group;
drivers/usb/usbip/vhci.h-124-
drivers/usb/usbip/vhci.h:125:/* vhci_hcd.c */
drivers/usb/usbip/vhci.h:126:void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed);
drivers/usb/usbip/vhci.h-127-
drivers/usb/usbip/vhci.h:128:/* vhci_sysfs.c */
drivers/usb/usbip/vhci.h:129:int vhci_init_attr_group(void);
drivers/usb/usbip/vhci.h:130:void vhci_finish_attr_group(void);
drivers/usb/usbip/vhci.h-131-
drivers/usb/usbip/vhci.h:132:/* vhci_rx.c */
drivers/usb/usbip/vhci.h:133:struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
drivers/usb/usbip/vhci.h:134:int vhci_rx_loop(void *data);
drivers/usb/usbip/vhci.h-135-
drivers/usb/usbip/vhci.h:136:/* vhci_tx.c */
drivers/usb/usbip/vhci.h:137:int vhci_tx_loop(void *data);
drivers/usb/usbip/vhci.h-138-
--
drivers/usb/usbip/vhci.h=144=static inline int port_to_pdev_nr(__u32 port)
--
drivers/usb/usbip/vhci.h-148-
drivers/usb/usbip/vhci.h:149:static inline struct vhci_hcd *hcd_to_vhci_hcd(struct usb_hcd *hcd)
drivers/usb/usbip/vhci.h-150-{
drivers/usb/usbip/vhci.h:151: return (struct vhci_hcd *) (hcd->hcd_priv);
drivers/usb/usbip/vhci.h-152-}
--
drivers/usb/usbip/vhci.h=159=static inline const char *hcd_name(struct usb_hcd *hcd)
--
drivers/usb/usbip/vhci.h-163-
drivers/usb/usbip/vhci.h:164:static inline struct usb_hcd *vhci_hcd_to_hcd(struct vhci_hcd *vhci_hcd)
drivers/usb/usbip/vhci.h-165-{
drivers/usb/usbip/vhci.h:166: return container_of((void *) vhci_hcd, struct usb_hcd, hcd_priv);
drivers/usb/usbip/vhci.h-167-}
drivers/usb/usbip/vhci.h-168-
drivers/usb/usbip/vhci.h:169:static inline struct vhci_hcd *vdev_to_vhci_hcd(struct vhci_device *vdev)
drivers/usb/usbip/vhci.h-170-{
drivers/usb/usbip/vhci.h:171: return container_of((void *)(vdev - vdev->rhport), struct vhci_hcd, vdev);
drivers/usb/usbip/vhci.h-172-}
--
drivers/usb/usbip/vhci_hcd.c-16-#include "usbip_common.h"
drivers/usb/usbip/vhci_hcd.c:17:#include "vhci.h"
drivers/usb/usbip/vhci_hcd.c-18-
--
drivers/usb/usbip/vhci_hcd.c-33-
drivers/usb/usbip/vhci_hcd.c:34:static int vhci_hub_status(struct usb_hcd *hcd, char *buff);
drivers/usb/usbip/vhci_hcd.c:35:static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
drivers/usb/usbip/vhci_hcd.c-36- u16 wIndex, char *buff, u16 wLength);
drivers/usb/usbip/vhci_hcd.c:37:static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
drivers/usb/usbip/vhci_hcd.c-38- gfp_t mem_flags);
drivers/usb/usbip/vhci_hcd.c:39:static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
drivers/usb/usbip/vhci_hcd.c:40:static int vhci_start(struct usb_hcd *vhci_hcd);
drivers/usb/usbip/vhci_hcd.c:41:static void vhci_stop(struct usb_hcd *hcd);
drivers/usb/usbip/vhci_hcd.c:42:static int vhci_get_frame_number(struct usb_hcd *hcd);
drivers/usb/usbip/vhci_hcd.c-43-
drivers/usb/usbip/vhci_hcd.c:44:static const char driver_name[] = "vhci_hcd";
drivers/usb/usbip/vhci_hcd.c-45-static const char driver_desc[] = "USB/IP Virtual Host Controller";
drivers/usb/usbip/vhci_hcd.c-46-
drivers/usb/usbip/vhci_hcd.c:47:int vhci_num_controllers = VHCI_NR_HCS;
drivers/usb/usbip/vhci_hcd.c:48:struct vhci *vhcis;
drivers/usb/usbip/vhci_hcd.c-49-
--
drivers/usb/usbip/vhci_hcd.c=120=static void dump_port_status_diff(u32 prev_status, u32 new_status, bool usb3)
]
]
|
| 80/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 91795
output: 44
thoughts: 76
Thoughts: Okay, here's my take on those thoughts, framed as my own mental process:
**Diving into the Bluetooth Virtual HCI Driver**
Right, so `/dev/vhci` β that's the Bluetooth Virtual HCI driver, no question. But hold on a second... let's check the permissions on that thing. Always important to understand access control, especially in a driver context. Where does this device node actually come from? My gut tells me it's probably being created using `misc_register` or maybe `module_misc_device`. Standard practice for something like this. Okay, time to confirm that. Let's jump straight into `drivers/bluetooth/hci_vhci.c` and hunt down `vhci_miscdev`. That'll likely give me the complete picture.
|
| 81/2 |
2026/05/21 02:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/bluetooth/hci_vhci.c IncludeLines:true Name:vhci_miscdev]
Results: map[SourceCode: 710: static struct miscdevice vhci_miscdev = {
711: .name = "vhci",
712: .fops = &vhci_fops,
713: .minor = VHCI_MINOR,
714: };
SourceFile:drivers/bluetooth/hci_vhci.c]
|
| 82/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 92192
output: 19
thoughts: 726
Thoughts: **Analyzing the Bluetooth HCI Device Bug**
Okay, let's break this down. First, the core issue seems to revolve around a race condition in the Bluetooth HCI device handling, specifically related to the `hci_tx_work` workqueue and device closing/resetting. The mode is unspecified, so it defaults to `0600` for root only, or `0666` depending on udev rules. The problem hinges on how `hci_dev_close_sync` is triggered and whether an unprivileged user can reach that path. Most distros have `/dev/vhci` either root-only or accessible to the `bluetooth` group; so an unprivileged user wouldn't be able to open that.
The Syzkaller report points to `workqueue: cannot queue hci_tx_work on wq hci0`. And that's triggering a warning. It's using `CAP_NET_ADMIN` in a user namespace? No, wait... `hci_sock_ioctl` uses `capable(CAP_NET_ADMIN)`, which checks the *initial* namespace. So, we can rule out triggering `HCIDEVDOWN` from within a user namespace. So, then, how did syzkaller trigger `hci_dev_close_sync`? The suspicion lands on a USB disconnect simulation. Syzkaller has the USB fuzzer (dummy_hcd), which can simulate USB disconnects. If syzkaller simulates that, it calls `hci_unregister_dev`, which then becomes the peripheral trigger.
Is there any other path to trigger it? Yes! `hci_dev_close_sync` is called from `hci_dev_do_close` which is called from `hci_unregister_dev`. `hci_unregister_dev` is called when a device is removed. But can it be triggered from `/dev/vhci`? Syzkaller should have access to `/dev/vhci` since it runs as root or sets permissions. But if this is the path, unprivileged users won't be able to reproduce it.
So, let's summarize the vulnerability: `l2cap_chan_send` calls `queue_work(hdev->workqueue, &hdev->tx_work)`. Concurrently, `hci_dev_close_sync` or `hci_dev_do_reset` is calling `drain_workqueue(hdev->workqueue)`. `queue_work` sees the `__WQ_DRAINING` flag, leading to the `WARN_ONCE`. The work is not queued. If `queue_work` is called *after* `drain_workqueue` finishes, it *does* queue `tx_work`. This can lead to `tx_work` running concurrently with `hci_conn_hash_flush`, resulting in a use-after-free on `hci_chan`. However, that critical `hci_dev_close_sync` is reachable only via `HCIDEVDOWN` (requires global `CAP_NET_ADMIN`), `HCIDEVRESET` (also global `CAP_NET_ADMIN`), or device unplug (requires physical access or root to use virtual devices). Therefore, this bug isn't exploitable by unprivileged users. And, it's not accessible from user namespaces because `capable(CAP_NET_ADMIN)` is enforced. Wait, what about `hci_dev_close`? Is there another path there?
|
| 83/2 |
2026/05/21 02:39 |
tool |
grepper |
0m
Args: map[Expression:hci_dev_close]
Results: map[Output:include/net/bluetooth/hci_core.h=1766=int hci_dev_open(__u16 dev);
include/net/bluetooth/hci_core.h:1767:int hci_dev_close(__u16 dev);
include/net/bluetooth/hci_core.h-1768-int hci_dev_do_close(struct hci_dev *hdev);
--
include/net/bluetooth/hci_sync.h=147=int hci_dev_open_sync(struct hci_dev *hdev);
include/net/bluetooth/hci_sync.h:148:int hci_dev_close_sync(struct hci_dev *hdev);
include/net/bluetooth/hci_sync.h-149-
--
net/bluetooth/hci_conn.c=1099=static void hci_conn_unlink(struct hci_conn *conn)
--
net/bluetooth/hci_conn.c-1113- /* If hdev is down it means
net/bluetooth/hci_conn.c:1114: * hci_dev_close_sync/hci_conn_hash_flush is in progress
net/bluetooth/hci_conn.c-1115- * and links don't need to be cleanup as all connections
--
net/bluetooth/hci_core.c=493=int hci_dev_do_close(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-500-
net/bluetooth/hci_core.c:501: err = hci_dev_close_sync(hdev);
net/bluetooth/hci_core.c-502-
--
net/bluetooth/hci_core.c-507-
net/bluetooth/hci_core.c:508:int hci_dev_close(__u16 dev)
net/bluetooth/hci_core.c-509-{
--
net/bluetooth/hci_sock.c=901=static int hci_sock_release(struct socket *sock)
--
net/bluetooth/hci_sock.c-940- * call hci_dev_do_close directly instead of calling
net/bluetooth/hci_sock.c:941: * hci_dev_close to ensure the exclusive access will
net/bluetooth/hci_sock.c-942- * be released and the controller brought back down.
--
net/bluetooth/hci_sock.c=1043=static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
--
net/bluetooth/hci_sock.c-1134- return -EPERM;
net/bluetooth/hci_sock.c:1135: return hci_dev_close(arg);
net/bluetooth/hci_sock.c-1136-
--
net/bluetooth/hci_sync.c=5195=static int hci_dev_shutdown(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5219-
net/bluetooth/hci_sync.c:5220:int hci_dev_close_sync(struct hci_dev *hdev)
net/bluetooth/hci_sync.c-5221-{
--
net/bluetooth/hci_sync.c=5365=static int hci_power_on_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5388- hci_dev_clear_flag(hdev, HCI_AUTO_OFF);
net/bluetooth/hci_sync.c:5389: hci_dev_close_sync(hdev);
net/bluetooth/hci_sync.c-5390- } else if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) {
--
net/bluetooth/hci_sync.c=5697=static int hci_disconnect_all_sync(struct hci_dev *hdev, u8 reason)
--
net/bluetooth/hci_sync.c-5725- * Disconnect all connections
net/bluetooth/hci_sync.c:5726: * hci_dev_close_sync
net/bluetooth/hci_sync.c-5727- */
net/bluetooth/hci_sync.c=5728=static int hci_power_off_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5757-
net/bluetooth/hci_sync.c:5758: err = hci_dev_close_sync(hdev);
net/bluetooth/hci_sync.c-5759-
]
|
| 84/2 |
2026/05/21 02:39 |
llm |
expert |
0m
Model:
Tokens:
input: 94802
output: 43
thoughts: 32
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating the Socket Release Process in HCI**
Right, so `hci_sock_release` calls `hci_dev_do_close`. Interesting. My initial assumption is that this is the mechanism for cleaning up HCI sockets upon closure, but I need to confirm the precise relationship. I'll need to dig into `hci_sock_release` itself, probably starting with the relevant data structures to see exactly what's being cleaned up and whether the device close is simply a side-effect, or the main action. I'm keen to understand the connection between these two functions and what resources are being managed at each point. This is probably related to how the socket is being tracked and released, and I'd need to consider resource dependencies between the socket layer and the device layer. Let's see what `hci_sock_release` reveals.
|
| 85/2 |
2026/05/21 02:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sock.c IncludeLines:true Name:hci_sock_release]
Results: map[SourceCode: 901: static int hci_sock_release(struct socket *sock)
902: {
903: struct sock *sk = sock->sk;
904: struct hci_dev *hdev;
905: struct sk_buff *skb;
906:
907: BT_DBG("sock %p sk %p", sock, sk);
908:
909: if (!sk)
910: return 0;
911:
912: lock_sock(sk);
913:
914: switch (hci_pi(sk)->channel) {
915: case HCI_CHANNEL_MONITOR:
916: atomic_dec(&monitor_promisc);
917: break;
918: case HCI_CHANNEL_RAW:
919: case HCI_CHANNEL_USER:
920: case HCI_CHANNEL_CONTROL:
921: /* Send event to monitor */
922: skb = create_monitor_ctrl_close(sk);
923: if (skb) {
924: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
925: HCI_SOCK_TRUSTED, NULL);
926: kfree_skb(skb);
927: }
928:
929: hci_sock_free_cookie(sk);
930: break;
931: }
932:
933: bt_sock_unlink(&hci_sk_list, sk);
934:
935: hdev = hci_pi(sk)->hdev;
936: if (hdev) {
937: if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
938: !hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
939: /* When releasing a user channel exclusive access,
940: * call hci_dev_do_close directly instead of calling
941: * hci_dev_close to ensure the exclusive access will
942: * be released and the controller brought back down.
943: *
944: * The checking of HCI_AUTO_OFF is not needed in this
945: * case since it will have been cleared already when
946: * opening the user channel.
947: *
948: * Make sure to also check that we haven't already
949: * unregistered since all the cleanup will have already
950: * been complete and hdev will get released when we put
951: * below.
952: */
953: hci_dev_do_close(hdev);
954: hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
955: mgmt_index_added(hdev);
956: }
957:
958: atomic_dec(&hdev->promisc);
959: hci_dev_put(hdev);
960: }
961:
962: sock_orphan(sk);
963: release_sock(sk);
964: sock_put(sk);
965: return 0;
966: }
SourceFile:net/bluetooth/hci_sock.c]
|
| 86/2 |
2026/05/21 02:40 |
llm |
expert |
0m
Model:
Tokens:
input: 96024
output: 43
thoughts: 67
Thoughts: **Investigating the `hci_sock_release` Function**
Okay, so I'm looking at `hci_sock_release`, and it seems straightforward enough. It calls `hci_dev_do_close(hdev)` if the socket is a user channel, indicated by `HCI_CHANNEL_USER`. My initial thought is, "Right, expected behavior, but..."
...Wait a minute. Can an *unprivileged* user actually open an `HCI_CHANNEL_USER` socket in the first place? That's the real question here, and it's key to understanding the security implications. I need to dig into `hci_sock_bind` to see how socket binding is handled and if there are any authorization checks in place. Let's see what's what.
|
| 87/2 |
2026/05/21 02:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sock.c IncludeLines:true Name:hci_sock_bind]
Results: map[SourceCode:1188: static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
1189: int addr_len)
1190: {
1191: struct sockaddr_hci haddr;
1192: struct sock *sk = sock->sk;
1193: struct hci_dev *hdev = NULL;
1194: struct sk_buff *skb;
1195: int len, err = 0;
1196:
1197: BT_DBG("sock %p sk %p", sock, sk);
1198:
1199: if (!addr)
1200: return -EINVAL;
1201:
1202: memset(&haddr, 0, sizeof(haddr));
1203: len = min_t(unsigned int, sizeof(haddr), addr_len);
1204: memcpy(&haddr, addr, len);
1205:
1206: if (haddr.hci_family != AF_BLUETOOTH)
1207: return -EINVAL;
1208:
1209: lock_sock(sk);
1210:
1211: /* Allow detaching from dead device and attaching to alive device, if
1212: * the caller wants to re-bind (instead of close) this socket in
1213: * response to hci_sock_dev_event(HCI_DEV_UNREG) notification.
1214: */
1215: hdev = hci_pi(sk)->hdev;
1216: if (hdev && hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
1217: hci_pi(sk)->hdev = NULL;
1218: sk->sk_state = BT_OPEN;
1219: hci_dev_put(hdev);
1220: }
1221: hdev = NULL;
1222:
1223: if (sk->sk_state == BT_BOUND) {
1224: err = -EALREADY;
1225: goto done;
1226: }
1227:
1228: switch (haddr.hci_channel) {
1229: case HCI_CHANNEL_RAW:
1230: if (hci_pi(sk)->hdev) {
1231: err = -EALREADY;
1232: goto done;
1233: }
1234:
1235: if (haddr.hci_dev != HCI_DEV_NONE) {
1236: hdev = hci_dev_get(haddr.hci_dev);
1237: if (!hdev) {
1238: err = -ENODEV;
1239: goto done;
1240: }
1241:
1242: atomic_inc(&hdev->promisc);
1243: }
1244:
1245: hci_pi(sk)->channel = haddr.hci_channel;
1246:
1247: if (!hci_sock_gen_cookie(sk)) {
1248: /* In the case when a cookie has already been assigned,
1249: * then there has been already an ioctl issued against
1250: * an unbound socket and with that triggered an open
1251: * notification. Send a close notification first to
1252: * allow the state transition to bounded.
1253: */
1254: skb = create_monitor_ctrl_close(sk);
1255: if (skb) {
1256: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1257: HCI_SOCK_TRUSTED, NULL);
1258: kfree_skb(skb);
1259: }
1260: }
1261:
1262: if (capable(CAP_NET_ADMIN))
1263: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1264:
1265: hci_pi(sk)->hdev = hdev;
1266:
1267: /* Send event to monitor */
1268: skb = create_monitor_ctrl_open(sk);
1269: if (skb) {
1270: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1271: HCI_SOCK_TRUSTED, NULL);
1272: kfree_skb(skb);
1273: }
1274: break;
1275:
1276: case HCI_CHANNEL_USER:
1277: if (hci_pi(sk)->hdev) {
1278: err = -EALREADY;
1279: goto done;
1280: }
1281:
1282: if (haddr.hci_dev == HCI_DEV_NONE) {
1283: err = -EINVAL;
1284: goto done;
1285: }
1286:
1287: if (!capable(CAP_NET_ADMIN)) {
1288: err = -EPERM;
1289: goto done;
1290: }
1291:
1292: hdev = hci_dev_get(haddr.hci_dev);
1293: if (!hdev) {
1294: err = -ENODEV;
1295: goto done;
1296: }
1297:
1298: if (test_bit(HCI_INIT, &hdev->flags) ||
1299: hci_dev_test_flag(hdev, HCI_SETUP) ||
1300: hci_dev_test_flag(hdev, HCI_CONFIG) ||
1301: (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
1302: test_bit(HCI_UP, &hdev->flags))) {
1303: err = -EBUSY;
1304: hci_dev_put(hdev);
1305: goto done;
1306: }
1307:
1308: if (hci_dev_test_and_set_flag(hdev, HCI_USER_CHANNEL)) {
1309: err = -EUSERS;
1310: hci_dev_put(hdev);
1311: goto done;
1312: }
1313:
1314: mgmt_index_removed(hdev);
1315:
1316: err = hci_dev_open(hdev->id);
1317: if (err) {
1318: if (err == -EALREADY) {
1319: /* In case the transport is already up and
1320: * running, clear the error here.
1321: *
1322: * This can happen when opening a user
1323: * channel and HCI_AUTO_OFF grace period
1324: * is still active.
1325: */
1326: err = 0;
1327: } else {
1328: hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
1329: mgmt_index_added(hdev);
1330: hci_dev_put(hdev);
1331: goto done;
1332: }
1333: }
1334:
1335: hci_pi(sk)->channel = haddr.hci_channel;
1336:
1337: if (!hci_sock_gen_cookie(sk)) {
1338: /* In the case when a cookie has already been assigned,
1339: * this socket will transition from a raw socket into
1340: * a user channel socket. For a clean transition, send
1341: * the close notification first.
1342: */
1343: skb = create_monitor_ctrl_close(sk);
1344: if (skb) {
1345: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1346: HCI_SOCK_TRUSTED, NULL);
1347: kfree_skb(skb);
1348: }
1349: }
1350:
1351: /* The user channel is restricted to CAP_NET_ADMIN
1352: * capabilities and with that implicitly trusted.
1353: */
1354: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1355:
1356: hci_pi(sk)->hdev = hdev;
1357:
1358: /* Send event to monitor */
1359: skb = create_monitor_ctrl_open(sk);
1360: if (skb) {
1361: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1362: HCI_SOCK_TRUSTED, NULL);
1363: kfree_skb(skb);
1364: }
1365:
1366: atomic_inc(&hdev->promisc);
1367: break;
1368:
1369: case HCI_CHANNEL_MONITOR:
1370: if (haddr.hci_dev != HCI_DEV_NONE) {
1371: err = -EINVAL;
1372: goto done;
1373: }
1374:
1375: if (!capable(CAP_NET_RAW)) {
1376: err = -EPERM;
1377: goto done;
1378: }
1379:
1380: hci_pi(sk)->channel = haddr.hci_channel;
1381:
1382: /* The monitor interface is restricted to CAP_NET_RAW
1383: * capabilities and with that implicitly trusted.
1384: */
1385: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1386:
1387: send_monitor_note(sk, "Linux version %s (%s)",
1388: init_utsname()->release,
1389: init_utsname()->machine);
1390: send_monitor_note(sk, "Bluetooth subsystem version %u.%u",
1391: BT_SUBSYS_VERSION, BT_SUBSYS_REVISION);
1392: send_monitor_replay(sk);
1393: send_monitor_control_replay(sk);
1394:
1395: atomic_inc(&monitor_promisc);
1396: break;
1397:
1398: case HCI_CHANNEL_LOGGING:
1399: if (haddr.hci_dev != HCI_DEV_NONE) {
1400: err = -EINVAL;
1401: goto done;
1402: }
1403:
1404: if (!capable(CAP_NET_ADMIN)) {
1405: err = -EPERM;
1406: goto done;
1407: }
1408:
1409: hci_pi(sk)->channel = haddr.hci_channel;
1410: break;
1411:
1412: default:
1413: if (!hci_mgmt_chan_find(haddr.hci_channel)) {
1414: err = -EINVAL;
1415: goto done;
1416: }
1417:
1418: if (haddr.hci_dev != HCI_DEV_NONE) {
1419: err = -EINVAL;
1420: goto done;
1421: }
1422:
1423: /* Users with CAP_NET_ADMIN capabilities are allowed
1424: * access to all management commands and events. For
1425: * untrusted users the interface is restricted and
1426: * also only untrusted events are sent.
1427: */
1428: if (capable(CAP_NET_ADMIN))
1429: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1430:
1431: hci_pi(sk)->channel = haddr.hci_channel;
1432:
1433: /* At the moment the index and unconfigured index events
1434: * are enabled unconditionally. Setting them on each
1435: * socket when binding keeps this functionality. They
1436: * however might be cleared later and then sending of these
1437: * events will be disabled, but that is then intentional.
1438: *
1439: * This also enables generic events that are safe to be
1440: * received by untrusted users. Example for such events
1441: * are changes to settings, class of device, name etc.
1442: */
1443: if (hci_pi(sk)->channel == HCI_CHANNEL_CONTROL) {
1444: if (!hci_sock_gen_cookie(sk)) {
1445: /* In the case when a cookie has already been
1446: * assigned, this socket will transition from
1447: * a raw socket into a control socket. To
1448: * allow for a clean transition, send the
1449: * close notification first.
1450: */
1451: skb = create_monitor_ctrl_close(sk);
1452: if (skb) {
1453: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1454: HCI_SOCK_TRUSTED, NULL);
1455: kfree_skb(skb);
1456: }
1457: }
1458:
1459: /* Send event to monitor */
1460: skb = create_monitor_ctrl_open(sk);
1461: if (skb) {
1462: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1463: HCI_SOCK_TRUSTED, NULL);
1464: kfree_skb(skb);
1465: }
1466:
1467: hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS);
1468: hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
1469: hci_sock_set_flag(sk, HCI_MGMT_OPTION_EVENTS);
1470: hci_sock_set_flag(sk, HCI_MGMT_SETTING_EVENTS);
1471: hci_sock_set_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS);
1472: hci_sock_set_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS);
1473: }
1474: break;
1475: }
1476:
1477: /* Default MTU to HCI_MAX_FRAME_SIZE if not set */
1478: if (!hci_pi(sk)->mtu)
1479: hci_pi(sk)->mtu = HCI_MAX_FRAME_SIZE;
1480:
1481: sk->sk_state = BT_BOUND;
1482:
1483: done:
1484: release_sock(sk);
1485: return err;
1486: }
SourceFile:net/bluetooth/hci_sock.c]
|
| 88/2 |
2026/05/21 02:40 |
llm |
expert |
0m
Model:
Tokens:
input: 100608
output: 39
thoughts: 852
Thoughts: **Analyzing the Bluetooth HCI Device Closure Vulnerability**
Okay, so I'm digging into this Bluetooth HCI device closure issue. The core problem, as I understand it, centers around `hci_dev_close_sync` and how it gets triggered. Let's break down the attack vectors.
First, `HCI_CHANNEL_USER` requires `CAP_NET_ADMIN`. That means unprivileged users are out β they can't open a user channel socket and subsequently trigger `hci_dev_do_close` via `hci_sock_release`. So that avenue is blocked off for local privilege escalation.
What else? We've got `HCIDEVDOWN` and `HCIDEVRESET` ioctls, also requiring `CAP_NET_ADMIN`. The socket close itself, same story. Physical device unplugging *seems* interesting, but obviously requires physical access. And `vhci` device closure needs root access to open `/dev/vhci`. So, bottom line, an unprivileged user can't directly trigger this. It's not reachable from user namespaces either, since `capable()` is used and not `ns_capable()`.
Ah, but the physical unplug. Now that's intriguing. Could a malicious USB device simulate an unplug event? Yes, it could. But to cause the UAF, it would need to *concurrently* send L2CAP data. Hold on... L2CAP data is sent *from the host* to the device (`l2cap_chan_send`). So, the host has to be sending data while the device unplugs itself. That's a race condition β something that could happen naturally if a user unplugs a device while it's in use. But a malicious device can't *force* the host to send the data; it can only unplug. So, it's a peripheral trigger, and the UAF is a race.
Let's do a quick sanity check to see if there are any other ways to trigger `hci_dev_close_sync`. Okay, `hci_dev_shutdown` is called *from* `hci_dev_close_sync`, so that's a dead end for triggering it. What about `hci_power_off_sync`? Oh, wait, `hci_power_off_sync` *calls* `hci_dev_close_sync`, not the other way around. Now, where is `hci_power_off_sync` called from? Ah, `mgmt_set_powered`. And that requires either `CAP_NET_ADMIN` *or* `HCI_SOCK_TRUSTED`. Let's think, `mgmt_set_powered` is a *management* command, right?
Can unprivileged users send management commands? Let's check `hci_sock_bind` for `HCI_CHANNEL_CONTROL`. Okay, it seems unprivileged users *can* open `HCI_CHANNEL_CONTROL` sockets, but they are not trusted. Can these untrusted users send `MGMT_OP_SET_POWERED`? I need to look into whether the untrusted socket can trigger a power-off event. It is unlikely that it can as only trusted users are allowed to trigger management commands.
So, in summary, this vulnerability is most likely a peripheral-triggered race condition, requiring physical (malicious USB device) access or high privileges.
|
| 89/2 |
2026/05/21 02:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/mgmt.c IncludeLines:true Name:mgmt_handlers]
Results: map[SourceCode:9204: static const struct hci_mgmt_handler mgmt_handlers[] = {
9205: { NULL }, /* 0x0000 (no command) */
9206: { read_version, MGMT_READ_VERSION_SIZE,
9207: HCI_MGMT_NO_HDEV |
9208: HCI_MGMT_UNTRUSTED },
9209: { read_commands, MGMT_READ_COMMANDS_SIZE,
9210: HCI_MGMT_NO_HDEV |
9211: HCI_MGMT_UNTRUSTED },
9212: { read_index_list, MGMT_READ_INDEX_LIST_SIZE,
9213: HCI_MGMT_NO_HDEV |
9214: HCI_MGMT_UNTRUSTED },
9215: { read_controller_info, MGMT_READ_INFO_SIZE,
9216: HCI_MGMT_UNTRUSTED },
9217: { set_powered, MGMT_SETTING_SIZE },
9218: { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE },
9219: { set_connectable, MGMT_SETTING_SIZE },
9220: { set_fast_connectable, MGMT_SETTING_SIZE },
9221: { set_bondable, MGMT_SETTING_SIZE },
9222: { set_link_security, MGMT_SETTING_SIZE },
9223: { set_ssp, MGMT_SETTING_SIZE },
9224: { set_hs, MGMT_SETTING_SIZE },
9225: { set_le, MGMT_SETTING_SIZE },
9226: { set_dev_class, MGMT_SET_DEV_CLASS_SIZE },
9227: { set_local_name, MGMT_SET_LOCAL_NAME_SIZE },
9228: { add_uuid, MGMT_ADD_UUID_SIZE },
9229: { remove_uuid, MGMT_REMOVE_UUID_SIZE },
9230: { load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE,
9231: HCI_MGMT_VAR_LEN },
9232: { load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE,
9233: HCI_MGMT_VAR_LEN },
9234: { disconnect, MGMT_DISCONNECT_SIZE },
9235: { get_connections, MGMT_GET_CONNECTIONS_SIZE },
9236: { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE },
9237: { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE },
9238: { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE },
9239: { pair_device, MGMT_PAIR_DEVICE_SIZE },
9240: { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE },
9241: { unpair_device, MGMT_UNPAIR_DEVICE_SIZE },
9242: { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE },
9243: { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
9244: { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE },
9245: { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
9246: { read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE },
9247: { add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE,
9248: HCI_MGMT_VAR_LEN },
9249: { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
9250: { start_discovery, MGMT_START_DISCOVERY_SIZE },
9251: { stop_discovery, MGMT_STOP_DISCOVERY_SIZE },
9252: { confirm_name, MGMT_CONFIRM_NAME_SIZE },
9253: { block_device, MGMT_BLOCK_DEVICE_SIZE },
9254: { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE },
9255: { set_device_id, MGMT_SET_DEVICE_ID_SIZE },
9256: { set_advertising, MGMT_SETTING_SIZE },
9257: { set_bredr, MGMT_SETTING_SIZE },
9258: { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE },
9259: { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE },
9260: { set_secure_conn, MGMT_SETTING_SIZE },
9261: { set_debug_keys, MGMT_SETTING_SIZE },
9262: { set_privacy, MGMT_SET_PRIVACY_SIZE },
9263: { load_irks, MGMT_LOAD_IRKS_SIZE,
9264: HCI_MGMT_VAR_LEN },
9265: { get_conn_info, MGMT_GET_CONN_INFO_SIZE },
9266: { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE },
9267: { add_device, MGMT_ADD_DEVICE_SIZE },
9268: { remove_device, MGMT_REMOVE_DEVICE_SIZE },
9269: { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE,
9270: HCI_MGMT_VAR_LEN },
9271: { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE,
9272: HCI_MGMT_NO_HDEV |
9273: HCI_MGMT_UNTRUSTED },
9274: { read_config_info, MGMT_READ_CONFIG_INFO_SIZE,
9275: HCI_MGMT_UNCONFIGURED |
9276: HCI_MGMT_UNTRUSTED },
9277: { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE,
9278: HCI_MGMT_UNCONFIGURED },
9279: { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE,
9280: HCI_MGMT_UNCONFIGURED },
9281: { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE,
9282: HCI_MGMT_VAR_LEN },
9283: { read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE },
9284: { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE,
9285: HCI_MGMT_NO_HDEV |
9286: HCI_MGMT_UNTRUSTED },
9287: { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE },
9288: { add_advertising, MGMT_ADD_ADVERTISING_SIZE,
9289: HCI_MGMT_VAR_LEN },
9290: { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
9291: { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
9292: { start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
9293: { read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE,
9294: HCI_MGMT_UNTRUSTED },
9295: { set_appearance, MGMT_SET_APPEARANCE_SIZE },
9296: { get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE },
9297: { set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE },
9298: { set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
9299: HCI_MGMT_VAR_LEN },
9300: { set_wideband_speech, MGMT_SETTING_SIZE },
9301: { read_controller_cap, MGMT_READ_CONTROLLER_CAP_SIZE,
9302: HCI_MGMT_UNTRUSTED },
9303: { read_exp_features_info, MGMT_READ_EXP_FEATURES_INFO_SIZE,
9304: HCI_MGMT_UNTRUSTED |
9305: HCI_MGMT_HDEV_OPTIONAL },
9306: { set_exp_feature, MGMT_SET_EXP_FEATURE_SIZE,
9307: HCI_MGMT_VAR_LEN |
9308: HCI_MGMT_HDEV_OPTIONAL },
9309: { read_def_system_config, MGMT_READ_DEF_SYSTEM_CONFIG_SIZE,
9310: HCI_MGMT_UNTRUSTED },
9311: { set_def_system_config, MGMT_SET_DEF_SYSTEM_CONFIG_SIZE,
9312: HCI_MGMT_VAR_LEN },
9313: { read_def_runtime_config, MGMT_READ_DEF_RUNTIME_CONFIG_SIZE,
9314: HCI_MGMT_UNTRUSTED },
9315: { set_def_runtime_config, MGMT_SET_DEF_RUNTIME_CONFIG_SIZE,
9316: HCI_MGMT_VAR_LEN },
9317: { get_device_flags, MGMT_GET_DEVICE_FLAGS_SIZE },
9318: { set_device_flags, MGMT_SET_DEVICE_FLAGS_SIZE },
9319: { read_adv_mon_features, MGMT_READ_ADV_MONITOR_FEATURES_SIZE },
9320: { add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE,
9321: HCI_MGMT_VAR_LEN },
9322: { remove_adv_monitor, MGMT_REMOVE_ADV_MONITOR_SIZE },
9323: { add_ext_adv_params, MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE,
9324: HCI_MGMT_VAR_LEN },
9325: { add_ext_adv_data, MGMT_ADD_EXT_ADV_DATA_SIZE,
9326: HCI_MGMT_VAR_LEN },
9327: { add_adv_patterns_monitor_rssi,
9328: MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE,
9329: HCI_MGMT_VAR_LEN },
9330: { set_mesh, MGMT_SET_MESH_RECEIVER_SIZE,
9331: HCI_MGMT_VAR_LEN },
9332: { mesh_features, MGMT_MESH_READ_FEATURES_SIZE },
9333: { mesh_send, MGMT_MESH_SEND_SIZE,
9334: HCI_MGMT_VAR_LEN },
9335: { mesh_send_cancel, MGMT_MESH_SEND_CANCEL_SIZE },
9336: { mgmt_hci_cmd_sync, MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN },
9337: };
SourceFile:net/bluetooth/mgmt.c]
|
| 90/2 |
2026/05/21 02:40 |
llm |
expert |
0m
Model:
Tokens:
input: 105236
output: 21
thoughts: 279
Thoughts: **My Assessment of the Vulnerability**
Okay, let me think this through. The `set_powered` function *doesn't* have `HCI_MGMT_UNTRUSTED`. That means an untrusted user can't send a `MGMT_OP_SET_POWERED` command. Consequently, unprivileged users also can't trigger `hci_dev_close_sync` through management commands.
Therefore, this bug isn't exploitable by an unprivileged process. It's also not accessible from within user namespaces, which is good. However, it *can* be triggered by something like a peripheral device getting unplugged β like a USB Bluetooth dongle. I suppose, technically, that's still possible. And the result *could* be a Denial of Service, particularly if `panic_on_warn` is enabled, or if the UAF leads to a crash. It could also potentially allow for exploitable memory corruption (a UAF of `hci_chan`), but that would require either a privileged user or physical access. So, it's not a remote, unprivileged exploit.
Now, I need to make sure there's absolutely no other way to trigger `drain_workqueue(hdev->workqueue)`. Let me see... I've grepped for `drain_workqueue` and found it in both `hci_dev_do_reset` and `hci_dev_close_sync`. It looks like both are protected, either by `CAP_NET_ADMIN` or physical unplug.
What about `hci_dev_do_reset`? It's called from `hci_dev_reset`, which is triggered by the `HCIDEVRESET` ioctl, and that requires `CAP_NET_ADMIN`. Is there any other path to that? I need to make sure I haven't missed a way to trigger this from another part of the code. Let me check the call-chains.
|
| 91/2 |
2026/05/21 02:40 |
tool |
grepper |
0m
Args: map[Expression:hci_dev_do_reset]
Results: map[Output:net/bluetooth/hci_core.c=508=int hci_dev_close(__u16 dev)
--
net/bluetooth/hci_core.c-532-
net/bluetooth/hci_core.c:533:static int hci_dev_do_reset(struct hci_dev *hdev)
net/bluetooth/hci_core.c-534-{
--
net/bluetooth/hci_core.c=586=int hci_dev_reset(__u16 dev)
--
net/bluetooth/hci_core.c-609-
net/bluetooth/hci_core.c:610: err = hci_dev_do_reset(hdev);
net/bluetooth/hci_core.c-611-
]
|
| 92/2 |
2026/05/21 02:40 |
llm |
expert |
0m
Model:
Tokens:
input: 106140
output: 21
thoughts: 21
Thoughts: Okay, here's my summary, in the first person, as if these are my thoughts, keeping in mind I'm an expert in this field:
**Digging into Device Handling**
Right, so I'm looking at this code and, initially, the trigger I'm keying in on is `hci_dev_reset`. That's where I'm starting, because it's the obvious core function for resetting a Bluetooth device. But wait a minute... that's too narrow of a focus. What about the process of closing a Bluetooth device? This can be just as important as resetting it; especially when considering the implications of error handling and thread safety. What's the impact of `hci_dev_close_sync`? I need to consider both the reset and close actions, and understand how they relate to the overall device lifecycle. I need to be sure that I am considering the complete picture.
|
| 93/2 |
2026/05/21 02:40 |
tool |
grepper |
0m
Args: map[Expression:hci_dev_close_sync]
Results: map[Output:include/net/bluetooth/hci_sync.h=147=int hci_dev_open_sync(struct hci_dev *hdev);
include/net/bluetooth/hci_sync.h:148:int hci_dev_close_sync(struct hci_dev *hdev);
include/net/bluetooth/hci_sync.h-149-
--
net/bluetooth/hci_conn.c=1099=static void hci_conn_unlink(struct hci_conn *conn)
--
net/bluetooth/hci_conn.c-1113- /* If hdev is down it means
net/bluetooth/hci_conn.c:1114: * hci_dev_close_sync/hci_conn_hash_flush is in progress
net/bluetooth/hci_conn.c-1115- * and links don't need to be cleanup as all connections
--
net/bluetooth/hci_core.c=493=int hci_dev_do_close(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-500-
net/bluetooth/hci_core.c:501: err = hci_dev_close_sync(hdev);
net/bluetooth/hci_core.c-502-
--
net/bluetooth/hci_sync.c=5195=static int hci_dev_shutdown(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5219-
net/bluetooth/hci_sync.c:5220:int hci_dev_close_sync(struct hci_dev *hdev)
net/bluetooth/hci_sync.c-5221-{
--
net/bluetooth/hci_sync.c=5365=static int hci_power_on_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5388- hci_dev_clear_flag(hdev, HCI_AUTO_OFF);
net/bluetooth/hci_sync.c:5389: hci_dev_close_sync(hdev);
net/bluetooth/hci_sync.c-5390- } else if (hci_dev_test_flag(hdev, HCI_AUTO_OFF)) {
--
net/bluetooth/hci_sync.c=5697=static int hci_disconnect_all_sync(struct hci_dev *hdev, u8 reason)
--
net/bluetooth/hci_sync.c-5725- * Disconnect all connections
net/bluetooth/hci_sync.c:5726: * hci_dev_close_sync
net/bluetooth/hci_sync.c-5727- */
net/bluetooth/hci_sync.c=5728=static int hci_power_off_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5757-
net/bluetooth/hci_sync.c:5758: err = hci_dev_close_sync(hdev);
net/bluetooth/hci_sync.c-5759-
]
|
| 94/2 |
2026/05/21 02:40 |
llm |
expert |
1m
Model:
Tokens:
input: 107135
output: 66
thoughts: 1837
Thoughts: **Vulnerability Analysis: Use-After-Free in Bluetooth Subsystem**
Okay, so here's what I've got. This looks like a nasty one, a classic use-after-free, and potentially exploitable.
First, the path to the potential vulnerability starts with Bluetooth device power-on/off sequences triggered by `mgmt_set_powered` or `hci_dev_open_sync`, and closed by `HCIDEVDOWN`. These functions call a series of synchronization functions, eventually leading to `hci_dev_close_sync`. A critical step is that `hci_dev_open_sync` requires `CAP_NET_ADMIN` to be executed.
I see that `hci_dev_close_sync` is key. Inside `hci_dev_close_sync`, we have a critical sequence: `flush_work(&hdev->tx_work)`, then `drain_workqueue(hdev->workqueue)`, and finally `hci_conn_hash_flush(hdev)`. The goal here is to ensure all outstanding work is flushed and that connection data is cleaned up.
The core of the issue boils down to a race condition. If, after `drain_workqueue` has finished, an L2CAP channel sends data (via `l2cap_chan_send`), it queues `tx_work`. However, `hci_conn_hash_flush` is called *after* `drain_workqueue` and that function could be deleting the connections that the new work accesses. There is a small window of opportunity before the hash table cleanup. So if `tx_work` runs concurrently with `hci_conn_hash_flush`, a use-after-free can occur.
The syzkaller report seems to confirm this. It looks like it was hitting the warning because it called `queue_work` *during* `drain_workqueue`. This race condition causes `queue_work` to issue a warning and *not* queue the work. However, if the timing is slightly different β the call is made *after* `drain_workqueue` but *before* `hci_conn_hash_flush` cleans up the connection data β the work *will* be queued, leading to a race condition and the use-after-free.
Looking deeper, the UAF targets `chan`, which is allocated with `kzalloc`. `tx_work` accesses `chan->data_q`, which is a `sk_buff_head`. If `chan` is freed and reallocated by an attacker, they can control the contents of `chan->data_q.next`. Then `skb_peek` is used and then, the attacker controls `skb`, potentially leading to an arbitrary write primitive.
The code confirms the `kfree(chan)` in `hci_chan_del`. And importantly, the `synchronize_rcu()` call doesn't wait for `tx_work`. It only waits for `hci_chan_sent` to complete, which returns before the free. So, `tx_work`, which uses `chan` will be left vulnerable.
The code confirms that `rcu_read_unlock()` is called *before* `hci_quote_sent(chan->conn, ...)` is called. This means that the code has a UAF on `chan->conn` *before* the attacker code would ever be called.
So the conclusion is: This UAF *is* exploitable, especially with a controlled `chan` allocation.
The triggering conditions are narrowed down. It *isn't* remotely exploitable, it *isn't* accessible from unprivileged processes or user namespaces. It is also not a VM issue. However, unplugging a USB Bluetooth dongle while sending L2CAP data seems to be a viable peripheral trigger to cause the race.
**Summary of Impact:**
* **Exploitable:** Yes, leading to a UAF.
* **Denial of Service:** Yes, either a crash or a panic if `panic_on_warn` is set.
* **Access:** Limited to local access, requires `CAP_NET_ADMIN` (or physical unplug).
* **Trigger:** Peripheral trigger by physically unplugging a USB Bluetooth dongle during L2CAP data transfer is possible.
|
| 95/2 |
2026/05/21 02:42 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 96/2 |
2026/05/21 02:42 |
llm |
expert |
0m
Model:
Tokens:
input: 110014
output: 733
thoughts: 564
Thoughts: **My Analysis of the Bluetooth Subsystem Vulnerability**
Okay, the analysis is complete, time to synthesize my final thoughts on this. The warning message, "workqueue: cannot queue hci_tx_work on wq hci0", is the crucial starting point. It appears when a workqueue is being drained, the `__WQ_DRAINING` flag is set, and a new work item is attempted to be queued on it.
Specifically, within the Bluetooth subsystem, the `drain_workqueue(hdev->workqueue)` call happens during device shutdown, initiated by `hci_dev_close_sync()`. Simultaneously, some user-space processes sending data via L2CAP sockets trigger a call to `hci_send_acl()`, which attempts to add `hdev->tx_work` to the very same `hdev->workqueue`.
If this "queue_work" action is happening *during* the draining, the warning fires, and the work item is dropped. However, the timing is critical. If `queue_work()` happens just *after* the drain is complete, but *before* the subsequent connection cleanup in `hci_conn_hash_flush()`, `tx_work` is successfully queued.
This creates a race condition. `hci_tx_work()` ends up running concurrently with `hci_conn_hash_flush()`. Now, `hci_conn_hash_flush()` deletes connections and their associated channels by calling `hci_chan_del()`, which frees the memory associated with these `hci_chan` structures.
The problem lies with `hci_tx_work()`. It calls `hci_sched_acl_pkt()`, which attempts to find a channel using `hci_chan_sent()`. `hci_chan_sent()` locks the `rcu_read_lock()` but releases the lock *before* returning the channel pointer. Consequently, `hci_chan_del()` can free the channel, and *then* `hci_sched_acl_pkt()` continues to use this freed channel. This is the classic Use-After-Free (UAF) situation on `chan->data_q`.
This UAF vulnerability is exploitable and leads to memory corruption, potentially allowing privilege escalation. The key to triggering it seems to involve initiating a device shutdown, which is controlled via the `HCIDEVDOWN` or `HCIDEVRESET` ioctls. These calls require global `CAP_NET_ADMIN` privileges. This means user namespaces won't help us here.
However, there's another, more interesting trigger - physically unplugging a Bluetooth peripheral, such as a USB dongle. This bypasses the privilege requirement. Thus, a local user *with physical access* to the Bluetooth hardware could theoretically trigger this. Without physical access, an unprivileged user cannot exploit this, due to the ioctl restrictions.
|
| 97/1 |
2026/05/21 02:42 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided kernel bug report and source code, here is
the assessment of the vulnerability:
**Root Cause Analysis:**
The warning `workqueue: cannot queue hci_tx_work on wq hci0` is triggered when
`queue_work()` is called on a workqueue that is currently being drained (i.e.,
has the `__WQ_DRAINING` flag set).
In the Bluetooth subsystem, `drain_workqueue(hdev->workqueue)` is called during
the device shutdown sequence in `hci_dev_close_sync()`. Concurrently, if a
process sends data over an L2CAP socket, the network stack calls
`l2cap_chan_send()` -> `hci_send_acl()`, which attempts to queue `hdev->tx_work`
on `hdev->workqueue`. If this queueing attempt happens exactly while the
workqueue is being drained, the kernel emits the reported `WARN_ONCE` and drops
the work.
**Security Impact (Exploitability & Denial of Service):**
While the warning itself is benign (unless `panic_on_warn` is enabled, causing a
DoS), the underlying race condition can lead to a severe **Use-After-Free
(UAF)**.
If the L2CAP data is sent slightly laterβafter `drain_workqueue()` finishes but
before `hci_dev_close_sync()` calls `hci_conn_hash_flush()` to destroy active
connectionsβthe `tx_work` will be successfully queued. This allows
`hci_tx_work()` to run concurrently with `hci_conn_hash_flush()`.
During this race, `hci_tx_work()` calls `hci_sched_acl_pkt()`, which retrieves
an `hci_chan` pointer via `hci_chan_sent()`. However, `hci_chan_sent()` releases
the `rcu_read_lock()` *before* returning the pointer. Concurrently,
`hci_conn_hash_flush()` deletes the connection and frees the `hci_chan`
structure via `kfree()`. The `tx_work` thread will then access the freed
`chan->data_q` (a linked list), leading to an exploitable Use-After-Free that
can be leveraged for arbitrary memory corruption and privilege escalation.
**Trigger Vectors & Accessibility:**
To trigger this vulnerability, an attacker must be able to initiate a Bluetooth
device shutdown (`hci_dev_close_sync()`) concurrently with sending L2CAP
traffic.
1. **Unprivileged / User Namespaces:** The device shutdown can be triggered via
the `HCIDEVDOWN` or `HCIDEVRESET` ioctls. However, the `hci_sock_ioctl()`
handler explicitly checks for global `CAP_NET_ADMIN` using
`capable(CAP_NET_ADMIN)` rather than `ns_capable()`. Therefore, unprivileged
users **cannot** trigger this bug, even from within a user namespace.
2. **Peripheral Trigger:** The shutdown sequence is also automatically invoked
when a Bluetooth controller is physically removed from the system. An attacker
with physical access could trigger the race condition by rapidly unplugging a
USB Bluetooth dongle while a background process floods the device with L2CAP
traffic.
In conclusion, this is an exploitable Use-After-Free vulnerability, but its
attack surface is restricted to privileged users (root/CAP_NET_ADMIN) or
attackers with physical access to plug/unplug peripheral devices.]
|