| 0/0 |
2026/05/22 22:20 |
flow |
assessment-security |
42m
Results: map[DenialOfService:false Explanation:The bug is a race condition between `drain_workqueue(hdev->workqueue)` and
`queue_delayed_work()` in the Bluetooth subsystem.
When a Bluetooth device is being closed (e.g., via `HCIDEVDOWN` ioctl,
`/dev/rfkill`, or device unregistration) or reset (due to a hardware error or
command timeout), the kernel calls `hci_dev_close_sync()` or
`hci_dev_do_reset()`. Both of these functions call
`drain_workqueue(hdev->workqueue)` without holding the `hci_dev_lock(hdev)`
mutex.
The `drain_workqueue()` function sets the `__WQ_DRAINING` flag on the workqueue
to prevent new works from being queued while it waits for the existing ones to
finish.
Concurrently, a process might be trying to establish a new SCO connection via
`sco_connect()`, which holds `hci_dev_lock(hdev)`. If the connection setup fails
(for example, if `hci_conn_add_unset()` fails because `hdev->sco_pkts` is 0 or
ID allocation fails), it drops the newly created ACL connection by calling
`hci_conn_drop()`. When the connection's reference count reaches 0,
`hci_conn_drop()` attempts to queue a disconnect work (`conn->disc_work`) on
`hdev->workqueue`.
Because `drain_workqueue()` is running concurrently and has set the
`__WQ_DRAINING` flag, `queue_delayed_work()` detects this and triggers a
`WARN_ONCE` in `__queue_work()`.
**Security Impact Assessment:**
* **Exploitable:** **False**. The warning is triggered because the work is
rejected by the draining workqueue. However, this is harmless because the device
is being closed or reset, and `hci_conn_hash_flush()` will soon be called (after
acquiring `hci_dev_lock`) to forcefully delete all connections and clean up
their state. There is no memory corruption, use-after-free, or memory leak.
* **Denial Of Service:** **False**. The bug only triggers a `WARN_ONCE`, which
does not halt the kernel or cause a panic in production builds (unless
`panic_on_warn` is explicitly enabled). It does not lead to deadlocks or
resource exhaustion.
* **Accessible From Unprivileged Processes / User Namespaces:** **False**.
Triggering the device close path requires privileges. The `HCIDEVDOWN` ioctl
requires `CAP_NET_ADMIN` in the initial namespace, and writing to `/dev/rfkill`
requires root privileges (or membership in specific privileged groups like
`netdev`). It cannot be reached from an unprivileged user namespace.
* **Peripheral Trigger:** **True**. A malicious or malfunctioning Bluetooth
peripheral (e.g., a USB dongle) can trigger this bug by disconnecting from the
system or causing a hardware error/command timeout exactly when a connection is
being established, forcing the kernel into the `hci_dev_close_sync()` or
`hci_dev_do_reset()` paths. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/22 22:20 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
const int kInitNetNsFd = 201;
static long syz_init_net_socket(volatile long domain, volatile long type, volatile long proto)
{
return syscall(__NR_socket, domain, type, proto);
}
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// openat$rfkill arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 72 66 6b 69 6c 6c 00} (length 0xc)
// }
// flags: open_flags = 0x40e02 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_rfkill
memcpy((void*)0x200000000180, "/dev/rfkill\000", 12);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000180ul, /*flags=O_TRUNC|O_NONBLOCK|O_NOATIME|O_APPEND|O_RDWR*/0x40e02, /*mode=*/0);
if (res != -1)
r[0] = res;
// write$rfkill arguments: [
// fd: fd_rfkill (resource)
// data: ptr[in, rfkill_event] {
// rfkill_event {
// idx: int32 = 0x0 (4 bytes)
// type: int8 = 0x2 (1 bytes)
// op: int8 = 0x3 (1 bytes)
// soft: int8 = 0x1 (1 bytes)
// hard: int8 = 0x1 (1 bytes)
// }
// }
// len: bytesize = 0x8 (8 bytes)
// ]
*(uint32_t*)0x200000000300 = 0;
*(uint8_t*)0x200000000304 = 2;
*(uint8_t*)0x200000000305 = 3;
*(uint8_t*)0x200000000306 = 1;
*(uint8_t*)0x200000000307 = 1;
syscall(__NR_write, /*fd=*/r[0], /*data=*/0x200000000300ul, /*len=*/8ul);
// syz_init_net_socket$bt_sco arguments: [
// fam: const = 0x1f (8 bytes)
// type: const = 0x5 (8 bytes)
// proto: const = 0x2 (8 bytes)
// ]
// returns sock_bt_sco
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=*/5, /*proto=*/2);
if (res != -1)
r[1] = res;
// connect$bt_sco arguments: [
// fd: sock_bt_sco (resource)
// addr: ptr[in, sockaddr_sco] {
// sockaddr_sco {
// fam: const = 0x1f (2 bytes)
// addr: union bdaddr_t {
// any: buffer: {00 00 00 00 00 00} (length 0x6)
// }
// }
// }
// addrlen: len = 0x8 (8 bytes)
// ]
*(uint16_t*)0x200000000340 = 0x1f;
memset((void*)0x200000000342, 0, 6);
syscall(__NR_connect, /*fd=*/r[1], /*addr=*/0x200000000340ul, /*addrlen=*/8ul);
return 0;
}
]
|
| 2/1 |
2026/05/22 22:20 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/739dd9692c3093d9780e708b72f28c376108dd83]
|
| 3/1 |
2026/05/22 22:21 |
action |
kernel-builder |
17m
Results: map[KernelObj:/app/workdir/cache/build/5c7fe5d0a8ef0e5c6daecca017b3c5500d160121]
|
| 4/1 |
2026/05/22 22:38 |
action |
codesearch-prepare |
13m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/22 22:52 |
agent |
expert |
10m
Model:
Results: map[DenialOfService:false Exploitable:false 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).
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.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
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_conn_timeout on wq hci0
WARNING: kernel/workqueue.c:2270 at __queue_work+0xd08/0x1150 kernel/workqueue.c:2269, CPU#2: syz.1.12503/32740
Modules linked in:
CPU: 2 UID: 0 PID: 32740 Comm: syz.1.12503 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:__queue_work+0xd0c/0x1150 kernel/workqueue.c:2269
Code: 00 00 00 fc ff df 49 8d 94 24 78 01 00 00 48 89 f9 48 c1 e9 03 80 3c 01 00 0f 85 04 04 00 00 48 8d 3d 58 9a 0c 0f 48 8b 75 18 <67> 48 0f b9 3a e9 2a f7 ff ff e8 85 d1 38 00 90 0f 0b 90 e9 b2 f5
RSP: 0018:ffffc9000f2dfbf0 EFLAGS: 00010046
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 1ffff11005d6514b
RDX: ffff888038cef178 RSI: ffffffff8a86d4e0 RDI: ffffffff90dc62e0
RBP: ffff88802eb28a40 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff888038cef000
R13: ffff888034b3c900 R14: ffff88802eb28a88 R15: ffff88802eb28a48
FS: 00007f6ce80ee6c0(0000) GS:ffff8880d654a000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fd931c17dac CR3: 000000003757c000 CR4: 0000000000352ef0
Call Trace:
<TASK>
__queue_delayed_work+0x365/0x470 kernel/workqueue.c:2522
queue_delayed_work_on+0x180/0x1e0 kernel/workqueue.c:2574
queue_delayed_work include/linux/workqueue.h:684 [inline]
hci_conn_drop include/net/bluetooth/hci_core.h:1712 [inline]
hci_conn_drop include/net/bluetooth/hci_core.h:1686 [inline]
hci_connect_sco+0x4d2/0xef0 net/bluetooth/hci_conn.c:1773
sco_connect net/bluetooth/sco.c:339 [inline]
sco_sock_connect+0x303/0xcb0 net/bluetooth/sco.c:665
__sys_connect_file+0x141/0x1a0 net/socket.c:2089
__sys_connect+0x141/0x170 net/socket.c:2108
__do_sys_connect net/socket.c:2114 [inline]
__se_sys_connect net/socket.c:2111 [inline]
__x64_sys_connect+0x72/0xb0 net/socket.c:2111
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x106/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f6ce719c629
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 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 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f6ce80ee028 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
RAX: ffffffffffffffda RBX: 00007f6ce7415fa0 RCX: 00007f6ce719c629
RDX: 0000000000000008 RSI: 0000200000000340 RDI: 0000000000000005
RBP: 00007f6ce7232b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f6ce7416038 R14: 00007f6ce7415fa0 R15: 00007ffd72cb3308
</TASK>
----------------
Code disassembly (best guess):
0: 00 00 add %al,(%rax)
2: 00 fc add %bh,%ah
4: ff lcall (bad)
5: df 49 8d fisttps -0x73(%rcx)
8: 94 xchg %eax,%esp
9: 24 78 and $0x78,%al
b: 01 00 add %eax,(%rax)
d: 00 48 89 add %cl,-0x77(%rax)
10: f9 stc
11: 48 c1 e9 03 shr $0x3,%rcx
15: 80 3c 01 00 cmpb $0x0,(%rcx,%rax,1)
19: 0f 85 04 04 00 00 jne 0x423
1f: 48 8d 3d 58 9a 0c 0f lea 0xf0c9a58(%rip),%rdi # 0xf0c9a7e
26: 48 8b 75 18 mov 0x18(%rbp),%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: e9 2a f7 ff ff jmp 0xfffff75e
34: e8 85 d1 38 00 call 0x38d1be
39: 90 nop
3a: 0f 0b ud2
3c: 90 nop
3d: e9 .byte 0xe9
3e: b2 f5 mov $0xf5,%dl
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
const int kInitNetNsFd = 201;
static long syz_init_net_socket(volatile long domain, volatile long type, volatile long proto)
{
return syscall(__NR_socket, domain, type, proto);
}
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// openat$rfkill arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 72 66 6b 69 6c 6c 00} (length 0xc)
// }
// flags: open_flags = 0x40e02 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_rfkill
memcpy((void*)0x200000000180, "/dev/rfkill\000", 12);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000180ul, /*flags=O_TRUNC|O_NONBLOCK|O_NOATIME|O_APPEND|O_RDWR*/0x40e02, /*mode=*/0);
if (res != -1)
r[0] = res;
// write$rfkill arguments: [
// fd: fd_rfkill (resource)
// data: ptr[in, rfkill_event] {
// rfkill_event {
// idx: int32 = 0x0 (4 bytes)
// type: int8 = 0x2 (1 bytes)
// op: int8 = 0x3 (1 bytes)
// soft: int8 = 0x1 (1 bytes)
// hard: int8 = 0x1 (1 bytes)
// }
// }
// len: bytesize = 0x8 (8 bytes)
// ]
*(uint32_t*)0x200000000300 = 0;
*(uint8_t*)0x200000000304 = 2;
*(uint8_t*)0x200000000305 = 3;
*(uint8_t*)0x200000000306 = 1;
*(uint8_t*)0x200000000307 = 1;
syscall(__NR_write, /*fd=*/r[0], /*data=*/0x200000000300ul, /*len=*/8ul);
// syz_init_net_socket$bt_sco arguments: [
// fam: const = 0x1f (8 bytes)
// type: const = 0x5 (8 bytes)
// proto: const = 0x2 (8 bytes)
// ]
// returns sock_bt_sco
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=*/5, /*proto=*/2);
if (res != -1)
r[1] = res;
// connect$bt_sco arguments: [
// fd: sock_bt_sco (resource)
// addr: ptr[in, sockaddr_sco] {
// sockaddr_sco {
// fam: const = 0x1f (2 bytes)
// addr: union bdaddr_t {
// any: buffer: {00 00 00 00 00 00} (length 0x6)
// }
// }
// }
// addrlen: len = 0x8 (8 bytes)
// ]
*(uint16_t*)0x200000000340 = 0x1f;
memset((void*)0x200000000342, 0, 6);
syscall(__NR_connect, /*fd=*/r[1], /*addr=*/0x200000000340ul, /*addrlen=*/8ul);
return 0;
}
Reply: The bug is a race condition between `drain_workqueue(hdev->workqueue)` and `queue_delayed_work()` in the Bluetooth subsystem.
When a Bluetooth device is being closed (e.g., via `HCIDEVDOWN` ioctl, `/dev/rfkill`, or device unregistration) or reset (due to a hardware error or command timeout), the kernel calls `hci_dev_close_sync()` or `hci_dev_do_reset()`. Both of these functions call `drain_workqueue(hdev->workqueue)` without holding the `hci_dev_lock(hdev)` mutex.
The `drain_workqueue()` function sets the `__WQ_DRAINING` flag on the workqueue to prevent new works from being queued while it waits for the existing ones to finish.
Concurrently, a process might be trying to establish a new SCO connection via `sco_connect()`, which holds `hci_dev_lock(hdev)`. If the connection setup fails (for example, if `hci_conn_add_unset()` fails because `hdev->sco_pkts` is 0 or ID allocation fails), it drops the newly created ACL connection by calling `hci_conn_drop()`. When the connection's reference count reaches 0, `hci_conn_drop()` attempts to queue a disconnect work (`conn->disc_work`) on `hdev->workqueue`.
Because `drain_workqueue()` is running concurrently and has set the `__WQ_DRAINING` flag, `queue_delayed_work()` detects this and triggers a `WARN_ONCE` in `__queue_work()`.
**Security Impact Assessment:**
* **Exploitable:** **False**. The warning is triggered because the work is rejected by the draining workqueue. However, this is harmless because the device is being closed or reset, and `hci_conn_hash_flush()` will soon be called (after acquiring `hci_dev_lock`) to forcefully delete all connections and clean up their state. There is no memory corruption, use-after-free, or memory leak.
* **Denial Of Service:** **False**. The bug only triggers a `WARN_ONCE`, which does not halt the kernel or cause a panic in production builds (unless `panic_on_warn` is explicitly enabled). It does not lead to deadlocks or resource exhaustion.
* **Accessible From Unprivileged Processes / User Namespaces:** **False**. Triggering the device close path requires privileges. The `HCIDEVDOWN` ioctl requires `CAP_NET_ADMIN` in the initial namespace, and writing to `/dev/rfkill` requires root privileges (or membership in specific privileged groups like `netdev`). It cannot be reached from an unprivileged user namespace.
* **Peripheral Trigger:** **True**. A malicious or malfunctioning Bluetooth peripheral (e.g., a USB dongle) can trigger this bug by disconnecting from the system or causing a hardware error/command timeout exactly when a connection is being established, forcing the kernel into the `hci_dev_close_sync()` or `hci_dev_do_reset()` paths.
|
| 6/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 9310
output: 20
thoughts: 462
Thoughts: **Analyzing a Kernel Workqueue Bug**
Okay, so I've got a kernel bug report on my desk: "workqueue: cannot queue hci_conn_timeout on wq hci0". The report includes a `WARNING` message and a call trace, which is always helpful. The call trace points directly to `kernel/workqueue.c:2269` and indicates an issue with `__queue_work`. I'll need to dig into the workqueue code to understand exactly what's going on at that line.
The trace suggests this is a delayed work queue, specifically related to an HCI connection timeout on work queue `hci0`. It looks like the issue stems from an SCO socket connection attempt. The reproducer details are interesting - apparently, a user is creating an SCO socket and calling `connect()`, but before that it interacts with `/dev/rfkill`. The interaction with RFKILL might be key, potentially causing some sort of race condition or unexpected state change that leads to this.
The stack trace reveals the code path: `__queue_delayed_work` -> `queue_delayed_work_on` -> ultimately, within the Bluetooth HCI stack: `hci_conn_drop` which then leads to `hci_connect_sco`. Looks like `hci_conn_drop` is being called somehow. Then the trace moves into `sco_connect`, and further down into `__sys_connect_file` and the standard `connect()` system call path. The fact that the call trace goes through multiple Bluetooth functions is good context.
I'll start by examining the surrounding code around `kernel/workqueue.c:2269` to see how work items are being added to the workqueue, and what conditions might be causing this specific timeout to be un-queueable. I'll need to figure out the exact state of the `hci0` work queue when the `hci_conn_timeout` is being queued. Maybe the RFKILL interaction is blocking something that should be released. A potential race condition between RFKILL and Bluetooth operations immediately comes to mind. Let's see...
|
| 7/2 |
2026/05/22 22:52 |
tool |
grepper |
0m
Args: map[Expression:cannot queue .* on wq]
Results: map[Output:kernel/workqueue.c=2248=static void __queue_work(int cpu, struct workqueue_struct *wq,
--
kernel/workqueue.c-2269- if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
kernel/workqueue.c:2270: WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n",
kernel/workqueue.c-2271- work->func, wq->name))) {
]
|
| 8/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 10384
output: 19
thoughts: 170
Thoughts: **Analyzing a Workqueue Warning**
Okay, so I've just encountered this `WARN_ONCE` message: "workqueue: cannot queue %ps on wq %s". It's a pretty clear indicator that I'm trying to queue some work, specifically something related to `%ps` (which, from the context, I understand to be a function pointer), onto a workqueue (`%s`) that's in a bad state. The warning tells me the problem boils down to `wq->flags` having either the `__WQ_DESTROYING` or `__WQ_DRAINING` bit set. That's my immediate clue.
Looking deeper, the workqueue involved is `hci0`. Given my knowledge of the Bluetooth stack, I can infer that the work that failed to queue is probably associated with an HCI device, specifically `hci0`, which is the Bluetooth controller. The work queue name has to be the same, so it's associated with that `hci0`. I'm guessing that `hci0` is in the process of being unregistered, powered off, or otherwise undergoing a state transition that leads to its workqueue being destroyed or drained.
Now the interesting point is that `hci_connect_sco` is *still* trying to queue work. So the work, given by `%ps`, probably maps to `hci_conn_timeout` and it's being scheduled even though the workqueue is already being torn down, this is where the bug lies.
My next move is to check `hci_connect_sco` in `net/bluetooth/hci_conn.c`. I need to figure out *why* it's trying to queue work on a workqueue that's already in the process of being destroyed. This will probably involve analyzing the timing of device unregistration/power-off and how `hci_connect_sco` interacts with it. There's almost certainly a race condition here.
|
| 9/2 |
2026/05/22 22:52 |
tool |
grepper |
0m
Args: map[Expression:hci_connect_sco]
Results: map[Output:include/net/bluetooth/hci_core.h=1596=struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
--
include/net/bluetooth/hci_core.h-1598- enum conn_reasons conn_reason, u16 timeout);
include/net/bluetooth/hci_core.h:1599:struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
include/net/bluetooth/hci_core.h-1600- __u16 setting, struct bt_codec *codec,
--
net/bluetooth/hci_conn.c=1728=static struct hci_link *hci_conn_link(struct hci_conn *parent,
--
net/bluetooth/hci_conn.c-1755-
net/bluetooth/hci_conn.c:1756:struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
net/bluetooth/hci_conn.c-1757- __u16 setting, struct bt_codec *codec,
--
net/bluetooth/sco.c=310=static int sco_connect(struct sock *sk)
--
net/bluetooth/sco.c-338-
net/bluetooth/sco.c:339: hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
net/bluetooth/sco.c-340- sco_pi(sk)->setting, &sco_pi(sk)->codec,
]
|
| 10/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 11389
output: 43
thoughts: 23
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into Bluetooth SCO Connection Establishment**
Okay, so I need to understand the SCO connection process in the Bluetooth stack. Let's start with `hci_connect_sco` within `net/bluetooth/hci_conn.c`. I expect this function will be the main entry point for initiating a synchronous connection-oriented (SCO) link. I'll be looking for how it handles the HCI commands, particularly the `HCI_Create_Connection` and `HCI_Create_Connection_Cancel` commands, and how it manages the state machine for the connection setup. I anticipate seeing the allocation of connection endpoints, and the interaction with the lower layers, ensuring the controller is correctly programmed for the link. I'll need to pay close attention to the error handling and timeout mechanisms to see how it recovers from failures. I'm also curious about the scheduling and locking strategies used to synchronize the connection procedure with other Bluetooth tasks. Ultimately, I'm aiming to build a solid mental model of the control flow for this critical piece of the Bluetooth protocol implementation.
|
| 11/2 |
2026/05/22 22:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_connect_sco]
Results: map[SourceCode:1756: struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
1757: __u16 setting, struct bt_codec *codec,
1758: u16 timeout)
1759: {
1760: struct hci_conn *acl;
1761: struct hci_conn *sco;
1762: struct hci_link *link;
1763:
1764: acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING,
1765: CONN_REASON_SCO_CONNECT, timeout);
1766: if (IS_ERR(acl))
1767: return acl;
1768:
1769: sco = hci_conn_hash_lookup_ba(hdev, type, dst);
1770: if (!sco) {
1771: sco = hci_conn_add_unset(hdev, type, dst, 0, HCI_ROLE_MASTER);
1772: if (IS_ERR(sco)) {
1773: hci_conn_drop(acl);
1774: return sco;
1775: }
1776: }
1777:
1778: link = hci_conn_link(acl, sco);
1779: if (!link) {
1780: hci_conn_drop(acl);
1781: hci_conn_drop(sco);
1782: return ERR_PTR(-ENOLINK);
1783: }
1784:
1785: sco->setting = setting;
1786: sco->codec = *codec;
1787:
1788: if (acl->state == BT_CONNECTED &&
1789: (sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
1790: set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
1791: hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
1792:
1793: if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) {
1794: /* defer SCO setup until mode change completed */
1795: set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
1796: return sco;
1797: }
1798:
1799: hci_sco_setup(acl, 0x00);
1800: }
1801:
1802: return sco;
1803: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 12/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 12453
output: 45
thoughts: 33
Thoughts: Here's a breakdown of my thought process:
**Diving into `hci_conn_drop`**
Okay, the warning popped up at line 1773, specifically within the `hci_conn_drop` function. That's our target. My initial instinct is to dig into the implementation of `hci_conn_drop`. I need to understand what's happening there, especially since we're seeing a warning. I'll need to trace the execution flow and identify the context in which this function is being called, and the likely cause.
|
| 13/2 |
2026/05/22 22:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/net/bluetooth/hci_core.h IncludeLines:true Name:hci_conn_drop]
Results: map[SourceCode:1686: static inline void hci_conn_drop(struct hci_conn *conn)
1687: {
1688: BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt));
1689:
1690: if (atomic_dec_and_test(&conn->refcnt)) {
1691: unsigned long timeo;
1692:
1693: switch (conn->type) {
1694: case ACL_LINK:
1695: case LE_LINK:
1696: cancel_delayed_work(&conn->idle_work);
1697: if (conn->state == BT_CONNECTED) {
1698: timeo = conn->disc_timeout;
1699: if (!conn->out)
1700: timeo *= 2;
1701: } else {
1702: timeo = 0;
1703: }
1704: break;
1705:
1706: default:
1707: timeo = 0;
1708: break;
1709: }
1710:
1711: cancel_delayed_work(&conn->disc_work);
1712: queue_delayed_work(conn->hdev->workqueue,
1713: &conn->disc_work, timeo);
1714: }
1715: }
SourceFile:include/net/bluetooth/hci_core.h]
|
| 14/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 13080
output: 21
thoughts: 107
Thoughts: **Investigating a Workqueue Concurrency Issue**
Okay, so I'm looking at this warning message, and it seems to be triggered by calling `queue_delayed_work` with a workqueue, `conn->hdev->workqueue`, that's in the process of being torn down. Right, that makes sense. We can't schedule work on a workqueue that's going away. The issue is *when* and *where* this workqueue is being destroyed.
Let me think⦠where do we release these HCI device resources? It has to be in places like `hci_release_dev` or `hci_unregister_dev`, or some other close/release function. I need to track down exactly how `conn->hdev->workqueue` is getting cleaned up. Time to dig into the `hci_release_dev` and `hci_dev_close` functions. I'll need to see the control flow to understand the sequence of events and pinpoint the exact point where the destruction is happening relative to this `queue_delayed_work` call. I need to understand the dependencies and race conditions that could be at play here.
|
| 15/2 |
2026/05/22 22:52 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue.*workqueue]
Results: map[Output:
Full output is too long, showing 500 out of 1089 lines.
Use more precise expression if possible.
[block/bio.c=1894=void bioset_exit(struct bio_set *bs)
--
block/bio.c-1897- if (bs->rescue_workqueue)
block/bio.c:1898: destroy_workqueue(bs->rescue_workqueue);
block/bio.c-1899- 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=1755=static void lo_free_disk(struct gendisk *disk)
--
drivers/block/loop.c-1759- if (lo->workqueue)
drivers/block/loop.c:1760: destroy_workqueue(lo->workqueue);
drivers/block/loop.c-1761- loop_free_idle_workers(lo, true);
--
drivers/block/zloop.c=793=static void zloop_free_disk(struct gendisk *disk)
--
drivers/block/zloop.c-808- fput(zlo->data_dir);
drivers/block/zloop.c:809: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-810- kfree(zlo->base_dir);
--
drivers/block/zloop.c=979=static int zloop_ctl_add(struct zloop_options *opts)
--
drivers/block/zloop.c-1144-out_destroy_workqueue:
drivers/block/zloop.c:1145: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-1146-out_free_idr:
--
drivers/bluetooth/btintel_pcie.c=2533=static void btintel_pcie_remove(struct pci_dev *pdev)
--
drivers/bluetooth/btintel_pcie.c-2556-
drivers/bluetooth/btintel_pcie.c:2557: destroy_workqueue(data->workqueue);
drivers/bluetooth/btintel_pcie.c-2558-
--
drivers/bluetooth/hci_qca.c=720=static int qca_close(struct hci_uart *hu)
--
drivers/bluetooth/hci_qca.c-738- timer_shutdown_sync(&qca->wake_retrans_timer);
drivers/bluetooth/hci_qca.c:739: destroy_workqueue(qca->workqueue);
drivers/bluetooth/hci_qca.c-740- qca->hu = NULL;
--
drivers/cdx/controller/mcdi.c=155=void cdx_mcdi_finish(struct cdx_mcdi *cdx)
--
drivers/cdx/controller/mcdi.c-164-
drivers/cdx/controller/mcdi.c:165: destroy_workqueue(mcdi->workqueue);
drivers/cdx/controller/mcdi.c-166- 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=1468=static int __init fw_core_init(void)
--
drivers/firewire/core-transaction.c-1478- if (ret < 0) {
drivers/firewire/core-transaction.c:1479: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1480- return ret;
--
drivers/firewire/core-transaction.c-1485- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1486: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1487- return fw_cdev_major;
--
drivers/firewire/core-transaction.c=1499=static void __exit fw_core_cleanup(void)
--
drivers/firewire/core-transaction.c-1502- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1503: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1504- xa_destroy(&fw_device_xa);
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c=2220=static void amdgpu_dm_fini(struct amdgpu_device *adev)
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2224- if (adev->dm.vblank_control_workqueue) {
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:2225: destroy_workqueue(adev->dm.vblank_control_workqueue);
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2226- adev->dm.vblank_control_workqueue = NULL;
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2620=static int anx7625_i2c_probe(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2763- if (platform->workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2764: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2765-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2767- if (platform->hdcp_workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2768: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2769-
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2773=static void anx7625_i2c_remove(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2779- if (platform->pdata.intp_irq)
drivers/gpu/drm/bridge/analogix/anx7625.c:2780: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2781-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2783- cancel_delayed_work(&platform->hdcp_work);
drivers/gpu/drm/bridge/analogix/anx7625.c:2784: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2785- }
--
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=2724=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2783- ib_sa_unregister_client(&ipoib_sa_client);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2784: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2785-
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2792=static void __exit ipoib_cleanup_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2800- ipoib_unregister_debugfs();
drivers/infiniband/ulp/ipoib/ipoib_main.c:2801: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2802-}
--
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=249=static int vpu_core_register(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-286- if (core->workqueue) {
drivers/media/platform/amphion/vpu_core.c:287: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-288- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_core.c=302=static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-313- cancel_delayed_work_sync(&core->msg_delayed_work);
drivers/media/platform/amphion/vpu_core.c:314: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-315- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=699=static int vpu_v4l2_release(struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-704- cancel_work_sync(&inst->msg_work);
drivers/media/platform/amphion/vpu_v4l2.c:705: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-706- inst->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=720=int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-771- if (ret) {
drivers/media/platform/amphion/vpu_v4l2.c:772: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-773- 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=354=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-521- media_device_cleanup(&dev->mdev_dec);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:522: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-523-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-528- if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch))
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:529: destroy_workqueue(dev->core_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-530-err_res:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c=568=static void mtk_vcodec_dec_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-571-
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:572: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-573-
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=235=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-365-err_enc_reg:
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:366: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-367-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=458=static void mtk_vcodec_enc_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-461-
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:462: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-463- 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=633=static int __init memstick_init(void)
--
drivers/memstick/core/memstick.c-653-error_destroy_workqueue:
drivers/memstick/core/memstick.c:654: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-655-
--
drivers/memstick/core/memstick.c=659=static void __exit memstick_exit(void)
--
drivers/memstick/core/memstick.c-662- bus_unregister(&memstick_bus_type);
drivers/memstick/core/memstick.c:663: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-664- 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=1272=static __init int sm_module_init(void)
--
drivers/mtd/sm_ftl.c-1281- if (error)
drivers/mtd/sm_ftl.c:1282: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1283- return error;
--
drivers/mtd/sm_ftl.c=1287=static void __exit sm_module_exit(void)
drivers/mtd/sm_ftl.c-1288-{
drivers/mtd/sm_ftl.c:1289: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1290- deregister_mtd_blktrans(&sm_ftl_ops);
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1586=static int xgbe_open(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1659-err_an_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1660: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1661-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1662-err_dev_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1663: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1664-
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1668=static int xgbe_close(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1680-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1681: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1682-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1683: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1684-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3006=static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3138-err_destroy_ordered_workqueue:
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3139: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3140-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3258=static void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3265-
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3266: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3267-
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=936=static int enetc4_pf_netdev_create(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-990-err_reg_netdev:
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:991: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-992-err_wq_init:
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=1004=static void enetc4_pf_netdev_destroy(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-1010- cancel_work(&si->rx_mode_task);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:1011: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-1012- 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=2676=static void ef4_fini_struct(struct ef4_nic *efx)
--
drivers/net/ethernet/sfc/falcon/efx.c-2685- if (efx->workqueue) {
drivers/net/ethernet/sfc/falcon/efx.c:2686: destroy_workqueue(efx->workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-2687- efx->workqueue = NULL;
--
drivers/net/ethernet/sfc/falcon/efx.c=3156=static int __init ef4_init_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3178- err_pci:
drivers/net/ethernet/sfc/falcon/efx.c:3179: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3180- err_reset:
--
drivers/net/ethernet/sfc/falcon/efx.c=3186=static void __exit ef4_exit_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3190- pci_unregister_driver(&ef4_pci_driver);
drivers/net/ethernet/sfc/falcon/efx.c:3191: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3192- 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=3617=struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
--
drivers/net/wireless/ath/ath10k/core.c-3752-err_free_tx_complete:
drivers/net/wireless/ath/ath10k/core.c:3753: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3754-err_free_aux_wq:
drivers/net/wireless/ath/ath10k/core.c:3755: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3756-err_free_wq:
drivers/net/wireless/ath/ath10k/core.c:3757: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3758-err_free_mac:
--
drivers/net/wireless/ath/ath10k/core.c=3765=void ath10k_core_destroy(struct ath10k *ar)
drivers/net/wireless/ath/ath10k/core.c-3766-{
drivers/net/wireless/ath/ath10k/core.c:3767: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3768-
drivers/net/wireless/ath/ath10k/core.c:3769: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3770-
drivers/net/wireless/ath/ath10k/core.c:3771: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3772-
--
drivers/net/wireless/ath/ath10k/sdio.c=2517=static int ath10k_sdio_probe(struct sdio_func *func,
--
drivers/net/wireless/ath/ath10k/sdio.c-2637-err_free_wq:
drivers/net/wireless/ath/ath10k/sdio.c:2638: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2639-err_core_destroy:
--
drivers/net/wireless/ath/ath10k/sdio.c=2645=static void ath10k_sdio_remove(struct sdio_func *func)
--
drivers/net/wireless/ath/ath10k/sdio.c-2657-
drivers/net/wireless/ath/ath10k/sdio.c:2658: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2659-
--
drivers/net/wireless/ath/ath11k/core.c=2736=void ath11k_core_free(struct ath11k_base *ab)
drivers/net/wireless/ath/ath11k/core.c-2737-{
drivers/net/wireless/ath/ath11k/core.c:2738: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath11k/core.c:2739: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2740-
--
drivers/net/wireless/ath/ath11k/core.c=2745=struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
--
drivers/net/wireless/ath/ath11k/core.c-2790-err_free_wq:
drivers/net/wireless/ath/ath11k/core.c:2791: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2792-err_sc_free:
--
drivers/net/wireless/ath/ath12k/core.c=2235=void ath12k_core_free(struct ath12k_base *ab)
--
drivers/net/wireless/ath/ath12k/core.c-2237- timer_delete_sync(&ab->rx_replenish_retry);
drivers/net/wireless/ath/ath12k/core.c:2238: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath12k/core.c:2239: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath12k/core.c-2240- kfree(ab);
--
drivers/net/wireless/ath/ath12k/core.c=2243=struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
--
drivers/net/wireless/ath/ath12k/core.c-2296-err_free_wq:
drivers/net/wireless/ath/ath12k/core.c:2297: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath12k/core.c-2298-err_sc_free:
--
]
]
|
| 16/2 |
2026/05/22 22:52 |
llm |
expert |
0m
Model:
Tokens:
input: 25350
output: 21
thoughts: 18
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Digging into Bluetooth Kernel Code**
Alright, time to get to work. I need to understand how Bluetooth's workqueue destruction is handled. The task at hand is `destroy_workqueue`, and I'm pretty sure I'll find the relevant code within the `net/bluetooth/` directory. That's the logical place to start digging. Let's see what we can unearth within that particular area of the kernel source. I expect to find the implementation, or at least calls to it, in the bluetooth subsystem code. Time to fire up the search tools and dive in.
|
| 17/2 |
2026/05/22 22:52 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue.*workqueue]
Results: map[Output:
Full output is too long, showing 500 out of 1089 lines.
Use more precise expression if possible.
[block/bio.c=1894=void bioset_exit(struct bio_set *bs)
--
block/bio.c-1897- if (bs->rescue_workqueue)
block/bio.c:1898: destroy_workqueue(bs->rescue_workqueue);
block/bio.c-1899- 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=1755=static void lo_free_disk(struct gendisk *disk)
--
drivers/block/loop.c-1759- if (lo->workqueue)
drivers/block/loop.c:1760: destroy_workqueue(lo->workqueue);
drivers/block/loop.c-1761- loop_free_idle_workers(lo, true);
--
drivers/block/zloop.c=793=static void zloop_free_disk(struct gendisk *disk)
--
drivers/block/zloop.c-808- fput(zlo->data_dir);
drivers/block/zloop.c:809: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-810- kfree(zlo->base_dir);
--
drivers/block/zloop.c=979=static int zloop_ctl_add(struct zloop_options *opts)
--
drivers/block/zloop.c-1144-out_destroy_workqueue:
drivers/block/zloop.c:1145: destroy_workqueue(zlo->workqueue);
drivers/block/zloop.c-1146-out_free_idr:
--
drivers/bluetooth/btintel_pcie.c=2533=static void btintel_pcie_remove(struct pci_dev *pdev)
--
drivers/bluetooth/btintel_pcie.c-2556-
drivers/bluetooth/btintel_pcie.c:2557: destroy_workqueue(data->workqueue);
drivers/bluetooth/btintel_pcie.c-2558-
--
drivers/bluetooth/hci_qca.c=720=static int qca_close(struct hci_uart *hu)
--
drivers/bluetooth/hci_qca.c-738- timer_shutdown_sync(&qca->wake_retrans_timer);
drivers/bluetooth/hci_qca.c:739: destroy_workqueue(qca->workqueue);
drivers/bluetooth/hci_qca.c-740- qca->hu = NULL;
--
drivers/cdx/controller/mcdi.c=155=void cdx_mcdi_finish(struct cdx_mcdi *cdx)
--
drivers/cdx/controller/mcdi.c-164-
drivers/cdx/controller/mcdi.c:165: destroy_workqueue(mcdi->workqueue);
drivers/cdx/controller/mcdi.c-166- 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=1468=static int __init fw_core_init(void)
--
drivers/firewire/core-transaction.c-1478- if (ret < 0) {
drivers/firewire/core-transaction.c:1479: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1480- return ret;
--
drivers/firewire/core-transaction.c-1485- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1486: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1487- return fw_cdev_major;
--
drivers/firewire/core-transaction.c=1499=static void __exit fw_core_cleanup(void)
--
drivers/firewire/core-transaction.c-1502- bus_unregister(&fw_bus_type);
drivers/firewire/core-transaction.c:1503: destroy_workqueue(fw_workqueue);
drivers/firewire/core-transaction.c-1504- xa_destroy(&fw_device_xa);
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c=2220=static void amdgpu_dm_fini(struct amdgpu_device *adev)
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2224- if (adev->dm.vblank_control_workqueue) {
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:2225: destroy_workqueue(adev->dm.vblank_control_workqueue);
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-2226- adev->dm.vblank_control_workqueue = NULL;
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2620=static int anx7625_i2c_probe(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2763- if (platform->workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2764: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2765-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2767- if (platform->hdcp_workqueue)
drivers/gpu/drm/bridge/analogix/anx7625.c:2768: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2769-
--
drivers/gpu/drm/bridge/analogix/anx7625.c=2773=static void anx7625_i2c_remove(struct i2c_client *client)
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2779- if (platform->pdata.intp_irq)
drivers/gpu/drm/bridge/analogix/anx7625.c:2780: destroy_workqueue(platform->workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2781-
--
drivers/gpu/drm/bridge/analogix/anx7625.c-2783- cancel_delayed_work(&platform->hdcp_work);
drivers/gpu/drm/bridge/analogix/anx7625.c:2784: destroy_workqueue(platform->hdcp_workqueue);
drivers/gpu/drm/bridge/analogix/anx7625.c-2785- }
--
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=2724=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2783- ib_sa_unregister_client(&ipoib_sa_client);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2784: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2785-
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2792=static void __exit ipoib_cleanup_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2800- ipoib_unregister_debugfs();
drivers/infiniband/ulp/ipoib/ipoib_main.c:2801: destroy_workqueue(ipoib_workqueue);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2802-}
--
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=249=static int vpu_core_register(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-286- if (core->workqueue) {
drivers/media/platform/amphion/vpu_core.c:287: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-288- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_core.c=302=static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
--
drivers/media/platform/amphion/vpu_core.c-313- cancel_delayed_work_sync(&core->msg_delayed_work);
drivers/media/platform/amphion/vpu_core.c:314: destroy_workqueue(core->workqueue);
drivers/media/platform/amphion/vpu_core.c-315- core->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=699=static int vpu_v4l2_release(struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-704- cancel_work_sync(&inst->msg_work);
drivers/media/platform/amphion/vpu_v4l2.c:705: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-706- inst->workqueue = NULL;
--
drivers/media/platform/amphion/vpu_v4l2.c=720=int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
--
drivers/media/platform/amphion/vpu_v4l2.c-771- if (ret) {
drivers/media/platform/amphion/vpu_v4l2.c:772: destroy_workqueue(inst->workqueue);
drivers/media/platform/amphion/vpu_v4l2.c-773- 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=354=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-521- media_device_cleanup(&dev->mdev_dec);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:522: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-523-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-528- if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch))
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:529: destroy_workqueue(dev->core_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-530-err_res:
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c=568=static void mtk_vcodec_dec_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-571-
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c:572: destroy_workqueue(dev->decode_workqueue);
drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c-573-
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=235=static int mtk_vcodec_probe(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-365-err_enc_reg:
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:366: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-367-err_event_workq:
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c=458=static void mtk_vcodec_enc_remove(struct platform_device *pdev)
--
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-461-
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c:462: destroy_workqueue(dev->encode_workqueue);
drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c-463- 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=633=static int __init memstick_init(void)
--
drivers/memstick/core/memstick.c-653-error_destroy_workqueue:
drivers/memstick/core/memstick.c:654: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-655-
--
drivers/memstick/core/memstick.c=659=static void __exit memstick_exit(void)
--
drivers/memstick/core/memstick.c-662- bus_unregister(&memstick_bus_type);
drivers/memstick/core/memstick.c:663: destroy_workqueue(workqueue);
drivers/memstick/core/memstick.c-664- 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=1272=static __init int sm_module_init(void)
--
drivers/mtd/sm_ftl.c-1281- if (error)
drivers/mtd/sm_ftl.c:1282: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1283- return error;
--
drivers/mtd/sm_ftl.c=1287=static void __exit sm_module_exit(void)
drivers/mtd/sm_ftl.c-1288-{
drivers/mtd/sm_ftl.c:1289: destroy_workqueue(cache_flush_workqueue);
drivers/mtd/sm_ftl.c-1290- deregister_mtd_blktrans(&sm_ftl_ops);
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1586=static int xgbe_open(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1659-err_an_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1660: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1661-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1662-err_dev_wq:
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1663: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1664-
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c=1668=static int xgbe_close(struct net_device *netdev)
--
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1680-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1681: destroy_workqueue(pdata->an_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1682-
drivers/net/ethernet/amd/xgbe/xgbe-drv.c:1683: destroy_workqueue(pdata->dev_workqueue);
drivers/net/ethernet/amd/xgbe/xgbe-drv.c-1684-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3006=static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3138-err_destroy_ordered_workqueue:
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3139: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3140-
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c=3258=static void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev)
--
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3265-
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c:3266: destroy_workqueue(ethsw->workqueue);
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c-3267-
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=936=static int enetc4_pf_netdev_create(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-990-err_reg_netdev:
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:991: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-992-err_wq_init:
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c=1004=static void enetc4_pf_netdev_destroy(struct enetc_si *si)
--
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-1010- cancel_work(&si->rx_mode_task);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c:1011: destroy_workqueue(si->workqueue);
drivers/net/ethernet/freescale/enetc/enetc4_pf.c-1012- 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=2676=static void ef4_fini_struct(struct ef4_nic *efx)
--
drivers/net/ethernet/sfc/falcon/efx.c-2685- if (efx->workqueue) {
drivers/net/ethernet/sfc/falcon/efx.c:2686: destroy_workqueue(efx->workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-2687- efx->workqueue = NULL;
--
drivers/net/ethernet/sfc/falcon/efx.c=3156=static int __init ef4_init_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3178- err_pci:
drivers/net/ethernet/sfc/falcon/efx.c:3179: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3180- err_reset:
--
drivers/net/ethernet/sfc/falcon/efx.c=3186=static void __exit ef4_exit_module(void)
--
drivers/net/ethernet/sfc/falcon/efx.c-3190- pci_unregister_driver(&ef4_pci_driver);
drivers/net/ethernet/sfc/falcon/efx.c:3191: destroy_workqueue(reset_workqueue);
drivers/net/ethernet/sfc/falcon/efx.c-3192- 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=3617=struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
--
drivers/net/wireless/ath/ath10k/core.c-3752-err_free_tx_complete:
drivers/net/wireless/ath/ath10k/core.c:3753: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3754-err_free_aux_wq:
drivers/net/wireless/ath/ath10k/core.c:3755: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3756-err_free_wq:
drivers/net/wireless/ath/ath10k/core.c:3757: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3758-err_free_mac:
--
drivers/net/wireless/ath/ath10k/core.c=3765=void ath10k_core_destroy(struct ath10k *ar)
drivers/net/wireless/ath/ath10k/core.c-3766-{
drivers/net/wireless/ath/ath10k/core.c:3767: destroy_workqueue(ar->workqueue);
drivers/net/wireless/ath/ath10k/core.c-3768-
drivers/net/wireless/ath/ath10k/core.c:3769: destroy_workqueue(ar->workqueue_aux);
drivers/net/wireless/ath/ath10k/core.c-3770-
drivers/net/wireless/ath/ath10k/core.c:3771: destroy_workqueue(ar->workqueue_tx_complete);
drivers/net/wireless/ath/ath10k/core.c-3772-
--
drivers/net/wireless/ath/ath10k/sdio.c=2517=static int ath10k_sdio_probe(struct sdio_func *func,
--
drivers/net/wireless/ath/ath10k/sdio.c-2637-err_free_wq:
drivers/net/wireless/ath/ath10k/sdio.c:2638: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2639-err_core_destroy:
--
drivers/net/wireless/ath/ath10k/sdio.c=2645=static void ath10k_sdio_remove(struct sdio_func *func)
--
drivers/net/wireless/ath/ath10k/sdio.c-2657-
drivers/net/wireless/ath/ath10k/sdio.c:2658: destroy_workqueue(ar_sdio->workqueue);
drivers/net/wireless/ath/ath10k/sdio.c-2659-
--
drivers/net/wireless/ath/ath11k/core.c=2736=void ath11k_core_free(struct ath11k_base *ab)
drivers/net/wireless/ath/ath11k/core.c-2737-{
drivers/net/wireless/ath/ath11k/core.c:2738: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath11k/core.c:2739: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2740-
--
drivers/net/wireless/ath/ath11k/core.c=2745=struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
--
drivers/net/wireless/ath/ath11k/core.c-2790-err_free_wq:
drivers/net/wireless/ath/ath11k/core.c:2791: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath11k/core.c-2792-err_sc_free:
--
drivers/net/wireless/ath/ath12k/core.c=2235=void ath12k_core_free(struct ath12k_base *ab)
--
drivers/net/wireless/ath/ath12k/core.c-2237- timer_delete_sync(&ab->rx_replenish_retry);
drivers/net/wireless/ath/ath12k/core.c:2238: destroy_workqueue(ab->workqueue_aux);
drivers/net/wireless/ath/ath12k/core.c:2239: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath12k/core.c-2240- kfree(ab);
--
drivers/net/wireless/ath/ath12k/core.c=2243=struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
--
drivers/net/wireless/ath/ath12k/core.c-2296-err_free_wq:
drivers/net/wireless/ath/ath12k/core.c:2297: destroy_workqueue(ab->workqueue);
drivers/net/wireless/ath/ath12k/core.c-2298-err_sc_free:
--
]
]
|
| 18/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 37429
output: 25
thoughts: 20
Thoughts: Okay, here's my summary, framed as my internal thought process:
**Diving into Bluetooth Workqueue Destruction**
Alright, I need to investigate something related to workqueue destruction within the Bluetooth networking stack. A broad search would be a waste of time at this point. I know what I'm looking for is *likely* within the `net/bluetooth/` directory, so let's start with a focused `grep`. It's time to zero in on any instances of `destroy_workqueue` within that specific area. This should give me a manageable dataset to analyze, rather than a mountain of irrelevant results. This targeted approach is the most efficient way to pinpoint the exact code I need to understand, or potentially identify a bug related to workqueue cleanup. Now, let's see what comes up.
|
| 19/2 |
2026/05/22 22:53 |
tool |
grepper |
0m
Error:
Args: map[Expression:destroy_workqueue.*workqueue.*net/bluetooth]
Results:
|
| 20/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 37658
output: 24
thoughts: 16
Thoughts: Okay, here's the summary, rendered as a first-person thought process, keeping in mind the user is an expert:
**Understanding Grep's Focus**
Right, so I'm looking at this output and my initial assumption was off. It's easy to get caught up in the file structure, but it's crucial to remember the fundamentals. Ah, the `grep` expression, when I'm using it on a file, is being applied *directly* to the content of that file. It isn't trying to match against the file's path itself, not directly anyway. This seems like a pretty basic thing, but even seasoned professionals need to make sure they're not misinterpreting the tool. Makes complete sense when you take a step back and think it through.
|
| 21/2 |
2026/05/22 22:53 |
tool |
grepper |
0m
Args: map[Expression:destroy_workqueue.*hdev->workqueue]
Results: map[Output:net/bluetooth/hci_core.c=2585=int hci_register_dev(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-2613- if (!hdev->req_workqueue) {
net/bluetooth/hci_core.c:2614: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2615- error = -ENOMEM;
--
net/bluetooth/hci_core.c-2680- debugfs_remove_recursive(hdev->debugfs);
net/bluetooth/hci_core.c:2681: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2682- destroy_workqueue(hdev->req_workqueue);
--
net/bluetooth/hci_core.c=2744=void hci_release_dev(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-2749-
net/bluetooth/hci_core.c:2750: destroy_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-2751- destroy_workqueue(hdev->req_workqueue);
]
|
| 22/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 38189
output: 43
thoughts: 28
Thoughts: Here's my take on the thought process, as though I were the one thinking it:
**Deconstructing hci_release_dev**
Okay, so I'm seeing that `hci_release_dev` is responsible for destroying the `hdev->workqueue`. That's a key detail to lock down. Let's dig into the code for `hci_release_dev` directly. I need to understand the implications of that destruction and how it ties into the overall device release process. I want to see what else it's doing, what preconditions need to be met before the workqueue is nuked, and what potential issues might arise from doing this. Specifically, I'm looking for synchronization primitives, potential race conditions, and any cleanup routines that are also being triggered. This will help me understand the full impact of this function, not just the single workqueue destruction. It's time to dive in and get the granular view.
|
| 23/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_release_dev]
Results: map[SourceCode:2743: /* Release HCI device */
2744: void hci_release_dev(struct hci_dev *hdev)
2745: {
2746: debugfs_remove_recursive(hdev->debugfs);
2747: kfree_const(hdev->hw_info);
2748: kfree_const(hdev->fw_info);
2749:
2750: destroy_workqueue(hdev->workqueue);
2751: destroy_workqueue(hdev->req_workqueue);
2752:
2753: hci_dev_lock(hdev);
2754: hci_bdaddr_list_clear(&hdev->reject_list);
2755: hci_bdaddr_list_clear(&hdev->accept_list);
2756: hci_uuids_clear(hdev);
2757: hci_link_keys_clear(hdev);
2758: hci_smp_ltks_clear(hdev);
2759: hci_smp_irks_clear(hdev);
2760: hci_remote_oob_data_clear(hdev);
2761: hci_adv_instances_clear(hdev);
2762: hci_adv_monitors_clear(hdev);
2763: hci_bdaddr_list_clear(&hdev->le_accept_list);
2764: hci_bdaddr_list_clear(&hdev->le_resolv_list);
2765: hci_conn_params_clear_all(hdev);
2766: hci_discovery_filter_clear(hdev);
2767: hci_blocked_keys_clear(hdev);
2768: hci_codec_list_clear(&hdev->local_codecs);
2769: msft_release(hdev);
2770: hci_dev_unlock(hdev);
2771:
2772: ida_destroy(&hdev->unset_handle_ida);
2773: ida_free(&hci_index_ida, hdev->id);
2774: kfree_skb(hdev->sent_cmd);
2775: kfree_skb(hdev->req_skb);
2776: kfree_skb(hdev->recv_event);
2777: kfree(hdev);
2778: }
SourceFile:net/bluetooth/hci_core.c]
|
| 24/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 39128
output: 43
thoughts: 31
Thoughts: Here's my take on the situation:
**Device Release Order β A Quick Query**
Okay, so I'm thinking about the device cleanup sequence here, specifically in relation to HCI drivers. I see `hci_release_dev` getting called, which makes perfect sense as the final step in freeing up the device resources. But hang on a second... isn't `hci_unregister_dev` called *before* that? It feels like something is off about the ordering there. I need to refresh my memory on the driver lifecycle and make sure that this arrangement is consistent. Is `unregister` responsible for releasing resources or the `release` function? I hope this isn't a race condition. I need to make sure the device is completely de-initialized before it gets unregistered. Time to dig into the code and clarify the logic flow.
|
| 25/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_unregister_dev]
Results: map[SourceCode:2690: /* Unregister HCI device */
2691: void hci_unregister_dev(struct hci_dev *hdev)
2692: {
2693: BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
2694:
2695: mutex_lock(&hdev->unregister_lock);
2696: hci_dev_set_flag(hdev, HCI_UNREGISTER);
2697: mutex_unlock(&hdev->unregister_lock);
2698:
2699: write_lock(&hci_dev_list_lock);
2700: list_del(&hdev->list);
2701: write_unlock(&hci_dev_list_lock);
2702:
2703: synchronize_srcu(&hdev->srcu);
2704: cleanup_srcu_struct(&hdev->srcu);
2705:
2706: disable_work_sync(&hdev->rx_work);
2707: disable_work_sync(&hdev->cmd_work);
2708: disable_work_sync(&hdev->tx_work);
2709: disable_work_sync(&hdev->power_on);
2710: disable_work_sync(&hdev->error_reset);
2711:
2712: hci_cmd_sync_clear(hdev);
2713:
2714: hci_unregister_suspend_notifier(hdev);
2715:
2716: hci_dev_do_close(hdev);
2717:
2718: if (!test_bit(HCI_INIT, &hdev->flags) &&
2719: !hci_dev_test_flag(hdev, HCI_SETUP) &&
2720: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
2721: hci_dev_lock(hdev);
2722: mgmt_index_removed(hdev);
2723: hci_dev_unlock(hdev);
2724: }
2725:
2726: /* mgmt_index_removed should take care of emptying the
2727: * pending list */
2728: BUG_ON(!list_empty(&hdev->mgmt_pending));
2729:
2730: hci_sock_dev_event(hdev, HCI_DEV_UNREG);
2731:
2732: if (hdev->rfkill) {
2733: rfkill_unregister(hdev->rfkill);
2734: rfkill_destroy(hdev->rfkill);
2735: }
2736:
2737: device_del(&hdev->dev);
2738: /* Actual cleanup is deferred until hci_release_dev(). */
2739: hci_dev_put(hdev);
2740: }
SourceFile:net/bluetooth/hci_core.c]
|
| 26/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 40204
output: 46
thoughts: 198
Thoughts: **My Analysis of the Reproducer's Actions**
Okay, let's break this down. The reproducer's doing something interesting, it looks like: First, it's directly interacting with `/dev/rfkill`, which is always a red flag when you're dealing with hardware management. Then it's creating and connecting a `bt_sco` socket, which points to a Bluetooth SCO connection. The likely scenario is that by writing to `/dev/rfkill`, it's trying to manipulate the RF kill switch.
Specifically, it's writing `idx=0, type=2, op=3, soft=1, hard=1` to `/dev/rfkill`. Now, let's parse this: `type=2` is definitely `RFKILL_TYPE_BLUETOOTH`. `op=3` is `RFKILL_OP_CHANGE_ALL` that's the tell. And `soft=1` means blocking, so this is where it gets interesting, that will block the use of all bluetooth devices. So, this sets an rfkill rule that blocks all Bluetooth devices, effectively disabling all of them. This likely calls `hci_rfkill_set_block`, which is where we want to investigate the code. This looks like the core of the exploit or unintended behavior.
|
| 27/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_rfkill_set_block]
Results: map[SourceCode: 903: static int hci_rfkill_set_block(void *data, bool blocked)
904: {
905: struct hci_dev *hdev = data;
906: int err;
907:
908: BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
909:
910: if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
911: return -EBUSY;
912:
913: if (blocked == hci_dev_test_flag(hdev, HCI_RFKILLED))
914: return 0;
915:
916: if (blocked) {
917: hci_dev_set_flag(hdev, HCI_RFKILLED);
918:
919: if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
920: !hci_dev_test_flag(hdev, HCI_CONFIG)) {
921: err = hci_dev_do_poweroff(hdev);
922: if (err) {
923: bt_dev_err(hdev, "Error when powering off device on rfkill (%d)",
924: err);
925:
926: /* Make sure the device is still closed even if
927: * anything during power off sequence (eg.
928: * disconnecting devices) failed.
929: */
930: hci_dev_do_close(hdev);
931: }
932: }
933: } else {
934: hci_dev_clear_flag(hdev, HCI_RFKILLED);
935: }
936:
937: return 0;
938: }
SourceFile:net/bluetooth/hci_core.c]
|
| 28/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 41282
output: 45
thoughts: 38
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Dissecting the Bluetooth Device Shutdown Sequence**
Alright, the power's going down, or the device is closing. Either way, we're in the critical shutdown path. Let me zoom in, it looks like `hci_dev_do_close` is the point of interest. What's happening in there? This is where the crucial actions to gracefully disconnect and release resources will be. I need to understand this part intimately. Where are the cleanup routines? What are the implications if something fails? Let's trace this and make sure the stack is being properly cleaned up. I'm going to follow the logic and ensure there aren't any lingering pointers or potential memory leaks. Got it.
|
| 29/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_dev_do_close]
Results: map[SourceCode: 494: int hci_dev_do_close(struct hci_dev *hdev)
495: {
496: int err;
497:
498: BT_DBG("%s %p", hdev->name, hdev);
499:
500: hci_req_sync_lock(hdev);
501:
502: err = hci_dev_close_sync(hdev);
503:
504: hci_req_sync_unlock(hdev);
505:
506: return err;
507: }
SourceFile:net/bluetooth/hci_core.c]
|
| 30/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 41739
output: 45
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on `hci_dev_close_sync`:
**Understanding hci_dev_close_sync**
Alright, so the task at hand is `hci_dev_close_sync`. Immediately, I'm thinking, this is a function within the HCI (Host Controller Interface) layer, likely related to Bluetooth device management. The "close" part suggests it's designed to release resources and terminate a connection, and the "sync" implies a synchronous operation. That is, the caller is blocked until the operation completes, a characteristic I'm immediately filing away for later consideration - especially when thinking about thread management and the overall system responsiveness.
So, I'm already imagining the internal workings. This function must be handling the graceful shutdown of a Bluetooth device. Itβll probably involve sending various HCI commands to the device itself. Things like disabling the controller, potentially disconnecting any active links, maybe even resetting certain device configurations. All of this needs to be done in a *very* controlled manner to avoid leaving the system in an inconsistent state.
Since it's synchronous, I expect there to be a wait state involved. The function probably sends the commands and then blocks until it receives an acknowledgement, perhaps a status event indicating the close operation is complete. That has direct implications on the design considerations: I need to be careful with timeout values! I can see that improper timeout handling here could potentially lead to blocking a critical process indefinitely if, for some reason, the device doesn't respond as expected.
I will need to consider how errors are reported. Is it a simple return code, or are there more granular error codes exposed? And what are the failure scenarios that I need to account for? Is it a hardware issue, a communication problem, or perhaps an invalid configuration? Knowing the underlying error mechanism is crucial when troubleshooting and designing robust error handling into the higher-level code.
My main concern would be understanding the implications of this function call in the context of the larger Bluetooth stack. How does `hci_dev_close_sync` interact with other parts of the system? Are there any data races to be aware of? I need to know the state transitions handled by this function to ensure a clean teardown that doesnβt leave any lingering resources or cause unexpected behaviour in other processes. Finally, are there any potential race conditions I need to understand with any other devices or tasks accessing this resource, and how might I need to deal with those?
|
| 31/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c IncludeLines:true Name:hci_dev_close_sync]
Results: map[SourceCode:5284: int hci_dev_close_sync(struct hci_dev *hdev)
5285: {
5286: bool auto_off;
5287: int err = 0;
5288:
5289: bt_dev_dbg(hdev, "");
5290:
5291: if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
5292: disable_delayed_work(&hdev->power_off);
5293: disable_delayed_work(&hdev->ncmd_timer);
5294: disable_delayed_work(&hdev->le_scan_disable);
5295: } else {
5296: cancel_delayed_work(&hdev->power_off);
5297: cancel_delayed_work(&hdev->ncmd_timer);
5298: cancel_delayed_work(&hdev->le_scan_disable);
5299: }
5300:
5301: hci_cmd_sync_cancel_sync(hdev, ENODEV);
5302:
5303: cancel_interleave_scan(hdev);
5304:
5305: if (hdev->adv_instance_timeout) {
5306: cancel_delayed_work_sync(&hdev->adv_instance_expire);
5307: hdev->adv_instance_timeout = 0;
5308: }
5309:
5310: err = hci_dev_shutdown(hdev);
5311:
5312: if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
5313: cancel_delayed_work_sync(&hdev->cmd_timer);
5314: return err;
5315: }
5316:
5317: hci_leds_update_powered(hdev, false);
5318:
5319: /* Flush RX and TX works */
5320: flush_work(&hdev->tx_work);
5321: flush_work(&hdev->rx_work);
5322:
5323: if (hdev->discov_timeout > 0) {
5324: hdev->discov_timeout = 0;
5325: hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
5326: hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
5327: }
5328:
5329: if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
5330: cancel_delayed_work(&hdev->service_cache);
5331:
5332: if (hci_dev_test_flag(hdev, HCI_MGMT)) {
5333: struct adv_info *adv_instance;
5334:
5335: cancel_delayed_work_sync(&hdev->rpa_expired);
5336:
5337: list_for_each_entry(adv_instance, &hdev->adv_instances, list)
5338: cancel_delayed_work_sync(&adv_instance->rpa_expired_cb);
5339: }
5340:
5341: /* Avoid potential lockdep warnings from the *_flush() calls by
5342: * ensuring the workqueue is empty up front.
5343: */
5344: drain_workqueue(hdev->workqueue);
5345:
5346: hci_dev_lock(hdev);
5347:
5348: hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
5349:
5350: auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
5351:
5352: if (!auto_off && !hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
5353: hci_dev_test_flag(hdev, HCI_MGMT))
5354: __mgmt_power_off(hdev);
5355:
5356: hci_inquiry_cache_flush(hdev);
5357: hci_pend_le_actions_clear(hdev);
5358: hci_conn_hash_flush(hdev);
5359: /* Prevent data races on hdev->smp_data or hdev->smp_bredr_data */
5360: smp_unregister(hdev);
5361: hci_dev_unlock(hdev);
5362:
5363: hci_sock_dev_event(hdev, HCI_DEV_DOWN);
5364:
5365: if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
5366: aosp_do_close(hdev);
5367: msft_do_close(hdev);
5368: }
5369:
5370: if (hdev->flush)
5371: hdev->flush(hdev);
5372:
5373: /* Reset device */
5374: skb_queue_purge(&hdev->cmd_q);
5375: atomic_set(&hdev->cmd_cnt, 1);
5376: if (hci_test_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE) &&
5377: !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
5378: set_bit(HCI_INIT, &hdev->flags);
5379: hci_reset_sync(hdev);
5380: clear_bit(HCI_INIT, &hdev->flags);
5381: }
5382:
5383: /* flush cmd work */
5384: flush_work(&hdev->cmd_work);
5385:
5386: /* Drop queues */
5387: skb_queue_purge(&hdev->rx_q);
5388: skb_queue_purge(&hdev->cmd_q);
5389: skb_queue_purge(&hdev->raw_q);
5390:
5391: /* Drop last sent command */
5392: if (hdev->sent_cmd) {
5393: cancel_delayed_work_sync(&hdev->cmd_timer);
5394: kfree_skb(hdev->sent_cmd);
5395: hdev->sent_cmd = NULL;
5396: }
5397:
5398: /* Drop last request */
5399: if (hdev->req_skb) {
5400: kfree_skb(hdev->req_skb);
5401: hdev->req_skb = NULL;
5402: }
5403:
5404: clear_bit(HCI_RUNNING, &hdev->flags);
5405: hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
5406:
5407: /* After this point our queues are empty and no tasks are scheduled. */
5408: hdev->close(hdev);
5409:
5410: /* Clear flags */
5411: hdev->flags &= BIT(HCI_RAW);
5412: hci_dev_clear_volatile_flags(hdev);
5413:
5414: memset(hdev->eir, 0, sizeof(hdev->eir));
5415: memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
5416: bacpy(&hdev->random_addr, BDADDR_ANY);
5417: hci_codec_list_clear(&hdev->local_codecs);
5418:
5419: hci_dev_put(hdev);
5420: return err;
5421: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 32/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 44538
output: 45
thoughts: 86
Thoughts: Here's my analysis of what's happening, keeping in mind my deep understanding of the intricacies of this system:
**Analyzing the Workqueue Draining Process**
Okay, so looking at this section of the code, I see the call to `drain_workqueue(hdev->workqueue);` at line 5344. That's immediately interesting. The `drain_workqueue` function, as I recall, sets the `__WQ_DRAINING` flag on the workqueue. That's the crucial first step to ensuring no new work items are accepted. Essentially, we're putting a temporary stop on the workqueue!
The real meat of the operation then follows. At line 5358, we have `hci_conn_hash_flush(hdev);`. *This* is where the interesting things happen. Given the context, my immediate thought is that this function likely interacts with some sort of connection hash table associated with the HCI device (`hdev`). I'm going to want to take a deeper dive into `hci_conn_hash_flush` to fully understand the flushing process after the workqueue has been drained. This is where the potential for data loss or corruption is possible, so i have to be very careful.
|
| 33/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_hash_flush]
Results: map[SourceCode:2628: /* Drop all connection on the device */
2629: void hci_conn_hash_flush(struct hci_dev *hdev)
2630: {
2631: struct list_head *head = &hdev->conn_hash.list;
2632: struct hci_conn *conn;
2633:
2634: BT_DBG("hdev %s", hdev->name);
2635:
2636: /* We should not traverse the list here, because hci_conn_del
2637: * can remove extra links, which may cause the list traversal
2638: * to hit items that have already been released.
2639: */
2640: while ((conn = list_first_entry_or_null(head,
2641: struct hci_conn,
2642: list)) != NULL) {
2643: conn->state = BT_CLOSED;
2644: hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
2645: hci_conn_del(conn);
2646: }
2647: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 34/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 45272
output: 45
thoughts: 410
Thoughts: **Analyzing a Potential Race Condition in Bluetooth Connection Handling**
Okay, so I'm looking at this code and trying to understand a possible race condition. The scenario involves `hci_conn_hash_flush`, which, as the name suggests, removes all connections. But the reproducer code calls `connect()` on a `bt_sco` socket, which leads to `hci_connect_sco`. And *that* calls `hci_connect_acl`.
`hci_connect_acl` seems to be responsible for creating a new ACL connection and either adding it to the `hdev->conn_hash` or looking it up. The crux of the matter appears to be concurrency. If `hci_dev_close_sync` is running concurrently with `hci_connect_sco`, we've got a potential issue.
Looking deeper into the `hci_connect_sco` function, it does a few things. It calls `hci_connect_acl` to set up the ACL connection. If that succeeds, it checks for an existing SCO connection using `hci_conn_hash_lookup_ba`. If no SCO exists it calls `hci_conn_add_unset` to create one. Crucially, if that function fails (i.e. returns an error), it then calls `hci_conn_drop(acl)`.
Now, if `hci_conn_add_unset` fails, `hci_conn_drop` gets called. `hci_conn_drop` then schedules a delayed work using `queue_delayed_work(conn->hdev->workqueue, &conn->disc_work, timeo);`. The problem here is if `hdev->workqueue` is being drained by `drain_workqueue(hdev->workqueue)` within `hci_dev_close_sync`, `queue_delayed_work` will trigger a warning.
The question then is why does `hci_conn_add_unset` fail? Let me go look at its code.
|
| 35/2 |
2026/05/22 22:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_add_unset]
Results: map[SourceCode:1079: struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
1080: bdaddr_t *dst, u8 dst_type, u8 role)
1081: {
1082: int handle;
1083:
1084: bt_dev_dbg(hdev, "dst %pMR", dst);
1085:
1086: handle = hci_conn_hash_alloc_unset(hdev);
1087: if (unlikely(handle < 0))
1088: return ERR_PTR(-ECONNREFUSED);
1089:
1090: return __hci_conn_add(hdev, type, dst, dst_type, role, handle);
1091: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 36/2 |
2026/05/22 22:53 |
llm |
expert |
0m
Model:
Tokens:
input: 46390
output: 47
thoughts: 15
Thoughts: Here's a summary of my understanding:
**Allocating a Connection Hash ID**
Okay, so I'm looking at this `hci_conn_hash_alloc_unset` function. My initial thought is, it's pretty clear from the name what it does: it's designed to allocate an ID within the connection hash. The "unset" part tells me it's likely dealing with an unassigned or available slot. So, I need to understand the underlying data structure and how it manages these IDs. I'm assuming there's some kind of internal hash table or similar mechanism. The question is, how does this function handle collisions or available slots within that structure? Is it just grabbing the first free one, or is there a more sophisticated search algorithm at play? I'll probably need to dig into the surrounding code to determine how the connections are indexed and what criteria are used to determine which slot to unset or use. This function is likely a critical piece in the connection management puzzle.
|
| 37/2 |
2026/05/22 22:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_hash_alloc_unset]
Results: map[SourceCode: 919: static int hci_conn_hash_alloc_unset(struct hci_dev *hdev)
920: {
921: return ida_alloc_range(&hdev->unset_handle_ida, HCI_CONN_HANDLE_MAX + 1,
922: U16_MAX, GFP_ATOMIC);
923: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 38/2 |
2026/05/22 22:54 |
llm |
expert |
0m
Model:
Tokens:
input: 46788
output: 41
thoughts: 52
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**Analyzing Potential Lock Contention in Bluetooth SCO Connection**
Alright, so I'm looking at this code and a potential failure scenario. First, `ida_alloc_range` failing is a common enough thing β we need to handle that gracefully, it's just an error return. But that's not what I'm focused on right now. My real concern is this call chain: `hci_connect_sco` is being invoked from `sco_sock_connect`. Fine, nothing inherently wrong there on its face. But... the lock. That's the key. **Does `sco_sock_connect` hold `hdev->lock` when it calls `hci_connect_sco`?** That's the critical piece of information I need to confirm. If `sco_sock_connect` is *already* holding that device lock, then we have a potential double-acquire risk within `hci_connect_sco` or even worse, a deadlock if something within the connection path requires it. I need to trace the lock acquisition in `sco_sock_connect` *very* carefully and make sure this isn't a concurrency problem waiting to happen.
|
| 39/2 |
2026/05/22 22:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/sco.c IncludeLines:true Name:sco_sock_connect]
Results: map[SourceCode: 642: static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, int alen, int flags)
643: {
644: struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
645: struct sock *sk = sock->sk;
646: int err;
647:
648: BT_DBG("sk %p", sk);
649:
650: if (alen < sizeof(struct sockaddr_sco) ||
651: addr->sa_family != AF_BLUETOOTH)
652: return -EINVAL;
653:
654: if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
655: return -EBADFD;
656:
657: if (sk->sk_type != SOCK_SEQPACKET)
658: err = -EINVAL;
659:
660: lock_sock(sk);
661: /* Set destination address and psm */
662: bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
663: release_sock(sk);
664:
665: err = sco_connect(sk);
666: if (err)
667: return err;
668:
669: lock_sock(sk);
670:
671: err = bt_sock_wait_state(sk, BT_CONNECTED,
672: sock_sndtimeo(sk, flags & O_NONBLOCK));
673:
674: release_sock(sk);
675: return err;
676: }
SourceFile:net/bluetooth/sco.c]
|
| 40/2 |
2026/05/22 22:54 |
llm |
expert |
0m
Model:
Tokens:
input: 47665
output: 39
thoughts: 22
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**My Immediate Assessment of the SCO Connection Process**
Alright, so the initial thought here is pretty straightforward. When I'm working with the `sco_connect` function, my mind immediately jumps to the underlying Bluetooth HCI layer. I see that `sco_connect` itself is orchestrating two crucial HCI commands. The first is a call to `hci_get_route`. This tells me that we need to determine the *best* hardware route for the SCO connection. Obviously, this is for performance and reliability reasons; things like available controllers and their individual capabilities are going to be factors here. Then, once the route is established, the next command to be invoked is `hci_connect_sco`. This is the actual *initiation* of the Synchronous Connection-Oriented link. The initial call must determine the route, and then the actual connection is established. It is a very basic process for establishing an SCO link.
|
| 41/2 |
2026/05/22 22:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/sco.c IncludeLines:true Name:sco_connect]
Results: map[SourceCode: 310: static int sco_connect(struct sock *sk)
311: {
312: struct sco_conn *conn;
313: struct hci_conn *hcon;
314: struct hci_dev *hdev;
315: int err, type;
316:
317: BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
318:
319: hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
320: if (!hdev)
321: return -EHOSTUNREACH;
322:
323: hci_dev_lock(hdev);
324:
325: if (lmp_esco_capable(hdev) && !disable_esco)
326: type = ESCO_LINK;
327: else
328: type = SCO_LINK;
329:
330: switch (sco_pi(sk)->setting & SCO_AIRMODE_MASK) {
331: case SCO_AIRMODE_TRANSP:
332: if (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)) {
333: err = -EOPNOTSUPP;
334: goto unlock;
335: }
336: break;
337: }
338:
339: hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
340: sco_pi(sk)->setting, &sco_pi(sk)->codec,
341: READ_ONCE(sk->sk_sndtimeo));
342: if (IS_ERR(hcon)) {
343: err = PTR_ERR(hcon);
344: goto unlock;
345: }
346:
347: conn = sco_conn_add(hcon);
348: if (!conn) {
349: hci_conn_drop(hcon);
350: err = -ENOMEM;
351: goto unlock;
352: }
353:
354: lock_sock(sk);
355:
356: err = sco_chan_add(conn, sk, NULL);
357: if (err) {
358: release_sock(sk);
359: goto unlock;
360: }
361:
362: /* Update source addr of the socket */
363: bacpy(&sco_pi(sk)->src, &hcon->src);
364:
365: if (hcon->state == BT_CONNECTED) {
366: sco_sock_clear_timer(sk);
367: sk->sk_state = BT_CONNECTED;
368: } else {
369: sk->sk_state = BT_CONNECT;
370: sco_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
371: }
372:
373: release_sock(sk);
374:
375: unlock:
376: hci_dev_unlock(hdev);
377: hci_dev_put(hdev);
378: return err;
379: }
SourceFile:net/bluetooth/sco.c]
|
| 42/2 |
2026/05/22 22:54 |
llm |
expert |
0m
Model:
Tokens:
input: 48957
output: 44
thoughts: 245
Thoughts: **Concurrency Concerns in SCO Connection Handling**
Okay, so I'm looking at this `sco_connect` function and immediately, my brain flags the `hci_dev_lock(hdev)` call. Standard stuff. But then, I remember seeing `hci_dev_close_sync` also grabbing that very same lock at line 5346. That's a potential concurrency issue, isn't it?
The real problem crystallizes when I see that `drain_workqueue(hdev->workqueue)` is called *before* `hci_dev_lock(hdev)` at line 5344. That means `drain_workqueue` is running without the protection of `hdev->lock`, which means `sco_connect` could potentially run concurrently with it and `hci_dev_close_sync` up until line 5346. That's a race condition waiting to happen.
So, let's say `sco_connect` wins the race and gets to run. If it calls `hci_connect_sco`, which in turn calls `hci_connect_acl`, that ACL connection might succeed. But then what? Then the call is made to `hci_conn_add_unset`, and *that's* where things could go sideways. I know it can fail if `ida_alloc_range` has an issue, or even if `__hci_conn_add` itself hiccups.
I'm getting that nagging feeling that `hci_conn_add_unset` is the key to this. Why would it fail? I need to refresh my memory and check out `__hci_conn_add` to see where the vulnerabilities are.
|
| 43/2 |
2026/05/22 22:54 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:__hci_conn_add]
Results: map[SourceCode: 925: static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
926: bdaddr_t *dst, u8 dst_type,
927: u8 role, u16 handle)
928: {
929: struct hci_conn *conn;
930: struct smp_irk *irk = NULL;
931:
932: switch (type) {
933: case ACL_LINK:
934: if (!hdev->acl_mtu)
935: return ERR_PTR(-ECONNREFUSED);
936: break;
937: case CIS_LINK:
938: case BIS_LINK:
939: case PA_LINK:
940: if (!hdev->iso_mtu)
941: return ERR_PTR(-ECONNREFUSED);
942: irk = hci_get_irk(hdev, dst, dst_type);
943: break;
944: case LE_LINK:
945: if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
946: return ERR_PTR(-ECONNREFUSED);
947: if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
948: return ERR_PTR(-ECONNREFUSED);
949: irk = hci_get_irk(hdev, dst, dst_type);
950: break;
951: case SCO_LINK:
952: case ESCO_LINK:
953: if (!hdev->sco_pkts)
954: /* Controller does not support SCO or eSCO over HCI */
955: return ERR_PTR(-ECONNREFUSED);
956: break;
957: default:
958: return ERR_PTR(-ECONNREFUSED);
959: }
960:
961: bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
962:
963: conn = kzalloc(sizeof(*conn), GFP_KERNEL);
964: if (!conn)
965: return ERR_PTR(-ENOMEM);
966:
967: /* If and IRK exists use its identity address */
968: if (!irk) {
969: bacpy(&conn->dst, dst);
970: conn->dst_type = dst_type;
971: } else {
972: bacpy(&conn->dst, &irk->bdaddr);
973: conn->dst_type = irk->addr_type;
974: }
975:
976: bacpy(&conn->src, &hdev->bdaddr);
977: conn->handle = handle;
978: conn->hdev = hdev;
979: conn->type = type;
980: conn->role = role;
981: conn->mode = HCI_CM_ACTIVE;
982: conn->state = BT_OPEN;
983: conn->auth_type = HCI_AT_GENERAL_BONDING;
984: conn->io_capability = hdev->io_capability;
985: conn->remote_auth = 0xff;
986: conn->key_type = 0xff;
987: conn->rssi = HCI_RSSI_INVALID;
988: conn->tx_power = HCI_TX_POWER_INVALID;
989: conn->max_tx_power = HCI_TX_POWER_INVALID;
990: conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
991: conn->sid = HCI_SID_INVALID;
992:
993: set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
994: conn->disc_timeout = HCI_DISCONN_TIMEOUT;
995:
996: /* Set Default Authenticated payload timeout to 30s */
997: conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
998:
999: if (conn->role == HCI_ROLE_MASTER)
1000: conn->out = true;
1001:
1002: switch (type) {
1003: case ACL_LINK:
1004: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
1005: conn->link_policy = hdev->link_policy;
1006: conn->mtu = hdev->acl_mtu;
1007: break;
1008: case LE_LINK:
1009: /* conn->src should reflect the local identity address */
1010: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1011: conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
1012: /* Use the controller supported PHYS as default until the
1013: * remote features are resolved.
1014: */
1015: conn->le_tx_def_phys = hdev->le_tx_def_phys;
1016: conn->le_rx_def_phys = hdev->le_tx_def_phys;
1017: break;
1018: case CIS_LINK:
1019: /* conn->src should reflect the local identity address */
1020: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1021:
1022: if (conn->role == HCI_ROLE_MASTER)
1023: conn->cleanup = cis_cleanup;
1024:
1025: conn->mtu = hdev->iso_mtu;
1026: break;
1027: case PA_LINK:
1028: case BIS_LINK:
1029: /* conn->src should reflect the local identity address */
1030: hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
1031: conn->cleanup = bis_cleanup;
1032: conn->mtu = hdev->iso_mtu;
1033: break;
1034: case SCO_LINK:
1035: if (lmp_esco_capable(hdev))
1036: conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
1037: (hdev->esco_type & EDR_ESCO_MASK);
1038: else
1039: conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
1040:
1041: conn->mtu = hdev->sco_mtu;
1042: break;
1043: case ESCO_LINK:
1044: conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
1045: conn->mtu = hdev->sco_mtu;
1046: break;
1047: }
1048:
1049: skb_queue_head_init(&conn->data_q);
1050: skb_queue_head_init(&conn->tx_q.queue);
1051:
1052: INIT_LIST_HEAD(&conn->chan_list);
1053: INIT_LIST_HEAD(&conn->link_list);
1054:
1055: INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
1056: INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
1057: INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
1058: INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
1059:
1060: atomic_set(&conn->refcnt, 0);
1061:
1062: hci_dev_hold(hdev);
1063:
1064: hci_conn_hash_add(hdev, conn);
1065:
1066: /* The SCO and eSCO connections will only be notified when their
1067: * setup has been completed. This is different to ACL links which
1068: * can be notified right away.
1069: */
1070: if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
1071: if (hdev->notify)
1072: hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
1073: }
1074:
1075: hci_conn_init_sysfs(conn);
1076: return conn;
1077: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 44/2 |
2026/05/22 22:54 |
llm |
expert |
0m
Model:
Tokens:
input: 52114
output: 43
thoughts: 1089
Thoughts: **Analyzing the Bluetooth SCO Connection and Workqueue Interaction**
Okay, let me break this down. It seems we have a potential issue related to how SCO (Synchronous Connection-Oriented) connections are handled and how they interact with the workqueue during device closure.
First, I see that `__hci_conn_add` can fail. If this happens, `hci_conn_add_unset` returns an error, and `hci_connect_sco` subsequently drops the ACL connection via `hci_conn_drop`. `hci_conn_drop` queues `disc_work` to the device's workqueue (`hdev->workqueue`).
The core of the problem appears to stem from the interaction between `hci_dev_close_sync` (which ultimately calls `drain_workqueue`) and `sco_connect`. `drain_workqueue` could be running concurrently with `sco_connect` because it's called from `hci_dev_close_sync` *without* holding `hdev->lock`, while `sco_connect` *does* hold `hdev->lock`. This is a race condition. Specifically, `drain_workqueue` sets the `__WQ_DRAINING` flag and waits for the workqueue to drain.
`drain_workqueue` is called within `hci_dev_close_sync`, which is called from `hci_dev_do_close` when an RFKill event occurs. `sco_connect` is initiated via `hci_get_route` which takes a reference to the `hdev` structure. The important point here is that there's no protection in place to prevent `hci_dev_close_sync` from running concurrently with `sco_connect`. `hci_dev_do_close` obtains `hci_req_sync_lock(hdev)` while `sco_connect` takes `hci_dev_lock(hdev)`. So, the lock is different.
Here's the sequence: If `drain_workqueue` gets called, it sets `__WQ_DRAINING`, which prevents new work from being queued. Now, the warning arises because when `sco_connect` tries to queue `disc_work` using `queue_delayed_work`, the workqueue is already draining. The `WARN_ONCE` is triggered because a work item is being queued to a workqueue that's in a draining state.
But if `drain_workqueue` *is* running, it waits for executing works to finish, and sets `__WQ_DRAINING` to prevent new works from being queued.
My thought: This is a bug because a work item is not properly scheduled, but will be canceled later. However, `hci_conn_hash_flush(hdev)` is called which will eventually delete all connections and cancel their works. Critically, `hci_conn_hash_flush` *does* hold `hci_dev_lock(hdev)`. But `drain_workqueue` is called *before* this lock.
So, the crux of the race condition is that `drain_workqueue` sets `__WQ_DRAINING` while `sco_connect` is running, holding the lock, and attempting to queue `disc_work`. The warning is triggered, but the work is not queued. Then `sco_connect` finishes and releases the lock. Finally, `hci_dev_close_sync` *acquires* `hci_dev_lock(hdev)` and *then* calls `hci_conn_hash_flush(hdev)`. The work that *couldn't* be queued (due to the `__WQ_DRAINING` flag) is ultimately canceled by `hci_conn_hash_flush`.
The warning is triggered, the work is not queued, the ACL connection is dropped, its refcnt goes to zero, and `disc_work` is not called. `disc_work` calls `hci_conn_timeout`.
I think this could be exploitable, but it's not a critical vulnerability. The `WARN_ONCE` is the only indicator of this issue.
|
| 45/2 |
2026/05/22 22:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_timeout]
Results: map[SourceCode: 559: static void hci_conn_timeout(struct work_struct *work)
560: {
561: struct hci_conn *conn = container_of(work, struct hci_conn,
562: disc_work.work);
563: int refcnt = atomic_read(&conn->refcnt);
564:
565: BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
566:
567: WARN_ON(refcnt < 0);
568:
569: /* FIXME: It was observed that in pairing failed scenario, refcnt
570: * drops below 0. Probably this is because l2cap_conn_del calls
571: * l2cap_chan_del for each channel, and inside l2cap_chan_del conn is
572: * dropped. After that loop hci_chan_del is called which also drops
573: * conn. For now make sure that ACL is alive if refcnt is higher then 0,
574: * otherwise drop it.
575: */
576: if (refcnt > 0)
577: return;
578:
579: hci_abort_conn(conn, hci_proto_disconn_ind(conn));
580: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 46/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 54522
output: 43
thoughts: 118
Thoughts: **Analyzing the Connection Cleanup Process**
Okay, so I'm trying to trace the connection teardown logic here. The core assumption is that if `disc_work` doesn't get executed, then we *shouldn't* expect `hci_abort_conn` to be called directly. But hold on, there's another point of execution I need to consider, and it's key: `hci_dev_close_sync` is likely to come into play. Right after `drain_workqueue`, it calls `hci_conn_hash_flush(hdev)`.
Now, `hci_conn_hash_flush` is interesting. From what I recall, it's the function that iterates through *all* the active connections within `hdev->conn_hash` and for each connection, it then calls `hci_conn_del(conn)`. This is the potential cleanup route I was missing!
And finally, `hci_conn_del`, is the key, this function should be responsible for cleaning up the connection data structures and freeing up the allocated memory. I think I'll go take a look at the code for that function now, to confirm the details.
|
| 47/2 |
2026/05/22 22:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_conn_del]
Results: map[SourceCode:1170: void hci_conn_del(struct hci_conn *conn)
1171: {
1172: struct hci_dev *hdev = conn->hdev;
1173:
1174: BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
1175:
1176: hci_conn_unlink(conn);
1177:
1178: disable_delayed_work_sync(&conn->disc_work);
1179: disable_delayed_work_sync(&conn->auto_accept_work);
1180: disable_delayed_work_sync(&conn->idle_work);
1181:
1182: /* Remove the connection from the list so unacked logic can detect when
1183: * a certain pool is not being utilized.
1184: */
1185: hci_conn_hash_del(hdev, conn);
1186:
1187: /* Handle unacked frames:
1188: *
1189: * - In case there are no connection, or if restoring the buffers
1190: * considered in transist would overflow, restore all buffers to the
1191: * pool.
1192: * - Otherwise restore just the buffers considered in transit for the
1193: * hci_conn
1194: */
1195: switch (conn->type) {
1196: case ACL_LINK:
1197: if (!hci_conn_num(hdev, ACL_LINK) ||
1198: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1199: hdev->acl_cnt = hdev->acl_pkts;
1200: else
1201: hdev->acl_cnt += conn->sent;
1202: break;
1203: case LE_LINK:
1204: cancel_delayed_work(&conn->le_conn_timeout);
1205:
1206: if (hdev->le_pkts) {
1207: if (!hci_conn_num(hdev, LE_LINK) ||
1208: hdev->le_cnt + conn->sent > hdev->le_pkts)
1209: hdev->le_cnt = hdev->le_pkts;
1210: else
1211: hdev->le_cnt += conn->sent;
1212: } else {
1213: if ((!hci_conn_num(hdev, LE_LINK) &&
1214: !hci_conn_num(hdev, ACL_LINK)) ||
1215: hdev->acl_cnt + conn->sent > hdev->acl_pkts)
1216: hdev->acl_cnt = hdev->acl_pkts;
1217: else
1218: hdev->acl_cnt += conn->sent;
1219: }
1220: break;
1221: case CIS_LINK:
1222: case BIS_LINK:
1223: case PA_LINK:
1224: if (!hci_iso_count(hdev) ||
1225: hdev->iso_cnt + conn->sent > hdev->iso_pkts)
1226: hdev->iso_cnt = hdev->iso_pkts;
1227: else
1228: hdev->iso_cnt += conn->sent;
1229: break;
1230: }
1231:
1232: skb_queue_purge(&conn->data_q);
1233: skb_queue_purge(&conn->tx_q.queue);
1234:
1235: /* Remove the connection from the list and cleanup its remaining
1236: * state. This is a separate function since for some cases like
1237: * BT_CONNECT_SCAN we *only* want the cleanup part without the
1238: * rest of hci_conn_del.
1239: */
1240: hci_conn_cleanup(conn);
1241:
1242: /* Dequeue callbacks using connection pointer as data */
1243: hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
1244: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 48/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 56136
output: 43
thoughts: 11
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Connection Cleanup: Finalizing a HCI Transaction**
Alright, so `hci_conn_cleanup`. Right. My understanding is that this is the final, essential step in freeing up a connection after a completed HCI (Host Controller Interface) interaction. We're talking about releasing all the associated resources. This includes, but isn't limited to, data structures holding connection-specific information like handles, states, and any allocated buffers. It's the point where we make sure nothing lingers that could potentially lead to memory leaks, state inconsistencies, or just general instability down the line. We need to be absolutely certain all related resources are de-allocated or set to a safe, default state. It's a critical point to ensure proper operation of the Bluetooth stack. Essentially, it's the last stage of a properly managed connection, and we want to ensure everything is tidy before we let go.
|
| 49/2 |
2026/05/22 22:55 |
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]
|
| 50/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 57001
output: 43
thoughts: 1203
Thoughts: **Analyzing the Connection Cleanup in Bluetooth HCI**
Okay, let me think this through. `hci_conn_del_sysfs` calls `device_del` and `put_device`, which is standard. The key is that the connection is freed when its refcount hits zero. That means `hci_conn_hash_flush` should properly clean things up. This warning, that's just a warning from `queue_delayed_work` due to the `__WQ_DRAINING` flag set by `drain_workqueue`.
The big question is always memory corruption or use-after-free, right? If `queue_delayed_work` fails (returns false), the work isn't queued. The connection *should* be freed later by `hci_conn_hash_flush`.
But wait... what happens if `sco_connect` completes before `hci_conn_hash_flush` runs? `sco_connect` returns an error, like `-ECONNREFUSED` or `-ENOMEM`. The socket isn't connected, and the connection *should* be freed. Seems fine, so far.
The warning is just a `WARN_ONCE` in `__queue_work`, which is triggered when trying to queue work on a draining workqueue. It's a `WARN_ONCE`, so it won't crash the kernel in production (unless `panic_on_warn` is set, but that's unusual). It's not a denial of service, really, except maybe if the work was critical and not executing it caused a hang. In this case, the work is `disc_work`, which calls `hci_conn_timeout` to abort the connection. Since `hci_conn_hash_flush` *will* delete the connection anyway, it doesn't matter if `disc_work` doesn't execute.
Wait, what if `queue_delayed_work` fails? `hci_conn_drop` drops the refcount. If the refcount reaches zero, it *should* queue `disc_work`. If `disc_work` is not queued, the connection is left in the hash table with refcount 0. But `hci_conn_hash_flush` will iterate and delete *all* connections, regardless of refcount.
Let me look at `hci_conn_hash_flush`:
```c
while ((conn = list_first_entry_or_null(head,
struct hci_conn,
list)) != NULL) {
conn->state = BT_CLOSED;
hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_del(conn);
}
```
Okay, it removes the connection from the list, calls `hci_conn_del`. `hci_conn_del` calls `disable_delayed_work_sync(&conn->disc_work);` which is fine even if `disc_work` wasn't queued. Then, it frees the connection. No memory leak!
Now, what if `hci_conn_hash_flush` runs BEFORE `sco_connect` drops the connection? `hci_conn_hash_flush` holds `hci_dev_lock(hdev)`. But `sco_connect` also holds `hci_dev_lock(hdev)`. So, they can't run concurrently, it's either before or after.
If `sco_connect` runs before `hci_conn_hash_flush`:
1. `drain_workqueue` runs (sets `__WQ_DRAINING`).
2. `sco_connect` runs, holds `hci_dev_lock`.
3. `sco_connect` calls `hci_connect_sco`.
4. `hci_connect_sco` calls `hci_connect_acl`, which adds `acl` to `conn_hash`.
5. `hci_connect_sco` calls `hci_conn_add_unset`, which fails.
6. `hci_connect_sco` calls `hci_conn_drop(acl)`.
7. `hci_conn_drop` drops refcount to 0, calls `queue_delayed_work`.
8. `queue_delayed_work` triggers the warning because `__WQ_DRAINING` is set.
9. `sco_connect` releases `hci_dev_lock`.
10. `hci_dev_close_sync` acquires `hci_dev_lock`.
11. `hci_dev_close_sync` calls `hci_conn_hash_flush`.
12. `hci_conn_hash_flush` deletes `acl` (and any other connections).
Perfectly safe. No memory leak
|
| 51/2 |
2026/05/22 22:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_conn.c IncludeLines:true Name:hci_connect_acl]
Results: map[SourceCode:1677: struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
1678: u8 sec_level, u8 auth_type,
1679: enum conn_reasons conn_reason, u16 timeout)
1680: {
1681: struct hci_conn *acl;
1682:
1683: if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
1684: if (lmp_bredr_capable(hdev))
1685: return ERR_PTR(-ECONNREFUSED);
1686:
1687: return ERR_PTR(-EOPNOTSUPP);
1688: }
1689:
1690: /* Reject outgoing connection to device with same BD ADDR against
1691: * CVE-2020-26555
1692: */
1693: if (!bacmp(&hdev->bdaddr, dst)) {
1694: bt_dev_dbg(hdev, "Reject connection with same BD_ADDR %pMR\n",
1695: dst);
1696: return ERR_PTR(-ECONNREFUSED);
1697: }
1698:
1699: acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
1700: if (!acl) {
1701: acl = hci_conn_add_unset(hdev, ACL_LINK, dst, 0,
1702: HCI_ROLE_MASTER);
1703: if (IS_ERR(acl))
1704: return acl;
1705: }
1706:
1707: hci_conn_hold(acl);
1708:
1709: acl->conn_reason = conn_reason;
1710: if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
1711: int err;
1712:
1713: acl->sec_level = BT_SECURITY_LOW;
1714: acl->pending_sec_level = sec_level;
1715: acl->auth_type = auth_type;
1716: acl->conn_timeout = timeout;
1717:
1718: err = hci_connect_acl_sync(hdev, acl);
1719: if (err) {
1720: hci_conn_del(acl);
1721: return ERR_PTR(err);
1722: }
1723: }
1724:
1725: return acl;
1726: }
SourceFile:net/bluetooth/hci_conn.c]
|
| 52/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 60035
output: 45
thoughts: 33
Thoughts: Okay, here's my interpretation of those thoughts, presented as my own mental processing, as a seasoned expert:
**HCI Connection Head-Scratching**
Right, so `hci_connect_acl`... it seems I'm leaving a bit of an opening here. Specifically, I'm noting that there's no explicit check within that function to ensure the HCI device, `hdev`, is actually up and running before attempting a connection. That's a potential vulnerability; it could lead to unexpected behavior or errors if the device isn't properly initialized or powered on.
However, the next thought kicks in - perhaps this check is actually implicitly handled by its synchronous counterpart, `hci_connect_acl_sync`. That's a good possibility. Synchronization often brings in more robust error handling and pre-flight checks, which might be where this is accounted for. So, I need to investigate that function's implementation to confirm if it has that crucial up-state check or if it relies on a higher-level function to check the device state. I am curious about what assumptions are made when deciding which approach to take, and when a synchronous or asynchronous approach might be better. I'll need to go deeper to understand the complete picture.
|
| 53/2 |
2026/05/22 22:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c IncludeLines:true Name:hci_connect_acl_sync]
Results: map[SourceCode:6959: int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
6960: {
6961: return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
6962: NULL);
6963: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 54/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 60494
output: 19
thoughts: 366
Thoughts: **Analyzing Potential Race Conditions in Bluetooth HCI Command Handling**
Okay, so I'm looking at this `hci_cmd_sync_queue_once` function and how it interacts with device closure. It queues work on `hdev->cmd_work`. The problem is, `hdev->cmd_work` gets flushed in `hci_dev_close_sync`, which could create a race. If `hdev` is closing, `hci_cmd_sync_queue_once` might still attempt to queue work *after* the flush, which is bad news.
Now, `hci_connect_acl` seems to complicate things. It calls `hci_conn_add_unset`, which in turn calls `__hci_conn_add`. `__hci_conn_add` adds the connection to `conn_hash`. Then, `hci_connect_acl_sync` queues work. This looks like a potential point of contention. Similarly, `hci_connect_sco` uses `hci_conn_add_unset` for SCO connections.
My concern now is how `hdev->sco_pkts` is handled during closure. If the device is closed, is `hdev->sco_pkts` correctly cleared? It doesn't seem to be in `hci_dev_close_sync`. But, `hci_conn_add_unset` might still succeed in adding a SCO connection, and then `hci_connect_sco` returns `sco`, and `sco_connect` adds it to `sco_conn_add`.
Here's the kicker: even if `hci_dev_close_sync` completes, is the workqueue fully drained? I seem to recall that the `hdev->workqueue` might still be draining. I need to clarify that. `drain_workqueue` should finish when *all* work is done, right? But the question is: does `__WQ_DRAINING` get cleared after the drain is complete? This is a critical point that needs further scrutiny. I need to double-check the implementation of `drain_workqueue` in `kernel/workqueue.c` to confirm. It feels like `__WQ_DRAINING` *should* be cleared but I need to make sure. I'm going to look right now.
|
| 55/2 |
2026/05/22 22:55 |
tool |
grepper |
0m
Args: map[Expression:__WQ_DRAINING]
Results: map[Output:include/linux/workqueue.h=370=enum wq_flags {
--
include/linux/workqueue.h-407- __WQ_DESTROYING = 1 << 15, /* internal: workqueue is destroying */
include/linux/workqueue.h:408: __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */
include/linux/workqueue.h-409- __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */
--
kernel/workqueue.c=2248=static void __queue_work(int cpu, struct workqueue_struct *wq,
--
kernel/workqueue.c-2268- */
kernel/workqueue.c:2269: if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
kernel/workqueue.c-2270- WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n",
--
kernel/workqueue.c=4181=void drain_workqueue(struct workqueue_struct *wq)
--
kernel/workqueue.c-4188- * hotter than drain_workqueue() and already looks at @wq->flags.
kernel/workqueue.c:4189: * Use __WQ_DRAINING so that queue doesn't have to check nr_drainers.
kernel/workqueue.c-4190- */
--
kernel/workqueue.c-4192- if (!wq->nr_drainers++)
kernel/workqueue.c:4193: wq->flags |= __WQ_DRAINING;
kernel/workqueue.c-4194- mutex_unlock(&wq->mutex);
--
kernel/workqueue.c-4219- if (!--wq->nr_drainers)
kernel/workqueue.c:4220: wq->flags &= ~__WQ_DRAINING;
kernel/workqueue.c-4221- mutex_unlock(&wq->mutex);
]
|
| 56/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 61894
output: 19
thoughts: 415
Thoughts: **Analyzing the Concurrency Issue and Its Implications**
Okay, so `drain_workqueue` clears the `__WQ_DRAINING` flag at the very end. That's key. It means `__WQ_DRAINING` is only set while `drain_workqueue` itself is running. Aha! The warning is triggered because `queue_delayed_work` is called *while* `drain_workqueue` is active. This confirms that `sco_connect` is indeed running concurrently with `drain_workqueue`, just like I suspected. Thinking it through, `drain_workqueue` is called in `hci_dev_close_sync` without the protection of `hci_dev_lock(hdev)`, and `sco_connect` *does* hold `hci_dev_lock(hdev)`. Therefore, they can, and are, running concurrently.
Now, the real question: any security impact? The warning gets triggered, but the work isn't actually queued. The connection is dropped. And since `drain_workqueue` is running, `hci_dev_close_sync` must be in progress. After `drain_workqueue` finishes, `hci_dev_close_sync` will acquire `hci_dev_lock(hdev)` and call `hci_conn_hash_flush(hdev)`. That function will sweep the hash, removing all connections, including the one that was blocked from queuing its `disc_work`. No memory leak, that's good.
So, the *only* issue seems to be the `WARN_ONCE`. Thankfully, a `WARN_ONCE` isn't a kernel-crashing event in production builds. It doesn't lead to a denial of service or memory corruption. But... it is reachable from unprivileged processes if they can open `/dev/rfkill` and create Bluetooth sockets. That's the part that makes me think, can unprivileged processes even *open* `/dev/rfkill`? Let's check the permissions on `/dev/rfkill` to be sure. It's usually root-only or requires `CAP_NET_ADMIN`. Better double-check in `net/rfkill/core.c` just to be certain.
|
| 57/2 |
2026/05/22 22:55 |
tool |
grepper |
0m
Args: map[Expression:rfkill_fops]
Results: map[Output:net/rfkill/core.c=1346=static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
--
net/rfkill/core.c-1387-
net/rfkill/core.c:1388:static const struct file_operations rfkill_fops = {
net/rfkill/core.c-1389- .owner = THIS_MODULE,
--
net/rfkill/core.c=1401=static struct miscdevice rfkill_miscdev = {
net/rfkill/core.c:1402: .fops = &rfkill_fops,
net/rfkill/core.c-1403- .name = RFKILL_NAME,
]
|
| 58/2 |
2026/05/22 22:55 |
llm |
expert |
0m
Model:
Tokens:
input: 62998
output: 44
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in the weeds of kernel-level networking/device management:
**Diving into `rfkill_fop_open`**
Alright, so the focus is `rfkill_fop_open`. That immediately tells me we're looking at the file operations related to the `rfkill` interface, likely within the character device or sysfs side of things. I'm probably trying to understand how userspace interacts with the radio killswitch functionality. What's the entry point here? Are we dealing with a specific device file or some sort of attribute? I need to quickly recall the `rfkill` data structures and the overall design. Is it a module entry point, or is this related to a specific hardware device? My mental checklist is running: access control, device enumeration, potentially some locking to prevent race conditions. The goal is likely to see *how* the open operation is handled, which tells me the initial setup, initialization of internal structures, and any checks before allowing userspace to access the functionality. This should give me a good handle on its entry point.
|
| 59/2 |
2026/05/22 22:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rfkill/core.c IncludeLines:true Name:rfkill_fop_open]
Results: map[SourceCode:1162: static int rfkill_fop_open(struct inode *inode, struct file *file)
1163: {
1164: struct rfkill_data *data;
1165: struct rfkill *rfkill;
1166: struct rfkill_int_event *ev, *tmp;
1167:
1168: data = kzalloc(sizeof(*data), GFP_KERNEL);
1169: if (!data)
1170: return -ENOMEM;
1171:
1172: data->max_size = RFKILL_EVENT_SIZE_V1;
1173:
1174: INIT_LIST_HEAD(&data->events);
1175: mutex_init(&data->mtx);
1176: init_waitqueue_head(&data->read_wait);
1177:
1178: mutex_lock(&rfkill_global_mutex);
1179: /*
1180: * start getting events from elsewhere but hold mtx to get
1181: * startup events added first
1182: */
1183:
1184: list_for_each_entry(rfkill, &rfkill_list, node) {
1185: ev = kzalloc(sizeof(*ev), GFP_KERNEL);
1186: if (!ev)
1187: goto free;
1188: rfkill_sync(rfkill);
1189: rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD);
1190: mutex_lock(&data->mtx);
1191: list_add_tail(&ev->list, &data->events);
1192: mutex_unlock(&data->mtx);
1193: }
1194: list_add(&data->list, &rfkill_fds);
1195: mutex_unlock(&rfkill_global_mutex);
1196:
1197: file->private_data = data;
1198:
1199: return stream_open(inode, file);
1200:
1201: free:
1202: mutex_unlock(&rfkill_global_mutex);
1203: mutex_destroy(&data->mtx);
1204: list_for_each_entry_safe(ev, tmp, &data->events, list)
1205: kfree(ev);
1206: kfree(data);
1207: return -ENOMEM;
1208: }
SourceFile:net/rfkill/core.c]
|
| 60/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 64010
output: 44
thoughts: 29
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating RFKill and Potential Security Holes**
Hmm, so I'm looking at the RFKill driver's file operations, specifically the `rfkill_fop_open` function. And immediately, something jumps out at me: there's *no* access control or capability check whatsoever in this `rfkill_fop_open` function. That's...unusual, and potentially a very significant oversight. We're essentially granting open access without restriction to this interface.
But that's just the open call. My mind immediately shifts to the next logical point of attack. What about the write operations? I need to quickly pivot and consider `rfkill_fop_write`. Does *that* function perform any capability checks or access validations before allowing writes? This is where the potential security implications really lie. If `rfkill_fop_write` *doesn't* adequately vet the user or process attempting to write, then the lack of checking on the `open` is a huge problem. It could open the door to all sorts of unwanted behavior and possibly privilege escalation if the data written has unexpected side-effects. This is where I'll be focusing my attention next.
|
| 61/2 |
2026/05/22 22:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rfkill/core.c IncludeLines:true Name:rfkill_fop_write]
Results: map[SourceCode:1268: static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
1269: size_t count, loff_t *pos)
1270: {
1271: struct rfkill_data *data = file->private_data;
1272: struct rfkill *rfkill;
1273: struct rfkill_event_ext ev;
1274: int ret;
1275:
1276: /* we don't need the 'hard' variable but accept it */
1277: if (count < RFKILL_EVENT_SIZE_V1 - 1)
1278: return -EINVAL;
1279:
1280: /*
1281: * Copy as much data as we can accept into our 'ev' buffer,
1282: * but tell userspace how much we've copied so it can determine
1283: * our API version even in a write() call, if it cares.
1284: */
1285: count = min(count, sizeof(ev));
1286: count = min_t(size_t, count, data->max_size);
1287: if (copy_from_user(&ev, buf, count))
1288: return -EFAULT;
1289:
1290: if (ev.type >= NUM_RFKILL_TYPES)
1291: return -EINVAL;
1292:
1293: mutex_lock(&rfkill_global_mutex);
1294:
1295: switch (ev.op) {
1296: case RFKILL_OP_CHANGE_ALL:
1297: rfkill_update_global_state(ev.type, ev.soft);
1298: list_for_each_entry(rfkill, &rfkill_list, node)
1299: if (rfkill->type == ev.type ||
1300: ev.type == RFKILL_TYPE_ALL)
1301: rfkill_set_block(rfkill, ev.soft);
1302: ret = 0;
1303: break;
1304: case RFKILL_OP_CHANGE:
1305: list_for_each_entry(rfkill, &rfkill_list, node)
1306: if (rfkill->idx == ev.idx &&
1307: (rfkill->type == ev.type ||
1308: ev.type == RFKILL_TYPE_ALL))
1309: rfkill_set_block(rfkill, ev.soft);
1310: ret = 0;
1311: break;
1312: default:
1313: ret = -EINVAL;
1314: break;
1315: }
1316:
1317: mutex_unlock(&rfkill_global_mutex);
1318:
1319: return ret ?: count;
1320: }
SourceFile:net/rfkill/core.c]
|
| 62/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 65194
output: 43
thoughts: 213
Thoughts: **Analyzing the Rfkill Vulnerability**
Okay, so I'm diving into this `rfkill_fop_write` function, and it's looking like there's no access control check in there. That's a potential weak point, but let's not jump to conclusions. First, I need to understand the environment. Normally, `/dev/rfkill` has permissions like `crw-rw-r-- 1 root root` or `crw-rw-r-- 1 root netdev`. Hmm, but this syzkaller reproducer is running either as root or within a namespace. Does the namespace even matter for `/dev/rfkill`?
Ah, right, it's a *misc* device. It's not namespaced. So, that means regardless of the namespace, if the reproducer can access the device, it can cause an issue. However, syzkaller might run with root privileges in the initial namespace, which means access to the device is guaranteed. So, a regular user *without* write access to `/dev/rfkill` wouldn't be able to trigger this.
Still, the bug is just a `WARN_ONCE`, not a crash. It's not a major security issue, but let's see if there is another way to trigger `hci_dev_close_sync` that might be more vulnerable. Okay, what about the `HCIDEVDOWN` ioctl? That might be another avenue to explore.
|
| 63/2 |
2026/05/22 22:56 |
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]
|
| 64/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 67522
output: 43
thoughts: 1076
Thoughts: **My Assessment of the Bluetooth HCI Device Close Warning**
Okay, let's break this down. My initial thought was centered around unprivileged user access and whether they could trigger `HCIDEVDOWN`. I quickly realized `HCIDEVDOWN` needs `CAP_NET_ADMIN` in the initial namespace due to its use of `capable()`, making it a no-go for unprivileged users right off the bat. Then I explored `/dev/rfkill`, but its standard permissions (root ownership, read access typically) also block unprivileged use, even though there might be a few systems with `netdev` access. So far, no dice.
I started digging for other ways to call `hci_dev_do_close` besides ioctls. That's when `hci_unregister_dev` popped into my head, and the peripheral trigger from disconnecting a USB Bluetooth dongle. Bingo! This means even if a user is locked out from everything else, unplugging the hardware is still a vector.
Now, to the actual bug. It seems that `hci_dev_close_sync` calls `drain_workqueue` without the necessary lock, a classic concurrency issue. This causes a race condition where `sco_connect` holds the lock, calls `hci_connect_sco`, and attempts to add an ACL connection. The connection addition fails, resulting in a dropped connection, which is then queued on a workqueue that's actively draining. This triggers the `WARN_ONCE`, because the work item ends up not being queued due to the `__WQ_DRAINING` flag. The closing thread grabs the lock and calls `hci_conn_hash_flush`, which cleans up all connections, including the one that was supposed to be reaped on the workqueue, but wasn't.
My conclusion is it's just a warning, which isn't a denial of service on production builds unless `panic_on_warn` is set, but that's not the default. It's not exploitable because there's no memory corruption or use-after-free situation. It can be triggered by either a physical unplug (peripheral trigger), root-level commands like `HCIDEVDOWN` or `/dev/rfkill`, or by a system suspend (though that doesn't feel especially relevant to the immediate security profile).
I immediately dismissed user namespaces and quickly confirmed, `HCIDEVDOWN` and device nodes have the permission restrictions as expected. It's a non-issue there.
I did a final check:
- `hci_rfkill_set_block`
- `hci_dev_close` (ioctl `HCIDEVDOWN`, `mgmt_set_powered`)
- `hci_unregister_dev` (device removal)
- `hci_suspend_dev` (system suspend)
- `hci_error_reset` (hardware error)
`hci_error_reset` is impossible as unprivileged users don't have access to HCI events, and the Management API (`mgmt_set_powered`) also requires `CAP_NET_ADMIN`.
|
| 65/2 |
2026/05/22 22:56 |
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_unsized *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: hci_dev_lock(hdev);
1315: mgmt_index_removed(hdev);
1316: hci_dev_unlock(hdev);
1317:
1318: err = hci_dev_open(hdev->id);
1319: if (err) {
1320: if (err == -EALREADY) {
1321: /* In case the transport is already up and
1322: * running, clear the error here.
1323: *
1324: * This can happen when opening a user
1325: * channel and HCI_AUTO_OFF grace period
1326: * is still active.
1327: */
1328: err = 0;
1329: } else {
1330: hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
1331: mgmt_index_added(hdev);
1332: hci_dev_put(hdev);
1333: goto done;
1334: }
1335: }
1336:
1337: hci_pi(sk)->channel = haddr.hci_channel;
1338:
1339: if (!hci_sock_gen_cookie(sk)) {
1340: /* In the case when a cookie has already been assigned,
1341: * this socket will transition from a raw socket into
1342: * a user channel socket. For a clean transition, send
1343: * the close notification first.
1344: */
1345: skb = create_monitor_ctrl_close(sk);
1346: if (skb) {
1347: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1348: HCI_SOCK_TRUSTED, NULL);
1349: kfree_skb(skb);
1350: }
1351: }
1352:
1353: /* The user channel is restricted to CAP_NET_ADMIN
1354: * capabilities and with that implicitly trusted.
1355: */
1356: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1357:
1358: hci_pi(sk)->hdev = hdev;
1359:
1360: /* Send event to monitor */
1361: skb = create_monitor_ctrl_open(sk);
1362: if (skb) {
1363: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1364: HCI_SOCK_TRUSTED, NULL);
1365: kfree_skb(skb);
1366: }
1367:
1368: atomic_inc(&hdev->promisc);
1369: break;
1370:
1371: case HCI_CHANNEL_MONITOR:
1372: if (haddr.hci_dev != HCI_DEV_NONE) {
1373: err = -EINVAL;
1374: goto done;
1375: }
1376:
1377: if (!capable(CAP_NET_RAW)) {
1378: err = -EPERM;
1379: goto done;
1380: }
1381:
1382: hci_pi(sk)->channel = haddr.hci_channel;
1383:
1384: /* The monitor interface is restricted to CAP_NET_RAW
1385: * capabilities and with that implicitly trusted.
1386: */
1387: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1388:
1389: send_monitor_note(sk, "Linux version %s (%s)",
1390: init_utsname()->release,
1391: init_utsname()->machine);
1392: send_monitor_note(sk, "Bluetooth subsystem version %u.%u",
1393: BT_SUBSYS_VERSION, BT_SUBSYS_REVISION);
1394: send_monitor_replay(sk);
1395: send_monitor_control_replay(sk);
1396:
1397: atomic_inc(&monitor_promisc);
1398: break;
1399:
1400: case HCI_CHANNEL_LOGGING:
1401: if (haddr.hci_dev != HCI_DEV_NONE) {
1402: err = -EINVAL;
1403: goto done;
1404: }
1405:
1406: if (!capable(CAP_NET_ADMIN)) {
1407: err = -EPERM;
1408: goto done;
1409: }
1410:
1411: hci_pi(sk)->channel = haddr.hci_channel;
1412: break;
1413:
1414: default:
1415: if (!hci_mgmt_chan_find(haddr.hci_channel)) {
1416: err = -EINVAL;
1417: goto done;
1418: }
1419:
1420: if (haddr.hci_dev != HCI_DEV_NONE) {
1421: err = -EINVAL;
1422: goto done;
1423: }
1424:
1425: /* Users with CAP_NET_ADMIN capabilities are allowed
1426: * access to all management commands and events. For
1427: * untrusted users the interface is restricted and
1428: * also only untrusted events are sent.
1429: */
1430: if (capable(CAP_NET_ADMIN))
1431: hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
1432:
1433: hci_pi(sk)->channel = haddr.hci_channel;
1434:
1435: /* At the moment the index and unconfigured index events
1436: * are enabled unconditionally. Setting them on each
1437: * socket when binding keeps this functionality. They
1438: * however might be cleared later and then sending of these
1439: * events will be disabled, but that is then intentional.
1440: *
1441: * This also enables generic events that are safe to be
1442: * received by untrusted users. Example for such events
1443: * are changes to settings, class of device, name etc.
1444: */
1445: if (hci_pi(sk)->channel == HCI_CHANNEL_CONTROL) {
1446: if (!hci_sock_gen_cookie(sk)) {
1447: /* In the case when a cookie has already been
1448: * assigned, this socket will transition from
1449: * a raw socket into a control socket. To
1450: * allow for a clean transition, send the
1451: * close notification first.
1452: */
1453: skb = create_monitor_ctrl_close(sk);
1454: if (skb) {
1455: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1456: HCI_SOCK_TRUSTED, NULL);
1457: kfree_skb(skb);
1458: }
1459: }
1460:
1461: /* Send event to monitor */
1462: skb = create_monitor_ctrl_open(sk);
1463: if (skb) {
1464: hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
1465: HCI_SOCK_TRUSTED, NULL);
1466: kfree_skb(skb);
1467: }
1468:
1469: hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS);
1470: hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
1471: hci_sock_set_flag(sk, HCI_MGMT_OPTION_EVENTS);
1472: hci_sock_set_flag(sk, HCI_MGMT_SETTING_EVENTS);
1473: hci_sock_set_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS);
1474: hci_sock_set_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS);
1475: }
1476: break;
1477: }
1478:
1479: /* Default MTU to HCI_MAX_FRAME_SIZE if not set */
1480: if (!hci_pi(sk)->mtu)
1481: hci_pi(sk)->mtu = HCI_MAX_FRAME_SIZE;
1482:
1483: sk->sk_state = BT_BOUND;
1484:
1485: done:
1486: release_sock(sk);
1487: return err;
1488: }
SourceFile:net/bluetooth/hci_sock.c]
|
| 66/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 73633
output: 39
thoughts: 86
Thoughts: Here's my analysis of this code snippet:
**HCI Channel Security Deep Dive**
Okay, so I'm digging into this `HCI_CHANNEL_CONTROL` situation. My initial thought was that binding to that channel required `CAP_NET_ADMIN`. But now I'm seeing something that contradicts that. Hang on, the code *does* check if `capable(CAP_NET_ADMIN)` is set, and if it is, `HCI_SOCK_TRUSTED` gets flagged. That's the first red flag...it implies a security consideration. But here's the kicker: an untrusted user *can* bind to `HCI_CHANNEL_CONTROL`! This is where things get interesting and where my expert brain kicks in, seeing a potential vulnerability. My next question is, can that untrusted user then leverage that binding to execute privileged commands? Can they, for instance, issue a `mgmt_set_powered` command? That's the key question now. I need to understand the implications of this untrusted binding and how far it extends into the control plane. This is potentially a serious security flaw that needs further investigation.
|
| 67/2 |
2026/05/22 22:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/mgmt.c IncludeLines:true Name:mgmt_handlers]
Results: map[SourceCode:9336: static const struct hci_mgmt_handler mgmt_handlers[] = {
9337: { NULL }, /* 0x0000 (no command) */
9338: { read_version, MGMT_READ_VERSION_SIZE,
9339: HCI_MGMT_NO_HDEV |
9340: HCI_MGMT_UNTRUSTED },
9341: { read_commands, MGMT_READ_COMMANDS_SIZE,
9342: HCI_MGMT_NO_HDEV |
9343: HCI_MGMT_UNTRUSTED },
9344: { read_index_list, MGMT_READ_INDEX_LIST_SIZE,
9345: HCI_MGMT_NO_HDEV |
9346: HCI_MGMT_UNTRUSTED },
9347: { read_controller_info, MGMT_READ_INFO_SIZE,
9348: HCI_MGMT_UNTRUSTED },
9349: { set_powered, MGMT_SETTING_SIZE },
9350: { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE },
9351: { set_connectable, MGMT_SETTING_SIZE },
9352: { set_fast_connectable, MGMT_SETTING_SIZE },
9353: { set_bondable, MGMT_SETTING_SIZE },
9354: { set_link_security, MGMT_SETTING_SIZE },
9355: { set_ssp, MGMT_SETTING_SIZE },
9356: { set_hs, MGMT_SETTING_SIZE },
9357: { set_le, MGMT_SETTING_SIZE },
9358: { set_dev_class, MGMT_SET_DEV_CLASS_SIZE },
9359: { set_local_name, MGMT_SET_LOCAL_NAME_SIZE },
9360: { add_uuid, MGMT_ADD_UUID_SIZE },
9361: { remove_uuid, MGMT_REMOVE_UUID_SIZE },
9362: { load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE,
9363: HCI_MGMT_VAR_LEN },
9364: { load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE,
9365: HCI_MGMT_VAR_LEN },
9366: { disconnect, MGMT_DISCONNECT_SIZE },
9367: { get_connections, MGMT_GET_CONNECTIONS_SIZE },
9368: { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE },
9369: { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE },
9370: { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE },
9371: { pair_device, MGMT_PAIR_DEVICE_SIZE },
9372: { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE },
9373: { unpair_device, MGMT_UNPAIR_DEVICE_SIZE },
9374: { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE },
9375: { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
9376: { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE },
9377: { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
9378: { read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE },
9379: { add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE,
9380: HCI_MGMT_VAR_LEN },
9381: { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
9382: { start_discovery, MGMT_START_DISCOVERY_SIZE },
9383: { stop_discovery, MGMT_STOP_DISCOVERY_SIZE },
9384: { confirm_name, MGMT_CONFIRM_NAME_SIZE },
9385: { block_device, MGMT_BLOCK_DEVICE_SIZE },
9386: { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE },
9387: { set_device_id, MGMT_SET_DEVICE_ID_SIZE },
9388: { set_advertising, MGMT_SETTING_SIZE },
9389: { set_bredr, MGMT_SETTING_SIZE },
9390: { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE },
9391: { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE },
9392: { set_secure_conn, MGMT_SETTING_SIZE },
9393: { set_debug_keys, MGMT_SETTING_SIZE },
9394: { set_privacy, MGMT_SET_PRIVACY_SIZE },
9395: { load_irks, MGMT_LOAD_IRKS_SIZE,
9396: HCI_MGMT_VAR_LEN },
9397: { get_conn_info, MGMT_GET_CONN_INFO_SIZE },
9398: { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE },
9399: { add_device, MGMT_ADD_DEVICE_SIZE },
9400: { remove_device, MGMT_REMOVE_DEVICE_SIZE },
9401: { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE,
9402: HCI_MGMT_VAR_LEN },
9403: { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE,
9404: HCI_MGMT_NO_HDEV |
9405: HCI_MGMT_UNTRUSTED },
9406: { read_config_info, MGMT_READ_CONFIG_INFO_SIZE,
9407: HCI_MGMT_UNCONFIGURED |
9408: HCI_MGMT_UNTRUSTED },
9409: { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE,
9410: HCI_MGMT_UNCONFIGURED },
9411: { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE,
9412: HCI_MGMT_UNCONFIGURED },
9413: { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE,
9414: HCI_MGMT_VAR_LEN },
9415: { read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE },
9416: { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE,
9417: HCI_MGMT_NO_HDEV |
9418: HCI_MGMT_UNTRUSTED },
9419: { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE },
9420: { add_advertising, MGMT_ADD_ADVERTISING_SIZE,
9421: HCI_MGMT_VAR_LEN },
9422: { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
9423: { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
9424: { start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
9425: { read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE,
9426: HCI_MGMT_UNTRUSTED },
9427: { set_appearance, MGMT_SET_APPEARANCE_SIZE },
9428: { get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE },
9429: { set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE },
9430: { set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
9431: HCI_MGMT_VAR_LEN },
9432: { set_wideband_speech, MGMT_SETTING_SIZE },
9433: { read_controller_cap, MGMT_READ_CONTROLLER_CAP_SIZE,
9434: HCI_MGMT_UNTRUSTED },
9435: { read_exp_features_info, MGMT_READ_EXP_FEATURES_INFO_SIZE,
9436: HCI_MGMT_UNTRUSTED |
9437: HCI_MGMT_HDEV_OPTIONAL },
9438: { set_exp_feature, MGMT_SET_EXP_FEATURE_SIZE,
9439: HCI_MGMT_VAR_LEN |
9440: HCI_MGMT_HDEV_OPTIONAL },
9441: { read_def_system_config, MGMT_READ_DEF_SYSTEM_CONFIG_SIZE,
9442: HCI_MGMT_UNTRUSTED },
9443: { set_def_system_config, MGMT_SET_DEF_SYSTEM_CONFIG_SIZE,
9444: HCI_MGMT_VAR_LEN },
9445: { read_def_runtime_config, MGMT_READ_DEF_RUNTIME_CONFIG_SIZE,
9446: HCI_MGMT_UNTRUSTED },
9447: { set_def_runtime_config, MGMT_SET_DEF_RUNTIME_CONFIG_SIZE,
9448: HCI_MGMT_VAR_LEN },
9449: { get_device_flags, MGMT_GET_DEVICE_FLAGS_SIZE },
9450: { set_device_flags, MGMT_SET_DEVICE_FLAGS_SIZE },
9451: { read_adv_mon_features, MGMT_READ_ADV_MONITOR_FEATURES_SIZE },
9452: { add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE,
9453: HCI_MGMT_VAR_LEN },
9454: { remove_adv_monitor, MGMT_REMOVE_ADV_MONITOR_SIZE },
9455: { add_ext_adv_params, MGMT_ADD_EXT_ADV_PARAMS_MIN_SIZE,
9456: HCI_MGMT_VAR_LEN },
9457: { add_ext_adv_data, MGMT_ADD_EXT_ADV_DATA_SIZE,
9458: HCI_MGMT_VAR_LEN },
9459: { add_adv_patterns_monitor_rssi,
9460: MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE,
9461: HCI_MGMT_VAR_LEN },
9462: { set_mesh, MGMT_SET_MESH_RECEIVER_SIZE,
9463: HCI_MGMT_VAR_LEN },
9464: { mesh_features, MGMT_MESH_READ_FEATURES_SIZE },
9465: { mesh_send, MGMT_MESH_SEND_SIZE,
9466: HCI_MGMT_VAR_LEN },
9467: { mesh_send_cancel, MGMT_MESH_SEND_CANCEL_SIZE },
9468: { mgmt_hci_cmd_sync, MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN },
9469: };
SourceFile:net/bluetooth/mgmt.c]
|
| 68/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 77021
output: 47
thoughts: 1537
Thoughts: **Analyzing the Bluetooth HCI Device Closure and Potential Race Condition**
Okay, let's break this down step-by-step. The `set_powered` command doesn't have `HCI_MGMT_UNTRUSTED` so it requires `HCI_SOCK_TRUSTED`. That means `CAP_NET_ADMIN` is needed to set `HCI_SOCK_TRUSTED` which prevents unprivileged users from sending `set_powered`. So, no unprivileged user can trigger `hci_dev_do_close` via the Management API. This bug is NOT accessible to unprivileged processes, and it's NOT exploitable from user namespaces either (because it relies on `CAP_NET_ADMIN`, not `ns_capable`). It *can* be triggered by a peripheral disconnect, but it doesn't cause a denial of service (it's just a `WARN_ONCE`). More precisely, `WARN_ONCE` is not a DoS unless `panic_on_warn` is set, which is not the default. The warning in the report is standard.
But I need to go deeper to verify no other issue can occur. Let's look at the consequences of failing `queue_delayed_work`. If it fails, `conn->disc_work` isn't queued, and the connection isn't aborted by `hci_conn_timeout`. However, as I saw before, `hci_conn_hash_flush` still deletes the connection eventually.
Wait a second, what if `hci_conn_hash_flush` has already run? If that's the case, `sco_connect` shouldn't be able to run because `hci_dev_close_sync` holds `hci_dev_lock(hdev)` while calling `hci_conn_hash_flush`. However, line 5361 of `hci_dev_close_sync` releases that lock before going on.
So if `sco_connect` runs after the lock is released. Let's trace further what happens.
If `sco_connect` runs AFTER `hci_dev_close_sync` releases the lock, and if `drain_workqueue` has already finished, `__WQ_DRAINING` is cleared! This means `queue_delayed_work` will actually SUCCEED, and `disc_work` will be queued. But wait, the device is closed, what happens here? Is `hdev->workqueue` destroyed? No, it isn't. It's destroyed in `hci_release_dev`, which is called when the *last* reference to `hdev` is dropped. Since `sco_connect` holds a reference to `hdev` (via `hci_get_route`), `hdev` isn't freed. So `hdev->workqueue` still exists!
If `disc_work` runs, it calls `hci_conn_timeout` which calls `hci_abort_conn`. This then queues `cmd_work` on `hdev->req_workqueue`. Since `hdev` is closed, this might fail or do nothing, correct?
But wait! If `sco_connect` runs AFTER `hci_dev_close_sync`, `hci_connect_acl` will add the connection to `hdev->conn_hash`! And since `hci_conn_hash_flush` has already run, this connection remains in `hdev->conn_hash` until the device is opened or freed!
But hold on! If `hdev` is closed, `hci_connect_acl` should be failing! Let me double-check `hci_connect_acl`. Okay, I see it checks `!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)`. When `hdev` is closed, is `HCI_BREDR_ENABLED` cleared? I need to check `hci_dev_clear_volatile_flags`.
|
| 69/2 |
2026/05/22 22:56 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_dev_clear_volatile_flags]
Results: map[SourceCode: SourceFile:]
|
| 70/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 79465
output: 23
thoughts: 16
Thoughts: Here's my thought process, as if I were considering this command:
**Digging into Bluetooth HCI Volatile Flags**
Okay, so I'm thinking about `hci_dev_clear_volatile_flags`. This function call immediately suggests a function that is intended to manipulate and/or clear volatile flags associated with a specific HCI (Host Controller Interface) device. In my experience, these flags are often used for temporary states, configurations, or operational parameters related to the Bluetooth controller. The term "volatile" implies that the flags are likely cleared or reset under certain conditions (like device power cycle) or by the operating system itself without direct user input. It's a clue that the information it's storing isn't meant to persist in the long term.
My next line of thinking would immediately go to: what *kinds* of flags are we talking about? Security considerations, connection state, error conditions, configuration bits... the possibilities are vast within the Bluetooth protocol stack. I'd want to know specifically what flags are considered "volatile" by this function and the system using it. To effectively understand how this function works, I need to understand what state it's setting, resetting, or altering, and what other functions/systems interact with these flags. Without seeing more context, it's hard to be sure.
My mind immediately turns to the potential impact. If this function is poorly implemented, it might lead to unintended behavior, unexpected disconnections, or a disruption in Bluetooth functionality. I'll need to research the surrounding code to determine how it is utilized and where it is called. That sounds like a good plan: I should start grepping for `hci_dev_clear_volatile_flags` throughout the Bluetooth stack and see where it's being used to paint a complete picture of its purpose and implications within the system.
|
| 71/2 |
2026/05/22 22:56 |
tool |
grepper |
0m
Args: map[Expression:hci_dev_clear_volatile_flags]
Results: map[Output:include/net/bluetooth/hci_core.h=835=extern struct mutex hci_cb_list_lock;
--
include/net/bluetooth/hci_core.h-844-
include/net/bluetooth/hci_core.h:845:#define hci_dev_clear_volatile_flags(hdev) \
include/net/bluetooth/hci_core.h-846- do { \
--
net/bluetooth/hci_event.c=267=static u8 hci_cc_reset(struct hci_dev *hdev, void *data, struct sk_buff *skb)
--
net/bluetooth/hci_event.c-278- /* Reset all non-persistent flags */
net/bluetooth/hci_event.c:279: hci_dev_clear_volatile_flags(hdev);
net/bluetooth/hci_event.c-280-
--
net/bluetooth/hci_sync.c=5284=int hci_dev_close_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5411- hdev->flags &= BIT(HCI_RAW);
net/bluetooth/hci_sync.c:5412: hci_dev_clear_volatile_flags(hdev);
net/bluetooth/hci_sync.c-5413-
]
|
| 72/2 |
2026/05/22 22:56 |
llm |
expert |
0m
Model:
Tokens:
input: 80225
output: 49
thoughts: 18
Thoughts: Here's a summary of my thought process, as requested:
**Deconstructing `hci_dev_clear_volatile_flags`**
Alright, let's dive into this. The function name, `hci_dev_clear_volatile_flags`, immediately suggests its purpose: to reset or modify certain flags associated with a HCI (Host Controller Interface) device. Given my background, I'm already anticipating this is likely related to the internal workings of the Bluetooth stack within an operating system or firmware. The "volatile" part is key. It signals these flags aren't permanent, meaning they represent transient or runtime states. My immediate question is, *which* flags? What specific conditions or behaviors are being modified? Are these hardware-related, software-related, or both?
I'll be looking for documentation, code comments, or any definitions associated with this function to get a full picture. I expect it's part of a broader set of management functions for a Bluetooth device. It's likely involved in error handling, resource management, or maybe even power state transitions. Understanding the specific flags that are being cleared will be critical to grasping the function's complete function. The context of its use will also be relevant. I need to figure out where it's called and under what conditions. What might trigger the clearing of these volatile flags?
|
| 73/2 |
2026/05/22 22:57 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:include/net/bluetooth/hci_core.h IncludeLines:true Name:hci_dev_clear_volatile_flags]
Results: map[SourceCode: SourceFile:]
|
| 74/2 |
2026/05/22 22:57 |
llm |
expert |
0m
Model:
Tokens:
input: 80592
output: 25
thoughts: 13
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Code Snippet**
Alright, so the first thing that jumps out is that we're dealing with a macro. The immediate task is to get a solid understanding of its functionality. My first instinct, without any context, is to dive right into the file itself. I need to see the actual code, the definitions, the logic. Assumptions are the enemy here, and only a direct examination will give me the insights required to deconstruct its behavior and its potential impact within the larger system. Time to crack it open and get to the core of this macro.
|
| 75/2 |
2026/05/22 22:57 |
tool |
grepper |
0m
Args: map[Expression:#define hci_dev_clear_volatile_flags]
Results: map[Output:include/net/bluetooth/hci_core.h=835=extern struct mutex hci_cb_list_lock;
--
include/net/bluetooth/hci_core.h-844-
include/net/bluetooth/hci_core.h:845:#define hci_dev_clear_volatile_flags(hdev) \
include/net/bluetooth/hci_core.h-846- do { \
]
|
| 76/2 |
2026/05/22 22:57 |
llm |
expert |
0m
Model:
Tokens:
input: 80879
output: 26
thoughts: 27
Thoughts: Here's my summary, as though I'm reading and processing the provided lines of code:
**Diving into `hci_core.h` - Lines 845-860**
Okay, let's zero in on lines 845 through 860 of `include/net/bluetooth/hci_core.h`. Right, I'm expecting this section to deal with some core HCI functionality, likely definitions or possibly data structures that are central to the Bluetooth Host Controller Interface. Given the file name, it's pretty much a guarantee. I'm hoping I can glean something specific about how the driver interacts with the hardware, the way commands are formatted, or some details about event handling. I'm mentally parsing this as I'm thinking... probably something related to HCI event handling or command construction. Let's dig in and see what goodies are there.
|
| 77/2 |
2026/05/22 22:57 |
tool |
grepper |
0m
Args: map[Expression:#define hci_dev_clear_volatile_flags.*]
Results: map[Output:include/net/bluetooth/hci_core.h=835=extern struct mutex hci_cb_list_lock;
--
include/net/bluetooth/hci_core.h-844-
include/net/bluetooth/hci_core.h:845:#define hci_dev_clear_volatile_flags(hdev) \
include/net/bluetooth/hci_core.h-846- do { \
]
|
| 78/2 |
2026/05/22 22:57 |
llm |
expert |
1m
Model:
Tokens:
input: 81235
output: 24
thoughts: 25
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Volatile Flags in HCI Devices**
Alright, let's dive in. I need to understand how the volatile flags are managed within the HCI device context. My initial instinct is to use `git grep` to pinpoint where `define hci_dev_clear_volatile_flags` is actually used. Specifically, I'll search for the definition itself, along with a bit of surrounding context to see the bigger picture. I'm choosing `-A 20` to give me 20 lines of "after" context, that should be enough to see function calls, variable assignments, and conditional logic surrounding the definition. This approach should quickly reveal the key locations where these volatile flags are being set, cleared, and checked. From there, I can start forming a more concrete picture of how this mechanism fits into the broader HCI device architecture and any potential areas of concern. Hopefully, this should lead me down the right path!
|
| 79/2 |
2026/05/22 22:58 |
tool |
grepper |
0m
Args: map[Expression:define hci_dev_clear_volatile_flags]
Results: map[Output:include/net/bluetooth/hci_core.h=835=extern struct mutex hci_cb_list_lock;
--
include/net/bluetooth/hci_core.h-844-
include/net/bluetooth/hci_core.h:845:#define hci_dev_clear_volatile_flags(hdev) \
include/net/bluetooth/hci_core.h-846- do { \
]
|
| 80/2 |
2026/05/22 22:58 |
llm |
expert |
1m
Model:
Tokens:
input: 81608
output: 26
thoughts: 7
Thoughts: Okay, I'm ready to dive into this. Here's how I'd approach summarizing the "thoughts" scenario, keeping in mind I'm speaking as an expert:
**Reading the File: A Quick Assessment**
Alright, the instruction says "Let's read the file." Immediately, my mind starts assessing the context. What kind of file are we talking about? Is it a script, code, data, a research paper, a document? The nature of the file dictates my approach. If it's code, I'll be looking for structure, logic, and potential areas for analysis. Data? I'm focusing on the format, potential biases, and how I can extract meaningful insights. Document? I'll be looking for the central argument or theme.
This first step is crucial. "Let's read the file" isn't just an instruction; it's a launchpad for a focused analysis. I need to quickly identify the file's type and purpose. With that understanding, I can begin to unpack the specific information, arguments, or data points presented.
|
| 81/2 |
2026/05/22 22:59 |
tool |
read-file |
0m
Args: map[File:include/net/bluetooth/hci_core.h]
Results: map[Contents:/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
Copyright 2023-2024 NXP
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#ifndef __HCI_CORE_H
#define __HCI_CORE_H
#include <linux/idr.h>
#include <linux/leds.h>
#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_drv.h>
#include <net/bluetooth/hci_sync.h>
#include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/coredump.h>
/* HCI priority */
#define HCI_PRIO_MAX 7
/* HCI maximum id value */
#define HCI_MAX_ID 10000
/* HCI Core structures */
struct inquiry_data {
bdaddr_t bdaddr;
__u8 pscan_rep_mode;
__u8 pscan_period_mode;
__u8 pscan_mode;
__u8 dev_class[3];
__le16 clock_offset;
__s8 rssi;
__u8 ssp_mode;
};
struct inquiry_entry {
struct list_head all; /* inq_cache.all */
struct list_head list; /* unknown or resolve */
enum {
NAME_NOT_KNOWN,
NAME_NEEDED,
NAME_PENDING,
NAME_KNOWN,
} name_state;
__u32 timestamp;
struct inquiry_data data;
};
struct discovery_state {
int type;
enum {
DISCOVERY_STOPPED,
DISCOVERY_STARTING,
DISCOVERY_FINDING,
DISCOVERY_RESOLVING,
DISCOVERY_STOPPING,
} state;
struct list_head all; /* All devices found during inquiry */
struct list_head unknown; /* Name state not known */
struct list_head resolve; /* Name needs to be resolved */
__u32 timestamp;
bdaddr_t last_adv_addr;
u8 last_adv_addr_type;
s8 last_adv_rssi;
u32 last_adv_flags;
u8 last_adv_data[HCI_MAX_EXT_AD_LENGTH];
u8 last_adv_data_len;
bool report_invalid_rssi;
bool result_filtering;
bool limited;
s8 rssi;
u16 uuid_count;
u8 (*uuids)[16];
unsigned long name_resolve_timeout;
spinlock_t lock;
};
#define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
enum suspend_tasks {
SUSPEND_PAUSE_DISCOVERY,
SUSPEND_UNPAUSE_DISCOVERY,
SUSPEND_PAUSE_ADVERTISING,
SUSPEND_UNPAUSE_ADVERTISING,
SUSPEND_SCAN_DISABLE,
SUSPEND_SCAN_ENABLE,
SUSPEND_DISCONNECTING,
SUSPEND_POWERING_DOWN,
SUSPEND_PREPARE_NOTIFIER,
SUSPEND_SET_ADV_FILTER,
__SUSPEND_NUM_TASKS
};
enum suspended_state {
BT_RUNNING = 0,
BT_SUSPEND_DISCONNECT,
BT_SUSPEND_CONFIGURE_WAKE,
};
struct hci_conn_hash {
struct list_head list;
unsigned int acl_num;
unsigned int sco_num;
unsigned int cis_num;
unsigned int bis_num;
unsigned int pa_num;
unsigned int le_num;
unsigned int le_num_peripheral;
};
struct bdaddr_list {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
};
struct codec_list {
struct list_head list;
u8 id;
__u16 cid;
__u16 vid;
u8 transport;
u8 num_caps;
u32 len;
struct hci_codec_caps caps[];
};
struct bdaddr_list_with_irk {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
u8 peer_irk[16];
u8 local_irk[16];
};
/* Bitmask of connection flags */
enum hci_conn_flags {
HCI_CONN_FLAG_REMOTE_WAKEUP = BIT(0),
HCI_CONN_FLAG_DEVICE_PRIVACY = BIT(1),
HCI_CONN_FLAG_ADDRESS_RESOLUTION = BIT(2),
HCI_CONN_FLAG_PAST = BIT(3),
};
typedef u8 hci_conn_flags_t;
struct bdaddr_list_with_flags {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
hci_conn_flags_t flags;
};
struct bt_uuid {
struct list_head list;
u8 uuid[16];
u8 size;
u8 svc_hint;
};
struct blocked_key {
struct list_head list;
struct rcu_head rcu;
u8 type;
u8 val[16];
};
struct smp_csrk {
bdaddr_t bdaddr;
u8 bdaddr_type;
u8 type;
u8 val[16];
};
struct smp_ltk {
struct list_head list;
struct rcu_head rcu;
bdaddr_t bdaddr;
u8 bdaddr_type;
u8 authenticated;
u8 type;
u8 enc_size;
__le16 ediv;
__le64 rand;
u8 val[16];
};
struct smp_irk {
struct list_head list;
struct rcu_head rcu;
bdaddr_t rpa;
bdaddr_t bdaddr;
u8 addr_type;
u8 val[16];
};
struct link_key {
struct list_head list;
struct rcu_head rcu;
bdaddr_t bdaddr;
u8 type;
u8 val[HCI_LINK_KEY_SIZE];
u8 pin_len;
};
struct oob_data {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
u8 present;
u8 hash192[16];
u8 rand192[16];
u8 hash256[16];
u8 rand256[16];
};
struct adv_info {
struct list_head list;
bool enabled;
bool pending;
bool periodic;
bool periodic_enabled;
__u8 mesh;
__u8 instance;
__u8 handle;
__u8 sid;
__u32 flags;
__u16 timeout;
__u16 remaining_time;
__u16 duration;
__u16 adv_data_len;
__u8 adv_data[HCI_MAX_EXT_AD_LENGTH];
bool adv_data_changed;
__u16 scan_rsp_len;
__u8 scan_rsp_data[HCI_MAX_EXT_AD_LENGTH];
bool scan_rsp_changed;
__u16 per_adv_data_len;
__u8 per_adv_data[HCI_MAX_PER_AD_LENGTH];
__s8 tx_power;
__u32 min_interval;
__u32 max_interval;
bdaddr_t random_addr;
bool rpa_expired;
struct delayed_work rpa_expired_cb;
};
struct tx_queue {
struct sk_buff_head queue;
unsigned int extra;
unsigned int tracked;
};
#define HCI_MAX_ADV_INSTANCES 5
#define HCI_DEFAULT_ADV_DURATION 2
#define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F
#define DATA_CMP(_d1, _l1, _d2, _l2) \
(_l1 == _l2 ? memcmp(_d1, _d2, _l1) : _l1 - _l2)
#define ADV_DATA_CMP(_adv, _data, _len) \
DATA_CMP((_adv)->adv_data, (_adv)->adv_data_len, _data, _len)
#define SCAN_RSP_CMP(_adv, _data, _len) \
DATA_CMP((_adv)->scan_rsp_data, (_adv)->scan_rsp_len, _data, _len)
struct monitored_device {
struct list_head list;
bdaddr_t bdaddr;
__u8 addr_type;
__u16 handle;
bool notified;
};
struct adv_pattern {
struct list_head list;
__u8 ad_type;
__u8 offset;
__u8 length;
__u8 value[HCI_MAX_EXT_AD_LENGTH];
};
struct adv_rssi_thresholds {
__s8 low_threshold;
__s8 high_threshold;
__u16 low_threshold_timeout;
__u16 high_threshold_timeout;
__u8 sampling_period;
};
struct adv_monitor {
struct list_head patterns;
struct adv_rssi_thresholds rssi;
__u16 handle;
enum {
ADV_MONITOR_STATE_NOT_REGISTERED,
ADV_MONITOR_STATE_REGISTERED,
ADV_MONITOR_STATE_OFFLOADED
} state;
};
#define HCI_MIN_ADV_MONITOR_HANDLE 1
#define HCI_MAX_ADV_MONITOR_NUM_HANDLES 32
#define HCI_MAX_ADV_MONITOR_NUM_PATTERNS 16
#define HCI_ADV_MONITOR_EXT_NONE 1
#define HCI_ADV_MONITOR_EXT_MSFT 2
#define HCI_MAX_SHORT_NAME_LENGTH 10
#define HCI_CONN_HANDLE_MAX 0x0eff
#define HCI_CONN_HANDLE_UNSET(_handle) (_handle > HCI_CONN_HANDLE_MAX)
/* Min encryption key size to match with SMP */
#define HCI_MIN_ENC_KEY_SIZE 7
/* Default LE RPA expiry time, 15 minutes */
#define HCI_DEFAULT_RPA_TIMEOUT (15 * 60)
/* Default min/max age of connection information (1s/3s) */
#define DEFAULT_CONN_INFO_MIN_AGE 1000
#define DEFAULT_CONN_INFO_MAX_AGE 3000
/* Default authenticated payload timeout 30s */
#define DEFAULT_AUTH_PAYLOAD_TIMEOUT 0x0bb8
#define HCI_MAX_PAGES 3
struct hci_dev {
struct list_head list;
struct srcu_struct srcu;
struct mutex lock;
struct ida unset_handle_ida;
const char *name;
unsigned long flags;
__u16 id;
__u8 bus;
bdaddr_t bdaddr;
bdaddr_t setup_addr;
bdaddr_t public_addr;
bdaddr_t random_addr;
bdaddr_t static_addr;
__u8 adv_addr_type;
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH];
__u16 appearance;
__u8 dev_class[3];
__u8 major_class;
__u8 minor_class;
__u8 max_page;
__u8 features[HCI_MAX_PAGES][8];
__u8 le_features[248];
__u8 le_accept_list_size;
__u8 le_resolv_list_size;
__u8 le_num_of_adv_sets;
__u8 le_states[8];
__u8 mesh_ad_types[16];
__u8 mesh_send_ref;
__u8 commands[64];
__u8 hci_ver;
__u16 hci_rev;
__u8 lmp_ver;
__u16 manufacturer;
__u16 lmp_subver;
__u16 voice_setting;
__u8 num_iac;
__u16 stored_max_keys;
__u16 stored_num_keys;
__u8 io_capability;
__s8 inq_tx_power;
__u8 err_data_reporting;
__u16 page_scan_interval;
__u16 page_scan_window;
__u8 page_scan_type;
__u8 le_adv_channel_map;
__u16 le_adv_min_interval;
__u16 le_adv_max_interval;
__u8 le_scan_type;
__u16 le_scan_interval;
__u16 le_scan_window;
__u16 le_scan_int_suspend;
__u16 le_scan_window_suspend;
__u16 le_scan_int_discovery;
__u16 le_scan_window_discovery;
__u16 le_scan_int_adv_monitor;
__u16 le_scan_window_adv_monitor;
__u16 le_scan_int_connect;
__u16 le_scan_window_connect;
__u16 le_conn_min_interval;
__u16 le_conn_max_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
__u16 le_def_tx_len;
__u16 le_def_tx_time;
__u16 le_max_tx_len;
__u16 le_max_tx_time;
__u16 le_max_rx_len;
__u16 le_max_rx_time;
__u8 le_max_key_size;
__u8 le_min_key_size;
__u16 discov_interleaved_timeout;
__u16 conn_info_min_age;
__u16 conn_info_max_age;
__u16 auth_payload_timeout;
__u8 min_enc_key_size;
__u8 max_enc_key_size;
__u8 pairing_opts;
__u8 ssp_debug_mode;
__u8 hw_error_code;
__u32 clock;
__u16 advmon_allowlist_duration;
__u16 advmon_no_filter_duration;
__u8 enable_advmon_interleave_scan;
__u16 devid_source;
__u16 devid_vendor;
__u16 devid_product;
__u16 devid_version;
__u8 def_page_scan_type;
__u16 def_page_scan_int;
__u16 def_page_scan_window;
__u8 def_inq_scan_type;
__u16 def_inq_scan_int;
__u16 def_inq_scan_window;
__u16 def_br_lsto;
__u16 def_page_timeout;
__u16 def_multi_adv_rotation_duration;
__u16 def_le_autoconnect_timeout;
__s8 min_le_tx_power;
__s8 max_le_tx_power;
__u16 pkt_type;
__u16 esco_type;
__u16 link_policy;
__u16 link_mode;
__u32 idle_timeout;
__u16 sniff_min_interval;
__u16 sniff_max_interval;
unsigned int auto_accept_delay;
DECLARE_BITMAP(quirk_flags, __HCI_NUM_QUIRKS);
atomic_t cmd_cnt;
unsigned int acl_cnt;
unsigned int sco_cnt;
unsigned int le_cnt;
unsigned int iso_cnt;
unsigned int acl_mtu;
unsigned int sco_mtu;
unsigned int le_mtu;
unsigned int iso_mtu;
unsigned int acl_pkts;
unsigned int sco_pkts;
unsigned int le_pkts;
unsigned int iso_pkts;
unsigned long acl_last_tx;
unsigned long le_last_tx;
unsigned long iso_last_tx;
__u8 le_tx_def_phys;
__u8 le_rx_def_phys;
struct workqueue_struct *workqueue;
struct workqueue_struct *req_workqueue;
struct work_struct power_on;
struct delayed_work power_off;
struct work_struct error_reset;
struct work_struct cmd_sync_work;
struct list_head cmd_sync_work_list;
struct mutex cmd_sync_work_lock;
struct mutex unregister_lock;
struct work_struct cmd_sync_cancel_work;
struct work_struct reenable_adv_work;
__u16 discov_timeout;
struct delayed_work discov_off;
struct delayed_work service_cache;
struct delayed_work cmd_timer;
struct delayed_work ncmd_timer;
struct work_struct rx_work;
struct work_struct cmd_work;
struct work_struct tx_work;
struct delayed_work le_scan_disable;
struct sk_buff_head rx_q;
struct sk_buff_head raw_q;
struct sk_buff_head cmd_q;
struct sk_buff *sent_cmd;
struct sk_buff *recv_event;
struct mutex req_lock;
wait_queue_head_t req_wait_q;
__u32 req_status;
__u32 req_result;
struct sk_buff *req_skb;
struct sk_buff *req_rsp;
void *smp_data;
void *smp_bredr_data;
struct discovery_state discovery;
bool discovery_paused;
int advertising_old_state;
bool advertising_paused;
struct notifier_block suspend_notifier;
enum suspended_state suspend_state_next;
enum suspended_state suspend_state;
bool scanning_paused;
bool suspended;
u8 wake_reason;
bdaddr_t wake_addr;
u8 wake_addr_type;
struct hci_conn_hash conn_hash;
struct list_head mesh_pending;
struct mutex mgmt_pending_lock;
struct list_head mgmt_pending;
struct list_head reject_list;
struct list_head accept_list;
struct list_head uuids;
struct list_head link_keys;
struct list_head long_term_keys;
struct list_head identity_resolving_keys;
struct list_head remote_oob_data;
struct list_head le_accept_list;
struct list_head le_resolv_list;
struct list_head le_conn_params;
struct list_head pend_le_conns;
struct list_head pend_le_reports;
struct list_head blocked_keys;
struct list_head local_codecs;
struct hci_dev_stats stat;
atomic_t promisc;
const char *hw_info;
const char *fw_info;
struct dentry *debugfs;
struct hci_devcoredump dump;
struct device dev;
struct rfkill *rfkill;
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
hci_conn_flags_t conn_flags;
__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_EXT_AD_LENGTH];
__u8 adv_data_len;
__u8 scan_rsp_data[HCI_MAX_EXT_AD_LENGTH];
__u8 scan_rsp_data_len;
__u8 per_adv_data[HCI_MAX_PER_AD_LENGTH];
__u8 per_adv_data_len;
struct list_head adv_instances;
unsigned int adv_instance_cnt;
__u8 cur_adv_instance;
__u16 adv_instance_timeout;
struct delayed_work adv_instance_expire;
struct idr adv_monitors_idr;
unsigned int adv_monitors_cnt;
__u8 irk[16];
__u32 rpa_timeout;
struct delayed_work rpa_expired;
bdaddr_t rpa;
struct delayed_work mesh_send_done;
enum {
INTERLEAVE_SCAN_NONE,
INTERLEAVE_SCAN_NO_FILTER,
INTERLEAVE_SCAN_ALLOWLIST
} interleave_scan_state;
struct delayed_work interleave_scan;
struct list_head monitored_devices;
bool advmon_pend_notify;
struct hci_drv *hci_drv;
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led;
#endif
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
__u16 msft_opcode;
void *msft_data;
bool msft_curve_validity;
#endif
#if IS_ENABLED(CONFIG_BT_AOSPEXT)
bool aosp_capable;
bool aosp_quality_report;
#endif
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
int (*setup)(struct hci_dev *hdev);
int (*shutdown)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
void (*notify)(struct hci_dev *hdev, unsigned int evt);
void (*hw_error)(struct hci_dev *hdev, u8 code);
int (*post_init)(struct hci_dev *hdev);
int (*set_diag)(struct hci_dev *hdev, bool enable);
int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
void (*reset)(struct hci_dev *hdev);
bool (*wakeup)(struct hci_dev *hdev);
int (*set_quality_report)(struct hci_dev *hdev, bool enable);
int (*get_data_path_id)(struct hci_dev *hdev, __u8 *data_path);
int (*get_codec_config_data)(struct hci_dev *hdev, __u8 type,
struct bt_codec *codec, __u8 *vnd_len,
__u8 **vnd_data);
u8 (*classify_pkt_type)(struct hci_dev *hdev, struct sk_buff *skb);
};
#define hci_set_quirk(hdev, nr) set_bit((nr), (hdev)->quirk_flags)
#define hci_clear_quirk(hdev, nr) clear_bit((nr), (hdev)->quirk_flags)
#define hci_test_quirk(hdev, nr) test_bit((nr), (hdev)->quirk_flags)
#define HCI_PHY_HANDLE(handle) (handle & 0xff)
enum conn_reasons {
CONN_REASON_PAIR_DEVICE,
CONN_REASON_L2CAP_CHAN,
CONN_REASON_SCO_CONNECT,
CONN_REASON_ISO_CONNECT,
};
struct hci_conn {
struct list_head list;
atomic_t refcnt;
bdaddr_t dst;
__u8 dst_type;
bdaddr_t src;
__u8 src_type;
bdaddr_t init_addr;
__u8 init_addr_type;
bdaddr_t resp_addr;
__u8 resp_addr_type;
__u8 adv_instance;
__u16 handle;
__u16 sync_handle;
__u8 sid;
__u16 state;
__u16 mtu;
__u8 mode;
__u8 type;
__u8 role;
bool out;
__u8 attempt;
__u8 dev_class[3];
__u8 features[HCI_MAX_PAGES][8];
__u8 le_features[248];
__u16 pkt_type;
__u16 link_policy;
__u8 key_type;
__u8 auth_type;
__u8 sec_level;
__u8 pending_sec_level;
__u8 pin_length;
__u8 enc_key_size;
__u8 io_capability;
__u32 passkey_notify;
__u8 passkey_entered;
__u16 disc_timeout;
__u16 conn_timeout;
__u16 setting;
__u16 auth_payload_timeout;
__u16 le_conn_min_interval;
__u16 le_conn_max_interval;
__u16 le_conn_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
__u8 le_adv_data[HCI_MAX_EXT_AD_LENGTH];
__u8 le_adv_data_len;
__u8 le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN];
__u16 le_per_adv_data_len;
__u16 le_per_adv_data_offset;
__u8 le_adv_phy;
__u8 le_adv_sec_phy;
__u8 le_tx_def_phys;
__u8 le_rx_def_phys;
__u8 le_tx_phy;
__u8 le_rx_phy;
__s8 rssi;
__s8 tx_power;
__s8 max_tx_power;
struct bt_iso_qos iso_qos;
__u8 num_bis;
__u8 bis[HCI_MAX_ISO_BIS];
unsigned long flags;
enum conn_reasons conn_reason;
__u8 abort_reason;
__u32 clock;
__u16 clock_accuracy;
unsigned long conn_info_timestamp;
__u8 remote_cap;
__u8 remote_auth;
unsigned int sent;
struct sk_buff_head data_q;
struct list_head chan_list;
struct tx_queue tx_q;
struct delayed_work disc_work;
struct delayed_work auto_accept_work;
struct delayed_work idle_work;
struct delayed_work le_conn_timeout;
struct device dev;
struct dentry *debugfs;
struct hci_dev *hdev;
void *l2cap_data;
void *sco_data;
void *iso_data;
struct list_head link_list;
struct hci_conn *parent;
struct hci_link *link;
struct bt_codec codec;
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
void (*cleanup)(struct hci_conn *conn);
};
struct hci_link {
struct list_head list;
struct hci_conn *conn;
};
struct hci_chan {
struct list_head list;
__u16 handle;
struct hci_conn *conn;
struct sk_buff_head data_q;
unsigned int sent;
__u8 state;
};
struct hci_conn_params {
struct list_head list;
struct list_head action;
bdaddr_t addr;
u8 addr_type;
u16 conn_min_interval;
u16 conn_max_interval;
u16 conn_latency;
u16 supervision_timeout;
enum {
HCI_AUTO_CONN_DISABLED,
HCI_AUTO_CONN_REPORT,
HCI_AUTO_CONN_DIRECT,
HCI_AUTO_CONN_ALWAYS,
HCI_AUTO_CONN_LINK_LOSS,
HCI_AUTO_CONN_EXPLICIT,
} auto_connect;
struct hci_conn *conn;
bool explicit_connect;
/* Accessed without hdev->lock: */
hci_conn_flags_t flags;
u8 privacy_mode;
};
extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock;
extern struct mutex hci_cb_list_lock;
#define hci_dev_set_flag(hdev, nr) set_bit((nr), (hdev)->dev_flags)
#define hci_dev_clear_flag(hdev, nr) clear_bit((nr), (hdev)->dev_flags)
#define hci_dev_change_flag(hdev, nr) change_bit((nr), (hdev)->dev_flags)
#define hci_dev_test_flag(hdev, nr) test_bit((nr), (hdev)->dev_flags)
#define hci_dev_test_and_set_flag(hdev, nr) test_and_set_bit((nr), (hdev)->dev_flags)
#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), (hdev)->dev_flags)
#define hci_dev_test_and_change_flag(hdev, nr) test_and_change_bit((nr), (hdev)->dev_flags)
#define hci_dev_clear_volatile_flags(hdev) \
do { \
hci_dev_clear_flag((hdev), HCI_LE_SCAN); \
hci_dev_clear_flag((hdev), HCI_LE_ADV); \
hci_dev_clear_flag((hdev), HCI_LL_RPA_RESOLUTION); \
hci_dev_clear_flag((hdev), HCI_PERIODIC_INQ); \
hci_dev_clear_flag((hdev), HCI_QUALITY_REPORT); \
} while (0)
#define hci_dev_le_state_simultaneous(hdev) \
(!hci_test_quirk((hdev), HCI_QUIRK_BROKEN_LE_STATES) && \
((hdev)->le_states[4] & 0x08) && /* Central */ \
((hdev)->le_states[4] & 0x40) && /* Peripheral */ \
((hdev)->le_states[3] & 0x10)) /* Simultaneous */
/* ----- HCI interface to upper protocols ----- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
int l2cap_disconn_ind(struct hci_conn *hcon);
int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb,
u16 flags);
#if IS_ENABLED(CONFIG_BT_BREDR)
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
int sco_recv_scodata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb);
#else
static inline int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 *flags)
{
return 0;
}
static inline int sco_recv_scodata(struct hci_dev *hdev, u16 handle,
struct sk_buff *skb)
{
kfree_skb(skb);
return -ENOENT;
}
#endif
#if IS_ENABLED(CONFIG_BT_LE)
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb,
u16 flags);
#else
static inline int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 *flags)
{
return 0;
}
static inline int iso_recv(struct hci_dev *hdev, u16 handle,
struct sk_buff *skb, u16 flags)
{
kfree_skb(skb);
return -ENOENT;
}
#endif
/* ----- Inquiry cache ----- */
#define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */
#define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */
static inline void discovery_init(struct hci_dev *hdev)
{
spin_lock_init(&hdev->discovery.lock);
hdev->discovery.state = DISCOVERY_STOPPED;
INIT_LIST_HEAD(&hdev->discovery.all);
INIT_LIST_HEAD(&hdev->discovery.unknown);
INIT_LIST_HEAD(&hdev->discovery.resolve);
hdev->discovery.report_invalid_rssi = true;
hdev->discovery.rssi = HCI_RSSI_INVALID;
}
static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
{
hdev->discovery.result_filtering = false;
hdev->discovery.report_invalid_rssi = true;
hdev->discovery.rssi = HCI_RSSI_INVALID;
hdev->discovery.uuid_count = 0;
spin_lock(&hdev->discovery.lock);
kfree(hdev->discovery.uuids);
hdev->discovery.uuids = NULL;
spin_unlock(&hdev->discovery.lock);
}
bool hci_discovery_active(struct hci_dev *hdev);
void hci_discovery_set_state(struct hci_dev *hdev, int state);
static inline int inquiry_cache_empty(struct hci_dev *hdev)
{
return list_empty(&hdev->discovery.all);
}
static inline long inquiry_cache_age(struct hci_dev *hdev)
{
struct discovery_state *c = &hdev->discovery;
return jiffies - c->timestamp;
}
static inline long inquiry_entry_age(struct inquiry_entry *e)
{
return jiffies - e->timestamp;
}
struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev,
bdaddr_t *bdaddr);
struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
bdaddr_t *bdaddr);
struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev,
bdaddr_t *bdaddr,
int state);
void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
struct inquiry_entry *ie);
u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
bool name_known);
void hci_inquiry_cache_flush(struct hci_dev *hdev);
/* ----- HCI Connections ----- */
enum {
HCI_CONN_AUTH_PEND,
HCI_CONN_ENCRYPT_PEND,
HCI_CONN_RSWITCH_PEND,
HCI_CONN_MODE_CHANGE_PEND,
HCI_CONN_SCO_SETUP_PEND,
HCI_CONN_MGMT_CONNECTED,
HCI_CONN_SSP_ENABLED,
HCI_CONN_SC_ENABLED,
HCI_CONN_AES_CCM,
HCI_CONN_POWER_SAVE,
HCI_CONN_FLUSH_KEY,
HCI_CONN_ENCRYPT,
HCI_CONN_AUTH,
HCI_CONN_SECURE,
HCI_CONN_FIPS,
HCI_CONN_STK_ENCRYPT,
HCI_CONN_AUTH_INITIATOR,
HCI_CONN_DROP,
HCI_CONN_CANCEL,
HCI_CONN_PARAM_REMOVAL_PEND,
HCI_CONN_NEW_LINK_KEY,
HCI_CONN_SCANNING,
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
HCI_CONN_CREATE_CIS,
HCI_CONN_CREATE_BIG_SYNC,
HCI_CONN_BIG_SYNC,
HCI_CONN_BIG_SYNC_FAILED,
HCI_CONN_CREATE_PA_SYNC,
HCI_CONN_PA_SYNC,
HCI_CONN_PA_SYNC_FAILED,
};
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
return hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
return hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
}
static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_add_tail_rcu(&c->list, &h->list);
switch (c->type) {
case ACL_LINK:
h->acl_num++;
break;
case LE_LINK:
h->le_num++;
if (c->role == HCI_ROLE_SLAVE)
h->le_num_peripheral++;
break;
case SCO_LINK:
case ESCO_LINK:
h->sco_num++;
break;
case CIS_LINK:
h->cis_num++;
break;
case BIS_LINK:
h->bis_num++;
break;
case PA_LINK:
h->pa_num++;
break;
}
}
static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_del_rcu(&c->list);
synchronize_rcu();
switch (c->type) {
case ACL_LINK:
h->acl_num--;
break;
case LE_LINK:
h->le_num--;
if (c->role == HCI_ROLE_SLAVE)
h->le_num_peripheral--;
break;
case SCO_LINK:
case ESCO_LINK:
h->sco_num--;
break;
case CIS_LINK:
h->cis_num--;
break;
case BIS_LINK:
h->bis_num--;
break;
case PA_LINK:
h->pa_num--;
break;
}
}
static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
switch (type) {
case ACL_LINK:
return h->acl_num;
case LE_LINK:
return h->le_num;
case SCO_LINK:
case ESCO_LINK:
return h->sco_num;
case CIS_LINK:
return h->cis_num;
case BIS_LINK:
return h->bis_num;
case PA_LINK:
return h->pa_num;
default:
return 0;
}
}
static inline unsigned int hci_conn_count(struct hci_dev *hdev)
{
struct hci_conn_hash *c = &hdev->conn_hash;
return c->acl_num + c->sco_num + c->le_num + c->cis_num + c->bis_num +
c->pa_num;
}
static inline unsigned int hci_iso_count(struct hci_dev *hdev)
{
struct hci_conn_hash *c = &hdev->conn_hash;
return c->cis_num + c->bis_num;
}
static inline bool hci_conn_valid(struct hci_dev *hdev, struct hci_conn *conn)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c == conn) {
rcu_read_unlock();
return true;
}
}
rcu_read_unlock();
return false;
}
static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
__u8 type = INVALID_LINK;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->handle == handle) {
type = c->type;
break;
}
}
rcu_read_unlock();
return type;
}
static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
bdaddr_t *ba, __u8 bis)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, ba) || c->type != BIS_LINK)
continue;
if (c->iso_qos.bcast.bis == bis) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_create_pa_sync(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != PA_LINK)
continue;
if (!test_bit(HCI_CONN_CREATE_PA_SYNC, &c->flags))
continue;
rcu_read_unlock();
return c;
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
bdaddr_t *ba,
__u8 big, __u8 bis)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
!test_bit(HCI_CONN_PER_ADV, &c->flags))
continue;
if (c->iso_qos.bcast.big == big &&
c->iso_qos.bcast.bis == bis) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
__u16 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->handle == handle) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
__u8 type, bdaddr_t *ba)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && !bacmp(&c->dst, ba)) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_role(struct hci_dev *hdev,
__u8 type, __u8 role,
bdaddr_t *ba)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->role == role && !bacmp(&c->dst, ba)) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev,
bdaddr_t *ba,
__u8 ba_type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != LE_LINK)
continue;
if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
bdaddr_t *ba,
__u8 ba_type,
__u8 cig,
__u8 id)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != CIS_LINK)
continue;
/* Match CIG ID if set */
if (cig != c->iso_qos.ucast.cig)
continue;
/* Match CIS ID if set */
if (id != c->iso_qos.ucast.cis)
continue;
/* Match destination address if set */
if (!ba || (ba_type == c->dst_type && !bacmp(&c->dst, ba))) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
__u8 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != CIS_LINK)
continue;
if (handle == c->iso_qos.ucast.cig) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
__u8 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != BIS_LINK)
continue;
if (handle == c->iso_qos.bcast.big) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_big_sync_pend(struct hci_dev *hdev,
__u8 handle, __u8 num_bis)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != PA_LINK)
continue;
if (handle == c->iso_qos.bcast.big && num_bis == c->num_bis) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state,
__u8 role)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != BIS_LINK || c->state != state || c->role != role)
continue;
if (handle == c->iso_qos.bcast.big) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != BIS_LINK ||
!test_bit(HCI_CONN_PA_SYNC, &c->flags))
continue;
if (c->iso_qos.bcast.big == big) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
static inline struct hci_conn *
hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != PA_LINK)
continue;
/* Ignore the listen hcon, we are looking
* for the child hcon that was created as
* a result of the PA sync established event.
*/
if (c->state == BT_LISTEN)
continue;
if (c->sync_handle == sync_handle) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
typedef void (*hci_conn_func_t)(struct hci_conn *conn, void *data);
static inline void hci_conn_hash_list_state(struct hci_dev *hdev,
hci_conn_func_t func, __u8 type,
__u16 state, void *data)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
if (!func)
return;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->state == state)
func(c, data);
}
rcu_read_unlock();
}
static inline void hci_conn_hash_list_flag(struct hci_dev *hdev,
hci_conn_func_t func, __u8 type,
__u8 flag, void *data)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
if (!func)
return;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && test_bit(flag, &c->flags))
func(c, data);
}
rcu_read_unlock();
}
static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == LE_LINK && c->state == BT_CONNECT &&
!test_bit(HCI_CONN_SCANNING, &c->flags)) {
rcu_read_unlock();
return c;
}
}
rcu_read_unlock();
return NULL;
}
/* Returns true if an le connection is in the scanning state */
static inline bool hci_is_le_conn_scanning(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *c;
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == LE_LINK && c->state == BT_CONNECT &&
test_bit(HCI_CONN_SCANNING, &c->flags)) {
rcu_read_unlock();
return true;
}
}
rcu_read_unlock();
return false;
}
int hci_disconnect(struct hci_conn *conn, __u8 reason);
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
void hci_sco_setup(struct hci_conn *conn, __u8 status);
bool hci_iso_setup_path(struct hci_conn *conn);
int hci_le_create_cis_pending(struct hci_dev *hdev);
int hci_conn_check_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 dst_type, u8 role, u16 handle);
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 dst_type, u8 role);
void hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev);
struct hci_chan *hci_chan_create(struct hci_conn *conn);
void hci_chan_del(struct hci_chan *chan);
void hci_chan_list_flush(struct hci_conn *conn);
struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
u16 conn_timeout,
enum conn_reasons conn_reason);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, bool dst_resolved, u8 sec_level,
u16 conn_timeout, u8 role, u8 phy, u8 sec_phy);
void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status);
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
u8 sec_level, u8 auth_type,
enum conn_reasons conn_reason, u16 timeout);
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting, struct bt_codec *codec,
u16 timeout);
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos,
u16 timeout);
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
struct bt_iso_qos *qos,
__u8 base_len, __u8 *base, u16 timeout);
int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type);
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos,
u16 timeout);
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
struct bt_iso_qos *qos,
__u8 data_len, __u8 *data, u16 timeout);
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid, struct bt_iso_qos *qos);
int hci_conn_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
struct bt_iso_qos *qos, __u16 sync_handle,
__u8 num_bis, __u8 bis[]);
int hci_conn_check_link_mode(struct hci_conn *conn);
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
bool initiator);
int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
void hci_conn_failed(struct hci_conn *conn, u8 status);
u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb);
void hci_conn_tx_dequeue(struct hci_conn *conn);
void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
const struct sockcm_cookie *sockc);
static inline void hci_sockcm_init(struct sockcm_cookie *sockc, struct sock *sk)
{
*sockc = (struct sockcm_cookie) {
.tsflags = READ_ONCE(sk->sk_tsflags),
};
}
/*
* hci_conn_get() and hci_conn_put() are used to control the life-time of an
* "hci_conn" object. They do not guarantee that the hci_conn object is running,
* working or anything else. They just guarantee that the object is available
* and can be dereferenced. So you can use its locks, local variables and any
* other constant data.
* Before accessing runtime data, you _must_ lock the object and then check that
* it is still running. As soon as you release the locks, the connection might
* get dropped, though.
*
* On the other hand, hci_conn_hold() and hci_conn_drop() are used to control
* how long the underlying connection is held. So every channel that runs on the
* hci_conn object calls this to prevent the connection from disappearing. As
* long as you hold a device, you must also guarantee that you have a valid
* reference to the device via hci_conn_get() (or the initial reference from
* hci_conn_add()).
* The hold()/drop() ref-count is known to drop below 0 sometimes, which doesn't
* break because nobody cares for that. But this means, we cannot use
* _get()/_drop() in it, but require the caller to have a valid ref (FIXME).
*/
static inline struct hci_conn *hci_conn_get(struct hci_conn *conn)
{
get_device(&conn->dev);
return conn;
}
static inline void hci_conn_put(struct hci_conn *conn)
{
put_device(&conn->dev);
}
static inline struct hci_conn *hci_conn_hold(struct hci_conn *conn)
{
BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt));
atomic_inc(&conn->refcnt);
cancel_delayed_work(&conn->disc_work);
return conn;
}
static inline void hci_conn_drop(struct hci_conn *conn)
{
BT_DBG("hcon %p orig refcnt %d", conn, atomic_read(&conn->refcnt));
if (atomic_dec_and_test(&conn->refcnt)) {
unsigned long timeo;
switch (conn->type) {
case ACL_LINK:
case LE_LINK:
cancel_delayed_work(&conn->idle_work);
if (conn->state == BT_CONNECTED) {
timeo = conn->disc_timeout;
if (!conn->out)
timeo *= 2;
} else {
timeo = 0;
}
break;
default:
timeo = 0;
break;
}
cancel_delayed_work(&conn->disc_work);
queue_delayed_work(conn->hdev->workqueue,
&conn->disc_work, timeo);
}
}
/* ----- HCI Devices ----- */
static inline void hci_dev_put(struct hci_dev *d)
{
BT_DBG("%s orig refcnt %d", d->name,
kref_read(&d->dev.kobj.kref));
put_device(&d->dev);
}
static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
{
BT_DBG("%s orig refcnt %d", d->name,
kref_read(&d->dev.kobj.kref));
get_device(&d->dev);
return d;
}
#define hci_dev_lock(d) mutex_lock(&d->lock)
#define hci_dev_unlock(d) mutex_unlock(&d->lock)
#define to_hci_dev(d) container_of(d, struct hci_dev, dev)
#define to_hci_conn(c) container_of(c, struct hci_conn, dev)
static inline void *hci_get_drvdata(struct hci_dev *hdev)
{
return dev_get_drvdata(&hdev->dev);
}
static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
{
dev_set_drvdata(&hdev->dev, data);
}
static inline void *hci_get_priv(struct hci_dev *hdev)
{
return (char *)hdev + sizeof(*hdev);
}
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
struct hci_dev *hci_alloc_dev_priv(int sizeof_priv);
static inline struct hci_dev *hci_alloc_dev(void)
{
return hci_alloc_dev_priv(0);
}
void hci_free_dev(struct hci_dev *hdev);
int hci_register_dev(struct hci_dev *hdev);
void hci_unregister_dev(struct hci_dev *hdev);
void hci_release_dev(struct hci_dev *hdev);
int hci_register_suspend_notifier(struct hci_dev *hdev);
int hci_unregister_suspend_notifier(struct hci_dev *hdev);
int hci_suspend_dev(struct hci_dev *hdev);
int hci_resume_dev(struct hci_dev *hdev);
int hci_reset_dev(struct hci_dev *hdev);
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
__printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
static inline void hci_set_msft_opcode(struct hci_dev *hdev, __u16 opcode)
{
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
hdev->msft_opcode = opcode;
#endif
}
static inline void hci_set_aosp_capable(struct hci_dev *hdev)
{
#if IS_ENABLED(CONFIG_BT_AOSPEXT)
hdev->aosp_capable = true;
#endif
}
static inline void hci_devcd_setup(struct hci_dev *hdev)
{
#ifdef CONFIG_DEV_COREDUMP
INIT_WORK(&hdev->dump.dump_rx, hci_devcd_rx);
INIT_DELAYED_WORK(&hdev->dump.dump_timeout, hci_devcd_timeout);
skb_queue_head_init(&hdev->dump.dump_q);
#endif
}
int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev);
int hci_dev_reset(__u16 dev);
int hci_dev_reset_stat(__u16 dev);
int hci_dev_cmd(unsigned int cmd, void __user *arg);
int hci_get_dev_list(void __user *arg);
int hci_get_dev_info(void __user *arg);
int hci_get_conn_list(void __user *arg);
int hci_get_conn_info(struct hci_dev *hdev, void __user *arg);
int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
int hci_inquiry(void __user *arg);
struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *list,
bdaddr_t *bdaddr, u8 type);
struct bdaddr_list_with_irk *hci_bdaddr_list_lookup_with_irk(
struct list_head *list, bdaddr_t *bdaddr,
u8 type);
struct bdaddr_list_with_flags *
hci_bdaddr_list_lookup_with_flags(struct list_head *list, bdaddr_t *bdaddr,
u8 type);
int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type);
int hci_bdaddr_list_add_with_irk(struct list_head *list, bdaddr_t *bdaddr,
u8 type, u8 *peer_irk, u8 *local_irk);
int hci_bdaddr_list_add_with_flags(struct list_head *list, bdaddr_t *bdaddr,
u8 type, u32 flags);
int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type);
int hci_bdaddr_list_del_with_irk(struct list_head *list, bdaddr_t *bdaddr,
u8 type);
void hci_bdaddr_list_clear(struct list_head *list);
struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type);
struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type);
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
void hci_conn_params_clear_disabled(struct hci_dev *hdev);
void hci_conn_params_free(struct hci_conn_params *param);
void hci_pend_le_list_del_init(struct hci_conn_params *param);
void hci_pend_le_list_add(struct hci_conn_params *param,
struct list_head *list);
struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
bdaddr_t *addr,
u8 addr_type);
void hci_uuids_clear(struct hci_dev *hdev);
void hci_link_keys_clear(struct hci_dev *hdev);
u8 *hci_conn_key_enc_size(struct hci_conn *conn);
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
bdaddr_t *bdaddr, u8 *val, u8 type,
u8 pin_len, bool *persistent);
struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type, u8 type, u8 authenticated,
u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type, u8 role);
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
void hci_smp_ltks_clear(struct hci_dev *hdev);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa);
struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type);
struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type, u8 val[16], bdaddr_t *rpa);
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16]);
void hci_blocked_keys_clear(struct hci_dev *hdev);
void hci_smp_irks_clear(struct hci_dev *hdev);
bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
void hci_remote_oob_data_clear(struct hci_dev *hdev);
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 bdaddr_type);
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 bdaddr_type, u8 *hash192, u8 *rand192,
u8 *hash256, u8 *rand256);
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 bdaddr_type);
void hci_adv_instances_clear(struct hci_dev *hdev);
struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance);
struct adv_info *hci_find_adv_sid(struct hci_dev *hdev, u8 sid);
struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance);
struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
u32 flags, u16 adv_data_len, u8 *adv_data,
u16 scan_rsp_len, u8 *scan_rsp_data,
u16 timeout, u16 duration, s8 tx_power,
u32 min_interval, u32 max_interval,
u8 mesh_handle);
struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance, u8 sid,
u32 flags, u8 data_len, u8 *data,
u32 min_interval, u32 max_interval);
int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
u16 adv_data_len, u8 *adv_data,
u16 scan_rsp_len, u8 *scan_rsp_data);
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
void hci_adv_instances_set_rpa_expired(struct hci_dev *hdev, bool rpa_expired);
u32 hci_adv_instance_flags(struct hci_dev *hdev, u8 instance);
bool hci_adv_instance_is_scannable(struct hci_dev *hdev, u8 instance);
void hci_adv_monitors_clear(struct hci_dev *hdev);
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
int hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle);
int hci_remove_all_adv_monitor(struct hci_dev *hdev);
bool hci_is_adv_monitoring(struct hci_dev *hdev);
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
void hci_init_sysfs(struct hci_dev *hdev);
void hci_conn_init_sysfs(struct hci_conn *conn);
void hci_conn_add_sysfs(struct hci_conn *conn);
void hci_conn_del_sysfs(struct hci_conn *conn);
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))
#define GET_HCIDEV_DEV(hdev) ((hdev)->dev.parent)
/* ----- LMP capabilities ----- */
#define lmp_encrypt_capable(dev) ((dev)->features[0][0] & LMP_ENCRYPT)
#define lmp_rswitch_capable(dev) ((dev)->features[0][0] & LMP_RSWITCH)
#define lmp_hold_capable(dev) ((dev)->features[0][0] & LMP_HOLD)
#define lmp_sniff_capable(dev) ((dev)->features[0][0] & LMP_SNIFF)
#define lmp_park_capable(dev) ((dev)->features[0][1] & LMP_PARK)
#define lmp_sco_capable(dev) ((dev)->features[0][1] & LMP_SCO)
#define lmp_inq_rssi_capable(dev) ((dev)->features[0][3] & LMP_RSSI_INQ)
#define lmp_esco_capable(dev) ((dev)->features[0][3] & LMP_ESCO)
#define lmp_bredr_capable(dev) (!((dev)->features[0][4] & LMP_NO_BREDR))
#define lmp_le_capable(dev) ((dev)->features[0][4] & LMP_LE)
#define lmp_sniffsubr_capable(dev) ((dev)->features[0][5] & LMP_SNIFF_SUBR)
#define lmp_pause_enc_capable(dev) ((dev)->features[0][5] & LMP_PAUSE_ENC)
#define lmp_esco_2m_capable(dev) ((dev)->features[0][5] & LMP_EDR_ESCO_2M)
#define lmp_ext_inq_capable(dev) ((dev)->features[0][6] & LMP_EXT_INQ)
#define lmp_le_br_capable(dev) (!!((dev)->features[0][6] & LMP_SIMUL_LE_BR))
#define lmp_ssp_capable(dev) ((dev)->features[0][6] & LMP_SIMPLE_PAIR)
#define lmp_no_flush_capable(dev) ((dev)->features[0][6] & LMP_NO_FLUSH)
#define lmp_lsto_capable(dev) ((dev)->features[0][7] & LMP_LSTO)
#define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR)
#define lmp_ext_feat_capable(dev) ((dev)->features[0][7] & LMP_EXTFEATURES)
#define lmp_transp_capable(dev) ((dev)->features[0][2] & LMP_TRANSPARENT)
#define lmp_edr_2m_capable(dev) ((dev)->features[0][3] & LMP_EDR_2M)
#define lmp_edr_3m_capable(dev) ((dev)->features[0][3] & LMP_EDR_3M)
#define lmp_edr_3slot_capable(dev) ((dev)->features[0][4] & LMP_EDR_3SLOT)
#define lmp_edr_5slot_capable(dev) ((dev)->features[0][5] & LMP_EDR_5SLOT)
/* ----- Extended LMP capabilities ----- */
#define lmp_cpb_central_capable(dev) ((dev)->features[2][0] & LMP_CPB_CENTRAL)
#define lmp_cpb_peripheral_capable(dev) ((dev)->features[2][0] & LMP_CPB_PERIPHERAL)
#define lmp_sync_train_capable(dev) ((dev)->features[2][0] & LMP_SYNC_TRAIN)
#define lmp_sync_scan_capable(dev) ((dev)->features[2][0] & LMP_SYNC_SCAN)
#define lmp_sc_capable(dev) ((dev)->features[2][1] & LMP_SC)
#define lmp_ping_capable(dev) ((dev)->features[2][1] & LMP_PING)
/* ----- Host capabilities ----- */
#define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP)
#define lmp_host_sc_capable(dev) ((dev)->features[1][0] & LMP_HOST_SC)
#define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE))
#define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
#define hdev_is_powered(dev) (test_bit(HCI_UP, &(dev)->flags) && \
!hci_dev_test_flag(dev, HCI_AUTO_OFF))
#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \
hci_dev_test_flag(dev, HCI_SC_ENABLED))
#define rpa_valid(dev) (bacmp(&dev->rpa, BDADDR_ANY) && \
!hci_dev_test_flag(dev, HCI_RPA_EXPIRED))
#define adv_rpa_valid(adv) (bacmp(&adv->random_addr, BDADDR_ANY) && \
!adv->rpa_expired)
#define le_enabled(dev) (lmp_le_capable(dev) && \
hci_dev_test_flag(dev, HCI_LE_ENABLED))
#define scan_1m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_1M) || \
((dev)->le_rx_def_phys & HCI_LE_SET_PHY_1M))
#define le_2m_capable(dev) (((dev)->le_features[1] & HCI_LE_PHY_2M))
#define scan_2m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_2M) || \
((dev)->le_rx_def_phys & HCI_LE_SET_PHY_2M))
#define le_coded_capable(dev) (((dev)->le_features[1] & HCI_LE_PHY_CODED) && \
!hci_test_quirk((dev), \
HCI_QUIRK_BROKEN_LE_CODED))
#define scan_coded(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_CODED) || \
((dev)->le_rx_def_phys & HCI_LE_SET_PHY_CODED))
#define ll_privacy_capable(dev) ((dev)->le_features[0] & HCI_LE_LL_PRIVACY)
#define ll_privacy_enabled(dev) (le_enabled(dev) && ll_privacy_capable(dev))
#define privacy_mode_capable(dev) (ll_privacy_capable(dev) && \
((dev)->commands[39] & 0x04))
#define read_key_size_capable(dev) \
((dev)->commands[20] & 0x10 && \
!hci_test_quirk((dev), HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE))
#define read_voice_setting_capable(dev) \
((dev)->commands[9] & 0x04 && \
!hci_test_quirk((dev), HCI_QUIRK_BROKEN_READ_VOICE_SETTING))
/* Use enhanced synchronous connection if command is supported and its quirk
* has not been set.
*/
#define enhanced_sync_conn_capable(dev) \
(((dev)->commands[29] & 0x08) && \
!hci_test_quirk((dev), HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN))
/* Use ext scanning if set ext scan param and ext scan enable is supported */
#define use_ext_scan(dev) (((dev)->commands[37] & 0x20) && \
((dev)->commands[37] & 0x40) && \
!hci_test_quirk((dev), HCI_QUIRK_BROKEN_EXT_SCAN))
/* Use ext create connection if command is supported */
#define use_ext_conn(dev) (((dev)->commands[37] & 0x80) && \
!hci_test_quirk((dev), HCI_QUIRK_BROKEN_EXT_CREATE_CONN))
/* Extended advertising support */
#define ext_adv_capable(dev) (((dev)->le_features[1] & HCI_LE_EXT_ADV))
/* Maximum advertising length */
#define max_adv_len(dev) \
(ext_adv_capable(dev) ? HCI_MAX_EXT_AD_LENGTH : HCI_MAX_AD_LENGTH)
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E page 1789:
*
* C24: Mandatory if the LE Controller supports Connection State and either
* LE Feature (LL Privacy) or LE Feature (Extended Advertising) is supported
*/
#define use_enhanced_conn_complete(dev) ((ll_privacy_capable(dev) || \
ext_adv_capable(dev)) && \
!hci_test_quirk((dev), \
HCI_QUIRK_BROKEN_EXT_CREATE_CONN))
/* Periodic advertising support */
#define per_adv_capable(dev) (((dev)->le_features[1] & HCI_LE_PERIODIC_ADV))
/* CIS Master/Slave and BIS support */
#define iso_capable(dev) (cis_capable(dev) || bis_capable(dev))
#define iso_enabled(dev) (le_enabled(dev) && iso_capable(dev))
#define cis_capable(dev) \
(cis_central_capable(dev) || cis_peripheral_capable(dev))
#define cis_enabled(dev) (le_enabled(dev) && cis_capable(dev))
#define cis_central_capable(dev) \
((dev)->le_features[3] & HCI_LE_CIS_CENTRAL)
#define cis_central_enabled(dev) \
(le_enabled(dev) && cis_central_capable(dev))
#define cis_peripheral_capable(dev) \
((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL)
#define cis_peripheral_enabled(dev) \
(le_enabled(dev) && cis_peripheral_capable(dev))
#define bis_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_BROADCASTER)
#define bis_enabled(dev) (le_enabled(dev) && bis_capable(dev))
#define sync_recv_capable(dev) \
((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
#define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev))
#define past_sender_capable(dev) \
((dev)->le_features[3] & HCI_LE_PAST_SENDER)
#define past_receiver_capable(dev) \
((dev)->le_features[3] & HCI_LE_PAST_RECEIVER)
#define past_capable(dev) \
(past_sender_capable(dev) || past_receiver_capable(dev))
#define past_sender_enabled(dev) \
(le_enabled(dev) && past_sender_capable(dev))
#define past_receiver_enabled(dev) \
(le_enabled(dev) && past_receiver_capable(dev))
#define past_enabled(dev) \
(past_sender_enabled(dev) || past_receiver_enabled(dev))
#define ll_ext_feature_capable(dev) \
((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE)
/* Channel sounding support */
#define le_cs_capable(dev) \
((dev)->le_features[5] & HCI_LE_CS)
#define le_cs_host_capable(dev) \
((dev)->le_features[5] & HCI_LE_CS_HOST)
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
/* ----- HCI protocols ----- */
#define HCI_PROTO_DEFER 0x01
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 type, __u8 *flags)
{
switch (type) {
case ACL_LINK:
return l2cap_connect_ind(hdev, bdaddr);
case SCO_LINK:
case ESCO_LINK:
return sco_connect_ind(hdev, bdaddr, flags);
case CIS_LINK:
case BIS_LINK:
case PA_LINK:
return iso_connect_ind(hdev, bdaddr, flags);
default:
BT_ERR("unknown link type %d", type);
return -EINVAL;
}
}
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
{
if (conn->type != ACL_LINK && conn->type != LE_LINK)
return HCI_ERROR_REMOTE_USER_TERM;
return l2cap_disconn_ind(conn);
}
/* ----- HCI callbacks ----- */
struct hci_cb {
struct list_head list;
char *name;
void (*connect_cfm) (struct hci_conn *conn, __u8 status);
void (*disconn_cfm) (struct hci_conn *conn, __u8 status);
void (*security_cfm) (struct hci_conn *conn, __u8 status,
__u8 encrypt);
void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
};
static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->connect_cfm)
cb->connect_cfm(conn, status);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->connect_cfm_cb)
conn->connect_cfm_cb(conn, status);
}
static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->disconn_cfm)
cb->disconn_cfm(conn, reason);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->disconn_cfm_cb)
conn->disconn_cfm_cb(conn, reason);
}
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{
struct hci_cb *cb;
__u8 encrypt;
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return;
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
}
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
{
struct hci_cb *cb;
__u8 encrypt;
if (conn->state == BT_CONFIG) {
if (!status)
conn->state = BT_CONNECTED;
hci_connect_cfm(conn, status);
hci_conn_drop(conn);
return;
}
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
encrypt = 0x00;
else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
encrypt = 0x02;
else
encrypt = 0x01;
if (!status) {
if (conn->sec_level == BT_SECURITY_SDP)
conn->sec_level = BT_SECURITY_LOW;
if (conn->pending_sec_level > conn->sec_level)
conn->sec_level = conn->pending_sec_level;
}
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
}
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->key_change_cfm)
cb->key_change_cfm(conn, status);
}
mutex_unlock(&hci_cb_list_lock);
}
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
__u8 role)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->role_switch_cfm)
cb->role_switch_cfm(conn, status, role);
}
mutex_unlock(&hci_cb_list_lock);
}
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
{
if (addr_type != ADDR_LE_DEV_RANDOM)
return false;
if ((bdaddr->b[5] & 0xc0) == 0x40)
return true;
return false;
}
static inline bool hci_is_identity_address(bdaddr_t *addr, u8 addr_type)
{
if (addr_type == ADDR_LE_DEV_PUBLIC)
return true;
/* Check for Random Static address type */
if ((addr->b[5] & 0xc0) == 0xc0)
return true;
return false;
}
static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 addr_type)
{
if (!hci_bdaddr_is_rpa(bdaddr, addr_type))
return NULL;
return hci_find_irk_by_rpa(hdev, bdaddr);
}
static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
u16 max_latency;
if (min > max) {
BT_WARN("min %d > max %d", min, max);
return -EINVAL;
}
if (min < 6) {
BT_WARN("min %d < 6", min);
return -EINVAL;
}
if (max > 3200) {
BT_WARN("max %d > 3200", max);
return -EINVAL;
}
if (to_multiplier < 10) {
BT_WARN("to_multiplier %d < 10", to_multiplier);
return -EINVAL;
}
if (to_multiplier > 3200) {
BT_WARN("to_multiplier %d > 3200", to_multiplier);
return -EINVAL;
}
if (max >= to_multiplier * 8) {
BT_WARN("max %d >= to_multiplier %d * 8", max, to_multiplier);
return -EINVAL;
}
max_latency = (to_multiplier * 4 / max) - 1;
if (latency > 499) {
BT_WARN("latency %d > 499", latency);
return -EINVAL;
}
if (latency > max_latency) {
BT_WARN("latency %d > max_latency %d", latency, max_latency);
return -EINVAL;
}
return 0;
}
int hci_register_cb(struct hci_cb *hcb);
int hci_unregister_cb(struct hci_cb *hcb);
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param);
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
u32 hci_conn_get_phy(struct hci_conn *conn);
int hci_conn_set_phy(struct hci_conn *conn, u32 phys);
/* ----- HCI Sockets ----- */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
int flag, struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
void hci_send_monitor_ctrl_event(struct hci_dev *hdev, u16 event,
void *data, u16 data_len, ktime_t tstamp,
int flag, struct sock *skip_sk);
void hci_sock_dev_event(struct hci_dev *hdev, int event);
#define HCI_MGMT_VAR_LEN BIT(0)
#define HCI_MGMT_NO_HDEV BIT(1)
#define HCI_MGMT_UNTRUSTED BIT(2)
#define HCI_MGMT_UNCONFIGURED BIT(3)
#define HCI_MGMT_HDEV_OPTIONAL BIT(4)
struct hci_mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
size_t data_len;
unsigned long flags;
};
struct hci_mgmt_chan {
struct list_head list;
unsigned short channel;
size_t handler_count;
const struct hci_mgmt_handler *handlers;
void (*hdev_init) (struct sock *sk, struct hci_dev *hdev);
};
int hci_mgmt_chan_register(struct hci_mgmt_chan *c);
void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
/* Management interface */
#define DISCOV_TYPE_BREDR (BIT(BDADDR_BREDR))
#define DISCOV_TYPE_LE (BIT(BDADDR_LE_PUBLIC) | \
BIT(BDADDR_LE_RANDOM))
#define DISCOV_TYPE_INTERLEAVED (BIT(BDADDR_BREDR) | \
BIT(BDADDR_LE_PUBLIC) | \
BIT(BDADDR_LE_RANDOM))
/* These LE scan and inquiry parameters were chosen according to LE General
* Discovery Procedure specification.
*/
#define DISCOV_LE_SCAN_WIN 0x0012 /* 11.25 msec */
#define DISCOV_LE_SCAN_INT 0x0012 /* 11.25 msec */
#define DISCOV_LE_SCAN_INT_FAST 0x0060 /* 60 msec */
#define DISCOV_LE_SCAN_WIN_FAST 0x0030 /* 30 msec */
#define DISCOV_LE_SCAN_INT_CONN 0x0060 /* 60 msec */
#define DISCOV_LE_SCAN_WIN_CONN 0x0060 /* 60 msec */
#define DISCOV_LE_SCAN_INT_SLOW1 0x0800 /* 1.28 sec */
#define DISCOV_LE_SCAN_WIN_SLOW1 0x0012 /* 11.25 msec */
#define DISCOV_LE_SCAN_INT_SLOW2 0x1000 /* 2.56 sec */
#define DISCOV_LE_SCAN_WIN_SLOW2 0x0024 /* 22.5 msec */
#define DISCOV_CODED_SCAN_INT_FAST 0x0120 /* 180 msec */
#define DISCOV_CODED_SCAN_WIN_FAST 0x0090 /* 90 msec */
#define DISCOV_CODED_SCAN_INT_SLOW1 0x1800 /* 3.84 sec */
#define DISCOV_CODED_SCAN_WIN_SLOW1 0x0036 /* 33.75 msec */
#define DISCOV_CODED_SCAN_INT_SLOW2 0x3000 /* 7.68 sec */
#define DISCOV_CODED_SCAN_WIN_SLOW2 0x006c /* 67.5 msec */
#define DISCOV_LE_TIMEOUT 10240 /* msec */
#define DISCOV_INTERLEAVED_TIMEOUT 5120 /* msec */
#define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
#define DISCOV_BREDR_INQUIRY_LEN 0x08
#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */
#define DISCOV_LE_FAST_ADV_INT_MIN 0x00A0 /* 100 msec */
#define DISCOV_LE_FAST_ADV_INT_MAX 0x00F0 /* 150 msec */
#define DISCOV_LE_PER_ADV_INT_MIN 0x00A0 /* 200 msec */
#define DISCOV_LE_PER_ADV_INT_MAX 0x00A0 /* 200 msec */
#define DISCOV_LE_ADV_MESH_MIN 0x00A0 /* 100 msec */
#define DISCOV_LE_ADV_MESH_MAX 0x00A0 /* 100 msec */
#define INTERVAL_TO_MS(x) (((x) * 10) / 0x10)
#define NAME_RESOLVE_DURATION msecs_to_jiffies(10240) /* 10.24 sec */
void mgmt_fill_version_info(void *ver);
int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev);
void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
void mgmt_power_on(struct hci_dev *hdev, int err);
void __mgmt_power_off(struct hci_dev *hdev);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 *name, u8 name_len);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected);
void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn,
u8 status);
void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u32 value,
u8 confirm_hint);
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type);
int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status);
int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u32 passkey,
u8 entered);
void mgmt_auth_failed(struct hci_conn *conn, u8 status);
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len,
u64 instant);
void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
void mgmt_suspending(struct hci_dev *hdev, u8 state);
void mgmt_resuming(struct hci_dev *hdev, u8 reason, bdaddr_t *bdaddr,
u8 addr_type);
bool mgmt_powering_down(struct hci_dev *hdev);
void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent);
void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
bool persistent);
void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 bdaddr_type, u8 store_hint, u16 min_interval,
u16 max_interval, u16 latency, u16 timeout);
void mgmt_smp_complete(struct hci_conn *conn, bool complete);
bool mgmt_get_connectable(struct hci_dev *hdev);
u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev);
void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
u8 instance);
void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
u8 instance);
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
bdaddr_t *bdaddr, u8 addr_type);
int hci_abort_conn(struct hci_conn *conn, u8 reason);
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
__u8 ltk[16], __u8 key_size);
void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 *bdaddr_type);
#define SCO_AIRMODE_MASK 0x0003
#define SCO_AIRMODE_CVSD 0x0000
#define SCO_AIRMODE_TRANSP 0x0003
#define LOCAL_CODEC_ACL_MASK BIT(0)
#define LOCAL_CODEC_SCO_MASK BIT(1)
#define TRANSPORT_TYPE_MAX 0x04
#endif /* __HCI_CORE_H */
]
|
| 82/2 |
2026/05/22 22:59 |
llm |
expert |
2m
Model:
Tokens:
input: 110246
output: 47
thoughts: 309
Thoughts: **Analyzing `hci_dev_clear_volatile_flags` and Its Implications**
Okay, so looking at the definition of `hci_dev_clear_volatile_flags`, specifically `hci_dev_clear_volatile_flags`, it's a macro that's clearing a few specific flags: `HCI_LE_SCAN`, `HCI_LE_ADV`, `HCI_LL_RPA_RESOLUTION`, `HCI_PERIODIC_INQ`, and `HCI_QUALITY_REPORT`. Crucially, it *doesn't* clear `HCI_BREDR_ENABLED`. This is interesting.
This immediately makes me think, "Hmm, so `hci_connect_acl` could potentially succeed even if the HCI device, `hdev`, has been closed." That's a definite possibility given that `hci_dev_clear_volatile_flags` doesn't cover all the relevant states.
The next thing that jumps to mind is whether the code checks if the device is actually up. If `hdev` is closed, the `test_bit(HCI_UP, &hdev->flags)` will be false. The question is: does `hci_connect_acl` itself do that check? It doesn't seem like it does. Ok, what about the synchronous version, `hci_connect_acl_sync`? It calls `hci_cmd_sync_queue_once`. I'll have to investigate that function to see if it performs the necessary checks.
|
| 83/2 |
2026/05/22 23:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c IncludeLines:true Name:hci_cmd_sync_queue_once]
Results: map[SourceCode: 774: /* Queue HCI command entry once:
775: *
776: * - Lookup if an entry already exist and only if it doesn't creates a new entry
777: * and queue it.
778: */
779: int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
780: void *data, hci_cmd_sync_work_destroy_t destroy)
781: {
782: if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
783: return 0;
784:
785: return hci_cmd_sync_queue(hdev, func, data, destroy);
786: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 84/2 |
2026/05/22 23:01 |
llm |
expert |
0m
Model:
Tokens:
input: 111175
output: 45
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating the `hci_cmd_sync_queue`**
Alright, let's dive in. My immediate thought is to check out the `hci_cmd_sync_queue`. Considering what we're working on, that's the logical starting point. It's where the synchronous HCI command flow resides. I need to get a handle on what's going on within that queue to understand the current bottleneck or any potential issues in the command processing pipeline. Depending on what I find, the next steps are clear. Let's see what's happening.
|
| 85/2 |
2026/05/22 23:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_sync.c IncludeLines:true Name:hci_cmd_sync_queue]
Results: map[SourceCode: 735: /* Queue HCI command:
736: *
737: * - hdev must be running
738: */
739: int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
740: void *data, hci_cmd_sync_work_destroy_t destroy)
741: {
742: /* Only queue command if hdev is running which means it had been opened
743: * and is either on init phase or is already up.
744: */
745: if (!test_bit(HCI_RUNNING, &hdev->flags))
746: return -ENETDOWN;
747:
748: return hci_cmd_sync_submit(hdev, func, data, destroy);
749: }
SourceFile:net/bluetooth/hci_sync.c]
|
| 86/2 |
2026/05/22 23:01 |
llm |
expert |
0m
Model:
Tokens:
input: 111649
output: 22
thoughts: 617
Thoughts: **My Analysis of the Bluetooth Driver Bug**
Okay, let me break this down. My initial thought was that there *might* be a memory leak after `hci_dev_close_sync` if `sco_connect` executed. But no, because `hci_cmd_sync_queue` quickly bails out with `-ENETDOWN` when the device is closed, thanks to `test_bit(HCI_RUNNING, &hdev->flags)` and the clearing of `HCI_RUNNING` during the close process. This then cascades down to other connection functions, and the connection gets deleted, so no leak.
Essentially, this narrows the issue down to a harmless `WARN_ONCE` resulting from a race condition between `drain_workqueue` and `queue_delayed_work`. The key thing is that this warning itself is not a security vulnerability.
Now, let's assess the impact. It's not exploitable, it won't cause a denial of service, and it's not accessible to unprivileged processes, user namespaces, or any remote attack vectors. The only trigger I can identify is a Peripheral Trigger, specifically a USB disconnect event of a Bluetooth dongle during a connection attempt.
However, I need to refine that. While an unprivileged user can't *directly* trigger a USB disconnect, a *malicious peripheral* could certainly simulate one. So, Peripheral Trigger *is* True.
Wait, are there other ways to close the device? Can an unprivileged user trigger `hci_dev_do_close`? I looked at unbinding drivers, using `rfkill`, `hciconfig down`, and all of those required root, `netdev` group, or `CAP_NET_ADMIN` respectively. So, that avenue is blocked.
Finally, I need to ensure that `drain_workqueue` is only being called from appropriate locations within the `net/bluetooth` code.
|
| 87/2 |
2026/05/22 23:01 |
tool |
grepper |
0m
Error:
Args: map[Expression:drain_workqueue.*net/bluetooth]
Results:
|
| 88/2 |
2026/05/22 23:01 |
llm |
expert |
0m
Model:
Tokens:
input: 112710
output: 21
thoughts: 32
Thoughts: Here's my thought process, as if I were the one thinking it:
**Digging into that Bluetooth Workqueue Call**
Okay, I need to remind myself. Didn't I *already* grep for `drain_workqueue` within the `net/bluetooth` directory? I swear I did that earlier⦠yeah, let me see⦠focusing, focusing⦠Yep, I remember it now. I recall it *only* showing up in `hci_dev_close_sync`. That's important. It narrows down the scope of where I need to focus my investigation. That specific function is likely the primary location of the workqueue drainage, which helps me eliminate a bunch of other potential code paths to look into. Knowing this is the sole result from my earlier search helps to confirm that I was on the right track initially. Good!
|
| 89/2 |
2026/05/22 23:01 |
tool |
grepper |
0m
Args: map[Expression:drain_workqueue.*hdev]
Results: map[Output:net/bluetooth/hci_core.c=534=static int hci_dev_do_reset(struct hci_dev *hdev)
--
net/bluetooth/hci_core.c-562- */
net/bluetooth/hci_core.c:563: drain_workqueue(hdev->workqueue);
net/bluetooth/hci_core.c-564-
--
net/bluetooth/hci_sync.c=5284=int hci_dev_close_sync(struct hci_dev *hdev)
--
net/bluetooth/hci_sync.c-5343- */
net/bluetooth/hci_sync.c:5344: drain_workqueue(hdev->workqueue);
net/bluetooth/hci_sync.c-5345-
]
|
| 90/2 |
2026/05/22 23:01 |
llm |
expert |
0m
Model:
Tokens:
input: 113143
output: 45
thoughts: 40
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into the Reset Sequence**
Right, so `hci_dev_do_reset` is the function we need to examine. And I see that it includes a crucial step: `drain_workqueue(hdev->workqueue)`. That's interesting, and potentially significant. My immediate thought is, "Okay, why is the workqueue being drained *specifically* during the reset process?" What implications does this have on pending operations and the overall state transition of the HCI device? This is likely a key point in understanding how the reset interacts with ongoing tasks. Let's dig deeper into the reset function to see the context.
|
| 91/2 |
2026/05/22 23:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hci_core.c IncludeLines:true Name:hci_dev_do_reset]
Results: map[SourceCode: 534: static int hci_dev_do_reset(struct hci_dev *hdev)
535: {
536: int ret;
537:
538: BT_DBG("%s %p", hdev->name, hdev);
539:
540: hci_req_sync_lock(hdev);
541:
542: /* Drop queues */
543: skb_queue_purge(&hdev->rx_q);
544: skb_queue_purge(&hdev->cmd_q);
545:
546: /* Cancel these to avoid queueing non-chained pending work */
547: hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
548: /* Wait for
549: *
550: * if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
551: * queue_delayed_work(&hdev->{cmd,ncmd}_timer)
552: *
553: * inside RCU section to see the flag or complete scheduling.
554: */
555: synchronize_rcu();
556: /* Explicitly cancel works in case scheduled after setting the flag. */
557: cancel_delayed_work(&hdev->cmd_timer);
558: cancel_delayed_work(&hdev->ncmd_timer);
559:
560: /* Avoid potential lockdep warnings from the *_flush() calls by
561: * ensuring the workqueue is empty up front.
562: */
563: drain_workqueue(hdev->workqueue);
564:
565: hci_dev_lock(hdev);
566: hci_inquiry_cache_flush(hdev);
567: hci_conn_hash_flush(hdev);
568: hci_dev_unlock(hdev);
569:
570: if (hdev->flush)
571: hdev->flush(hdev);
572:
573: hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
574:
575: atomic_set(&hdev->cmd_cnt, 1);
576: hdev->acl_cnt = 0;
577: hdev->sco_cnt = 0;
578: hdev->le_cnt = 0;
579: hdev->iso_cnt = 0;
580:
581: ret = hci_reset_sync(hdev);
582:
583: hci_req_sync_unlock(hdev);
584: return ret;
585: }
SourceFile:net/bluetooth/hci_core.c]
|
| 92/2 |
2026/05/22 23:01 |
llm |
expert |
0m
Model:
Tokens:
input: 114194
output: 66
thoughts: 802
Thoughts: **My Analysis of the HCI Device Reset Race Condition**
Okay, let's break this down. My initial understanding is that `hci_dev_do_reset` is called indirectly through `hci_error_reset`, which is a work item (`hdev->error_reset`) managed by the workqueue. This work item gets queued either due to a hardware error (`hci_hw_error`) or a command timeout (`hci_cmd_timeout`). So, a hardware glitch or a sluggish peripheral connection can kick this process off. The heart of the problem seems to be a race condition between `drain_workqueue` (when resetting or closing the device) and `sco_connect` which attempts to establish a connection. The consequence? A `WARN_ONCE`.
My analysis suggests that this race is triggered when `drain_workqueue` is called and sets `__WQ_DRAINING` flag, but then a concurrent attempt is made to queue a work item, specifically during a dropped connection (for instance, when `sco_connect()` is executed). While the work item isn't queued, `__queue_work` issues a `WARN_ONCE`, which, in itself, is harmless in a production environment (unless panic-on-warn is enabled). It looks like the connections will be flushed anyway after the race in `hci_conn_hash_flush()`. So, I'm not seeing any exploitable memory corruption or leaks here.
I need to confirm whether an unprivileged user can trigger this. If so, they could potentially trigger a command timeout? An unprivileged user can't directly send HCI commands. So what about a command that times out? My initial thought is to connect via a socket, sending an HCI command (like `HCI_OP_CREATE_CONN`). But after a deeper review, I confirmed this is not possible. The `hci_connect_acl` does send `HCI_OP_CREATE_CONN`, but the timeout is handled by `hci_conn_timeout`, which aborts the connection. This does not trigger `hci_cmd_timeout`. The `hci_cmd_timeout` is triggered when the controller does not send a Command Complete or Command Status event. And that's a hardware, firmware issue, or something malicious coming from the peripheral.
So, it's definitely a **Peripheral Trigger**. This aligns with my understanding, and there is no way for a local unprivileged user to do this because `HCIDEVDOWN` and `rfkill` access requires `CAP_NET_ADMIN` privileges or root. It can be caused by the peripheral disconnecting, causing an error, or a command timeout.
Let me double-check for other `drain_workqueue` entrypoints: nope, just `hci_dev_do_reset` and `hci_dev_close_sync`.
Final Assessment:
* Exploitable: False
* Denial Of Service: False
* Unprivileged: False
* UserNamespace: False
* VMGuestTrigger: False
* VMHostTrigger: False
* NetworkTrigger: False
* RemoteTrigger: False
* PeripheralTrigger: True
* FilesystemTrigger: False
|
| 93/2 |
2026/05/22 23:02 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 94/2 |
2026/05/22 23:02 |
llm |
expert |
0m
Model:
Tokens:
input: 115800
output: 670
thoughts: 13
Thoughts: Okay, here's the summary, rendered in the first person, assuming the user is an expert:
**Final Reasoning and Conclusion**
Alright, the data's in, the analyses are done, and the findings are conclusive. I'm now at the point of finalizing my reasoning. There's no further processing needed, all parameters have been accounted for, and the implications of this outcome are clear. This section is where I'll connect the dots and present my definitive interpretation of the results, drawing on my deep understanding of the subject matter to articulate why these specific outcomes are what they are. I'll need to clarify the scope, the limits, and the most probable inferences that can be drawn with confidence.
|
| 95/1 |
2026/05/22 23:02 |
action |
format-explanation |
0m
Results: map[Explanation:The bug is a race condition between `drain_workqueue(hdev->workqueue)` and
`queue_delayed_work()` in the Bluetooth subsystem.
When a Bluetooth device is being closed (e.g., via `HCIDEVDOWN` ioctl,
`/dev/rfkill`, or device unregistration) or reset (due to a hardware error or
command timeout), the kernel calls `hci_dev_close_sync()` or
`hci_dev_do_reset()`. Both of these functions call
`drain_workqueue(hdev->workqueue)` without holding the `hci_dev_lock(hdev)`
mutex.
The `drain_workqueue()` function sets the `__WQ_DRAINING` flag on the workqueue
to prevent new works from being queued while it waits for the existing ones to
finish.
Concurrently, a process might be trying to establish a new SCO connection via
`sco_connect()`, which holds `hci_dev_lock(hdev)`. If the connection setup fails
(for example, if `hci_conn_add_unset()` fails because `hdev->sco_pkts` is 0 or
ID allocation fails), it drops the newly created ACL connection by calling
`hci_conn_drop()`. When the connection's reference count reaches 0,
`hci_conn_drop()` attempts to queue a disconnect work (`conn->disc_work`) on
`hdev->workqueue`.
Because `drain_workqueue()` is running concurrently and has set the
`__WQ_DRAINING` flag, `queue_delayed_work()` detects this and triggers a
`WARN_ONCE` in `__queue_work()`.
**Security Impact Assessment:**
* **Exploitable:** **False**. The warning is triggered because the work is
rejected by the draining workqueue. However, this is harmless because the device
is being closed or reset, and `hci_conn_hash_flush()` will soon be called (after
acquiring `hci_dev_lock`) to forcefully delete all connections and clean up
their state. There is no memory corruption, use-after-free, or memory leak.
* **Denial Of Service:** **False**. The bug only triggers a `WARN_ONCE`, which
does not halt the kernel or cause a panic in production builds (unless
`panic_on_warn` is explicitly enabled). It does not lead to deadlocks or
resource exhaustion.
* **Accessible From Unprivileged Processes / User Namespaces:** **False**.
Triggering the device close path requires privileges. The `HCIDEVDOWN` ioctl
requires `CAP_NET_ADMIN` in the initial namespace, and writing to `/dev/rfkill`
requires root privileges (or membership in specific privileged groups like
`netdev`). It cannot be reached from an unprivileged user namespace.
* **Peripheral Trigger:** **True**. A malicious or malfunctioning Bluetooth
peripheral (e.g., a USB dongle) can trigger this bug by disconnecting from the
system or causing a hardware error/command timeout exactly when a connection is
being established, forcing the kernel into the `hci_dev_close_sync()` or
`hci_dev_do_reset()` paths.]
|