| 0/0 |
2026/05/25 22:58 |
flow |
assessment-security |
1h09m
Results: map[DenialOfService:true Explanation:The bug report describes a hung task situation where multiple threads are
blocked, leading to a system deadlock.
### Analysis of the Deadlock
1. **`khidpd` (PID 5507)** is executing `hidp_session_thread` and calls
`l2cap_unregister_user`.
- It acquires `hdev->lock` inside `l2cap_unregister_user`.
- It then calls the user's `remove` callback, which is `hidp_session_remove`.
- `hidp_session_remove` acquires `hidp_session_sem` for WRITE.
- It then calls `cancel_work_sync(&session->dev_init)` and blocks, waiting
for the `dev_init` work (`hidp_session_dev_work`) to finish.
2. **`syz.0.54` (PID 5547)** is executing `l2cap_sock_connect` ->
`l2cap_chan_connect`.
- It attempts to acquire `hdev->lock` but is blocked because `khidpd` is
already holding it.
3. **`syz.1.55` to `syz.7.61`** are executing `hidp_session_find`.
- They attempt to acquire `hidp_session_sem` for READ but are blocked because
`khidpd` is holding it for WRITE.
4. **`kworker/0:3`** is executing the `dev_init` work (`hidp_session_dev_work`).
- This work calls `hidp_session_dev_add`, which registers the HID device
(`hid_add_device`).
- Device registration triggers driver probing and uevents. If any part of
this device registration path attempts to acquire `hdev->lock` (e.g., via
some indirect L2CAP or HCI interaction) or is blocked behind another lock
(like `rtnl_mutex` which is currently held by `kworker/u4:4` and heavily
contended), it will deadlock because `khidpd` is holding `hdev->lock` and
waiting for this exact work to finish.
This is a classic ABBA deadlock or workqueue synchronization hang where a thread
holding a lock (`hdev->lock`) waits for a work item to finish, but the work item
requires the same lock (or is blocked behind something else that requires it) to
complete.
### Security Impact Assessment
- **Exploitable:** False. This is a synchronization issue (deadlock) resulting
in threads hanging in the `D` state. It does not lead to memory corruption,
use-after-free, or out-of-bounds accesses that could be leveraged for privilege
escalation or arbitrary code execution.
- **Denial Of Service:** True. The deadlock permanently blocks critical
Bluetooth locks (`hdev->lock` and `hidp_session_sem`), which will hang the
Bluetooth subsystem and any processes attempting to interact with it,
effectively causing a local denial of service.
- **Accessible From Unprivileged Processes:** False. The bug is triggered by
creating a HIDP session via the `HIDPCONNADD` ioctl on a Bluetooth socket. The
`do_hidp_sock_ioctl` function explicitly checks for global `CAP_NET_ADMIN` using
`capable(CAP_NET_ADMIN)`. Therefore, a normal unprivileged user cannot reach the
vulnerable code path.
- **Accessible From User Namespaces:** False. Because the code uses
`capable(CAP_NET_ADMIN)` instead of `ns_capable()`, the capability must be held
in the initial (global) user namespace. Having `CAP_NET_ADMIN` inside a
restricted user namespace is not sufficient to bypass this check.
- **Remote Trigger:** True. While a local unprivileged user cannot trigger this
via ioctls, a remote attacker acting as a malicious Bluetooth HID device can.
When a paired remote HID device connects, the host's `bluetoothd` (running as
root with `CAP_NET_ADMIN`) automatically accepts the connection and issues the
`HIDPCONNADD` ioctl. If the remote device then immediately drops the L2CAP
connection at the right moment while the `dev_init` work is still running, it
will invoke `hidp_session_remove` and trigger the deadlock remotely.
- **Peripheral Trigger:** False. This is triggered via Bluetooth network
protocols, not by physically plugging in a USB or similar peripheral device.
- **VM Guest/Host Trigger:** False. This is specific to the Bluetooth subsystem
and not related to virtualization boundaries or paravirtualized devices. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/25 22:58 |
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)) {}
// syz_init_net_socket$bt_l2cap arguments: [
// fam: const = 0x1f (8 bytes)
// type: bt_l2cap_type = 0x3 (8 bytes)
// proto: const = 0x0 (8 bytes)
// ]
// returns sock_bt_l2cap
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=SOCK_RAW*/3, /*proto=*/0);
if (res != -1)
r[0] = res;
// connect$bt_l2cap arguments: [
// fd: sock_bt_l2cap (resource)
// addr: ptr[in, sockaddr_l2] {
// sockaddr_l2 {
// l2_family: const = 0x1f (2 bytes)
// l2_psm: int16 = 0x0 (2 bytes)
// l2_bdaddr: union bdaddr_t {
// fixed: bdaddr_fixed {
// b: buffer: {aa aa aa aa aa} (length 0x5)
// a: int8 = 0x10 (1 bytes)
// }
// }
// l2_cid: int16 = 0x7ff (2 bytes)
// l2_bdaddr_type: bdaddr_type = 0x0 (1 bytes)
// pad = 0x0 (1 bytes)
// }
// }
// addrlen: len = 0xe (8 bytes)
// ]
*(uint16_t*)0x200000000080 = 0x1f;
*(uint16_t*)0x200000000082 = 0;
memset((void*)0x200000000084, 170, 5);
*(uint8_t*)0x200000000089 = 0x10;
*(uint16_t*)0x20000000008a = 0x7ff;
*(uint8_t*)0x20000000008c = 0;
syscall(__NR_connect, /*fd=*/r[0], /*addr=*/0x200000000080ul, /*addrlen=*/0xeul);
// syz_init_net_socket$bt_hidp arguments: [
// fam: const = 0x1f (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: const = 0x6 (8 bytes)
// ]
// returns sock_bt_hidp
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=*/3, /*proto=*/6);
if (res != -1)
r[1] = res;
// ioctl$sock_bt_hidp_HIDPCONNADD arguments: [
// fd: sock_bt_hidp (resource)
// cmd: const = 0x400448c8 (4 bytes)
// arg: ptr[in, hidp_connadd_req] {
// hidp_connadd_req {
// ctrlsk: sock (resource)
// intrsk: sock (resource)
// parser: int16 = 0x2 (2 bytes)
// rdsize: len = 0xfffffffffffffea4 (2 bytes)
// pad = 0x0 (4 bytes)
// rddata: ptr[in, buffer] {
// buffer: {22 ad 35} (length 0x3)
// }
// country: int8 = 0xb8 (1 bytes)
// subclas: int8 = 0x88 (1 bytes)
// vendor: int16 = 0xd (2 bytes)
// product: int16 = 0x2f19 (2 bytes)
// version: int16 = 0xb514 (2 bytes)
// flags: hidp_connadd_flags = 0x2 (4 bytes)
// idleto: int32 = 0x2 (4 bytes)
// name: buffer: {73 79 7a 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x80)
// }
// }
// ]
*(uint32_t*)0x2000000001c0 = r[0];
*(uint32_t*)0x2000000001c4 = r[0];
*(uint16_t*)0x2000000001c8 = 2;
*(uint16_t*)0x2000000001ca = 0xfea4;
*(uint64_t*)0x2000000001d0 = 0x200000000500;
memcpy((void*)0x200000000500, "\x22\xad\x35", 3);
*(uint8_t*)0x2000000001d8 = 0xb8;
*(uint8_t*)0x2000000001d9 = 0x88;
*(uint16_t*)0x2000000001da = 0xd;
*(uint16_t*)0x2000000001dc = 0x2f19;
*(uint16_t*)0x2000000001de = 0xb514;
*(uint32_t*)0x2000000001e0 = 2;
*(uint32_t*)0x2000000001e4 = 2;
memcpy((void*)0x2000000001e8, "... [truncated large byte array] ...", 128);
syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0x400448c8, /*arg=*/0x2000000001c0ul);
return 0;
}
]
|
| 2/1 |
2026/05/25 22:58 |
action |
kernel-checkouter |
2m
Results: map[KernelSrc:/app/workdir/cache/src/ef90747cb9cae1086cbbdb17a6be7a5ab4a7df32]
|
| 3/1 |
2026/05/25 23:00 |
action |
kernel-builder |
22m
Results: map[KernelObj:/app/workdir/cache/build/4fbf865607703adc8dde9975fc3280d07613e23c]
|
| 4/1 |
2026/05/25 23:23 |
action |
codesearch-prepare |
18m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/25 23:41 |
agent |
expert |
25m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true 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:
INFO: task khidpd_000d2f19:5507 blocked for more than 143 seconds.
Not tainted 6.16.0-rc3-syzkaller-00306-gaaf724ed6926 #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:khidpd_000d2f19 state:D stack:29000 pid:5507 tgid:5507 ppid:2 task_flags:0x208040 flags:0x00004000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5396 [inline]
__schedule+0x16f5/0x4d00 kernel/sched/core.c:6785
__schedule_loop kernel/sched/core.c:6863 [inline]
schedule+0x165/0x360 kernel/sched/core.c:6878
schedule_timeout+0x9a/0x270 kernel/time/sleep_timeout.c:75
do_wait_for_common kernel/sched/completion.c:95 [inline]
__wait_for_common kernel/sched/completion.c:116 [inline]
wait_for_common kernel/sched/completion.c:127 [inline]
wait_for_completion+0x2bf/0x5d0 kernel/sched/completion.c:148
__flush_work+0x9b9/0xbc0 kernel/workqueue.c:4246
__cancel_work_sync+0xbe/0x110 kernel/workqueue.c:4366
hidp_session_remove+0x62/0x260 net/bluetooth/hidp/core.c:1169
l2cap_unregister_user+0x18e/0x1b0 net/bluetooth/l2cap_core.c:1734
hidp_session_thread+0x3c9/0x410 net/bluetooth/hidp/core.c:1304
kthread+0x70e/0x8a0 kernel/kthread.c:464
ret_from_fork+0x3fc/0x770 arch/x86/kernel/process.c:148
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
INFO: task syz.0.54:5547 blocked for more than 143 seconds.
Not tainted 6.16.0-rc3-syzkaller-00306-gaaf724ed6926 #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.0.54 state:D stack:28008 pid:5547 tgid:5547 ppid:5445 task_flags:0x400140 flags:0x00004004
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5396 [inline]
__schedule+0x16f5/0x4d00 kernel/sched/core.c:6785
__schedule_loop kernel/sched/core.c:6863 [inline]
schedule+0x165/0x360 kernel/sched/core.c:6878
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:6935
__mutex_lock_common kernel/locking/mutex.c:679 [inline]
__mutex_lock+0x724/0xe80 kernel/locking/mutex.c:747
l2cap_chan_connect+0x102/0xe30 net/bluetooth/l2cap_core.c:6964
l2cap_sock_connect+0x5c5/0x7a0 net/bluetooth/l2cap_sock.c:256
__sys_connect_file net/socket.c:2038 [inline]
__sys_connect+0x316/0x440 net/socket.c:2057
__do_sys_connect net/socket.c:2063 [inline]
__se_sys_connect net/socket.c:2060 [inline]
__x64_sys_connect+0x7a/0x90 net/socket.c:2060
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f23aef8e929
RSP: 002b:00007ffe899a2238 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
RAX: ffffffffffffffda RBX: 00007f23af1b5fa0 RCX: 00007f23aef8e929
RDX: 000000000000000e RSI: 0000200000000080 RDI: 0000000000000004
RBP: 00007f23af010b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f23af1b5fa0 R14: 00007f23af1b5fa0 R15: 0000000000000003
</TASK>
Showing all locks held in the system:
1 lock held by khungtaskd/26:
#0: ffffffff8e13ee60 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:331 [inline]
#0: ffffffff8e13ee60 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:841 [inline]
#0: ffffffff8e13ee60 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180 kernel/locking/lockdep.c:6770
4 locks held by kworker/u4:4/1034:
#0: ffff88801a479148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3213 [inline]
#0: ffff88801a479148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x9b4/0x17b0 kernel/workqueue.c:3321
#1: ffffc900024c7bc0 ((linkwatch_work).work){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3214 [inline]
#1: ffffc900024c7bc0 ((linkwatch_work).work){+.+.}-{0:0}, at: process_scheduled_works+0x9ef/0x17b0 kernel/workqueue.c:3321
#2: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: linkwatch_event+0xe/0x60 net/core/link_watch.c:303
#3: ffffffff8e144978 (rcu_state.exp_mutex){+.+.}-{4:4}, at: exp_funnel_lock kernel/rcu/tree_exp.h:304 [inline]
#3: ffffffff8e144978 (rcu_state.exp_mutex){+.+.}-{4:4}, at: synchronize_rcu_expedited+0x2f6/0x730 kernel/rcu/tree_exp.h:998
3 locks held by kworker/0:3/1353:
2 locks held by getty/5111:
#0: ffff8880002c90a0 (&tty->ldisc_sem){++++}-{0:0}, at: tty_ldisc_ref_wait+0x25/0x70 drivers/tty/tty_ldisc.c:243
#1: ffffc9000018e2f0 (&ldata->atomic_read_lock){+.+.}-{4:4}, at: n_tty_read+0x43e/0x1400 drivers/tty/n_tty.c:2222
2 locks held by khidpd_000d2f19/5507:
#0: ffff88804f23c0b8 (&hdev->lock){+.+.}-{4:4}, at: l2cap_unregister_user+0x6a/0x1b0 net/bluetooth/l2cap_core.c:1728
#1: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_remove+0x2c/0x260 net/bluetooth/hidp/core.c:1165
1 lock held by syz.0.54/5547:
#0: ffff88804f23c0b8 (&hdev->lock){+.+.}-{4:4}, at: l2cap_chan_connect+0x102/0xe30 net/bluetooth/l2cap_core.c:6964
1 lock held by syz.1.55/5578:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.2.56/5616:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.3.57/5652:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.4.58/5688:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.5.59/5724:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.6.60/5768:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz.7.61/5804:
#0: ffffffff8f6a0890 (hidp_session_sem){++++}-{4:4}, at: hidp_session_find+0x1a/0x100 net/bluetooth/hidp/core.c:1026
1 lock held by syz-executor/5806:
#0: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock net/core/rtnetlink.c:80 [inline]
#0: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_nets_lock net/core/rtnetlink.c:341 [inline]
#0: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_newlink+0x8db/0x1c70 net/core/rtnetlink.c:4054
=============================================
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 26 Comm: khungtaskd Not tainted 6.16.0-rc3-syzkaller-00306-gaaf724ed6926 #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120
nmi_cpu_backtrace+0x39e/0x3d0 lib/nmi_backtrace.c:113
nmi_trigger_cpumask_backtrace+0x17a/0x300 lib/nmi_backtrace.c:62
trigger_all_cpu_backtrace include/linux/nmi.h:158 [inline]
check_hung_uninterruptible_tasks kernel/hung_task.c:307 [inline]
watchdog+0xfee/0x1030 kernel/hung_task.c:470
kthread+0x70e/0x8a0 kernel/kthread.c:464
ret_from_fork+0x3fc/0x770 arch/x86/kernel/process.c:148
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
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)) {}
// syz_init_net_socket$bt_l2cap arguments: [
// fam: const = 0x1f (8 bytes)
// type: bt_l2cap_type = 0x3 (8 bytes)
// proto: const = 0x0 (8 bytes)
// ]
// returns sock_bt_l2cap
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=SOCK_RAW*/3, /*proto=*/0);
if (res != -1)
r[0] = res;
// connect$bt_l2cap arguments: [
// fd: sock_bt_l2cap (resource)
// addr: ptr[in, sockaddr_l2] {
// sockaddr_l2 {
// l2_family: const = 0x1f (2 bytes)
// l2_psm: int16 = 0x0 (2 bytes)
// l2_bdaddr: union bdaddr_t {
// fixed: bdaddr_fixed {
// b: buffer: {aa aa aa aa aa} (length 0x5)
// a: int8 = 0x10 (1 bytes)
// }
// }
// l2_cid: int16 = 0x7ff (2 bytes)
// l2_bdaddr_type: bdaddr_type = 0x0 (1 bytes)
// pad = 0x0 (1 bytes)
// }
// }
// addrlen: len = 0xe (8 bytes)
// ]
*(uint16_t*)0x200000000080 = 0x1f;
*(uint16_t*)0x200000000082 = 0;
memset((void*)0x200000000084, 170, 5);
*(uint8_t*)0x200000000089 = 0x10;
*(uint16_t*)0x20000000008a = 0x7ff;
*(uint8_t*)0x20000000008c = 0;
syscall(__NR_connect, /*fd=*/r[0], /*addr=*/0x200000000080ul, /*addrlen=*/0xeul);
// syz_init_net_socket$bt_hidp arguments: [
// fam: const = 0x1f (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: const = 0x6 (8 bytes)
// ]
// returns sock_bt_hidp
res = -1;
res = syz_init_net_socket(/*fam=*/0x1f, /*type=*/3, /*proto=*/6);
if (res != -1)
r[1] = res;
// ioctl$sock_bt_hidp_HIDPCONNADD arguments: [
// fd: sock_bt_hidp (resource)
// cmd: const = 0x400448c8 (4 bytes)
// arg: ptr[in, hidp_connadd_req] {
// hidp_connadd_req {
// ctrlsk: sock (resource)
// intrsk: sock (resource)
// parser: int16 = 0x2 (2 bytes)
// rdsize: len = 0xfffffffffffffea4 (2 bytes)
// pad = 0x0 (4 bytes)
// rddata: ptr[in, buffer] {
// buffer: {22 ad 35} (length 0x3)
// }
// country: int8 = 0xb8 (1 bytes)
// subclas: int8 = 0x88 (1 bytes)
// vendor: int16 = 0xd (2 bytes)
// product: int16 = 0x2f19 (2 bytes)
// version: int16 = 0xb514 (2 bytes)
// flags: hidp_connadd_flags = 0x2 (4 bytes)
// idleto: int32 = 0x2 (4 bytes)
// name: buffer: {73 79 7a 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x80)
// }
// }
// ]
*(uint32_t*)0x2000000001c0 = r[0];
*(uint32_t*)0x2000000001c4 = r[0];
*(uint16_t*)0x2000000001c8 = 2;
*(uint16_t*)0x2000000001ca = 0xfea4;
*(uint64_t*)0x2000000001d0 = 0x200000000500;
memcpy((void*)0x200000000500, "\x22\xad\x35", 3);
*(uint8_t*)0x2000000001d8 = 0xb8;
*(uint8_t*)0x2000000001d9 = 0x88;
*(uint16_t*)0x2000000001da = 0xd;
*(uint16_t*)0x2000000001dc = 0x2f19;
*(uint16_t*)0x2000000001de = 0xb514;
*(uint32_t*)0x2000000001e0 = 2;
*(uint32_t*)0x2000000001e4 = 2;
memcpy((void*)0x2000000001e8, "... [truncated large byte array] ...", 128);
syscall(__NR_ioctl, /*fd=*/r[1], /*cmd=*/0x400448c8, /*arg=*/0x2000000001c0ul);
return 0;
}
Reply: The bug report describes a hung task situation where multiple threads are blocked, leading to a system deadlock.
### Analysis of the Deadlock
1. **`khidpd` (PID 5507)** is executing `hidp_session_thread` and calls `l2cap_unregister_user`.
- It acquires `hdev->lock` inside `l2cap_unregister_user`.
- It then calls the user's `remove` callback, which is `hidp_session_remove`.
- `hidp_session_remove` acquires `hidp_session_sem` for WRITE.
- It then calls `cancel_work_sync(&session->dev_init)` and blocks, waiting for the `dev_init` work (`hidp_session_dev_work`) to finish.
2. **`syz.0.54` (PID 5547)** is executing `l2cap_sock_connect` -> `l2cap_chan_connect`.
- It attempts to acquire `hdev->lock` but is blocked because `khidpd` is already holding it.
3. **`syz.1.55` to `syz.7.61`** are executing `hidp_session_find`.
- They attempt to acquire `hidp_session_sem` for READ but are blocked because `khidpd` is holding it for WRITE.
4. **`kworker/0:3`** is executing the `dev_init` work (`hidp_session_dev_work`).
- This work calls `hidp_session_dev_add`, which registers the HID device (`hid_add_device`).
- Device registration triggers driver probing and uevents. If any part of this device registration path attempts to acquire `hdev->lock` (e.g., via some indirect L2CAP or HCI interaction) or is blocked behind another lock (like `rtnl_mutex` which is currently held by `kworker/u4:4` and heavily contended), it will deadlock because `khidpd` is holding `hdev->lock` and waiting for this exact work to finish.
This is a classic ABBA deadlock or workqueue synchronization hang where a thread holding a lock (`hdev->lock`) waits for a work item to finish, but the work item requires the same lock (or is blocked behind something else that requires it) to complete.
### Security Impact Assessment
- **Exploitable:** False. This is a synchronization issue (deadlock) resulting in threads hanging in the `D` state. It does not lead to memory corruption, use-after-free, or out-of-bounds accesses that could be leveraged for privilege escalation or arbitrary code execution.
- **Denial Of Service:** True. The deadlock permanently blocks critical Bluetooth locks (`hdev->lock` and `hidp_session_sem`), which will hang the Bluetooth subsystem and any processes attempting to interact with it, effectively causing a local denial of service.
- **Accessible From Unprivileged Processes:** False. The bug is triggered by creating a HIDP session via the `HIDPCONNADD` ioctl on a Bluetooth socket. The `do_hidp_sock_ioctl` function explicitly checks for global `CAP_NET_ADMIN` using `capable(CAP_NET_ADMIN)`. Therefore, a normal unprivileged user cannot reach the vulnerable code path.
- **Accessible From User Namespaces:** False. Because the code uses `capable(CAP_NET_ADMIN)` instead of `ns_capable()`, the capability must be held in the initial (global) user namespace. Having `CAP_NET_ADMIN` inside a restricted user namespace is not sufficient to bypass this check.
- **Remote Trigger:** True. While a local unprivileged user cannot trigger this via ioctls, a remote attacker acting as a malicious Bluetooth HID device can. When a paired remote HID device connects, the host's `bluetoothd` (running as root with `CAP_NET_ADMIN`) automatically accepts the connection and issues the `HIDPCONNADD` ioctl. If the remote device then immediately drops the L2CAP connection at the right moment while the `dev_init` work is still running, it will invoke `hidp_session_remove` and trigger the deadlock remotely.
- **Peripheral Trigger:** False. This is triggered via Bluetooth network protocols, not by physically plugging in a USB or similar peripheral device.
- **VM Guest/Host Trigger:** False. This is specific to the Bluetooth subsystem and not related to virtualization boundaries or paravirtualized devices.
|
| 6/2 |
2026/05/25 23:41 |
llm |
expert |
0m
Model:
Tokens:
input: 11699
output: 21
thoughts: 835
Thoughts: **Analyzing a Bluetooth Task Hang**
Okay, so we've got a couple of tasks, `khidpd_000d2f19` and `syz.0.54`, that have been blocked for over 143 seconds. That's a clear indication of a problem, and let's dig into the call traces to understand why.
`khidpd_000d2f19` is stuck in `hidp_session_remove`. The trace shows it's waiting on `wait_for_completion`, eventually leading back to work queue operations. This points to a potential issue with cleanup or synchronization within the HID over Bluetooth protocol, specifically in the `hidp_session_remove` function.
Now, `syz.0.54` is blocked inside `l2cap_chan_connect`. That's interesting, as it's dealing with L2CAP channel connection establishment. My immediate thought is, a mutex issue. It appears to be waiting on a mutex lock within `l2cap_chan_connect`.
Let's look at the lock information... aha! Both tasks are using the same `hdev->lock`! `khidpd_000d2f19` holds the `hdev->lock` and the `hidp_session_sem` at `hidp_session_remove+0x2c/0x260`. `syz.0.54` is trying to get it but it can't. The stack trace confirms `syz.0.54` is waiting on `__mutex_lock` in `l2cap_chan_connect`. So `syz.0.54` is not trying to acquire the lock, it holds the lock, but is still blocked. Why is it blocked? Let's check `l2cap_chan_connect` at `net/bluetooth/l2cap_core.c:6964`. This is where the root of the problem lies. We'll need to carefully examine that function for potential deadlocks or incorrect lock usage. We need to follow the execution flow, especially within that `l2cap_chan_connect` function, to figure out what's going on and if `khidpd_000d2f19` is the reason.
|
| 7/2 |
2026/05/25 23:42 |
tool |
grepper |
0m
Args: map[Expression:l2cap_chan_connect]
Results: map[Output:include/net/bluetooth/l2cap.h=954=void l2cap_chan_close(struct l2cap_chan *chan, int reason);
include/net/bluetooth/l2cap.h:955:int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
include/net/bluetooth/l2cap.h-956- bdaddr_t *dst, u8 dst_type, u16 timeout);
--
net/bluetooth/6lowpan.c=889=static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
--
net/bluetooth/6lowpan.c-899-
net/bluetooth/6lowpan.c:900: err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
net/bluetooth/6lowpan.c-901- addr, dst_type, L2CAP_CONN_TIMEOUT);
--
net/bluetooth/l2cap_core.c=780=static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan)
--
net/bluetooth/l2cap_core.c-786-
net/bluetooth/l2cap_core.c:787:static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
net/bluetooth/l2cap_core.c-788-{
--
net/bluetooth/l2cap_core.c=808=void l2cap_chan_close(struct l2cap_chan *chan, int reason)
--
net/bluetooth/l2cap_core.c-830- if (conn->hcon->type == ACL_LINK)
net/bluetooth/l2cap_core.c:831: l2cap_chan_connect_reject(chan);
net/bluetooth/l2cap_core.c-832- else if (conn->hcon->type == LE_LINK) {
--
net/bluetooth/l2cap_core.c=6928=static void l2cap_chan_by_pid(struct l2cap_chan *chan, void *data)
--
net/bluetooth/l2cap_core.c-6948-
net/bluetooth/l2cap_core.c:6949:int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
net/bluetooth/l2cap_core.c-6950- bdaddr_t *dst, u8 dst_type, u16 timeout)
--
net/bluetooth/l2cap_core.c-7131-}
net/bluetooth/l2cap_core.c:7132:EXPORT_SYMBOL_GPL(l2cap_chan_connect);
net/bluetooth/l2cap_core.c-7133-
--
net/bluetooth/l2cap_sock.c=181=static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
--
net/bluetooth/l2cap_sock.c-255-
net/bluetooth/l2cap_sock.c:256: err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
net/bluetooth/l2cap_sock.c-257- &la.l2_bdaddr, la.l2_bdaddr_type,
]
|
| 8/2 |
2026/05/25 23:42 |
llm |
expert |
0m
Model:
Tokens:
input: 13950
output: 26
thoughts: 35
Thoughts: Here's my take on the thought process:
**Diving into the L2CAP Connection Logic**
Alright, time to dig into this `l2cap_chan_connect` function. It looks like the place to start is in `net/bluetooth/l2cap_core.c`, specifically around line 6964. I need to understand the connection establishment process for L2CAP channels. That function call is likely the core of the connection handshake. I'm hoping to get a clear picture of the state transitions, the data structures involved, and the potential error conditions handled during channel creation. Let's see what we can find. I expect to parse the connection parameters, negotiate the MTU, and handle security requirements in that method. Hopefully, I can find the connection timeouts and retransmission handling, too.
|
| 9/2 |
2026/05/25 23:42 |
tool |
read-file |
0m
Args: map[File:net/bluetooth/l2cap_core.c]
Results: map[Contents:/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
Copyright (C) 2010 Google Inc.
Copyright (C) 2011 ProFUSION Embedded Systems
Copyright (c) 2012 Code Aurora Forum. All rights reserved.
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.
*/
/* Bluetooth L2CAP core. */
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/crc16.h>
#include <linux/filter.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "smp.h"
#define LE_FLOWCTL_MAX_CREDITS 65535
bool disable_ertm;
bool enable_ecred = IS_ENABLED(CONFIG_BT_LE_L2CAP_ECRED);
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
void *data);
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size);
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event);
static void l2cap_retrans_timeout(struct work_struct *work);
static void l2cap_monitor_timeout(struct work_struct *work);
static void l2cap_ack_timeout(struct work_struct *work);
static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
{
if (link_type == LE_LINK) {
if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
return BDADDR_LE_PUBLIC;
else
return BDADDR_LE_RANDOM;
}
return BDADDR_BREDR;
}
static inline u8 bdaddr_src_type(struct hci_conn *hcon)
{
return bdaddr_type(hcon->type, hcon->src_type);
}
static inline u8 bdaddr_dst_type(struct hci_conn *hcon)
{
return bdaddr_type(hcon->type, hcon->dst_type);
}
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->dcid == cid)
return c;
}
return NULL;
}
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->scid == cid)
return c;
}
return NULL;
}
/* Find channel with given SCID.
* Returns a reference locked channel.
*/
static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
c = __l2cap_get_chan_by_scid(conn, cid);
if (c) {
/* Only lock if chan reference is not 0 */
c = l2cap_chan_hold_unless_zero(c);
if (c)
l2cap_chan_lock(c);
}
return c;
}
/* Find channel with given DCID.
* Returns a reference locked channel.
*/
static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
c = __l2cap_get_chan_by_dcid(conn, cid);
if (c) {
/* Only lock if chan reference is not 0 */
c = l2cap_chan_hold_unless_zero(c);
if (c)
l2cap_chan_lock(c);
}
return c;
}
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
u8 ident)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->ident == ident)
return c;
}
return NULL;
}
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src,
u8 src_type)
{
struct l2cap_chan *c;
list_for_each_entry(c, &chan_list, global_l) {
if (src_type == BDADDR_BREDR && c->src_type != BDADDR_BREDR)
continue;
if (src_type != BDADDR_BREDR && c->src_type == BDADDR_BREDR)
continue;
if (c->sport == psm && !bacmp(&c->src, src))
return c;
}
return NULL;
}
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
{
int err;
write_lock(&chan_list_lock);
if (psm && __l2cap_global_chan_by_addr(psm, src, chan->src_type)) {
err = -EADDRINUSE;
goto done;
}
if (psm) {
chan->psm = psm;
chan->sport = psm;
err = 0;
} else {
u16 p, start, end, incr;
if (chan->src_type == BDADDR_BREDR) {
start = L2CAP_PSM_DYN_START;
end = L2CAP_PSM_AUTO_END;
incr = 2;
} else {
start = L2CAP_PSM_LE_DYN_START;
end = L2CAP_PSM_LE_DYN_END;
incr = 1;
}
err = -EINVAL;
for (p = start; p <= end; p += incr)
if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src,
chan->src_type)) {
chan->psm = cpu_to_le16(p);
chan->sport = cpu_to_le16(p);
err = 0;
break;
}
}
done:
write_unlock(&chan_list_lock);
return err;
}
EXPORT_SYMBOL_GPL(l2cap_add_psm);
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
write_lock(&chan_list_lock);
/* Override the defaults (which are for conn-oriented) */
chan->omtu = L2CAP_DEFAULT_MTU;
chan->chan_type = L2CAP_CHAN_FIXED;
chan->scid = scid;
write_unlock(&chan_list_lock);
return 0;
}
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
{
u16 cid, dyn_end;
if (conn->hcon->type == LE_LINK)
dyn_end = L2CAP_CID_LE_DYN_END;
else
dyn_end = L2CAP_CID_DYN_END;
for (cid = L2CAP_CID_DYN_START; cid <= dyn_end; cid++) {
if (!__l2cap_get_chan_by_scid(conn, cid))
return cid;
}
return 0;
}
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
chan->ops->state_change(chan, state, 0);
}
static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
int state, int err)
{
chan->state = state;
chan->ops->state_change(chan, chan->state, err);
}
static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
{
chan->ops->state_change(chan, chan->state, err);
}
static void __set_retrans_timer(struct l2cap_chan *chan)
{
if (!delayed_work_pending(&chan->monitor_timer) &&
chan->retrans_timeout) {
l2cap_set_timer(chan, &chan->retrans_timer,
secs_to_jiffies(chan->retrans_timeout));
}
}
static void __set_monitor_timer(struct l2cap_chan *chan)
{
__clear_retrans_timer(chan);
if (chan->monitor_timeout) {
l2cap_set_timer(chan, &chan->monitor_timer,
secs_to_jiffies(chan->monitor_timeout));
}
}
static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
u16 seq)
{
struct sk_buff *skb;
skb_queue_walk(head, skb) {
if (bt_cb(skb)->l2cap.txseq == seq)
return skb;
}
return NULL;
}
/* ---- L2CAP sequence number lists ---- */
/* For ERTM, ordered lists of sequence numbers must be tracked for
* SREJ requests that are received and for frames that are to be
* retransmitted. These seq_list functions implement a singly-linked
* list in an array, where membership in the list can also be checked
* in constant time. Items can also be added to the tail of the list
* and removed from the head in constant time, without further memory
* allocs or frees.
*/
static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
{
size_t alloc_size, i;
/* Allocated size is a power of 2 to map sequence numbers
* (which may be up to 14 bits) in to a smaller array that is
* sized for the negotiated ERTM transmit windows.
*/
alloc_size = roundup_pow_of_two(size);
seq_list->list = kmalloc_array(alloc_size, sizeof(u16), GFP_KERNEL);
if (!seq_list->list)
return -ENOMEM;
seq_list->mask = alloc_size - 1;
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
for (i = 0; i < alloc_size; i++)
seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
return 0;
}
static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list)
{
kfree(seq_list->list);
}
static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list,
u16 seq)
{
/* Constant-time check for list membership */
return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR;
}
static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
{
u16 seq = seq_list->head;
u16 mask = seq_list->mask;
seq_list->head = seq_list->list[seq & mask];
seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
}
return seq;
}
static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
{
u16 i;
if (seq_list->head == L2CAP_SEQ_LIST_CLEAR)
return;
for (i = 0; i <= seq_list->mask; i++)
seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
}
static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq)
{
u16 mask = seq_list->mask;
/* All appends happen in constant time */
if (seq_list->list[seq & mask] != L2CAP_SEQ_LIST_CLEAR)
return;
if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR)
seq_list->head = seq;
else
seq_list->list[seq_list->tail & mask] = seq;
seq_list->tail = seq;
seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL;
}
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
struct l2cap_conn *conn = chan->conn;
int reason;
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
if (!conn)
return;
mutex_lock(&conn->lock);
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
* this work. No need to call l2cap_chan_hold(chan) here again.
*/
l2cap_chan_lock(chan);
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
else if (chan->state == BT_CONNECT &&
chan->sec_level != BT_SECURITY_SDP)
reason = ECONNREFUSED;
else
reason = ETIMEDOUT;
l2cap_chan_close(chan, reason);
chan->ops->close(chan);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
mutex_unlock(&conn->lock);
}
struct l2cap_chan *l2cap_chan_create(void)
{
struct l2cap_chan *chan;
chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
if (!chan)
return NULL;
skb_queue_head_init(&chan->tx_q);
skb_queue_head_init(&chan->srej_q);
mutex_init(&chan->lock);
/* Set default lock nesting level */
atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
/* Available receive buffer space is initially unknown */
chan->rx_avail = -1;
write_lock(&chan_list_lock);
list_add(&chan->global_l, &chan_list);
write_unlock(&chan_list_lock);
INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout);
INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout);
chan->state = BT_OPEN;
kref_init(&chan->kref);
/* This flag is cleared in l2cap_chan_ready() */
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
BT_DBG("chan %p", chan);
return chan;
}
EXPORT_SYMBOL_GPL(l2cap_chan_create);
static void l2cap_chan_destroy(struct kref *kref)
{
struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
BT_DBG("chan %p", chan);
write_lock(&chan_list_lock);
list_del(&chan->global_l);
write_unlock(&chan_list_lock);
kfree(chan);
}
void l2cap_chan_hold(struct l2cap_chan *c)
{
BT_DBG("chan %p orig refcnt %u", c, kref_read(&c->kref));
kref_get(&c->kref);
}
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c)
{
BT_DBG("chan %p orig refcnt %u", c, kref_read(&c->kref));
if (!kref_get_unless_zero(&c->kref))
return NULL;
return c;
}
void l2cap_chan_put(struct l2cap_chan *c)
{
BT_DBG("chan %p orig refcnt %u", c, kref_read(&c->kref));
kref_put(&c->kref, l2cap_chan_destroy);
}
EXPORT_SYMBOL_GPL(l2cap_chan_put);
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
{
chan->fcs = L2CAP_FCS_CRC16;
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
chan->remote_max_tx = chan->max_tx;
chan->remote_tx_win = chan->tx_win;
chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
chan->conf_state = 0;
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
static __u16 l2cap_le_rx_credits(struct l2cap_chan *chan)
{
size_t sdu_len = chan->sdu ? chan->sdu->len : 0;
if (chan->mps == 0)
return 0;
/* If we don't know the available space in the receiver buffer, give
* enough credits for a full packet.
*/
if (chan->rx_avail == -1)
return (chan->imtu / chan->mps) + 1;
/* If we know how much space is available in the receive buffer, give
* out as many credits as would fill the buffer.
*/
if (chan->rx_avail <= sdu_len)
return 0;
return DIV_ROUND_UP(chan->rx_avail - sdu_len, chan->mps);
}
static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits)
{
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
chan->tx_credits = tx_credits;
/* Derive MPS from connection MTU to stop HCI fragmentation */
chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE);
chan->rx_credits = l2cap_le_rx_credits(chan);
skb_queue_head_init(&chan->tx_q);
}
static void l2cap_ecred_init(struct l2cap_chan *chan, u16 tx_credits)
{
l2cap_le_flowctl_init(chan, tx_credits);
/* L2CAP implementations shall support a minimum MPS of 64 octets */
if (chan->mps < L2CAP_ECRED_MIN_MPS) {
chan->mps = L2CAP_ECRED_MIN_MPS;
chan->rx_credits = l2cap_le_rx_credits(chan);
}
}
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
__le16_to_cpu(chan->psm), chan->dcid);
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
chan->conn = conn;
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
/* Alloc CID for connection-oriented socket */
chan->scid = l2cap_alloc_cid(conn);
if (conn->hcon->type == ACL_LINK)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
case L2CAP_CHAN_CONN_LESS:
/* Connectionless socket */
chan->scid = L2CAP_CID_CONN_LESS;
chan->dcid = L2CAP_CID_CONN_LESS;
chan->omtu = L2CAP_DEFAULT_MTU;
break;
case L2CAP_CHAN_FIXED:
/* Caller will set CID and CID specific MTU values */
break;
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
chan->dcid = L2CAP_CID_SIGNALING;
chan->omtu = L2CAP_DEFAULT_MTU;
}
chan->local_id = L2CAP_BESTEFFORT_ID;
chan->local_stype = L2CAP_SERV_BESTEFFORT;
chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE;
chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME;
chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT;
chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO;
l2cap_chan_hold(chan);
/* Only keep a reference for fixed channels if they requested it */
if (chan->chan_type != L2CAP_CHAN_FIXED ||
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
hci_conn_hold(conn->hcon);
/* Append to the list since the order matters for ECRED */
list_add_tail(&chan->list, &conn->chan_l);
}
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
mutex_lock(&conn->lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->lock);
}
void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
__clear_chan_timer(chan);
BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
state_to_string(chan->state));
chan->ops->teardown(chan, err);
if (conn) {
/* Delete from channel list */
list_del(&chan->list);
l2cap_chan_put(chan);
chan->conn = NULL;
/* Reference was only held for non-fixed channels or
* fixed channels that explicitly requested it using the
* FLAG_HOLD_HCI_CONN flag.
*/
if (chan->chan_type != L2CAP_CHAN_FIXED ||
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
hci_conn_drop(conn->hcon);
}
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_EXT_FLOWCTL:
skb_queue_purge(&chan->tx_q);
break;
case L2CAP_MODE_ERTM:
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
skb_queue_purge(&chan->srej_q);
l2cap_seq_list_free(&chan->srej_list);
l2cap_seq_list_free(&chan->retrans_list);
fallthrough;
case L2CAP_MODE_STREAMING:
skb_queue_purge(&chan->tx_q);
break;
}
}
EXPORT_SYMBOL_GPL(l2cap_chan_del);
static void __l2cap_chan_list_id(struct l2cap_conn *conn, u16 id,
l2cap_chan_func_t func, void *data)
{
struct l2cap_chan *chan, *l;
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
if (chan->ident == id)
func(chan, data);
}
}
static void __l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
void *data)
{
struct l2cap_chan *chan;
list_for_each_entry(chan, &conn->chan_l, list) {
func(chan, data);
}
}
void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
void *data)
{
if (!conn)
return;
mutex_lock(&conn->lock);
__l2cap_chan_list(conn, func, data);
mutex_unlock(&conn->lock);
}
EXPORT_SYMBOL_GPL(l2cap_chan_list);
static void l2cap_conn_update_id_addr(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
id_addr_timer.work);
struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan;
mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
bacpy(&chan->dst, &hcon->dst);
chan->dst_type = bdaddr_dst_type(hcon);
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
}
static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_le_conn_rsp rsp;
u16 result;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
result = L2CAP_CR_LE_AUTHORIZATION;
else
result = L2CAP_CR_LE_BAD_PSM;
l2cap_state_change(chan, BT_DISCONN);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
&rsp);
}
static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan)
{
l2cap_state_change(chan, BT_DISCONN);
__l2cap_ecred_conn_rsp_defer(chan);
}
static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_conn_rsp rsp;
u16 result;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
l2cap_state_change(chan, BT_DISCONN);
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
}
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
{
struct l2cap_conn *conn = chan->conn;
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
switch (chan->state) {
case BT_LISTEN:
chan->ops->teardown(chan, 0);
break;
case BT_CONNECTED:
case BT_CONFIG:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
l2cap_chan_del(chan, reason);
break;
case BT_CONNECT2:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
if (conn->hcon->type == ACL_LINK)
l2cap_chan_connect_reject(chan);
else if (conn->hcon->type == LE_LINK) {
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
l2cap_chan_le_connect_reject(chan);
break;
case L2CAP_MODE_EXT_FLOWCTL:
l2cap_chan_ecred_connect_reject(chan);
return;
}
}
}
l2cap_chan_del(chan, reason);
break;
case BT_CONNECT:
case BT_DISCONN:
l2cap_chan_del(chan, reason);
break;
default:
chan->ops->teardown(chan, 0);
break;
}
}
EXPORT_SYMBOL(l2cap_chan_close);
static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
{
switch (chan->chan_type) {
case L2CAP_CHAN_RAW:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
case BT_SECURITY_FIPS:
return HCI_AT_DEDICATED_BONDING_MITM;
case BT_SECURITY_MEDIUM:
return HCI_AT_DEDICATED_BONDING;
default:
return HCI_AT_NO_BONDING;
}
break;
case L2CAP_CHAN_CONN_LESS:
if (chan->psm == cpu_to_le16(L2CAP_PSM_3DSP)) {
if (chan->sec_level == BT_SECURITY_LOW)
chan->sec_level = BT_SECURITY_SDP;
}
if (chan->sec_level == BT_SECURITY_HIGH ||
chan->sec_level == BT_SECURITY_FIPS)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
break;
case L2CAP_CHAN_CONN_ORIENTED:
if (chan->psm == cpu_to_le16(L2CAP_PSM_SDP)) {
if (chan->sec_level == BT_SECURITY_LOW)
chan->sec_level = BT_SECURITY_SDP;
if (chan->sec_level == BT_SECURITY_HIGH ||
chan->sec_level == BT_SECURITY_FIPS)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
}
fallthrough;
default:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
case BT_SECURITY_FIPS:
return HCI_AT_GENERAL_BONDING_MITM;
case BT_SECURITY_MEDIUM:
return HCI_AT_GENERAL_BONDING;
default:
return HCI_AT_NO_BONDING;
}
break;
}
}
/* Service level security */
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
{
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
if (conn->hcon->type == LE_LINK)
return smp_conn_security(conn->hcon, chan->sec_level);
auth_type = l2cap_get_auth_type(chan);
return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
initiator);
}
static u8 l2cap_get_ident(struct l2cap_conn *conn)
{
u8 id;
/* Get next available identificator.
* 1 - 128 are used by kernel.
* 129 - 199 are reserved.
* 200 - 254 are used by utilities like l2ping, etc.
*/
mutex_lock(&conn->ident_lock);
if (++conn->tx_ident > 128)
conn->tx_ident = 1;
id = conn->tx_ident;
mutex_unlock(&conn->ident_lock);
return id;
}
static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
u8 flags)
{
/* Check if the hcon still valid before attempting to send */
if (hci_conn_valid(conn->hcon->hdev, conn->hcon))
hci_send_acl(conn->hchan, skb, flags);
else
kfree_skb(skb);
}
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
void *data)
{
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
u8 flags;
BT_DBG("code 0x%2.2x", code);
if (!skb)
return;
/* Use NO_FLUSH if supported or we have an LE link (which does
* not support auto-flushing packets) */
if (lmp_no_flush_capable(conn->hcon->hdev) ||
conn->hcon->type == LE_LINK)
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
skb->priority = HCI_PRIO_MAX;
l2cap_send_acl(conn, skb, flags);
}
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct hci_conn *hcon = chan->conn->hcon;
u16 flags;
BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
skb->priority);
/* Use NO_FLUSH for LE links (where this is the only option) or
* if the BR/EDR link supports it and flushing has not been
* explicitly requested (through FLAG_FLUSHABLE).
*/
if (hcon->type == LE_LINK ||
(!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
lmp_no_flush_capable(hcon->hdev)))
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
hci_send_acl(chan->conn->hchan, skb, flags);
}
static void __unpack_enhanced_control(u16 enh, struct l2cap_ctrl *control)
{
control->reqseq = (enh & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
control->final = (enh & L2CAP_CTRL_FINAL) >> L2CAP_CTRL_FINAL_SHIFT;
if (enh & L2CAP_CTRL_FRAME_TYPE) {
/* S-Frame */
control->sframe = 1;
control->poll = (enh & L2CAP_CTRL_POLL) >> L2CAP_CTRL_POLL_SHIFT;
control->super = (enh & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
control->sar = 0;
control->txseq = 0;
} else {
/* I-Frame */
control->sframe = 0;
control->sar = (enh & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
control->txseq = (enh & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
control->poll = 0;
control->super = 0;
}
}
static void __unpack_extended_control(u32 ext, struct l2cap_ctrl *control)
{
control->reqseq = (ext & L2CAP_EXT_CTRL_REQSEQ) >> L2CAP_EXT_CTRL_REQSEQ_SHIFT;
control->final = (ext & L2CAP_EXT_CTRL_FINAL) >> L2CAP_EXT_CTRL_FINAL_SHIFT;
if (ext & L2CAP_EXT_CTRL_FRAME_TYPE) {
/* S-Frame */
control->sframe = 1;
control->poll = (ext & L2CAP_EXT_CTRL_POLL) >> L2CAP_EXT_CTRL_POLL_SHIFT;
control->super = (ext & L2CAP_EXT_CTRL_SUPERVISE) >> L2CAP_EXT_CTRL_SUPER_SHIFT;
control->sar = 0;
control->txseq = 0;
} else {
/* I-Frame */
control->sframe = 0;
control->sar = (ext & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
control->txseq = (ext & L2CAP_EXT_CTRL_TXSEQ) >> L2CAP_EXT_CTRL_TXSEQ_SHIFT;
control->poll = 0;
control->super = 0;
}
}
static inline void __unpack_control(struct l2cap_chan *chan,
struct sk_buff *skb)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
__unpack_extended_control(get_unaligned_le32(skb->data),
&bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
} else {
__unpack_enhanced_control(get_unaligned_le16(skb->data),
&bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
}
}
static u32 __pack_extended_control(struct l2cap_ctrl *control)
{
u32 packed;
packed = control->reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT;
packed |= control->final << L2CAP_EXT_CTRL_FINAL_SHIFT;
if (control->sframe) {
packed |= control->poll << L2CAP_EXT_CTRL_POLL_SHIFT;
packed |= control->super << L2CAP_EXT_CTRL_SUPER_SHIFT;
packed |= L2CAP_EXT_CTRL_FRAME_TYPE;
} else {
packed |= control->sar << L2CAP_EXT_CTRL_SAR_SHIFT;
packed |= control->txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT;
}
return packed;
}
static u16 __pack_enhanced_control(struct l2cap_ctrl *control)
{
u16 packed;
packed = control->reqseq << L2CAP_CTRL_REQSEQ_SHIFT;
packed |= control->final << L2CAP_CTRL_FINAL_SHIFT;
if (control->sframe) {
packed |= control->poll << L2CAP_CTRL_POLL_SHIFT;
packed |= control->super << L2CAP_CTRL_SUPER_SHIFT;
packed |= L2CAP_CTRL_FRAME_TYPE;
} else {
packed |= control->sar << L2CAP_CTRL_SAR_SHIFT;
packed |= control->txseq << L2CAP_CTRL_TXSEQ_SHIFT;
}
return packed;
}
static inline void __pack_control(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
put_unaligned_le32(__pack_extended_control(control),
skb->data + L2CAP_HDR_SIZE);
} else {
put_unaligned_le16(__pack_enhanced_control(control),
skb->data + L2CAP_HDR_SIZE);
}
}
static inline unsigned int __ertm_hdr_size(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_HDR_SIZE;
else
return L2CAP_ENH_HDR_SIZE;
}
static struct sk_buff *l2cap_create_sframe_pdu(struct l2cap_chan *chan,
u32 control)
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
int hlen = __ertm_hdr_size(chan);
if (chan->fcs == L2CAP_FCS_CRC16)
hlen += L2CAP_FCS_SIZE;
skb = bt_skb_alloc(hlen, GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
put_unaligned_le32(control, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
else
put_unaligned_le16(control, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *)skb->data, skb->len);
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
}
skb->priority = HCI_PRIO_MAX;
return skb;
}
static void l2cap_send_sframe(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
struct sk_buff *skb;
u32 control_field;
BT_DBG("chan %p, control %p", chan, control);
if (!control->sframe)
return;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
!control->poll)
control->final = 1;
if (control->super == L2CAP_SUPER_RR)
clear_bit(CONN_RNR_SENT, &chan->conn_state);
else if (control->super == L2CAP_SUPER_RNR)
set_bit(CONN_RNR_SENT, &chan->conn_state);
if (control->super != L2CAP_SUPER_SREJ) {
chan->last_acked_seq = control->reqseq;
__clear_ack_timer(chan);
}
BT_DBG("reqseq %d, final %d, poll %d, super %d", control->reqseq,
control->final, control->poll, control->super);
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
control_field = __pack_extended_control(control);
else
control_field = __pack_enhanced_control(control);
skb = l2cap_create_sframe_pdu(chan, control_field);
if (!IS_ERR(skb))
l2cap_do_send(chan, skb);
}
static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll)
{
struct l2cap_ctrl control;
BT_DBG("chan %p, poll %d", chan, poll);
memset(&control, 0, sizeof(control));
control.sframe = 1;
control.poll = poll;
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
control.super = L2CAP_SUPER_RNR;
else
control.super = L2CAP_SUPER_RR;
control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &control);
}
static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
{
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
return true;
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
void l2cap_send_conn_req(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid);
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* The channel may have already been flagged as connected in
* case of receiving data before the L2CAP info req/rsp
* procedure is complete.
*/
if (chan->state == BT_CONNECTED)
return;
/* This clears all conf flags, including CONF_NOT_COMPLETE */
chan->conf_state = 0;
__clear_chan_timer(chan);
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_EXT_FLOWCTL:
if (!chan->tx_credits)
chan->ops->suspend(chan);
break;
}
chan->state = BT_CONNECTED;
chan->ops->ready(chan);
}
static void l2cap_le_connect(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_le_conn_req req;
if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
return;
if (!chan->imtu)
chan->imtu = chan->conn->mtu;
l2cap_le_flowctl_init(chan, 0);
memset(&req, 0, sizeof(req));
req.psm = chan->psm;
req.scid = cpu_to_le16(chan->scid);
req.mtu = cpu_to_le16(chan->imtu);
req.mps = cpu_to_le16(chan->mps);
req.credits = cpu_to_le16(chan->rx_credits);
chan->ident = l2cap_get_ident(conn);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_REQ,
sizeof(req), &req);
}
struct l2cap_ecred_conn_data {
struct {
struct l2cap_ecred_conn_req_hdr req;
__le16 scid[5];
} __packed pdu;
struct l2cap_chan *chan;
struct pid *pid;
int count;
};
static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
{
struct l2cap_ecred_conn_data *conn = data;
struct pid *pid;
if (chan == conn->chan)
return;
if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
pid = chan->ops->get_peer_pid(chan);
/* Only add deferred channels with the same PID/PSM */
if (conn->pid != pid || chan->psm != conn->chan->psm || chan->ident ||
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
return;
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
l2cap_ecred_init(chan, 0);
/* Set the same ident so we can match on the rsp */
chan->ident = conn->chan->ident;
/* Include all channels deferred */
conn->pdu.scid[conn->count] = cpu_to_le16(chan->scid);
conn->count++;
}
static void l2cap_ecred_connect(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_ecred_conn_data data;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
l2cap_ecred_init(chan, 0);
memset(&data, 0, sizeof(data));
data.pdu.req.psm = chan->psm;
data.pdu.req.mtu = cpu_to_le16(chan->imtu);
data.pdu.req.mps = cpu_to_le16(chan->mps);
data.pdu.req.credits = cpu_to_le16(chan->rx_credits);
data.pdu.scid[0] = cpu_to_le16(chan->scid);
chan->ident = l2cap_get_ident(conn);
data.count = 1;
data.chan = chan;
data.pid = chan->ops->get_peer_pid(chan);
__l2cap_chan_list(conn, l2cap_ecred_defer_connect, &data);
l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_CONN_REQ,
sizeof(data.pdu.req) + data.count * sizeof(__le16),
&data.pdu);
}
static void l2cap_le_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
if (!smp_conn_security(conn->hcon, chan->sec_level))
return;
if (!chan->psm) {
l2cap_chan_ready(chan);
return;
}
if (chan->state == BT_CONNECT) {
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL)
l2cap_ecred_connect(chan);
else
l2cap_le_connect(chan);
}
}
static void l2cap_start_connection(struct l2cap_chan *chan)
{
if (chan->conn->hcon->type == LE_LINK) {
l2cap_le_start(chan);
} else {
l2cap_send_conn_req(chan);
}
}
static void l2cap_request_info(struct l2cap_conn *conn)
{
struct l2cap_info_req req;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
return;
req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
sizeof(req), &req);
}
static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
struct l2cap_chan *chan)
{
/* The minimum encryption key size needs to be enforced by the
* host stack before establishing any L2CAP connections. The
* specification in theory allows a minimum of 1, but to align
* BR/EDR and LE transports, a minimum of 7 is chosen.
*
* This check might also be called for unencrypted connections
* that have no key size requirements. Ensure that the link is
* actually encrypted before enforcing a key size.
*/
int min_key_size = hcon->hdev->min_enc_key_size;
/* On FIPS security level, key size must be 16 bytes */
if (chan->sec_level == BT_SECURITY_FIPS)
min_key_size = 16;
return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
hcon->enc_key_size >= min_key_size);
}
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
if (conn->hcon->type == LE_LINK) {
l2cap_le_start(chan);
return;
}
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
l2cap_request_info(conn);
return;
}
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
if (!l2cap_chan_check_security(chan, true) ||
!__l2cap_no_conn_pending(chan))
return;
if (l2cap_check_enc_key_size(conn->hcon, chan))
l2cap_start_connection(chan);
else
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
{
u32 local_feat_mask = l2cap_feat_mask;
if (!disable_ertm)
local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING;
switch (mode) {
case L2CAP_MODE_ERTM:
return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask;
case L2CAP_MODE_STREAMING:
return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask;
default:
return 0x00;
}
}
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_disconn_req req;
if (!conn)
return;
if (chan->mode == L2CAP_MODE_ERTM && chan->state == BT_CONNECTED) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
}
req.dcid = cpu_to_le16(chan->dcid);
req.scid = cpu_to_le16(chan->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
sizeof(req), &req);
l2cap_state_change_and_error(chan, BT_DISCONN, err);
}
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
struct l2cap_chan *chan, *tmp;
BT_DBG("conn %p", conn);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
l2cap_chan_lock(chan);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
l2cap_chan_ready(chan);
l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
if (!l2cap_chan_check_security(chan, true) ||
!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
}
if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
&& test_bit(CONF_STATE2_DEVICE,
&chan->conf_state)) {
l2cap_chan_close(chan, ECONNRESET);
l2cap_chan_unlock(chan);
continue;
}
if (l2cap_check_enc_key_size(conn->hcon, chan))
l2cap_start_connection(chan);
else
l2cap_chan_close(chan, ECONNREFUSED);
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
char buf[128];
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_chan_check_security(chan, false)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
chan->ops->defer(chan);
} else {
l2cap_state_change(chan, BT_CONFIG);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
} else {
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
}
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
sizeof(rsp), &rsp);
if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
l2cap_chan_unlock(chan);
continue;
}
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
chan->num_conf_req++;
}
l2cap_chan_unlock(chan);
}
}
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
BT_DBG("%s conn %p", hdev->name, conn);
/* For outgoing pairing which doesn't necessarily have an
* associated socket (e.g. mgmt_pair_device).
*/
if (hcon->out)
smp_conn_security(hcon, hcon->pending_sec_level);
/* For LE peripheral connections, make sure the connection interval
* is in the range of the minimum and maximum interval that has
* been configured for this connection. If not, then trigger
* the connection update procedure.
*/
if (hcon->role == HCI_ROLE_SLAVE &&
(hcon->le_conn_interval < hcon->le_conn_min_interval ||
hcon->le_conn_interval > hcon->le_conn_max_interval)) {
struct l2cap_conn_param_update_req req;
req.min = cpu_to_le16(hcon->le_conn_min_interval);
req.max = cpu_to_le16(hcon->le_conn_max_interval);
req.latency = cpu_to_le16(hcon->le_conn_latency);
req.to_multiplier = cpu_to_le16(hcon->le_supv_timeout);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req);
}
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
struct hci_conn *hcon = conn->hcon;
BT_DBG("conn %p", conn);
if (hcon->type == ACL_LINK)
l2cap_request_info(conn);
mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
if (hcon->type == LE_LINK) {
l2cap_le_start(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
l2cap_chan_ready(chan);
} else if (chan->state == BT_CONNECT) {
l2cap_do_start(chan);
}
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
if (hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);
queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
}
/* Notify sockets that we cannot guaranty reliability anymore */
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
{
struct l2cap_chan *chan;
BT_DBG("conn %p", conn);
list_for_each_entry(chan, &conn->chan_l, list) {
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
l2cap_chan_set_err(chan, err);
}
}
static void l2cap_info_timeout(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
info_timer.work);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
mutex_lock(&conn->lock);
l2cap_conn_start(conn);
mutex_unlock(&conn->lock);
}
/*
* l2cap_user
* External modules can register l2cap_user objects on l2cap_conn. The ->probe
* callback is called during registration. The ->remove callback is called
* during unregistration.
* An l2cap_user object can either be explicitly unregistered or when the
* underlying l2cap_conn object is deleted. This guarantees that l2cap->hcon,
* l2cap->hchan, .. are valid as long as the remove callback hasn't been called.
* External modules must own a reference to the l2cap_conn object if they intend
* to call l2cap_unregister_user(). The l2cap_conn object might get destroyed at
* any time if they don't.
*/
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
struct hci_dev *hdev = conn->hcon->hdev;
int ret;
/* We need to check whether l2cap_conn is registered. If it is not, we
* must not register the l2cap_user. l2cap_conn_del() is unregisters
* l2cap_conn objects, but doesn't provide its own locking. Instead, it
* relies on the parent hci_conn object to be locked. This itself relies
* on the hci_dev object to be locked. So we must lock the hci device
* here, too. */
hci_dev_lock(hdev);
if (!list_empty(&user->list)) {
ret = -EINVAL;
goto out_unlock;
}
/* conn->hchan is NULL after l2cap_conn_del() was called */
if (!conn->hchan) {
ret = -ENODEV;
goto out_unlock;
}
ret = user->probe(conn, user);
if (ret)
goto out_unlock;
list_add(&user->list, &conn->users);
ret = 0;
out_unlock:
hci_dev_unlock(hdev);
return ret;
}
EXPORT_SYMBOL(l2cap_register_user);
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
struct hci_dev *hdev = conn->hcon->hdev;
hci_dev_lock(hdev);
if (list_empty(&user->list))
goto out_unlock;
list_del_init(&user->list);
user->remove(conn, user);
out_unlock:
hci_dev_unlock(hdev);
}
EXPORT_SYMBOL(l2cap_unregister_user);
static void l2cap_unregister_all_users(struct l2cap_conn *conn)
{
struct l2cap_user *user;
while (!list_empty(&conn->users)) {
user = list_first_entry(&conn->users, struct l2cap_user, list);
list_del_init(&user->list);
user->remove(conn, user);
}
}
static void l2cap_conn_del(struct hci_conn *hcon, int err)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan, *l;
if (!conn)
return;
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
mutex_lock(&conn->lock);
kfree_skb(conn->rx_skb);
skb_queue_purge(&conn->pending_rx);
/* We can not call flush_work(&conn->pending_rx_work) here since we
* might block if we are running on a worker from the same workqueue
* pending_rx_work is waiting on.
*/
if (work_pending(&conn->pending_rx_work))
cancel_work_sync(&conn->pending_rx_work);
cancel_delayed_work_sync(&conn->id_addr_timer);
l2cap_unregister_all_users(conn);
/* Force the connection to be immediately dropped */
hcon->disc_timeout = 0;
/* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
l2cap_chan_hold(chan);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, err);
chan->ops->close(chan);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
cancel_delayed_work_sync(&conn->info_timer);
hci_chan_del(conn->hchan);
conn->hchan = NULL;
hcon->l2cap_data = NULL;
mutex_unlock(&conn->lock);
l2cap_conn_put(conn);
}
static void l2cap_conn_free(struct kref *ref)
{
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
hci_conn_put(conn->hcon);
kfree(conn);
}
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn)
{
kref_get(&conn->ref);
return conn;
}
EXPORT_SYMBOL(l2cap_conn_get);
void l2cap_conn_put(struct l2cap_conn *conn)
{
kref_put(&conn->ref, l2cap_conn_free);
}
EXPORT_SYMBOL(l2cap_conn_put);
/* ---- Socket interface ---- */
/* Find socket with psm and source / destination bdaddr.
* Returns closest match.
*/
static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
bdaddr_t *src,
bdaddr_t *dst,
u8 link_type)
{
struct l2cap_chan *c, *tmp, *c1 = NULL;
read_lock(&chan_list_lock);
list_for_each_entry_safe(c, tmp, &chan_list, global_l) {
if (state && c->state != state)
continue;
if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
continue;
if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
continue;
if (c->chan_type != L2CAP_CHAN_FIXED && c->psm == psm) {
int src_match, dst_match;
int src_any, dst_any;
/* Exact match. */
src_match = !bacmp(&c->src, src);
dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
if (!l2cap_chan_hold_unless_zero(c))
continue;
read_unlock(&chan_list_lock);
return c;
}
/* Closest match */
src_any = !bacmp(&c->src, BDADDR_ANY);
dst_any = !bacmp(&c->dst, BDADDR_ANY);
if ((src_match && dst_any) || (src_any && dst_match) ||
(src_any && dst_any))
c1 = c;
}
}
if (c1)
c1 = l2cap_chan_hold_unless_zero(c1);
read_unlock(&chan_list_lock);
return c1;
}
static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
monitor_timer.work);
BT_DBG("chan %p", chan);
l2cap_chan_lock(chan);
if (!chan->conn) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
}
l2cap_tx(chan, NULL, NULL, L2CAP_EV_MONITOR_TO);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
static void l2cap_retrans_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
retrans_timer.work);
BT_DBG("chan %p", chan);
l2cap_chan_lock(chan);
if (!chan->conn) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
}
l2cap_tx(chan, NULL, NULL, L2CAP_EV_RETRANS_TO);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
static void l2cap_streaming_send(struct l2cap_chan *chan,
struct sk_buff_head *skbs)
{
struct sk_buff *skb;
struct l2cap_ctrl *control;
BT_DBG("chan %p, skbs %p", chan, skbs);
skb_queue_splice_tail_init(skbs, &chan->tx_q);
while (!skb_queue_empty(&chan->tx_q)) {
skb = skb_dequeue(&chan->tx_q);
bt_cb(skb)->l2cap.retries = 1;
control = &bt_cb(skb)->l2cap;
control->reqseq = 0;
control->txseq = chan->next_tx_seq;
__pack_control(chan, control, skb);
if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
}
l2cap_do_send(chan, skb);
BT_DBG("Sent txseq %u", control->txseq);
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
chan->frames_sent++;
}
}
static int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
struct l2cap_ctrl *control;
int sent = 0;
BT_DBG("chan %p", chan);
if (chan->state != BT_CONNECTED)
return -ENOTCONN;
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return 0;
while (chan->tx_send_head &&
chan->unacked_frames < chan->remote_tx_win &&
chan->tx_state == L2CAP_TX_STATE_XMIT) {
skb = chan->tx_send_head;
bt_cb(skb)->l2cap.retries = 1;
control = &bt_cb(skb)->l2cap;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control->final = 1;
control->reqseq = chan->buffer_seq;
chan->last_acked_seq = chan->buffer_seq;
control->txseq = chan->next_tx_seq;
__pack_control(chan, control, skb);
if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
}
/* Clone after data has been modified. Data is assumed to be
read-only (for locking purposes) on cloned sk_buffs.
*/
tx_skb = skb_clone(skb, GFP_KERNEL);
if (!tx_skb)
break;
__set_retrans_timer(chan);
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
chan->unacked_frames++;
chan->frames_sent++;
sent++;
if (skb_queue_is_last(&chan->tx_q, skb))
chan->tx_send_head = NULL;
else
chan->tx_send_head = skb_queue_next(&chan->tx_q, skb);
l2cap_do_send(chan, tx_skb);
BT_DBG("Sent txseq %u", control->txseq);
}
BT_DBG("Sent %d, %u unacked, %u in ERTM queue", sent,
chan->unacked_frames, skb_queue_len(&chan->tx_q));
return sent;
}
static void l2cap_ertm_resend(struct l2cap_chan *chan)
{
struct l2cap_ctrl control;
struct sk_buff *skb;
struct sk_buff *tx_skb;
u16 seq;
BT_DBG("chan %p", chan);
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return;
while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
seq = l2cap_seq_list_pop(&chan->retrans_list);
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq);
if (!skb) {
BT_DBG("Error: Can't retransmit seq %d, frame missing",
seq);
continue;
}
bt_cb(skb)->l2cap.retries++;
control = bt_cb(skb)->l2cap;
if (chan->max_tx != 0 &&
bt_cb(skb)->l2cap.retries > chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_seq_list_clear(&chan->retrans_list);
break;
}
control.reqseq = chan->buffer_seq;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control.final = 1;
else
control.final = 0;
if (skb_cloned(skb)) {
/* Cloned sk_buffs are read-only, so we need a
* writeable copy
*/
tx_skb = skb_copy(skb, GFP_KERNEL);
} else {
tx_skb = skb_clone(skb, GFP_KERNEL);
}
if (!tx_skb) {
l2cap_seq_list_clear(&chan->retrans_list);
break;
}
/* Update skb contents */
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
put_unaligned_le32(__pack_extended_control(&control),
tx_skb->data + L2CAP_HDR_SIZE);
} else {
put_unaligned_le16(__pack_enhanced_control(&control),
tx_skb->data + L2CAP_HDR_SIZE);
}
/* Update FCS */
if (chan->fcs == L2CAP_FCS_CRC16) {
u16 fcs = crc16(0, (u8 *) tx_skb->data,
tx_skb->len - L2CAP_FCS_SIZE);
put_unaligned_le16(fcs, skb_tail_pointer(tx_skb) -
L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
BT_DBG("Resent txseq %d", control.txseq);
chan->last_acked_seq = chan->buffer_seq;
}
}
static void l2cap_retransmit(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
BT_DBG("chan %p, control %p", chan, control);
l2cap_seq_list_append(&chan->retrans_list, control->reqseq);
l2cap_ertm_resend(chan);
}
static void l2cap_retransmit_all(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
struct sk_buff *skb;
BT_DBG("chan %p, control %p", chan, control);
if (control->poll)
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_seq_list_clear(&chan->retrans_list);
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return;
if (chan->unacked_frames) {
skb_queue_walk(&chan->tx_q, skb) {
if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
skb == chan->tx_send_head)
break;
}
skb_queue_walk_from(&chan->tx_q, skb) {
if (skb == chan->tx_send_head)
break;
l2cap_seq_list_append(&chan->retrans_list,
bt_cb(skb)->l2cap.txseq);
}
l2cap_ertm_resend(chan);
}
}
static void l2cap_send_ack(struct l2cap_chan *chan)
{
struct l2cap_ctrl control;
u16 frames_to_ack = __seq_offset(chan, chan->buffer_seq,
chan->last_acked_seq);
int threshold;
BT_DBG("chan %p last_acked_seq %d buffer_seq %d",
chan, chan->last_acked_seq, chan->buffer_seq);
memset(&control, 0, sizeof(control));
control.sframe = 1;
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
chan->rx_state == L2CAP_RX_STATE_RECV) {
__clear_ack_timer(chan);
control.super = L2CAP_SUPER_RNR;
control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &control);
} else {
if (!test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) {
l2cap_ertm_send(chan);
/* If any i-frames were sent, they included an ack */
if (chan->buffer_seq == chan->last_acked_seq)
frames_to_ack = 0;
}
/* Ack now if the window is 3/4ths full.
* Calculate without mul or div
*/
threshold = chan->ack_win;
threshold += threshold << 1;
threshold >>= 2;
BT_DBG("frames_to_ack %u, threshold %d", frames_to_ack,
threshold);
if (frames_to_ack >= threshold) {
__clear_ack_timer(chan);
control.super = L2CAP_SUPER_RR;
control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &control);
frames_to_ack = 0;
}
if (frames_to_ack)
__set_ack_timer(chan);
}
}
static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
struct msghdr *msg, int len,
int count, struct sk_buff *skb)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff **frag;
int sent = 0;
if (!copy_from_iter_full(skb_put(skb, count), count, &msg->msg_iter))
return -EFAULT;
sent += count;
len -= count;
/* Continuation fragments (no L2CAP header) */
frag = &skb_shinfo(skb)->frag_list;
while (len) {
struct sk_buff *tmp;
count = min_t(unsigned int, conn->mtu, len);
tmp = chan->ops->alloc_skb(chan, 0, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
*frag = tmp;
if (!copy_from_iter_full(skb_put(*frag, count), count,
&msg->msg_iter))
return -EFAULT;
sent += count;
len -= count;
skb->len += (*frag)->len;
skb->data_len += (*frag)->len;
frag = &(*frag)->next;
}
return sent;
}
static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;
BT_DBG("chan %p psm 0x%2.2x len %zu", chan,
__le16_to_cpu(chan->psm), len);
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
/* Create L2CAP header */
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
}
return skb;
}
static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count;
struct l2cap_hdr *lh;
BT_DBG("chan %p len %zu", chan, len);
count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
/* Create L2CAP header */
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len);
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
}
return skb;
}
static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u16 sdulen)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen;
struct l2cap_hdr *lh;
BT_DBG("chan %p len %zu", chan, len);
if (!conn)
return ERR_PTR(-ENOTCONN);
hlen = __ertm_hdr_size(chan);
if (sdulen)
hlen += L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
hlen += L2CAP_FCS_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
/* Create L2CAP header */
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
/* Control header is populated later */
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
put_unaligned_le32(0, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
else
put_unaligned_le16(0, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
}
bt_cb(skb)->l2cap.fcs = chan->fcs;
bt_cb(skb)->l2cap.retries = 0;
return skb;
}
static int l2cap_segment_sdu(struct l2cap_chan *chan,
struct sk_buff_head *seg_queue,
struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
u16 sdu_len;
size_t pdu_len;
u8 sar;
BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
/* It is critical that ERTM PDUs fit in a single HCI fragment,
* so fragmented skbs are not used. The HCI layer's handling
* of fragmented skbs is not compatible with ERTM's queueing.
*/
/* PDU size is derived from the HCI MTU */
pdu_len = chan->conn->mtu;
/* Constrain PDU size for BR/EDR connections */
pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
/* Adjust for largest possible L2CAP overhead. */
if (chan->fcs)
pdu_len -= L2CAP_FCS_SIZE;
pdu_len -= __ertm_hdr_size(chan);
/* Remote device may have requested smaller PDUs */
pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
if (len <= pdu_len) {
sar = L2CAP_SAR_UNSEGMENTED;
sdu_len = 0;
pdu_len = len;
} else {
sar = L2CAP_SAR_START;
sdu_len = len;
}
while (len > 0) {
skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len);
if (IS_ERR(skb)) {
__skb_queue_purge(seg_queue);
return PTR_ERR(skb);
}
bt_cb(skb)->l2cap.sar = sar;
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
if (sdu_len)
sdu_len = 0;
if (len <= pdu_len) {
sar = L2CAP_SAR_END;
pdu_len = len;
} else {
sar = L2CAP_SAR_CONTINUE;
}
}
return 0;
}
static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
struct msghdr *msg,
size_t len, u16 sdulen)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen;
struct l2cap_hdr *lh;
BT_DBG("chan %p len %zu", chan, len);
if (!conn)
return ERR_PTR(-ENOTCONN);
hlen = L2CAP_HDR_SIZE;
if (sdulen)
hlen += L2CAP_SDULEN_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
/* Create L2CAP header */
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
}
return skb;
}
static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
struct sk_buff_head *seg_queue,
struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
size_t pdu_len;
u16 sdu_len;
BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
sdu_len = len;
pdu_len = chan->remote_mps - L2CAP_SDULEN_SIZE;
while (len > 0) {
if (len <= pdu_len)
pdu_len = len;
skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
if (IS_ERR(skb)) {
__skb_queue_purge(seg_queue);
return PTR_ERR(skb);
}
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
if (sdu_len) {
sdu_len = 0;
pdu_len += L2CAP_SDULEN_SIZE;
}
}
return 0;
}
static void l2cap_le_flowctl_send(struct l2cap_chan *chan)
{
int sent = 0;
BT_DBG("chan %p", chan);
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
chan->tx_credits--;
sent++;
}
BT_DBG("Sent %d credits %u queued %u", sent, chan->tx_credits,
skb_queue_len(&chan->tx_q));
}
static void l2cap_tx_timestamp(struct sk_buff *skb,
const struct sockcm_cookie *sockc,
size_t len)
{
struct sock *sk = skb ? skb->sk : NULL;
if (sk && sk->sk_type == SOCK_STREAM)
hci_setup_tx_timestamp(skb, len, sockc);
else
hci_setup_tx_timestamp(skb, 1, sockc);
}
static void l2cap_tx_timestamp_seg(struct sk_buff_head *queue,
const struct sockcm_cookie *sockc,
size_t len)
{
struct sk_buff *skb = skb_peek(queue);
struct sock *sk = skb ? skb->sk : NULL;
if (sk && sk->sk_type == SOCK_STREAM)
l2cap_tx_timestamp(skb_peek_tail(queue), sockc, len);
else
l2cap_tx_timestamp(skb, sockc, len);
}
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
const struct sockcm_cookie *sockc)
{
struct sk_buff *skb;
int err;
struct sk_buff_head seg_queue;
if (!chan->conn)
return -ENOTCONN;
/* Connectionless channel */
if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
skb = l2cap_create_connless_pdu(chan, msg, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
l2cap_tx_timestamp(skb, sockc, len);
l2cap_do_send(chan, skb);
return len;
}
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_EXT_FLOWCTL:
/* Check outgoing MTU */
if (len > chan->omtu)
return -EMSGSIZE;
__skb_queue_head_init(&seg_queue);
err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
if (chan->state != BT_CONNECTED) {
__skb_queue_purge(&seg_queue);
err = -ENOTCONN;
}
if (err)
return err;
l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
l2cap_le_flowctl_send(chan);
if (!chan->tx_credits)
chan->ops->suspend(chan);
err = len;
break;
case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
if (len > chan->omtu)
return -EMSGSIZE;
/* Create a basic PDU */
skb = l2cap_create_basic_pdu(chan, msg, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
l2cap_tx_timestamp(skb, sockc, len);
l2cap_do_send(chan, skb);
err = len;
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
/* Check outgoing MTU */
if (len > chan->omtu) {
err = -EMSGSIZE;
break;
}
__skb_queue_head_init(&seg_queue);
/* Do segmentation before calling in to the state machine,
* since it's possible to block while waiting for memory
* allocation.
*/
err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
if (err)
break;
if (chan->mode == L2CAP_MODE_ERTM) {
/* TODO: ERTM mode timestamping */
l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST);
} else {
l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
l2cap_streaming_send(chan, &seg_queue);
}
err = len;
/* If the skbs were not queued for sending, they'll still be in
* seg_queue and need to be purged.
*/
__skb_queue_purge(&seg_queue);
break;
default:
BT_DBG("bad state %1.1x", chan->mode);
err = -EBADFD;
}
return err;
}
EXPORT_SYMBOL_GPL(l2cap_chan_send);
static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
{
struct l2cap_ctrl control;
u16 seq;
BT_DBG("chan %p, txseq %u", chan, txseq);
memset(&control, 0, sizeof(control));
control.sframe = 1;
control.super = L2CAP_SUPER_SREJ;
for (seq = chan->expected_tx_seq; seq != txseq;
seq = __next_seq(chan, seq)) {
if (!l2cap_ertm_seq_in_queue(&chan->srej_q, seq)) {
control.reqseq = seq;
l2cap_send_sframe(chan, &control);
l2cap_seq_list_append(&chan->srej_list, seq);
}
}
chan->expected_tx_seq = __next_seq(chan, txseq);
}
static void l2cap_send_srej_tail(struct l2cap_chan *chan)
{
struct l2cap_ctrl control;
BT_DBG("chan %p", chan);
if (chan->srej_list.tail == L2CAP_SEQ_LIST_CLEAR)
return;
memset(&control, 0, sizeof(control));
control.sframe = 1;
control.super = L2CAP_SUPER_SREJ;
control.reqseq = chan->srej_list.tail;
l2cap_send_sframe(chan, &control);
}
static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq)
{
struct l2cap_ctrl control;
u16 initial_head;
u16 seq;
BT_DBG("chan %p, txseq %u", chan, txseq);
memset(&control, 0, sizeof(control));
control.sframe = 1;
control.super = L2CAP_SUPER_SREJ;
/* Capture initial list head to allow only one pass through the list. */
initial_head = chan->srej_list.head;
do {
seq = l2cap_seq_list_pop(&chan->srej_list);
if (seq == txseq || seq == L2CAP_SEQ_LIST_CLEAR)
break;
control.reqseq = seq;
l2cap_send_sframe(chan, &control);
l2cap_seq_list_append(&chan->srej_list, seq);
} while (chan->srej_list.head != initial_head);
}
static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
{
struct sk_buff *acked_skb;
u16 ackseq;
BT_DBG("chan %p, reqseq %u", chan, reqseq);
if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq)
return;
BT_DBG("expected_ack_seq %u, unacked_frames %u",
chan->expected_ack_seq, chan->unacked_frames);
for (ackseq = chan->expected_ack_seq; ackseq != reqseq;
ackseq = __next_seq(chan, ackseq)) {
acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq);
if (acked_skb) {
skb_unlink(acked_skb, &chan->tx_q);
kfree_skb(acked_skb);
chan->unacked_frames--;
}
}
chan->expected_ack_seq = reqseq;
if (chan->unacked_frames == 0)
__clear_retrans_timer(chan);
BT_DBG("unacked_frames %u", chan->unacked_frames);
}
static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan)
{
BT_DBG("chan %p", chan);
chan->expected_tx_seq = chan->buffer_seq;
l2cap_seq_list_clear(&chan->srej_list);
skb_queue_purge(&chan->srej_q);
chan->rx_state = L2CAP_RX_STATE_RECV;
}
static void l2cap_tx_state_xmit(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event)
{
BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
event);
switch (event) {
case L2CAP_EV_DATA_REQUEST:
if (chan->tx_send_head == NULL)
chan->tx_send_head = skb_peek(skbs);
skb_queue_splice_tail_init(skbs, &chan->tx_q);
l2cap_ertm_send(chan);
break;
case L2CAP_EV_LOCAL_BUSY_DETECTED:
BT_DBG("Enter LOCAL_BUSY");
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
/* The SREJ_SENT state must be aborted if we are to
* enter the LOCAL_BUSY state.
*/
l2cap_abort_rx_srej_sent(chan);
}
l2cap_send_ack(chan);
break;
case L2CAP_EV_LOCAL_BUSY_CLEAR:
BT_DBG("Exit LOCAL_BUSY");
clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
struct l2cap_ctrl local_control;
memset(&local_control, 0, sizeof(local_control));
local_control.sframe = 1;
local_control.super = L2CAP_SUPER_RR;
local_control.poll = 1;
local_control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &local_control);
chan->retry_count = 1;
__set_monitor_timer(chan);
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
}
break;
case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
l2cap_process_reqseq(chan, control->reqseq);
break;
case L2CAP_EV_EXPLICIT_POLL:
l2cap_send_rr_or_rnr(chan, 1);
chan->retry_count = 1;
__set_monitor_timer(chan);
__clear_ack_timer(chan);
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
break;
case L2CAP_EV_RETRANS_TO:
l2cap_send_rr_or_rnr(chan, 1);
chan->retry_count = 1;
__set_monitor_timer(chan);
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
break;
case L2CAP_EV_RECV_FBIT:
/* Nothing to process */
break;
default:
break;
}
}
static void l2cap_tx_state_wait_f(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event)
{
BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
event);
switch (event) {
case L2CAP_EV_DATA_REQUEST:
if (chan->tx_send_head == NULL)
chan->tx_send_head = skb_peek(skbs);
/* Queue data, but don't send. */
skb_queue_splice_tail_init(skbs, &chan->tx_q);
break;
case L2CAP_EV_LOCAL_BUSY_DETECTED:
BT_DBG("Enter LOCAL_BUSY");
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
/* The SREJ_SENT state must be aborted if we are to
* enter the LOCAL_BUSY state.
*/
l2cap_abort_rx_srej_sent(chan);
}
l2cap_send_ack(chan);
break;
case L2CAP_EV_LOCAL_BUSY_CLEAR:
BT_DBG("Exit LOCAL_BUSY");
clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
struct l2cap_ctrl local_control;
memset(&local_control, 0, sizeof(local_control));
local_control.sframe = 1;
local_control.super = L2CAP_SUPER_RR;
local_control.poll = 1;
local_control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &local_control);
chan->retry_count = 1;
__set_monitor_timer(chan);
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
}
break;
case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
l2cap_process_reqseq(chan, control->reqseq);
fallthrough;
case L2CAP_EV_RECV_FBIT:
if (control && control->final) {
__clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
__set_retrans_timer(chan);
chan->retry_count = 0;
chan->tx_state = L2CAP_TX_STATE_XMIT;
BT_DBG("recv fbit tx_state 0x2.2%x", chan->tx_state);
}
break;
case L2CAP_EV_EXPLICIT_POLL:
/* Ignore */
break;
case L2CAP_EV_MONITOR_TO:
if (chan->max_tx == 0 || chan->retry_count < chan->max_tx) {
l2cap_send_rr_or_rnr(chan, 1);
__set_monitor_timer(chan);
chan->retry_count++;
} else {
l2cap_send_disconn_req(chan, ECONNABORTED);
}
break;
default:
break;
}
}
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event)
{
BT_DBG("chan %p, control %p, skbs %p, event %d, state %d",
chan, control, skbs, event, chan->tx_state);
switch (chan->tx_state) {
case L2CAP_TX_STATE_XMIT:
l2cap_tx_state_xmit(chan, control, skbs, event);
break;
case L2CAP_TX_STATE_WAIT_F:
l2cap_tx_state_wait_f(chan, control, skbs, event);
break;
default:
/* Ignore event */
break;
}
}
static void l2cap_pass_to_tx(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
BT_DBG("chan %p, control %p", chan, control);
l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_REQSEQ_AND_FBIT);
}
static void l2cap_pass_to_tx_fbit(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
BT_DBG("chan %p, control %p", chan, control);
l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_FBIT);
}
/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct sk_buff *nskb;
struct l2cap_chan *chan;
BT_DBG("conn %p", conn);
list_for_each_entry(chan, &conn->chan_l, list) {
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
/* Don't send frame to the channel it came from */
if (bt_cb(skb)->l2cap.chan == chan)
continue;
nskb = skb_clone(skb, GFP_KERNEL);
if (!nskb)
continue;
if (chan->ops->recv(chan, nskb))
kfree_skb(nskb);
}
}
/* ---- L2CAP signalling commands ---- */
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
u8 ident, u16 dlen, void *data)
{
struct sk_buff *skb, **frag;
struct l2cap_cmd_hdr *cmd;
struct l2cap_hdr *lh;
int len, count;
BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %u",
conn, code, ident, dlen);
if (conn->mtu < L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE)
return NULL;
len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
count = min_t(unsigned int, conn->mtu, len);
skb = bt_skb_alloc(count, GFP_KERNEL);
if (!skb)
return NULL;
lh = skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
if (conn->hcon->type == LE_LINK)
lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING);
else
lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
cmd = skb_put(skb, L2CAP_CMD_HDR_SIZE);
cmd->code = code;
cmd->ident = ident;
cmd->len = cpu_to_le16(dlen);
if (dlen) {
count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
skb_put_data(skb, data, count);
data += count;
}
len -= skb->len;
/* Continuation fragments (no L2CAP header) */
frag = &skb_shinfo(skb)->frag_list;
while (len) {
count = min_t(unsigned int, conn->mtu, len);
*frag = bt_skb_alloc(count, GFP_KERNEL);
if (!*frag)
goto fail;
skb_put_data(*frag, data, count);
len -= count;
data += count;
frag = &(*frag)->next;
}
return skb;
fail:
kfree_skb(skb);
return NULL;
}
static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
unsigned long *val)
{
struct l2cap_conf_opt *opt = *ptr;
int len;
len = L2CAP_CONF_OPT_SIZE + opt->len;
*ptr += len;
*type = opt->type;
*olen = opt->len;
switch (opt->len) {
case 1:
*val = *((u8 *) opt->val);
break;
case 2:
*val = get_unaligned_le16(opt->val);
break;
case 4:
*val = get_unaligned_le32(opt->val);
break;
default:
*val = (unsigned long) opt->val;
break;
}
BT_DBG("type 0x%2.2x len %u val 0x%lx", *type, opt->len, *val);
return len;
}
static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val, size_t size)
{
struct l2cap_conf_opt *opt = *ptr;
BT_DBG("type 0x%2.2x len %u val 0x%lx", type, len, val);
if (size < L2CAP_CONF_OPT_SIZE + len)
return;
opt->type = type;
opt->len = len;
switch (len) {
case 1:
*((u8 *) opt->val) = val;
break;
case 2:
put_unaligned_le16(val, opt->val);
break;
case 4:
put_unaligned_le32(val, opt->val);
break;
default:
memcpy(opt->val, (void *) val, len);
break;
}
*ptr += L2CAP_CONF_OPT_SIZE + len;
}
static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan, size_t size)
{
struct l2cap_conf_efs efs;
switch (chan->mode) {
case L2CAP_MODE_ERTM:
efs.id = chan->local_id;
efs.stype = chan->local_stype;
efs.msdu = cpu_to_le16(chan->local_msdu);
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
efs.flush_to = cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO);
break;
case L2CAP_MODE_STREAMING:
efs.id = 1;
efs.stype = L2CAP_SERV_BESTEFFORT;
efs.msdu = cpu_to_le16(chan->local_msdu);
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
efs.acc_lat = 0;
efs.flush_to = 0;
break;
default:
return;
}
l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
(unsigned long) &efs, size);
}
static void l2cap_ack_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
ack_timer.work);
u16 frames_to_ack;
BT_DBG("chan %p", chan);
l2cap_chan_lock(chan);
frames_to_ack = __seq_offset(chan, chan->buffer_seq,
chan->last_acked_seq);
if (frames_to_ack)
l2cap_send_rr_or_rnr(chan, 0);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
int l2cap_ertm_init(struct l2cap_chan *chan)
{
int err;
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
chan->expected_ack_seq = 0;
chan->unacked_frames = 0;
chan->buffer_seq = 0;
chan->frames_sent = 0;
chan->last_acked_seq = 0;
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
skb_queue_head_init(&chan->tx_q);
if (chan->mode != L2CAP_MODE_ERTM)
return 0;
chan->rx_state = L2CAP_RX_STATE_RECV;
chan->tx_state = L2CAP_TX_STATE_XMIT;
skb_queue_head_init(&chan->srej_q);
err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win);
if (err < 0)
return err;
err = l2cap_seq_list_init(&chan->retrans_list, chan->remote_tx_win);
if (err < 0)
l2cap_seq_list_free(&chan->srej_list);
return err;
}
static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
{
switch (mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
if (l2cap_mode_supported(mode, remote_feat_mask))
return mode;
fallthrough;
default:
return L2CAP_MODE_BASIC;
}
}
static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
{
return (conn->feat_mask & L2CAP_FEAT_EXT_WINDOW);
}
static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
{
return (conn->feat_mask & L2CAP_FEAT_EXT_FLOW);
}
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
struct l2cap_conf_rfc *rfc)
{
rfc->retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
__l2cap_ews_supported(chan->conn)) {
/* use extended control field */
set_bit(FLAG_EXT_CTRL, &chan->flags);
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
} else {
chan->tx_win = min_t(u16, chan->tx_win,
L2CAP_DEFAULT_TX_WINDOW);
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
}
chan->ack_win = chan->tx_win;
}
static void l2cap_mtu_auto(struct l2cap_chan *chan)
{
struct hci_conn *conn = chan->conn->hcon;
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
/* The 2-DH1 packet has between 2 and 56 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_2DH1))
chan->imtu = 54;
/* The 3-DH1 packet has between 2 and 85 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_3DH1))
chan->imtu = 83;
/* The 2-DH3 packet has between 2 and 369 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_2DH3))
chan->imtu = 367;
/* The 3-DH3 packet has between 2 and 554 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_3DH3))
chan->imtu = 552;
/* The 2-DH5 packet has between 2 and 681 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_2DH5))
chan->imtu = 679;
/* The 3-DH5 packet has between 2 and 1023 information bytes
* (including the 2-byte payload header)
*/
if (!(conn->pkt_type & HCI_3DH5))
chan->imtu = 1021;
}
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
{
struct l2cap_conf_req *req = data;
struct l2cap_conf_rfc rfc = { .mode = chan->mode };
void *ptr = req->data;
void *endptr = data + data_size;
u16 size;
BT_DBG("chan %p", chan);
if (chan->num_conf_req || chan->num_conf_rsp)
goto done;
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
break;
if (__l2cap_efs_supported(chan->conn))
set_bit(FLAG_EFS_ENABLE, &chan->flags);
fallthrough;
default:
chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask);
break;
}
done:
if (chan->imtu != L2CAP_DEFAULT_MTU) {
if (!chan->imtu)
l2cap_mtu_auto(chan);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
endptr - ptr);
}
switch (chan->mode) {
case L2CAP_MODE_BASIC:
if (disable_ertm)
break;
if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
!(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
break;
rfc.mode = L2CAP_MODE_BASIC;
rfc.txwin_size = 0;
rfc.max_transmit = 0;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
rfc.max_pdu_size = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
break;
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
rfc.max_transmit = chan->max_tx;
__l2cap_set_ertm_timeouts(chan, &rfc);
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
l2cap_txwin_setup(chan);
rfc.txwin_size = min_t(u16, chan->tx_win,
L2CAP_DEFAULT_TX_WINDOW);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
chan->tx_win, endptr - ptr);
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
chan->fcs, endptr - ptr);
}
break;
case L2CAP_MODE_STREAMING:
l2cap_txwin_setup(chan);
rfc.mode = L2CAP_MODE_STREAMING;
rfc.txwin_size = 0;
rfc.max_transmit = 0;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
chan->fcs, endptr - ptr);
}
break;
}
req->dcid = cpu_to_le16(chan->dcid);
req->flags = cpu_to_le16(0);
return ptr - data;
}
static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
{
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
void *endptr = data + data_size;
void *req = chan->conf_req;
int len = chan->conf_len;
int type, hint, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
struct l2cap_conf_efs efs;
u8 remote_efs = 0;
u16 mtu = 0;
u16 result = L2CAP_CONF_SUCCESS;
u16 size;
BT_DBG("chan %p", chan);
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
if (len < 0)
break;
hint = type & L2CAP_CONF_HINT;
type &= L2CAP_CONF_MASK;
switch (type) {
case L2CAP_CONF_MTU:
if (olen != 2)
break;
mtu = val;
break;
case L2CAP_CONF_FLUSH_TO:
if (olen != 2)
break;
chan->flush_to = val;
break;
case L2CAP_CONF_QOS:
break;
case L2CAP_CONF_RFC:
if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *) val, olen);
break;
case L2CAP_CONF_FCS:
if (olen != 1)
break;
if (val == L2CAP_FCS_NONE)
set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
break;
case L2CAP_CONF_EFS:
if (olen != sizeof(efs))
break;
remote_efs = 1;
memcpy(&efs, (void *) val, olen);
break;
case L2CAP_CONF_EWS:
if (olen != 2)
break;
return -ECONNREFUSED;
default:
if (hint)
break;
result = L2CAP_CONF_UNKNOWN;
l2cap_add_conf_opt(&ptr, (u8)type, sizeof(u8), type, endptr - ptr);
break;
}
}
if (chan->num_conf_rsp || chan->num_conf_req > 1)
goto done;
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
chan->mode = l2cap_select_mode(rfc.mode,
chan->conn->feat_mask);
break;
}
if (remote_efs) {
if (__l2cap_efs_supported(chan->conn))
set_bit(FLAG_EFS_ENABLE, &chan->flags);
else
return -ECONNREFUSED;
}
if (chan->mode != rfc.mode)
return -ECONNREFUSED;
break;
}
done:
if (chan->mode != rfc.mode) {
result = L2CAP_CONF_UNACCEPT;
rfc.mode = chan->mode;
if (chan->num_conf_rsp == 1)
return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
}
if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know
* which ones we don't like. */
/* If MTU is not provided in configure request, use the most recently
* explicitly or implicitly accepted value for the other direction,
* or the default value.
*/
if (mtu == 0)
mtu = chan->imtu ? chan->imtu : L2CAP_DEFAULT_MTU;
if (mtu < L2CAP_DEFAULT_MIN_MTU)
result = L2CAP_CONF_UNACCEPT;
else {
chan->omtu = mtu;
set_bit(CONF_MTU_DONE, &chan->conf_state);
}
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr);
if (remote_efs) {
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != chan->local_stype) {
result = L2CAP_CONF_UNACCEPT;
if (chan->num_conf_req >= 1)
return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
sizeof(efs),
(unsigned long) &efs, endptr - ptr);
} else {
/* Send PENDING Conf Rsp */
result = L2CAP_CONF_PENDING;
set_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
}
}
switch (rfc.mode) {
case L2CAP_MODE_BASIC:
chan->fcs = L2CAP_FCS_NONE;
set_bit(CONF_MODE_DONE, &chan->conf_state);
break;
case L2CAP_MODE_ERTM:
if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
chan->remote_tx_win = rfc.txwin_size;
else
rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
chan->remote_max_tx = rfc.max_transmit;
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size;
__l2cap_set_ertm_timeouts(chan, &rfc);
set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
if (remote_efs &&
test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
chan->remote_id = efs.id;
chan->remote_stype = efs.stype;
chan->remote_msdu = le16_to_cpu(efs.msdu);
chan->remote_flush_to =
le32_to_cpu(efs.flush_to);
chan->remote_acc_lat =
le32_to_cpu(efs.acc_lat);
chan->remote_sdu_itime =
le32_to_cpu(efs.sdu_itime);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
sizeof(efs),
(unsigned long) &efs, endptr - ptr);
}
break;
case L2CAP_MODE_STREAMING:
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size;
set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
break;
default:
result = L2CAP_CONF_UNACCEPT;
memset(&rfc, 0, sizeof(rfc));
rfc.mode = chan->mode;
}
if (result == L2CAP_CONF_SUCCESS)
set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
}
rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(0);
return ptr - data;
}
static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
void *data, size_t size, u16 *result)
{
struct l2cap_conf_req *req = data;
void *ptr = req->data;
void *endptr = data + size;
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
struct l2cap_conf_efs efs;
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
if (len < 0)
break;
switch (type) {
case L2CAP_CONF_MTU:
if (olen != 2)
break;
if (val < L2CAP_DEFAULT_MIN_MTU) {
*result = L2CAP_CONF_UNACCEPT;
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
} else
chan->imtu = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
endptr - ptr);
break;
case L2CAP_CONF_FLUSH_TO:
if (olen != 2)
break;
chan->flush_to = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
chan->flush_to, endptr - ptr);
break;
case L2CAP_CONF_RFC:
if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *)val, olen);
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
rfc.mode != chan->mode)
return -ECONNREFUSED;
chan->fcs = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc, endptr - ptr);
break;
case L2CAP_CONF_EWS:
if (olen != 2)
break;
chan->ack_win = min_t(u16, val, chan->ack_win);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
chan->tx_win, endptr - ptr);
break;
case L2CAP_CONF_EFS:
if (olen != sizeof(efs))
break;
memcpy(&efs, (void *)val, olen);
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != chan->local_stype)
return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
(unsigned long) &efs, endptr - ptr);
break;
case L2CAP_CONF_FCS:
if (olen != 1)
break;
if (*result == L2CAP_CONF_PENDING)
if (val == L2CAP_FCS_NONE)
set_bit(CONF_RECV_NO_FCS,
&chan->conf_state);
break;
}
}
if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode)
return -ECONNREFUSED;
chan->mode = rfc.mode;
if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) {
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
chan->mps = le16_to_cpu(rfc.max_pdu_size);
if (!test_bit(FLAG_EXT_CTRL, &chan->flags))
chan->ack_win = min_t(u16, chan->ack_win,
rfc.txwin_size);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
chan->local_msdu = le16_to_cpu(efs.msdu);
chan->local_sdu_itime =
le32_to_cpu(efs.sdu_itime);
chan->local_acc_lat = le32_to_cpu(efs.acc_lat);
chan->local_flush_to =
le32_to_cpu(efs.flush_to);
}
break;
case L2CAP_MODE_STREAMING:
chan->mps = le16_to_cpu(rfc.max_pdu_size);
}
}
req->dcid = cpu_to_le16(chan->dcid);
req->flags = cpu_to_le16(0);
return ptr - data;
}
static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
u16 result, u16 flags)
{
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
BT_DBG("chan %p", chan);
rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(flags);
return ptr - data;
}
void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
{
struct l2cap_le_conn_rsp rsp;
struct l2cap_conn *conn = chan->conn;
BT_DBG("chan %p", chan);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
&rsp);
}
static void l2cap_ecred_list_defer(struct l2cap_chan *chan, void *data)
{
int *result = data;
if (*result || test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
switch (chan->state) {
case BT_CONNECT2:
/* If channel still pending accept add to result */
(*result)++;
return;
case BT_CONNECTED:
return;
default:
/* If not connected or pending accept it has been refused */
*result = -ECONNREFUSED;
return;
}
}
struct l2cap_ecred_rsp_data {
struct {
struct l2cap_ecred_conn_rsp_hdr rsp;
__le16 scid[L2CAP_ECRED_MAX_CID];
} __packed pdu;
int count;
};
static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
{
struct l2cap_ecred_rsp_data *rsp = data;
struct l2cap_ecred_conn_rsp *rsp_flex =
container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr);
/* Check if channel for outgoing connection or if it wasn't deferred
* since in those cases it must be skipped.
*/
if (test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags) ||
!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
/* Reset ident so only one response is sent */
chan->ident = 0;
/* Include all channels pending with the same ident */
if (!rsp->pdu.rsp.result)
rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid);
else
l2cap_chan_del(chan, ECONNRESET);
}
void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_ecred_rsp_data data;
u16 id = chan->ident;
int result = 0;
if (!id)
return;
BT_DBG("chan %p id %d", chan, id);
memset(&data, 0, sizeof(data));
data.pdu.rsp.mtu = cpu_to_le16(chan->imtu);
data.pdu.rsp.mps = cpu_to_le16(chan->mps);
data.pdu.rsp.credits = cpu_to_le16(chan->rx_credits);
data.pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS);
/* Verify that all channels are ready */
__l2cap_chan_list_id(conn, id, l2cap_ecred_list_defer, &result);
if (result > 0)
return;
if (result < 0)
data.pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_AUTHORIZATION);
/* Build response */
__l2cap_chan_list_id(conn, id, l2cap_ecred_rsp_defer, &data);
l2cap_send_cmd(conn, id, L2CAP_ECRED_CONN_RSP,
sizeof(data.pdu.rsp) + (data.count * sizeof(__le16)),
&data.pdu);
}
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
{
struct l2cap_conn_rsp rsp;
struct l2cap_conn *conn = chan->conn;
u8 buf[128];
u8 rsp_code;
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
rsp_code = L2CAP_CONN_RSP;
BT_DBG("chan %p rsp_code %u", chan, rsp_code);
l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp);
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
chan->num_conf_req++;
}
static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
{
int type, olen;
unsigned long val;
/* Use sane default values in case a misbehaving remote device
* did not send an RFC or extended window size option.
*/
u16 txwin_ext = chan->ack_win;
struct l2cap_conf_rfc rfc = {
.mode = chan->mode,
.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO),
.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO),
.max_pdu_size = cpu_to_le16(chan->imtu),
.txwin_size = min_t(u16, chan->ack_win, L2CAP_DEFAULT_TX_WINDOW),
};
BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len);
if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING))
return;
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
if (len < 0)
break;
switch (type) {
case L2CAP_CONF_RFC:
if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *)val, olen);
break;
case L2CAP_CONF_EWS:
if (olen != 2)
break;
txwin_ext = val;
break;
}
}
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
chan->mps = le16_to_cpu(rfc.max_pdu_size);
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
chan->ack_win = min_t(u16, chan->ack_win, txwin_ext);
else
chan->ack_win = min_t(u16, chan->ack_win,
rfc.txwin_size);
break;
case L2CAP_MODE_STREAMING:
chan->mps = le16_to_cpu(rfc.max_pdu_size);
}
}
static inline int l2cap_command_rej(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
if (cmd_len < sizeof(*rej))
return -EPROTO;
if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
return 0;
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) {
cancel_delayed_work(&conn->info_timer);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
}
return 0;
}
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
u8 *data, u8 rsp_code)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
struct l2cap_chan *chan = NULL, *pchan = NULL;
int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
__le16 psm = req->psm;
BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
/* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
&conn->hcon->dst, ACL_LINK);
if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto response;
}
l2cap_chan_lock(pchan);
/* Check if the ACL is secure enough (if not SDP) */
if (psm != cpu_to_le16(L2CAP_PSM_SDP) &&
(!hci_conn_check_link_mode(conn->hcon) ||
!l2cap_check_enc_key_size(conn->hcon, pchan))) {
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
result = L2CAP_CR_SEC_BLOCK;
goto response;
}
result = L2CAP_CR_NO_MEM;
/* Check for valid dynamic CID range (as per Erratum 3253) */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_DYN_END) {
result = L2CAP_CR_INVALID_SCID;
goto response;
}
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(conn, scid)) {
result = L2CAP_CR_SCID_IN_USE;
goto response;
}
chan = pchan->ops->new_connection(pchan);
if (!chan)
goto response;
/* For certain devices (ex: HID mouse), support for authentication,
* pairing and bonding is optional. For such devices, inorder to avoid
* the ACL alive for too long after L2CAP disconnection, reset the ACL
* disc_timeout back to HCI_DISCONN_TIMEOUT during L2CAP connect.
*/
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
chan->dst_type = bdaddr_dst_type(conn->hcon);
chan->psm = psm;
chan->dcid = scid;
__l2cap_chan_add(conn, chan);
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan, false)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} else {
l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
response:
rsp.scid = cpu_to_le16(scid);
rsp.dcid = cpu_to_le16(dcid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(status);
l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
if (!pchan)
return;
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
struct l2cap_info_req info;
info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
sizeof(info), &info);
}
if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
result == L2CAP_CR_SUCCESS) {
u8 buf[128];
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
chan->num_conf_req++;
}
l2cap_chan_unlock(pchan);
l2cap_chan_put(pchan);
}
static int l2cap_connect_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
{
if (cmd_len < sizeof(struct l2cap_conn_req))
return -EPROTO;
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP);
return 0;
}
static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
struct l2cap_chan *chan;
u8 req[128];
int err;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
result = __le16_to_cpu(rsp->result);
status = __le16_to_cpu(rsp->status);
if (result == L2CAP_CR_SUCCESS && (dcid < L2CAP_CID_DYN_START ||
dcid > L2CAP_CID_DYN_END))
return -EPROTO;
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
dcid, scid, result, status);
if (scid) {
chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan)
return -EBADSLT;
} else {
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan)
return -EBADSLT;
}
chan = l2cap_chan_hold_unless_zero(chan);
if (!chan)
return -EBADSLT;
err = 0;
l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CR_SUCCESS:
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
err = -EBADSLT;
break;
}
l2cap_state_change(chan, BT_CONFIG);
chan->ident = 0;
chan->dcid = dcid;
clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
break;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, req, sizeof(req)), req);
chan->num_conf_req++;
break;
case L2CAP_CR_PEND:
set_bit(CONF_CONNECT_PEND, &chan->conf_state);
break;
default:
l2cap_chan_del(chan, ECONNREFUSED);
break;
}
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return err;
}
static inline void set_default_fcs(struct l2cap_chan *chan)
{
/* FCS is enabled only in ERTM or streaming mode, if one or both
* sides request it.
*/
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
chan->fcs = L2CAP_FCS_NONE;
else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state))
chan->fcs = L2CAP_FCS_CRC16;
}
static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
u8 ident, u16 flags)
{
struct l2cap_conn *conn = chan->conn;
BT_DBG("conn %p chan %p ident %d flags 0x%4.4x", conn, chan, ident,
flags);
clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
l2cap_send_cmd(conn, ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, data,
L2CAP_CONF_SUCCESS, flags), data);
}
static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
u16 scid, u16 dcid)
{
struct l2cap_cmd_rej_cid rej;
rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID);
rej.scid = __cpu_to_le16(scid);
rej.dcid = __cpu_to_le16(dcid);
l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
}
static inline int l2cap_config_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;
u16 dcid, flags;
u8 rsp[64];
struct l2cap_chan *chan;
int len, err = 0;
if (cmd_len < sizeof(*req))
return -EPROTO;
dcid = __le16_to_cpu(req->dcid);
flags = __le16_to_cpu(req->flags);
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
chan = l2cap_get_chan_by_scid(conn, dcid);
if (!chan) {
cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
return 0;
}
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2 &&
chan->state != BT_CONNECTED) {
cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
chan->dcid);
goto unlock;
}
/* Reject if config buffer is too small. */
len = cmd_len - sizeof(*req);
if (chan->conf_len + len > sizeof(chan->conf_req)) {
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_REJECT, flags), rsp);
goto unlock;
}
/* Store config. */
memcpy(chan->conf_req + chan->conf_len, req->data, len);
chan->conf_len += len;
if (flags & L2CAP_CONF_FLAG_CONTINUATION) {
/* Incomplete config. Send empty response. */
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_SUCCESS, flags), rsp);
goto unlock;
}
/* Complete config. */
len = l2cap_parse_conf_req(chan, rsp, sizeof(rsp));
if (len < 0) {
l2cap_send_disconn_req(chan, ECONNRESET);
goto unlock;
}
chan->ident = cmd->ident;
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
if (chan->num_conf_rsp < L2CAP_CONF_MAX_CONF_RSP)
chan->num_conf_rsp++;
/* Reset config buffer. */
chan->conf_len = 0;
if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state))
goto unlock;
if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
if (chan->mode == L2CAP_MODE_ERTM ||
chan->mode == L2CAP_MODE_STREAMING)
err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
goto unlock;
}
if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
u8 buf[64];
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
chan->num_conf_req++;
}
/* Got Conf Rsp PENDING from remote side and assume we sent
Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
/* check compatibility */
/* Send rsp for BR/EDR channel */
l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags);
}
unlock:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return err;
}
static inline int l2cap_config_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
struct l2cap_chan *chan;
int len = cmd_len - sizeof(*rsp);
int err = 0;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
scid = __le16_to_cpu(rsp->scid);
flags = __le16_to_cpu(rsp->flags);
result = __le16_to_cpu(rsp->result);
BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x len %d", scid, flags,
result, len);
chan = l2cap_get_chan_by_scid(conn, scid);
if (!chan)
return 0;
switch (result) {
case L2CAP_CONF_SUCCESS:
l2cap_conf_rfc_get(chan, rsp->data, len);
clear_bit(CONF_REM_CONF_PEND, &chan->conf_state);
break;
case L2CAP_CONF_PENDING:
set_bit(CONF_REM_CONF_PEND, &chan->conf_state);
if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
char buf[64];
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
buf, sizeof(buf), &result);
if (len < 0) {
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0);
}
goto done;
case L2CAP_CONF_UNKNOWN:
case L2CAP_CONF_UNACCEPT:
if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
char req[64];
if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
/* throw out any old stored conf requests */
result = L2CAP_CONF_SUCCESS;
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
req, sizeof(req), &result);
if (len < 0) {
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONF_REQ, len, req);
chan->num_conf_req++;
if (result != L2CAP_CONF_SUCCESS)
goto done;
break;
}
fallthrough;
default:
l2cap_chan_set_err(chan, ECONNRESET);
__set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
if (flags & L2CAP_CONF_FLAG_CONTINUATION)
goto done;
set_bit(CONF_INPUT_DONE, &chan->conf_state);
if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
if (chan->mode == L2CAP_MODE_ERTM ||
chan->mode == L2CAP_MODE_STREAMING)
err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
}
done:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return err;
}
static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
struct l2cap_disconn_rsp rsp;
u16 dcid, scid;
struct l2cap_chan *chan;
if (cmd_len != sizeof(*req))
return -EPROTO;
scid = __le16_to_cpu(req->scid);
dcid = __le16_to_cpu(req->dcid);
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
chan = l2cap_get_chan_by_scid(conn, dcid);
if (!chan) {
cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
return 0;
}
rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
chan->ops->set_shutdown(chan);
l2cap_chan_del(chan, ECONNRESET);
chan->ops->close(chan);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return 0;
}
static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
struct l2cap_chan *chan;
if (cmd_len != sizeof(*rsp))
return -EPROTO;
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
chan = l2cap_get_chan_by_scid(conn, scid);
if (!chan) {
return 0;
}
if (chan->state != BT_DISCONN) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return 0;
}
l2cap_chan_del(chan, 0);
chan->ops->close(chan);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return 0;
}
static inline int l2cap_information_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_info_req *req = (struct l2cap_info_req *) data;
u16 type;
if (cmd_len != sizeof(*req))
return -EPROTO;
type = __le16_to_cpu(req->type);
BT_DBG("type 0x%4.4x", type);
if (type == L2CAP_IT_FEAT_MASK) {
u8 buf[8];
u32 feat_mask = l2cap_feat_mask;
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
put_unaligned_le32(feat_mask, rsp->data);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
buf);
} else if (type == L2CAP_IT_FIXED_CHAN) {
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
rsp->data[0] = conn->local_fixed_chan;
memset(rsp->data + 1, 0, 7);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
buf);
} else {
struct l2cap_info_rsp rsp;
rsp.type = cpu_to_le16(type);
rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp),
&rsp);
}
return 0;
}
static inline int l2cap_information_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
u16 type, result;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
type = __le16_to_cpu(rsp->type);
result = __le16_to_cpu(rsp->result);
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
/* L2CAP Info req/rsp are unbound to channels, add extra checks */
if (cmd->ident != conn->info_ident ||
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
return 0;
cancel_delayed_work(&conn->info_timer);
if (result != L2CAP_IR_SUCCESS) {
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
return 0;
}
switch (type) {
case L2CAP_IT_FEAT_MASK:
conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
struct l2cap_info_req req;
req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
conn->info_ident = l2cap_get_ident(conn);
l2cap_send_cmd(conn, conn->info_ident,
L2CAP_INFO_REQ, sizeof(req), &req);
} else {
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
}
break;
case L2CAP_IT_FIXED_CHAN:
conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
break;
}
return 0;
}
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, u8 *data)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_conn_param_update_req *req;
struct l2cap_conn_param_update_rsp rsp;
u16 min, max, latency, to_multiplier;
int err;
if (hcon->role != HCI_ROLE_MASTER)
return -EINVAL;
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
return -EPROTO;
req = (struct l2cap_conn_param_update_req *) data;
min = __le16_to_cpu(req->min);
max = __le16_to_cpu(req->max);
latency = __le16_to_cpu(req->latency);
to_multiplier = __le16_to_cpu(req->to_multiplier);
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x",
min, max, latency, to_multiplier);
memset(&rsp, 0, sizeof(rsp));
err = hci_check_conn_params(min, max, latency, to_multiplier);
if (err)
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
else
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
sizeof(rsp), &rsp);
if (!err) {
u8 store_hint;
store_hint = hci_le_conn_update(hcon, min, max, latency,
to_multiplier);
mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
store_hint, min, max, latency,
to_multiplier);
}
return 0;
}
static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
struct hci_conn *hcon = conn->hcon;
u16 dcid, mtu, mps, credits, result;
struct l2cap_chan *chan;
int err, sec_level;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
dcid = __le16_to_cpu(rsp->dcid);
mtu = __le16_to_cpu(rsp->mtu);
mps = __le16_to_cpu(rsp->mps);
credits = __le16_to_cpu(rsp->credits);
result = __le16_to_cpu(rsp->result);
if (result == L2CAP_CR_LE_SUCCESS && (mtu < 23 || mps < 23 ||
dcid < L2CAP_CID_DYN_START ||
dcid > L2CAP_CID_LE_DYN_END))
return -EPROTO;
BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
dcid, mtu, mps, credits, result);
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan)
return -EBADSLT;
err = 0;
l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CR_LE_SUCCESS:
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
err = -EBADSLT;
break;
}
chan->ident = 0;
chan->dcid = dcid;
chan->omtu = mtu;
chan->remote_mps = mps;
chan->tx_credits = credits;
l2cap_chan_ready(chan);
break;
case L2CAP_CR_LE_AUTHENTICATION:
case L2CAP_CR_LE_ENCRYPTION:
/* If we already have MITM protection we can't do
* anything.
*/
if (hcon->sec_level > BT_SECURITY_MEDIUM) {
l2cap_chan_del(chan, ECONNREFUSED);
break;
}
sec_level = hcon->sec_level + 1;
if (chan->sec_level < sec_level)
chan->sec_level = sec_level;
/* We'll need to send a new Connect Request */
clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
smp_conn_security(hcon, chan->sec_level);
break;
default:
l2cap_chan_del(chan, ECONNREFUSED);
break;
}
l2cap_chan_unlock(chan);
return err;
}
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
int err = 0;
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
l2cap_command_rej(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_REQ:
err = l2cap_connect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_RSP:
l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_CONF_REQ:
err = l2cap_config_req(conn, cmd, cmd_len, data);
break;
case L2CAP_CONF_RSP:
l2cap_config_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_REQ:
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_RSP:
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_ECHO_REQ:
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data);
break;
case L2CAP_ECHO_RSP:
break;
case L2CAP_INFO_REQ:
err = l2cap_information_req(conn, cmd, cmd_len, data);
break;
case L2CAP_INFO_RSP:
l2cap_information_rsp(conn, cmd, cmd_len, data);
break;
default:
BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
err = -EINVAL;
break;
}
return err;
}
static int l2cap_le_connect_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data;
struct l2cap_le_conn_rsp rsp;
struct l2cap_chan *chan, *pchan;
u16 dcid, scid, credits, mtu, mps;
__le16 psm;
u8 result;
if (cmd_len != sizeof(*req))
return -EPROTO;
scid = __le16_to_cpu(req->scid);
mtu = __le16_to_cpu(req->mtu);
mps = __le16_to_cpu(req->mps);
psm = req->psm;
dcid = 0;
credits = 0;
if (mtu < 23 || mps < 23)
return -EPROTO;
BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm),
scid, mtu, mps);
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 3, Part A
* page 1059:
*
* Valid range: 0x0001-0x00ff
*
* Table 4.15: L2CAP_LE_CREDIT_BASED_CONNECTION_REQ SPSM ranges
*/
if (!psm || __le16_to_cpu(psm) > L2CAP_PSM_LE_DYN_END) {
result = L2CAP_CR_LE_BAD_PSM;
chan = NULL;
goto response;
}
/* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
&conn->hcon->dst, LE_LINK);
if (!pchan) {
result = L2CAP_CR_LE_BAD_PSM;
chan = NULL;
goto response;
}
l2cap_chan_lock(pchan);
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
SMP_ALLOW_STK)) {
result = pchan->sec_level == BT_SECURITY_MEDIUM ?
L2CAP_CR_LE_ENCRYPTION : L2CAP_CR_LE_AUTHENTICATION;
chan = NULL;
goto response_unlock;
}
/* Check for valid dynamic CID range */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
result = L2CAP_CR_LE_INVALID_SCID;
chan = NULL;
goto response_unlock;
}
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(conn, scid)) {
result = L2CAP_CR_LE_SCID_IN_USE;
chan = NULL;
goto response_unlock;
}
chan = pchan->ops->new_connection(pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
goto response_unlock;
}
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
chan->dst_type = bdaddr_dst_type(conn->hcon);
chan->psm = psm;
chan->dcid = scid;
chan->omtu = mtu;
chan->remote_mps = mps;
__l2cap_chan_add(conn, chan);
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
credits = chan->rx_credits;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
/* The following result value is actually not defined
* for LE CoC but we use it to let the function know
* that it should bail out after doing its cleanup
* instead of sending a response.
*/
result = L2CAP_CR_PEND;
chan->ops->defer(chan);
} else {
l2cap_chan_ready(chan);
result = L2CAP_CR_LE_SUCCESS;
}
response_unlock:
l2cap_chan_unlock(pchan);
l2cap_chan_put(pchan);
if (result == L2CAP_CR_PEND)
return 0;
response:
if (chan) {
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = cpu_to_le16(chan->mps);
} else {
rsp.mtu = 0;
rsp.mps = 0;
}
rsp.dcid = cpu_to_le16(dcid);
rsp.credits = cpu_to_le16(credits);
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp);
return 0;
}
static inline int l2cap_le_credits(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_le_credits *pkt;
struct l2cap_chan *chan;
u16 cid, credits, max_credits;
if (cmd_len != sizeof(*pkt))
return -EPROTO;
pkt = (struct l2cap_le_credits *) data;
cid = __le16_to_cpu(pkt->cid);
credits = __le16_to_cpu(pkt->credits);
BT_DBG("cid 0x%4.4x credits 0x%4.4x", cid, credits);
chan = l2cap_get_chan_by_dcid(conn, cid);
if (!chan)
return -EBADSLT;
max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
if (credits > max_credits) {
BT_ERR("LE credits overflow");
l2cap_send_disconn_req(chan, ECONNRESET);
/* Return 0 so that we don't trigger an unnecessary
* command reject packet.
*/
goto unlock;
}
chan->tx_credits += credits;
/* Resume sending */
l2cap_le_flowctl_send(chan);
if (chan->tx_credits)
chan->ops->resume(chan);
unlock:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return 0;
}
static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_ecred_conn_req *req = (void *) data;
DEFINE_RAW_FLEX(struct l2cap_ecred_conn_rsp, pdu, dcid, L2CAP_ECRED_MAX_CID);
struct l2cap_chan *chan, *pchan;
u16 mtu, mps;
__le16 psm;
u8 result, len = 0;
int i, num_scid;
bool defer = false;
if (!enable_ecred)
return -EINVAL;
if (cmd_len < sizeof(*req) || (cmd_len - sizeof(*req)) % sizeof(u16)) {
result = L2CAP_CR_LE_INVALID_PARAMS;
goto response;
}
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
if (num_scid > L2CAP_ECRED_MAX_CID) {
result = L2CAP_CR_LE_INVALID_PARAMS;
goto response;
}
mtu = __le16_to_cpu(req->mtu);
mps = __le16_to_cpu(req->mps);
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MPS) {
result = L2CAP_CR_LE_UNACCEPT_PARAMS;
goto response;
}
psm = req->psm;
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 3, Part A
* page 1059:
*
* Valid range: 0x0001-0x00ff
*
* Table 4.15: L2CAP_LE_CREDIT_BASED_CONNECTION_REQ SPSM ranges
*/
if (!psm || __le16_to_cpu(psm) > L2CAP_PSM_LE_DYN_END) {
result = L2CAP_CR_LE_BAD_PSM;
goto response;
}
BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps);
memset(pdu, 0, sizeof(*pdu));
/* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
&conn->hcon->dst, LE_LINK);
if (!pchan) {
result = L2CAP_CR_LE_BAD_PSM;
goto response;
}
l2cap_chan_lock(pchan);
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
SMP_ALLOW_STK)) {
result = L2CAP_CR_LE_AUTHENTICATION;
goto unlock;
}
result = L2CAP_CR_LE_SUCCESS;
for (i = 0; i < num_scid; i++) {
u16 scid = __le16_to_cpu(req->scid[i]);
BT_DBG("scid[%d] 0x%4.4x", i, scid);
pdu->dcid[i] = 0x0000;
len += sizeof(*pdu->dcid);
/* Check for valid dynamic CID range */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
result = L2CAP_CR_LE_INVALID_SCID;
continue;
}
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(conn, scid)) {
result = L2CAP_CR_LE_SCID_IN_USE;
continue;
}
chan = pchan->ops->new_connection(pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
continue;
}
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_src_type(conn->hcon);
chan->dst_type = bdaddr_dst_type(conn->hcon);
chan->psm = psm;
chan->dcid = scid;
chan->omtu = mtu;
chan->remote_mps = mps;
__l2cap_chan_add(conn, chan);
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
if (!pdu->credits) {
pdu->mtu = cpu_to_le16(chan->imtu);
pdu->mps = cpu_to_le16(chan->mps);
pdu->credits = cpu_to_le16(chan->rx_credits);
}
pdu->dcid[i] = cpu_to_le16(chan->scid);
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
chan->mode = L2CAP_MODE_EXT_FLOWCTL;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
defer = true;
chan->ops->defer(chan);
} else {
l2cap_chan_ready(chan);
}
}
unlock:
l2cap_chan_unlock(pchan);
l2cap_chan_put(pchan);
response:
pdu->result = cpu_to_le16(result);
if (defer)
return 0;
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_CONN_RSP,
sizeof(*pdu) + len, pdu);
return 0;
}
static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_ecred_conn_rsp *rsp = (void *) data;
struct hci_conn *hcon = conn->hcon;
u16 mtu, mps, credits, result;
struct l2cap_chan *chan, *tmp;
int err = 0, sec_level;
int i = 0;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
mtu = __le16_to_cpu(rsp->mtu);
mps = __le16_to_cpu(rsp->mps);
credits = __le16_to_cpu(rsp->credits);
result = __le16_to_cpu(rsp->result);
BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits,
result);
cmd_len -= sizeof(*rsp);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
u16 dcid;
if (chan->ident != cmd->ident ||
chan->mode != L2CAP_MODE_EXT_FLOWCTL ||
chan->state == BT_CONNECTED)
continue;
l2cap_chan_lock(chan);
/* Check that there is a dcid for each pending channel */
if (cmd_len < sizeof(dcid)) {
l2cap_chan_del(chan, ECONNREFUSED);
l2cap_chan_unlock(chan);
continue;
}
dcid = __le16_to_cpu(rsp->dcid[i++]);
cmd_len -= sizeof(u16);
BT_DBG("dcid[%d] 0x%4.4x", i, dcid);
/* Check if dcid is already in use */
if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) {
/* If a device receives a
* L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an
* already-assigned Destination CID, then both the
* original channel and the new channel shall be
* immediately discarded and not used.
*/
l2cap_chan_del(chan, ECONNREFUSED);
l2cap_chan_unlock(chan);
chan = __l2cap_get_chan_by_dcid(conn, dcid);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, ECONNRESET);
l2cap_chan_unlock(chan);
continue;
}
switch (result) {
case L2CAP_CR_LE_AUTHENTICATION:
case L2CAP_CR_LE_ENCRYPTION:
/* If we already have MITM protection we can't do
* anything.
*/
if (hcon->sec_level > BT_SECURITY_MEDIUM) {
l2cap_chan_del(chan, ECONNREFUSED);
break;
}
sec_level = hcon->sec_level + 1;
if (chan->sec_level < sec_level)
chan->sec_level = sec_level;
/* We'll need to send a new Connect Request */
clear_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags);
smp_conn_security(hcon, chan->sec_level);
break;
case L2CAP_CR_LE_BAD_PSM:
l2cap_chan_del(chan, ECONNREFUSED);
break;
default:
/* If dcid was not set it means channels was refused */
if (!dcid) {
l2cap_chan_del(chan, ECONNREFUSED);
break;
}
chan->ident = 0;
chan->dcid = dcid;
chan->omtu = mtu;
chan->remote_mps = mps;
chan->tx_credits = credits;
l2cap_chan_ready(chan);
break;
}
l2cap_chan_unlock(chan);
}
return err;
}
static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_ecred_reconf_req *req = (void *) data;
struct l2cap_ecred_reconf_rsp rsp;
u16 mtu, mps, result;
struct l2cap_chan *chan;
int i, num_scid;
if (!enable_ecred)
return -EINVAL;
if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) {
result = L2CAP_CR_LE_INVALID_PARAMS;
goto respond;
}
mtu = __le16_to_cpu(req->mtu);
mps = __le16_to_cpu(req->mps);
BT_DBG("mtu %u mps %u", mtu, mps);
if (mtu < L2CAP_ECRED_MIN_MTU) {
result = L2CAP_RECONF_INVALID_MTU;
goto respond;
}
if (mps < L2CAP_ECRED_MIN_MPS) {
result = L2CAP_RECONF_INVALID_MPS;
goto respond;
}
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
result = L2CAP_RECONF_SUCCESS;
for (i = 0; i < num_scid; i++) {
u16 scid;
scid = __le16_to_cpu(req->scid[i]);
if (!scid)
return -EPROTO;
chan = __l2cap_get_chan_by_dcid(conn, scid);
if (!chan)
continue;
/* If the MTU value is decreased for any of the included
* channels, then the receiver shall disconnect all
* included channels.
*/
if (chan->omtu > mtu) {
BT_ERR("chan %p decreased MTU %u -> %u", chan,
chan->omtu, mtu);
result = L2CAP_RECONF_INVALID_MTU;
}
chan->omtu = mtu;
chan->remote_mps = mps;
}
respond:
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_RECONF_RSP, sizeof(rsp),
&rsp);
return 0;
}
static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_chan *chan, *tmp;
struct l2cap_ecred_conn_rsp *rsp = (void *) data;
u16 result;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
result = __le16_to_cpu(rsp->result);
BT_DBG("result 0x%4.4x", rsp->result);
if (!result)
return 0;
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
if (chan->ident != cmd->ident)
continue;
l2cap_chan_del(chan, ECONNRESET);
}
return 0;
}
static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
struct l2cap_chan *chan;
if (cmd_len < sizeof(*rej))
return -EPROTO;
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan)
goto done;
chan = l2cap_chan_hold_unless_zero(chan);
if (!chan)
goto done;
l2cap_chan_lock(chan);
l2cap_chan_del(chan, ECONNREFUSED);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
done:
return 0;
}
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
int err = 0;
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
l2cap_le_command_rej(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_PARAM_UPDATE_REQ:
err = l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_PARAM_UPDATE_RSP:
break;
case L2CAP_LE_CONN_RSP:
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_LE_CONN_REQ:
err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_LE_CREDITS:
err = l2cap_le_credits(conn, cmd, cmd_len, data);
break;
case L2CAP_ECRED_CONN_REQ:
err = l2cap_ecred_conn_req(conn, cmd, cmd_len, data);
break;
case L2CAP_ECRED_CONN_RSP:
err = l2cap_ecred_conn_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_ECRED_RECONF_REQ:
err = l2cap_ecred_reconf_req(conn, cmd, cmd_len, data);
break;
case L2CAP_ECRED_RECONF_RSP:
err = l2cap_ecred_reconf_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_REQ:
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_RSP:
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
break;
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
err = -EINVAL;
break;
}
return err;
}
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_cmd_hdr *cmd;
u16 len;
int err;
if (hcon->type != LE_LINK)
goto drop;
if (skb->len < L2CAP_CMD_HDR_SIZE)
goto drop;
cmd = (void *) skb->data;
skb_pull(skb, L2CAP_CMD_HDR_SIZE);
len = le16_to_cpu(cmd->len);
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
if (len != skb->len || !cmd->ident) {
BT_DBG("corrupted command");
goto drop;
}
err = l2cap_le_sig_cmd(conn, cmd, len, skb->data);
if (err) {
struct l2cap_cmd_rej_unk rej;
BT_ERR("Wrong link type (%d)", err);
rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
}
drop:
kfree_skb(skb);
}
static inline void l2cap_sig_send_rej(struct l2cap_conn *conn, u16 ident)
{
struct l2cap_cmd_rej_unk rej;
rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
}
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_cmd_hdr *cmd;
int err;
l2cap_raw_recv(conn, skb);
if (hcon->type != ACL_LINK)
goto drop;
while (skb->len >= L2CAP_CMD_HDR_SIZE) {
u16 len;
cmd = (void *) skb->data;
skb_pull(skb, L2CAP_CMD_HDR_SIZE);
len = le16_to_cpu(cmd->len);
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len,
cmd->ident);
if (len > skb->len || !cmd->ident) {
BT_DBG("corrupted command");
l2cap_sig_send_rej(conn, cmd->ident);
skb_pull(skb, len > skb->len ? skb->len : len);
continue;
}
err = l2cap_bredr_sig_cmd(conn, cmd, len, skb->data);
if (err) {
BT_ERR("Wrong link type (%d)", err);
l2cap_sig_send_rej(conn, cmd->ident);
}
skb_pull(skb, len);
}
if (skb->len > 0) {
BT_DBG("corrupted command");
l2cap_sig_send_rej(conn, 0);
}
drop:
kfree_skb(skb);
}
static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
{
u16 our_fcs, rcv_fcs;
int hdr_size;
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
hdr_size = L2CAP_EXT_HDR_SIZE;
else
hdr_size = L2CAP_ENH_HDR_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16) {
skb_trim(skb, skb->len - L2CAP_FCS_SIZE);
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
if (our_fcs != rcv_fcs)
return -EBADMSG;
}
return 0;
}
static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
{
struct l2cap_ctrl control;
BT_DBG("chan %p", chan);
memset(&control, 0, sizeof(control));
control.sframe = 1;
control.final = 1;
control.reqseq = chan->buffer_seq;
set_bit(CONN_SEND_FBIT, &chan->conn_state);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control.super = L2CAP_SUPER_RNR;
l2cap_send_sframe(chan, &control);
}
if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
chan->unacked_frames > 0)
__set_retrans_timer(chan);
/* Send pending iframes */
l2cap_ertm_send(chan);
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
test_bit(CONN_SEND_FBIT, &chan->conn_state)) {
/* F-bit wasn't sent in an s-frame or i-frame yet, so
* send it now.
*/
control.super = L2CAP_SUPER_RR;
l2cap_send_sframe(chan, &control);
}
}
static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag,
struct sk_buff **last_frag)
{
/* skb->len reflects data in skb as well as all fragments
* skb->data_len reflects only data in fragments
*/
if (!skb_has_frag_list(skb))
skb_shinfo(skb)->frag_list = new_frag;
new_frag->next = NULL;
(*last_frag)->next = new_frag;
*last_frag = new_frag;
skb->len += new_frag->len;
skb->data_len += new_frag->len;
skb->truesize += new_frag->truesize;
}
static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
struct l2cap_ctrl *control)
{
int err = -EINVAL;
switch (control->sar) {
case L2CAP_SAR_UNSEGMENTED:
if (chan->sdu)
break;
err = chan->ops->recv(chan, skb);
break;
case L2CAP_SAR_START:
if (chan->sdu)
break;
if (!pskb_may_pull(skb, L2CAP_SDULEN_SIZE))
break;
chan->sdu_len = get_unaligned_le16(skb->data);
skb_pull(skb, L2CAP_SDULEN_SIZE);
if (chan->sdu_len > chan->imtu) {
err = -EMSGSIZE;
break;
}
if (skb->len >= chan->sdu_len)
break;
chan->sdu = skb;
chan->sdu_last_frag = skb;
skb = NULL;
err = 0;
break;
case L2CAP_SAR_CONTINUE:
if (!chan->sdu)
break;
append_skb_frag(chan->sdu, skb,
&chan->sdu_last_frag);
skb = NULL;
if (chan->sdu->len >= chan->sdu_len)
break;
err = 0;
break;
case L2CAP_SAR_END:
if (!chan->sdu)
break;
append_skb_frag(chan->sdu, skb,
&chan->sdu_last_frag);
skb = NULL;
if (chan->sdu->len != chan->sdu_len)
break;
err = chan->ops->recv(chan, chan->sdu);
if (!err) {
/* Reassembly complete */
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
}
break;
}
if (err) {
kfree_skb(skb);
kfree_skb(chan->sdu);
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
}
return err;
}
static int l2cap_resegment(struct l2cap_chan *chan)
{
/* Placeholder */
return 0;
}
void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
{
u8 event;
if (chan->mode != L2CAP_MODE_ERTM)
return;
event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR;
l2cap_tx(chan, NULL, NULL, event);
}
static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
{
int err = 0;
/* Pass sequential frames to l2cap_reassemble_sdu()
* until a gap is encountered.
*/
BT_DBG("chan %p", chan);
while (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
struct sk_buff *skb;
BT_DBG("Searching for skb with txseq %d (queue len %d)",
chan->buffer_seq, skb_queue_len(&chan->srej_q));
skb = l2cap_ertm_seq_in_queue(&chan->srej_q, chan->buffer_seq);
if (!skb)
break;
skb_unlink(skb, &chan->srej_q);
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
if (err)
break;
}
if (skb_queue_empty(&chan->srej_q)) {
chan->rx_state = L2CAP_RX_STATE_RECV;
l2cap_send_ack(chan);
}
return err;
}
static void l2cap_handle_srej(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
struct sk_buff *skb;
BT_DBG("chan %p, control %p", chan, control);
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
if (skb == NULL) {
BT_DBG("Seq %d not available for retransmission",
control->reqseq);
return;
}
if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (control->poll) {
l2cap_pass_to_tx(chan, control);
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_retransmit(chan, control);
l2cap_ertm_send(chan);
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
set_bit(CONN_SREJ_ACT, &chan->conn_state);
chan->srej_save_reqseq = control->reqseq;
}
} else {
l2cap_pass_to_tx_fbit(chan, control);
if (control->final) {
if (chan->srej_save_reqseq != control->reqseq ||
!test_and_clear_bit(CONN_SREJ_ACT,
&chan->conn_state))
l2cap_retransmit(chan, control);
} else {
l2cap_retransmit(chan, control);
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
set_bit(CONN_SREJ_ACT, &chan->conn_state);
chan->srej_save_reqseq = control->reqseq;
}
}
}
}
static void l2cap_handle_rej(struct l2cap_chan *chan,
struct l2cap_ctrl *control)
{
struct sk_buff *skb;
BT_DBG("chan %p, control %p", chan, control);
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
if (chan->max_tx && skb &&
bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
l2cap_pass_to_tx(chan, control);
if (control->final) {
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_all(chan, control);
} else {
l2cap_retransmit_all(chan, control);
l2cap_ertm_send(chan);
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F)
set_bit(CONN_REJ_ACT, &chan->conn_state);
}
}
static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
{
BT_DBG("chan %p, txseq %d", chan, txseq);
BT_DBG("last_acked_seq %d, expected_tx_seq %d", chan->last_acked_seq,
chan->expected_tx_seq);
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
chan->tx_win) {
/* See notes below regarding "double poll" and
* invalid packets.
*/
if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
BT_DBG("Invalid/Ignore - after SREJ");
return L2CAP_TXSEQ_INVALID_IGNORE;
} else {
BT_DBG("Invalid - in window after SREJ sent");
return L2CAP_TXSEQ_INVALID;
}
}
if (chan->srej_list.head == txseq) {
BT_DBG("Expected SREJ");
return L2CAP_TXSEQ_EXPECTED_SREJ;
}
if (l2cap_ertm_seq_in_queue(&chan->srej_q, txseq)) {
BT_DBG("Duplicate SREJ - txseq already stored");
return L2CAP_TXSEQ_DUPLICATE_SREJ;
}
if (l2cap_seq_list_contains(&chan->srej_list, txseq)) {
BT_DBG("Unexpected SREJ - not requested");
return L2CAP_TXSEQ_UNEXPECTED_SREJ;
}
}
if (chan->expected_tx_seq == txseq) {
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
chan->tx_win) {
BT_DBG("Invalid - txseq outside tx window");
return L2CAP_TXSEQ_INVALID;
} else {
BT_DBG("Expected");
return L2CAP_TXSEQ_EXPECTED;
}
}
if (__seq_offset(chan, txseq, chan->last_acked_seq) <
__seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)) {
BT_DBG("Duplicate - expected_tx_seq later than txseq");
return L2CAP_TXSEQ_DUPLICATE;
}
if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) {
/* A source of invalid packets is a "double poll" condition,
* where delays cause us to send multiple poll packets. If
* the remote stack receives and processes both polls,
* sequence numbers can wrap around in such a way that a
* resent frame has a sequence number that looks like new data
* with a sequence gap. This would trigger an erroneous SREJ
* request.
*
* Fortunately, this is impossible with a tx window that's
* less than half of the maximum sequence number, which allows
* invalid frames to be safely ignored.
*
* With tx window sizes greater than half of the tx window
* maximum, the frame is invalid and cannot be ignored. This
* causes a disconnect.
*/
if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
BT_DBG("Invalid/Ignore - txseq outside tx window");
return L2CAP_TXSEQ_INVALID_IGNORE;
} else {
BT_DBG("Invalid - txseq outside tx window");
return L2CAP_TXSEQ_INVALID;
}
} else {
BT_DBG("Unexpected - txseq indicates missing frames");
return L2CAP_TXSEQ_UNEXPECTED;
}
}
static int l2cap_rx_state_recv(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
struct l2cap_ctrl local_control;
int err = 0;
bool skb_in_use = false;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
switch (event) {
case L2CAP_EV_RECV_IFRAME:
switch (l2cap_classify_txseq(chan, control->txseq)) {
case L2CAP_TXSEQ_EXPECTED:
l2cap_pass_to_tx(chan, control);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
BT_DBG("Busy, discarding expected seq %d",
control->txseq);
break;
}
chan->expected_tx_seq = __next_seq(chan,
control->txseq);
chan->buffer_seq = chan->expected_tx_seq;
skb_in_use = true;
/* l2cap_reassemble_sdu may free skb, hence invalidate
* control, so make a copy in advance to use it after
* l2cap_reassemble_sdu returns and to avoid the race
* condition, for example:
*
* The current thread calls:
* l2cap_reassemble_sdu
* chan->ops->recv == l2cap_sock_recv_cb
* __sock_queue_rcv_skb
* Another thread calls:
* bt_sock_recvmsg
* skb_recv_datagram
* skb_free_datagram
* Then the current thread tries to access control, but
* it was freed by skb_free_datagram.
*/
local_control = *control;
err = l2cap_reassemble_sdu(chan, skb, control);
if (err)
break;
if (local_control.final) {
if (!test_and_clear_bit(CONN_REJ_ACT,
&chan->conn_state)) {
local_control.final = 0;
l2cap_retransmit_all(chan, &local_control);
l2cap_ertm_send(chan);
}
}
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
l2cap_send_ack(chan);
break;
case L2CAP_TXSEQ_UNEXPECTED:
l2cap_pass_to_tx(chan, control);
/* Can't issue SREJ frames in the local busy state.
* Drop this frame, it will be seen as missing
* when local busy is exited.
*/
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
BT_DBG("Busy, discarding unexpected seq %d",
control->txseq);
break;
}
/* There was a gap in the sequence, so an SREJ
* must be sent for each missing frame. The
* current frame is stored for later use.
*/
skb_queue_tail(&chan->srej_q, skb);
skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
l2cap_seq_list_clear(&chan->srej_list);
l2cap_send_srej(chan, control->txseq);
chan->rx_state = L2CAP_RX_STATE_SREJ_SENT;
break;
case L2CAP_TXSEQ_DUPLICATE:
l2cap_pass_to_tx(chan, control);
break;
case L2CAP_TXSEQ_INVALID_IGNORE:
break;
case L2CAP_TXSEQ_INVALID:
default:
l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
case L2CAP_EV_RECV_RR:
l2cap_pass_to_tx(chan, control);
if (control->final) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (!test_and_clear_bit(CONN_REJ_ACT,
&chan->conn_state)) {
control->final = 0;
l2cap_retransmit_all(chan, control);
}
l2cap_ertm_send(chan);
} else if (control->poll) {
l2cap_send_i_or_rr_or_rnr(chan);
} else {
if (test_and_clear_bit(CONN_REMOTE_BUSY,
&chan->conn_state) &&
chan->unacked_frames)
__set_retrans_timer(chan);
l2cap_ertm_send(chan);
}
break;
case L2CAP_EV_RECV_RNR:
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
l2cap_pass_to_tx(chan, control);
if (control && control->poll) {
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_send_rr_or_rnr(chan, 0);
}
__clear_retrans_timer(chan);
l2cap_seq_list_clear(&chan->retrans_list);
break;
case L2CAP_EV_RECV_REJ:
l2cap_handle_rej(chan, control);
break;
case L2CAP_EV_RECV_SREJ:
l2cap_handle_srej(chan, control);
break;
default:
break;
}
if (skb && !skb_in_use) {
BT_DBG("Freeing %p", skb);
kfree_skb(skb);
}
return err;
}
static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err = 0;
u16 txseq = control->txseq;
bool skb_in_use = false;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
switch (event) {
case L2CAP_EV_RECV_IFRAME:
switch (l2cap_classify_txseq(chan, txseq)) {
case L2CAP_TXSEQ_EXPECTED:
/* Keep frame for reassembly later */
l2cap_pass_to_tx(chan, control);
skb_queue_tail(&chan->srej_q, skb);
skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
chan->expected_tx_seq = __next_seq(chan, txseq);
break;
case L2CAP_TXSEQ_EXPECTED_SREJ:
l2cap_seq_list_pop(&chan->srej_list);
l2cap_pass_to_tx(chan, control);
skb_queue_tail(&chan->srej_q, skb);
skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
err = l2cap_rx_queued_iframes(chan);
if (err)
break;
break;
case L2CAP_TXSEQ_UNEXPECTED:
/* Got a frame that can't be reassembled yet.
* Save it for later, and send SREJs to cover
* the missing frames.
*/
skb_queue_tail(&chan->srej_q, skb);
skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
l2cap_pass_to_tx(chan, control);
l2cap_send_srej(chan, control->txseq);
break;
case L2CAP_TXSEQ_UNEXPECTED_SREJ:
/* This frame was requested with an SREJ, but
* some expected retransmitted frames are
* missing. Request retransmission of missing
* SREJ'd frames.
*/
skb_queue_tail(&chan->srej_q, skb);
skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
l2cap_pass_to_tx(chan, control);
l2cap_send_srej_list(chan, control->txseq);
break;
case L2CAP_TXSEQ_DUPLICATE_SREJ:
/* We've already queued this frame. Drop this copy. */
l2cap_pass_to_tx(chan, control);
break;
case L2CAP_TXSEQ_DUPLICATE:
/* Expecting a later sequence number, so this frame
* was already received. Ignore it completely.
*/
break;
case L2CAP_TXSEQ_INVALID_IGNORE:
break;
case L2CAP_TXSEQ_INVALID:
default:
l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
case L2CAP_EV_RECV_RR:
l2cap_pass_to_tx(chan, control);
if (control->final) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (!test_and_clear_bit(CONN_REJ_ACT,
&chan->conn_state)) {
control->final = 0;
l2cap_retransmit_all(chan, control);
}
l2cap_ertm_send(chan);
} else if (control->poll) {
if (test_and_clear_bit(CONN_REMOTE_BUSY,
&chan->conn_state) &&
chan->unacked_frames) {
__set_retrans_timer(chan);
}
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_send_srej_tail(chan);
} else {
if (test_and_clear_bit(CONN_REMOTE_BUSY,
&chan->conn_state) &&
chan->unacked_frames)
__set_retrans_timer(chan);
l2cap_send_ack(chan);
}
break;
case L2CAP_EV_RECV_RNR:
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
l2cap_pass_to_tx(chan, control);
if (control->poll) {
l2cap_send_srej_tail(chan);
} else {
struct l2cap_ctrl rr_control;
memset(&rr_control, 0, sizeof(rr_control));
rr_control.sframe = 1;
rr_control.super = L2CAP_SUPER_RR;
rr_control.reqseq = chan->buffer_seq;
l2cap_send_sframe(chan, &rr_control);
}
break;
case L2CAP_EV_RECV_REJ:
l2cap_handle_rej(chan, control);
break;
case L2CAP_EV_RECV_SREJ:
l2cap_handle_srej(chan, control);
break;
}
if (skb && !skb_in_use) {
BT_DBG("Freeing %p", skb);
kfree_skb(skb);
}
return err;
}
static int l2cap_finish_move(struct l2cap_chan *chan)
{
BT_DBG("chan %p", chan);
chan->rx_state = L2CAP_RX_STATE_RECV;
chan->conn->mtu = chan->conn->hcon->mtu;
return l2cap_resegment(chan);
}
static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
if (!control->poll)
return -EPROTO;
l2cap_process_reqseq(chan, control->reqseq);
if (!skb_queue_empty(&chan->tx_q))
chan->tx_send_head = skb_peek(&chan->tx_q);
else
chan->tx_send_head = NULL;
/* Rewind next_tx_seq to the point expected
* by the receiver.
*/
chan->next_tx_seq = control->reqseq;
chan->unacked_frames = 0;
err = l2cap_finish_move(chan);
if (err)
return err;
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_send_i_or_rr_or_rnr(chan);
if (event == L2CAP_EV_RECV_IFRAME)
return -EPROTO;
return l2cap_rx_state_recv(chan, control, NULL, event);
}
static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err;
if (!control->final)
return -EPROTO;
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->rx_state = L2CAP_RX_STATE_RECV;
l2cap_process_reqseq(chan, control->reqseq);
if (!skb_queue_empty(&chan->tx_q))
chan->tx_send_head = skb_peek(&chan->tx_q);
else
chan->tx_send_head = NULL;
/* Rewind next_tx_seq to the point expected
* by the receiver.
*/
chan->next_tx_seq = control->reqseq;
chan->unacked_frames = 0;
chan->conn->mtu = chan->conn->hcon->mtu;
err = l2cap_resegment(chan);
if (!err)
err = l2cap_rx_state_recv(chan, control, skb, event);
return err;
}
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
{
/* Make sure reqseq is for a packet that has been sent but not acked */
u16 unacked;
unacked = __seq_offset(chan, chan->next_tx_seq, chan->expected_ack_seq);
return __seq_offset(chan, chan->next_tx_seq, reqseq) <= unacked;
}
static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err = 0;
BT_DBG("chan %p, control %p, skb %p, event %d, state %d", chan,
control, skb, event, chan->rx_state);
if (__valid_reqseq(chan, control->reqseq)) {
switch (chan->rx_state) {
case L2CAP_RX_STATE_RECV:
err = l2cap_rx_state_recv(chan, control, skb, event);
break;
case L2CAP_RX_STATE_SREJ_SENT:
err = l2cap_rx_state_srej_sent(chan, control, skb,
event);
break;
case L2CAP_RX_STATE_WAIT_P:
err = l2cap_rx_state_wait_p(chan, control, skb, event);
break;
case L2CAP_RX_STATE_WAIT_F:
err = l2cap_rx_state_wait_f(chan, control, skb, event);
break;
default:
/* shut it down */
break;
}
} else {
BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d",
control->reqseq, chan->next_tx_seq,
chan->expected_ack_seq);
l2cap_send_disconn_req(chan, ECONNRESET);
}
return err;
}
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb)
{
/* l2cap_reassemble_sdu may free skb, hence invalidate control, so store
* the txseq field in advance to use it after l2cap_reassemble_sdu
* returns and to avoid the race condition, for example:
*
* The current thread calls:
* l2cap_reassemble_sdu
* chan->ops->recv == l2cap_sock_recv_cb
* __sock_queue_rcv_skb
* Another thread calls:
* bt_sock_recvmsg
* skb_recv_datagram
* skb_free_datagram
* Then the current thread tries to access control, but it was freed by
* skb_free_datagram.
*/
u16 txseq = control->txseq;
BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
chan->rx_state);
if (l2cap_classify_txseq(chan, txseq) == L2CAP_TXSEQ_EXPECTED) {
l2cap_pass_to_tx(chan, control);
BT_DBG("buffer_seq %u->%u", chan->buffer_seq,
__next_seq(chan, chan->buffer_seq));
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
l2cap_reassemble_sdu(chan, skb, control);
} else {
if (chan->sdu) {
kfree_skb(chan->sdu);
chan->sdu = NULL;
}
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
if (skb) {
BT_DBG("Freeing %p", skb);
kfree_skb(skb);
}
}
chan->last_acked_seq = txseq;
chan->expected_tx_seq = __next_seq(chan, txseq);
return 0;
}
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
u16 len;
u8 event;
__unpack_control(chan, skb);
len = skb->len;
/*
* We can just drop the corrupted I-frame here.
* Receiver will miss it and start proper recovery
* procedures and ask for retransmission.
*/
if (l2cap_check_fcs(chan, skb))
goto drop;
if (!control->sframe && control->sar == L2CAP_SAR_START)
len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
len -= L2CAP_FCS_SIZE;
if (len > chan->mps) {
l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
if (chan->ops->filter) {
if (chan->ops->filter(chan, skb))
goto drop;
}
if (!control->sframe) {
int err;
BT_DBG("iframe sar %d, reqseq %d, final %d, txseq %d",
control->sar, control->reqseq, control->final,
control->txseq);
/* Validate F-bit - F=0 always valid, F=1 only
* valid in TX WAIT_F
*/
if (control->final && chan->tx_state != L2CAP_TX_STATE_WAIT_F)
goto drop;
if (chan->mode != L2CAP_MODE_STREAMING) {
event = L2CAP_EV_RECV_IFRAME;
err = l2cap_rx(chan, control, skb, event);
} else {
err = l2cap_stream_rx(chan, control, skb);
}
if (err)
l2cap_send_disconn_req(chan, ECONNRESET);
} else {
const u8 rx_func_to_event[4] = {
L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ,
L2CAP_EV_RECV_RNR, L2CAP_EV_RECV_SREJ
};
/* Only I-frames are expected in streaming mode */
if (chan->mode == L2CAP_MODE_STREAMING)
goto drop;
BT_DBG("sframe reqseq %d, final %d, poll %d, super %d",
control->reqseq, control->final, control->poll,
control->super);
if (len != 0) {
BT_ERR("Trailing bytes: %d in sframe", len);
l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
/* Validate F and P bits */
if (control->final && (control->poll ||
chan->tx_state != L2CAP_TX_STATE_WAIT_F))
goto drop;
event = rx_func_to_event[control->super];
if (l2cap_rx(chan, control, skb, event))
l2cap_send_disconn_req(chan, ECONNRESET);
}
return 0;
drop:
kfree_skb(skb);
return 0;
}
static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_le_credits pkt;
u16 return_credits = l2cap_le_rx_credits(chan);
if (chan->rx_credits >= return_credits)
return;
return_credits -= chan->rx_credits;
BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
chan->rx_credits += return_credits;
pkt.cid = cpu_to_le16(chan->scid);
pkt.credits = cpu_to_le16(return_credits);
chan->ident = l2cap_get_ident(conn);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
}
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail)
{
if (chan->rx_avail == rx_avail)
return;
BT_DBG("chan %p has %zd bytes avail for rx", chan, rx_avail);
chan->rx_avail = rx_avail;
if (chan->state == BT_CONNECTED)
l2cap_chan_le_send_credits(chan);
}
static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb)
{
int err;
BT_DBG("SDU reassemble complete: chan %p skb->len %u", chan, skb->len);
/* Wait recv to confirm reception before updating the credits */
err = chan->ops->recv(chan, skb);
if (err < 0 && chan->rx_avail != -1) {
BT_ERR("Queueing received LE L2CAP data failed");
l2cap_send_disconn_req(chan, ECONNRESET);
return err;
}
/* Update credits whenever an SDU is received */
l2cap_chan_le_send_credits(chan);
return err;
}
static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
int err;
if (!chan->rx_credits) {
BT_ERR("No credits to receive LE L2CAP data");
l2cap_send_disconn_req(chan, ECONNRESET);
return -ENOBUFS;
}
if (chan->imtu < skb->len) {
BT_ERR("Too big LE L2CAP PDU");
return -ENOBUFS;
}
chan->rx_credits--;
BT_DBG("chan %p: rx_credits %u -> %u",
chan, chan->rx_credits + 1, chan->rx_credits);
/* Update if remote had run out of credits, this should only happens
* if the remote is not using the entire MPS.
*/
if (!chan->rx_credits)
l2cap_chan_le_send_credits(chan);
err = 0;
if (!chan->sdu) {
u16 sdu_len;
sdu_len = get_unaligned_le16(skb->data);
skb_pull(skb, L2CAP_SDULEN_SIZE);
BT_DBG("Start of new SDU. sdu_len %u skb->len %u imtu %u",
sdu_len, skb->len, chan->imtu);
if (sdu_len > chan->imtu) {
BT_ERR("Too big LE L2CAP SDU length received");
err = -EMSGSIZE;
goto failed;
}
if (skb->len > sdu_len) {
BT_ERR("Too much LE L2CAP data received");
err = -EINVAL;
goto failed;
}
if (skb->len == sdu_len)
return l2cap_ecred_recv(chan, skb);
chan->sdu = skb;
chan->sdu_len = sdu_len;
chan->sdu_last_frag = skb;
/* Detect if remote is not able to use the selected MPS */
if (skb->len + L2CAP_SDULEN_SIZE < chan->mps) {
u16 mps_len = skb->len + L2CAP_SDULEN_SIZE;
/* Adjust the number of credits */
BT_DBG("chan->mps %u -> %u", chan->mps, mps_len);
chan->mps = mps_len;
l2cap_chan_le_send_credits(chan);
}
return 0;
}
BT_DBG("SDU fragment. chan->sdu->len %u skb->len %u chan->sdu_len %u",
chan->sdu->len, skb->len, chan->sdu_len);
if (chan->sdu->len + skb->len > chan->sdu_len) {
BT_ERR("Too much LE L2CAP data received");
err = -EINVAL;
goto failed;
}
append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag);
skb = NULL;
if (chan->sdu->len == chan->sdu_len) {
err = l2cap_ecred_recv(chan, chan->sdu);
if (!err) {
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
}
}
failed:
if (err) {
kfree_skb(skb);
kfree_skb(chan->sdu);
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
}
/* We can't return an error here since we took care of the skb
* freeing internally. An error return would cause the caller to
* do a double-free of the skb.
*/
return 0;
}
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
struct sk_buff *skb)
{
struct l2cap_chan *chan;
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
BT_DBG("unknown cid 0x%4.4x", cid);
/* Drop packet and return */
kfree_skb(skb);
return;
}
BT_DBG("chan %p, len %d", chan, skb->len);
/* If we receive data on a fixed channel before the info req/rsp
* procedure is done simply assume that the channel is supported
* and mark it as ready.
*/
if (chan->chan_type == L2CAP_CHAN_FIXED)
l2cap_chan_ready(chan);
if (chan->state != BT_CONNECTED)
goto drop;
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_EXT_FLOWCTL:
if (l2cap_ecred_data_rcv(chan, skb) < 0)
goto drop;
goto done;
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
* But we don't have any other choice. L2CAP doesn't
* provide flow control mechanism. */
if (chan->imtu < skb->len) {
BT_ERR("Dropping L2CAP data: receive buffer overflow");
goto drop;
}
if (!chan->ops->recv(chan, skb))
goto done;
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
l2cap_data_rcv(chan, skb);
goto done;
default:
BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode);
break;
}
drop:
kfree_skb(skb);
done:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
struct sk_buff *skb)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan;
if (hcon->type != ACL_LINK)
goto free_skb;
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
ACL_LINK);
if (!chan)
goto free_skb;
BT_DBG("chan %p, len %d", chan, skb->len);
l2cap_chan_lock(chan);
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
if (chan->imtu < skb->len)
goto drop;
/* Store remote BD_ADDR and PSM for msg_name */
bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
bt_cb(skb)->l2cap.psm = psm;
if (!chan->ops->recv(chan, skb)) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
}
drop:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
free_skb:
kfree_skb(skb);
}
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_hdr *lh = (void *) skb->data;
struct hci_conn *hcon = conn->hcon;
u16 cid, len;
__le16 psm;
if (hcon->state != BT_CONNECTED) {
BT_DBG("queueing pending rx skb");
skb_queue_tail(&conn->pending_rx, skb);
return;
}
skb_pull(skb, L2CAP_HDR_SIZE);
cid = __le16_to_cpu(lh->cid);
len = __le16_to_cpu(lh->len);
if (len != skb->len) {
kfree_skb(skb);
return;
}
/* Since we can't actively block incoming LE connections we must
* at least ensure that we ignore incoming data from them.
*/
if (hcon->type == LE_LINK &&
hci_bdaddr_list_lookup(&hcon->hdev->reject_list, &hcon->dst,
bdaddr_dst_type(hcon))) {
kfree_skb(skb);
return;
}
BT_DBG("len %d, cid 0x%4.4x", len, cid);
switch (cid) {
case L2CAP_CID_SIGNALING:
l2cap_sig_channel(conn, skb);
break;
case L2CAP_CID_CONN_LESS:
psm = get_unaligned((__le16 *) skb->data);
skb_pull(skb, L2CAP_PSMLEN_SIZE);
l2cap_conless_channel(conn, psm, skb);
break;
case L2CAP_CID_LE_SIGNALING:
l2cap_le_sig_channel(conn, skb);
break;
default:
l2cap_data_channel(conn, cid, skb);
break;
}
}
static void process_pending_rx(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
pending_rx_work);
struct sk_buff *skb;
BT_DBG("");
mutex_lock(&conn->lock);
while ((skb = skb_dequeue(&conn->pending_rx)))
l2cap_recv_frame(conn, skb);
mutex_unlock(&conn->lock);
}
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct hci_chan *hchan;
if (conn)
return conn;
hchan = hci_chan_create(hcon);
if (!hchan)
return NULL;
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) {
hci_chan_del(hchan);
return NULL;
}
kref_init(&conn->ref);
hcon->l2cap_data = conn;
conn->hcon = hci_conn_get(hcon);
conn->hchan = hchan;
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
conn->mtu = hcon->mtu;
conn->feat_mask = 0;
conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
if (hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED) &&
(bredr_sc_enabled(hcon->hdev) ||
hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP)))
conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
mutex_init(&conn->ident_lock);
mutex_init(&conn->lock);
INIT_LIST_HEAD(&conn->chan_l);
INIT_LIST_HEAD(&conn->users);
INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
skb_queue_head_init(&conn->pending_rx);
INIT_WORK(&conn->pending_rx_work, process_pending_rx);
INIT_DELAYED_WORK(&conn->id_addr_timer, l2cap_conn_update_id_addr);
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
return conn;
}
static bool is_valid_psm(u16 psm, u8 dst_type)
{
if (!psm)
return false;
if (bdaddr_type_is_le(dst_type))
return (psm <= 0x00ff);
/* PSM must be odd and lsb of upper byte must be 0 */
return ((psm & 0x0101) == 0x0001);
}
struct l2cap_chan_data {
struct l2cap_chan *chan;
struct pid *pid;
int count;
};
static void l2cap_chan_by_pid(struct l2cap_chan *chan, void *data)
{
struct l2cap_chan_data *d = data;
struct pid *pid;
if (chan == d->chan)
return;
if (!test_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
pid = chan->ops->get_peer_pid(chan);
/* Only count deferred channels with the same PID/PSM */
if (d->pid != pid || chan->psm != d->chan->psm || chan->ident ||
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
return;
d->count++;
}
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type, u16 timeout)
{
struct l2cap_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
int err;
BT_DBG("%pMR -> %pMR (type %u) psm 0x%4.4x mode 0x%2.2x", &chan->src,
dst, dst_type, __le16_to_cpu(psm), chan->mode);
hdev = hci_get_route(dst, &chan->src, chan->src_type);
if (!hdev)
return -EHOSTUNREACH;
hci_dev_lock(hdev);
if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !psm) {
err = -EINVAL;
goto done;
}
if (chan->chan_type == L2CAP_CHAN_FIXED && !cid) {
err = -EINVAL;
goto done;
}
switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_EXT_FLOWCTL:
if (!enable_ecred) {
err = -EOPNOTSUPP;
goto done;
}
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
fallthrough;
default:
err = -EOPNOTSUPP;
goto done;
}
switch (chan->state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
err = 0;
goto done;
case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
goto done;
case BT_OPEN:
case BT_BOUND:
/* Can connect */
break;
default:
err = -EBADFD;
goto done;
}
/* Set destination address and psm */
bacpy(&chan->dst, dst);
chan->dst_type = dst_type;
chan->psm = psm;
chan->dcid = cid;
if (bdaddr_type_is_le(dst_type)) {
/* Convert from L2CAP channel address type to HCI address type
*/
if (dst_type == BDADDR_LE_PUBLIC)
dst_type = ADDR_LE_DEV_PUBLIC;
else
dst_type = ADDR_LE_DEV_RANDOM;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
hcon = hci_connect_le(hdev, dst, dst_type, false,
chan->sec_level, timeout,
HCI_ROLE_SLAVE, 0, 0);
else
hcon = hci_connect_le_scan(hdev, dst, dst_type,
chan->sec_level, timeout,
CONN_REASON_L2CAP_CHAN);
} else {
u8 auth_type = l2cap_get_auth_type(chan);
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type,
CONN_REASON_L2CAP_CHAN, timeout);
}
if (IS_ERR(hcon)) {
err = PTR_ERR(hcon);
goto done;
}
conn = l2cap_conn_add(hcon);
if (!conn) {
hci_conn_drop(hcon);
err = -ENOMEM;
goto done;
}
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
struct l2cap_chan_data data;
data.chan = chan;
data.pid = chan->ops->get_peer_pid(chan);
data.count = 1;
l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
/* Check if there isn't too many channels being connected */
if (data.count > L2CAP_ECRED_CONN_SCID_MAX) {
hci_conn_drop(hcon);
err = -EPROTO;
goto done;
}
}
mutex_lock(&conn->lock);
l2cap_chan_lock(chan);
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
hci_conn_drop(hcon);
err = -EBUSY;
goto chan_unlock;
}
/* Update source addr of the socket */
bacpy(&chan->src, &hcon->src);
chan->src_type = bdaddr_src_type(hcon);
__l2cap_chan_add(conn, chan);
/* l2cap_chan_add takes its own ref so we can drop this one */
hci_conn_drop(hcon);
l2cap_state_change(chan, BT_CONNECT);
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
/* Release chan->sport so that it can be reused by other
* sockets (as it's only used for listening sockets).
*/
write_lock(&chan_list_lock);
chan->sport = 0;
write_unlock(&chan_list_lock);
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_chan_check_security(chan, true))
l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
}
err = 0;
chan_unlock:
l2cap_chan_unlock(chan);
mutex_unlock(&conn->lock);
done:
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
EXPORT_SYMBOL_GPL(l2cap_chan_connect);
static void l2cap_ecred_reconfigure(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
DEFINE_RAW_FLEX(struct l2cap_ecred_reconf_req, pdu, scid, 1);
pdu->mtu = cpu_to_le16(chan->imtu);
pdu->mps = cpu_to_le16(chan->mps);
pdu->scid[0] = cpu_to_le16(chan->scid);
chan->ident = l2cap_get_ident(conn);
l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ,
sizeof(pdu), &pdu);
}
int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu)
{
if (chan->imtu > mtu)
return -EINVAL;
BT_DBG("chan %p mtu 0x%4.4x", chan, mtu);
chan->imtu = mtu;
l2cap_ecred_reconfigure(chan);
return 0;
}
/* ---- L2CAP interface with lower layer (HCI) ---- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
int exact = 0, lm1 = 0, lm2 = 0;
struct l2cap_chan *c;
BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
/* Find listening sockets and check their link_mode */
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
if (c->state != BT_LISTEN)
continue;
if (!bacmp(&c->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm1 |= HCI_LM_MASTER;
exact++;
} else if (!bacmp(&c->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm2 |= HCI_LM_MASTER;
}
}
read_unlock(&chan_list_lock);
return exact ? lm1 : lm2;
}
/* Find the next fixed channel in BT_LISTEN state, continue iteration
* from an existing channel in the list or from the beginning of the
* global list (by passing NULL as first parameter).
*/
static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
struct hci_conn *hcon)
{
u8 src_type = bdaddr_src_type(hcon);
read_lock(&chan_list_lock);
if (c)
c = list_next_entry(c, global_l);
else
c = list_entry(chan_list.next, typeof(*c), global_l);
list_for_each_entry_from(c, &chan_list, global_l) {
if (c->chan_type != L2CAP_CHAN_FIXED)
continue;
if (c->state != BT_LISTEN)
continue;
if (bacmp(&c->src, &hcon->src) && bacmp(&c->src, BDADDR_ANY))
continue;
if (src_type != c->src_type)
continue;
c = l2cap_chan_hold_unless_zero(c);
read_unlock(&chan_list_lock);
return c;
}
read_unlock(&chan_list_lock);
return NULL;
}
static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{
struct hci_dev *hdev = hcon->hdev;
struct l2cap_conn *conn;
struct l2cap_chan *pchan;
u8 dst_type;
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (status) {
l2cap_conn_del(hcon, bt_to_errno(status));
return;
}
conn = l2cap_conn_add(hcon);
if (!conn)
return;
dst_type = bdaddr_dst_type(hcon);
/* If device is blocked, do not create channels for it */
if (hci_bdaddr_list_lookup(&hdev->reject_list, &hcon->dst, dst_type))
return;
/* Find fixed channels and notify them of the new connection. We
* use multiple individual lookups, continuing each time where
* we left off, because the list lock would prevent calling the
* potentially sleeping l2cap_chan_lock() function.
*/
pchan = l2cap_global_fixed_chan(NULL, hcon);
while (pchan) {
struct l2cap_chan *chan, *next;
/* Client fixed channels should override server ones */
if (__l2cap_get_chan_by_dcid(conn, pchan->scid))
goto next;
l2cap_chan_lock(pchan);
chan = pchan->ops->new_connection(pchan);
if (chan) {
bacpy(&chan->src, &hcon->src);
bacpy(&chan->dst, &hcon->dst);
chan->src_type = bdaddr_src_type(hcon);
chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
}
l2cap_chan_unlock(pchan);
next:
next = l2cap_global_fixed_chan(pchan, hcon);
l2cap_chan_put(pchan);
pchan = next;
}
l2cap_conn_ready(conn);
}
int l2cap_disconn_ind(struct hci_conn *hcon)
{
struct l2cap_conn *conn = hcon->l2cap_data;
BT_DBG("hcon %p", hcon);
if (!conn)
return HCI_ERROR_REMOTE_USER_TERM;
return conn->disc_reason;
}
static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason);
l2cap_conn_del(hcon, bt_to_errno(reason));
}
static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
{
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
return;
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
__set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
} else if (chan->sec_level == BT_SECURITY_HIGH ||
chan->sec_level == BT_SECURITY_FIPS)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
if (chan->sec_level == BT_SECURITY_MEDIUM)
__clear_chan_timer(chan);
}
}
static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan;
if (!conn)
return;
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
state_to_string(chan->state));
if (!status && encrypt)
chan->sec_level = hcon->sec_level;
if (!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
}
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
chan->ops->resume(chan);
l2cap_check_encryption(chan, encrypt);
l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
if (!status && l2cap_check_enc_key_size(hcon, chan))
l2cap_start_connection(chan);
else
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
} else if (chan->state == BT_CONNECT2 &&
!(chan->mode == L2CAP_MODE_EXT_FLOWCTL ||
chan->mode == L2CAP_MODE_LE_FLOWCTL)) {
struct l2cap_conn_rsp rsp;
__u16 res, stat;
if (!status && l2cap_check_enc_key_size(hcon, chan)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} else {
l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
l2cap_state_change(chan, BT_DISCONN);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(res);
rsp.status = cpu_to_le16(stat);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
sizeof(rsp), &rsp);
if (!test_bit(CONF_REQ_SENT, &chan->conf_state) &&
res == L2CAP_CR_SUCCESS) {
char buf[128];
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf, sizeof(buf)),
buf);
chan->num_conf_req++;
}
}
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->lock);
}
/* Append fragment into frame respecting the maximum len of rx_skb */
static int l2cap_recv_frag(struct l2cap_conn *conn, struct sk_buff *skb,
u16 len)
{
if (!conn->rx_skb) {
/* Allocate skb for the complete frame (with header) */
conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
if (!conn->rx_skb)
return -ENOMEM;
/* Init rx_len */
conn->rx_len = len;
skb_set_delivery_time(conn->rx_skb, skb->tstamp,
skb->tstamp_type);
}
/* Copy as much as the rx_skb can hold */
len = min_t(u16, len, skb->len);
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, len), len);
skb_pull(skb, len);
conn->rx_len -= len;
return len;
}
static int l2cap_recv_len(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct sk_buff *rx_skb;
int len;
/* Append just enough to complete the header */
len = l2cap_recv_frag(conn, skb, L2CAP_LEN_SIZE - conn->rx_skb->len);
/* If header could not be read just continue */
if (len < 0 || conn->rx_skb->len < L2CAP_LEN_SIZE)
return len;
rx_skb = conn->rx_skb;
len = get_unaligned_le16(rx_skb->data);
/* Check if rx_skb has enough space to received all fragments */
if (len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE) <= skb_tailroom(rx_skb)) {
/* Update expected len */
conn->rx_len = len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE);
return L2CAP_LEN_SIZE;
}
/* Reset conn->rx_skb since it will need to be reallocated in order to
* fit all fragments.
*/
conn->rx_skb = NULL;
/* Reallocates rx_skb using the exact expected length */
len = l2cap_recv_frag(conn, rx_skb,
len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE));
kfree_skb(rx_skb);
return len;
}
static void l2cap_recv_reset(struct l2cap_conn *conn)
{
kfree_skb(conn->rx_skb);
conn->rx_skb = NULL;
conn->rx_len = 0;
}
struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
{
if (!c)
return NULL;
BT_DBG("conn %p orig refcnt %u", c, kref_read(&c->ref));
if (!kref_get_unless_zero(&c->ref))
return NULL;
return c;
}
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
{
struct l2cap_conn *conn;
int len;
/* Lock hdev to access l2cap_data to avoid race with l2cap_conn_del */
hci_dev_lock(hcon->hdev);
conn = hcon->l2cap_data;
if (!conn)
conn = l2cap_conn_add(hcon);
conn = l2cap_conn_hold_unless_zero(conn);
hci_dev_unlock(hcon->hdev);
if (!conn) {
kfree_skb(skb);
return;
}
BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags);
mutex_lock(&conn->lock);
switch (flags) {
case ACL_START:
case ACL_START_NO_FLUSH:
case ACL_COMPLETE:
if (conn->rx_skb) {
BT_ERR("Unexpected start frame (len %d)", skb->len);
l2cap_recv_reset(conn);
l2cap_conn_unreliable(conn, ECOMM);
}
/* Start fragment may not contain the L2CAP length so just
* copy the initial byte when that happens and use conn->mtu as
* expected length.
*/
if (skb->len < L2CAP_LEN_SIZE) {
l2cap_recv_frag(conn, skb, conn->mtu);
break;
}
len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE;
if (len == skb->len) {
/* Complete frame received */
l2cap_recv_frame(conn, skb);
goto unlock;
}
BT_DBG("Start: total len %d, frag len %u", len, skb->len);
if (skb->len > len) {
BT_ERR("Frame is too long (len %u, expected len %d)",
skb->len, len);
/* PTS test cases L2CAP/COS/CED/BI-14-C and BI-15-C
* (Multiple Signaling Command in one PDU, Data
* Truncated, BR/EDR) send a C-frame to the IUT with
* PDU Length set to 8 and Channel ID set to the
* correct signaling channel for the logical link.
* The Information payload contains one L2CAP_ECHO_REQ
* packet with Data Length set to 0 with 0 octets of
* echo data and one invalid command packet due to
* data truncated in PDU but present in HCI packet.
*
* Shorter the socket buffer to the PDU length to
* allow to process valid commands from the PDU before
* setting the socket unreliable.
*/
skb->len = len;
l2cap_recv_frame(conn, skb);
l2cap_conn_unreliable(conn, ECOMM);
goto unlock;
}
/* Append fragment into frame (with header) */
if (l2cap_recv_frag(conn, skb, len) < 0)
goto drop;
break;
case ACL_CONT:
BT_DBG("Cont: frag len %u (expecting %u)", skb->len, conn->rx_len);
if (!conn->rx_skb) {
BT_ERR("Unexpected continuation frame (len %d)", skb->len);
l2cap_conn_unreliable(conn, ECOMM);
goto drop;
}
/* Complete the L2CAP length if it has not been read */
if (conn->rx_skb->len < L2CAP_LEN_SIZE) {
if (l2cap_recv_len(conn, skb) < 0) {
l2cap_conn_unreliable(conn, ECOMM);
goto drop;
}
/* Header still could not be read just continue */
if (conn->rx_skb->len < L2CAP_LEN_SIZE)
break;
}
if (skb->len > conn->rx_len) {
BT_ERR("Fragment is too long (len %u, expected %u)",
skb->len, conn->rx_len);
l2cap_recv_reset(conn);
l2cap_conn_unreliable(conn, ECOMM);
goto drop;
}
/* Append fragment into frame (with header) */
l2cap_recv_frag(conn, skb, skb->len);
if (!conn->rx_len) {
/* Complete frame received. l2cap_recv_frame
* takes ownership of the skb so set the global
* rx_skb pointer to NULL first.
*/
struct sk_buff *rx_skb = conn->rx_skb;
conn->rx_skb = NULL;
l2cap_recv_frame(conn, rx_skb);
}
break;
}
drop:
kfree_skb(skb);
unlock:
mutex_unlock(&conn->lock);
l2cap_conn_put(conn);
}
static struct hci_cb l2cap_cb = {
.name = "L2CAP",
.connect_cfm = l2cap_connect_cfm,
.disconn_cfm = l2cap_disconn_cfm,
.security_cfm = l2cap_security_cfm,
};
static int l2cap_debugfs_show(struct seq_file *f, void *p)
{
struct l2cap_chan *c;
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
seq_printf(f, "%pMR (%u) %pMR (%u) %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
&c->src, c->src_type, &c->dst, c->dst_type,
c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
}
read_unlock(&chan_list_lock);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(l2cap_debugfs);
static struct dentry *l2cap_debugfs;
int __init l2cap_init(void)
{
int err;
err = l2cap_init_sockets();
if (err < 0)
return err;
hci_register_cb(&l2cap_cb);
if (IS_ERR_OR_NULL(bt_debugfs))
return 0;
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
NULL, &l2cap_debugfs_fops);
return 0;
}
void l2cap_exit(void)
{
debugfs_remove(l2cap_debugfs);
hci_unregister_cb(&l2cap_cb);
l2cap_cleanup_sockets();
}
module_param(disable_ertm, bool, 0644);
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
module_param(enable_ecred, bool, 0644);
MODULE_PARM_DESC(enable_ecred, "Enable enhanced credit flow control mode");
]
|
| 10/2 |
2026/05/25 23:42 |
llm |
expert |
0m
Model:
Tokens:
input: 91371
output: 21
thoughts: 1065
Thoughts: **Analyzing a Bluetooth L2CAP Deadlock**
Okay, so I'm looking at this bug report for `syz.0.54`, and it looks like a classic deadlock. The trace shows it's stuck in `__mutex_lock` within `l2cap_chan_connect`, specifically at `l2cap_chan_connect+0x102/0xe30`, which corresponds to a call to `mutex_lock(&conn->lock)`. Time to dig into the code.
Let's start with `l2cap_chan_connect`. Right, found it at `net/bluetooth/l2cap_core.c:6964`. It appears to grab the `conn->lock` pretty early. It looks like it calls `l2cap_conn_add` which is interesting... let's continue to investigate the code. Aha, now I see the call to `l2cap_chan_list`. That function also locks the same `conn->lock`.
Wait, isn't that a double lock? It looks like `l2cap_chan_list` is called, which calls `mutex_lock(&conn->lock)`, then `l2cap_chan_connect` calls `mutex_lock(&conn->lock)` again directly. So the stack trace points to the second lock. Okay, so `mutex_lock` is being called *twice* on the same `conn->lock` within the `l2cap_chan_connect` function. It *should* be at line 7083, but that offset is off.
Let's cross-reference the line numbers. `l2cap_chan_connect` starts at line 6949. So `l2cap_chan_connect+0x102` is probably around line 6964? Ah, no, the offset isn't from the start, but from the beginning of the function, so it *could* be line 7083. Let me make sure that's correct. Okay, I've confirmed that the source code and the stack trace are consistent. The bug is where I thought it was.
Okay, now let's see what's blocking `khidpd_000d2f19`. This thread is stuck in `hidp_session_remove`, specifically in `wait_for_completion`. Going further up the stack it goes through `l2cap_unregister_user`. Let's check what `l2cap_unregister_user` is doing at `net/bluetooth/l2cap_core.c:1734`.
|
| 11/2 |
2026/05/25 23:42 |
tool |
grepper |
0m
Args: map[Expression:l2cap_unregister_user]
Results: map[Output:include/net/bluetooth/l2cap.h=977=int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user);
include/net/bluetooth/l2cap.h:978:void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user);
include/net/bluetooth/l2cap.h-979-
--
net/bluetooth/hidp/core.c=1263=static int hidp_session_thread(void *arg)
--
net/bluetooth/hidp/core.c-1303- */
net/bluetooth/hidp/core.c:1304: l2cap_unregister_user(session->conn, &session->user);
net/bluetooth/hidp/core.c-1305- hidp_session_put(session);
--
net/bluetooth/hidp/core.c=1391=int hidp_connection_del(struct hidp_conndel_req *req)
--
net/bluetooth/hidp/core.c-1408- else
net/bluetooth/hidp/core.c:1409: l2cap_unregister_user(session->conn, &session->user);
net/bluetooth/hidp/core.c-1410-
--
net/bluetooth/l2cap_core.c=1660=static void l2cap_info_timeout(struct work_struct *work)
--
net/bluetooth/l2cap_core.c-1681- * External modules must own a reference to the l2cap_conn object if they intend
net/bluetooth/l2cap_core.c:1682: * to call l2cap_unregister_user(). The l2cap_conn object might get destroyed at
net/bluetooth/l2cap_core.c-1683- * any time if they don't.
--
net/bluetooth/l2cap_core.c=1722=EXPORT_SYMBOL(l2cap_register_user);
net/bluetooth/l2cap_core.c-1723-
net/bluetooth/l2cap_core.c:1724:void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
net/bluetooth/l2cap_core.c-1725-{
--
net/bluetooth/l2cap_core.c-1738-}
net/bluetooth/l2cap_core.c:1739:EXPORT_SYMBOL(l2cap_unregister_user);
net/bluetooth/l2cap_core.c-1740-
]
|
| 12/2 |
2026/05/25 23:42 |
llm |
expert |
0m
Model:
Tokens:
input: 93698
output: 25
thoughts: 223
Thoughts: **Analyzing the L2CAP User Unregistration Process**
Alright, let's dive into this `l2cap_unregister_user` function. I want to understand how this unregistration process works at the L2CAP level, specifically when dealing with HIDP. First, I see it's in `net/bluetooth/l2cap_core.c`. Okay, here's the code: `void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)`.
I immediately notice the critical use of `hci_dev_lock(hdev)` at the beginning. This is important; it tells me this function is protecting shared resources related to the HCI device, `hdev`, probably to prevent race conditions during unregistration. It looks like it's taking a lock before touching any of the `user`'s data structures. The check `if (list_empty(&user->list))` suggests a quick bail-out if the user isn't actually registered.
The core of this unregistration, I see, is `list_del_init(&user->list);` – it's unlinking the user from its internal list. Then, it's calling `user->remove(conn, user);`. Now that's the interesting part, that `remove` function pointer. This is where the device-specific clean-up occurs. I'm guessing this is `hidp_session_remove`, based on the context. Let's verify that. I'll need to jump over to `net/bluetooth/hidp/core.c` to see exactly what `hidp_session_remove` does. Understanding the details of what `hidp_session_remove` does is crucial for understanding the complete unregistration process. Finally, the lock is released with `hci_dev_unlock(hdev)`.
|
| 13/2 |
2026/05/25 23:42 |
tool |
read-file |
0m
Args: map[File:net/bluetooth/hidp/core.c]
Results: map[Contents:/*
HIDP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
Copyright (C) 2013 David Herrmann <dh.herrmann@gmail.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.
*/
#include <linux/kref.h>
#include <linux/module.h>
#include <linux/file.h>
#include <linux/kthread.h>
#include <linux/hidraw.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "hidp.h"
#define VERSION "1.2"
static DECLARE_RWSEM(hidp_session_sem);
static DECLARE_WAIT_QUEUE_HEAD(hidp_session_wq);
static LIST_HEAD(hidp_session_list);
static unsigned char hidp_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
};
static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
static int hidp_session_probe(struct l2cap_conn *conn,
struct l2cap_user *user);
static void hidp_session_remove(struct l2cap_conn *conn,
struct l2cap_user *user);
static int hidp_session_thread(void *arg);
static void hidp_session_terminate(struct hidp_session *s);
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
{
u32 valid_flags = 0;
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
ci->flags = session->flags & valid_flags;
ci->state = BT_CONNECTED;
if (session->input) {
ci->vendor = session->input->id.vendor;
ci->product = session->input->id.product;
ci->version = session->input->id.version;
if (session->input->name)
strscpy(ci->name, session->input->name, 128);
else
strscpy(ci->name, "HID Boot Device", 128);
} else if (session->hid) {
ci->vendor = session->hid->vendor;
ci->product = session->hid->product;
ci->version = session->hid->version;
strscpy(ci->name, session->hid->name, 128);
}
}
/* assemble skb, queue message on @transmit and wake up the session thread */
static int hidp_send_message(struct hidp_session *session, struct socket *sock,
struct sk_buff_head *transmit, unsigned char hdr,
const unsigned char *data, int size)
{
struct sk_buff *skb;
struct sock *sk = sock->sk;
int ret;
BT_DBG("session %p data %p size %d", session, data, size);
if (atomic_read(&session->terminate))
return -EIO;
skb = alloc_skb(size + 1, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate memory for new frame");
return -ENOMEM;
}
skb_put_u8(skb, hdr);
if (data && size > 0) {
skb_put_data(skb, data, size);
ret = size;
} else {
ret = 0;
}
skb_queue_tail(transmit, skb);
wake_up_interruptible(sk_sleep(sk));
return ret;
}
static int hidp_send_ctrl_message(struct hidp_session *session,
unsigned char hdr, const unsigned char *data,
int size)
{
return hidp_send_message(session, session->ctrl_sock,
&session->ctrl_transmit, hdr, data, size);
}
static int hidp_send_intr_message(struct hidp_session *session,
unsigned char hdr, const unsigned char *data,
int size)
{
return hidp_send_message(session, session->intr_sock,
&session->intr_transmit, hdr, data, size);
}
static int hidp_input_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct hidp_session *session = input_get_drvdata(dev);
unsigned char newleds;
unsigned char hdr, data[2];
BT_DBG("session %p type %d code %d value %d",
session, type, code, value);
if (type != EV_LED)
return -1;
newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
(!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) |
(!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led) << 0);
if (session->leds == newleds)
return 0;
session->leds = newleds;
hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
data[0] = 0x01;
data[1] = newleds;
return hidp_send_intr_message(session, hdr, data, 2);
}
static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
{
struct input_dev *dev = session->input;
unsigned char *keys = session->keys;
unsigned char *udata = skb->data + 1;
signed char *sdata = skb->data + 1;
int i, size = skb->len - 1;
switch (skb->data[0]) {
case 0x01: /* Keyboard report */
for (i = 0; i < 8; i++)
input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
/* If all the key codes have been set to 0x01, it means
* too many keys were pressed at the same time. */
if (!memcmp(udata + 2, hidp_mkeyspat, 6))
break;
for (i = 2; i < 8; i++) {
if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
if (hidp_keycode[keys[i]])
input_report_key(dev, hidp_keycode[keys[i]], 0);
else
BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
}
if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
if (hidp_keycode[udata[i]])
input_report_key(dev, hidp_keycode[udata[i]], 1);
else
BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
}
}
memcpy(keys, udata, 8);
break;
case 0x02: /* Mouse report */
input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
input_report_rel(dev, REL_X, sdata[1]);
input_report_rel(dev, REL_Y, sdata[2]);
if (size > 3)
input_report_rel(dev, REL_WHEEL, sdata[3]);
break;
}
input_sync(dev);
}
static int hidp_get_raw_report(struct hid_device *hid,
unsigned char report_number,
unsigned char *data, size_t count,
unsigned char report_type)
{
struct hidp_session *session = hid->driver_data;
struct sk_buff *skb;
size_t len;
int numbered_reports = hid->report_enum[report_type].numbered;
int ret;
if (atomic_read(&session->terminate))
return -EIO;
switch (report_type) {
case HID_FEATURE_REPORT:
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
break;
case HID_INPUT_REPORT:
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
break;
case HID_OUTPUT_REPORT:
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
break;
default:
return -EINVAL;
}
if (mutex_lock_interruptible(&session->report_mutex))
return -ERESTARTSYS;
/* Set up our wait, and send the report request to the device. */
session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK;
session->waiting_report_number = numbered_reports ? report_number : -1;
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
data[0] = report_number;
ret = hidp_send_ctrl_message(session, report_type, data, 1);
if (ret < 0)
goto err;
/* Wait for the return of the report. The returned report
gets put in session->report_return. */
while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
!atomic_read(&session->terminate)) {
int res;
res = wait_event_interruptible_timeout(session->report_queue,
!test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)
|| atomic_read(&session->terminate),
5*HZ);
if (res == 0) {
/* timeout */
ret = -EIO;
goto err;
}
if (res < 0) {
/* signal */
ret = -ERESTARTSYS;
goto err;
}
}
skb = session->report_return;
if (skb) {
len = skb->len < count ? skb->len : count;
memcpy(data, skb->data, len);
kfree_skb(skb);
session->report_return = NULL;
} else {
/* Device returned a HANDSHAKE, indicating protocol error. */
len = -EIO;
}
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex);
return len;
err:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex);
return ret;
}
static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum,
unsigned char *data, size_t count,
unsigned char report_type)
{
struct hidp_session *session = hid->driver_data;
int ret;
switch (report_type) {
case HID_FEATURE_REPORT:
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
break;
case HID_INPUT_REPORT:
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT;
break;
case HID_OUTPUT_REPORT:
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT;
break;
default:
return -EINVAL;
}
if (mutex_lock_interruptible(&session->report_mutex))
return -ERESTARTSYS;
/* Set up our wait, and send the report request to the device. */
data[0] = reportnum;
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
ret = hidp_send_ctrl_message(session, report_type, data, count);
if (ret < 0)
goto err;
/* Wait for the ACK from the device. */
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) &&
!atomic_read(&session->terminate)) {
int res;
res = wait_event_interruptible_timeout(session->report_queue,
!test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)
|| atomic_read(&session->terminate),
10*HZ);
if (res == 0) {
/* timeout */
ret = -EIO;
goto err;
}
if (res < 0) {
/* signal */
ret = -ERESTARTSYS;
goto err;
}
}
if (!session->output_report_success) {
ret = -EIO;
goto err;
}
ret = count;
err:
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
mutex_unlock(&session->report_mutex);
return ret;
}
static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
{
struct hidp_session *session = hid->driver_data;
return hidp_send_intr_message(session,
HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
data, count);
}
static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype,
int reqtype)
{
switch (reqtype) {
case HID_REQ_GET_REPORT:
return hidp_get_raw_report(hid, reportnum, buf, len, rtype);
case HID_REQ_SET_REPORT:
return hidp_set_raw_report(hid, reportnum, buf, len, rtype);
default:
return -EIO;
}
}
static void hidp_idle_timeout(struct timer_list *t)
{
struct hidp_session *session = timer_container_of(session, t, timer);
/* The HIDP user-space API only contains calls to add and remove
* devices. There is no way to forward events of any kind. Therefore,
* we have to forcefully disconnect a device on idle-timeouts. This is
* unfortunate and weird API design, but it is spec-compliant and
* required for backwards-compatibility. Hence, on idle-timeout, we
* signal driver-detach events, so poll() will be woken up with an
* error-condition on both sockets.
*/
session->intr_sock->sk->sk_err = EUNATCH;
session->ctrl_sock->sk->sk_err = EUNATCH;
wake_up_interruptible(sk_sleep(session->intr_sock->sk));
wake_up_interruptible(sk_sleep(session->ctrl_sock->sk));
hidp_session_terminate(session);
}
static void hidp_set_timer(struct hidp_session *session)
{
if (session->idle_to > 0)
mod_timer(&session->timer, jiffies + HZ * session->idle_to);
}
static void hidp_del_timer(struct hidp_session *session)
{
if (session->idle_to > 0)
timer_delete_sync(&session->timer);
}
static void hidp_process_report(struct hidp_session *session, int type,
const u8 *data, unsigned int len, int intr)
{
if (len > HID_MAX_BUFFER_SIZE)
len = HID_MAX_BUFFER_SIZE;
memcpy(session->input_buf, data, len);
hid_input_report(session->hid, type, session->input_buf, len, intr);
}
static void hidp_process_handshake(struct hidp_session *session,
unsigned char param)
{
BT_DBG("session %p param 0x%02x", session, param);
session->output_report_success = 0; /* default condition */
switch (param) {
case HIDP_HSHK_SUCCESSFUL:
/* FIXME: Call into SET_ GET_ handlers here */
session->output_report_success = 1;
break;
case HIDP_HSHK_NOT_READY:
case HIDP_HSHK_ERR_INVALID_REPORT_ID:
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
case HIDP_HSHK_ERR_INVALID_PARAMETER:
if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
wake_up_interruptible(&session->report_queue);
/* FIXME: Call into SET_ GET_ handlers here */
break;
case HIDP_HSHK_ERR_UNKNOWN:
break;
case HIDP_HSHK_ERR_FATAL:
/* Device requests a reboot, as this is the only way this error
* can be recovered. */
hidp_send_ctrl_message(session,
HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
break;
default:
hidp_send_ctrl_message(session,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
break;
}
/* Wake up the waiting thread. */
if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
wake_up_interruptible(&session->report_queue);
}
static void hidp_process_hid_control(struct hidp_session *session,
unsigned char param)
{
BT_DBG("session %p param 0x%02x", session, param);
if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
/* Flush the transmit queues */
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
hidp_session_terminate(session);
}
}
/* Returns true if the passed-in skb should be freed by the caller. */
static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
unsigned char param)
{
int done_with_skb = 1;
BT_DBG("session %p skb %p len %u param 0x%02x", session, skb, skb->len, param);
switch (param) {
case HIDP_DATA_RTYPE_INPUT:
hidp_set_timer(session);
if (session->input)
hidp_input_report(session, skb);
if (session->hid)
hidp_process_report(session, HID_INPUT_REPORT,
skb->data, skb->len, 0);
break;
case HIDP_DATA_RTYPE_OTHER:
case HIDP_DATA_RTYPE_OUPUT:
case HIDP_DATA_RTYPE_FEATURE:
break;
default:
hidp_send_ctrl_message(session,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
}
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
param == session->waiting_report_type) {
if (session->waiting_report_number < 0 ||
session->waiting_report_number == skb->data[0]) {
/* hidp_get_raw_report() is waiting on this report. */
session->report_return = skb;
done_with_skb = 0;
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
wake_up_interruptible(&session->report_queue);
}
}
return done_with_skb;
}
static void hidp_recv_ctrl_frame(struct hidp_session *session,
struct sk_buff *skb)
{
unsigned char hdr, type, param;
int free_skb = 1;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
hdr = skb->data[0];
skb_pull(skb, 1);
type = hdr & HIDP_HEADER_TRANS_MASK;
param = hdr & HIDP_HEADER_PARAM_MASK;
switch (type) {
case HIDP_TRANS_HANDSHAKE:
hidp_process_handshake(session, param);
break;
case HIDP_TRANS_HID_CONTROL:
hidp_process_hid_control(session, param);
break;
case HIDP_TRANS_DATA:
free_skb = hidp_process_data(session, skb, param);
break;
default:
hidp_send_ctrl_message(session,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
break;
}
if (free_skb)
kfree_skb(skb);
}
static void hidp_recv_intr_frame(struct hidp_session *session,
struct sk_buff *skb)
{
unsigned char hdr;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
hdr = skb->data[0];
skb_pull(skb, 1);
if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
hidp_set_timer(session);
if (session->input)
hidp_input_report(session, skb);
if (session->hid) {
hidp_process_report(session, HID_INPUT_REPORT,
skb->data, skb->len, 1);
BT_DBG("report len %d", skb->len);
}
} else {
BT_DBG("Unsupported protocol header 0x%02x", hdr);
}
kfree_skb(skb);
}
static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
{
struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("sock %p data %p len %d", sock, data, len);
if (!len)
return 0;
memset(&msg, 0, sizeof(msg));
return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
/* dequeue message from @transmit and send via @sock */
static void hidp_process_transmit(struct hidp_session *session,
struct sk_buff_head *transmit,
struct socket *sock)
{
struct sk_buff *skb;
int ret;
BT_DBG("session %p", session);
while ((skb = skb_dequeue(transmit))) {
ret = hidp_send_frame(sock, skb->data, skb->len);
if (ret == -EAGAIN) {
skb_queue_head(transmit, skb);
break;
} else if (ret < 0) {
hidp_session_terminate(session);
kfree_skb(skb);
break;
}
hidp_set_timer(session);
kfree_skb(skb);
}
}
static int hidp_setup_input(struct hidp_session *session,
const struct hidp_connadd_req *req)
{
struct input_dev *input;
int i;
input = input_allocate_device();
if (!input)
return -ENOMEM;
session->input = input;
input_set_drvdata(input, session);
input->name = "Bluetooth HID Boot Protocol Device";
input->id.bustype = BUS_BLUETOOTH;
input->id.vendor = req->vendor;
input->id.product = req->product;
input->id.version = req->version;
if (req->subclass & 0x40) {
set_bit(EV_KEY, input->evbit);
set_bit(EV_LED, input->evbit);
set_bit(EV_REP, input->evbit);
set_bit(LED_NUML, input->ledbit);
set_bit(LED_CAPSL, input->ledbit);
set_bit(LED_SCROLLL, input->ledbit);
set_bit(LED_COMPOSE, input->ledbit);
set_bit(LED_KANA, input->ledbit);
for (i = 0; i < sizeof(hidp_keycode); i++)
set_bit(hidp_keycode[i], input->keybit);
clear_bit(0, input->keybit);
}
if (req->subclass & 0x80) {
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA);
input->relbit[0] |= BIT_MASK(REL_WHEEL);
}
input->dev.parent = &session->conn->hcon->dev;
input->event = hidp_input_event;
return 0;
}
static int hidp_open(struct hid_device *hid)
{
return 0;
}
static void hidp_close(struct hid_device *hid)
{
}
static int hidp_parse(struct hid_device *hid)
{
struct hidp_session *session = hid->driver_data;
return hid_parse_report(session->hid, session->rd_data,
session->rd_size);
}
static int hidp_start(struct hid_device *hid)
{
return 0;
}
static void hidp_stop(struct hid_device *hid)
{
struct hidp_session *session = hid->driver_data;
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
hid->claimed = 0;
}
static const struct hid_ll_driver hidp_hid_driver = {
.parse = hidp_parse,
.start = hidp_start,
.stop = hidp_stop,
.open = hidp_open,
.close = hidp_close,
.raw_request = hidp_raw_request,
.output_report = hidp_output_report,
};
/* This function sets up the hid device. It does not add it
to the HID system. That is done in hidp_add_connection(). */
static int hidp_setup_hid(struct hidp_session *session,
const struct hidp_connadd_req *req)
{
struct hid_device *hid;
int err;
session->rd_data = memdup_user(req->rd_data, req->rd_size);
if (IS_ERR(session->rd_data))
return PTR_ERR(session->rd_data);
session->rd_size = req->rd_size;
hid = hid_allocate_device();
if (IS_ERR(hid)) {
err = PTR_ERR(hid);
goto fault;
}
session->hid = hid;
hid->driver_data = session;
hid->bus = BUS_BLUETOOTH;
hid->vendor = req->vendor;
hid->product = req->product;
hid->version = req->version;
hid->country = req->country;
strscpy(hid->name, req->name, sizeof(hid->name));
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
&l2cap_pi(session->ctrl_sock->sk)->chan->src);
/* NOTE: Some device modules depend on the dst address being stored in
* uniq. Please be aware of this before making changes to this behavior.
*/
snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
&l2cap_pi(session->ctrl_sock->sk)->chan->dst);
hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver;
/* True if device is blocked in drivers/hid/hid-quirks.c */
if (hid_ignore(hid)) {
hid_destroy_device(session->hid);
session->hid = NULL;
return -ENODEV;
}
return 0;
fault:
kfree(session->rd_data);
session->rd_data = NULL;
return err;
}
/* initialize session devices */
static int hidp_session_dev_init(struct hidp_session *session,
const struct hidp_connadd_req *req)
{
int ret;
if (req->rd_size > 0) {
ret = hidp_setup_hid(session, req);
if (ret && ret != -ENODEV)
return ret;
}
if (!session->hid) {
ret = hidp_setup_input(session, req);
if (ret < 0)
return ret;
}
return 0;
}
/* destroy session devices */
static void hidp_session_dev_destroy(struct hidp_session *session)
{
if (session->hid)
put_device(&session->hid->dev);
else if (session->input)
input_put_device(session->input);
kfree(session->rd_data);
session->rd_data = NULL;
}
/* add HID/input devices to their underlying bus systems */
static int hidp_session_dev_add(struct hidp_session *session)
{
int ret;
/* Both HID and input systems drop a ref-count when unregistering the
* device but they don't take a ref-count when registering them. Work
* around this by explicitly taking a refcount during registration
* which is dropped automatically by unregistering the devices. */
if (session->hid) {
ret = hid_add_device(session->hid);
if (ret)
return ret;
get_device(&session->hid->dev);
} else if (session->input) {
ret = input_register_device(session->input);
if (ret)
return ret;
input_get_device(session->input);
}
return 0;
}
/* remove HID/input devices from their bus systems */
static void hidp_session_dev_del(struct hidp_session *session)
{
if (session->hid)
hid_destroy_device(session->hid);
else if (session->input)
input_unregister_device(session->input);
}
/*
* Asynchronous device registration
* HID device drivers might want to perform I/O during initialization to
* detect device types. Therefore, call device registration in a separate
* worker so the HIDP thread can schedule I/O operations.
* Note that this must be called after the worker thread was initialized
* successfully. This will then add the devices and increase session state
* on success, otherwise it will terminate the session thread.
*/
static void hidp_session_dev_work(struct work_struct *work)
{
struct hidp_session *session = container_of(work,
struct hidp_session,
dev_init);
int ret;
ret = hidp_session_dev_add(session);
if (!ret)
atomic_inc(&session->state);
else
hidp_session_terminate(session);
}
/*
* Create new session object
* Allocate session object, initialize static fields, copy input data into the
* object and take a reference to all sub-objects.
* This returns 0 on success and puts a pointer to the new session object in
* \out. Otherwise, an error code is returned.
* The new session object has an initial ref-count of 1.
*/
static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
struct socket *ctrl_sock,
struct socket *intr_sock,
const struct hidp_connadd_req *req,
struct l2cap_conn *conn)
{
struct hidp_session *session;
int ret;
struct bt_sock *ctrl, *intr;
ctrl = bt_sk(ctrl_sock->sk);
intr = bt_sk(intr_sock->sk);
session = kzalloc(sizeof(*session), GFP_KERNEL);
if (!session)
return -ENOMEM;
/* object and runtime management */
kref_init(&session->ref);
atomic_set(&session->state, HIDP_SESSION_IDLING);
init_waitqueue_head(&session->state_queue);
session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
/* connection management */
bacpy(&session->bdaddr, bdaddr);
session->conn = l2cap_conn_get(conn);
session->user.probe = hidp_session_probe;
session->user.remove = hidp_session_remove;
INIT_LIST_HEAD(&session->user.list);
session->ctrl_sock = ctrl_sock;
session->intr_sock = intr_sock;
skb_queue_head_init(&session->ctrl_transmit);
skb_queue_head_init(&session->intr_transmit);
session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl)->chan->omtu,
l2cap_pi(ctrl)->chan->imtu);
session->intr_mtu = min_t(uint, l2cap_pi(intr)->chan->omtu,
l2cap_pi(intr)->chan->imtu);
session->idle_to = req->idle_to;
/* device management */
INIT_WORK(&session->dev_init, hidp_session_dev_work);
timer_setup(&session->timer, hidp_idle_timeout, 0);
/* session data */
mutex_init(&session->report_mutex);
init_waitqueue_head(&session->report_queue);
ret = hidp_session_dev_init(session, req);
if (ret)
goto err_free;
get_file(session->intr_sock->file);
get_file(session->ctrl_sock->file);
*out = session;
return 0;
err_free:
l2cap_conn_put(session->conn);
kfree(session);
return ret;
}
/* increase ref-count of the given session by one */
static void hidp_session_get(struct hidp_session *session)
{
kref_get(&session->ref);
}
/* release callback */
static void session_free(struct kref *ref)
{
struct hidp_session *session = container_of(ref, struct hidp_session,
ref);
hidp_session_dev_destroy(session);
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
fput(session->intr_sock->file);
fput(session->ctrl_sock->file);
l2cap_conn_put(session->conn);
kfree(session);
}
/* decrease ref-count of the given session by one */
static void hidp_session_put(struct hidp_session *session)
{
kref_put(&session->ref, session_free);
}
/*
* Search the list of active sessions for a session with target address
* \bdaddr. You must hold at least a read-lock on \hidp_session_sem. As long as
* you do not release this lock, the session objects cannot vanish and you can
* safely take a reference to the session yourself.
*/
static struct hidp_session *__hidp_session_find(const bdaddr_t *bdaddr)
{
struct hidp_session *session;
list_for_each_entry(session, &hidp_session_list, list) {
if (!bacmp(bdaddr, &session->bdaddr))
return session;
}
return NULL;
}
/*
* Same as __hidp_session_find() but no locks must be held. This also takes a
* reference of the returned session (if non-NULL) so you must drop this
* reference if you no longer use the object.
*/
static struct hidp_session *hidp_session_find(const bdaddr_t *bdaddr)
{
struct hidp_session *session;
down_read(&hidp_session_sem);
session = __hidp_session_find(bdaddr);
if (session)
hidp_session_get(session);
up_read(&hidp_session_sem);
return session;
}
/*
* Start session synchronously
* This starts a session thread and waits until initialization
* is done or returns an error if it couldn't be started.
* If this returns 0 the session thread is up and running. You must call
* hipd_session_stop_sync() before deleting any runtime resources.
*/
static int hidp_session_start_sync(struct hidp_session *session)
{
unsigned int vendor, product;
if (session->hid) {
vendor = session->hid->vendor;
product = session->hid->product;
} else if (session->input) {
vendor = session->input->id.vendor;
product = session->input->id.product;
} else {
vendor = 0x0000;
product = 0x0000;
}
session->task = kthread_run(hidp_session_thread, session,
"khidpd_%04x%04x", vendor, product);
if (IS_ERR(session->task))
return PTR_ERR(session->task);
while (atomic_read(&session->state) <= HIDP_SESSION_IDLING)
wait_event(session->state_queue,
atomic_read(&session->state) > HIDP_SESSION_IDLING);
return 0;
}
/*
* Terminate session thread
* Wake up session thread and notify it to stop. This is asynchronous and
* returns immediately. Call this whenever a runtime error occurs and you want
* the session to stop.
* Note: wake_up_interruptible() performs any necessary memory-barriers for us.
*/
static void hidp_session_terminate(struct hidp_session *session)
{
atomic_inc(&session->terminate);
/*
* See the comment preceding the call to wait_woken()
* in hidp_session_run().
*/
wake_up_interruptible(&hidp_session_wq);
}
/*
* Probe HIDP session
* This is called from the l2cap_conn core when our l2cap_user object is bound
* to the hci-connection. We get the session via the \user object and can now
* start the session thread, link it into the global session list and
* schedule HID/input device registration.
* The global session-list owns its own reference to the session object so you
* can drop your own reference after registering the l2cap_user object.
*/
static int hidp_session_probe(struct l2cap_conn *conn,
struct l2cap_user *user)
{
struct hidp_session *session = container_of(user,
struct hidp_session,
user);
struct hidp_session *s;
int ret;
down_write(&hidp_session_sem);
/* check that no other session for this device exists */
s = __hidp_session_find(&session->bdaddr);
if (s) {
ret = -EEXIST;
goto out_unlock;
}
if (session->input) {
ret = hidp_session_dev_add(session);
if (ret)
goto out_unlock;
}
ret = hidp_session_start_sync(session);
if (ret)
goto out_del;
/* HID device registration is async to allow I/O during probe */
if (session->input)
atomic_inc(&session->state);
else
schedule_work(&session->dev_init);
hidp_session_get(session);
list_add(&session->list, &hidp_session_list);
ret = 0;
goto out_unlock;
out_del:
if (session->input)
hidp_session_dev_del(session);
out_unlock:
up_write(&hidp_session_sem);
return ret;
}
/*
* Remove HIDP session
* Called from the l2cap_conn core when either we explicitly unregistered
* the l2cap_user object or if the underlying connection is shut down.
* We signal the hidp-session thread to shut down, unregister the HID/input
* devices and unlink the session from the global list.
* This drops the reference to the session that is owned by the global
* session-list.
* Note: We _must_ not synchronosly wait for the session-thread to shut down.
* This is, because the session-thread might be waiting for an HCI lock that is
* held while we are called. Therefore, we only unregister the devices and
* notify the session-thread to terminate. The thread itself owns a reference
* to the session object so it can safely shut down.
*/
static void hidp_session_remove(struct l2cap_conn *conn,
struct l2cap_user *user)
{
struct hidp_session *session = container_of(user,
struct hidp_session,
user);
down_write(&hidp_session_sem);
hidp_session_terminate(session);
cancel_work_sync(&session->dev_init);
if (session->input ||
atomic_read(&session->state) > HIDP_SESSION_PREPARING)
hidp_session_dev_del(session);
list_del(&session->list);
up_write(&hidp_session_sem);
hidp_session_put(session);
}
/*
* Session Worker
* This performs the actual main-loop of the HIDP worker. We first check
* whether the underlying connection is still alive, then parse all pending
* messages and finally send all outstanding messages.
*/
static void hidp_session_run(struct hidp_session *session)
{
struct sock *ctrl_sk = session->ctrl_sock->sk;
struct sock *intr_sk = session->intr_sock->sk;
struct sk_buff *skb;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
add_wait_queue(&hidp_session_wq, &wait);
for (;;) {
/*
* This thread can be woken up two ways:
* - You call hidp_session_terminate() which sets the
* session->terminate flag and wakes this thread up.
* - Via modifying the socket state of ctrl/intr_sock. This
* thread is woken up by ->sk_state_changed().
*/
if (atomic_read(&session->terminate))
break;
if (ctrl_sk->sk_state != BT_CONNECTED ||
intr_sk->sk_state != BT_CONNECTED)
break;
/* parse incoming intr-skbs */
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
hidp_recv_intr_frame(session, skb);
else
kfree_skb(skb);
}
/* send pending intr-skbs */
hidp_process_transmit(session, &session->intr_transmit,
session->intr_sock);
/* parse incoming ctrl-skbs */
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
hidp_recv_ctrl_frame(session, skb);
else
kfree_skb(skb);
}
/* send pending ctrl-skbs */
hidp_process_transmit(session, &session->ctrl_transmit,
session->ctrl_sock);
/*
* wait_woken() performs the necessary memory barriers
* for us; see the header comment for this primitive.
*/
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
remove_wait_queue(&hidp_session_wq, &wait);
atomic_inc(&session->terminate);
}
static int hidp_session_wake_function(wait_queue_entry_t *wait,
unsigned int mode,
int sync, void *key)
{
wake_up_interruptible(&hidp_session_wq);
return false;
}
/*
* HIDP session thread
* This thread runs the I/O for a single HIDP session. Startup is synchronous
* which allows us to take references to ourself here instead of doing that in
* the caller.
* When we are ready to run we notify the caller and call hidp_session_run().
*/
static int hidp_session_thread(void *arg)
{
struct hidp_session *session = arg;
DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function);
DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function);
BT_DBG("session %p", session);
/* initialize runtime environment */
hidp_session_get(session);
__module_get(THIS_MODULE);
set_user_nice(current, -15);
hidp_set_timer(session);
add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
/* This memory barrier is paired with wq_has_sleeper(). See
* sock_poll_wait() for more information why this is needed. */
smp_mb__before_atomic();
/* notify synchronous startup that we're ready */
atomic_inc(&session->state);
wake_up(&session->state_queue);
/* run session */
hidp_session_run(session);
/* cleanup runtime environment */
remove_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
remove_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
wake_up_interruptible(&session->report_queue);
hidp_del_timer(session);
/*
* If we stopped ourself due to any internal signal, we should try to
* unregister our own session here to avoid having it linger until the
* parent l2cap_conn dies or user-space cleans it up.
* This does not deadlock as we don't do any synchronous shutdown.
* Instead, this call has the same semantics as if user-space tried to
* delete the session.
*/
l2cap_unregister_user(session->conn, &session->user);
hidp_session_put(session);
module_put_and_kthread_exit(0);
return 0;
}
static int hidp_verify_sockets(struct socket *ctrl_sock,
struct socket *intr_sock)
{
struct l2cap_chan *ctrl_chan, *intr_chan;
struct bt_sock *ctrl, *intr;
struct hidp_session *session;
if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock))
return -EINVAL;
ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan;
intr_chan = l2cap_pi(intr_sock->sk)->chan;
if (bacmp(&ctrl_chan->src, &intr_chan->src) ||
bacmp(&ctrl_chan->dst, &intr_chan->dst))
return -ENOTUNIQ;
ctrl = bt_sk(ctrl_sock->sk);
intr = bt_sk(intr_sock->sk);
if (ctrl->sk.sk_state != BT_CONNECTED ||
intr->sk.sk_state != BT_CONNECTED)
return -EBADFD;
/* early session check, we check again during session registration */
session = hidp_session_find(&ctrl_chan->dst);
if (session) {
hidp_session_put(session);
return -EEXIST;
}
return 0;
}
int hidp_connection_add(const struct hidp_connadd_req *req,
struct socket *ctrl_sock,
struct socket *intr_sock)
{
u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG) |
BIT(HIDP_BOOT_PROTOCOL_MODE);
struct hidp_session *session;
struct l2cap_conn *conn;
struct l2cap_chan *chan;
int ret;
ret = hidp_verify_sockets(ctrl_sock, intr_sock);
if (ret)
return ret;
if (req->flags & ~valid_flags)
return -EINVAL;
chan = l2cap_pi(ctrl_sock->sk)->chan;
conn = NULL;
l2cap_chan_lock(chan);
if (chan->conn)
conn = l2cap_conn_get(chan->conn);
l2cap_chan_unlock(chan);
if (!conn)
return -EBADFD;
ret = hidp_session_new(&session, &chan->dst, ctrl_sock,
intr_sock, req, conn);
if (ret)
goto out_conn;
ret = l2cap_register_user(conn, &session->user);
if (ret)
goto out_session;
ret = 0;
out_session:
hidp_session_put(session);
out_conn:
l2cap_conn_put(conn);
return ret;
}
int hidp_connection_del(struct hidp_conndel_req *req)
{
u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
struct hidp_session *session;
if (req->flags & ~valid_flags)
return -EINVAL;
session = hidp_session_find(&req->bdaddr);
if (!session)
return -ENOENT;
if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
hidp_send_ctrl_message(session,
HIDP_TRANS_HID_CONTROL |
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
NULL, 0);
else
l2cap_unregister_user(session->conn, &session->user);
hidp_session_put(session);
return 0;
}
int hidp_get_connlist(struct hidp_connlist_req *req)
{
struct hidp_session *session;
int err = 0, n = 0;
BT_DBG("");
down_read(&hidp_session_sem);
list_for_each_entry(session, &hidp_session_list, list) {
struct hidp_conninfo ci;
hidp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
if (++n >= req->cnum)
break;
req->ci++;
}
req->cnum = n;
up_read(&hidp_session_sem);
return err;
}
int hidp_get_conninfo(struct hidp_conninfo *ci)
{
struct hidp_session *session;
session = hidp_session_find(&ci->bdaddr);
if (session) {
hidp_copy_session(session, ci);
hidp_session_put(session);
}
return session ? 0 : -ENOENT;
}
static int __init hidp_init(void)
{
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
return hidp_init_sockets();
}
static void __exit hidp_exit(void)
{
hidp_cleanup_sockets();
}
module_init(hidp_init);
module_exit(hidp_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-6");
]
|
| 14/2 |
2026/05/25 23:42 |
llm |
expert |
0m
Model:
Tokens:
input: 109153
output: 22
thoughts: 3834
Thoughts: **Deep Dive into a Bluetooth HIDP Deadlock**
Alright, let's break this down. We have `hidp_session_remove`, which kicks off a sequence that's clearly causing trouble. It starts by acquiring `hidp_session_sem` with `down_write`. Then, it calls `cancel_work_sync` on `session->dev_init`. I immediately zero in on that `cancel_work_sync` call. That means `hidp_session_remove` is *waiting* for `session->dev_init` to finish. I want to know what this `session->dev_init` is up to.
Peeking inside reveals `hidp_session_dev_work`. This function calls `hidp_session_dev_add` which, crucially, calls `hid_add_device(session->hid)`. `hid_add_device`... that's a known potential blocking point! It's likely doing stuff with the HID core and, as a device addition, could take a while. I recall that `khidpd` is blocked for a significant amount of time, a staggering 143 seconds. That sounds like this blocking call, so let's check it.
Now, we're told `khidpd` is blocked in `__cancel_work_sync`, which aligns with the wait in `hidp_session_remove`. Okay, `khidpd` (which I see is also process `5507`) is waiting. We've narrowed down that `kworker/0:3/1353` is likely the worker thread running `hidp_session_dev_work`. However, what's interesting is, the kworker thread holding this work item's locks are not listed. We *don't* see the 3 locks this kworker is holding in the snippet, which is strange.
I then look into the other kworker thread. `kworker/u4:4/1034` is holding locks that are totally unrelated to the functions we are looking at. It is running `linkwatch_work` and holds the `rtnl_mutex` lock. That seems normal, at least for this particular kworker.
However, something else catches my eye: `syz-executor/5806`. This task *also* holds `rtnl_mutex` and is doing `rtnl_newlink`. Wait, we have two threads both holding the same mutex? That's impossible, unless one of them is trying to *acquire* it, which is the problem. It is blocked in `rtnl_lock` inside `rtnl_newlink` and the lockdep shows it as *held*. Something is seriously wrong! The trace says it is blocked while trying to *acquire* the lock. I suspect it's deadlocked.
Next, I look into the other syz tasks. I see that syz.1.55 is blocked on `hidp_session_sem` which is fine. The important bit is that `khidpd` also holds `hidp_session_sem` (write lock). This is likely because the khidpd task is running `hidp_session_remove`, and that means `session->dev_init` (run by a worker) is still running.
So `kworker/0:3/1353` *should* be running `hidp_session_dev_work`. We have a good idea of what that entails.
Now, I look at why there is a deadlock. I start to trace down the locks and see that `khidpd_000d2f19` holds a lock and is blocked, and `syz.0.54/5547` holds a different lock. This is wrong and indicates a circular dependency. I focus on `syz.0.54` because it seems to be blocked at the beginning of an `l2cap_chan_connect`. `syz.0.54` holds `&hdev->lock`, the mutex is acquired at the start of `l2cap_chan_connect`. That task is blocked because it is trying to acquire the device lock, but it seems that `khidpd` is blocked while holding it. After carefully looking through the code, I see that `l2cap_chan_connect` is trying to acquire `&hdev->lock`, and `khidpd` has *also* acquired that same lock! But if `syz.0.54` is holding `&hdev->lock`, then `khidpd_000d2f19` can't, so what is happening?
Aha! Lockdep is showing that `sy
|
| 15/2 |
2026/05/25 23:43 |
tool |
grepper |
0m
Args: map[Expression:hidp_session_dev_add]
Results: map[Output:net/bluetooth/hidp/core.c=834=static void hidp_session_dev_destroy(struct hidp_session *session)
--
net/bluetooth/hidp/core.c-845-/* add HID/input devices to their underlying bus systems */
net/bluetooth/hidp/core.c:846:static int hidp_session_dev_add(struct hidp_session *session)
net/bluetooth/hidp/core.c-847-{
--
net/bluetooth/hidp/core.c=888=static void hidp_session_dev_work(struct work_struct *work)
--
net/bluetooth/hidp/core.c-894-
net/bluetooth/hidp/core.c:895: ret = hidp_session_dev_add(session);
net/bluetooth/hidp/core.c-896- if (!ret)
--
net/bluetooth/hidp/core.c=1097=static int hidp_session_probe(struct l2cap_conn *conn,
--
net/bluetooth/hidp/core.c-1115- if (session->input) {
net/bluetooth/hidp/core.c:1116: ret = hidp_session_dev_add(session);
net/bluetooth/hidp/core.c-1117- if (ret)
]
|
| 16/2 |
2026/05/25 23:43 |
llm |
expert |
0m
Model:
Tokens:
input: 114384
output: 19
thoughts: 1439
Thoughts: **Analyzing a Potential Deadlock in HIDP Session Device Addition**
Okay, let's break this down. I'm looking at the `hidp_session_dev_add` function, which is responsible for adding a HID device to the system. The primary concern here is the potential for `hid_add_device` to be blocking. The bug report initially points to tasks being blocked, but crucially, not directly within `hid_add_device`.
I see `khidpd` and another `syz` task blocked. My immediate thought is to trace the execution path. The output initially mentions `kworker/0:3/1353` and `kworker/u4:4` holding locks. However, I am missing the crucial lockdep output for the former, which is a key piece of the puzzle. I can see that `kworker/u4:4` is holding `rtnl_mutex` in `linkwatch_event`, implying contention with something also acquiring that lock.
My analysis reveals that `syz-executor/5806` *also* holds `rtnl_mutex`, specifically at `rtnl_newlink`. This is a critical observation, as `rtnl_newlink` acquires the `rtnl_mutex`. If `syz-executor/5806` is holding `rtnl_mutex`, then `kworker/u4:4` *must* be blocked on that mutex within `linkwatch_event`.
Here's the problem: `khidpd` is blocked in `__cancel_work_sync`, waiting for the `session->dev_init` work to complete. This work is queued on the `system_wq` workqueue. The work function calls `hidp_session_dev_add` and ultimately `hid_add_device`.
My current theory is that `hidp_session_dev_work` might be somehow blocked, preventing `khidpd` from completing its `__cancel_work_sync` call. That work eventually calls `hid_add_device`. But `hidp_session_dev_work` doesn't directly acquire `hidp_session_sem`. Nor does `hid_add_device`. I am looking to discover what other resource is involved, and currently, I'm stuck, since the key lockdep output for `kworker/0:3/1353` is missing.
I need to confirm if `hid_add_device` itself might be indirectly blocked or whether a contention on the system workqueue or resource contention during the `hidp_session_dev_work` work is the culprit. The puzzle continues.
|
| 17/2 |
2026/05/25 23:43 |
tool |
grepper |
0m
Args: map[Expression:hid_add_device]
Results: map[Output:Documentation/hid/hid-transport.rst=190=with HID core::
--
Documentation/hid/hid-transport.rst-213-
Documentation/hid/hid-transport.rst:214: ret = hid_add_device(hid);
Documentation/hid/hid-transport.rst-215- if (ret)
--
Documentation/hid/hid-transport.rst-217-
Documentation/hid/hid-transport.rst:218:Once hid_add_device() is entered, HID core might use the callbacks provided in
Documentation/hid/hid-transport.rst-219-"custom_ll_driver". Note that fields like "country" can be ignored by underlying
--
Documentation/hid/intel-ish-hid.rst=276=End to End HID transport Sequence Diagram
--
Documentation/hid/intel-ish-hid.rst-337- | | | |
Documentation/hid/intel-ish-hid.rst:338: hid_add_device | | |
Documentation/hid/intel-ish-hid.rst-339- | | | |
--
drivers/hid/amd-sfh-hid/amd_sfh_hid.c-22- *
drivers/hid/amd-sfh-hid/amd_sfh_hid.c:23: * This function gets called during call to hid_add_device
drivers/hid/amd-sfh-hid/amd_sfh_hid.c-24- *
--
drivers/hid/amd-sfh-hid/amd_sfh_hid.c=126=int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data)
--
drivers/hid/amd-sfh-hid/amd_sfh_hid.c-158-
drivers/hid/amd-sfh-hid/amd_sfh_hid.c:159: rc = hid_add_device(hid);
drivers/hid/amd-sfh-hid/amd_sfh_hid.c-160- if (rc)
--
drivers/hid/hid-core.c=2852=EXPORT_SYMBOL(hid_bus_type);
drivers/hid/hid-core.c-2853-
drivers/hid/hid-core.c:2854:int hid_add_device(struct hid_device *hdev)
drivers/hid/hid-core.c-2855-{
--
drivers/hid/hid-core.c-2914-}
drivers/hid/hid-core.c:2915:EXPORT_SYMBOL_GPL(hid_add_device);
drivers/hid/hid-core.c-2916-
--
drivers/hid/hid-goodix-spi.c=206=static int goodix_dev_confirm(struct goodix_ts_data *ts)
--
drivers/hid/hid-goodix-spi.c-240- *
drivers/hid/hid-goodix-spi.c:241: * This function gets called during call to hid_add_device
drivers/hid/hid-goodix-spi.c-242- *
--
drivers/hid/hid-goodix-spi.c=634=static int goodix_hid_init(struct goodix_ts_data *ts)
--
drivers/hid/hid-goodix-spi.c-661-
drivers/hid/hid-goodix-spi.c:662: error = hid_add_device(hid);
drivers/hid/hid-goodix-spi.c-663- if (error) {
--
drivers/hid/hid-hyperv.c=464=static int mousevsc_probe(struct hv_device *device,
--
drivers/hid/hid-hyperv.c-513-
drivers/hid/hid-hyperv.c:514: ret = hid_add_device(hid_dev);
drivers/hid/hid-hyperv.c-515- if (ret)
--
drivers/hid/hid-logitech-dj.c=702=static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
--
drivers/hid/hid-logitech-dj.c-783-
drivers/hid/hid-logitech-dj.c:784: if (hid_add_device(dj_hiddev)) {
drivers/hid/hid-logitech-dj.c-785- hid_err(djrcv_hdev, "%s: failed adding dj_device\n", __func__);
drivers/hid/hid-logitech-dj.c:786: goto hid_add_device_fail;
drivers/hid/hid-logitech-dj.c-787- }
--
drivers/hid/hid-logitech-dj.c-790-
drivers/hid/hid-logitech-dj.c:791:hid_add_device_fail:
drivers/hid/hid-logitech-dj.c-792- spin_lock_irqsave(&djrcv_dev->lock, flags);
--
drivers/hid/hid-steam.c=1210=static int steam_probe(struct hid_device *hdev,
--
drivers/hid/hid-steam.c-1291-
drivers/hid/hid-steam.c:1292: ret = hid_add_device(steam->client_hdev);
drivers/hid/hid-steam.c-1293- if (ret)
--
drivers/hid/i2c-hid/i2c-hid-core.c=1066=static int i2c_hid_core_register_hid(struct i2c_hid *ihid)
--
drivers/hid/i2c-hid/i2c-hid-core.c-1073-
drivers/hid/i2c-hid/i2c-hid-core.c:1074: ret = hid_add_device(hid);
drivers/hid/i2c-hid/i2c-hid-core.c-1075- if (ret) {
--
drivers/hid/intel-ish-hid/ishtp-hid.c-16- *
drivers/hid/intel-ish-hid/ishtp-hid.c:17: * This function gets called during call to hid_add_device
drivers/hid/intel-ish-hid/ishtp-hid.c-18- *
--
drivers/hid/intel-ish-hid/ishtp-hid.c=206=int ishtp_hid_probe(unsigned int cur_hid_dev,
--
drivers/hid/intel-ish-hid/ishtp-hid.c-240-
drivers/hid/intel-ish-hid/ishtp-hid.c:241: rv = hid_add_device(hid);
drivers/hid/intel-ish-hid/ishtp-hid.c-242- if (rv)
--
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c-16- *
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c:17: * This function gets called during call to hid_add_device
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c-18- *
--
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c=105=int quicki2c_hid_probe(struct quicki2c_device *qcdev)
--
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c-123-
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c:124: ret = hid_add_device(hid);
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c-125- if (ret) {
--
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c-15- *
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c:16: * This function gets called during call to hid_add_device
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c-17- *
--
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c=104=int quickspi_hid_probe(struct quickspi_device *qsdev)
--
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c-122-
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c:123: ret = hid_add_device(hid);
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c-124- if (ret) {
--
drivers/hid/surface-hid/surface_hid_core.c=189=int surface_hid_device_add(struct surface_hid_device *shid)
--
drivers/hid/surface-hid/surface_hid_core.c-219-
drivers/hid/surface-hid/surface_hid_core.c:220: status = hid_add_device(shid->hid);
drivers/hid/surface-hid/surface_hid_core.c-221- if (status)
--
drivers/hid/uhid.c=29=struct uhid_device {
--
drivers/hid/uhid.c-32- /* This flag tracks whether the HID device is usable for commands from
drivers/hid/uhid.c:33: * userspace. The flag is already set before hid_add_device(), which
drivers/hid/uhid.c:34: * runs in workqueue context, to allow hid_add_device() to communicate
drivers/hid/uhid.c-35- * with userspace.
drivers/hid/uhid.c:36: * However, if hid_add_device() fails, the flag is cleared without
drivers/hid/uhid.c-37- * holding devlock.
--
drivers/hid/uhid.c=68=static void uhid_device_add_worker(struct work_struct *work)
--
drivers/hid/uhid.c-72-
drivers/hid/uhid.c:73: ret = hid_add_device(uhid->hid);
drivers/hid/uhid.c-74- if (ret) {
--
drivers/hid/usbhid/hid-core.c=1347=static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id)
--
drivers/hid/usbhid/hid-core.c-1434-
drivers/hid/usbhid/hid-core.c:1435: ret = hid_add_device(hid);
drivers/hid/usbhid/hid-core.c-1436- if (ret) {
--
drivers/platform/x86/asus-tf103c-dock.c=716=static int tf103c_dock_probe(struct i2c_client *client)
--
drivers/platform/x86/asus-tf103c-dock.c-851-
drivers/platform/x86/asus-tf103c-dock.c:852: ret = hid_add_device(dock->hid);
drivers/platform/x86/asus-tf103c-dock.c-853- if (ret)
--
drivers/platform/x86/tuxedo/nb04/wmi_ab.c=825=static int tux_virt_lamparray_add_device(struct wmi_device *wdev,
--
drivers/platform/x86/tuxedo/nb04/wmi_ab.c-845-
drivers/platform/x86/tuxedo/nb04/wmi_ab.c:846: ret = hid_add_device(hdev);
drivers/platform/x86/tuxedo/nb04/wmi_ab.c-847- if (ret)
--
drivers/staging/greybus/hid.c=421=static int gb_hid_probe(struct gb_bundle *bundle,
--
drivers/staging/greybus/hid.c-469-
drivers/staging/greybus/hid.c:470: ret = hid_add_device(hid);
drivers/staging/greybus/hid.c-471- if (ret) {
--
include/linux/hid.h=925=extern bool hid_ignore(struct hid_device *);
include/linux/hid.h:926:extern int hid_add_device(struct hid_device *);
include/linux/hid.h-927-extern void hid_destroy_device(struct hid_device *);
--
net/bluetooth/hidp/core.c=846=static int hidp_session_dev_add(struct hidp_session *session)
--
net/bluetooth/hidp/core.c-855- if (session->hid) {
net/bluetooth/hidp/core.c:856: ret = hid_add_device(session->hid);
net/bluetooth/hidp/core.c-857- if (ret)
]
|
| 18/2 |
2026/05/25 23:43 |
llm |
expert |
0m
Model:
Tokens:
input: 119783
output: 20
thoughts: 5650
Thoughts: **Deadlock Analysis in Bluetooth HIDP Driver**
Okay, so here's what I've pieced together. A HID device is being added, and that triggers a sequence of events. First, the `hid_add_device` function is called, which, after many checks, ends up calling `device_add`, which triggers udev events. In parallel, `kworker/u4:4` is running `linkwatch_event`, which is blocked, likely waiting on `synchronize_rcu_expedited`. This work queue is holding the `rtnl_mutex` and `rcu_state.exp_mutex`.
Meanwhile, another process, `syz-executor/5806`, seems to also want the `rtnl_mutex`, but it's blocked. So, that's not the root issue. It is a red herring. Then, I look at the trace and see `syz.0.54` is blocked in `l2cap_chan_connect+0x102`. A closer look shows it's waiting on `&hdev->lock`, which is held by `khidpd_000d2f19/5507`. `khidpd` holds `&hdev->lock` and `hidp_session_sem` and is blocked in `__cancel_work_sync(&session->dev_init)`.
`hidp_session_dev_work` runs `hidp_session_dev_add` which, in turn calls `hid_add_device`. The call to `hid_add_device` triggered the HID driver probe function which is the root cause. This causes the udev events, which can take a while. It appears the probe function calls `hid_hw_raw_request` or `hid_hw_request`, likely to get information from the device. This then calls `hidp_raw_request`, which is where the problem lies.
The `hidp_raw_request` sends a message to the device and blocks, waiting for a reply by using `wait_event_interruptible_timeout`. The reply, however, is handled by the `khidpd` thread. However, that thread is blocked because it is waiting for `__cancel_work_sync` to finish, which is the session thread, waiting for the workqueue. The workqueue's `dev_init` which is being blocked by a timeout. A *real* driver is waiting for a reply that will never come, because the waiter is also blocked.
In summary: a HID driver is waiting for a reply from the device, and the thread that handles that reply (`khidpd`) is blocked in a `cancel_work_sync` call. This is because the device has been registered and is waiting for it to be removed, but the cleanup is blocked on `khidpd`. This means the HID driver probe is blocked, and `khidpd` is blocked waiting for it to finish. **This is a deadlock caused by the driver probe calling a blocking function that relies on the worker thread, which is waiting for the probe to finish**.
|
| 19/2 |
2026/05/25 23:44 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 5403 lines.
Use more precise expression if possible.
[arch/arm/boot/compressed/head.S=759=__armv3_mpu_cache_on:
--
arch/arm/boot/compressed/head.S-777- @ .... .... .... WC.M
arch/arm/boot/compressed/head.S:778: orr r0, r0, #0x000d @ .... .... .... 11.1
arch/arm/boot/compressed/head.S-779- /* ?? this overwrites the value constructed above? */
--
arch/arm/boot/compressed/head.S=920=__common_mmu_cache_on:
--
arch/arm/boot/compressed/head.S-922-#ifndef DEBUG
arch/arm/boot/compressed/head.S:923: orr r0, r0, #0x000d @ Write buffer, mmu
arch/arm/boot/compressed/head.S-924-#endif
--
arch/arm/boot/compressed/head.S=1159=__armv4_mpu_cache_off:
arch/arm/boot/compressed/head.S-1160- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1161: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1162- mcr p15, 0, r0, c1, c0 @ turn MPU and cache off
--
arch/arm/boot/compressed/head.S=1169=__armv3_mpu_cache_off:
arch/arm/boot/compressed/head.S-1170- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1171: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1172- mcr p15, 0, r0, c1, c0, 0 @ turn MPU and cache off
--
arch/arm/boot/compressed/head.S=1177=__armv4_mmu_cache_off:
--
arch/arm/boot/compressed/head.S-1179- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1180: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1181- mcr p15, 0, r0, c1, c0 @ turn MMU and cache off
--
arch/arm/boot/dts/broadcom/bcm953012hr.dts=81= partition@0 {
arch/arm/boot/dts/broadcom/bcm953012hr.dts-82- label = "boot";
arch/arm/boot/dts/broadcom/bcm953012hr.dts:83: reg = <0x00000000 0x000d0000>;
arch/arm/boot/dts/broadcom/bcm953012hr.dts-84- };
arch/arm/boot/dts/broadcom/bcm953012hr.dts=85= partition@d000 {
arch/arm/boot/dts/broadcom/bcm953012hr.dts-86- label = "env";
arch/arm/boot/dts/broadcom/bcm953012hr.dts:87: reg = <0x000d0000 0x00030000>;
arch/arm/boot/dts/broadcom/bcm953012hr.dts-88- };
--
arch/arm/boot/dts/broadcom/bcm953012k.dts=91= partition@0 {
arch/arm/boot/dts/broadcom/bcm953012k.dts-92- label = "boot";
arch/arm/boot/dts/broadcom/bcm953012k.dts:93: reg = <0x00000000 0x000d0000>;
arch/arm/boot/dts/broadcom/bcm953012k.dts-94- };
arch/arm/boot/dts/broadcom/bcm953012k.dts=95= partition@d000 {
arch/arm/boot/dts/broadcom/bcm953012k.dts-96- label = "env";
arch/arm/boot/dts/broadcom/bcm953012k.dts:97: reg = <0x000d0000 0x00030000>;
arch/arm/boot/dts/broadcom/bcm953012k.dts-98- };
--
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts=293= partition@0 { /* u-boot */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-294- label = "RedBoot";
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts:295: reg = <0x00000000 0x000d0000>; /* 832KB */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-296- };
--
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts=298= partition@c0000 { /* uImage */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-299- label = "zImage";
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts:300: reg = <0x000d0000 0x002d0000>; /* 2880KB */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-301- };
--
arch/arm/boot/dts/marvell/dove.dtsi=446= pmu: power-management@d0000 {
--
arch/arm/boot/dts/marvell/dove.dtsi-448- reg = <0xd0000 0x8000>, <0xd8000 0x8000>;
arch/arm/boot/dts/marvell/dove.dtsi:449: ranges = <0x00000000 0x000d0000 0x8000
arch/arm/boot/dts/marvell/dove.dtsi:450: 0x00008000 0x000d8000 0x8000>;
arch/arm/boot/dts/marvell/dove.dtsi-451- interrupts = <33>;
--
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi=223= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi-233- 0x00000004 0x00000006
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi:234: 0x06040202 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi-235- 0x7586120a 0x70000f03
--
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi=282= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi-300- 0x06040202
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi:301: 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi-302- 0x7586120a
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=441= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-459- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:460: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-461- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=731= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-749- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:750: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-751- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=1021= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-1039- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:1040: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-1041- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi=270= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi-288- 0x06040202
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi:289: 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi-290- 0x7586120a
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=156= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-162- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:163: 0x04040001 0x000d090c 0x71c6120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-164- };
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=516= timing-204000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-543- 0x00000000 0x00000000 0x00000000 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:544: 0x00000000 0x000d0000 0x000d0000 0x000d0000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:545: 0x000d0000 0x000e0220 0x0800201c 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-546- 0x77ffc004 0x01f1f008 0x00000000 0x00000007
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=552= timing-500000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-582- 0x08000068 0x08000000 0x00000802 0x00064000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:583: 0x000000b4 0x000d000d 0xa0f10404 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-584- 0x00000000 0x80000fde 0xe0000000 0xff00ff88 >;
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts=185= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-191- 0x00000002 0x00000002 0x00000003 0x00000006
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts:192: 0x06030202 0x000d0709 0x7086120a 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-193- };
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts=250= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-256- 0x00000002 0x00000002 0x00000003 0x00000006
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts:257: 0x06030202 0x000d0709 0x7086120a 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-258- };
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2308= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2325- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2326: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2327- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2456= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2473- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2474: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2475- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2604= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2621- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2622: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2623- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts=1579= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1585- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts:1586: 0x04040001 0x000d090c 0x7026120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1587- };
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts=1634= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1640- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts:1641: 0x04040001 0x000d090c 0x7026120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1642- };
--
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi=148= partition@d0000 {
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi-149- label = "DDRPARAMS";
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi:150: reg = <0x000d0000 0x00010000>;
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi-151- read-only;
--
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts=107= partition@d0000 {
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts-108- label = "DDRPARAMS";
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts:109: reg = <0x000d0000 0x00010000>;
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts-110- read-only;
--
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi=1617= segment@100000 { /* 0x48100000 */
--
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi-1640- <0x000ce000 0x001ce000 0x002000>, /* ap 61 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1641: <0x000d0000 0x001d0000 0x002000>, /* ap 62 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1642: <0x000d2000 0x001d2000 0x002000>, /* ap 63 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1643: <0x000d8000 0x001d8000 0x001000>, /* ap 64 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1644: <0x000d9000 0x001d9000 0x001000>, /* ap 65 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi-1645- <0x000a0000 0x001a0000 0x001000>, /* ap 79 */
--
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi=1205= segment@100000 { /* 0x48100000 */
--
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi-1226- <0x000ce000 0x001ce000 0x002000>, /* ap 51 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1227: <0x000d0000 0x001d0000 0x002000>, /* ap 52 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1228: <0x000d2000 0x001d2000 0x002000>, /* ap 53 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1229: <0x000d8000 0x001d8000 0x001000>, /* ap 54 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1230: <0x000d9000 0x001d9000 0x001000>, /* ap 55 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi-1231- <0x000a0000 0x001a0000 0x001000>, /* ap 67 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi=17= segment@0 { /* 0x4a000000 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-37- <0x000a8000 0x000a8000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:38: <0x000d9000 0x000d9000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:39: <0x000da000 0x000da000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:40: <0x000dd000 0x000dd000 0x001000>, /* ap 19 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:41: <0x000de000 0x000de000 0x001000>, /* ap 20 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-42- <0x000e0000 0x000e0000 0x001000>, /* ap 21 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi=1052= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-1126- <0x000bb000 0x000bb000 0x001000>, /* ap 70 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1127: <0x000d1000 0x000d1000 0x001000>, /* ap 71 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1128: <0x000d2000 0x000d2000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1129: <0x000d5000 0x000d5000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1130: <0x000d6000 0x000d6000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-1131- <0x000a2000 0x000a2000 0x001000>, /* ap 75 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi=351= segment@80000 { /* 0x4a080000 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-354- #size-cells = <1>;
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:355: ranges = <0x00059000 0x000d9000 0x001000>, /* ap 13 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:356: <0x0005a000 0x000da000 0x001000>, /* ap 14 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:357: <0x0005b000 0x000db000 0x001000>, /* ap 15 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:358: <0x0005c000 0x000dc000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:359: <0x0005d000 0x000dd000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:360: <0x0005e000 0x000de000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-361- <0x00060000 0x000e0000 0x001000>, /* ap 19 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi=1305= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-1381- <0x000bb000 0x000bb000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1382: <0x000d1000 0x000d1000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1383: <0x000d2000 0x000d2000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1384: <0x000d5000 0x000d5000 0x001000>, /* ap 75 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1385: <0x000d6000 0x000d6000 0x001000>, /* ap 76 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-1386- <0x000a2000 0x000a2000 0x001000>, /* ap 79 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi=396= segment@80000 { /* 0x4a080000 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-399- #size-cells = <1>;
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:400: ranges = <0x00059000 0x000d9000 0x001000>, /* ap 13 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:401: <0x0005a000 0x000da000 0x001000>, /* ap 14 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:402: <0x0005b000 0x000db000 0x001000>, /* ap 15 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:403: <0x0005c000 0x000dc000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:404: <0x0005d000 0x000dd000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:405: <0x0005e000 0x000de000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-406- <0x00060000 0x000e0000 0x001000>, /* ap 19 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi=969= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-1044- <0x000bb000 0x000bb000 0x001000>, /* ap 70 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1045: <0x000d1000 0x000d1000 0x001000>, /* ap 71 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1046: <0x000d2000 0x000d2000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1047: <0x000d5000 0x000d5000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1048: <0x000d6000 0x000d6000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-1049- <0x000a2000 0x000a2000 0x001000>, /* ap 75 */
--
arch/arm/probes/kprobes/checkers-arm.c=13=static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
--
arch/arm/probes/kprobes/checkers-arm.c-38- DECODE_OR (0x0e5000bf, 0x000000bd),
arch/arm/probes/kprobes/checkers-arm.c:39: DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),
arch/arm/probes/kprobes/checkers-arm.c-40-
--
arch/arm/vfp/vfpinstr.h-45-#define FEXT_FTOUIZ (0x000c0080)
arch/arm/vfp/vfpinstr.h:46:#define FEXT_FTOSI (0x000d0000)
arch/arm/vfp/vfpinstr.h:47:#define FEXT_FTOSIZ (0x000d0080)
arch/arm/vfp/vfpinstr.h-48-
--
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi=446= gpio_hsls: gpio@d0000 {
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi-447- compatible = "brcm,iproc-gpio";
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi:448: reg = <0x000d0000 0x864>;
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi-449- ngpios = <151>;
--
arch/m68k/ifpsp060/fpsp.sa-416- .long 0xff68ff44,0x206eff44,0x58aeff44,0x61ffffff
arch/m68k/ifpsp060/fpsp.sa:417: .long 0xe7f82d40,0xff400800,0x000d6600,0x0106e9c0
arch/m68k/ifpsp060/fpsp.sa-418- .long 0x04c36622,0x0c6e401e,0xff6c661a,0xf23c9000
--
arch/mips/include/asm/cpu.h-264-#define PRID_REV_LOONGSON3A_R2_1 0x000c
arch/mips/include/asm/cpu.h:265:#define PRID_REV_LOONGSON3A_R3_1 0x000d
arch/mips/include/asm/cpu.h-266-
--
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h-14-#define CP0_DCACHE_ERR_REG $27, 1
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h:15:#define CP0_PRID_OCTEON_PASS1 0x000d0000
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h:16:#define CP0_PRID_OCTEON_CN30XX 0x000d0200
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h-17-
--
arch/mips/include/asm/octeon/octeon-model.h-76-
arch/mips/include/asm/octeon/octeon-model.h:77:#define OCTEON_CNF75XX_PASS1_0 0x000d9800
arch/mips/include/asm/octeon/octeon-model.h-78-#define OCTEON_CNF75XX (OCTEON_CNF75XX_PASS1_0 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-80-
arch/mips/include/asm/octeon/octeon-model.h:81:#define OCTEON_CN73XX_PASS1_0 0x000d9700
arch/mips/include/asm/octeon/octeon-model.h:82:#define OCTEON_CN73XX_PASS1_1 0x000d9701
arch/mips/include/asm/octeon/octeon-model.h-83-#define OCTEON_CN73XX (OCTEON_CN73XX_PASS1_0 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-86-
arch/mips/include/asm/octeon/octeon-model.h:87:#define OCTEON_CN70XX_PASS1_0 0x000d9600
arch/mips/include/asm/octeon/octeon-model.h:88:#define OCTEON_CN70XX_PASS1_1 0x000d9601
arch/mips/include/asm/octeon/octeon-model.h:89:#define OCTEON_CN70XX_PASS1_2 0x000d9602
arch/mips/include/asm/octeon/octeon-model.h-90-
arch/mips/include/asm/octeon/octeon-model.h:91:#define OCTEON_CN70XX_PASS2_0 0x000d9608
arch/mips/include/asm/octeon/octeon-model.h-92-
--
arch/mips/include/asm/octeon/octeon-model.h-100-
arch/mips/include/asm/octeon/octeon-model.h:101:#define OCTEON_CN78XX_PASS1_0 0x000d9500
arch/mips/include/asm/octeon/octeon-model.h:102:#define OCTEON_CN78XX_PASS1_1 0x000d9501
arch/mips/include/asm/octeon/octeon-model.h:103:#define OCTEON_CN78XX_PASS2_0 0x000d9508
arch/mips/include/asm/octeon/octeon-model.h-104-
--
arch/mips/include/asm/octeon/octeon-model.h-110-
arch/mips/include/asm/octeon/octeon-model.h:111:#define OCTEON_CN76XX (0x000d9540 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-112-
--
arch/mips/include/asm/octeon/octeon-model.h-115- */
arch/mips/include/asm/octeon/octeon-model.h:116:#define OCTEON_CNF71XX_PASS1_0 0x000d9400
arch/mips/include/asm/octeon/octeon-model.h:117:#define OCTEON_CNF71XX_PASS1_1 0x000d9401
arch/mips/include/asm/octeon/octeon-model.h-118-
--
arch/mips/include/asm/octeon/octeon-model.h-124- */
arch/mips/include/asm/octeon/octeon-model.h:125:#define OCTEON_CN68XX_PASS1_0 0x000d9100
arch/mips/include/asm/octeon/octeon-model.h:126:#define OCTEON_CN68XX_PASS1_1 0x000d9101
arch/mips/include/asm/octeon/octeon-model.h:127:#define OCTEON_CN68XX_PASS1_2 0x000d9102
arch/mips/include/asm/octeon/octeon-model.h:128:#define OCTEON_CN68XX_PASS2_0 0x000d9108
arch/mips/include/asm/octeon/octeon-model.h:129:#define OCTEON_CN68XX_PASS2_1 0x000d9109
arch/mips/include/asm/octeon/octeon-model.h:130:#define OCTEON_CN68XX_PASS2_2 0x000d910a
arch/mips/include/asm/octeon/octeon-model.h-131-
--
arch/mips/include/asm/octeon/octeon-model.h-138-
arch/mips/include/asm/octeon/octeon-model.h:139:#define OCTEON_CN66XX_PASS1_0 0x000d9200
arch/mips/include/asm/octeon/octeon-model.h:140:#define OCTEON_CN66XX_PASS1_2 0x000d9202
arch/mips/include/asm/octeon/octeon-model.h-141-
--
arch/mips/include/asm/octeon/octeon-model.h-144-
arch/mips/include/asm/octeon/octeon-model.h:145:#define OCTEON_CN63XX_PASS1_0 0x000d9000
arch/mips/include/asm/octeon/octeon-model.h:146:#define OCTEON_CN63XX_PASS1_1 0x000d9001
arch/mips/include/asm/octeon/octeon-model.h:147:#define OCTEON_CN63XX_PASS1_2 0x000d9002
arch/mips/include/asm/octeon/octeon-model.h:148:#define OCTEON_CN63XX_PASS2_0 0x000d9008
arch/mips/include/asm/octeon/octeon-model.h:149:#define OCTEON_CN63XX_PASS2_1 0x000d9009
arch/mips/include/asm/octeon/octeon-model.h:150:#define OCTEON_CN63XX_PASS2_2 0x000d900a
arch/mips/include/asm/octeon/octeon-model.h-151-
--
arch/mips/include/asm/octeon/octeon-model.h-158-
arch/mips/include/asm/octeon/octeon-model.h:159:#define OCTEON_CN61XX_PASS1_0 0x000d9300
arch/mips/include/asm/octeon/octeon-model.h:160:#define OCTEON_CN61XX_PASS1_1 0x000d9301
arch/mips/include/asm/octeon/octeon-model.h-161-
--
arch/mips/include/asm/octeon/octeon-model.h-170- */
arch/mips/include/asm/octeon/octeon-model.h:171:#define OCTEON_CN58XX_PASS1_0 0x000d0300
arch/mips/include/asm/octeon/octeon-model.h:172:#define OCTEON_CN58XX_PASS1_1 0x000d0301
arch/mips/include/asm/octeon/octeon-model.h:173:#define OCTEON_CN58XX_PASS1_2 0x000d0303
arch/mips/include/asm/octeon/octeon-model.h:174:#define OCTEON_CN58XX_PASS2_0 0x000d0308
arch/mips/include/asm/octeon/octeon-model.h:175:#define OCTEON_CN58XX_PASS2_1 0x000d0309
arch/mips/include/asm/octeon/octeon-model.h:176:#define OCTEON_CN58XX_PASS2_2 0x000d030a
arch/mips/include/asm/octeon/octeon-model.h:177:#define OCTEON_CN58XX_PASS2_3 0x000d030b
arch/mips/include/asm/octeon/octeon-model.h-178-
--
arch/mips/include/asm/octeon/octeon-model.h-184-
arch/mips/include/asm/octeon/octeon-model.h:185:#define OCTEON_CN56XX_PASS1_0 0x000d0400
arch/mips/include/asm/octeon/octeon-model.h:186:#define OCTEON_CN56XX_PASS1_1 0x000d0401
arch/mips/include/asm/octeon/octeon-model.h:187:#define OCTEON_CN56XX_PASS2_0 0x000d0408
arch/mips/include/asm/octeon/octeon-model.h:188:#define OCTEON_CN56XX_PASS2_1 0x000d0409
arch/mips/include/asm/octeon/octeon-model.h-189-
--
arch/mips/include/asm/octeon/octeon-model.h-207-
arch/mips/include/asm/octeon/octeon-model.h:208:#define OCTEON_CN50XX_PASS1_0 0x000d0600
arch/mips/include/asm/octeon/octeon-model.h-209-
--
arch/mips/include/asm/octeon/octeon-model.h-218-
arch/mips/include/asm/octeon/octeon-model.h:219:#define OCTEON_CN52XX_PASS1_0 0x000d0700
arch/mips/include/asm/octeon/octeon-model.h:220:#define OCTEON_CN52XX_PASS2_0 0x000d0708
arch/mips/include/asm/octeon/octeon-model.h-221-
--
arch/mips/include/asm/octeon/octeon-model.h-230- */
arch/mips/include/asm/octeon/octeon-model.h:231:#define OCTEON_CN38XX_PASS1 0x000d0000
arch/mips/include/asm/octeon/octeon-model.h:232:#define OCTEON_CN38XX_PASS2 0x000d0001
arch/mips/include/asm/octeon/octeon-model.h:233:#define OCTEON_CN38XX_PASS3 0x000d0003
arch/mips/include/asm/octeon/octeon-model.h-234-#define OCTEON_CN38XX (OCTEON_CN38XX_PASS3 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-240-/* The OCTEON_CN31XX matches CN31XX models and the CN3020 */
arch/mips/include/asm/octeon/octeon-model.h:241:#define OCTEON_CN31XX_PASS1 0x000d0100
arch/mips/include/asm/octeon/octeon-model.h:242:#define OCTEON_CN31XX_PASS1_1 0x000d0102
arch/mips/include/asm/octeon/octeon-model.h-243-#define OCTEON_CN31XX (OCTEON_CN31XX_PASS1 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-249- */
arch/mips/include/asm/octeon/octeon-model.h:250:#define OCTEON_CN30XX_PASS1 0x000d0200
arch/mips/include/asm/octeon/octeon-model.h:251:#define OCTEON_CN30XX_PASS1_1 0x000d0202
arch/mips/include/asm/octeon/octeon-model.h-252-#define OCTEON_CN30XX (OCTEON_CN30XX_PASS1 | OM_IGNORE_REVISION)
arch/mips/include/asm/octeon/octeon-model.h-253-
arch/mips/include/asm/octeon/octeon-model.h:254:#define OCTEON_CN3005_PASS1 (0x000d0210 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:255:#define OCTEON_CN3005_PASS1_0 (0x000d0210 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:256:#define OCTEON_CN3005_PASS1_1 (0x000d0212 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-257-#define OCTEON_CN3005 (OCTEON_CN3005_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-258-
arch/mips/include/asm/octeon/octeon-model.h:259:#define OCTEON_CN3010_PASS1 (0x000d0200 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:260:#define OCTEON_CN3010_PASS1_0 (0x000d0200 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:261:#define OCTEON_CN3010_PASS1_1 (0x000d0202 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-262-#define OCTEON_CN3010 (OCTEON_CN3010_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-263-
arch/mips/include/asm/octeon/octeon-model.h:264:#define OCTEON_CN3020_PASS1 (0x000d0110 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:265:#define OCTEON_CN3020_PASS1_0 (0x000d0110 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:266:#define OCTEON_CN3020_PASS1_1 (0x000d0112 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-267-#define OCTEON_CN3020 (OCTEON_CN3020_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
--
arch/mips/include/asm/uprobes.h=19=typedef u32 uprobe_opcode_t;
--
arch/mips/include/asm/uprobes.h-28-
arch/mips/include/asm/uprobes.h:29:#define UPROBE_BRK_UPROBE 0x000d000d /* break 13 */
arch/mips/include/asm/uprobes.h-30-#define UPROBE_BRK_UPROBE_XOL 0x000e000d /* break 14 */
--
arch/mips/kernel/octeon_switch.S-149- sd t0, OCTEON_CP2_AES_KEYLEN(a0)
arch/mips/kernel/octeon_switch.S:150: li v1, 0x000d0000 /* This is the processor ID of Octeon Pass1 */
arch/mips/kernel/octeon_switch.S-151- sd t1, OCTEON_CP2_AES_RESULT(a0)
--
arch/mips/kernel/octeon_switch.S-315- dmtc2 t0, 0x0110
arch/mips/kernel/octeon_switch.S:316: li v0, 0x000d0000 /* This is the processor ID of Octeon Pass1 */
arch/mips/kernel/octeon_switch.S-317- dmtc2 t1, 0x0100
--
arch/parisc/kernel/perf_images.h=2083=static uint32_t cuda_images[][PCXW_IMAGE_SIZE/sizeof(uint32_t)] __ro_after_init = {
--
arch/parisc/kernel/perf_images.h-2296- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2297: 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff,
arch/parisc/kernel/perf_images.h-2298- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2342- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2343: 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff,
arch/parisc/kernel/perf_images.h-2344- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2481- 0x00005555, 0xff80bf8b, 0xab030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2482: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-2483- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2909- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2910: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-2911- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3003- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3004: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3005- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3049- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3050: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3051- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3096- 0x0000ff00, 0xff83bf03, 0xdf030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3097: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3098- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/powerpc/include/asm/cpm1.h-42-#define CPM_CR_CH_SCC4 ((ushort)0x000c)
arch/powerpc/include/asm/cpm1.h:43:#define CPM_CR_CH_SMC2 ((ushort)0x000d) /* SMC2 / DSP2 */
arch/powerpc/include/asm/cpm1.h-44-
--
arch/powerpc/include/asm/mpic.h-51-#define MPIC_GREG_IPI_VECTOR_PRI_2 0x000c0
arch/powerpc/include/asm/mpic.h:52:#define MPIC_GREG_IPI_VECTOR_PRI_3 0x000d0
arch/powerpc/include/asm/mpic.h-53-#define MPIC_GREG_IPI_STRIDE 0x10
--
arch/powerpc/include/asm/ps3av.h-138-#define PS3AV_CMD_VIDEO_VID_1080P_50HZ 0x000c
arch/powerpc/include/asm/ps3av.h:139:#define PS3AV_CMD_VIDEO_VID_WXGA 0x000d
arch/powerpc/include/asm/ps3av.h-140-#define PS3AV_CMD_VIDEO_VID_SXGA 0x000e
--
arch/powerpc/include/asm/ps3av.h=670=extern u8 ps3av_mode_cs_info[];
--
arch/powerpc/include/asm/ps3av.h-685-#define PS3AV_STATUS_UNSUPPORTED_COMMAND 0x000c /* unsupported cid */
arch/powerpc/include/asm/ps3av.h:686:#define PS3AV_STATUS_BUFFER_OVERFLOW 0x000d /* write buffer overflow */
arch/powerpc/include/asm/ps3av.h-687-#define PS3AV_STATUS_INVALID_VIDEO_PARAM 0x000e /* invalid video param */
--
arch/s390/net/bpf_jit_comp.c=859=static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
--
arch/s390/net/bpf_jit_comp.c-1186- /* dsg %w0,<d(imm)>(%l) */
arch/s390/net/bpf_jit_comp.c:1187: EMIT6_DISP_LH(0xe3000000, 0x000d, REG_W0, REG_0,
arch/s390/net/bpf_jit_comp.c-1188- REG_L, EMIT_CONST_U64(imm));
--
arch/s390/net/bpf_jit_comp.c-1328- /* sllg %dst,%dst,0(%src) */
arch/s390/net/bpf_jit_comp.c:1329: EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, src_reg, 0);
arch/s390/net/bpf_jit_comp.c-1330- break;
--
arch/s390/net/bpf_jit_comp.c-1341- /* sllg %dst,%dst,imm(%r0) */
arch/s390/net/bpf_jit_comp.c:1342: EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, REG_0, imm);
arch/s390/net/bpf_jit_comp.c-1343- break;
--
arch/s390/net/bpf_jit_comp.c-1855- /* sllg %r1,%r1,3: %r1 *= 8 */
arch/s390/net/bpf_jit_comp.c:1856: EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, REG_1, REG_0, 3);
arch/s390/net/bpf_jit_comp.c-1857- /* ltg %r1,prog(%b2,%r1) */
--
arch/sparc/include/uapi/asm/socket.h-40-
arch/sparc/include/uapi/asm/socket.h:41:#define SO_BINDTODEVICE 0x000d
arch/sparc/include/uapi/asm/socket.h-42-
--
arch/x86/crypto/camellia_glue.c=224=__visible const u64 camellia_sp03303033[256] = {
--
arch/x86/crypto/camellia_glue.c-239- 0x006e6e006e006e6eULL, 0x00afaf00af00afafULL, 0x002f2f002f002f2fULL,
arch/x86/crypto/camellia_glue.c:240: 0x00e2e200e200e2e2ULL, 0x0085850085008585ULL, 0x000d0d000d000d0dULL,
arch/x86/crypto/camellia_glue.c-241- 0x0053530053005353ULL, 0x00f0f000f000f0f0ULL, 0x009c9c009c009c9cULL,
--
arch/x86/crypto/camellia_glue.c=402=__visible const u64 camellia_sp02220222[256] = {
--
arch/x86/crypto/camellia_glue.c-412- 0x009c9c9c009c9c9cULL, 0x003a3a3a003a3a3aULL, 0x00cacaca00cacacaULL,
arch/x86/crypto/camellia_glue.c:413: 0x0025252500252525ULL, 0x007b7b7b007b7b7bULL, 0x000d0d0d000d0d0dULL,
arch/x86/crypto/camellia_glue.c-414- 0x0071717100717171ULL, 0x005f5f5f005f5f5fULL, 0x001f1f1f001f1f1fULL,
--
arch/x86/events/intel/knc.c=22=static const u64 __initconst knc_hw_cache_event_ids
--
arch/x86/events/intel/knc.c-92- [ C(RESULT_ACCESS) ] = 0x000c, /* CODE_READ */
arch/x86/events/intel/knc.c:93: [ C(RESULT_MISS) ] = 0x000d, /* CODE_PAGE_WALK */
arch/x86/events/intel/knc.c-94- },
--
arch/x86/kernel/cpu/amd.c=92=static void init_amd_k5(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/amd.c-96- * General Systems BIOSen alias the cpu frequency registers
arch/x86/kernel/cpu/amd.c:97: * of the Elan at 0x000df000. Unfortunately, one of the Linux
arch/x86/kernel/cpu/amd.c-98- * drivers subsequently pokes it, and changes the CPU speed.
--
]
]
|
| 20/2 |
2026/05/25 23:44 |
llm |
expert |
0m
Model:
Tokens:
input: 145247
output: 19
thoughts: 1216
Thoughts: **Analyzing the HIDP Bluetooth Device Addition and Potential Deadlock**
Okay, so `hidp_session_dev_add` seems to be the entry point for registering a HID device. The flow is straightforward initially, calling `hidp_setup_input` if `req->rd_size` is zero or `hidp_setup_hid` otherwise. The reproducer sets `rd_size` to `0xfea4` (65188), and `rd_data` points to a small 3-byte buffer. This immediately raises a red flag. `memdup_user` is going to attempt to copy 65188 bytes from user space into `rd_data`? Given the size difference, that's almost certainly going to cause trouble. But `rddata` points to a mapped area, so it's a valid address, and the copy is performed. Then `hid_add_device` is called.
Inside `hid_add_device`, the HID report descriptor gets parsed via `hid_parse`. If the descriptor is invalid, `hid_parse` fails, `hid_add_device` fails, and the whole `hidp_session_dev_add` fails. Critically, `hidp_parse`, which is the `ll_driver->parse` callback, calls `hid_parse_report` which operates on this user-provided data. If that fails, the process is terminated. The subsequent call to `hidp_session_terminate` then triggers a wakeup on `hidp_session_wq`. Great. The critical thing here is `hidp_session_dev_work` is finishing, `cancel_work_sync(&session->dev_init)` in `hidp_session_remove` returns and the `khidpd` thread should not be blocked for 143 seconds.
But *wait*, `khidpd` *is* blocked for 143 seconds. That implies `hidp_session_dev_work` isn't finishing. Why? Because it's stuck in `hid_add_device`. Can `hid_add_device` block? It calls `device_add`, which calls `kobject_uevent`, and that, crucially, can call `call_usermodehelper`. `call_usermodehelper` *can* block. That's a potential problem.
Now, `syz-executor/5806` holds `rtnl_mutex` and is blocked in `rtnl_newlink`? And `kworker/u4:4` is blocked in `synchronize_rcu_expedited`, also holding `rtnl_mutex`? `kworker/u4:4` also holds `rcu_state.exp_mutex`. That's a classic deadlock situation! `synchronize_rcu_expedited` is blocked because some CPU is in an RCU read-side critical section.
So, who's in that critical section? It's `syz.0.54`, blocked in `hci_dev_lock(hdev)` inside `l2cap_chan_connect`. Does `l2cap_chan_connect` hold an RCU read lock? Let's re-examine `l2cap_chan_connect`, specifically the call to `hci_get_route`. Bingo! We've found the potential point of the deadlock: if `hci_get_route` gets held up for some reason, and then `l2cap_chan_connect` is called... This is the critical juncture.
|
| 21/2 |
2026/05/25 23:44 |
tool |
grepper |
0m
Args: map[Expression:hci_get_route]
Results: map[Output:include/net/bluetooth/hci_core.h=1706=struct hci_dev *hci_dev_get(int index);
include/net/bluetooth/hci_core.h:1707:struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
include/net/bluetooth/hci_core.h-1708-
--
net/bluetooth/6lowpan.c=958=static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
--
net/bluetooth/6lowpan.c-973- /* The LE_PUBLIC address type is ignored because of BDADDR_ANY */
net/bluetooth/6lowpan.c:974: hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC);
net/bluetooth/6lowpan.c-975- if (!hdev)
--
net/bluetooth/hci_conn.c=1130=void hci_conn_del(struct hci_conn *conn)
--
net/bluetooth/hci_conn.c-1178-
net/bluetooth/hci_conn.c:1179:struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
net/bluetooth/hci_conn.c-1180-{
--
net/bluetooth/hci_conn.c-1236-}
net/bluetooth/hci_conn.c:1237:EXPORT_SYMBOL(hci_get_route);
net/bluetooth/hci_conn.c-1238-
--
net/bluetooth/iso.c=332=static int iso_connect_bis(struct sock *sk)
--
net/bluetooth/iso.c-340-
net/bluetooth/iso.c:341: hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
net/bluetooth/iso.c-342- iso_pi(sk)->src_type);
--
net/bluetooth/iso.c=427=static int iso_connect_cis(struct sock *sk)
--
net/bluetooth/iso.c-435-
net/bluetooth/iso.c:436: hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
net/bluetooth/iso.c-437- iso_pi(sk)->src_type);
--
net/bluetooth/iso.c=1112=static int iso_listen_bis(struct sock *sk)
--
net/bluetooth/iso.c-1132-
net/bluetooth/iso.c:1133: hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
net/bluetooth/iso.c-1134- iso_pi(sk)->src_type);
--
net/bluetooth/iso.c=1461=static void iso_conn_big_sync(struct sock *sk)
--
net/bluetooth/iso.c-1465-
net/bluetooth/iso.c:1466: hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src,
net/bluetooth/iso.c-1467- iso_pi(sk)->src_type);
--
net/bluetooth/l2cap_core.c=6949=int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
--
net/bluetooth/l2cap_core.c-6959-
net/bluetooth/l2cap_core.c:6960: hdev = hci_get_route(dst, &chan->src, chan->src_type);
net/bluetooth/l2cap_core.c-6961- if (!hdev)
--
net/bluetooth/rfcomm/tty.c=175=static void rfcomm_reparent_device(struct rfcomm_dev *dev)
--
net/bluetooth/rfcomm/tty.c-179-
net/bluetooth/rfcomm/tty.c:180: hdev = hci_get_route(&dev->dst, &dev->src, BDADDR_BREDR);
net/bluetooth/rfcomm/tty.c-181- if (!hdev)
--
net/bluetooth/sco.c=310=static int sco_connect(struct sock *sk)
--
net/bluetooth/sco.c-318-
net/bluetooth/sco.c:319: hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
net/bluetooth/sco.c-320- if (!hdev)
--
net/bluetooth/sco.c=902=static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
--
net/bluetooth/sco.c-949- sco_pi(sk)->setting = voice.setting;
net/bluetooth/sco.c:950: hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
net/bluetooth/sco.c-951- BDADDR_BREDR);
--
net/bluetooth/sco.c-984-
net/bluetooth/sco.c:985: hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
net/bluetooth/sco.c-986- BDADDR_BREDR);
--
net/bluetooth/sco.c=1101=static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
--
net/bluetooth/sco.c-1180-
net/bluetooth/sco.c:1181: hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
net/bluetooth/sco.c-1182- if (!hdev) {
]
|
| 22/2 |
2026/05/25 23:44 |
llm |
expert |
0m
Model:
Tokens:
input: 148802
output: 24
thoughts: 23
Thoughts: Here's my thought process, summarized as you requested:
**Navigating Bluetooth Connection Routing**
Okay, so I need to understand how Bluetooth connections are being routed. My instinct is to dive into the core connection handling, specifically the part dealing with HCI (Host Controller Interface). Let's start with `hci_get_route`. Where would that be defined? My initial guess is within the `net/bluetooth` directory, and based on my knowledge of the stack, probably within a file related to connection management. `hci_conn.c` sounds like a promising candidate. So, let's start there and see what `hci_get_route` is doing within that file. I'm expecting to find the logic responsible for determining the path a connection takes within the Bluetooth stack. This should give me a good entry point to analyze the routing process.
|
| 23/2 |
2026/05/25 23:44 |
tool |
read-file |
0m
Args: map[File:net/bluetooth/hci_conn.c]
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.
*/
/* Bluetooth HCI connection handling. */
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/errqueue.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/iso.h>
#include <net/bluetooth/mgmt.h>
#include "smp.h"
#include "eir.h"
struct sco_param {
u16 pkt_type;
u16 max_latency;
u8 retrans_effort;
};
struct conn_handle_t {
struct hci_conn *conn;
__u16 handle;
};
static const struct sco_param esco_param_cvsd[] = {
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a, 0x01 }, /* S3 */
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */
{ EDR_ESCO_MASK | ESCO_EV3, 0x0007, 0x01 }, /* S1 */
{ EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0x01 }, /* D1 */
{ EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0x01 }, /* D0 */
};
static const struct sco_param sco_param_cvsd[] = {
{ EDR_ESCO_MASK | ESCO_HV3, 0xffff, 0xff }, /* D1 */
{ EDR_ESCO_MASK | ESCO_HV1, 0xffff, 0xff }, /* D0 */
};
static const struct sco_param esco_param_msbc[] = {
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d, 0x02 }, /* T2 */
{ EDR_ESCO_MASK | ESCO_EV3, 0x0008, 0x02 }, /* T1 */
};
/* This function requires the caller holds hdev->lock */
void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
{
struct hci_conn_params *params;
struct hci_dev *hdev = conn->hdev;
struct smp_irk *irk;
bdaddr_t *bdaddr;
u8 bdaddr_type;
bdaddr = &conn->dst;
bdaddr_type = conn->dst_type;
/* Check if we need to convert to identity address */
irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
if (irk) {
bdaddr = &irk->bdaddr;
bdaddr_type = irk->addr_type;
}
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
bdaddr_type);
if (!params)
return;
if (params->conn) {
hci_conn_drop(params->conn);
hci_conn_put(params->conn);
params->conn = NULL;
}
if (!params->explicit_connect)
return;
/* If the status indicates successful cancellation of
* the attempt (i.e. Unknown Connection Id) there's no point of
* notifying failure since we'll go back to keep trying to
* connect. The only exception is explicit connect requests
* where a timeout + cancel does indicate an actual failure.
*/
if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
mgmt_connect_failed(hdev, conn, status);
/* The connection attempt was doing scan for new RPA, and is
* in scan phase. If params are not associated with any other
* autoconnect action, remove them completely. If they are, just unmark
* them as waiting for connection, by clearing explicit_connect field.
*/
params->explicit_connect = false;
hci_pend_le_list_del_init(params);
switch (params->auto_connect) {
case HCI_AUTO_CONN_EXPLICIT:
hci_conn_params_del(hdev, bdaddr, bdaddr_type);
/* return instead of break to avoid duplicate scan update */
return;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
hci_pend_le_list_add(params, &hdev->pend_le_conns);
break;
case HCI_AUTO_CONN_REPORT:
hci_pend_le_list_add(params, &hdev->pend_le_reports);
break;
default:
break;
}
hci_update_passive_scan(hdev);
}
static void hci_conn_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
hci_remove_link_key(hdev, &conn->dst);
hci_chan_list_flush(conn);
hci_conn_hash_del(hdev, conn);
if (HCI_CONN_HANDLE_UNSET(conn->handle))
ida_free(&hdev->unset_handle_ida, conn->handle);
if (conn->cleanup)
conn->cleanup(conn);
if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
switch (conn->setting & SCO_AIRMODE_MASK) {
case SCO_AIRMODE_CVSD:
case SCO_AIRMODE_TRANSP:
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
break;
}
} else {
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
}
debugfs_remove_recursive(conn->debugfs);
hci_conn_del_sysfs(conn);
hci_dev_put(hdev);
}
int hci_disconnect(struct hci_conn *conn, __u8 reason)
{
BT_DBG("hcon %p", conn);
/* When we are central of an established connection and it enters
* the disconnect timeout, then go ahead and try to read the
* current clock offset. Processing of the result is done
* within the event handling and hci_clock_offset_evt function.
*/
if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER &&
(conn->state == BT_CONNECTED || conn->state == BT_CONFIG)) {
struct hci_dev *hdev = conn->hdev;
struct hci_cp_read_clock_offset clkoff_cp;
clkoff_cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
&clkoff_cp);
}
return hci_abort_conn(conn, reason);
}
static void hci_add_sco(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_add_sco cp;
BT_DBG("hcon %p", conn);
conn->state = BT_CONNECT;
conn->out = true;
conn->attempt++;
cp.handle = cpu_to_le16(handle);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
}
static bool find_next_esco_param(struct hci_conn *conn,
const struct sco_param *esco_param, int size)
{
if (!conn->parent)
return false;
for (; conn->attempt <= size; conn->attempt++) {
if (lmp_esco_2m_capable(conn->parent) ||
(esco_param[conn->attempt - 1].pkt_type & ESCO_2EV3))
break;
BT_DBG("hcon %p skipped attempt %d, eSCO 2M not supported",
conn, conn->attempt);
}
return conn->attempt <= size;
}
static int configure_datapath_sync(struct hci_dev *hdev, struct bt_codec *codec)
{
int err;
__u8 vnd_len, *vnd_data = NULL;
struct hci_op_configure_data_path *cmd = NULL;
/* Do not take below 2 checks as error since the 1st means user do not
* want to use HFP offload mode and the 2nd means the vendor controller
* do not need to send below HCI command for offload mode.
*/
if (!codec->data_path || !hdev->get_codec_config_data)
return 0;
err = hdev->get_codec_config_data(hdev, ESCO_LINK, codec, &vnd_len,
&vnd_data);
if (err < 0)
goto error;
cmd = kzalloc(sizeof(*cmd) + vnd_len, GFP_KERNEL);
if (!cmd) {
err = -ENOMEM;
goto error;
}
err = hdev->get_data_path_id(hdev, &cmd->data_path_id);
if (err < 0)
goto error;
cmd->vnd_len = vnd_len;
memcpy(cmd->vnd_data, vnd_data, vnd_len);
cmd->direction = 0x00;
__hci_cmd_sync_status(hdev, HCI_CONFIGURE_DATA_PATH,
sizeof(*cmd) + vnd_len, cmd, HCI_CMD_TIMEOUT);
cmd->direction = 0x01;
err = __hci_cmd_sync_status(hdev, HCI_CONFIGURE_DATA_PATH,
sizeof(*cmd) + vnd_len, cmd,
HCI_CMD_TIMEOUT);
error:
kfree(cmd);
kfree(vnd_data);
return err;
}
static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
{
struct conn_handle_t *conn_handle = data;
struct hci_conn *conn = conn_handle->conn;
__u16 handle = conn_handle->handle;
struct hci_cp_enhanced_setup_sync_conn cp;
const struct sco_param *param;
kfree(conn_handle);
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
bt_dev_dbg(hdev, "hcon %p", conn);
configure_datapath_sync(hdev, &conn->codec);
conn->state = BT_CONNECT;
conn->out = true;
conn->attempt++;
memset(&cp, 0x00, sizeof(cp));
cp.handle = cpu_to_le16(handle);
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return -EINVAL;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x05;
cp.rx_coding_format.id = 0x05;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(32000);
cp.out_bandwidth = __cpu_to_le32(32000);
cp.in_coding_format.id = 0x04;
cp.out_coding_format.id = 0x04;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 1;
cp.out_transport_unit_size = 1;
break;
case BT_CODEC_TRANSPARENT:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return false;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x03;
cp.rx_coding_format.id = 0x03;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(0x1f40);
cp.out_bandwidth = __cpu_to_le32(0x1f40);
cp.in_coding_format.id = 0x03;
cp.out_coding_format.id = 0x03;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 1;
cp.out_transport_unit_size = 1;
break;
case BT_CODEC_CVSD:
if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd)))
return -EINVAL;
param = &esco_param_cvsd[conn->attempt - 1];
} else {
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
return -EINVAL;
param = &sco_param_cvsd[conn->attempt - 1];
}
cp.tx_coding_format.id = 2;
cp.rx_coding_format.id = 2;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(16000);
cp.out_bandwidth = __cpu_to_le32(16000);
cp.in_coding_format.id = 4;
cp.out_coding_format.id = 4;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 16;
cp.out_transport_unit_size = 16;
break;
default:
return -EINVAL;
}
cp.retrans_effort = param->retrans_effort;
cp.pkt_type = __cpu_to_le16(param->pkt_type);
cp.max_latency = __cpu_to_le16(param->max_latency);
if (hci_send_cmd(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
return -EIO;
return 0;
}
static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_setup_sync_conn cp;
const struct sco_param *param;
bt_dev_dbg(hdev, "hcon %p", conn);
conn->state = BT_CONNECT;
conn->out = true;
conn->attempt++;
cp.handle = cpu_to_le16(handle);
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
cp.voice_setting = cpu_to_le16(conn->setting);
switch (conn->setting & SCO_AIRMODE_MASK) {
case SCO_AIRMODE_TRANSP:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return false;
param = &esco_param_msbc[conn->attempt - 1];
break;
case SCO_AIRMODE_CVSD:
if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd)))
return false;
param = &esco_param_cvsd[conn->attempt - 1];
} else {
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
return false;
param = &sco_param_cvsd[conn->attempt - 1];
}
break;
default:
return false;
}
cp.retrans_effort = param->retrans_effort;
cp.pkt_type = __cpu_to_le16(param->pkt_type);
cp.max_latency = __cpu_to_le16(param->max_latency);
if (hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
return false;
return true;
}
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
int result;
struct conn_handle_t *conn_handle;
if (enhanced_sync_conn_capable(conn->hdev)) {
conn_handle = kzalloc(sizeof(*conn_handle), GFP_KERNEL);
if (!conn_handle)
return false;
conn_handle->conn = conn;
conn_handle->handle = handle;
result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync,
conn_handle, NULL);
if (result < 0)
kfree(conn_handle);
return result == 0;
}
return hci_setup_sync_conn(conn, handle);
}
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
struct hci_dev *hdev = conn->hdev;
struct hci_conn_params *params;
struct hci_cp_le_conn_update cp;
hci_dev_lock(hdev);
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
params->conn_min_interval = min;
params->conn_max_interval = max;
params->conn_latency = latency;
params->supervision_timeout = to_multiplier;
}
hci_dev_unlock(hdev);
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
cp.conn_interval_min = cpu_to_le16(min);
cp.conn_interval_max = cpu_to_le16(max);
cp.conn_latency = cpu_to_le16(latency);
cp.supervision_timeout = cpu_to_le16(to_multiplier);
cp.min_ce_len = cpu_to_le16(0x0000);
cp.max_ce_len = cpu_to_le16(0x0000);
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
if (params)
return 0x01;
return 0x00;
}
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
__u8 ltk[16], __u8 key_size)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_start_enc cp;
BT_DBG("hcon %p", conn);
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
cp.rand = rand;
cp.ediv = ediv;
memcpy(cp.ltk, ltk, key_size);
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
}
/* Device _must_ be locked */
void hci_sco_setup(struct hci_conn *conn, __u8 status)
{
struct hci_link *link;
link = list_first_entry_or_null(&conn->link_list, struct hci_link, list);
if (!link || !link->conn)
return;
BT_DBG("hcon %p", conn);
if (!status) {
if (lmp_esco_capable(conn->hdev))
hci_setup_sync(link->conn, conn->handle);
else
hci_add_sco(link->conn, conn->handle);
} else {
hci_connect_cfm(link->conn, status);
hci_conn_del(link->conn);
}
}
static void hci_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
disc_work.work);
int refcnt = atomic_read(&conn->refcnt);
BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
WARN_ON(refcnt < 0);
/* FIXME: It was observed that in pairing failed scenario, refcnt
* drops below 0. Probably this is because l2cap_conn_del calls
* l2cap_chan_del for each channel, and inside l2cap_chan_del conn is
* dropped. After that loop hci_chan_del is called which also drops
* conn. For now make sure that ACL is alive if refcnt is higher then 0,
* otherwise drop it.
*/
if (refcnt > 0)
return;
hci_abort_conn(conn, hci_proto_disconn_ind(conn));
}
/* Enter sniff mode */
static void hci_conn_idle(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
idle_work.work);
struct hci_dev *hdev = conn->hdev;
BT_DBG("hcon %p mode %d", conn, conn->mode);
if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
return;
if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
return;
if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
struct hci_cp_sniff_subrate cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_latency = cpu_to_le16(0);
cp.min_remote_timeout = cpu_to_le16(0);
cp.min_local_timeout = cpu_to_le16(0);
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
cp.attempt = cpu_to_le16(4);
cp.timeout = cpu_to_le16(1);
hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
}
}
static void hci_conn_auto_accept(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
auto_accept_work.work);
hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
&conn->dst);
}
static void le_disable_advertising(struct hci_dev *hdev)
{
if (ext_adv_capable(hdev)) {
struct hci_cp_le_set_ext_adv_enable cp;
cp.enable = 0x00;
cp.num_of_sets = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_EXT_ADV_ENABLE, sizeof(cp),
&cp);
} else {
u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
}
}
static void le_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
le_conn_timeout.work);
struct hci_dev *hdev = conn->hdev;
BT_DBG("");
/* We could end up here due to having done directed advertising,
* so clean up the state if necessary. This should however only
* happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own).
*/
if (conn->role == HCI_ROLE_SLAVE) {
/* Disable LE Advertising */
le_disable_advertising(hdev);
hci_dev_lock(hdev);
hci_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT);
hci_dev_unlock(hdev);
return;
}
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
}
struct iso_list_data {
union {
u8 cig;
u8 big;
};
union {
u8 cis;
u8 bis;
u16 sync_handle;
};
int count;
bool big_term;
bool pa_sync_term;
bool big_sync_term;
};
static void bis_list(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
/* Skip if not broadcast/ANY address */
if (bacmp(&conn->dst, BDADDR_ANY))
return;
if (d->big != conn->iso_qos.bcast.big || d->bis == BT_ISO_QOS_BIS_UNSET ||
d->bis != conn->iso_qos.bcast.bis)
return;
d->count++;
}
static int terminate_big_sync(struct hci_dev *hdev, void *data)
{
struct iso_list_data *d = data;
bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", d->big, d->bis);
hci_disable_per_advertising_sync(hdev, d->bis);
hci_remove_ext_adv_instance_sync(hdev, d->bis, NULL);
/* Only terminate BIG if it has been created */
if (!d->big_term)
return 0;
return hci_le_terminate_big_sync(hdev, d->big,
HCI_ERROR_LOCAL_HOST_TERM);
}
static void terminate_big_destroy(struct hci_dev *hdev, void *data, int err)
{
kfree(data);
}
static int hci_le_terminate_big(struct hci_dev *hdev, struct hci_conn *conn)
{
struct iso_list_data *d;
int ret;
bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", conn->iso_qos.bcast.big,
conn->iso_qos.bcast.bis);
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
d->big = conn->iso_qos.bcast.big;
d->bis = conn->iso_qos.bcast.bis;
d->big_term = test_and_clear_bit(HCI_CONN_BIG_CREATED, &conn->flags);
ret = hci_cmd_sync_queue(hdev, terminate_big_sync, d,
terminate_big_destroy);
if (ret)
kfree(d);
return ret;
}
static int big_terminate_sync(struct hci_dev *hdev, void *data)
{
struct iso_list_data *d = data;
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", d->big,
d->sync_handle);
if (d->big_sync_term)
hci_le_big_terminate_sync(hdev, d->big);
if (d->pa_sync_term)
return hci_le_pa_terminate_sync(hdev, d->sync_handle);
return 0;
}
static void find_bis(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
/* Ignore if BIG doesn't match */
if (d->big != conn->iso_qos.bcast.big)
return;
d->count++;
}
static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *conn)
{
struct iso_list_data *d;
int ret;
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, conn->sync_handle);
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
d->big = big;
d->sync_handle = conn->sync_handle;
if (test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_PA_SYNC, d);
if (!d->count)
d->pa_sync_term = true;
d->count = 0;
}
if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_BIG_SYNC, d);
if (!d->count)
d->big_sync_term = true;
}
ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
terminate_big_destroy);
if (ret)
kfree(d);
return ret;
}
/* Cleanup BIS connection
*
* Detects if there any BIS left connected in a BIG
* broadcaster: Remove advertising instance and terminate BIG.
* broadcaster receiver: Teminate BIG sync and terminate PA sync.
*/
static void bis_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct hci_conn *bis;
bt_dev_dbg(hdev, "conn %p", conn);
if (conn->role == HCI_ROLE_MASTER) {
if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
return;
/* Check if ISO connection is a BIS and terminate advertising
* set and BIG if there are no other connections using it.
*/
bis = hci_conn_hash_lookup_big(hdev, conn->iso_qos.bcast.big);
if (bis)
return;
hci_le_terminate_big(hdev, conn);
} else {
hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
conn);
}
}
static int remove_cig_sync(struct hci_dev *hdev, void *data)
{
u8 handle = PTR_UINT(data);
return hci_le_remove_cig_sync(hdev, handle);
}
static int hci_le_remove_cig(struct hci_dev *hdev, u8 handle)
{
bt_dev_dbg(hdev, "handle 0x%2.2x", handle);
return hci_cmd_sync_queue(hdev, remove_cig_sync, UINT_PTR(handle),
NULL);
}
static void find_cis(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
/* Ignore broadcast or if CIG don't match */
if (!bacmp(&conn->dst, BDADDR_ANY) || d->cig != conn->iso_qos.ucast.cig)
return;
d->count++;
}
/* Cleanup CIS connection:
*
* Detects if there any CIS left connected in a CIG and remove it.
*/
static void cis_cleanup(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data d;
if (conn->iso_qos.ucast.cig == BT_ISO_QOS_CIG_UNSET)
return;
memset(&d, 0, sizeof(d));
d.cig = conn->iso_qos.ucast.cig;
/* Check if ISO connection is a CIS and remove CIG if there are
* no other connections using it.
*/
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECT,
&d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECTED,
&d);
if (d.count)
return;
hci_le_remove_cig(hdev, conn->iso_qos.ucast.cig);
}
static int hci_conn_hash_alloc_unset(struct hci_dev *hdev)
{
return ida_alloc_range(&hdev->unset_handle_ida, HCI_CONN_HANDLE_MAX + 1,
U16_MAX, GFP_ATOMIC);
}
static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role, u16 handle)
{
struct hci_conn *conn;
switch (type) {
case ACL_LINK:
if (!hdev->acl_mtu)
return ERR_PTR(-ECONNREFUSED);
break;
case CIS_LINK:
case BIS_LINK:
if (hdev->iso_mtu)
/* Dedicated ISO Buffer exists */
break;
fallthrough;
case LE_LINK:
if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
return ERR_PTR(-ECONNREFUSED);
break;
case SCO_LINK:
case ESCO_LINK:
if (!hdev->sco_pkts)
/* Controller does not support SCO or eSCO over HCI */
return ERR_PTR(-ECONNREFUSED);
break;
default:
return ERR_PTR(-ECONNREFUSED);
}
bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn)
return ERR_PTR(-ENOMEM);
bacpy(&conn->dst, dst);
bacpy(&conn->src, &hdev->bdaddr);
conn->handle = handle;
conn->hdev = hdev;
conn->type = type;
conn->role = role;
conn->mode = HCI_CM_ACTIVE;
conn->state = BT_OPEN;
conn->auth_type = HCI_AT_GENERAL_BONDING;
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
conn->key_type = 0xff;
conn->rssi = HCI_RSSI_INVALID;
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_INVALID;
conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
conn->sid = HCI_SID_INVALID;
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
/* Set Default Authenticated payload timeout to 30s */
conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
if (conn->role == HCI_ROLE_MASTER)
conn->out = true;
switch (type) {
case ACL_LINK:
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
conn->mtu = hdev->acl_mtu;
break;
case LE_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
break;
case CIS_LINK:
case BIS_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
/* set proper cleanup function */
if (!bacmp(dst, BDADDR_ANY))
conn->cleanup = bis_cleanup;
else if (conn->role == HCI_ROLE_MASTER)
conn->cleanup = cis_cleanup;
conn->mtu = hdev->iso_mtu ? hdev->iso_mtu :
hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
break;
case SCO_LINK:
if (lmp_esco_capable(hdev))
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
(hdev->esco_type & EDR_ESCO_MASK);
else
conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
conn->mtu = hdev->sco_mtu;
break;
case ESCO_LINK:
conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
conn->mtu = hdev->sco_mtu;
break;
}
skb_queue_head_init(&conn->data_q);
skb_queue_head_init(&conn->tx_q.queue);
INIT_LIST_HEAD(&conn->chan_list);
INIT_LIST_HEAD(&conn->link_list);
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
atomic_set(&conn->refcnt, 0);
hci_dev_hold(hdev);
hci_conn_hash_add(hdev, conn);
/* The SCO and eSCO connections will only be notified when their
* setup has been completed. This is different to ACL links which
* can be notified right away.
*/
if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
}
hci_conn_init_sysfs(conn);
return conn;
}
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 role)
{
int handle;
bt_dev_dbg(hdev, "dst %pMR", dst);
handle = hci_conn_hash_alloc_unset(hdev);
if (unlikely(handle < 0))
return ERR_PTR(-ECONNREFUSED);
return __hci_conn_add(hdev, type, dst, role, handle);
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role, u16 handle)
{
if (handle > HCI_CONN_HANDLE_MAX)
return ERR_PTR(-EINVAL);
return __hci_conn_add(hdev, type, dst, role, handle);
}
static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
{
if (!reason)
reason = HCI_ERROR_REMOTE_USER_TERM;
/* Due to race, SCO/ISO conn might be not established yet at this point,
* and nothing else will clean it up. In other cases it is done via HCI
* events.
*/
switch (conn->type) {
case SCO_LINK:
case ESCO_LINK:
if (HCI_CONN_HANDLE_UNSET(conn->handle))
hci_conn_failed(conn, reason);
break;
case CIS_LINK:
case BIS_LINK:
if ((conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
hci_conn_failed(conn, reason);
break;
}
}
static void hci_conn_unlink(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
bt_dev_dbg(hdev, "hcon %p", conn);
if (!conn->parent) {
struct hci_link *link, *t;
list_for_each_entry_safe(link, t, &conn->link_list, list) {
struct hci_conn *child = link->conn;
hci_conn_unlink(child);
/* If hdev is down it means
* hci_dev_close_sync/hci_conn_hash_flush is in progress
* and links don't need to be cleanup as all connections
* would be cleanup.
*/
if (!test_bit(HCI_UP, &hdev->flags))
continue;
hci_conn_cleanup_child(child, conn->abort_reason);
}
return;
}
if (!conn->link)
return;
list_del_rcu(&conn->link->list);
synchronize_rcu();
hci_conn_drop(conn->parent);
hci_conn_put(conn->parent);
conn->parent = NULL;
kfree(conn->link);
conn->link = NULL;
}
void hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
hci_conn_unlink(conn);
disable_delayed_work_sync(&conn->disc_work);
disable_delayed_work_sync(&conn->auto_accept_work);
disable_delayed_work_sync(&conn->idle_work);
if (conn->type == ACL_LINK) {
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) {
cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
else
hdev->acl_cnt += conn->sent;
} else {
/* Unacked ISO frames */
if (conn->type == CIS_LINK ||
conn->type == BIS_LINK) {
if (hdev->iso_pkts)
hdev->iso_cnt += conn->sent;
else if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
else
hdev->acl_cnt += conn->sent;
}
}
skb_queue_purge(&conn->data_q);
skb_queue_purge(&conn->tx_q.queue);
/* Remove the connection from the list and cleanup its remaining
* state. This is a separate function since for some cases like
* BT_CONNECT_SCAN we *only* want the cleanup part without the
* rest of hci_conn_del.
*/
hci_conn_cleanup(conn);
/* Dequeue callbacks using connection pointer as data */
hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
}
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
{
int use_src = bacmp(src, BDADDR_ANY);
struct hci_dev *hdev = NULL, *d;
BT_DBG("%pMR -> %pMR", src, dst);
read_lock(&hci_dev_list_lock);
list_for_each_entry(d, &hci_dev_list, list) {
if (!test_bit(HCI_UP, &d->flags) ||
hci_dev_test_flag(d, HCI_USER_CHANNEL))
continue;
/* Simple routing:
* No source address - find interface with bdaddr != dst
* Source address - find interface with bdaddr == src
*/
if (use_src) {
bdaddr_t id_addr;
u8 id_addr_type;
if (src_type == BDADDR_BREDR) {
if (!lmp_bredr_capable(d))
continue;
bacpy(&id_addr, &d->bdaddr);
id_addr_type = BDADDR_BREDR;
} else {
if (!lmp_le_capable(d))
continue;
hci_copy_identity_address(d, &id_addr,
&id_addr_type);
/* Convert from HCI to three-value type */
if (id_addr_type == ADDR_LE_DEV_PUBLIC)
id_addr_type = BDADDR_LE_PUBLIC;
else
id_addr_type = BDADDR_LE_RANDOM;
}
if (!bacmp(&id_addr, src) && id_addr_type == src_type) {
hdev = d; break;
}
} else {
if (bacmp(&d->bdaddr, dst)) {
hdev = d; break;
}
}
}
if (hdev)
hdev = hci_dev_hold(hdev);
read_unlock(&hci_dev_list_lock);
return hdev;
}
EXPORT_SYMBOL(hci_get_route);
/* This function requires the caller holds hdev->lock */
static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
{
struct hci_dev *hdev = conn->hdev;
hci_connect_le_scan_cleanup(conn, status);
/* Enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
hci_enable_advertising(hdev);
}
/* This function requires the caller holds hdev->lock */
void hci_conn_failed(struct hci_conn *conn, u8 status)
{
struct hci_dev *hdev = conn->hdev;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
switch (conn->type) {
case LE_LINK:
hci_le_conn_failed(conn, status);
break;
case ACL_LINK:
mgmt_connect_failed(hdev, conn, status);
break;
}
/* In case of BIG/PA sync failed, clear conn flags so that
* the conns will be correctly cleaned up by ISO layer
*/
test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, &conn->flags);
test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, &conn->flags);
conn->state = BT_CLOSED;
hci_connect_cfm(conn, status);
hci_conn_del(conn);
}
/* This function requires the caller holds hdev->lock */
u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle)
{
struct hci_dev *hdev = conn->hdev;
bt_dev_dbg(hdev, "hcon %p handle 0x%4.4x", conn, handle);
if (conn->handle == handle)
return 0;
if (handle > HCI_CONN_HANDLE_MAX) {
bt_dev_err(hdev, "Invalid handle: 0x%4.4x > 0x%4.4x",
handle, HCI_CONN_HANDLE_MAX);
return HCI_ERROR_INVALID_PARAMETERS;
}
/* If abort_reason has been sent it means the connection is being
* aborted and the handle shall not be changed.
*/
if (conn->abort_reason)
return conn->abort_reason;
if (HCI_CONN_HANDLE_UNSET(conn->handle))
ida_free(&hdev->unset_handle_ida, conn->handle);
conn->handle = handle;
return 0;
}
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)
{
struct hci_conn *conn;
struct smp_irk *irk;
int err;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
if (lmp_le_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Since the controller supports only one LE connection attempt at a
* time, we return -EBUSY if there is any connection attempt running.
*/
if (hci_lookup_le_connect(hdev))
return ERR_PTR(-EBUSY);
/* If there's already a connection object but it's not in
* scanning state it means it must already be established, in
* which case we can't do anything else except report a failure
* to connect.
*/
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) {
return ERR_PTR(-EBUSY);
}
/* Check if the destination address has been resolved by the controller
* since if it did then the identity address shall be used.
*/
if (!dst_resolved) {
/* When given an identity address with existing identity
* resolving key, the connection needs to be established
* to a resolvable random address.
*
* Storing the resolvable random address is required here
* to handle connection failures. The address will later
* be resolved back into the original identity address
* from the connect request.
*/
irk = hci_find_irk_by_addr(hdev, dst, dst_type);
if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
dst = &irk->rpa;
dst_type = ADDR_LE_DEV_RANDOM;
}
}
if (conn) {
bacpy(&conn->dst, dst);
} else {
conn = hci_conn_add_unset(hdev, LE_LINK, dst, role);
if (IS_ERR(conn))
return conn;
hci_conn_hold(conn);
conn->pending_sec_level = sec_level;
}
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout;
conn->le_adv_phy = phy;
conn->le_adv_sec_phy = sec_phy;
err = hci_connect_le_sync(hdev, conn);
if (err) {
hci_conn_del(conn);
return ERR_PTR(err);
}
return conn;
}
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
conn = hci_conn_hash_lookup_le(hdev, addr, type);
if (!conn)
return false;
if (conn->state != BT_CONNECTED)
return false;
return true;
}
/* This function requires the caller holds hdev->lock */
static int hci_explicit_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type)
{
struct hci_conn_params *params;
if (is_connected(hdev, addr, addr_type))
return -EISCONN;
params = hci_conn_params_lookup(hdev, addr, addr_type);
if (!params) {
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return -ENOMEM;
/* If we created new params, mark them to be deleted in
* hci_connect_le_scan_cleanup. It's different case than
* existing disabled params, those will stay after cleanup.
*/
params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
}
/* We're trying to connect, so make sure params are at pend_le_conns */
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_REPORT ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
hci_pend_le_list_del_init(params);
hci_pend_le_list_add(params, &hdev->pend_le_conns);
}
params->explicit_connect = true;
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
params->auto_connect);
return 0;
}
static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
{
struct hci_conn *conn;
u8 big;
/* Allocate a BIG if not set */
if (qos->bcast.big == BT_ISO_QOS_BIG_UNSET) {
for (big = 0x00; big < 0xef; big++) {
conn = hci_conn_hash_lookup_big(hdev, big);
if (!conn)
break;
}
if (big == 0xef)
return -EADDRNOTAVAIL;
/* Update BIG */
qos->bcast.big = big;
}
return 0;
}
static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
{
struct hci_conn *conn;
u8 bis;
/* Allocate BIS if not set */
if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) {
if (qos->bcast.big != BT_ISO_QOS_BIG_UNSET) {
conn = hci_conn_hash_lookup_big(hdev, qos->bcast.big);
if (conn) {
/* If the BIG handle is already matched to an advertising
* handle, do not allocate a new one.
*/
qos->bcast.bis = conn->iso_qos.bcast.bis;
return 0;
}
}
/* Find an unused adv set to advertise BIS, skip instance 0x00
* since it is reserved as general purpose set.
*/
for (bis = 0x01; bis < hdev->le_num_of_adv_sets;
bis++) {
conn = hci_conn_hash_lookup_bis(hdev, BDADDR_ANY, bis);
if (!conn)
break;
}
if (bis == hdev->le_num_of_adv_sets)
return -EADDRNOTAVAIL;
/* Update BIS */
qos->bcast.bis = bis;
}
return 0;
}
/* This function requires the caller holds hdev->lock */
static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 sid, struct bt_iso_qos *qos,
__u8 base_len, __u8 *base)
{
struct hci_conn *conn;
int err;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
if (lmp_le_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
err = qos_set_big(hdev, qos);
if (err)
return ERR_PTR(err);
err = qos_set_bis(hdev, qos);
if (err)
return ERR_PTR(err);
/* Check if the LE Create BIG command has already been sent */
conn = hci_conn_hash_lookup_per_adv_bis(hdev, dst, qos->bcast.big,
qos->bcast.big);
if (conn)
return ERR_PTR(-EADDRINUSE);
/* Check BIS settings against other bound BISes, since all
* BISes in a BIG must have the same value for all parameters
*/
conn = hci_conn_hash_lookup_big(hdev, qos->bcast.big);
if (conn && (memcmp(qos, &conn->iso_qos, sizeof(*qos)) ||
base_len != conn->le_per_adv_data_len ||
memcmp(conn->le_per_adv_data, base, base_len)))
return ERR_PTR(-EADDRINUSE);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
conn->state = BT_CONNECT;
conn->sid = sid;
hci_conn_hold(conn);
return conn;
}
/* This function requires the caller holds hdev->lock */
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 *conn;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
if (lmp_le_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Some devices send ATT messages as soon as the physical link is
* established. To be able to handle these ATT messages, the user-
* space first establishes the connection and then starts the pairing
* process.
*
* So if a hci_conn object already exists for the following connection
* attempt, we simply update pending_sec_level and auth_type fields
* and return the object found.
*/
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
if (conn) {
if (conn->pending_sec_level < sec_level)
conn->pending_sec_level = sec_level;
goto done;
}
BT_DBG("requesting refresh of dst_addr");
conn = hci_conn_add_unset(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0) {
hci_conn_del(conn);
return ERR_PTR(-EBUSY);
}
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
conn->conn_reason = conn_reason;
hci_update_passive_scan(hdev);
done:
hci_conn_hold(conn);
return conn;
}
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 *acl;
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
if (lmp_bredr_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Reject outgoing connection to device with same BD ADDR against
* CVE-2020-26555
*/
if (!bacmp(&hdev->bdaddr, dst)) {
bt_dev_dbg(hdev, "Reject connection with same BD_ADDR %pMR\n",
dst);
return ERR_PTR(-ECONNREFUSED);
}
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
acl = hci_conn_add_unset(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
if (IS_ERR(acl))
return acl;
}
hci_conn_hold(acl);
acl->conn_reason = conn_reason;
if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
int err;
acl->sec_level = BT_SECURITY_LOW;
acl->pending_sec_level = sec_level;
acl->auth_type = auth_type;
acl->conn_timeout = timeout;
err = hci_connect_acl_sync(hdev, acl);
if (err) {
hci_conn_del(acl);
return ERR_PTR(err);
}
}
return acl;
}
static struct hci_link *hci_conn_link(struct hci_conn *parent,
struct hci_conn *conn)
{
struct hci_dev *hdev = parent->hdev;
struct hci_link *link;
bt_dev_dbg(hdev, "parent %p hcon %p", parent, conn);
if (conn->link)
return conn->link;
if (conn->parent)
return NULL;
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
link->conn = hci_conn_hold(conn);
conn->link = link;
conn->parent = hci_conn_get(parent);
/* Use list_add_tail_rcu append to the list */
list_add_tail_rcu(&link->list, &parent->link_list);
return link;
}
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 *acl;
struct hci_conn *sco;
struct hci_link *link;
acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING,
CONN_REASON_SCO_CONNECT, timeout);
if (IS_ERR(acl))
return acl;
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
if (!sco) {
sco = hci_conn_add_unset(hdev, type, dst, HCI_ROLE_MASTER);
if (IS_ERR(sco)) {
hci_conn_drop(acl);
return sco;
}
}
link = hci_conn_link(acl, sco);
if (!link) {
hci_conn_drop(acl);
hci_conn_drop(sco);
return ERR_PTR(-ENOLINK);
}
sco->setting = setting;
sco->codec = *codec;
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) {
/* defer SCO setup until mode change completed */
set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
return sco;
}
hci_sco_setup(acl, 0x00);
}
return sco;
}
static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_create_big cp;
struct iso_list_data data;
memset(&cp, 0, sizeof(cp));
data.big = qos->bcast.big;
data.bis = qos->bcast.bis;
data.count = 0;
/* Create a BIS for each bound connection */
hci_conn_hash_list_state(hdev, bis_list, BIS_LINK,
BT_BOUND, &data);
cp.handle = qos->bcast.big;
cp.adv_handle = qos->bcast.bis;
cp.num_bis = data.count;
hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval);
cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu);
cp.bis.latency = cpu_to_le16(qos->bcast.out.latency);
cp.bis.rtn = qos->bcast.out.rtn;
cp.bis.phy = qos->bcast.out.phy;
cp.bis.packing = qos->bcast.packing;
cp.bis.framing = qos->bcast.framing;
cp.bis.encryption = qos->bcast.encryption;
memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
}
static int set_cig_params_sync(struct hci_dev *hdev, void *data)
{
DEFINE_FLEX(struct hci_cp_le_set_cig_params, pdu, cis, num_cis, 0x1f);
u8 cig_id = PTR_UINT(data);
struct hci_conn *conn;
struct bt_iso_qos *qos;
u8 aux_num_cis = 0;
u8 cis_id;
conn = hci_conn_hash_lookup_cig(hdev, cig_id);
if (!conn)
return 0;
qos = &conn->iso_qos;
pdu->cig_id = cig_id;
hci_cpu_to_le24(qos->ucast.out.interval, pdu->c_interval);
hci_cpu_to_le24(qos->ucast.in.interval, pdu->p_interval);
pdu->sca = qos->ucast.sca;
pdu->packing = qos->ucast.packing;
pdu->framing = qos->ucast.framing;
pdu->c_latency = cpu_to_le16(qos->ucast.out.latency);
pdu->p_latency = cpu_to_le16(qos->ucast.in.latency);
/* Reprogram all CIS(s) with the same CIG, valid range are:
* num_cis: 0x00 to 0x1F
* cis_id: 0x00 to 0xEF
*/
for (cis_id = 0x00; cis_id < 0xf0 &&
aux_num_cis < pdu->num_cis; cis_id++) {
struct hci_cis_params *cis;
conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id);
if (!conn)
continue;
qos = &conn->iso_qos;
cis = &pdu->cis[aux_num_cis++];
cis->cis_id = cis_id;
cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu);
cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu);
cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy :
qos->ucast.in.phy;
cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy :
qos->ucast.out.phy;
cis->c_rtn = qos->ucast.out.rtn;
cis->p_rtn = qos->ucast.in.rtn;
}
pdu->num_cis = aux_num_cis;
if (!pdu->num_cis)
return 0;
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS,
struct_size(pdu, cis, pdu->num_cis),
pdu, HCI_CMD_TIMEOUT);
}
static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data data;
memset(&data, 0, sizeof(data));
/* Allocate first still reconfigurable CIG if not set */
if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) {
for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
data.count = 0;
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECT, &data);
if (data.count)
continue;
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECTED, &data);
if (!data.count)
break;
}
if (data.cig == 0xf0)
return false;
/* Update CIG */
qos->ucast.cig = data.cig;
}
if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) {
if (hci_conn_hash_lookup_cis(hdev, NULL, 0, qos->ucast.cig,
qos->ucast.cis))
return false;
goto done;
}
/* Allocate first available CIS if not set */
for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0xf0;
data.cis++) {
if (!hci_conn_hash_lookup_cis(hdev, NULL, 0, data.cig,
data.cis)) {
/* Update CIS */
qos->ucast.cis = data.cis;
break;
}
}
if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET)
return false;
done:
if (hci_cmd_sync_queue(hdev, set_cig_params_sync,
UINT_PTR(qos->ucast.cig), NULL) < 0)
return false;
return true;
}
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos)
{
struct hci_conn *cis;
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
qos->ucast.cis);
if (!cis) {
cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
HCI_ROLE_MASTER);
if (IS_ERR(cis))
return cis;
cis->cleanup = cis_cleanup;
cis->dst_type = dst_type;
cis->iso_qos.ucast.cig = BT_ISO_QOS_CIG_UNSET;
cis->iso_qos.ucast.cis = BT_ISO_QOS_CIS_UNSET;
}
if (cis->state == BT_CONNECTED)
return cis;
/* Check if CIS has been set and the settings matches */
if (cis->state == BT_BOUND &&
!memcmp(&cis->iso_qos, qos, sizeof(*qos)))
return cis;
/* Update LINK PHYs according to QoS preference */
cis->le_tx_phy = qos->ucast.out.phy;
cis->le_rx_phy = qos->ucast.in.phy;
/* If output interval is not set use the input interval as it cannot be
* 0x000000.
*/
if (!qos->ucast.out.interval)
qos->ucast.out.interval = qos->ucast.in.interval;
/* If input interval is not set use the output interval as it cannot be
* 0x000000.
*/
if (!qos->ucast.in.interval)
qos->ucast.in.interval = qos->ucast.out.interval;
/* If output latency is not set use the input latency as it cannot be
* 0x0000.
*/
if (!qos->ucast.out.latency)
qos->ucast.out.latency = qos->ucast.in.latency;
/* If input latency is not set use the output latency as it cannot be
* 0x0000.
*/
if (!qos->ucast.in.latency)
qos->ucast.in.latency = qos->ucast.out.latency;
if (!hci_le_set_cig_params(cis, qos)) {
hci_conn_drop(cis);
return ERR_PTR(-EINVAL);
}
hci_conn_hold(cis);
cis->iso_qos = *qos;
cis->state = BT_BOUND;
return cis;
}
bool hci_iso_setup_path(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_setup_iso_path cmd;
memset(&cmd, 0, sizeof(cmd));
if (conn->iso_qos.ucast.out.sdu) {
cmd.handle = cpu_to_le16(conn->handle);
cmd.direction = 0x00; /* Input (Host to Controller) */
cmd.path = 0x00; /* HCI path if enabled */
cmd.codec = 0x03; /* Transparent Data */
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
&cmd) < 0)
return false;
}
if (conn->iso_qos.ucast.in.sdu) {
cmd.handle = cpu_to_le16(conn->handle);
cmd.direction = 0x01; /* Output (Controller to Host) */
cmd.path = 0x00; /* HCI path if enabled */
cmd.codec = 0x03; /* Transparent Data */
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
&cmd) < 0)
return false;
}
return true;
}
int hci_conn_check_create_cis(struct hci_conn *conn)
{
if (conn->type != CIS_LINK)
return -EINVAL;
if (!conn->parent || conn->parent->state != BT_CONNECTED ||
conn->state != BT_CONNECT || HCI_CONN_HANDLE_UNSET(conn->handle))
return 1;
return 0;
}
static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
{
return hci_le_create_cis_sync(hdev);
}
int hci_le_create_cis_pending(struct hci_dev *hdev)
{
struct hci_conn *conn;
bool pending = false;
rcu_read_lock();
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) {
rcu_read_unlock();
return -EBUSY;
}
if (!hci_conn_check_create_cis(conn))
pending = true;
}
rcu_read_unlock();
if (!pending)
return 0;
/* Queue Create CIS */
return hci_cmd_sync_queue(hdev, hci_create_cis_sync, NULL, NULL);
}
static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
struct bt_iso_io_qos *qos, __u8 phy)
{
/* Only set MTU if PHY is enabled */
if (!qos->sdu && qos->phy)
qos->sdu = conn->mtu;
/* Use the same PHY as ACL if set to any */
if (qos->phy == BT_ISO_PHY_ANY)
qos->phy = phy;
/* Use LE ACL connection interval if not set */
if (!qos->interval)
/* ACL interval unit in 1.25 ms to us */
qos->interval = conn->le_conn_interval * 1250;
/* Use LE ACL connection latency if not set */
if (!qos->latency)
qos->latency = conn->le_conn_latency;
}
static int create_big_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
struct bt_iso_qos *qos = &conn->iso_qos;
u16 interval, sync_interval = 0;
u32 flags = 0;
int err;
if (qos->bcast.out.phy == 0x02)
flags |= MGMT_ADV_FLAG_SEC_2M;
/* Align intervals */
interval = (qos->bcast.out.interval / 1250) * qos->bcast.sync_factor;
if (qos->bcast.bis)
sync_interval = interval * 4;
err = hci_start_per_adv_sync(hdev, qos->bcast.bis, conn->sid,
conn->le_per_adv_data_len,
conn->le_per_adv_data, flags, interval,
interval, sync_interval);
if (err)
return err;
return hci_le_create_big(conn, &conn->iso_qos);
}
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
struct bt_iso_qos *qos)
{
struct hci_conn *conn;
bt_dev_dbg(hdev, "dst %pMR type %d sid %d", dst, dst_type, sid);
conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_SLAVE);
if (IS_ERR(conn))
return conn;
conn->iso_qos = *qos;
conn->dst_type = dst_type;
conn->sid = sid;
conn->state = BT_LISTEN;
conn->conn_timeout = msecs_to_jiffies(qos->bcast.sync_timeout * 10);
hci_conn_hold(conn);
hci_connect_pa_sync(hdev, conn);
return conn;
}
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 err;
if (num_bis < 0x01 || num_bis > ISO_MAX_NUM_BIS)
return -EINVAL;
err = qos_set_big(hdev, qos);
if (err)
return err;
if (hcon) {
/* Update hcon QoS */
hcon->iso_qos = *qos;
hcon->num_bis = num_bis;
memcpy(hcon->bis, bis, num_bis);
hcon->conn_timeout = msecs_to_jiffies(qos->bcast.timeout * 10);
}
return hci_connect_big_sync(hdev, hcon);
}
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
{
struct hci_conn *conn = data;
bt_dev_dbg(hdev, "conn %p", conn);
if (err) {
bt_dev_err(hdev, "Unable to create BIG: %d", err);
hci_connect_cfm(conn, err);
hci_conn_del(conn);
}
}
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)
{
struct hci_conn *conn;
struct hci_conn *parent;
__u8 eir[HCI_MAX_PER_AD_LENGTH];
struct hci_link *link;
/* Look for any BIS that is open for rebinding */
conn = hci_conn_hash_lookup_big_state(hdev, qos->bcast.big, BT_OPEN);
if (conn) {
memcpy(qos, &conn->iso_qos, sizeof(*qos));
conn->state = BT_CONNECTED;
return conn;
}
if (base_len && base)
base_len = eir_append_service_data(eir, 0, 0x1851,
base, base_len);
/* We need hci_conn object using the BDADDR_ANY as dst */
conn = hci_add_bis(hdev, dst, sid, qos, base_len, eir);
if (IS_ERR(conn))
return conn;
/* Update LINK PHYs according to QoS preference */
conn->le_tx_phy = qos->bcast.out.phy;
conn->le_tx_phy = qos->bcast.out.phy;
/* Add Basic Announcement into Peridic Adv Data if BASE is set */
if (base_len && base) {
memcpy(conn->le_per_adv_data, eir, sizeof(eir));
conn->le_per_adv_data_len = base_len;
}
hci_iso_qos_setup(hdev, conn, &qos->bcast.out,
conn->le_tx_phy ? conn->le_tx_phy :
hdev->le_tx_def_phys);
conn->iso_qos = *qos;
conn->state = BT_BOUND;
/* Link BISes together */
parent = hci_conn_hash_lookup_big(hdev,
conn->iso_qos.bcast.big);
if (parent && parent != conn) {
link = hci_conn_link(parent, conn);
hci_conn_drop(conn);
if (!link)
return ERR_PTR(-ENOLINK);
}
return conn;
}
static void bis_mark_per_adv(struct hci_conn *conn, void *data)
{
struct iso_list_data *d = data;
/* Skip if not broadcast/ANY address */
if (bacmp(&conn->dst, BDADDR_ANY))
return;
if (d->big != conn->iso_qos.bcast.big ||
d->bis == BT_ISO_QOS_BIS_UNSET ||
d->bis != conn->iso_qos.bcast.bis)
return;
set_bit(HCI_CONN_PER_ADV, &conn->flags);
}
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
struct bt_iso_qos *qos,
__u8 base_len, __u8 *base)
{
struct hci_conn *conn;
int err;
struct iso_list_data data;
conn = hci_bind_bis(hdev, dst, sid, qos, base_len, base);
if (IS_ERR(conn))
return conn;
if (conn->state == BT_CONNECTED)
return conn;
/* Check if SID needs to be allocated then search for the first
* available.
*/
if (conn->sid == HCI_SID_INVALID) {
u8 sid;
for (sid = 0; sid <= 0x0f; sid++) {
if (!hci_find_adv_sid(hdev, sid)) {
conn->sid = sid;
break;
}
}
}
data.big = qos->bcast.big;
data.bis = qos->bcast.bis;
/* Set HCI_CONN_PER_ADV for all bound connections, to mark that
* the start periodic advertising and create BIG commands have
* been queued
*/
hci_conn_hash_list_state(hdev, bis_mark_per_adv, BIS_LINK,
BT_BOUND, &data);
/* Queue start periodic advertising and create BIG */
err = hci_cmd_sync_queue(hdev, create_big_sync, conn,
create_big_complete);
if (err < 0) {
hci_conn_drop(conn);
return ERR_PTR(err);
}
return conn;
}
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, struct bt_iso_qos *qos)
{
struct hci_conn *le;
struct hci_conn *cis;
struct hci_link *link;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
le = hci_connect_le(hdev, dst, dst_type, false,
BT_SECURITY_LOW,
HCI_LE_CONN_TIMEOUT,
HCI_ROLE_SLAVE, 0, 0);
else
le = hci_connect_le_scan(hdev, dst, dst_type,
BT_SECURITY_LOW,
HCI_LE_CONN_TIMEOUT,
CONN_REASON_ISO_CONNECT);
if (IS_ERR(le))
return le;
hci_iso_qos_setup(hdev, le, &qos->ucast.out,
le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys);
hci_iso_qos_setup(hdev, le, &qos->ucast.in,
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
cis = hci_bind_cis(hdev, dst, dst_type, qos);
if (IS_ERR(cis)) {
hci_conn_drop(le);
return cis;
}
link = hci_conn_link(le, cis);
hci_conn_drop(cis);
if (!link) {
hci_conn_drop(le);
return ERR_PTR(-ENOLINK);
}
cis->state = BT_CONNECT;
hci_le_create_cis_pending(hdev);
return cis;
}
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
/* In Secure Connections Only mode, it is required that Secure
* Connections is used and the link is encrypted with AES-CCM
* using a P-256 authenticated combination key.
*/
if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) {
if (!hci_conn_sc_enabled(conn) ||
!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)
return 0;
}
/* AES encryption is required for Level 4:
*
* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part C
* page 1319:
*
* 128-bit equivalent strength for link and encryption keys
* required using FIPS approved algorithms (E0 not allowed,
* SAFER+ not allowed, and P-192 not allowed; encryption key
* not shortened)
*/
if (conn->sec_level == BT_SECURITY_FIPS &&
!test_bit(HCI_CONN_AES_CCM, &conn->flags)) {
bt_dev_err(conn->hdev,
"Invalid security: Missing AES-CCM usage");
return 0;
}
if (hci_conn_ssp_enabled(conn) &&
!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
return 0;
return 1;
}
/* Authenticate remote device */
static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("hcon %p", conn);
if (conn->pending_sec_level > sec_level)
sec_level = conn->pending_sec_level;
if (sec_level > conn->sec_level)
conn->pending_sec_level = sec_level;
else if (test_bit(HCI_CONN_AUTH, &conn->flags))
return 1;
/* Make sure we preserve an existing MITM requirement*/
auth_type |= (conn->auth_type & 0x01);
conn->auth_type = auth_type;
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
struct hci_cp_auth_requested cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
/* Set the ENCRYPT_PEND to trigger encryption after
* authentication.
*/
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
}
return 0;
}
/* Encrypt the link */
static void hci_conn_encrypt(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = cpu_to_le16(conn->handle);
cp.encrypt = 0x01;
hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
&cp);
}
}
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
bool initiator)
{
BT_DBG("hcon %p", conn);
if (conn->type == LE_LINK)
return smp_conn_security(conn, sec_level);
/* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
/* For non 2.1 devices and low security level we don't need the link
key. */
if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn))
return 1;
/* For other security levels we need the link key. */
if (!test_bit(HCI_CONN_AUTH, &conn->flags))
goto auth;
switch (conn->key_type) {
case HCI_LK_AUTH_COMBINATION_P256:
/* An authenticated FIPS approved combination key has
* sufficient security for security level 4 or lower.
*/
if (sec_level <= BT_SECURITY_FIPS)
goto encrypt;
break;
case HCI_LK_AUTH_COMBINATION_P192:
/* An authenticated combination key has sufficient security for
* security level 3 or lower.
*/
if (sec_level <= BT_SECURITY_HIGH)
goto encrypt;
break;
case HCI_LK_UNAUTH_COMBINATION_P192:
case HCI_LK_UNAUTH_COMBINATION_P256:
/* An unauthenticated combination key has sufficient security
* for security level 2 or lower.
*/
if (sec_level <= BT_SECURITY_MEDIUM)
goto encrypt;
break;
case HCI_LK_COMBINATION:
/* A combination key has always sufficient security for the
* security levels 2 or lower. High security level requires the
* combination key is generated using maximum PIN code length
* (16). For pre 2.1 units.
*/
if (sec_level <= BT_SECURITY_MEDIUM || conn->pin_length == 16)
goto encrypt;
break;
default:
break;
}
auth:
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return 0;
if (initiator)
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
if (!hci_conn_auth(conn, sec_level, auth_type))
return 0;
encrypt:
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) {
/* Ensure that the encryption key size has been read,
* otherwise stall the upper layer responses.
*/
if (!conn->enc_key_size)
return 0;
/* Nothing else needed, all requirements are met */
return 1;
}
hci_conn_encrypt(conn);
return 0;
}
EXPORT_SYMBOL(hci_conn_security);
/* Check secure link requirement */
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
{
BT_DBG("hcon %p", conn);
/* Accept if non-secure or higher security level is required */
if (sec_level != BT_SECURITY_HIGH && sec_level != BT_SECURITY_FIPS)
return 1;
/* Accept if secure or higher security level is already present */
if (conn->sec_level == BT_SECURITY_HIGH ||
conn->sec_level == BT_SECURITY_FIPS)
return 1;
/* Reject not secure link */
return 0;
}
EXPORT_SYMBOL(hci_conn_check_secure);
/* Switch role */
int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
{
BT_DBG("hcon %p", conn);
if (role == conn->role)
return 1;
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
struct hci_cp_switch_role cp;
bacpy(&cp.bdaddr, &conn->dst);
cp.role = role;
hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp);
}
return 0;
}
EXPORT_SYMBOL(hci_conn_switch_role);
/* Enter active mode */
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("hcon %p mode %d", conn, conn->mode);
if (conn->mode != HCI_CM_SNIFF)
goto timer;
if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active)
goto timer;
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
struct hci_cp_exit_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp);
}
timer:
if (hdev->idle_timeout > 0)
queue_delayed_work(hdev->workqueue, &conn->idle_work,
msecs_to_jiffies(hdev->idle_timeout));
}
/* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev)
{
struct list_head *head = &hdev->conn_hash.list;
struct hci_conn *conn;
BT_DBG("hdev %s", hdev->name);
/* We should not traverse the list here, because hci_conn_del
* can remove extra links, which may cause the list traversal
* to hit items that have already been released.
*/
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);
}
}
static u32 get_link_mode(struct hci_conn *conn)
{
u32 link_mode = 0;
if (conn->role == HCI_ROLE_MASTER)
link_mode |= HCI_LM_MASTER;
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
link_mode |= HCI_LM_ENCRYPT;
if (test_bit(HCI_CONN_AUTH, &conn->flags))
link_mode |= HCI_LM_AUTH;
if (test_bit(HCI_CONN_SECURE, &conn->flags))
link_mode |= HCI_LM_SECURE;
if (test_bit(HCI_CONN_FIPS, &conn->flags))
link_mode |= HCI_LM_FIPS;
return link_mode;
}
int hci_get_conn_list(void __user *arg)
{
struct hci_conn *c;
struct hci_conn_list_req req, *cl;
struct hci_conn_info *ci;
struct hci_dev *hdev;
int n = 0, size, err;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci))
return -EINVAL;
size = sizeof(req) + req.conn_num * sizeof(*ci);
cl = kmalloc(size, GFP_KERNEL);
if (!cl)
return -ENOMEM;
hdev = hci_dev_get(req.dev_id);
if (!hdev) {
kfree(cl);
return -ENODEV;
}
ci = cl->conn_info;
hci_dev_lock(hdev);
list_for_each_entry(c, &hdev->conn_hash.list, list) {
bacpy(&(ci + n)->bdaddr, &c->dst);
(ci + n)->handle = c->handle;
(ci + n)->type = c->type;
(ci + n)->out = c->out;
(ci + n)->state = c->state;
(ci + n)->link_mode = get_link_mode(c);
if (++n >= req.conn_num)
break;
}
hci_dev_unlock(hdev);
cl->dev_id = hdev->id;
cl->conn_num = n;
size = sizeof(req) + n * sizeof(*ci);
hci_dev_put(hdev);
err = copy_to_user(arg, cl, size);
kfree(cl);
return err ? -EFAULT : 0;
}
int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
{
struct hci_conn_info_req req;
struct hci_conn_info ci;
struct hci_conn *conn;
char __user *ptr = arg + sizeof(req);
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
if (conn) {
bacpy(&ci.bdaddr, &conn->dst);
ci.handle = conn->handle;
ci.type = conn->type;
ci.out = conn->out;
ci.state = conn->state;
ci.link_mode = get_link_mode(conn);
}
hci_dev_unlock(hdev);
if (!conn)
return -ENOENT;
return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
}
int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
{
struct hci_auth_info_req req;
struct hci_conn *conn;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr);
if (conn)
req.type = conn->auth_type;
hci_dev_unlock(hdev);
if (!conn)
return -ENOENT;
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
}
struct hci_chan *hci_chan_create(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct hci_chan *chan;
BT_DBG("%s hcon %p", hdev->name, conn);
if (test_bit(HCI_CONN_DROP, &conn->flags)) {
BT_DBG("Refusing to create new hci_chan");
return NULL;
}
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan)
return NULL;
chan->conn = hci_conn_get(conn);
skb_queue_head_init(&chan->data_q);
chan->state = BT_CONNECTED;
list_add_rcu(&chan->list, &conn->chan_list);
return chan;
}
void hci_chan_del(struct hci_chan *chan)
{
struct hci_conn *conn = chan->conn;
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s hcon %p chan %p", hdev->name, conn, chan);
list_del_rcu(&chan->list);
synchronize_rcu();
/* Prevent new hci_chan's to be created for this hci_conn */
set_bit(HCI_CONN_DROP, &conn->flags);
hci_conn_put(conn);
skb_queue_purge(&chan->data_q);
kfree(chan);
}
void hci_chan_list_flush(struct hci_conn *conn)
{
struct hci_chan *chan, *n;
BT_DBG("hcon %p", conn);
list_for_each_entry_safe(chan, n, &conn->chan_list, list)
hci_chan_del(chan);
}
static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon,
__u16 handle)
{
struct hci_chan *hchan;
list_for_each_entry(hchan, &hcon->chan_list, list) {
if (hchan->handle == handle)
return hchan;
}
return NULL;
}
struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *hcon;
struct hci_chan *hchan = NULL;
rcu_read_lock();
list_for_each_entry_rcu(hcon, &h->list, list) {
hchan = __hci_chan_lookup_handle(hcon, handle);
if (hchan)
break;
}
rcu_read_unlock();
return hchan;
}
u32 hci_conn_get_phy(struct hci_conn *conn)
{
u32 phys = 0;
/* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 2, Part B page 471:
* Table 6.2: Packets defined for synchronous, asynchronous, and
* CPB logical transport types.
*/
switch (conn->type) {
case SCO_LINK:
/* SCO logical transport (1 Mb/s):
* HV1, HV2, HV3 and DV.
*/
phys |= BT_PHY_BR_1M_1SLOT;
break;
case ACL_LINK:
/* ACL logical transport (1 Mb/s) ptt=0:
* DH1, DM3, DH3, DM5 and DH5.
*/
phys |= BT_PHY_BR_1M_1SLOT;
if (conn->pkt_type & (HCI_DM3 | HCI_DH3))
phys |= BT_PHY_BR_1M_3SLOT;
if (conn->pkt_type & (HCI_DM5 | HCI_DH5))
phys |= BT_PHY_BR_1M_5SLOT;
/* ACL logical transport (2 Mb/s) ptt=1:
* 2-DH1, 2-DH3 and 2-DH5.
*/
if (!(conn->pkt_type & HCI_2DH1))
phys |= BT_PHY_EDR_2M_1SLOT;
if (!(conn->pkt_type & HCI_2DH3))
phys |= BT_PHY_EDR_2M_3SLOT;
if (!(conn->pkt_type & HCI_2DH5))
phys |= BT_PHY_EDR_2M_5SLOT;
/* ACL logical transport (3 Mb/s) ptt=1:
* 3-DH1, 3-DH3 and 3-DH5.
*/
if (!(conn->pkt_type & HCI_3DH1))
phys |= BT_PHY_EDR_3M_1SLOT;
if (!(conn->pkt_type & HCI_3DH3))
phys |= BT_PHY_EDR_3M_3SLOT;
if (!(conn->pkt_type & HCI_3DH5))
phys |= BT_PHY_EDR_3M_5SLOT;
break;
case ESCO_LINK:
/* eSCO logical transport (1 Mb/s): EV3, EV4 and EV5 */
phys |= BT_PHY_BR_1M_1SLOT;
if (!(conn->pkt_type & (ESCO_EV4 | ESCO_EV5)))
phys |= BT_PHY_BR_1M_3SLOT;
/* eSCO logical transport (2 Mb/s): 2-EV3, 2-EV5 */
if (!(conn->pkt_type & ESCO_2EV3))
phys |= BT_PHY_EDR_2M_1SLOT;
if (!(conn->pkt_type & ESCO_2EV5))
phys |= BT_PHY_EDR_2M_3SLOT;
/* eSCO logical transport (3 Mb/s): 3-EV3, 3-EV5 */
if (!(conn->pkt_type & ESCO_3EV3))
phys |= BT_PHY_EDR_3M_1SLOT;
if (!(conn->pkt_type & ESCO_3EV5))
phys |= BT_PHY_EDR_3M_3SLOT;
break;
case LE_LINK:
if (conn->le_tx_phy & HCI_LE_SET_PHY_1M)
phys |= BT_PHY_LE_1M_TX;
if (conn->le_rx_phy & HCI_LE_SET_PHY_1M)
phys |= BT_PHY_LE_1M_RX;
if (conn->le_tx_phy & HCI_LE_SET_PHY_2M)
phys |= BT_PHY_LE_2M_TX;
if (conn->le_rx_phy & HCI_LE_SET_PHY_2M)
phys |= BT_PHY_LE_2M_RX;
if (conn->le_tx_phy & HCI_LE_SET_PHY_CODED)
phys |= BT_PHY_LE_CODED_TX;
if (conn->le_rx_phy & HCI_LE_SET_PHY_CODED)
phys |= BT_PHY_LE_CODED_RX;
break;
}
return phys;
}
static int abort_conn_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
}
int hci_abort_conn(struct hci_conn *conn, u8 reason)
{
struct hci_dev *hdev = conn->hdev;
/* If abort_reason has already been set it means the connection is
* already being aborted so don't attempt to overwrite it.
*/
if (conn->abort_reason)
return 0;
bt_dev_dbg(hdev, "handle 0x%2.2x reason 0x%2.2x", conn->handle, reason);
conn->abort_reason = reason;
/* If the connection is pending check the command opcode since that
* might be blocking on hci_cmd_sync_work while waiting its respective
* event so we need to hci_cmd_sync_cancel to cancel it.
*
* hci_connect_le serializes the connection attempts so only one
* connection can be in BT_CONNECT at time.
*/
if (conn->state == BT_CONNECT && hdev->req_status == HCI_REQ_PEND) {
switch (hci_skb_event(hdev->sent_cmd)) {
case HCI_EV_CONN_COMPLETE:
case HCI_EV_LE_CONN_COMPLETE:
case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
case HCI_EVT_LE_CIS_ESTABLISHED:
hci_cmd_sync_cancel(hdev, ECANCELED);
break;
}
/* Cancel connect attempt if still queued/pending */
} else if (!hci_cancel_connect_sync(hdev, conn)) {
return 0;
}
/* Run immediately if on cmd_sync_work since this may be called
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
* already queue its callback on cmd_sync_work.
*/
return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
}
void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
const struct sockcm_cookie *sockc)
{
struct sock *sk = skb ? skb->sk : NULL;
int key;
/* This shall be called on a single skb of those generated by user
* sendmsg(), and only when the sendmsg() does not return error to
* user. This is required for keeping the tskey that increments here in
* sync with possible sendmsg() counting by user.
*
* Stream sockets shall set key_offset to sendmsg() length in bytes
* and call with the last fragment, others to 1 and first fragment.
*/
if (!skb || !sockc || !sk || !key_offset)
return;
sock_tx_timestamp(sk, sockc, &skb_shinfo(skb)->tx_flags);
if (sk->sk_type == SOCK_STREAM)
key = atomic_add_return(key_offset, &sk->sk_tskey);
if (sockc->tsflags & SOF_TIMESTAMPING_OPT_ID &&
sockc->tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) {
if (sockc->tsflags & SOCKCM_FLAG_TS_OPT_ID) {
skb_shinfo(skb)->tskey = sockc->ts_opt_id;
} else {
if (sk->sk_type != SOCK_STREAM)
key = atomic_inc_return(&sk->sk_tskey);
skb_shinfo(skb)->tskey = key - 1;
}
}
}
void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
{
struct tx_queue *comp = &conn->tx_q;
bool track = false;
/* Emit SND now, ie. just before sending to driver */
if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP)
__skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SND);
/* COMPLETION tstamp is emitted for tracked skb later in Number of
* Completed Packets event. Available only for flow controlled cases.
*
* TODO: SCO support without flowctl (needs to be done in drivers)
*/
switch (conn->type) {
case CIS_LINK:
case BIS_LINK:
case ACL_LINK:
case LE_LINK:
break;
case SCO_LINK:
case ESCO_LINK:
if (!hci_dev_test_flag(conn->hdev, HCI_SCO_FLOWCTL))
return;
break;
default:
return;
}
if (skb->sk && (skb_shinfo(skb)->tx_flags & SKBTX_COMPLETION_TSTAMP))
track = true;
/* If nothing is tracked, just count extra skbs at the queue head */
if (!track && !comp->tracked) {
comp->extra++;
return;
}
if (track) {
skb = skb_clone_sk(skb);
if (!skb)
goto count_only;
comp->tracked++;
} else {
skb = skb_clone(skb, GFP_KERNEL);
if (!skb)
goto count_only;
}
skb_queue_tail(&comp->queue, skb);
return;
count_only:
/* Stop tracking skbs, and only count. This will not emit timestamps for
* the packets, but if we get here something is more seriously wrong.
*/
comp->tracked = 0;
comp->extra += skb_queue_len(&comp->queue) + 1;
skb_queue_purge(&comp->queue);
}
void hci_conn_tx_dequeue(struct hci_conn *conn)
{
struct tx_queue *comp = &conn->tx_q;
struct sk_buff *skb;
/* If there are tracked skbs, the counted extra go before dequeuing real
* skbs, to keep ordering. When nothing is tracked, the ordering doesn't
* matter so dequeue real skbs first to get rid of them ASAP.
*/
if (comp->extra && (comp->tracked || skb_queue_empty(&comp->queue))) {
comp->extra--;
return;
}
skb = skb_dequeue(&comp->queue);
if (!skb)
return;
if (skb->sk) {
comp->tracked--;
__skb_tstamp_tx(skb, NULL, NULL, skb->sk,
SCM_TSTAMP_COMPLETION);
}
kfree_skb(skb);
}
u8 *hci_conn_key_enc_size(struct hci_conn *conn)
{
if (conn->type == ACL_LINK) {
struct link_key *key;
key = hci_find_link_key(conn->hdev, &conn->dst);
if (!key)
return NULL;
return &key->pin_len;
} else if (conn->type == LE_LINK) {
struct smp_ltk *ltk;
ltk = hci_find_ltk(conn->hdev, &conn->dst, conn->dst_type,
conn->role);
if (!ltk)
return NULL;
return <k->enc_size;
}
return NULL;
}
int hci_ethtool_ts_info(unsigned int index, int sk_proto,
struct kernel_ethtool_ts_info *info)
{
struct hci_dev *hdev;
hdev = hci_dev_get(index);
if (!hdev)
return -ENODEV;
info->so_timestamping =
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
info->phc_index = -1;
info->tx_types = BIT(HWTSTAMP_TX_OFF);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
switch (sk_proto) {
case BTPROTO_ISO:
case BTPROTO_L2CAP:
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
break;
case BTPROTO_SCO:
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
if (hci_dev_test_flag(hdev, HCI_SCO_FLOWCTL))
info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
break;
}
hci_dev_put(hdev);
return 0;
}
]
|
| 24/2 |
2026/05/25 23:44 |
llm |
expert |
0m
Model:
Tokens:
input: 180063
output: 20
thoughts: 1138
Thoughts: **Debugging a Bluetooth HID Race Condition**
Okay, let's break this down. We have a deadlock, and I need to figure out *why*. `hci_get_route` grabs a read lock on the device list, then returns a held `hdev` structure. But crucially, it *doesn't* lock `hdev->lock`! That's our first clue. Later on, `l2cap_chan_connect` tries to `hci_dev_lock(hdev)`, which is really a `mutex_lock(&hdev->lock)`. Bingo – `syz.0.54` is blocked waiting for that mutex. But `khidpd_000d2f19` *already* holds it.
Why? `khidpd_000d2f19` got that lock by calling `l2cap_unregister_user`. That in turn called `hci_dev_lock`. Then, `hidp_session_remove` calls `down_write(&hidp_session_sem)`. After that, `cancel_work_sync(&session->dev_init)` gets called. And `session->dev_init` is `hidp_session_dev_work`.
`hidp_session_dev_work` is the key. It then calls `hidp_session_dev_add`, which calls `hid_add_device`. Here's where it gets interesting because `hid_add_device` eventually calls `device_add` and calls `hid_parse` *before* calling `device_add`. `hid_parse` calls `hid_parse_report`. Does that block? No, it shouldn't. And neither should `device_add`, so there's probably a blocking call *after* `device_add`.
`device_add` triggers `kobject_uevent`, which calls `call_usermodehelper`. Is *that* blocking? It could be. We know `syz-executor/5806` is holding `rtnl_mutex` and blocked in `rtnl_newlink`. Also `kworker/u4:4` is holding `rtnl_mutex` but is blocked in `synchronize_rcu_expedited`. That means somebody's in an RCU critical section somewhere.
Wait... if `kworker/u4:4` holds `rtnl_mutex`, then `syz-executor/5806` is also being blocked from acquiring `rtnl_mutex`. Why is the worker blocked? because something is inside a critical RCU section.
Does `call_usermodehelper` block on `rtnl_mutex` itself? No, not directly. It spawns a user-mode process like `udevd` or `modprobe`. If the system is out of memory, or if *that* process, `udevd`, is blocked on `rtnl_mutex`... Aha! `udevd` frequently grabs `rtnl_mutex` (netlink sockets, ioctls). Could `call_usermodehelper` block indirectly because udevd's blocked? No, but `call_usermodehelper` uses `UMH_WAIT_EXEC`, which only waits for the process to be exec'd, not for it to *finish*.
And `kobject_uevent` *also* broadcasts a uevent via netlink. If `udevd` is blocked, it might not be able to read that netlink socket. No, the `kobject_uevent` doesn't block if the netlink socket is full.
What if `hidp_session_dev_work` *is* blocked in `hid_add_device`? What is the specific call that would block? What about `hid_add_device` calling `hid_hw_start`?
Wait, `hid_add_device` doesn't call `hid_hw_start`. That's done by the HID *driver's* `probe` function, not `hid_add_device`. The driver's `probe` calls `hid_hw_start`. That's it!
If this reproducer creates a HID device, the kernel will attempt to bind a driver. Probably the generic HID driver (`hid-generic`) will be bound. `hid_generic_probe` calls `hid_parse` *and* `hid_hw_start`. `hid_hw_start` then calls `hidp_start`. Does `hid_generic_probe` block? No.
Wait, what if the device matches a *specific* HID driver? Vendor 0x000d, Product 0x2f19. Is there a driver for this
|
| 25/2 |
2026/05/25 23:44 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 5403 lines.
Use more precise expression if possible.
[arch/arm/boot/compressed/head.S=759=__armv3_mpu_cache_on:
--
arch/arm/boot/compressed/head.S-777- @ .... .... .... WC.M
arch/arm/boot/compressed/head.S:778: orr r0, r0, #0x000d @ .... .... .... 11.1
arch/arm/boot/compressed/head.S-779- /* ?? this overwrites the value constructed above? */
--
arch/arm/boot/compressed/head.S=920=__common_mmu_cache_on:
--
arch/arm/boot/compressed/head.S-922-#ifndef DEBUG
arch/arm/boot/compressed/head.S:923: orr r0, r0, #0x000d @ Write buffer, mmu
arch/arm/boot/compressed/head.S-924-#endif
--
arch/arm/boot/compressed/head.S=1159=__armv4_mpu_cache_off:
arch/arm/boot/compressed/head.S-1160- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1161: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1162- mcr p15, 0, r0, c1, c0 @ turn MPU and cache off
--
arch/arm/boot/compressed/head.S=1169=__armv3_mpu_cache_off:
arch/arm/boot/compressed/head.S-1170- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1171: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1172- mcr p15, 0, r0, c1, c0, 0 @ turn MPU and cache off
--
arch/arm/boot/compressed/head.S=1177=__armv4_mmu_cache_off:
--
arch/arm/boot/compressed/head.S-1179- mrc p15, 0, r0, c1, c0
arch/arm/boot/compressed/head.S:1180: bic r0, r0, #0x000d
arch/arm/boot/compressed/head.S-1181- mcr p15, 0, r0, c1, c0 @ turn MMU and cache off
--
arch/arm/boot/dts/broadcom/bcm953012hr.dts=81= partition@0 {
arch/arm/boot/dts/broadcom/bcm953012hr.dts-82- label = "boot";
arch/arm/boot/dts/broadcom/bcm953012hr.dts:83: reg = <0x00000000 0x000d0000>;
arch/arm/boot/dts/broadcom/bcm953012hr.dts-84- };
arch/arm/boot/dts/broadcom/bcm953012hr.dts=85= partition@d000 {
arch/arm/boot/dts/broadcom/bcm953012hr.dts-86- label = "env";
arch/arm/boot/dts/broadcom/bcm953012hr.dts:87: reg = <0x000d0000 0x00030000>;
arch/arm/boot/dts/broadcom/bcm953012hr.dts-88- };
--
arch/arm/boot/dts/broadcom/bcm953012k.dts=91= partition@0 {
arch/arm/boot/dts/broadcom/bcm953012k.dts-92- label = "boot";
arch/arm/boot/dts/broadcom/bcm953012k.dts:93: reg = <0x00000000 0x000d0000>;
arch/arm/boot/dts/broadcom/bcm953012k.dts-94- };
arch/arm/boot/dts/broadcom/bcm953012k.dts=95= partition@d000 {
arch/arm/boot/dts/broadcom/bcm953012k.dts-96- label = "env";
arch/arm/boot/dts/broadcom/bcm953012k.dts:97: reg = <0x000d0000 0x00030000>;
arch/arm/boot/dts/broadcom/bcm953012k.dts-98- };
--
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts=293= partition@0 { /* u-boot */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-294- label = "RedBoot";
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts:295: reg = <0x00000000 0x000d0000>; /* 832KB */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-296- };
--
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts=298= partition@c0000 { /* uImage */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-299- label = "zImage";
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts:300: reg = <0x000d0000 0x002d0000>; /* 2880KB */
arch/arm/boot/dts/marvell/armada-xp-synology-ds414.dts-301- };
--
arch/arm/boot/dts/marvell/dove.dtsi=446= pmu: power-management@d0000 {
--
arch/arm/boot/dts/marvell/dove.dtsi-448- reg = <0xd0000 0x8000>, <0xd8000 0x8000>;
arch/arm/boot/dts/marvell/dove.dtsi:449: ranges = <0x00000000 0x000d0000 0x8000
arch/arm/boot/dts/marvell/dove.dtsi:450: 0x00008000 0x000d8000 0x8000>;
arch/arm/boot/dts/marvell/dove.dtsi-451- interrupts = <33>;
--
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi=223= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi-233- 0x00000004 0x00000006
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi:234: 0x06040202 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-apalis-emc.dtsi-235- 0x7586120a 0x70000f03
--
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi=282= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi-300- 0x06040202
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi:301: 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-jetson-tk1-emc.dtsi-302- 0x7586120a
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=441= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-459- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:460: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-461- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=731= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-749- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:750: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-751- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi=1021= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-1039- 0x06040202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi:1040: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra124-nyan-big-emc.dtsi-1041- 0x7586120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi=270= timing-396000000 {
--
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi-288- 0x06040202
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi:289: 0x000d0709
arch/arm/boot/dts/nvidia/tegra124-nyan-blaze-emc.dtsi-290- 0x7586120a
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=156= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-162- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:163: 0x04040001 0x000d090c 0x71c6120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-164- };
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=516= timing-204000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-543- 0x00000000 0x00000000 0x00000000 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:544: 0x00000000 0x000d0000 0x000d0000 0x000d0000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:545: 0x000d0000 0x000e0220 0x0800201c 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-546- 0x77ffc004 0x01f1f008 0x00000000 0x00000007
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts=552= timing-500000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-582- 0x08000068 0x08000000 0x00000802 0x00064000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts:583: 0x000000b4 0x000d000d 0xa0f10404 0x00000000
arch/arm/boot/dts/nvidia/tegra30-asus-tf201.dts-584- 0x00000000 0x80000fde 0xe0000000 0xff00ff88 >;
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts=185= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-191- 0x00000002 0x00000002 0x00000003 0x00000006
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts:192: 0x06030202 0x000d0709 0x7086120a 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-193- };
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts=250= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-256- 0x00000002 0x00000002 0x00000003 0x00000006
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts:257: 0x06030202 0x000d0709 0x7086120a 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-asus-tf700t.dts-258- };
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2308= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2325- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2326: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2327- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2456= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2473- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2474: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2475- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts=2604= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2621- 0x06030202 /* MC_EMEM_ARB_DA_TURNS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts:2622: 0x000d0709 /* MC_EMEM_ARB_DA_COVERS */
arch/arm/boot/dts/nvidia/tegra30-ouya.dts-2623- 0x7086120a /* MC_EMEM_ARB_MISC0 */
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts=1579= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1585- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts:1586: 0x04040001 0x000d090c 0x7026120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1587- };
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts=1634= timing-400000000 {
--
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1640- 0x00000001 0x00000000 0x00000004 0x00000004
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts:1641: 0x04040001 0x000d090c 0x7026120d 0x001f0000 >;
arch/arm/boot/dts/nvidia/tegra30-pegatron-chagall.dts-1642- };
--
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi=148= partition@d0000 {
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi-149- label = "DDRPARAMS";
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi:150: reg = <0x000d0000 0x00010000>;
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi-151- read-only;
--
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts=107= partition@d0000 {
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts-108- label = "DDRPARAMS";
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts:109: reg = <0x000d0000 0x00010000>;
arch/arm/boot/dts/qcom/qcom-ipq4018-jalapeno.dts-110- read-only;
--
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi=1617= segment@100000 { /* 0x48100000 */
--
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi-1640- <0x000ce000 0x001ce000 0x002000>, /* ap 61 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1641: <0x000d0000 0x001d0000 0x002000>, /* ap 62 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1642: <0x000d2000 0x001d2000 0x002000>, /* ap 63 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1643: <0x000d8000 0x001d8000 0x001000>, /* ap 64 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi:1644: <0x000d9000 0x001d9000 0x001000>, /* ap 65 */
arch/arm/boot/dts/ti/omap/am33xx-l4.dtsi-1645- <0x000a0000 0x001a0000 0x001000>, /* ap 79 */
--
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi=1205= segment@100000 { /* 0x48100000 */
--
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi-1226- <0x000ce000 0x001ce000 0x002000>, /* ap 51 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1227: <0x000d0000 0x001d0000 0x002000>, /* ap 52 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1228: <0x000d2000 0x001d2000 0x002000>, /* ap 53 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1229: <0x000d8000 0x001d8000 0x001000>, /* ap 54 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi:1230: <0x000d9000 0x001d9000 0x001000>, /* ap 55 */
arch/arm/boot/dts/ti/omap/am437x-l4.dtsi-1231- <0x000a0000 0x001a0000 0x001000>, /* ap 67 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi=17= segment@0 { /* 0x4a000000 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-37- <0x000a8000 0x000a8000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:38: <0x000d9000 0x000d9000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:39: <0x000da000 0x000da000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:40: <0x000dd000 0x000dd000 0x001000>, /* ap 19 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:41: <0x000de000 0x000de000 0x001000>, /* ap 20 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-42- <0x000e0000 0x000e0000 0x001000>, /* ap 21 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi=1052= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-1126- <0x000bb000 0x000bb000 0x001000>, /* ap 70 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1127: <0x000d1000 0x000d1000 0x001000>, /* ap 71 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1128: <0x000d2000 0x000d2000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1129: <0x000d5000 0x000d5000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi:1130: <0x000d6000 0x000d6000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/dra7-l4.dtsi-1131- <0x000a2000 0x000a2000 0x001000>, /* ap 75 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi=351= segment@80000 { /* 0x4a080000 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-354- #size-cells = <1>;
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:355: ranges = <0x00059000 0x000d9000 0x001000>, /* ap 13 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:356: <0x0005a000 0x000da000 0x001000>, /* ap 14 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:357: <0x0005b000 0x000db000 0x001000>, /* ap 15 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:358: <0x0005c000 0x000dc000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:359: <0x0005d000 0x000dd000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:360: <0x0005e000 0x000de000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-361- <0x00060000 0x000e0000 0x001000>, /* ap 19 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi=1305= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-1381- <0x000bb000 0x000bb000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1382: <0x000d1000 0x000d1000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1383: <0x000d2000 0x000d2000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1384: <0x000d5000 0x000d5000 0x001000>, /* ap 75 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi:1385: <0x000d6000 0x000d6000 0x001000>, /* ap 76 */
arch/arm/boot/dts/ti/omap/omap4-l4.dtsi-1386- <0x000a2000 0x000a2000 0x001000>, /* ap 79 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi=396= segment@80000 { /* 0x4a080000 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-399- #size-cells = <1>;
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:400: ranges = <0x00059000 0x000d9000 0x001000>, /* ap 13 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:401: <0x0005a000 0x000da000 0x001000>, /* ap 14 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:402: <0x0005b000 0x000db000 0x001000>, /* ap 15 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:403: <0x0005c000 0x000dc000 0x001000>, /* ap 16 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:404: <0x0005d000 0x000dd000 0x001000>, /* ap 17 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:405: <0x0005e000 0x000de000 0x001000>, /* ap 18 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-406- <0x00060000 0x000e0000 0x001000>, /* ap 19 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi=969= segment@0 { /* 0x48000000 */
--
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-1044- <0x000bb000 0x000bb000 0x001000>, /* ap 70 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1045: <0x000d1000 0x000d1000 0x001000>, /* ap 71 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1046: <0x000d2000 0x000d2000 0x001000>, /* ap 72 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1047: <0x000d5000 0x000d5000 0x001000>, /* ap 73 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi:1048: <0x000d6000 0x000d6000 0x001000>, /* ap 74 */
arch/arm/boot/dts/ti/omap/omap5-l4.dtsi-1049- <0x000a2000 0x000a2000 0x001000>, /* ap 75 */
--
arch/arm/probes/kprobes/checkers-arm.c=13=static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
--
arch/arm/probes/kprobes/checkers-arm.c-38- DECODE_OR (0x0e5000bf, 0x000000bd),
arch/arm/probes/kprobes/checkers-arm.c:39: DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),
arch/arm/probes/kprobes/checkers-arm.c-40-
--
arch/arm/vfp/vfpinstr.h-45-#define FEXT_FTOUIZ (0x000c0080)
arch/arm/vfp/vfpinstr.h:46:#define FEXT_FTOSI (0x000d0000)
arch/arm/vfp/vfpinstr.h:47:#define FEXT_FTOSIZ (0x000d0080)
arch/arm/vfp/vfpinstr.h-48-
--
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi=446= gpio_hsls: gpio@d0000 {
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi-447- compatible = "brcm,iproc-gpio";
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi:448: reg = <0x000d0000 0x864>;
arch/arm64/boot/dts/broadcom/stingray/stingray.dtsi-449- ngpios = <151>;
--
arch/m68k/ifpsp060/fpsp.sa-416- .long 0xff68ff44,0x206eff44,0x58aeff44,0x61ffffff
arch/m68k/ifpsp060/fpsp.sa:417: .long 0xe7f82d40,0xff400800,0x000d6600,0x0106e9c0
arch/m68k/ifpsp060/fpsp.sa-418- .long 0x04c36622,0x0c6e401e,0xff6c661a,0xf23c9000
--
arch/mips/include/asm/cpu.h-264-#define PRID_REV_LOONGSON3A_R2_1 0x000c
arch/mips/include/asm/cpu.h:265:#define PRID_REV_LOONGSON3A_R3_1 0x000d
arch/mips/include/asm/cpu.h-266-
--
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h-14-#define CP0_DCACHE_ERR_REG $27, 1
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h:15:#define CP0_PRID_OCTEON_PASS1 0x000d0000
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h:16:#define CP0_PRID_OCTEON_CN30XX 0x000d0200
arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h-17-
--
arch/mips/include/asm/octeon/octeon-model.h-76-
arch/mips/include/asm/octeon/octeon-model.h:77:#define OCTEON_CNF75XX_PASS1_0 0x000d9800
arch/mips/include/asm/octeon/octeon-model.h-78-#define OCTEON_CNF75XX (OCTEON_CNF75XX_PASS1_0 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-80-
arch/mips/include/asm/octeon/octeon-model.h:81:#define OCTEON_CN73XX_PASS1_0 0x000d9700
arch/mips/include/asm/octeon/octeon-model.h:82:#define OCTEON_CN73XX_PASS1_1 0x000d9701
arch/mips/include/asm/octeon/octeon-model.h-83-#define OCTEON_CN73XX (OCTEON_CN73XX_PASS1_0 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-86-
arch/mips/include/asm/octeon/octeon-model.h:87:#define OCTEON_CN70XX_PASS1_0 0x000d9600
arch/mips/include/asm/octeon/octeon-model.h:88:#define OCTEON_CN70XX_PASS1_1 0x000d9601
arch/mips/include/asm/octeon/octeon-model.h:89:#define OCTEON_CN70XX_PASS1_2 0x000d9602
arch/mips/include/asm/octeon/octeon-model.h-90-
arch/mips/include/asm/octeon/octeon-model.h:91:#define OCTEON_CN70XX_PASS2_0 0x000d9608
arch/mips/include/asm/octeon/octeon-model.h-92-
--
arch/mips/include/asm/octeon/octeon-model.h-100-
arch/mips/include/asm/octeon/octeon-model.h:101:#define OCTEON_CN78XX_PASS1_0 0x000d9500
arch/mips/include/asm/octeon/octeon-model.h:102:#define OCTEON_CN78XX_PASS1_1 0x000d9501
arch/mips/include/asm/octeon/octeon-model.h:103:#define OCTEON_CN78XX_PASS2_0 0x000d9508
arch/mips/include/asm/octeon/octeon-model.h-104-
--
arch/mips/include/asm/octeon/octeon-model.h-110-
arch/mips/include/asm/octeon/octeon-model.h:111:#define OCTEON_CN76XX (0x000d9540 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-112-
--
arch/mips/include/asm/octeon/octeon-model.h-115- */
arch/mips/include/asm/octeon/octeon-model.h:116:#define OCTEON_CNF71XX_PASS1_0 0x000d9400
arch/mips/include/asm/octeon/octeon-model.h:117:#define OCTEON_CNF71XX_PASS1_1 0x000d9401
arch/mips/include/asm/octeon/octeon-model.h-118-
--
arch/mips/include/asm/octeon/octeon-model.h-124- */
arch/mips/include/asm/octeon/octeon-model.h:125:#define OCTEON_CN68XX_PASS1_0 0x000d9100
arch/mips/include/asm/octeon/octeon-model.h:126:#define OCTEON_CN68XX_PASS1_1 0x000d9101
arch/mips/include/asm/octeon/octeon-model.h:127:#define OCTEON_CN68XX_PASS1_2 0x000d9102
arch/mips/include/asm/octeon/octeon-model.h:128:#define OCTEON_CN68XX_PASS2_0 0x000d9108
arch/mips/include/asm/octeon/octeon-model.h:129:#define OCTEON_CN68XX_PASS2_1 0x000d9109
arch/mips/include/asm/octeon/octeon-model.h:130:#define OCTEON_CN68XX_PASS2_2 0x000d910a
arch/mips/include/asm/octeon/octeon-model.h-131-
--
arch/mips/include/asm/octeon/octeon-model.h-138-
arch/mips/include/asm/octeon/octeon-model.h:139:#define OCTEON_CN66XX_PASS1_0 0x000d9200
arch/mips/include/asm/octeon/octeon-model.h:140:#define OCTEON_CN66XX_PASS1_2 0x000d9202
arch/mips/include/asm/octeon/octeon-model.h-141-
--
arch/mips/include/asm/octeon/octeon-model.h-144-
arch/mips/include/asm/octeon/octeon-model.h:145:#define OCTEON_CN63XX_PASS1_0 0x000d9000
arch/mips/include/asm/octeon/octeon-model.h:146:#define OCTEON_CN63XX_PASS1_1 0x000d9001
arch/mips/include/asm/octeon/octeon-model.h:147:#define OCTEON_CN63XX_PASS1_2 0x000d9002
arch/mips/include/asm/octeon/octeon-model.h:148:#define OCTEON_CN63XX_PASS2_0 0x000d9008
arch/mips/include/asm/octeon/octeon-model.h:149:#define OCTEON_CN63XX_PASS2_1 0x000d9009
arch/mips/include/asm/octeon/octeon-model.h:150:#define OCTEON_CN63XX_PASS2_2 0x000d900a
arch/mips/include/asm/octeon/octeon-model.h-151-
--
arch/mips/include/asm/octeon/octeon-model.h-158-
arch/mips/include/asm/octeon/octeon-model.h:159:#define OCTEON_CN61XX_PASS1_0 0x000d9300
arch/mips/include/asm/octeon/octeon-model.h:160:#define OCTEON_CN61XX_PASS1_1 0x000d9301
arch/mips/include/asm/octeon/octeon-model.h-161-
--
arch/mips/include/asm/octeon/octeon-model.h-170- */
arch/mips/include/asm/octeon/octeon-model.h:171:#define OCTEON_CN58XX_PASS1_0 0x000d0300
arch/mips/include/asm/octeon/octeon-model.h:172:#define OCTEON_CN58XX_PASS1_1 0x000d0301
arch/mips/include/asm/octeon/octeon-model.h:173:#define OCTEON_CN58XX_PASS1_2 0x000d0303
arch/mips/include/asm/octeon/octeon-model.h:174:#define OCTEON_CN58XX_PASS2_0 0x000d0308
arch/mips/include/asm/octeon/octeon-model.h:175:#define OCTEON_CN58XX_PASS2_1 0x000d0309
arch/mips/include/asm/octeon/octeon-model.h:176:#define OCTEON_CN58XX_PASS2_2 0x000d030a
arch/mips/include/asm/octeon/octeon-model.h:177:#define OCTEON_CN58XX_PASS2_3 0x000d030b
arch/mips/include/asm/octeon/octeon-model.h-178-
--
arch/mips/include/asm/octeon/octeon-model.h-184-
arch/mips/include/asm/octeon/octeon-model.h:185:#define OCTEON_CN56XX_PASS1_0 0x000d0400
arch/mips/include/asm/octeon/octeon-model.h:186:#define OCTEON_CN56XX_PASS1_1 0x000d0401
arch/mips/include/asm/octeon/octeon-model.h:187:#define OCTEON_CN56XX_PASS2_0 0x000d0408
arch/mips/include/asm/octeon/octeon-model.h:188:#define OCTEON_CN56XX_PASS2_1 0x000d0409
arch/mips/include/asm/octeon/octeon-model.h-189-
--
arch/mips/include/asm/octeon/octeon-model.h-207-
arch/mips/include/asm/octeon/octeon-model.h:208:#define OCTEON_CN50XX_PASS1_0 0x000d0600
arch/mips/include/asm/octeon/octeon-model.h-209-
--
arch/mips/include/asm/octeon/octeon-model.h-218-
arch/mips/include/asm/octeon/octeon-model.h:219:#define OCTEON_CN52XX_PASS1_0 0x000d0700
arch/mips/include/asm/octeon/octeon-model.h:220:#define OCTEON_CN52XX_PASS2_0 0x000d0708
arch/mips/include/asm/octeon/octeon-model.h-221-
--
arch/mips/include/asm/octeon/octeon-model.h-230- */
arch/mips/include/asm/octeon/octeon-model.h:231:#define OCTEON_CN38XX_PASS1 0x000d0000
arch/mips/include/asm/octeon/octeon-model.h:232:#define OCTEON_CN38XX_PASS2 0x000d0001
arch/mips/include/asm/octeon/octeon-model.h:233:#define OCTEON_CN38XX_PASS3 0x000d0003
arch/mips/include/asm/octeon/octeon-model.h-234-#define OCTEON_CN38XX (OCTEON_CN38XX_PASS3 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-240-/* The OCTEON_CN31XX matches CN31XX models and the CN3020 */
arch/mips/include/asm/octeon/octeon-model.h:241:#define OCTEON_CN31XX_PASS1 0x000d0100
arch/mips/include/asm/octeon/octeon-model.h:242:#define OCTEON_CN31XX_PASS1_1 0x000d0102
arch/mips/include/asm/octeon/octeon-model.h-243-#define OCTEON_CN31XX (OCTEON_CN31XX_PASS1 | OM_IGNORE_REVISION)
--
arch/mips/include/asm/octeon/octeon-model.h-249- */
arch/mips/include/asm/octeon/octeon-model.h:250:#define OCTEON_CN30XX_PASS1 0x000d0200
arch/mips/include/asm/octeon/octeon-model.h:251:#define OCTEON_CN30XX_PASS1_1 0x000d0202
arch/mips/include/asm/octeon/octeon-model.h-252-#define OCTEON_CN30XX (OCTEON_CN30XX_PASS1 | OM_IGNORE_REVISION)
arch/mips/include/asm/octeon/octeon-model.h-253-
arch/mips/include/asm/octeon/octeon-model.h:254:#define OCTEON_CN3005_PASS1 (0x000d0210 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:255:#define OCTEON_CN3005_PASS1_0 (0x000d0210 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:256:#define OCTEON_CN3005_PASS1_1 (0x000d0212 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-257-#define OCTEON_CN3005 (OCTEON_CN3005_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-258-
arch/mips/include/asm/octeon/octeon-model.h:259:#define OCTEON_CN3010_PASS1 (0x000d0200 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:260:#define OCTEON_CN3010_PASS1_0 (0x000d0200 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:261:#define OCTEON_CN3010_PASS1_1 (0x000d0202 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-262-#define OCTEON_CN3010 (OCTEON_CN3010_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-263-
arch/mips/include/asm/octeon/octeon-model.h:264:#define OCTEON_CN3020_PASS1 (0x000d0110 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:265:#define OCTEON_CN3020_PASS1_0 (0x000d0110 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h:266:#define OCTEON_CN3020_PASS1_1 (0x000d0112 | OM_CHECK_SUBMODEL)
arch/mips/include/asm/octeon/octeon-model.h-267-#define OCTEON_CN3020 (OCTEON_CN3020_PASS1 | OM_IGNORE_REVISION | OM_CHECK_SUBMODEL)
--
arch/mips/include/asm/uprobes.h=19=typedef u32 uprobe_opcode_t;
--
arch/mips/include/asm/uprobes.h-28-
arch/mips/include/asm/uprobes.h:29:#define UPROBE_BRK_UPROBE 0x000d000d /* break 13 */
arch/mips/include/asm/uprobes.h-30-#define UPROBE_BRK_UPROBE_XOL 0x000e000d /* break 14 */
--
arch/mips/kernel/octeon_switch.S-149- sd t0, OCTEON_CP2_AES_KEYLEN(a0)
arch/mips/kernel/octeon_switch.S:150: li v1, 0x000d0000 /* This is the processor ID of Octeon Pass1 */
arch/mips/kernel/octeon_switch.S-151- sd t1, OCTEON_CP2_AES_RESULT(a0)
--
arch/mips/kernel/octeon_switch.S-315- dmtc2 t0, 0x0110
arch/mips/kernel/octeon_switch.S:316: li v0, 0x000d0000 /* This is the processor ID of Octeon Pass1 */
arch/mips/kernel/octeon_switch.S-317- dmtc2 t1, 0x0100
--
arch/parisc/kernel/perf_images.h=2083=static uint32_t cuda_images[][PCXW_IMAGE_SIZE/sizeof(uint32_t)] __ro_after_init = {
--
arch/parisc/kernel/perf_images.h-2296- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2297: 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff,
arch/parisc/kernel/perf_images.h-2298- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2342- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2343: 0x000dbfff, 0xffffff00, 0x00000000, 0x0f0fffff,
arch/parisc/kernel/perf_images.h-2344- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2481- 0x00005555, 0xff80bf8b, 0xab030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2482: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-2483- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-2909- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:2910: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-2911- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3003- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3004: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3005- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3049- 0x000000ff, 0xff00bfdf, 0x03030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3050: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3051- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/parisc/kernel/perf_images.h-3096- 0x0000ff00, 0xff83bf03, 0xdf030303, 0x03030000,
arch/parisc/kernel/perf_images.h:3097: 0x000dbfff, 0xffffff00, 0x00000000, 0x000fffff,
arch/parisc/kernel/perf_images.h-3098- 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff000,
--
arch/powerpc/include/asm/cpm1.h-42-#define CPM_CR_CH_SCC4 ((ushort)0x000c)
arch/powerpc/include/asm/cpm1.h:43:#define CPM_CR_CH_SMC2 ((ushort)0x000d) /* SMC2 / DSP2 */
arch/powerpc/include/asm/cpm1.h-44-
--
arch/powerpc/include/asm/mpic.h-51-#define MPIC_GREG_IPI_VECTOR_PRI_2 0x000c0
arch/powerpc/include/asm/mpic.h:52:#define MPIC_GREG_IPI_VECTOR_PRI_3 0x000d0
arch/powerpc/include/asm/mpic.h-53-#define MPIC_GREG_IPI_STRIDE 0x10
--
arch/powerpc/include/asm/ps3av.h-138-#define PS3AV_CMD_VIDEO_VID_1080P_50HZ 0x000c
arch/powerpc/include/asm/ps3av.h:139:#define PS3AV_CMD_VIDEO_VID_WXGA 0x000d
arch/powerpc/include/asm/ps3av.h-140-#define PS3AV_CMD_VIDEO_VID_SXGA 0x000e
--
arch/powerpc/include/asm/ps3av.h=670=extern u8 ps3av_mode_cs_info[];
--
arch/powerpc/include/asm/ps3av.h-685-#define PS3AV_STATUS_UNSUPPORTED_COMMAND 0x000c /* unsupported cid */
arch/powerpc/include/asm/ps3av.h:686:#define PS3AV_STATUS_BUFFER_OVERFLOW 0x000d /* write buffer overflow */
arch/powerpc/include/asm/ps3av.h-687-#define PS3AV_STATUS_INVALID_VIDEO_PARAM 0x000e /* invalid video param */
--
arch/s390/net/bpf_jit_comp.c=859=static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
--
arch/s390/net/bpf_jit_comp.c-1186- /* dsg %w0,<d(imm)>(%l) */
arch/s390/net/bpf_jit_comp.c:1187: EMIT6_DISP_LH(0xe3000000, 0x000d, REG_W0, REG_0,
arch/s390/net/bpf_jit_comp.c-1188- REG_L, EMIT_CONST_U64(imm));
--
arch/s390/net/bpf_jit_comp.c-1328- /* sllg %dst,%dst,0(%src) */
arch/s390/net/bpf_jit_comp.c:1329: EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, src_reg, 0);
arch/s390/net/bpf_jit_comp.c-1330- break;
--
arch/s390/net/bpf_jit_comp.c-1341- /* sllg %dst,%dst,imm(%r0) */
arch/s390/net/bpf_jit_comp.c:1342: EMIT6_DISP_LH(0xeb000000, 0x000d, dst_reg, dst_reg, REG_0, imm);
arch/s390/net/bpf_jit_comp.c-1343- break;
--
arch/s390/net/bpf_jit_comp.c-1855- /* sllg %r1,%r1,3: %r1 *= 8 */
arch/s390/net/bpf_jit_comp.c:1856: EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, REG_1, REG_0, 3);
arch/s390/net/bpf_jit_comp.c-1857- /* ltg %r1,prog(%b2,%r1) */
--
arch/sparc/include/uapi/asm/socket.h-40-
arch/sparc/include/uapi/asm/socket.h:41:#define SO_BINDTODEVICE 0x000d
arch/sparc/include/uapi/asm/socket.h-42-
--
arch/x86/crypto/camellia_glue.c=224=__visible const u64 camellia_sp03303033[256] = {
--
arch/x86/crypto/camellia_glue.c-239- 0x006e6e006e006e6eULL, 0x00afaf00af00afafULL, 0x002f2f002f002f2fULL,
arch/x86/crypto/camellia_glue.c:240: 0x00e2e200e200e2e2ULL, 0x0085850085008585ULL, 0x000d0d000d000d0dULL,
arch/x86/crypto/camellia_glue.c-241- 0x0053530053005353ULL, 0x00f0f000f000f0f0ULL, 0x009c9c009c009c9cULL,
--
arch/x86/crypto/camellia_glue.c=402=__visible const u64 camellia_sp02220222[256] = {
--
arch/x86/crypto/camellia_glue.c-412- 0x009c9c9c009c9c9cULL, 0x003a3a3a003a3a3aULL, 0x00cacaca00cacacaULL,
arch/x86/crypto/camellia_glue.c:413: 0x0025252500252525ULL, 0x007b7b7b007b7b7bULL, 0x000d0d0d000d0d0dULL,
arch/x86/crypto/camellia_glue.c-414- 0x0071717100717171ULL, 0x005f5f5f005f5f5fULL, 0x001f1f1f001f1f1fULL,
--
arch/x86/events/intel/knc.c=22=static const u64 __initconst knc_hw_cache_event_ids
--
arch/x86/events/intel/knc.c-92- [ C(RESULT_ACCESS) ] = 0x000c, /* CODE_READ */
arch/x86/events/intel/knc.c:93: [ C(RESULT_MISS) ] = 0x000d, /* CODE_PAGE_WALK */
arch/x86/events/intel/knc.c-94- },
--
arch/x86/kernel/cpu/amd.c=92=static void init_amd_k5(struct cpuinfo_x86 *c)
--
arch/x86/kernel/cpu/amd.c-96- * General Systems BIOSen alias the cpu frequency registers
arch/x86/kernel/cpu/amd.c:97: * of the Elan at 0x000df000. Unfortunately, one of the Linux
arch/x86/kernel/cpu/amd.c-98- * drivers subsequently pokes it, and changes the CPU speed.
--
]
]
|
| 26/2 |
2026/05/25 23:44 |
llm |
expert |
0m
Model:
Tokens:
input: 201374
output: 20
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating a Hexadecimal Signature**
Alright, I'm encountering something, and my initial instinct is to dig into it. The immediate impulse is to look up that hex value, `0x2f19`. That format suggests it's likely a memory address, an opcode, a data value, or perhaps part of a file signature. My brain's already making quick associations based on context. Is this from a memory dump? A network capture? A disassembly listing? The possibilities are numerous, and the first step is always to ground it in reality. I need to figure out *where* I'm seeing this value to understand its significance. Is it within a known malware sample, a specific protocol, a targeted system, a file header, or something else entirely? The hunt begins – Google, strings, IDA Pro, Wireshark, all are now immediately in play. I'm already anticipating the need to cross-reference it with known databases, signature repositories, and perhaps even my own personal notes on previously encountered patterns. Let's see what we can find.
|
| 27/2 |
2026/05/25 23:44 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h-12424-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h:12425:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h-12426-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h-11533-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h:11534:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h-11535-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h-12289-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h:12290:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h-12291-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h-13020-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h:13021:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h-13022-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h-11698-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h:11699:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h-11700-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h-11707-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h:11708:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h-11709-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h-10432-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h:10433:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h-10434-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h-10411-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h:10412:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h-10413-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h-12509-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h:12510:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h-12511-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h-11888-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h:11889:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h-11890-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h-7167-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h:7168:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h-7169-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h-85-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h:86:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h-87-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h-72-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h:73:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h-74-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h-89-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h:90:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h-91-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
sound/soc/codecs/rt715-sdca-sdw.h=13=static const struct reg_default rt715_reg_defaults_sdca[] = {
--
sound/soc/codecs/rt715-sdca-sdw.h-66- { 0x2f18, 0x00 },
sound/soc/codecs/rt715-sdca-sdw.h:67: { 0x2f19, 0x03 },
sound/soc/codecs/rt715-sdca-sdw.h-68- { 0x2f1a, 0x00 },
]
|
| 28/2 |
2026/05/25 23:44 |
llm |
expert |
0m
Model:
Tokens:
input: 204253
output: 19
thoughts: 6004
Thoughts: **Analyzing a Bluetooth HID Device Deadlock**
Okay, here's the breakdown of what I've pieced together from this trace. It looks like we have a nasty deadlock situation involving Bluetooth HID devices and the network stack, likely triggered by a race condition or a specific interaction during device initialization.
The starting point appears to be a random number, `0x2f19`, but it's not relevant. The crucial bit seems to be the interaction between `hid_add_device`, `hidp_session_dev_work`, and the Bluetooth L2CAP connection setup. I see that `hid_add_device` calls `device_add`, which can potentially trigger a user-mode helper (like `udevd`). `udevd` can be blocked on `rtnl_mutex`.
The critical observation here is that `khidpd_000d2f19` is blocked in `cancel_work_sync`, *holding* `&hdev->lock`. At the same time, `syz.0.54` is also blocked in `l2cap_chan_connect`, trying to acquire the *same* `&hdev->lock`. This immediately suggests a locking order issue. Further, `syz.0.54` holds `&hdev->lock`, the Bluetooth device lock.
I see that `khidpd_000d2f19` holds `&hdev->lock` in `l2cap_unregister_user`, implying it's in the process of unregistering the Bluetooth connection and associated Bluetooth device.
Digging further, I observe that `kworker/u4:4` is blocked in `synchronize_rcu_expedited` *holding* `rtnl_mutex`. If I recall correctly, `synchronize_rcu_expedited` waits for a grace period, which occurs after RCU read-side critical sections are completed. The important point here is that if a CPU is stuck in an RCU read-side critical section, this will trigger an RCU stall warning. However, I didn't see an RCU stall warning, but a hung task warning! This means there's some issue with `kworker/u4:4` because it is holding `rtnl_mutex` and blocked, waiting for `synchronize_rcu_expedited` to complete, which is waiting for RCU grace period. This also means that some code path might be blocking RCU grace period.
Let's trace: I realize `syz.0.54` is blocked inside `l2cap_chan_connect`. Inside `l2cap_chan_connect` is `hci_dev_lock(hdev)` which is trying to acquire the `&hdev->lock`. This is where `syz.0.54` is blocked.
This `kworker/u4:4` is blocked because it is holding `rtnl_mutex`!
`khidpd_000d2f19` holds `&hdev->lock` while `cancel_work_sync` is called.
The key is that `khidpd_000d2f19` holds `&hdev->lock` *and* is blocked in `cancel_work_sync`. This blocking is a result of it trying to cancel `session->dev_init` which is running in `hidp_session_dev_work`.
`hidp_session_dev_work` runs in `kworker/0:3`. Now it all makes sense.
Here's the likely scenario:
1. `khidpd_000d2f19` acquires `&hdev->lock` in `l2cap_unregister_user` because something needs to be unregistered.
2. `hidp_session_dev_work` is running on `kworker/0:3` and starts to initialize a HID device with `hid_add_device`. The device is being added by another task at the same time and is blocked, leading to a race condition.
3. `hidp_session_dev_work` is running.
4. `kworker/u4:4` holds `rtnl_mutex`. This is important because a user-mode helper *might* (like `modprobe`) acquire `rtnl_mutex` and block the RCU grace period.
5. `khidpd_000d2f19` starts running, then gets to `cancel_work_sync`.
6. `syz.0.54` runs and hits `hci_dev_lock`, waiting for `&hdev->lock`.
7. Now the deadlock happens: `khidpd_00
|
| 29/2 |
2026/05/25 23:45 |
tool |
grepper |
0m
Args: map[Expression:rtnl_newlink]
Results: map[Output:drivers/infiniband/ulp/ipoib/ipoib_netlink.c=100=static int ipoib_new_child_link(struct net_device *dev,
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:101: struct rtnl_newlink_params *params,
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-102- struct netlink_ext_ack *extack)
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-103-{
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:104: struct net *link_net = rtnl_newlink_link_net(params);
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-105- struct nlattr **data = params->data;
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c=52=static bool is_child_unique(struct ipoib_dev_priv *ppriv,
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-93- * If (ndev->reg_state == NETREG_UNINITIALIZED) then it is up to the caller to
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:94: * free the net_device (just as rtnl_newlink does) otherwise the net_device
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-95- * will be freed when the rtnl is unlocked.
--
drivers/net/amt.c=3164=static int amt_newlink(struct net_device *dev,
drivers/net/amt.c:3165: struct rtnl_newlink_params *params,
drivers/net/amt.c-3166- struct netlink_ext_ack *extack)
drivers/net/amt.c-3167-{
drivers/net/amt.c:3168: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/amt.c-3169- struct amt_dev *amt = netdev_priv(dev);
--
drivers/net/bareudp.c=701=static int bareudp_newlink(struct net_device *dev,
drivers/net/bareudp.c:702: struct rtnl_newlink_params *params,
drivers/net/bareudp.c-703- struct netlink_ext_ack *extack)
drivers/net/bareudp.c-704-{
drivers/net/bareudp.c:705: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/bareudp.c-706- struct nlattr **data = params->data;
--
drivers/net/bonding/bond_netlink.c=567=static int bond_newlink(struct net_device *bond_dev,
drivers/net/bonding/bond_netlink.c:568: struct rtnl_newlink_params *params,
drivers/net/bonding/bond_netlink.c-569- struct netlink_ext_ack *extack)
--
drivers/net/can/dev/netlink.c=627=static int can_newlink(struct net_device *dev,
drivers/net/can/dev/netlink.c:628: struct rtnl_newlink_params *params,
drivers/net/can/dev/netlink.c-629- struct netlink_ext_ack *extack)
--
drivers/net/can/vxcan.c=175=static int vxcan_newlink(struct net_device *dev,
drivers/net/can/vxcan.c:176: struct rtnl_newlink_params *params,
drivers/net/can/vxcan.c-177- struct netlink_ext_ack *extack)
drivers/net/can/vxcan.c-178-{
drivers/net/can/vxcan.c:179: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/can/vxcan.c-180- struct nlattr **data = params->data;
--
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c=120=static int rmnet_newlink(struct net_device *dev,
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c:121: struct rtnl_newlink_params *params,
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-122- struct netlink_ext_ack *extack)
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-123-{
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c:124: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-125- u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
--
drivers/net/geneve.c=1650=static int geneve_newlink(struct net_device *dev,
drivers/net/geneve.c:1651: struct rtnl_newlink_params *params,
drivers/net/geneve.c-1652- struct netlink_ext_ack *extack)
drivers/net/geneve.c-1653-{
drivers/net/geneve.c:1654: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/geneve.c-1655- struct nlattr **data = params->data;
--
drivers/net/gtp.c=1465=static int gtp_newlink(struct net_device *dev,
drivers/net/gtp.c:1466: struct rtnl_newlink_params *params,
drivers/net/gtp.c-1467- struct netlink_ext_ack *extack)
drivers/net/gtp.c-1468-{
drivers/net/gtp.c:1469: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/gtp.c-1470- struct nlattr **data = params->data;
--
drivers/net/ipvlan/ipvlan.h=167=void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
drivers/net/ipvlan/ipvlan.h-168- unsigned int len, bool success, bool mcast);
drivers/net/ipvlan/ipvlan.h:169:int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvlan.h-170- struct netlink_ext_ack *extack);
--
drivers/net/ipvlan/ipvlan_main.c=514=static int ipvlan_nl_fillinfo(struct sk_buff *skb,
--
drivers/net/ipvlan/ipvlan_main.c-535-
drivers/net/ipvlan/ipvlan_main.c:536:int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvlan_main.c-537- struct netlink_ext_ack *extack)
drivers/net/ipvlan/ipvlan_main.c-538-{
drivers/net/ipvlan/ipvlan_main.c:539: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ipvlan/ipvlan_main.c-540- struct ipvl_dev *ipvlan = netdev_priv(dev);
--
drivers/net/ipvlan/ipvtap.c=76=static int ipvtap_newlink(struct net_device *dev,
drivers/net/ipvlan/ipvtap.c:77: struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvtap.c-78- struct netlink_ext_ack *extack)
--
drivers/net/macsec.c=4173=static int macsec_newlink(struct net_device *dev,
drivers/net/macsec.c:4174: struct rtnl_newlink_params *params,
drivers/net/macsec.c-4175- struct netlink_ext_ack *extack)
drivers/net/macsec.c-4176-{
drivers/net/macsec.c:4177: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/macsec.c-4178- struct macsec_dev *macsec = macsec_priv(dev);
--
drivers/net/macvlan.c=1444=int macvlan_common_newlink(struct net_device *dev,
drivers/net/macvlan.c:1445: struct rtnl_newlink_params *params,
drivers/net/macvlan.c-1446- struct netlink_ext_ack *extack)
drivers/net/macvlan.c-1447-{
drivers/net/macvlan.c:1448: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/macvlan.c-1449- struct macvlan_dev *vlan = netdev_priv(dev);
--
drivers/net/macvlan.c=1572=static int macvlan_newlink(struct net_device *dev,
drivers/net/macvlan.c:1573: struct rtnl_newlink_params *params,
drivers/net/macvlan.c-1574- struct netlink_ext_ack *extack)
--
drivers/net/macvtap.c=80=static int macvtap_newlink(struct net_device *dev,
drivers/net/macvtap.c:81: struct rtnl_newlink_params *params,
drivers/net/macvtap.c-82- struct netlink_ext_ack *extack)
--
drivers/net/netkit.c=329=static int netkit_new_link(struct net_device *dev,
drivers/net/netkit.c:330: struct rtnl_newlink_params *params,
drivers/net/netkit.c-331- struct netlink_ext_ack *extack)
drivers/net/netkit.c-332-{
drivers/net/netkit.c:333: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/netkit.c-334- enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT;
--
drivers/net/ovpn/main.c=177=static int ovpn_newlink(struct net_device *dev,
drivers/net/ovpn/main.c:178: struct rtnl_newlink_params *params,
drivers/net/ovpn/main.c-179- struct netlink_ext_ack *extack)
--
drivers/net/pfcp.c=187=static int pfcp_newlink(struct net_device *dev,
drivers/net/pfcp.c:188: struct rtnl_newlink_params *params,
drivers/net/pfcp.c-189- struct netlink_ext_ack *extack)
drivers/net/pfcp.c-190-{
drivers/net/pfcp.c:191: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/pfcp.c-192- struct pfcp_dev *pfcp = netdev_priv(dev);
--
drivers/net/ppp/ppp_generic.c=1313=static int ppp_nl_newlink(struct net_device *dev,
drivers/net/ppp/ppp_generic.c:1314: struct rtnl_newlink_params *params,
drivers/net/ppp/ppp_generic.c-1315- struct netlink_ext_ack *extack)
drivers/net/ppp/ppp_generic.c-1316-{
drivers/net/ppp/ppp_generic.c:1317: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ppp/ppp_generic.c-1318- struct nlattr **data = params->data;
--
drivers/net/team/team_core.c=2222=static int team_newlink(struct net_device *dev,
drivers/net/team/team_core.c:2223: struct rtnl_newlink_params *params,
drivers/net/team/team_core.c-2224- struct netlink_ext_ack *extack)
--
drivers/net/veth.c=1805=static int veth_newlink(struct net_device *dev,
drivers/net/veth.c:1806: struct rtnl_newlink_params *params,
drivers/net/veth.c-1807- struct netlink_ext_ack *extack)
drivers/net/veth.c-1808-{
drivers/net/veth.c:1809: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/veth.c-1810- struct nlattr **data = params->data;
--
drivers/net/vrf.c=1677=static int vrf_newlink(struct net_device *dev,
drivers/net/vrf.c:1678: struct rtnl_newlink_params *params,
drivers/net/vrf.c-1679- struct netlink_ext_ack *extack)
--
drivers/net/vxlan/vxlan_core.c=4347=static int vxlan_newlink(struct net_device *dev,
drivers/net/vxlan/vxlan_core.c:4348: struct rtnl_newlink_params *params,
drivers/net/vxlan/vxlan_core.c-4349- struct netlink_ext_ack *extack)
drivers/net/vxlan/vxlan_core.c-4350-{
drivers/net/vxlan/vxlan_core.c:4351: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/vxlan/vxlan_core.c-4352- struct nlattr **data = params->data;
--
drivers/net/wireguard/device.c=310=static int wg_newlink(struct net_device *dev,
drivers/net/wireguard/device.c:311: struct rtnl_newlink_params *params,
drivers/net/wireguard/device.c-312- struct netlink_ext_ack *extack)
drivers/net/wireguard/device.c-313-{
drivers/net/wireguard/device.c:314: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/wireguard/device.c-315- struct wg_device *wg = netdev_priv(dev);
--
drivers/net/wireless/virtual/virt_wifi.c=522=static int virt_wifi_newlink(struct net_device *dev,
drivers/net/wireless/virtual/virt_wifi.c:523: struct rtnl_newlink_params *params,
drivers/net/wireless/virtual/virt_wifi.c-524- struct netlink_ext_ack *extack)
--
drivers/net/wireless/virtual/virt_wifi.c-526- struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
drivers/net/wireless/virtual/virt_wifi.c:527: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/wireless/virtual/virt_wifi.c-528- struct nlattr **tb = params->tb;
--
drivers/net/wwan/wwan_core.c=935=static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[],
--
drivers/net/wwan/wwan_core.c-969-
drivers/net/wwan/wwan_core.c:970:static int wwan_rtnl_newlink(struct net_device *dev,
drivers/net/wwan/wwan_core.c:971: struct rtnl_newlink_params *params,
drivers/net/wwan/wwan_core.c-972- struct netlink_ext_ack *extack)
--
drivers/net/wwan/wwan_core.c=1050=static struct rtnl_link_ops wwan_rtnl_link_ops __read_mostly = {
--
drivers/net/wwan/wwan_core.c-1054- .validate = wwan_rtnl_validate,
drivers/net/wwan/wwan_core.c:1055: .newlink = wwan_rtnl_newlink,
drivers/net/wwan/wwan_core.c-1056- .dellink = wwan_rtnl_dellink,
--
drivers/net/wwan/wwan_core.c=1062=static void wwan_create_default_link(struct wwan_device *wwandev,
--
drivers/net/wwan/wwan_core.c-1066- struct nlattr *data[IFLA_WWAN_MAX + 1];
drivers/net/wwan/wwan_core.c:1067: struct rtnl_newlink_params params = {
drivers/net/wwan/wwan_core.c-1068- .src_net = &init_net,
--
drivers/net/wwan/wwan_core.c-1115-
drivers/net/wwan/wwan_core.c:1116: if (WARN_ON(wwan_rtnl_newlink(dev, ¶ms, NULL))) {
drivers/net/wwan/wwan_core.c-1117- free_netdev(dev);
--
include/linux/if_macvlan.h=60=extern void macvlan_common_setup(struct net_device *dev);
include/linux/if_macvlan.h-61-
include/linux/if_macvlan.h:62:struct rtnl_newlink_params;
include/linux/if_macvlan.h-63-
include/linux/if_macvlan.h=64=extern int macvlan_common_newlink(struct net_device *dev,
include/linux/if_macvlan.h:65: struct rtnl_newlink_params *params,
include/linux/if_macvlan.h-66- struct netlink_ext_ack *extack);
--
include/net/rtnetlink.h=64=static inline int rtnl_msg_family(const struct nlmsghdr *nlh)
--
include/net/rtnetlink.h-72-/**
include/net/rtnetlink.h:73: * struct rtnl_newlink_params - parameters of rtnl_link_ops::newlink()
include/net/rtnetlink.h-74- *
--
include/net/rtnetlink.h-80- */
include/net/rtnetlink.h:81:struct rtnl_newlink_params {
include/net/rtnetlink.h-82- struct net *src_net;
--
include/net/rtnetlink.h-92- */
include/net/rtnetlink.h:93:static inline struct net *rtnl_newlink_link_net(struct rtnl_newlink_params *p)
include/net/rtnetlink.h-94-{
--
include/net/rtnetlink.h-100- */
include/net/rtnetlink.h:101:static inline struct net *rtnl_newlink_peer_net(struct rtnl_newlink_params *p)
include/net/rtnetlink.h-102-{
include/net/rtnetlink.h:103: return p->peer_net ? : rtnl_newlink_link_net(p);
include/net/rtnetlink.h-104-}
--
include/net/rtnetlink.h=140=struct rtnl_link_ops {
--
include/net/rtnetlink.h-162- int (*newlink)(struct net_device *dev,
include/net/rtnetlink.h:163: struct rtnl_newlink_params *params,
include/net/rtnetlink.h-164- struct netlink_ext_ack *extack);
--
net/8021q/vlan_netlink.c=138=static int vlan_newlink(struct net_device *dev,
net/8021q/vlan_netlink.c:139: struct rtnl_newlink_params *params,
net/8021q/vlan_netlink.c-140- struct netlink_ext_ack *extack)
net/8021q/vlan_netlink.c-141-{
net/8021q/vlan_netlink.c:142: struct net *link_net = rtnl_newlink_link_net(params);
net/8021q/vlan_netlink.c-143- struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
--
net/batman-adv/mesh-interface.c=1072=static int batadv_meshif_newlink(struct net_device *dev,
net/batman-adv/mesh-interface.c:1073: struct rtnl_newlink_params *params,
net/batman-adv/mesh-interface.c-1074- struct netlink_ext_ack *extack)
--
net/bridge/br_netlink.c=1556=static int br_dev_newlink(struct net_device *dev,
net/bridge/br_netlink.c:1557: struct rtnl_newlink_params *params,
net/bridge/br_netlink.c-1558- struct netlink_ext_ack *extack)
--
net/caif/chnl_net.c=441=static int ipcaif_newlink(struct net_device *dev,
net/caif/chnl_net.c:442: struct rtnl_newlink_params *params,
net/caif/chnl_net.c-443- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c=3688=EXPORT_SYMBOL(rtnl_create_link);
net/core/rtnetlink.c-3689-
net/core/rtnetlink.c:3690:struct rtnl_newlink_tbs {
net/core/rtnetlink.c-3691- struct nlattr *tb[IFLA_MAX + 1];
--
net/core/rtnetlink.c=3697=static int rtnl_changelink(const struct sk_buff *skb, struct nlmsghdr *nlh,
--
net/core/rtnetlink.c-3699- struct net_device *dev, struct net *tgt_net,
net/core/rtnetlink.c:3700: struct rtnl_newlink_tbs *tbs,
net/core/rtnetlink.c-3701- struct nlattr **data,
--
net/core/rtnetlink.c=3762=static int rtnl_group_changelink(const struct sk_buff *skb,
--
net/core/rtnetlink.c-3781-
net/core/rtnetlink.c:3782:static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
net/core/rtnetlink.c-3783- const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3790- unsigned char name_assign_type = NET_NAME_USER;
net/core/rtnetlink.c:3791: struct rtnl_newlink_params params = {
net/core/rtnetlink.c-3792- .src_net = sock_net(skb->sk),
--
net/core/rtnetlink.c=3858=static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3881-
net/core/rtnetlink.c:3882:static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-3883- const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3885- struct net *peer_net,
net/core/rtnetlink.c:3886: struct rtnl_newlink_tbs *tbs,
net/core/rtnetlink.c-3887- struct nlattr **data,
--
net/core/rtnetlink.c-3939-
net/core/rtnetlink.c:3940: return rtnl_newlink_create(skb, ifm, ops, tgt_net, link_net, peer_net, nlh,
net/core/rtnetlink.c-3941- tb, data, extack);
--
net/core/rtnetlink.c-3943-
net/core/rtnetlink.c:3944:static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-3945- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c-3949- struct rtnl_link_ops *ops = NULL;
net/core/rtnetlink.c:3950: struct rtnl_newlink_tbs *tbs;
net/core/rtnetlink.c-3951- struct rtnl_nets rtnl_nets;
--
net/core/rtnetlink.c-4054- rtnl_nets_lock(&rtnl_nets);
net/core/rtnetlink.c:4055: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
net/core/rtnetlink.c-4056- rtnl_nets_unlock(&rtnl_nets);
--
net/core/rtnetlink.c=4232=static int rtnl_linkprop(int cmd, struct sk_buff *skb, struct nlmsghdr *nlh,
--
net/core/rtnetlink.c-4279-
net/core/rtnetlink.c:4280:static int rtnl_newlinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-4281- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c=7046=static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst = {
net/core/rtnetlink.c:7047: {.msgtype = RTM_NEWLINK, .doit = rtnl_newlink,
net/core/rtnetlink.c-7048- .flags = RTNL_FLAG_DOIT_PERNET},
--
net/core/rtnetlink.c-7060- {.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
net/core/rtnetlink.c:7061: {.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
net/core/rtnetlink.c-7062- {.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
--
net/hsr/hsr_netlink.c=32=static int hsr_newlink(struct net_device *dev,
net/hsr/hsr_netlink.c:33: struct rtnl_newlink_params *params,
net/hsr/hsr_netlink.c-34- struct netlink_ext_ack *extack)
net/hsr/hsr_netlink.c-35-{
net/hsr/hsr_netlink.c:36: struct net *link_net = rtnl_newlink_link_net(params);
net/hsr/hsr_netlink.c-37- struct nlattr **data = params->data;
--
net/ieee802154/6lowpan/core.c=133=static int lowpan_newlink(struct net_device *ldev,
net/ieee802154/6lowpan/core.c:134: struct rtnl_newlink_params *params,
net/ieee802154/6lowpan/core.c-135- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1398=static int ipgre_newlink(struct net_device *dev,
net/ipv4/ip_gre.c:1399: struct rtnl_newlink_params *params,
net/ipv4/ip_gre.c-1400- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1419=static int erspan_newlink(struct net_device *dev,
net/ipv4/ip_gre.c:1420: struct rtnl_newlink_params *params,
net/ipv4/ip_gre.c-1421- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1702=struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
--
net/ipv4/ip_gre.c-1704-{
net/ipv4/ip_gre.c:1705: struct rtnl_newlink_params params = { .src_net = net };
net/ipv4/ip_gre.c-1706- struct nlattr *tb[IFLA_MAX + 1];
--
net/ipv4/ip_vti.c=577=static int vti_newlink(struct net_device *dev,
net/ipv4/ip_vti.c:578: struct rtnl_newlink_params *params,
net/ipv4/ip_vti.c-579- struct netlink_ext_ack *extack)
--
net/ipv4/ipip.c=439=static int ipip_newlink(struct net_device *dev,
net/ipv4/ipip.c:440: struct rtnl_newlink_params *params,
net/ipv4/ipip.c-441- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_gre.c=1997=static int ip6gre_newlink(struct net_device *dev,
net/ipv6/ip6_gre.c:1998: struct rtnl_newlink_params *params,
net/ipv6/ip6_gre.c-1999- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_gre.c=2235=static int ip6erspan_newlink(struct net_device *dev,
net/ipv6/ip6_gre.c:2236: struct rtnl_newlink_params *params,
net/ipv6/ip6_gre.c-2237- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_tunnel.c=2005=static int ip6_tnl_newlink(struct net_device *dev,
net/ipv6/ip6_tunnel.c:2006: struct rtnl_newlink_params *params,
net/ipv6/ip6_tunnel.c-2007- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_vti.c=1000=static int vti6_newlink(struct net_device *dev,
net/ipv6/ip6_vti.c:1001: struct rtnl_newlink_params *params,
net/ipv6/ip6_vti.c-1002- struct netlink_ext_ack *extack)
--
net/ipv6/sit.c=1553=static int ipip6_newlink(struct net_device *dev,
net/ipv6/sit.c:1554: struct rtnl_newlink_params *params,
net/ipv6/sit.c-1555- struct netlink_ext_ack *extack)
--
net/xfrm/xfrm_interface_core.c=816=static int xfrmi_newlink(struct net_device *dev,
net/xfrm/xfrm_interface_core.c:817: struct rtnl_newlink_params *params,
net/xfrm/xfrm_interface_core.c-818- struct netlink_ext_ack *extack)
]
|
| 30/2 |
2026/05/25 23:45 |
llm |
expert |
0m
Model:
Tokens:
input: 219959
output: 19
thoughts: 7893
Thoughts: **Analyzing a Bluetooth HIDP Deadlock**
Okay, here's the breakdown of what I'm seeing. It seems we've got a classic deadlock scenario playing out, all stemming from Bluetooth HIDP and some workqueue interactions. It all starts with `rtnl_newlink` – a netlink message handler – which is triggered by, or perhaps triggering, this syzkaller reproducer. `syz-executor`, the main syzkaller process, seems to be the one sending the RTM_NEWLINK, though the exact trigger isn't immediately obvious.
Right off the bat, `syz-executor` gets blocked trying to get the `rtnl_mutex`. Then `kworker/u4:4` tries to acquire the same `rtnl_mutex` while in `synchronize_rcu_expedited`, which seems to hang for a really long time. The reason for the hang is `synchronize_rcu_expedited` is waiting on RCU and we need to determine which task is blocking it.
The lockdep output points to something happening inside `l2cap_chan_connect`, specifically at `hci_dev_lock(hdev)`. We see that `syz.0.54` is blocked in `hci_dev_lock(hdev)`. But it's not truly *holding* `&hdev->lock`, it's trying to acquire it. It shows up as "held" in the lockdep output because the `lock_acquire` was called.
The holder of `&hdev->lock` turns out to be `khidpd_000d2f19`. This daemon is blocked in `__cancel_work_sync(&session->dev_init)`, meaning it's waiting for something to finish. That `dev_init` is a work item, specifically `hidp_session_dev_work`. This work involves device registration, which ultimately involves interactions with the device model via `hid_add_device`, `device_add`, which includes usermode helper calls that cause `udevd` to trigger.
So, `khidpd_000d2f19` is stuck waiting for `hidp_session_dev_work` to finish its job. The issue is that the work item is a chain of calls that leads to `call_usermodehelper`, and that's where the problem arises: the usermode process, `udevd`, is blocked trying to acquire `rtnl_mutex`, the same mutex held by `kworker/u4:4`. If the workqueue is blocking, so is the function. And, since `kworker/u4:4` is blocked as well in `synchronize_rcu_expedited`, which is waiting for an RCU grace period, and that's when things get ugly.
`syz.1.55` to `syz.7.61` are blocked in `hidp_session_find` on `hidp_session_sem` (read lock), and`khidpd_000d2f19` holds the write lock.
`kworker/u4:4` is in `rtnl_lock` and we know that we've got this work item to run that's blocked. If `kworker/u4:4` is blocked, this means syzkaller can't acquire the `rtnl_mutex`.
So, the real deadlock is this: `kworker/u4:4` *holds* `rtnl_mutex` (acquired at `linkwatch_event`), and is blocked in `synchronize_rcu_expedited`. `syz-executor` is trying to acquire the same `rtnl_mutex`. `udevd` needs `rtnl_mutex`, and the workqueues, because of the RCU issues, are not finishing their work. It's a classic resource contention situation with RCU sprinkled in to make it hang even longer.
|
| 31/2 |
2026/05/25 23:46 |
tool |
grepper |
0m
Args: map[Expression:hid_generic_probe]
Results: map[Output:drivers/hid/hid-generic.c=37=static bool hid_generic_match(struct hid_device *hdev,
--
drivers/hid/hid-generic.c-58-
drivers/hid/hid-generic.c:59:static int hid_generic_probe(struct hid_device *hdev,
drivers/hid/hid-generic.c-60- const struct hid_device_id *id)
--
drivers/hid/hid-generic.c=79=static struct hid_driver hid_generic = {
--
drivers/hid/hid-generic.c-82- .match = hid_generic_match,
drivers/hid/hid-generic.c:83: .probe = hid_generic_probe,
drivers/hid/hid-generic.c-84-};
]
|
| 32/2 |
2026/05/25 23:46 |
llm |
expert-compressor |
0m
Model:
Reply: Here is a comprehensive summary of the current state of the workspace and the investigation:
### 1. Approaches Tried and Results
- **Analyzed the Hung Task Trace:** The investigation started by examining the provided `khidpd` and `syz.0.54` hung task traces. `khidpd` is blocked in `__cancel_work_sync` from `hidp_session_remove`, while `syz.0.54` is blocked in `__mutex_lock` from `l2cap_chan_connect`.
- **Examined `l2cap_chan_connect`:** Grepped and read `net/bluetooth/l2cap_core.c` to understand the locking behavior. It was found that `l2cap_chan_connect` attempts to acquire `hci_dev_lock(hdev)` (which maps to `hdev->lock`).
- **Examined `l2cap_unregister_user` and `hidp_session_remove`:** Read `net/bluetooth/hidp/core.c` and `net/bluetooth/l2cap_core.c`. `l2cap_unregister_user` acquires `hci_dev_lock(hdev)` and then calls the user's `remove` callback, which is `hidp_session_remove`. `hidp_session_remove` then calls `cancel_work_sync(&session->dev_init)`.
- **Investigated `dev_init` work:** Grepped for `hidp_session_dev_add` and `hid_add_device`. The `dev_init` work item executes `hidp_session_dev_work`, which calls `hidp_session_dev_add` to register the HID and input devices (`hid_add_device` / `input_register_device`).
- **Investigated `rtnl_newlink`:** Grepped to see what `syz-executor` is doing, as it holds `rtnl_mutex`. It appears to be creating a new network link, which is a standard syzkaller background activity, but it might be interacting with the deadlock if the `dev_init` work also requires `rtnl_mutex`.
### 2. Current Hypotheses and Active Lines of Investigation
- **Primary Deadlock Hypothesis:** The bug is a classic ABBA deadlock or a workqueue synchronization hang involving `hdev->lock`.
- `khidpd` (PID 5507) is executing `hidp_session_thread` -> `l2cap_unregister_user`. It **acquires `hdev->lock`** and then calls `hidp_session_remove`.
- `hidp_session_remove` calls `cancel_work_sync(&session->dev_init)` and blocks waiting for the `dev_init` work to finish.
- If the `dev_init` work (`hidp_session_dev_work`) is currently running and attempts to acquire `hdev->lock` (either directly or indirectly via device registration/probing), it will block forever, causing a deadlock.
- Meanwhile, `syz.0.54` (PID 5547) is executing `l2cap_sock_connect` -> `l2cap_chan_connect` and attempts to acquire `hdev->lock`, blocking behind `khidpd`.
- **Alternative RCU/RTNL Hypothesis:** The system shows `kworker/u4:4` holding `rtnl_mutex` and blocked in `synchronize_rcu_expedited`. If the `dev_init` work (which registers a HID device) somehow triggers a path that requires `rtnl_mutex` (e.g., a HID driver that creates a network interface), it would block behind `kworker/u4:4`. If `kworker/u4:4`'s RCU synchronization is delayed indefinitely (perhaps due to another thread holding an RCU read lock or a scheduling issue), this could transitively hang `khidpd`.
### 3. Critical File Paths, Code Snippets, and Configurations
- **`net/bluetooth/l2cap_core.c`**:
```c
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user) {
struct hci_dev *hdev = conn->hcon->hdev;
hci_dev_lock(hdev); // ACQUIRES hdev->lock
list_del_init(&user->list);
user->remove(conn, user); // Calls hidp_session_remove
hci_dev_unlock(hdev);
}
```
- **`net/bluetooth/hidp/core.c`**:
```c
static void hidp_session_remove(struct l2cap_conn *conn, struct l2cap_user *user) {
struct hidp_session *session = container_of(user, struct hidp_session, user);
down_write(&hidp_session_sem);
hidp_session_terminate(session);
cancel_work_sync(&session->dev_init); // BLOCKS HERE
// ...
}
```
- **`net/bluetooth/hidp/core.c`** (`dev_init` work):
```c
static void hidp_session_dev_work(struct work_struct *work) {
struct hidp_session *session = container_of(work, struct hidp_session, dev_init);
ret = hidp_session_dev_add(session); // Calls hid_add_device / input_register_device
// ...
}
```
### 4. Potential Reasoning Loops or Repetitive Tool Calls
- The agent has successfully gathered the necessary source code context without falling into repetitive loops. However, it should be careful not to blindly grep for generic hex values (like `0x000d` or `0x2f19` from the syzkaller reproducer), as these are often just arbitrary constants generated by the fuzzer for ioctl arguments or memory addresses, leading to massive and irrelevant grep outputs (as seen in the recent tool calls). Future steps should focus on the lock dependency chain between `hid_add_device` and `hdev->lock` or `rtnl_mutex`.
Tokens:
input: 195864
output: 1398
thoughts: 3085
|
| 33/2 |
2026/05/25 23:47 |
llm |
expert |
0m
Model:
Tokens:
input: 13109
output: 47
thoughts: 991
Thoughts: **Analyzing a Hung Task Report: A Deep Dive into Bluetooth and RCU Concurrency**
Okay, so we have a hung task report, and the culprit seems to be `khidpd` blocked in `__cancel_work_sync` from `hidp_session_remove`. My immediate thought is, "Where's the contention?" `hidp_session_remove` is called from `l2cap_unregister_user`, which is under `hdev->lock`. `khidpd` already holds `hdev->lock` *and* `hidp_session_sem`, and is waiting for `session->dev_init` to finish. Clearly, it's a lock ordering issue.
My next step is always to trace the work. I need to understand what `hidp_session_dev_work` is doing. This work function calls `hidp_session_dev_add`. I need to figure out whether `hidp_session_dev_add`, or anything it calls, is acquiring either `hdev->lock` or `hidp_session_sem` *or* maybe even `rtnl_mutex` .
Ah, `syz-executor` seems to be holding `rtnl_mutex`. But wait, it's not actually blocked... it just *holds* it. Now, it seems like `kworker/u4:4` *does* hold `rtnl_mutex`, and it's also blocked in `synchronize_rcu_expedited`. Hang on... `kworker/u4:4` holds `rtnl_mutex` *and* `rcu_state.exp_mutex`. That's interesting, but I need to figure out how this is connected to Bluetooth. Also, `khungtaskd` holds `rcu_read_lock`.
I'm starting to get a picture. I suspect `kworker/u4:4` is executing `linkwatch_event`, which calls `rtnl_lock` and then `synchronize_rcu_expedited`. But let's verify that. Is the calling sequence truly `linkwatch_event` -> `rtnl_lock` -> `synchronize_rcu_expedited`? No, `linkwatch_event` *holds* `rtnl_mutex` and then probably calls `synchronize_rcu_expedited` from somewhere else? Is it a direct call, or an indirect one?
Let's look at the lock order reported.
```
4 locks held by kworker/u4:4/1034:
#0: ffff88801a479148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3213 [inline]
#0: ffff88801a479148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x9b4/0x17b0 kernel/workqueue.c:3321
#1: ffffc900024c7bc0 ((linkwatch_work).work){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3214 [inline]
#1: ffffc900024c7bc0 ((linkwatch_work).work){+.+.}-{0:0}, at: process_scheduled_works+0x9ef/0x17b0 kernel/workqueue.c:3321
#2: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: linkwatch_event+0xe/0x60 net/core/link_watch.c:303
#3: ffffffff8e144978 (rcu_state.exp_mutex){+.+.}-{4:4}, at: exp_funnel_lock kernel/rcu/tree_exp.h:304 [inline]
#3: ffffffff8e144978 (rcu_state.exp_mutex){+.+.}-{4:4}, at: synchronize_rcu_expedited+0x2f6/0x730 kernel/rcu/tree_exp.h:998
```
Okay, `linkwatch_event` *does* call `rtnl_lock()` and it calls `__linkwatch_run_queue()`, and then `rtnl_unlock()`. So, `rtnl_unlock()` calls `netdev_run_
|
| 34/2 |
2026/05/25 23:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_session_dev_work]
Results: map[SourceCode: 879: /*
880: * Asynchronous device registration
881: * HID device drivers might want to perform I/O during initialization to
882: * detect device types. Therefore, call device registration in a separate
883: * worker so the HIDP thread can schedule I/O operations.
884: * Note that this must be called after the worker thread was initialized
885: * successfully. This will then add the devices and increase session state
886: * on success, otherwise it will terminate the session thread.
887: */
888: static void hidp_session_dev_work(struct work_struct *work)
889: {
890: struct hidp_session *session = container_of(work,
891: struct hidp_session,
892: dev_init);
893: int ret;
894:
895: ret = hidp_session_dev_add(session);
896: if (!ret)
897: atomic_inc(&session->state);
898: else
899: hidp_session_terminate(session);
900: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 35/2 |
2026/05/25 23:47 |
llm |
expert |
0m
Model:
Tokens:
input: 15529
output: 47
thoughts: 14
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `hidp_session_dev_add`**
Alright, let's take a look at `hidp_session_dev_add`. Immediately, I'm thinking, this likely deals with the addition of a new HIDP session, probably related to a device. This implies a significant interaction point in the HIDP stack. My experience tells me that such a function would probably be responsible for:
* **Initialization:** Allocating any necessary data structures for this new session, perhaps tracking device-specific information and session state.
* **Association:** Connecting the newly added device (presumably via some identifier) to the existing HIDP session management. I'd expect internal lookups or linking to be happening here.
* **Resource Management:** Potentially acquiring resources needed for communication with the device or the HIDP protocol itself. Could be memory allocation, queue setup, or other underlying resources.
* **Security Context Setup:** Depending on the context, there could be some kind of security initialization. Authentication/pairing, encryption key exchange, or similar setup would be possible elements of the function.
* **Error Handling:** Important here would be robust error handling. What happens if the device is already present? What if there are resource allocation failures? How are those failures signaled up the stack?
My initial questions would revolve around what data structures are involved (e.g., are there session tables, device-specific structs, etc.?). I'd also want to know the scope of this function - is it called from a specific driver context, or from a more general HIDP manager? How does it interact with other HIDP components? And, importantly, what are the expected failure modes and how are they managed? Time to go deeper.
|
| 36/2 |
2026/05/25 23:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_session_dev_add]
Results: map[SourceCode: 845: /* add HID/input devices to their underlying bus systems */
846: static int hidp_session_dev_add(struct hidp_session *session)
847: {
848: int ret;
849:
850: /* Both HID and input systems drop a ref-count when unregistering the
851: * device but they don't take a ref-count when registering them. Work
852: * around this by explicitly taking a refcount during registration
853: * which is dropped automatically by unregistering the devices. */
854:
855: if (session->hid) {
856: ret = hid_add_device(session->hid);
857: if (ret)
858: return ret;
859: get_device(&session->hid->dev);
860: } else if (session->input) {
861: ret = input_register_device(session->input);
862: if (ret)
863: return ret;
864: input_get_device(session->input);
865: }
866:
867: return 0;
868: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 37/2 |
2026/05/25 23:47 |
llm |
expert |
0m
Model:
Tokens:
input: 16353
output: 45
thoughts: 184
Thoughts: **Debugging a Potential HID Driver Deadlock**
Okay, so I'm tracking down a potential deadlock related to HID drivers. The core issue seems to stem from `hid_add_device`, which registers a HID device. This, in turn, can initiate probing of the relevant HID drivers. Now, if a driver gets overzealous and attempts something that requires either `rtnl_mutex` or `hdev->lock`, we could have a nasty deadlock situation. I need to figure out *why* a recent system hang occurred, and I suspect a HID driver.
My initial clue is that `kworker/u4:4` is blocked in `synchronize_rcu_expedited`. That's a red flag. What's holding it up? My first thought was `khungtaskd` and its `rcu_read_lock`. Ah, but I see that's the wrong track. `khungtaskd` is *printing* the lockdep output; the print routine itself is holding the lock. It's not the root cause. This means I'm looking for *something else* that has an RCU read lock, that's not releasing it. What is holding that lock for a long time? Hmmm...
Could it be `hidp_session_thread`? I'll need to check the state of that thread. Let's dig into `hidp_session_thread` to see what it's up to. That's the most likely suspect at this point.
|
| 38/2 |
2026/05/25 23:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_session_thread]
Results: map[SourceCode:1256: /*
1257: * HIDP session thread
1258: * This thread runs the I/O for a single HIDP session. Startup is synchronous
1259: * which allows us to take references to ourself here instead of doing that in
1260: * the caller.
1261: * When we are ready to run we notify the caller and call hidp_session_run().
1262: */
1263: static int hidp_session_thread(void *arg)
1264: {
1265: struct hidp_session *session = arg;
1266: DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function);
1267: DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function);
1268:
1269: BT_DBG("session %p", session);
1270:
1271: /* initialize runtime environment */
1272: hidp_session_get(session);
1273: __module_get(THIS_MODULE);
1274: set_user_nice(current, -15);
1275: hidp_set_timer(session);
1276:
1277: add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
1278: add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
1279: /* This memory barrier is paired with wq_has_sleeper(). See
1280: * sock_poll_wait() for more information why this is needed. */
1281: smp_mb__before_atomic();
1282:
1283: /* notify synchronous startup that we're ready */
1284: atomic_inc(&session->state);
1285: wake_up(&session->state_queue);
1286:
1287: /* run session */
1288: hidp_session_run(session);
1289:
1290: /* cleanup runtime environment */
1291: remove_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
1292: remove_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
1293: wake_up_interruptible(&session->report_queue);
1294: hidp_del_timer(session);
1295:
1296: /*
1297: * If we stopped ourself due to any internal signal, we should try to
1298: * unregister our own session here to avoid having it linger until the
1299: * parent l2cap_conn dies or user-space cleans it up.
1300: * This does not deadlock as we don't do any synchronous shutdown.
1301: * Instead, this call has the same semantics as if user-space tried to
1302: * delete the session.
1303: */
1304: l2cap_unregister_user(session->conn, &session->user);
1305: hidp_session_put(session);
1306:
1307: module_put_and_kthread_exit(0);
1308: return 0;
1309: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 39/2 |
2026/05/25 23:47 |
llm |
expert |
0m
Model:
Tokens:
input: 17824
output: 47
thoughts: 43
Thoughts: Okay, here's my interpretation of this scenario, focusing on the potential implications and flow:
**Unpacking the `hidp_session_thread` Disconnect**
Alright, so the `hidp_session_thread` is cleaning up after a session. It's calling `l2cap_unregister_user(session->conn, &session->user);`. This is a pretty standard cleanup step in a Bluetooth stack, I'd imagine. So, the session is ending, and we are unregistering something.
My immediate thought is, let's look at `l2cap_unregister_user`. I need to see what's actually *happening* under the hood here. What's being unregistered? Is it a callback? A data structure? What resources are being freed? Is it safe?
I'm particularly interested in how this unregistration ties into the broader L2CAP connection management. Is it a graceful shutdown of the L2CAP channel, or something more abrupt? Are there any potential race conditions? I need to understand the implications of this function call in the wider context. I need to understand if the session is being properly cleaned up.
|
| 40/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_unregister_user]
Results: map[SourceCode:1724: void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
1725: {
1726: struct hci_dev *hdev = conn->hcon->hdev;
1727:
1728: hci_dev_lock(hdev);
1729:
1730: if (list_empty(&user->list))
1731: goto out_unlock;
1732:
1733: list_del_init(&user->list);
1734: user->remove(conn, user);
1735:
1736: out_unlock:
1737: hci_dev_unlock(hdev);
1738: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 41/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 18402
output: 45
thoughts: 14
Thoughts: Here's a summary of my understanding:
**Mapping Function Calls: A Quick Deduction**
Alright, so I've quickly mapped that `user->remove` corresponds directly to `hidp_session_remove`. Nothing too complicated here. It's a straightforward association, likely a call in a function table or a macro that points to a specific session removal process. This suggests a low-level interaction, likely dealing with the cleanup of a HIDP session object. That specific naming convention gives a bit of context.
|
| 42/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_session_remove]
Results: map[SourceCode:1144: /*
1145: * Remove HIDP session
1146: * Called from the l2cap_conn core when either we explicitly unregistered
1147: * the l2cap_user object or if the underlying connection is shut down.
1148: * We signal the hidp-session thread to shut down, unregister the HID/input
1149: * devices and unlink the session from the global list.
1150: * This drops the reference to the session that is owned by the global
1151: * session-list.
1152: * Note: We _must_ not synchronosly wait for the session-thread to shut down.
1153: * This is, because the session-thread might be waiting for an HCI lock that is
1154: * held while we are called. Therefore, we only unregister the devices and
1155: * notify the session-thread to terminate. The thread itself owns a reference
1156: * to the session object so it can safely shut down.
1157: */
1158: static void hidp_session_remove(struct l2cap_conn *conn,
1159: struct l2cap_user *user)
1160: {
1161: struct hidp_session *session = container_of(user,
1162: struct hidp_session,
1163: user);
1164:
1165: down_write(&hidp_session_sem);
1166:
1167: hidp_session_terminate(session);
1168:
1169: cancel_work_sync(&session->dev_init);
1170: if (session->input ||
1171: atomic_read(&session->state) > HIDP_SESSION_PREPARING)
1172: hidp_session_dev_del(session);
1173:
1174: list_del(&session->list);
1175:
1176: up_write(&hidp_session_sem);
1177:
1178: hidp_session_put(session);
1179: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 43/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 19172
output: 45
thoughts: 309
Thoughts: **Analyzing a Potential Deadlock in HIDP Session Removal**
Okay, so I'm looking at this `hidp_session_remove` function and immediately see it calls `cancel_work_sync` on `session->dev_init`. Right away, that raises a flag. If that `dev_init` work is currently running, it's going to call `hidp_session_dev_work`, which then calls `hidp_session_dev_add`. The alarm bells are already going off: if `hidp_session_dev_add` tries to grab either `hdev->lock` or `hidp_session_sem` while `hidp_session_remove` is also running, we've got a potential deadlock situation.
The next question, of course, is whether `hidp_session_dev_add` *actually* acquires `hdev->lock`. I need to trace the code. Alright, `hidp_session_dev_add` calls either `hid_add_device` or `input_register_device`. Let's assume the path of execution is `hid_add_device`. Does *that* acquire `hdev->lock`? Hmm, no, but it registers the device, and that could trigger driver probing. So the next logical question is what the driver does.
If the driver is something generic, like `hid-generic`, does it somehow acquire `hdev->lock`? Unlikely directly, but maybe it calls `hid_hw_start`, which in turn calls `hidp_start`? Let me see... Wait, wait, no, `hid_add_device` calls `device_add`, which triggers driver probing. Ah, so it's the driver's probe function that ultimately gets called. It calls `hid_parse` and then `hid_hw_start`. So that's the key. `hid_hw_start` calls `hdev->ll_driver->start(hdev)`. Okay, and for HIDP, that `ll_driver` is `hidp_hid_driver`. So, the `hidp_hid_driver` will need to be investigated. I need to understand what locks are held during the `start` call. That is where the locking behavior might cause a problem.
|
| 44/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_hid_driver]
Results: map[SourceCode: 742: static const struct hid_ll_driver hidp_hid_driver = {
743: .parse = hidp_parse,
744: .start = hidp_start,
745: .stop = hidp_stop,
746: .open = hidp_open,
747: .close = hidp_close,
748: .raw_request = hidp_raw_request,
749: .output_report = hidp_output_report,
750: };
SourceFile:net/bluetooth/hidp/core.c]
|
| 45/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 20193
output: 43
thoughts: 19
Thoughts: Here's my analysis of the provided context, expressed as a first-person expert perspective:
**Diving into the HIDP Implementation**
Okay, so the prompt points us directly to the core HIDP-related functions. My mind immediately jumps to `hidp_start`, `hidp_open`, and any related routines. It's clear we need to thoroughly dissect the initialization sequence, the opening and setup of HIDP connections. I'm focusing on the low-level details, the interactions with the underlying Bluetooth stack, the handling of device discovery, authentication (if applicable), and connection establishment. I'm thinking about the data structures being used to represent the HIDP connection state, the buffer management, and any error handling strategies. What are the mutexes and locks doing here? Are there any obvious race conditions that need to be addressed? Are we dealing with any unusual constraints, maybe memory limitations on the target hardware? I'm already formulating questions in my head: What is the overall architecture design here? Is this a synchronous or asynchronous model? What happens if connection fails? What are the underlying mechanisms for dealing with device pairing and security? Are we using any of the standard Bluetooth profiles, or do we have custom HIDP extensions? I'm anticipating some complex interactions with the Bluetooth Host Controller Interface (HCI) and the lower layers of the stack.
|
| 46/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_start]
Results: map[SourceCode: 727: static int hidp_start(struct hid_device *hid)
728: {
729: return 0;
730: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 47/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 20617
output: 43
thoughts: 9
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `hidp_open`**
Hmm, so I'm thinking about a specific approach here. What about the potential of using `hidp_open`? It's a key function, but I need to quickly run through its implications in my current line of thinking. I need to figure out if it will facilitate my goals. Perhaps I need to reconsider if there are any advantages to another function, but my first instinct is to think about how `hidp_open` might provide the functionality I need.
|
| 48/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_open]
Results: map[SourceCode: 710: static int hidp_open(struct hid_device *hid)
711: {
712: return 0;
713: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 49/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 20885
output: 42
thoughts: 825
Thoughts: **Analyzing a Potential Deadlock**
Okay, let's break this down. My initial thought was that `hid_add_device` might trigger a chain of events starting with `hid_device_probe`, and subsequently calling `hid_hw_start`. The critical question is whether `hid_add_device` itself acquires the `rtnl_mutex`. It doesn't seem to directly, but maybe some HID driver internally does? This is key.
Then I saw that `syz-executor` *was* holding the `rtnl_mutex` and seemed blocked in `rtnl_newlink`. However, on second glance, `syz-executor` wasn't actually *blocked*, just holding the lock at the time of the dump. That's a crucial distinction.
The real culprit looks like `kworker/u4:4`. It's holding the `rtnl_mutex` and blocked in `synchronize_rcu_expedited`. This strongly suggests a deadlock scenario. My immediate thought was that `linkwatch_event` -> `rtnl_lock` -> `synchronize_rcu_expedited` was the execution path, but I realized `linkwatch_event` *already* holds the `rtnl_mutex`, and `synchronize_rcu_expedited` is probably called from somewhere else.
To confirm, I examined the held locks. Here's what I found: `kworker/u4:4` holds the `rtnl_mutex` and the `rcu_state.exp_mutex` *within* `synchronize_rcu_expedited`, confirming the suspicion.
Now, let's trace the code path: `linkwatch_event` calls `rtnl_lock()`, then `__linkwatch_run_queue()`, and then `rtnl_unlock()`. This `rtnl_unlock()` is the key. It then calls `netdev_run_todo()`. My next thought is, does `netdev_run_todo()` call `synchronize_rcu()` or `synchronize_net()`? Wait, `synchronize_net()` calls `synchronize_rcu_expedited()`? No, it calls `synchronize_rcu()`. But `netdev_run_todo()` *could* call something that *eventually* calls `synchronize_rcu_expedited()`. I need to investigate `netdev_run_todo()` further. That's my next step.
|
| 50/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/dev.c IncludeLines:true Name:netdev_run_todo]
Results: map[SourceCode:11327: /* The sequence is:
11328: *
11329: * rtnl_lock();
11330: * ...
11331: * register_netdevice(x1);
11332: * register_netdevice(x2);
11333: * ...
11334: * unregister_netdevice(y1);
11335: * unregister_netdevice(y2);
11336: * ...
11337: * rtnl_unlock();
11338: * free_netdev(y1);
11339: * free_netdev(y2);
11340: *
11341: * We are invoked by rtnl_unlock().
11342: * This allows us to deal with problems:
11343: * 1) We can delete sysfs objects which invoke hotplug
11344: * without deadlocking with linkwatch via keventd.
11345: * 2) Since we run with the RTNL semaphore not held, we can sleep
11346: * safely in order to wait for the netdev refcnt to drop to zero.
11347: *
11348: * We must not return until all unregister events added during
11349: * the interval the lock was held have been completed.
11350: */
11351: void netdev_run_todo(void)
11352: {
11353: struct net_device *dev, *tmp;
11354: struct list_head list;
11355: int cnt;
11356: #ifdef CONFIG_LOCKDEP
11357: struct list_head unlink_list;
11358:
11359: list_replace_init(&net_unlink_list, &unlink_list);
11360:
11361: while (!list_empty(&unlink_list)) {
11362: dev = list_first_entry(&unlink_list, struct net_device,
11363: unlink_list);
11364: list_del_init(&dev->unlink_list);
11365: dev->nested_level = dev->lower_level - 1;
11366: }
11367: #endif
11368:
11369: /* Snapshot list, allow later requests */
11370: list_replace_init(&net_todo_list, &list);
11371:
11372: __rtnl_unlock();
11373:
11374: /* Wait for rcu callbacks to finish before next phase */
11375: if (!list_empty(&list))
11376: rcu_barrier();
11377:
11378: list_for_each_entry_safe(dev, tmp, &list, todo_list) {
11379: if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {
11380: netdev_WARN(dev, "run_todo but not unregistering\n");
11381: list_del(&dev->todo_list);
11382: continue;
11383: }
11384:
11385: netdev_lock(dev);
11386: WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERED);
11387: netdev_unlock(dev);
11388: linkwatch_sync_dev(dev);
11389: }
11390:
11391: cnt = 0;
11392: while (!list_empty(&list)) {
11393: dev = netdev_wait_allrefs_any(&list);
11394: list_del(&dev->todo_list);
11395:
11396: /* paranoia */
11397: BUG_ON(netdev_refcnt_read(dev) != 1);
11398: BUG_ON(!list_empty(&dev->ptype_all));
11399: BUG_ON(!list_empty(&dev->ptype_specific));
11400: WARN_ON(rcu_access_pointer(dev->ip_ptr));
11401: WARN_ON(rcu_access_pointer(dev->ip6_ptr));
11402:
11403: netdev_do_free_pcpu_stats(dev);
11404: if (dev->priv_destructor)
11405: dev->priv_destructor(dev);
11406: if (dev->needs_free_netdev)
11407: free_netdev(dev);
11408:
11409: cnt++;
11410:
11411: /* Free network device */
11412: kobject_put(&dev->dev.kobj);
11413: }
11414: if (cnt && atomic_sub_and_test(cnt, &dev_unreg_count))
11415: wake_up(&netdev_unregistering_wq);
11416: }
SourceFile:net/core/dev.c]
|
| 51/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 23751
output: 42
thoughts: 129
Thoughts: Okay, let me break this down.
**Investigating a Kernel Block**
Hmm, interesting. I'm seeing that `kworker/u4:4` is getting hung up in `synchronize_rcu_expedited`. That's not ideal. My gut tells me this is related to something locking and unlocking the routing netlink layer. I need to trace this.
So, where does `rtnl_unlock` call `synchronize_rcu_expedited`? I vaguely recall that process being somewhat convoluted.
Okay, let me think this through. My working hypothesis is that `linkwatch_event` is involved. Specifically, I'm guessing it calls `rtnl_lock` at some point to get a lock on the routing netlink layer. Then, it calls `rtnl_unlock` eventually to release that lock. And within `rtnl_unlock`, it probably calls `netdev_run_todo`, and *that* then somehow gets around to calling `rcu_barrier`. But wait...that doesn't sound right. `rcu_barrier` and `synchronize_rcu_expedited` are not the same thing. I'll need to check my work.
Right, let me revisit `linkwatch_event` and see if I missed something. Time to dive back in.
|
| 52/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/link_watch.c IncludeLines:true Name:linkwatch_event]
Results: map[SourceCode: 301: static void linkwatch_event(struct work_struct *dummy)
302: {
303: rtnl_lock();
304: __linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies));
305: rtnl_unlock();
306: }
SourceFile:net/core/link_watch.c]
|
| 53/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 24329
output: 45
thoughts: 294
Thoughts: **Debugging a Kernel Hang**
Okay, here's what's running through my mind as I look at this trace. My initial concern is that `kworker/u4:4` is blocked, and specifically, it's hung up within `synchronize_rcu_expedited`. That's a red flag. Let's dig deeper into the stack trace to understand why.
Alright, the stack trace indicates that this worker thread currently *holds* `rtnl_mutex`. We can see it was acquired within `linkwatch_event`, precisely when it called `rtnl_lock()`. Now, it's attempting to call `synchronize_rcu_expedited`. That's the problem. Where is `synchronize_rcu_expedited` called? I need to understand the relationship between `linkwatch_event` and where `synchronize_rcu_expedited` is used in relation to that. I'm going to have to find how `__linkwatch_run_queue` is interacting with this critical section to figure this out.
|
| 54/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/link_watch.c IncludeLines:true Name:__linkwatch_run_queue]
Results: map[SourceCode: 194: static void __linkwatch_run_queue(int urgent_only)
195: {
196: #define MAX_DO_DEV_PER_LOOP 100
197:
198: int do_dev = MAX_DO_DEV_PER_LOOP;
199: /* Use a local list here since we add non-urgent
200: * events back to the global one when called with
201: * urgent_only=1.
202: */
203: LIST_HEAD(wrk);
204:
205: /* Give urgent case more budget */
206: if (urgent_only)
207: do_dev += MAX_DO_DEV_PER_LOOP;
208:
209: /*
210: * Limit the number of linkwatch events to one
211: * per second so that a runaway driver does not
212: * cause a storm of messages on the netlink
213: * socket. This limit does not apply to up events
214: * while the device qdisc is down.
215: */
216: if (!urgent_only)
217: linkwatch_nextevent = jiffies + HZ;
218: /* Limit wrap-around effect on delay. */
219: else if (time_after(linkwatch_nextevent, jiffies + HZ))
220: linkwatch_nextevent = jiffies;
221:
222: clear_bit(LW_URGENT, &linkwatch_flags);
223:
224: spin_lock_irq(&lweventlist_lock);
225: list_splice_init(&lweventlist, &wrk);
226:
227: while (!list_empty(&wrk) && do_dev > 0) {
228: struct net_device *dev;
229:
230: dev = list_first_entry(&wrk, struct net_device, link_watch_list);
231: list_del_init(&dev->link_watch_list);
232:
233: if (!netif_device_present(dev) ||
234: (urgent_only && !linkwatch_urgent_event(dev))) {
235: list_add_tail(&dev->link_watch_list, &lweventlist);
236: continue;
237: }
238: /* We must free netdev tracker under
239: * the spinlock protection.
240: */
241: netdev_tracker_free(dev, &dev->linkwatch_dev_tracker);
242: spin_unlock_irq(&lweventlist_lock);
243: netdev_lock_ops(dev);
244: linkwatch_do_dev(dev);
245: netdev_unlock_ops(dev);
246: do_dev--;
247: spin_lock_irq(&lweventlist_lock);
248: }
249:
250: /* Add the remaining work back to lweventlist */
251: list_splice_init(&wrk, &lweventlist);
252:
253: if (!list_empty(&lweventlist))
254: linkwatch_schedule_work(0);
255: spin_unlock_irq(&lweventlist_lock);
256: }
SourceFile:net/core/link_watch.c]
|
| 55/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 25907
output: 44
thoughts: 20
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm talking to a fellow expert:
**Reflecting on Network Device State Changes**
Alright, so I'm thinking about `linkwatch_do_dev`. That function, as I recall, is a key part of our network link monitoring, right? It's the one that handles the "did the link go up or down?" events. And what's interesting me right now is how it interacts with the lower levels of the networking stack. Specifically, it seems like `linkwatch_do_dev` is going to make a call to `netdev_state_change`, or some similar function that signals a change in the network device's operational state. That's the mechanism that propagates the link status change up the stack, allowing us to react appropriately – whether we need to adjust routing tables, reconfigure interfaces, or alert the user. It's a fundamental piece, and I'm just mentally tracing the control flow, ensuring my understanding of its role in the system is sound.
|
| 56/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/link_watch.c IncludeLines:true Name:linkwatch_do_dev]
Results: map[SourceCode: 166: static void linkwatch_do_dev(struct net_device *dev)
167: {
168: /*
169: * Make sure the above read is complete since it can be
170: * rewritten as soon as we clear the bit below.
171: */
172: smp_mb__before_atomic();
173:
174: /* We are about to handle this device,
175: * so new events can be accepted
176: */
177: clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
178:
179: rfc2863_policy(dev);
180: if (dev->flags & IFF_UP) {
181: if (netif_carrier_ok(dev))
182: dev_activate(dev);
183: else
184: dev_deactivate(dev);
185:
186: netif_state_change(dev);
187: }
188: /* Note: our callers are responsible for calling netdev_tracker_free().
189: * This is the reason we use __dev_put() instead of dev_put().
190: */
191: __dev_put(dev);
192: }
SourceFile:net/core/link_watch.c]
|
| 57/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 26604
output: 42
thoughts: 72
Thoughts: Okay, here's my breakdown of what's going on, thinking through this code path:
**Tracing the Deactivation Process**
Alright, so `dev_deactivate` is the function that kicks this whole process off. I need to understand what happens during deactivation, and specifically how it impacts RCU synchronization. The first thing that jumps out is that `dev_deactivate` calls `synchronize_net()`. That's a key function to examine further. Wait a minute... `synchronize_net()` is the one calling `synchronize_rcu()`. I thought that was it, but this raises a potential question...
Does `synchronize_net()` also use the expedited version of RCU synchronization? Is `synchronize_rcu_expedited()` called from here? That would be good to know. I better go back and double check the code for `dev_deactivate` to confirm the exact function calls and understand the complete synchronization flow. I need to be sure I have the whole picture here.
|
| 58/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/sched/sch_generic.c IncludeLines:true Name:dev_deactivate]
Results: map[SourceCode:1394: void dev_deactivate(struct net_device *dev)
1395: {
1396: LIST_HEAD(single);
1397:
1398: list_add(&dev->close_list, &single);
1399: dev_deactivate_many(&single);
1400: list_del(&single);
1401: }
SourceFile:net/sched/sch_generic.c]
|
| 59/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 27071
output: 44
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my thought process summarized, assuming I'm already well-versed in the context:
**Checking `dev_deactivate_many`**
Alright, time to get to it. My initial thought is to take a look at `dev_deactivate_many`. I want to quickly evaluate its state and any potential impacts. Since I'm familiar with the codebase, my mind is already running through a mental checklist: what are the likely dependencies? Are there any obvious areas for concern? Is this part of a recent feature or a longstanding piece of functionality? What tests are in place, and how comprehensive are they? It's a quick assessment to get a handle on the current situation before I go any deeper. I'm going to start by getting a mental map of this function.
|
| 60/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/sched/sch_generic.c IncludeLines:true Name:dev_deactivate_many]
Results: map[SourceCode:1349: /**
1350: * dev_deactivate_many - deactivate transmissions on several devices
1351: * @head: list of devices to deactivate
1352: *
1353: * This function returns only when all outstanding transmissions
1354: * have completed, unless all devices are in dismantle phase.
1355: */
1356: void dev_deactivate_many(struct list_head *head)
1357: {
1358: bool sync_needed = false;
1359: struct net_device *dev;
1360:
1361: list_for_each_entry(dev, head, close_list) {
1362: netdev_for_each_tx_queue(dev, dev_deactivate_queue,
1363: &sync_needed);
1364: if (dev_ingress_queue(dev))
1365: dev_deactivate_queue(dev, dev_ingress_queue(dev),
1366: &sync_needed);
1367:
1368: netdev_watchdog_down(dev);
1369: }
1370:
1371: /* Wait for outstanding qdisc enqueuing calls. */
1372: if (sync_needed)
1373: synchronize_net();
1374:
1375: list_for_each_entry(dev, head, close_list) {
1376: netdev_for_each_tx_queue(dev, dev_reset_queue, NULL);
1377:
1378: if (dev_ingress_queue(dev))
1379: dev_reset_queue(dev, dev_ingress_queue(dev), NULL);
1380: }
1381:
1382: /* Wait for outstanding qdisc_run calls. */
1383: list_for_each_entry(dev, head, close_list) {
1384: while (some_qdisc_is_busy(dev)) {
1385: /* wait_event() would avoid this sleep-loop but would
1386: * require expensive checks in the fast paths of packet
1387: * processing which isn't worth it.
1388: */
1389: schedule_timeout_uninterruptible(1);
1390: }
1391: }
1392: }
SourceFile:net/sched/sch_generic.c]
|
| 61/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 27987
output: 40
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: Synchronization Initiated**
Alright, `synchronize_net()` has been invoked. My first thought is to assess the context. What's the triggering event? What's the current state of the network, and the intended target state? I immediately start running through the possibilities. Is this a full network refresh, a partial update based on a detected change, or perhaps a more granular synchronization of specific components or data streams? The implications of the function call depend entirely on the broader orchestration. Is the system going to be blocking on this, or is it an asynchronous operation? What error handling or fallback mechanisms are in place if the synchronization fails, or if there's a problem with the network? How about potential impacts on latency, bandwidth usage, and overall system performance? I'm already anticipating the diagnostic challenges if this becomes a bottleneck. I'm also considering the data model: what information is being synchronized, and what validation protocols are in place? Are there any potential consistency conflicts or race conditions to be aware of? I need to quickly recall the relevant dependencies and dataflows to accurately pinpoint the key components. In essence, my goal is to immediately have a mental model of the synchronization operation and its scope.
|
| 62/2 |
2026/05/25 23:48 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/dev.c IncludeLines:true Name:synchronize_net]
Results: map[SourceCode:11917: /**
11918: * synchronize_net - Synchronize with packet receive processing
11919: *
11920: * Wait for packets currently being received to be done.
11921: * Does not block later packets from starting.
11922: */
11923: void synchronize_net(void)
11924: {
11925: might_sleep();
11926: if (from_cleanup_net() || rtnl_is_locked())
11927: synchronize_rcu_expedited();
11928: else
11929: synchronize_rcu();
11930: }
SourceFile:net/core/dev.c]
|
| 63/2 |
2026/05/25 23:48 |
llm |
expert |
0m
Model:
Tokens:
input: 28538
output: 43
thoughts: 1322
Thoughts: **Debugging a Network Lockup**
Okay, let's break this down. It looks like `synchronize_net()` is calling `synchronize_rcu_expedited()` under a certain condition - specifically, when `rtnl_is_locked()` is true. And we know `linkwatch_event` holds the `rtnl_mutex`. So, we've got a `kworker` thread, `kworker/u4:4`, blocked inside `synchronize_rcu_expedited()`. The critical question is, *why* is it blocking?
My initial thought is that `synchronize_rcu_expedited()` is blocked because someone's holding an `rcu_read_lock()` for an extended period. Let's see who. The lockdep output points the finger at `khungtaskd`, but that's just because it's in a debugging context, printing locks. Hmm, the lockdep output *doesn't* show any other tasks holding an RCU read lock. Which makes the problem interesting.
Aha! `synchronize_rcu_expedited` *should* return immediately if there are no existing RCU readers, since it is expedited. So, if we are in this situation, it indicates something more insidious, such as a full deadlock - maybe a CPU is stuck, and `synchronize_rcu_expedited` is waiting on it.
Let's look at what the other tasks are up to. I see that `syz.0.54` is blocked in `__mutex_lock` on `hdev->lock`, and `khidpd` is blocked in `__cancel_work_sync` on `session->dev_init`. I need to track down this `dev_init` work, it's the key. Where is it executing? It looks like it's on a worker thread. I need to check the locks being held by those workers.
The lock output shows `kworker/u4:4` holding a series of locks, notably `rtnl_mutex`, and `rcu_state.exp_mutex`. But then I see output that says `3 locks held by kworker/0:3/1353:` but doesn't actually *list* the locks. That's a red flag – is the output truncated? I need to consult the complete bug report to be sure.
Aha! the full bug report does, in fact, show locks held by `kworker/0:3` that *weren't* displayed in the initial snippet. Now we're getting somewhere. Could `kworker/0:3` be executing `session->dev_init`? It seems possible, and it would be a critical link in the chain. If so, what is it blocked on?
`session->dev_init` is a call chain: `hidp_session_dev_add` -> `hid_add_device`. `hid_add_device` is responsible for registering the HID device. This registration is critical, as it likely triggers `hid_device_probe`, which calls `hid_hw_start`. We know that `hidp_start` returns 0. So, let's explore `hid_device_probe` in more detail.
`hid_device_probe` actually calls the driver's specific probe function (e.g., `hid_generic_probe`). Now we are getting somewhere. Does `hid_generic_probe` do anything that would block? It calls `hid_parse` and then `hid_hw_start`. `hid_parse` in turn calls `hid_hw_parse`. Now, for HIDP, the `hid_hw_parse` function calls `hidp_parse`. And from there we can continue to hunt. The `kworker/0:3` thread is probably waiting on something in this subtree. It's time to dig into the individual functions in that call chain to find where the real wait is.
|
| 64/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_parse]
Results: map[SourceCode: 719: static int hidp_parse(struct hid_device *hid)
720: {
721: struct hidp_session *session = hid->driver_data;
722:
723: return hid_parse_report(session->hid, session->rd_data,
724: session->rd_size);
725: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 65/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 30899
output: 43
thoughts: 903
Thoughts: **Analyzing a Potential Kernel Concurrency Issue**
Okay, let's break this down. My initial focus is on `hid_parse_report`. I need to figure out if either `hid_add_device` or the driver probe mechanism acquires the `rtnl_mutex`. If this HID device is somehow a network device, maybe it does, but that seems unlikely given the reproducer.
The reproducer gives me this report descriptor: `{22 ad 35}` with a length of 3, but the report size `rdsize` is incredibly large – `0xfffffffffffffea4`. Aha! `rdsize` is a 16-bit integer! `0xfea4` is actually 65188. Still, `rddata` points to a buffer. `hid_parse_report` might be significant here.
I need to confirm if `hid_add_device` can block on `rtnl_mutex`. If the device is recognized and registers things like LEDs, that's a potential area to investigate further.
Now, the crucial part: I see a scenario where `kworker/0:3` is blocked on `rtnl_mutex`. If `kworker/u4:4` *also* holds `rtnl_mutex` and is blocked on `synchronize_rcu_expedited()`, this is interesting. `synchronize_rcu_expedited()` blocks if someone holds `rcu_read_lock()`.
The puzzle is: who's holding the `rcu_read_lock()`?
Wait! `syz-executor/5806` is holding `rtnl_mutex`! The output clearly shows this. But then, `kworker/u4:4` is also reported to be holding `rtnl_mutex`! That can't be! It's a mutex, only one thread can hold it at a time. The kernel lockdep output *should* only show held locks, not blocked attempts, right?
So, if `syz-executor/5806` is listed as *holding* it, and `kworker/u4:4` *also* listed as holding it, then one is blocked trying to acquire? No, lockdep *only* shows held locks!
`syz-executor/5806` is at `rtnl_newlink+0x8db`. Let me check `rtnl_newlink` at `net/core/rtnetlink.c:4054`. That's where I need to look closer.
|
| 66/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnl_newlink]
Results: map[SourceCode:3944: static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3945: struct netlink_ext_ack *extack)
3946: {
3947: struct net *tgt_net, *link_net = NULL, *peer_net = NULL;
3948: struct nlattr **tb, **linkinfo, **data = NULL;
3949: struct rtnl_link_ops *ops = NULL;
3950: struct rtnl_newlink_tbs *tbs;
3951: struct rtnl_nets rtnl_nets;
3952: int ops_srcu_index;
3953: int ret;
3954:
3955: tbs = kmalloc(sizeof(*tbs), GFP_KERNEL);
3956: if (!tbs)
3957: return -ENOMEM;
3958:
3959: tb = tbs->tb;
3960: ret = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg), tb,
3961: IFLA_MAX, ifla_policy, extack);
3962: if (ret < 0)
3963: goto free;
3964:
3965: ret = rtnl_ensure_unique_netns(tb, extack, false);
3966: if (ret < 0)
3967: goto free;
3968:
3969: linkinfo = tbs->linkinfo;
3970: if (tb[IFLA_LINKINFO]) {
3971: ret = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
3972: tb[IFLA_LINKINFO],
3973: ifla_info_policy, NULL);
3974: if (ret < 0)
3975: goto free;
3976: } else {
3977: memset(linkinfo, 0, sizeof(tbs->linkinfo));
3978: }
3979:
3980: if (linkinfo[IFLA_INFO_KIND]) {
3981: char kind[MODULE_NAME_LEN];
3982:
3983: nla_strscpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
3984: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
3985: #ifdef CONFIG_MODULES
3986: if (!ops) {
3987: request_module("rtnl-link-%s", kind);
3988: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
3989: }
3990: #endif
3991: }
3992:
3993: rtnl_nets_init(&rtnl_nets);
3994:
3995: if (ops) {
3996: if (ops->maxtype > RTNL_MAX_TYPE) {
3997: ret = -EINVAL;
3998: goto put_ops;
3999: }
4000:
4001: if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
4002: ret = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
4003: linkinfo[IFLA_INFO_DATA],
4004: ops->policy, extack);
4005: if (ret < 0)
4006: goto put_ops;
4007:
4008: data = tbs->attr;
4009: }
4010:
4011: if (ops->validate) {
4012: ret = ops->validate(tb, data, extack);
4013: if (ret < 0)
4014: goto put_ops;
4015: }
4016:
4017: if (ops->peer_type) {
4018: peer_net = rtnl_get_peer_net(ops, tb, data, extack);
4019: if (IS_ERR(peer_net)) {
4020: ret = PTR_ERR(peer_net);
4021: goto put_ops;
4022: }
4023: if (peer_net)
4024: rtnl_nets_add(&rtnl_nets, peer_net);
4025: }
4026: }
4027:
4028: tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN);
4029: if (IS_ERR(tgt_net)) {
4030: ret = PTR_ERR(tgt_net);
4031: goto put_net;
4032: }
4033:
4034: rtnl_nets_add(&rtnl_nets, tgt_net);
4035:
4036: if (tb[IFLA_LINK_NETNSID]) {
4037: int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
4038:
4039: link_net = get_net_ns_by_id(tgt_net, id);
4040: if (!link_net) {
4041: NL_SET_ERR_MSG(extack, "Unknown network namespace id");
4042: ret = -EINVAL;
4043: goto put_net;
4044: }
4045:
4046: rtnl_nets_add(&rtnl_nets, link_net);
4047:
4048: if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) {
4049: ret = -EPERM;
4050: goto put_net;
4051: }
4052: }
4053:
4054: rtnl_nets_lock(&rtnl_nets);
4055: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
4056: rtnl_nets_unlock(&rtnl_nets);
4057:
4058: put_net:
4059: rtnl_nets_destroy(&rtnl_nets);
4060: put_ops:
4061: if (ops)
4062: rtnl_link_ops_put(ops, ops_srcu_index);
4063: free:
4064: kfree(tbs);
4065: return ret;
4066: }
SourceFile:net/core/rtnetlink.c]
|
| 67/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 34314
output: 44
thoughts: 42
Thoughts: Here's my take on the thought process, summarized as you requested:
**Navigating the Codebase**
Okay, let's see. Line 4054… ah, there it is: `rtnl_nets_lock(&rtnl_nets)`. That’s the call I was looking for. Now, let me just confirm… yes, `rtnl_nets_lock` is acquiring `rtnl_mutex`. Right, so this is where the critical section related to that resource is being entered. Makes sense, given the potential for concurrent access. I'll need to remember that when I go over the logic around the `rtnl_mutex` acquisition.
|
| 68/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnl_nets_lock]
Results: map[SourceCode: 337: static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets)
338: {
339: int i;
340:
341: rtnl_lock();
342:
343: for (i = 0; i < rtnl_nets->len; i++)
344: __rtnl_net_lock(rtnl_nets->net[i]);
345: }
SourceFile:net/core/rtnetlink.c]
|
| 69/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 34704
output: 45
thoughts: 1295
Thoughts: **Analyzing a Potential `rtnl_mutex` Contention Issue**
Okay, so I'm looking at this situation: `syz-executor/5806` seems to be holding `rtnl_mutex`, and then `kworker/u4:4` is also reported as holding `rtnl_mutex`. This immediately jumps out as impossible; a mutex is designed to prevent exactly this situation. I know that `rtnl_lock()` calls `mutex_lock(&rtnl_mutex)`, so I need to understand what's going on.
My first thought is, is `syz-executor/5806` just *waiting* on the mutex? The typical behavior of lockdep is to only show currently held locks, not ones a task is trying to acquire. But wait, no, the output states it *holds* the lock, which is why it's so strange.
Let's re-examine the lockdep output for `syz-executor/5806`. The crucial line from lockdep is: "#0: ffffffff8f50f788 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock net/core/rtnetlink.c:80 [inline]". This is what's throwing me off; it's saying `syz-executor/5806` *holds* the mutex, yet the stack trace also puts it in `rtnl_lock` or other functions calling it. This is inconsistent. I would expect it to be blocked in the `mutex_lock` call if it was waiting.
Then looking at the stacktrace for `kworker/u4:4` , which shows that it holds `rtnl_mutex` AND `rcu_state.exp_mutex`. That is the giveaway! `kworker/u4:4` definitely *holds* the `rtnl_mutex`, and so, the `synchronize_rcu_expedited` function must have been called within `linkwatch_event`, which is why it can acquire both locks.
However, if `kworker/u4:4` *holds* `rtnl_mutex`, how can `syz-executor/5806` also hold it? Now I look deeper at `syz-executor/5806`, the lockdep output shows `syz-executor/5806` is at `rtnl_newlink+0x8db`. I see line 4054 does `rtnl_nets_lock(&rtnl_nets);`, which calls `rtnl_lock()`. If `syz-executor/5806` is *blocked* in `rtnl_lock()`, it would *not* hold the lock.
Ah, I think I'm starting to see the issue. If the output says "1 lock held", it *has* to hold it. So, if lockdep tells me a task holds `rtnl_mutex`, it *actually* holds it. I need to understand what's happening within `rtnl_newlink` because that's what's causing the problem. My next step will be to examine the code around the `rtnl_nets_lock` call and the actual locking mechanisms there.
|
| 70/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:__rtnl_net_lock]
Results: map[SourceCode: 125: static inline void __rtnl_net_lock(struct net *net) {}
SourceFile:include/linux/rtnetlink.h]
|
| 71/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 36802
output: 45
thoughts: 40
Thoughts: Okay, let's see. This kernel code has me puzzled.
**Hmm, `__rtnl_net_lock` is empty?**
That's unexpected. I was expecting to see some initialization or use here. Let me double-check the source code. Right, let's go straight to `net/core/rtnetlink.c` and hunt down that symbol, `__rtnl_net_lock`. I need to understand what's *supposed* to be happening with that lock, and why it appears absent. This probably indicates a key issue in how the rtnetlink subsystem is handling concurrency, or possibly some optimization where a lock is no longer needed but the code hasn't been fully updated. I need to get to the root of this.
|
| 72/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:__rtnl_net_lock]
Results: map[SourceCode: 125: static inline void __rtnl_net_lock(struct net *net) {}
SourceFile:include/linux/rtnetlink.h]
|
| 73/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 37107
output: 21
thoughts: 66
Thoughts: Here's my take on those thoughts:
**Navigating the RTNL Lock**
Okay, so I'm trying to nail down where `__rtnl_net_lock` is defined. I *thought* it was in `net/core/rtnetlink.c`, but wait a second... isn't that a header file kind of definition? Yes, it is in `include/linux/rtnetlink.h`, of course. Let's make sure that's correct, as I have seen this thing defined in multiple places over the years and it can be confusing. To be safe, I'm going to do a quick `grep` within `net/core/rtnetlink.c` just to confirm its usage within that specific file and make sure I'm not missing any immediate context. Gotta keep things precise, right? Gotta keep things precise.
|
| 74/2 |
2026/05/25 23:49 |
tool |
grepper |
0m
Args: map[Expression:__rtnl_net_lock]
Results: map[Output:include/linux/rtnetlink.h=62=static inline bool lockdep_rtnl_is_held(void)
--
include/linux/rtnetlink.h-100-#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
include/linux/rtnetlink.h:101:void __rtnl_net_lock(struct net *net);
include/linux/rtnetlink.h-102-void __rtnl_net_unlock(struct net *net);
--
include/linux/rtnetlink.h=116=bool lockdep_rtnl_net_is_held(struct net *net);
--
include/linux/rtnetlink.h-124-#else
include/linux/rtnetlink.h:125:static inline void __rtnl_net_lock(struct net *net) {}
include/linux/rtnetlink.h-126-static inline void __rtnl_net_unlock(struct net *net) {}
--
net/core/dev.c=1948=int register_netdevice_notifier(struct notifier_block *nb)
--
net/core/dev.c-1964- for_each_net(net) {
net/core/dev.c:1965: __rtnl_net_lock(net);
net/core/dev.c-1966- err = call_netdevice_register_net_notifiers(nb, net);
--
net/core/dev.c-1978- for_each_net_continue_reverse(net) {
net/core/dev.c:1979: __rtnl_net_lock(net);
net/core/dev.c-1980- call_netdevice_unregister_net_notifiers(nb, net);
--
net/core/dev.c=2003=int unregister_netdevice_notifier(struct notifier_block *nb)
--
net/core/dev.c-2015- for_each_net(net) {
net/core/dev.c:2016: __rtnl_net_lock(net);
net/core/dev.c-2017- call_netdevice_unregister_net_notifiers(nb, net);
--
net/core/net_namespace.c=166=static void ops_exit_rtnl_list(const struct list_head *ops_list,
--
net/core/net_namespace.c-176- list_for_each_entry(net, net_exit_list, exit_list) {
net/core/net_namespace.c:177: __rtnl_net_lock(net);
net/core/net_namespace.c-178-
--
net/core/rtnetlink.c=184=EXPORT_SYMBOL(lockdep_rtnl_is_held);
--
net/core/rtnetlink.c-187-#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
net/core/rtnetlink.c:188:void __rtnl_net_lock(struct net *net)
net/core/rtnetlink.c-189-{
--
net/core/rtnetlink.c-193-}
net/core/rtnetlink.c:194:EXPORT_SYMBOL(__rtnl_net_lock);
net/core/rtnetlink.c-195-
--
net/core/rtnetlink.c=204=void rtnl_net_lock(struct net *net)
--
net/core/rtnetlink.c-206- rtnl_lock();
net/core/rtnetlink.c:207: __rtnl_net_lock(net);
net/core/rtnetlink.c-208-}
--
net/core/rtnetlink.c=218=int rtnl_net_trylock(struct net *net)
--
net/core/rtnetlink.c-222- if (ret)
net/core/rtnetlink.c:223: __rtnl_net_lock(net);
net/core/rtnetlink.c-224-
--
net/core/rtnetlink.c=229=int rtnl_net_lock_killable(struct net *net)
--
net/core/rtnetlink.c-233- if (!ret)
net/core/rtnetlink.c:234: __rtnl_net_lock(net);
net/core/rtnetlink.c-235-
--
net/core/rtnetlink.c=337=static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets)
--
net/core/rtnetlink.c-343- for (i = 0; i < rtnl_nets->len; i++)
net/core/rtnetlink.c:344: __rtnl_net_lock(rtnl_nets->net[i]);
net/core/rtnetlink.c-345-}
--
net/ipv4/fib_frontend.c=1671=static void __net_exit fib_net_exit_batch(struct list_head *net_list)
--
net/ipv4/fib_frontend.c-1676- list_for_each_entry(net, net_list, exit_list) {
net/ipv4/fib_frontend.c:1677: __rtnl_net_lock(net);
net/ipv4/fib_frontend.c-1678- ip_fib_net_exit(net);
]
|
| 75/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 38759
output: 44
thoughts: 438
Thoughts: **Investigating a Potential Mutex Conflict**
Okay, so `CONFIG_DEBUG_NET_SMALL_RTNL` is enabled, which is a good starting point for this investigation. I see that the code uses `net->rtnl_mutex`. That's interesting. It looks like `rtnl_nets_lock` calls `rtnl_lock()`, which acquires the global `rtnl_mutex`, and *then* calls `__rtnl_net_lock(net)` to grab the `net->rtnl_mutex`.
The puzzling thing is that if "syz-executor/5806" holds the global `rtnl_mutex` (which it must have successfully acquired, as confirmed by the lockdep output), then "kworker/u4:4" absolutely CANNOT also hold the same global mutex. It's impossible.
But the lockdep output says: "syz-executor/5806" holds `rtnl_mutex`, and, crucially, "kworker/u4:4" also appears to hold it. And it's the exact same memory address for `rtnl_mutex`, which is `ffffffff8f50f788`. That's a huge red flag! How can two tasks simultaneously hold the same `struct mutex`? This makes no sense.
Okay, let me think. Since `rtnl_mutex` is a `struct mutex`, if "syz-executor/5806" is *blocked* inside `rtnl_lock()`, does lockdep still show it as held? Perhaps lockdep has a quirk? Time to consult `debug_show_all_locks` to try and figure this out.
|
| 76/2 |
2026/05/25 23:49 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/locking/lockdep.c IncludeLines:true Name:debug_show_all_locks]
Results: map[SourceCode:6760: void debug_show_all_locks(void)
6761: {
6762: struct task_struct *g, *p;
6763:
6764: if (unlikely(!debug_locks)) {
6765: pr_warn("INFO: lockdep is turned off.\n");
6766: return;
6767: }
6768: pr_warn("\nShowing all locks held in the system:\n");
6769:
6770: rcu_read_lock();
6771: for_each_process_thread(g, p) {
6772: if (!p->lockdep_depth)
6773: continue;
6774: lockdep_print_held_locks(p);
6775: touch_nmi_watchdog();
6776: touch_all_softlockup_watchdogs();
6777: }
6778: rcu_read_unlock();
6779:
6780: pr_warn("\n");
6781: pr_warn("=============================================\n\n");
6782: }
SourceFile:kernel/locking/lockdep.c]
|
| 77/2 |
2026/05/25 23:49 |
llm |
expert |
0m
Model:
Tokens:
input: 39947
output: 46
thoughts: 446
Thoughts: **Analyzing a Potential Kernel Lock Issue**
Okay, so I'm looking at `lockdep_print_held_locks` and it's showing some lock held by a task. I need to understand this. First, I ask myself, if a task is blocked waiting on `mutex_lock`, does it *hold* the lock? No, that's not how it works; the lock is added to the held locks list *after* the acquisition. Good, that's clear.
Now, I'm focused on `rtnl_lock`. I see it's just a wrapper around `mutex_lock(&rtnl_mutex)`. Hmm, if `syz-executor/5806` shows it *holds* `rtnl_mutex`, it means it's acquired it through `rtnl_lock()`. But wait a minute, `kworker/u4:4` *also* holds `rtnl_mutex`! That's strange...
Could `rtnl_mutex` be a semaphore? Let me double-check... no, it's defined as a `DEFINE_MUTEX`, so it's a mutex. How can two tasks possibly hold the same mutex? That's a classic deadlock situation waiting to happen.
Let's think this through. `syz-executor/5806` could be executing `rtnl_newlink`. And if it's in `rtnl_newlink`, then `rtnl_lock` must have been acquired via `rtnl_lock()`. Okay, where is `rtnl_newlink` called? Aha, it's called from `rtnetlink_rcv_msg`. So, `rtnetlink_rcv_msg` calls `rtnl_lock()` if it needs it.
Okay, let me look at `rtnl_newlink` again... I see it uses `rtnl_nets_lock`. Okay, that's fine. But wait... `rtnl_nets_lock` calls `rtnl_lock()`! If `rtnetlink_rcv_msg` *already* called `rtnl_lock()` before calling `rtnl_nets_lock`, then `rtnl_nets_lock` calling `rtnl_lock()` again would cause a deadlock! Bingo! That's the likely issue. I need to go and analyze the logic within `rtnetlink_rcv_msg` now.
|
| 78/2 |
2026/05/25 23:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnetlink_rcv_msg]
Results: map[SourceCode:6845: static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
6846: struct netlink_ext_ack *extack)
6847: {
6848: struct net *net = sock_net(skb->sk);
6849: struct rtnl_link *link;
6850: enum rtnl_kinds kind;
6851: struct module *owner;
6852: int err = -EOPNOTSUPP;
6853: rtnl_doit_func doit;
6854: unsigned int flags;
6855: int family;
6856: int type;
6857:
6858: type = nlh->nlmsg_type;
6859: if (type > RTM_MAX)
6860: return -EOPNOTSUPP;
6861:
6862: type -= RTM_BASE;
6863:
6864: /* All the messages must have at least 1 byte length */
6865: if (nlmsg_len(nlh) < sizeof(struct rtgenmsg))
6866: return 0;
6867:
6868: family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
6869: kind = rtnl_msgtype_kind(type);
6870:
6871: if (kind != RTNL_KIND_GET && !netlink_net_capable(skb, CAP_NET_ADMIN))
6872: return -EPERM;
6873:
6874: rcu_read_lock();
6875: if (kind == RTNL_KIND_GET && (nlh->nlmsg_flags & NLM_F_DUMP)) {
6876: struct sock *rtnl;
6877: rtnl_dumpit_func dumpit;
6878: u32 min_dump_alloc = 0;
6879:
6880: link = rtnl_get_link(family, type);
6881: if (!link || !link->dumpit) {
6882: family = PF_UNSPEC;
6883: link = rtnl_get_link(family, type);
6884: if (!link || !link->dumpit)
6885: goto err_unlock;
6886: }
6887: owner = link->owner;
6888: dumpit = link->dumpit;
6889: flags = link->flags;
6890:
6891: if (type == RTM_GETLINK - RTM_BASE)
6892: min_dump_alloc = rtnl_calcit(skb, nlh);
6893:
6894: err = 0;
6895: /* need to do this before rcu_read_unlock() */
6896: if (!try_module_get(owner))
6897: err = -EPROTONOSUPPORT;
6898:
6899: rcu_read_unlock();
6900:
6901: rtnl = net->rtnl;
6902: if (err == 0) {
6903: struct netlink_dump_control c = {
6904: .dump = dumpit,
6905: .min_dump_alloc = min_dump_alloc,
6906: .module = owner,
6907: .flags = flags,
6908: };
6909: err = rtnetlink_dump_start(rtnl, skb, nlh, &c);
6910: /* netlink_dump_start() will keep a reference on
6911: * module if dump is still in progress.
6912: */
6913: module_put(owner);
6914: }
6915: return err;
6916: }
6917:
6918: link = rtnl_get_link(family, type);
6919: if (!link || !link->doit) {
6920: family = PF_UNSPEC;
6921: link = rtnl_get_link(PF_UNSPEC, type);
6922: if (!link || !link->doit)
6923: goto out_unlock;
6924: }
6925:
6926: owner = link->owner;
6927: if (!try_module_get(owner)) {
6928: err = -EPROTONOSUPPORT;
6929: goto out_unlock;
6930: }
6931:
6932: flags = link->flags;
6933: if (kind == RTNL_KIND_DEL && (nlh->nlmsg_flags & NLM_F_BULK) &&
6934: !(flags & RTNL_FLAG_BULK_DEL_SUPPORTED)) {
6935: NL_SET_ERR_MSG(extack, "Bulk delete is not supported");
6936: module_put(owner);
6937: goto err_unlock;
6938: }
6939:
6940: if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
6941: doit = link->doit;
6942: rcu_read_unlock();
6943: if (doit)
6944: err = doit(skb, nlh, extack);
6945: module_put(owner);
6946: return err;
6947: }
6948: rcu_read_unlock();
6949:
6950: rtnl_lock();
6951: link = rtnl_get_link(family, type);
6952: if (link && link->doit)
6953: err = link->doit(skb, nlh, extack);
6954: rtnl_unlock();
6955:
6956: module_put(owner);
6957:
6958: return err;
6959:
6960: out_unlock:
6961: rcu_read_unlock();
6962: return err;
6963:
6964: err_unlock:
6965: rcu_read_unlock();
6966: return -EOPNOTSUPP;
6967: }
SourceFile:net/core/rtnetlink.c]
|
| 79/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 42769
output: 23
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `rtnl_newlink` and its Implications**
Right, *rtnl_newlink* has that *RTNL_FLAG_DOIT_UNLOCKED* flag. That's immediately interesting. It suggests that there's a specific reason for skipping the usual locking mechanisms in this particular netlink operation. Let me dig into the kernel code and see where *rtnl_newlink* is actually registered. I need to understand its context within the broader netlink framework, specifically to identify the lock dependencies, the critical sections it might be protecting, or potentially circumventing. Understanding *where* it's registered will give me the best clue to *why* the *DOIT_UNLOCKED* flag is used in this instance. This will dictate how I should evaluate this function in my analyses.
|
| 80/2 |
2026/05/25 23:50 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:<nil> TruncatedOutput:false]
|
| 81/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 43037
output: 24
thoughts: 21
Thoughts: Here's my thought process, summarized as you requested:
**Navigating the Registration Process**
Okay, hold on a second. Before I proceed any further with this, I need to make absolutely sure I'm using the correct registration function for this network routing table interaction. Am I thinking clearly? I need to double-check whether I should be calling `rtnl_register` or `rtnl_register_module`. There's a subtle but important difference here, and getting it wrong could lead to some nasty debugging later, especially in a kernel environment. Let me be clear in my understanding, and be certain about the correct choice based on what I am trying to achieve. I need to refresh my knowledge, or at least my memory, of those functions before I commit to anything. Best to prevent a headache later with a quick check now.
|
| 82/2 |
2026/05/25 23:50 |
tool |
grepper |
0m
Error:
Args: map[Expression:rtnl_register.*RTM_NEWLINK]
Results:
|
| 83/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 43271
output: 19
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing Network Interface Creation**
Okay, so I need to understand what's happening when a new network interface is created. My immediate instinct is to dive into the low-level details, particularly where the kernel handles the socket calls. Let's see... a quick `grep` for `rtnl_newlink` should get me pointed in the right direction. That's usually the function responsible for processing the NETLINK messages related to interface addition. I'm expecting this will be the entry point or, at least, a major coordinating function in that process. I'm really looking to understand the whole dance from user space call, through the system calls and the kernel's processing. Specifically, I'm thinking about the data structures being set up, the memory allocation, and the interaction with the relevant network drivers. This is where the core of the action happens. I anticipate there will be a series of calls after this point, each leading me through the logic to properly initialize a network device. I'm trying to get a quick overview of the code path to understand its logic.
|
| 84/2 |
2026/05/25 23:50 |
tool |
grepper |
0m
Args: map[Expression:rtnl_newlink]
Results: map[Output:drivers/infiniband/ulp/ipoib/ipoib_netlink.c=100=static int ipoib_new_child_link(struct net_device *dev,
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:101: struct rtnl_newlink_params *params,
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-102- struct netlink_ext_ack *extack)
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-103-{
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:104: struct net *link_net = rtnl_newlink_link_net(params);
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-105- struct nlattr **data = params->data;
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c=52=static bool is_child_unique(struct ipoib_dev_priv *ppriv,
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-93- * If (ndev->reg_state == NETREG_UNINITIALIZED) then it is up to the caller to
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:94: * free the net_device (just as rtnl_newlink does) otherwise the net_device
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-95- * will be freed when the rtnl is unlocked.
--
drivers/net/amt.c=3164=static int amt_newlink(struct net_device *dev,
drivers/net/amt.c:3165: struct rtnl_newlink_params *params,
drivers/net/amt.c-3166- struct netlink_ext_ack *extack)
drivers/net/amt.c-3167-{
drivers/net/amt.c:3168: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/amt.c-3169- struct amt_dev *amt = netdev_priv(dev);
--
drivers/net/bareudp.c=701=static int bareudp_newlink(struct net_device *dev,
drivers/net/bareudp.c:702: struct rtnl_newlink_params *params,
drivers/net/bareudp.c-703- struct netlink_ext_ack *extack)
drivers/net/bareudp.c-704-{
drivers/net/bareudp.c:705: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/bareudp.c-706- struct nlattr **data = params->data;
--
drivers/net/bonding/bond_netlink.c=567=static int bond_newlink(struct net_device *bond_dev,
drivers/net/bonding/bond_netlink.c:568: struct rtnl_newlink_params *params,
drivers/net/bonding/bond_netlink.c-569- struct netlink_ext_ack *extack)
--
drivers/net/can/dev/netlink.c=627=static int can_newlink(struct net_device *dev,
drivers/net/can/dev/netlink.c:628: struct rtnl_newlink_params *params,
drivers/net/can/dev/netlink.c-629- struct netlink_ext_ack *extack)
--
drivers/net/can/vxcan.c=175=static int vxcan_newlink(struct net_device *dev,
drivers/net/can/vxcan.c:176: struct rtnl_newlink_params *params,
drivers/net/can/vxcan.c-177- struct netlink_ext_ack *extack)
drivers/net/can/vxcan.c-178-{
drivers/net/can/vxcan.c:179: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/can/vxcan.c-180- struct nlattr **data = params->data;
--
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c=120=static int rmnet_newlink(struct net_device *dev,
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c:121: struct rtnl_newlink_params *params,
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-122- struct netlink_ext_ack *extack)
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-123-{
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c:124: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c-125- u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
--
drivers/net/geneve.c=1650=static int geneve_newlink(struct net_device *dev,
drivers/net/geneve.c:1651: struct rtnl_newlink_params *params,
drivers/net/geneve.c-1652- struct netlink_ext_ack *extack)
drivers/net/geneve.c-1653-{
drivers/net/geneve.c:1654: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/geneve.c-1655- struct nlattr **data = params->data;
--
drivers/net/gtp.c=1465=static int gtp_newlink(struct net_device *dev,
drivers/net/gtp.c:1466: struct rtnl_newlink_params *params,
drivers/net/gtp.c-1467- struct netlink_ext_ack *extack)
drivers/net/gtp.c-1468-{
drivers/net/gtp.c:1469: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/gtp.c-1470- struct nlattr **data = params->data;
--
drivers/net/ipvlan/ipvlan.h=167=void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
drivers/net/ipvlan/ipvlan.h-168- unsigned int len, bool success, bool mcast);
drivers/net/ipvlan/ipvlan.h:169:int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvlan.h-170- struct netlink_ext_ack *extack);
--
drivers/net/ipvlan/ipvlan_main.c=514=static int ipvlan_nl_fillinfo(struct sk_buff *skb,
--
drivers/net/ipvlan/ipvlan_main.c-535-
drivers/net/ipvlan/ipvlan_main.c:536:int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvlan_main.c-537- struct netlink_ext_ack *extack)
drivers/net/ipvlan/ipvlan_main.c-538-{
drivers/net/ipvlan/ipvlan_main.c:539: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ipvlan/ipvlan_main.c-540- struct ipvl_dev *ipvlan = netdev_priv(dev);
--
drivers/net/ipvlan/ipvtap.c=76=static int ipvtap_newlink(struct net_device *dev,
drivers/net/ipvlan/ipvtap.c:77: struct rtnl_newlink_params *params,
drivers/net/ipvlan/ipvtap.c-78- struct netlink_ext_ack *extack)
--
drivers/net/macsec.c=4173=static int macsec_newlink(struct net_device *dev,
drivers/net/macsec.c:4174: struct rtnl_newlink_params *params,
drivers/net/macsec.c-4175- struct netlink_ext_ack *extack)
drivers/net/macsec.c-4176-{
drivers/net/macsec.c:4177: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/macsec.c-4178- struct macsec_dev *macsec = macsec_priv(dev);
--
drivers/net/macvlan.c=1444=int macvlan_common_newlink(struct net_device *dev,
drivers/net/macvlan.c:1445: struct rtnl_newlink_params *params,
drivers/net/macvlan.c-1446- struct netlink_ext_ack *extack)
drivers/net/macvlan.c-1447-{
drivers/net/macvlan.c:1448: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/macvlan.c-1449- struct macvlan_dev *vlan = netdev_priv(dev);
--
drivers/net/macvlan.c=1572=static int macvlan_newlink(struct net_device *dev,
drivers/net/macvlan.c:1573: struct rtnl_newlink_params *params,
drivers/net/macvlan.c-1574- struct netlink_ext_ack *extack)
--
drivers/net/macvtap.c=80=static int macvtap_newlink(struct net_device *dev,
drivers/net/macvtap.c:81: struct rtnl_newlink_params *params,
drivers/net/macvtap.c-82- struct netlink_ext_ack *extack)
--
drivers/net/netkit.c=329=static int netkit_new_link(struct net_device *dev,
drivers/net/netkit.c:330: struct rtnl_newlink_params *params,
drivers/net/netkit.c-331- struct netlink_ext_ack *extack)
drivers/net/netkit.c-332-{
drivers/net/netkit.c:333: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/netkit.c-334- enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT;
--
drivers/net/ovpn/main.c=177=static int ovpn_newlink(struct net_device *dev,
drivers/net/ovpn/main.c:178: struct rtnl_newlink_params *params,
drivers/net/ovpn/main.c-179- struct netlink_ext_ack *extack)
--
drivers/net/pfcp.c=187=static int pfcp_newlink(struct net_device *dev,
drivers/net/pfcp.c:188: struct rtnl_newlink_params *params,
drivers/net/pfcp.c-189- struct netlink_ext_ack *extack)
drivers/net/pfcp.c-190-{
drivers/net/pfcp.c:191: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/pfcp.c-192- struct pfcp_dev *pfcp = netdev_priv(dev);
--
drivers/net/ppp/ppp_generic.c=1313=static int ppp_nl_newlink(struct net_device *dev,
drivers/net/ppp/ppp_generic.c:1314: struct rtnl_newlink_params *params,
drivers/net/ppp/ppp_generic.c-1315- struct netlink_ext_ack *extack)
drivers/net/ppp/ppp_generic.c-1316-{
drivers/net/ppp/ppp_generic.c:1317: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/ppp/ppp_generic.c-1318- struct nlattr **data = params->data;
--
drivers/net/team/team_core.c=2222=static int team_newlink(struct net_device *dev,
drivers/net/team/team_core.c:2223: struct rtnl_newlink_params *params,
drivers/net/team/team_core.c-2224- struct netlink_ext_ack *extack)
--
drivers/net/veth.c=1805=static int veth_newlink(struct net_device *dev,
drivers/net/veth.c:1806: struct rtnl_newlink_params *params,
drivers/net/veth.c-1807- struct netlink_ext_ack *extack)
drivers/net/veth.c-1808-{
drivers/net/veth.c:1809: struct net *peer_net = rtnl_newlink_peer_net(params);
drivers/net/veth.c-1810- struct nlattr **data = params->data;
--
drivers/net/vrf.c=1677=static int vrf_newlink(struct net_device *dev,
drivers/net/vrf.c:1678: struct rtnl_newlink_params *params,
drivers/net/vrf.c-1679- struct netlink_ext_ack *extack)
--
drivers/net/vxlan/vxlan_core.c=4347=static int vxlan_newlink(struct net_device *dev,
drivers/net/vxlan/vxlan_core.c:4348: struct rtnl_newlink_params *params,
drivers/net/vxlan/vxlan_core.c-4349- struct netlink_ext_ack *extack)
drivers/net/vxlan/vxlan_core.c-4350-{
drivers/net/vxlan/vxlan_core.c:4351: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/vxlan/vxlan_core.c-4352- struct nlattr **data = params->data;
--
drivers/net/wireguard/device.c=310=static int wg_newlink(struct net_device *dev,
drivers/net/wireguard/device.c:311: struct rtnl_newlink_params *params,
drivers/net/wireguard/device.c-312- struct netlink_ext_ack *extack)
drivers/net/wireguard/device.c-313-{
drivers/net/wireguard/device.c:314: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/wireguard/device.c-315- struct wg_device *wg = netdev_priv(dev);
--
drivers/net/wireless/virtual/virt_wifi.c=522=static int virt_wifi_newlink(struct net_device *dev,
drivers/net/wireless/virtual/virt_wifi.c:523: struct rtnl_newlink_params *params,
drivers/net/wireless/virtual/virt_wifi.c-524- struct netlink_ext_ack *extack)
--
drivers/net/wireless/virtual/virt_wifi.c-526- struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
drivers/net/wireless/virtual/virt_wifi.c:527: struct net *link_net = rtnl_newlink_link_net(params);
drivers/net/wireless/virtual/virt_wifi.c-528- struct nlattr **tb = params->tb;
--
drivers/net/wwan/wwan_core.c=935=static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[],
--
drivers/net/wwan/wwan_core.c-969-
drivers/net/wwan/wwan_core.c:970:static int wwan_rtnl_newlink(struct net_device *dev,
drivers/net/wwan/wwan_core.c:971: struct rtnl_newlink_params *params,
drivers/net/wwan/wwan_core.c-972- struct netlink_ext_ack *extack)
--
drivers/net/wwan/wwan_core.c=1050=static struct rtnl_link_ops wwan_rtnl_link_ops __read_mostly = {
--
drivers/net/wwan/wwan_core.c-1054- .validate = wwan_rtnl_validate,
drivers/net/wwan/wwan_core.c:1055: .newlink = wwan_rtnl_newlink,
drivers/net/wwan/wwan_core.c-1056- .dellink = wwan_rtnl_dellink,
--
drivers/net/wwan/wwan_core.c=1062=static void wwan_create_default_link(struct wwan_device *wwandev,
--
drivers/net/wwan/wwan_core.c-1066- struct nlattr *data[IFLA_WWAN_MAX + 1];
drivers/net/wwan/wwan_core.c:1067: struct rtnl_newlink_params params = {
drivers/net/wwan/wwan_core.c-1068- .src_net = &init_net,
--
drivers/net/wwan/wwan_core.c-1115-
drivers/net/wwan/wwan_core.c:1116: if (WARN_ON(wwan_rtnl_newlink(dev, ¶ms, NULL))) {
drivers/net/wwan/wwan_core.c-1117- free_netdev(dev);
--
include/linux/if_macvlan.h=60=extern void macvlan_common_setup(struct net_device *dev);
include/linux/if_macvlan.h-61-
include/linux/if_macvlan.h:62:struct rtnl_newlink_params;
include/linux/if_macvlan.h-63-
include/linux/if_macvlan.h=64=extern int macvlan_common_newlink(struct net_device *dev,
include/linux/if_macvlan.h:65: struct rtnl_newlink_params *params,
include/linux/if_macvlan.h-66- struct netlink_ext_ack *extack);
--
include/net/rtnetlink.h=64=static inline int rtnl_msg_family(const struct nlmsghdr *nlh)
--
include/net/rtnetlink.h-72-/**
include/net/rtnetlink.h:73: * struct rtnl_newlink_params - parameters of rtnl_link_ops::newlink()
include/net/rtnetlink.h-74- *
--
include/net/rtnetlink.h-80- */
include/net/rtnetlink.h:81:struct rtnl_newlink_params {
include/net/rtnetlink.h-82- struct net *src_net;
--
include/net/rtnetlink.h-92- */
include/net/rtnetlink.h:93:static inline struct net *rtnl_newlink_link_net(struct rtnl_newlink_params *p)
include/net/rtnetlink.h-94-{
--
include/net/rtnetlink.h-100- */
include/net/rtnetlink.h:101:static inline struct net *rtnl_newlink_peer_net(struct rtnl_newlink_params *p)
include/net/rtnetlink.h-102-{
include/net/rtnetlink.h:103: return p->peer_net ? : rtnl_newlink_link_net(p);
include/net/rtnetlink.h-104-}
--
include/net/rtnetlink.h=140=struct rtnl_link_ops {
--
include/net/rtnetlink.h-162- int (*newlink)(struct net_device *dev,
include/net/rtnetlink.h:163: struct rtnl_newlink_params *params,
include/net/rtnetlink.h-164- struct netlink_ext_ack *extack);
--
net/8021q/vlan_netlink.c=138=static int vlan_newlink(struct net_device *dev,
net/8021q/vlan_netlink.c:139: struct rtnl_newlink_params *params,
net/8021q/vlan_netlink.c-140- struct netlink_ext_ack *extack)
net/8021q/vlan_netlink.c-141-{
net/8021q/vlan_netlink.c:142: struct net *link_net = rtnl_newlink_link_net(params);
net/8021q/vlan_netlink.c-143- struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
--
net/batman-adv/mesh-interface.c=1072=static int batadv_meshif_newlink(struct net_device *dev,
net/batman-adv/mesh-interface.c:1073: struct rtnl_newlink_params *params,
net/batman-adv/mesh-interface.c-1074- struct netlink_ext_ack *extack)
--
net/bridge/br_netlink.c=1556=static int br_dev_newlink(struct net_device *dev,
net/bridge/br_netlink.c:1557: struct rtnl_newlink_params *params,
net/bridge/br_netlink.c-1558- struct netlink_ext_ack *extack)
--
net/caif/chnl_net.c=441=static int ipcaif_newlink(struct net_device *dev,
net/caif/chnl_net.c:442: struct rtnl_newlink_params *params,
net/caif/chnl_net.c-443- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c=3688=EXPORT_SYMBOL(rtnl_create_link);
net/core/rtnetlink.c-3689-
net/core/rtnetlink.c:3690:struct rtnl_newlink_tbs {
net/core/rtnetlink.c-3691- struct nlattr *tb[IFLA_MAX + 1];
--
net/core/rtnetlink.c=3697=static int rtnl_changelink(const struct sk_buff *skb, struct nlmsghdr *nlh,
--
net/core/rtnetlink.c-3699- struct net_device *dev, struct net *tgt_net,
net/core/rtnetlink.c:3700: struct rtnl_newlink_tbs *tbs,
net/core/rtnetlink.c-3701- struct nlattr **data,
--
net/core/rtnetlink.c=3762=static int rtnl_group_changelink(const struct sk_buff *skb,
--
net/core/rtnetlink.c-3781-
net/core/rtnetlink.c:3782:static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
net/core/rtnetlink.c-3783- const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3790- unsigned char name_assign_type = NET_NAME_USER;
net/core/rtnetlink.c:3791: struct rtnl_newlink_params params = {
net/core/rtnetlink.c-3792- .src_net = sock_net(skb->sk),
--
net/core/rtnetlink.c=3858=static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3881-
net/core/rtnetlink.c:3882:static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-3883- const struct rtnl_link_ops *ops,
--
net/core/rtnetlink.c-3885- struct net *peer_net,
net/core/rtnetlink.c:3886: struct rtnl_newlink_tbs *tbs,
net/core/rtnetlink.c-3887- struct nlattr **data,
--
net/core/rtnetlink.c-3939-
net/core/rtnetlink.c:3940: return rtnl_newlink_create(skb, ifm, ops, tgt_net, link_net, peer_net, nlh,
net/core/rtnetlink.c-3941- tb, data, extack);
--
net/core/rtnetlink.c-3943-
net/core/rtnetlink.c:3944:static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-3945- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c-3949- struct rtnl_link_ops *ops = NULL;
net/core/rtnetlink.c:3950: struct rtnl_newlink_tbs *tbs;
net/core/rtnetlink.c-3951- struct rtnl_nets rtnl_nets;
--
net/core/rtnetlink.c-4054- rtnl_nets_lock(&rtnl_nets);
net/core/rtnetlink.c:4055: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
net/core/rtnetlink.c-4056- rtnl_nets_unlock(&rtnl_nets);
--
net/core/rtnetlink.c=4232=static int rtnl_linkprop(int cmd, struct sk_buff *skb, struct nlmsghdr *nlh,
--
net/core/rtnetlink.c-4279-
net/core/rtnetlink.c:4280:static int rtnl_newlinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
net/core/rtnetlink.c-4281- struct netlink_ext_ack *extack)
--
net/core/rtnetlink.c=7046=static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst = {
net/core/rtnetlink.c:7047: {.msgtype = RTM_NEWLINK, .doit = rtnl_newlink,
net/core/rtnetlink.c-7048- .flags = RTNL_FLAG_DOIT_PERNET},
--
net/core/rtnetlink.c-7060- {.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
net/core/rtnetlink.c:7061: {.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
net/core/rtnetlink.c-7062- {.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
--
net/hsr/hsr_netlink.c=32=static int hsr_newlink(struct net_device *dev,
net/hsr/hsr_netlink.c:33: struct rtnl_newlink_params *params,
net/hsr/hsr_netlink.c-34- struct netlink_ext_ack *extack)
net/hsr/hsr_netlink.c-35-{
net/hsr/hsr_netlink.c:36: struct net *link_net = rtnl_newlink_link_net(params);
net/hsr/hsr_netlink.c-37- struct nlattr **data = params->data;
--
net/ieee802154/6lowpan/core.c=133=static int lowpan_newlink(struct net_device *ldev,
net/ieee802154/6lowpan/core.c:134: struct rtnl_newlink_params *params,
net/ieee802154/6lowpan/core.c-135- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1398=static int ipgre_newlink(struct net_device *dev,
net/ipv4/ip_gre.c:1399: struct rtnl_newlink_params *params,
net/ipv4/ip_gre.c-1400- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1419=static int erspan_newlink(struct net_device *dev,
net/ipv4/ip_gre.c:1420: struct rtnl_newlink_params *params,
net/ipv4/ip_gre.c-1421- struct netlink_ext_ack *extack)
--
net/ipv4/ip_gre.c=1702=struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
--
net/ipv4/ip_gre.c-1704-{
net/ipv4/ip_gre.c:1705: struct rtnl_newlink_params params = { .src_net = net };
net/ipv4/ip_gre.c-1706- struct nlattr *tb[IFLA_MAX + 1];
--
net/ipv4/ip_vti.c=577=static int vti_newlink(struct net_device *dev,
net/ipv4/ip_vti.c:578: struct rtnl_newlink_params *params,
net/ipv4/ip_vti.c-579- struct netlink_ext_ack *extack)
--
net/ipv4/ipip.c=439=static int ipip_newlink(struct net_device *dev,
net/ipv4/ipip.c:440: struct rtnl_newlink_params *params,
net/ipv4/ipip.c-441- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_gre.c=1997=static int ip6gre_newlink(struct net_device *dev,
net/ipv6/ip6_gre.c:1998: struct rtnl_newlink_params *params,
net/ipv6/ip6_gre.c-1999- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_gre.c=2235=static int ip6erspan_newlink(struct net_device *dev,
net/ipv6/ip6_gre.c:2236: struct rtnl_newlink_params *params,
net/ipv6/ip6_gre.c-2237- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_tunnel.c=2005=static int ip6_tnl_newlink(struct net_device *dev,
net/ipv6/ip6_tunnel.c:2006: struct rtnl_newlink_params *params,
net/ipv6/ip6_tunnel.c-2007- struct netlink_ext_ack *extack)
--
net/ipv6/ip6_vti.c=1000=static int vti6_newlink(struct net_device *dev,
net/ipv6/ip6_vti.c:1001: struct rtnl_newlink_params *params,
net/ipv6/ip6_vti.c-1002- struct netlink_ext_ack *extack)
--
net/ipv6/sit.c=1553=static int ipip6_newlink(struct net_device *dev,
net/ipv6/sit.c:1554: struct rtnl_newlink_params *params,
net/ipv6/sit.c-1555- struct netlink_ext_ack *extack)
--
net/xfrm/xfrm_interface_core.c=816=static int xfrmi_newlink(struct net_device *dev,
net/xfrm/xfrm_interface_core.c:817: struct rtnl_newlink_params *params,
net/xfrm/xfrm_interface_core.c-818- struct netlink_ext_ack *extack)
]
|
| 85/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 52202
output: 48
thoughts: 525
Thoughts: **Analyzing the rtnetlink Locking Mechanism**
Okay, so `rtnl_newlink` is registered with the `RTNL_FLAG_DOIT_PERNET` flag. My initial thought is to quickly ascertain the implications of this. Now, the crucial thing to remember is that this flag is *not* `RTNL_FLAG_DOIT_UNLOCKED`. This means that we should delve into how `rtnetlink_rcv_msg` handles this scenario.
Let's see: `rtnetlink_rcv_msg` checks for `RTNL_FLAG_DOIT_UNLOCKED`. If it's *not* set (which is our case), the code executes a different path. It calls `rtnl_lock()` first, then retrieves the `link` structure. If `link` and its `doit` function are valid, it calls the `doit` function, which in our case is `rtnl_newlink`.
Here's where it gets interesting. Inside `rtnl_newlink`, there's a call to `rtnl_nets_lock(&rtnl_nets)`. That function in turn calls `rtnl_lock()` again! But, `rtnetlink_rcv_msg` already acquired `rtnl_lock()` before calling `rtnl_newlink`. This leads me to conclude that calling `rtnl_lock()` within `rtnl_newlink` *after* it's been locked by `rtnetlink_rcv_msg` will lead to a deadlock.
Therefore, the obvious conclusion is, `rtnl_newlink` *must* have `RTNL_FLAG_DOIT_UNLOCKED`. So the problem, if there is one, must lie somewhere else, and there should be a check for this somewhere. I need to verify that `rtnl_newlink` *does* indeed have `RTNL_FLAG_DOIT_UNLOCKED`, to confirm this assumption. Let me go back and re-check...
|
| 86/2 |
2026/05/25 23:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnetlink_rtnl_msg_handlers]
Results: map[SourceCode:7046: static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst = {
7047: {.msgtype = RTM_NEWLINK, .doit = rtnl_newlink,
7048: .flags = RTNL_FLAG_DOIT_PERNET},
7049: {.msgtype = RTM_DELLINK, .doit = rtnl_dellink,
7050: .flags = RTNL_FLAG_DOIT_PERNET_WIP},
7051: {.msgtype = RTM_GETLINK, .doit = rtnl_getlink,
7052: .dumpit = rtnl_dump_ifinfo, .flags = RTNL_FLAG_DUMP_SPLIT_NLM_DONE},
7053: {.msgtype = RTM_SETLINK, .doit = rtnl_setlink,
7054: .flags = RTNL_FLAG_DOIT_PERNET_WIP},
7055: {.msgtype = RTM_GETADDR, .dumpit = rtnl_dump_all},
7056: {.msgtype = RTM_GETROUTE, .dumpit = rtnl_dump_all},
7057: {.msgtype = RTM_GETNETCONF, .dumpit = rtnl_dump_all},
7058: {.msgtype = RTM_GETSTATS, .doit = rtnl_stats_get,
7059: .dumpit = rtnl_stats_dump},
7060: {.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
7061: {.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
7062: {.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
7063: {.protocol = PF_BRIDGE, .msgtype = RTM_GETLINK,
7064: .dumpit = rtnl_bridge_getlink},
7065: {.protocol = PF_BRIDGE, .msgtype = RTM_DELLINK,
7066: .doit = rtnl_bridge_dellink},
7067: {.protocol = PF_BRIDGE, .msgtype = RTM_SETLINK,
7068: .doit = rtnl_bridge_setlink},
7069: {.protocol = PF_BRIDGE, .msgtype = RTM_NEWNEIGH, .doit = rtnl_fdb_add},
7070: {.protocol = PF_BRIDGE, .msgtype = RTM_DELNEIGH, .doit = rtnl_fdb_del,
7071: .flags = RTNL_FLAG_BULK_DEL_SUPPORTED},
7072: {.protocol = PF_BRIDGE, .msgtype = RTM_GETNEIGH, .doit = rtnl_fdb_get,
7073: .dumpit = rtnl_fdb_dump},
7074: {.protocol = PF_BRIDGE, .msgtype = RTM_NEWMDB, .doit = rtnl_mdb_add},
7075: {.protocol = PF_BRIDGE, .msgtype = RTM_DELMDB, .doit = rtnl_mdb_del,
7076: .flags = RTNL_FLAG_BULK_DEL_SUPPORTED},
7077: {.protocol = PF_BRIDGE, .msgtype = RTM_GETMDB, .doit = rtnl_mdb_get,
7078: .dumpit = rtnl_mdb_dump},
7079: };
SourceFile:net/core/rtnetlink.c]
|
| 87/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 54144
output: 24
thoughts: 18
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, rendered in the first person:
**Decoding That `RTNL_FLAG_DOIT_PERNET` Flag...**
Hmm, let me think... `RTNL_FLAG_DOIT_PERNET`. Okay, I need to remind myself what that flag actually represents. It's used within the context of... routing netlink, right? And the name itself, "DOIT_PERNET"... that suggests it has something to do with *per-network* operations or configurations, as opposed to something system-wide. I need to check the definitions to be precise.
|
| 88/2 |
2026/05/25 23:50 |
tool |
grepper |
0m
Args: map[Expression:RTNL_FLAG_DOIT_PERNET]
Results: map[Output:include/net/rtnetlink.h=13=enum rtnl_link_flags {
include/net/rtnetlink.h-14- RTNL_FLAG_DOIT_UNLOCKED = BIT(0),
include/net/rtnetlink.h:15:#define RTNL_FLAG_DOIT_PERNET RTNL_FLAG_DOIT_UNLOCKED
include/net/rtnetlink.h:16:#define RTNL_FLAG_DOIT_PERNET_WIP RTNL_FLAG_DOIT_UNLOCKED
include/net/rtnetlink.h-17- RTNL_FLAG_BULK_DEL_SUPPORTED = BIT(1),
--
net/core/fib_rules.c=1446=static const struct rtnl_msg_handler fib_rules_rtnl_msg_handlers[] __initconst = {
net/core/fib_rules.c-1447- {.msgtype = RTM_NEWRULE, .doit = fib_nl_newrule,
net/core/fib_rules.c:1448: .flags = RTNL_FLAG_DOIT_PERNET},
net/core/fib_rules.c-1449- {.msgtype = RTM_DELRULE, .doit = fib_nl_delrule,
net/core/fib_rules.c:1450: .flags = RTNL_FLAG_DOIT_PERNET},
net/core/fib_rules.c-1451- {.msgtype = RTM_GETRULE, .dumpit = fib_nl_dumprule,
--
net/core/rtnetlink.c=7046=static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst = {
net/core/rtnetlink.c-7047- {.msgtype = RTM_NEWLINK, .doit = rtnl_newlink,
net/core/rtnetlink.c:7048: .flags = RTNL_FLAG_DOIT_PERNET},
net/core/rtnetlink.c-7049- {.msgtype = RTM_DELLINK, .doit = rtnl_dellink,
net/core/rtnetlink.c:7050: .flags = RTNL_FLAG_DOIT_PERNET_WIP},
net/core/rtnetlink.c-7051- {.msgtype = RTM_GETLINK, .doit = rtnl_getlink,
--
net/core/rtnetlink.c-7053- {.msgtype = RTM_SETLINK, .doit = rtnl_setlink,
net/core/rtnetlink.c:7054: .flags = RTNL_FLAG_DOIT_PERNET_WIP},
net/core/rtnetlink.c-7055- {.msgtype = RTM_GETADDR, .dumpit = rtnl_dump_all},
--
net/ipv4/devinet.c=2886=static const struct rtnl_msg_handler devinet_rtnl_msg_handlers[] __initconst = {
net/ipv4/devinet.c-2887- {.protocol = PF_INET, .msgtype = RTM_NEWADDR, .doit = inet_rtm_newaddr,
net/ipv4/devinet.c:2888: .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/devinet.c-2889- {.protocol = PF_INET, .msgtype = RTM_DELADDR, .doit = inet_rtm_deladdr,
net/ipv4/devinet.c:2890: .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/devinet.c-2891- {.protocol = PF_INET, .msgtype = RTM_GETADDR, .dumpit = inet_dump_ifaddr,
--
net/ipv4/fib_frontend.c=1693=static const struct rtnl_msg_handler fib_rtnl_msg_handlers[] __initconst = {
net/ipv4/fib_frontend.c-1694- {.protocol = PF_INET, .msgtype = RTM_NEWROUTE,
net/ipv4/fib_frontend.c:1695: .doit = inet_rtm_newroute, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/fib_frontend.c-1696- {.protocol = PF_INET, .msgtype = RTM_DELROUTE,
net/ipv4/fib_frontend.c:1697: .doit = inet_rtm_delroute, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/fib_frontend.c-1698- {.protocol = PF_INET, .msgtype = RTM_GETROUTE, .dumpit = inet_dump_fib,
--
net/ipv4/nexthop.c=4086=static const struct rtnl_msg_handler nexthop_rtnl_msg_handlers[] __initconst = {
net/ipv4/nexthop.c-4087- {.msgtype = RTM_NEWNEXTHOP, .doit = rtm_new_nexthop,
net/ipv4/nexthop.c:4088: .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/nexthop.c-4089- {.msgtype = RTM_DELNEXTHOP, .doit = rtm_del_nexthop,
net/ipv4/nexthop.c:4090: .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/nexthop.c-4091- {.msgtype = RTM_GETNEXTHOP, .doit = rtm_get_nexthop,
--
net/ipv4/nexthop.c-4095- {.protocol = PF_INET, .msgtype = RTM_NEWNEXTHOP,
net/ipv4/nexthop.c:4096: .doit = rtm_new_nexthop, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/nexthop.c-4097- {.protocol = PF_INET, .msgtype = RTM_GETNEXTHOP,
--
net/ipv4/nexthop.c-4099- {.protocol = PF_INET6, .msgtype = RTM_NEWNEXTHOP,
net/ipv4/nexthop.c:4100: .doit = rtm_new_nexthop, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv4/nexthop.c-4101- {.protocol = PF_INET6, .msgtype = RTM_GETNEXTHOP,
--
net/ipv6/addrconf.c=7433=static const struct rtnl_msg_handler addrconf_rtnl_msg_handlers[] __initconst_or_module = {
--
net/ipv6/addrconf.c-7436- {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_NEWADDR,
net/ipv6/addrconf.c:7437: .doit = inet6_rtm_newaddr, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv6/addrconf.c-7438- {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_DELADDR,
net/ipv6/addrconf.c:7439: .doit = inet6_rtm_deladdr, .flags = RTNL_FLAG_DOIT_PERNET},
net/ipv6/addrconf.c-7440- {.owner = THIS_MODULE, .protocol = PF_INET6, .msgtype = RTM_GETADDR,
]
|
| 89/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 56278
output: 42
thoughts: 767
Thoughts: **Analyzing a Kernel Lock Contention Issue**
Okay, so I'm looking at this kernel trace and it's got me thinking. It looks like `RTNL_FLAG_DOIT_PERNET` is essentially the same as `RTNL_FLAG_DOIT_UNLOCKED`. That's the first key. Now, `rtnl_newlink` is being called without the global `rtnl_mutex` held by `rtnetlink_rcv_msg`, which is interesting in itself. Then `rtnl_newlink` goes on to call `rtnl_nets_lock(&rtnl_nets)`. `rtnl_nets_lock` internally calls `rtnl_lock()`.
The trace tells me that `syz-executor/5806` is currently at the `rtnl_lock()` function, which is within the `rtnl_nets_lock()` call stack. That would imply it's trying to acquire the `rtnl_mutex`. But, hang on a second. The lockdep output says "1 lock held" which seems odd.
My understanding is that `rtnl_lock()` calls `mutex_lock(&rtnl_mutex)`. If `mutex_lock` is blocking, then lockdep wouldn't show the lock as being held because it hasn't acquired it yet. Lockdep only shows locks that are *successfully* acquired.
So, if `syz-executor/5806` *did* successfully acquire `rtnl_mutex`, it should be holding it, right? But the trace also shows `kworker/u4:4` *also* holding `rtnl_mutex`! That can't be! How can two tasks simultaneously hold the same mutex? That's a classic mutex violation. I need to understand that.
Wait, `rtnl_mutex` is a `struct mutex`, and the core locking primitive doesn't allow simultaneous ownership. Could `kworker/u4:4` be *blocked* within `rtnl_lock()`? If it was blocked, it would not be holding the lock! That wouldn't explain what's being reported. And it wouldn't be holding `rcu_state.exp_mutex` which is typical for being in `rtnl_lock()`.
Okay, I think I see a potential issue. `linkwatch_event` calls `rtnl_lock()`, then `__linkwatch_run_queue()`, and then `rtnl_unlock()`. Inside `rtnl_unlock()`, there is a call to `netdev_run_todo()`. `netdev_run_todo()` is interesting, because it will call `__rtnl_unlock()`, which *releases* `rtnl_mutex`! After the lock is released, it calls either `rcu_barrier()` or `synchronize_net()`.
Aha! `netdev_run_todo()` *releases* `rtnl_mutex`! That explains everything. It looks like there's a window where `rtnl_mutex` is released and then possibly reacquired or grabbed by another task before `kworker/u4:4` completes its operations! I need to re-examine `netdev_run_todo()` with this in mind to fully verify the issue. That's where the concurrency issue lies!
|
| 90/2 |
2026/05/25 23:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/dev.c IncludeLines:true Name:netdev_run_todo]
Results: map[SourceCode:11327: /* The sequence is:
11328: *
11329: * rtnl_lock();
11330: * ...
11331: * register_netdevice(x1);
11332: * register_netdevice(x2);
11333: * ...
11334: * unregister_netdevice(y1);
11335: * unregister_netdevice(y2);
11336: * ...
11337: * rtnl_unlock();
11338: * free_netdev(y1);
11339: * free_netdev(y2);
11340: *
11341: * We are invoked by rtnl_unlock().
11342: * This allows us to deal with problems:
11343: * 1) We can delete sysfs objects which invoke hotplug
11344: * without deadlocking with linkwatch via keventd.
11345: * 2) Since we run with the RTNL semaphore not held, we can sleep
11346: * safely in order to wait for the netdev refcnt to drop to zero.
11347: *
11348: * We must not return until all unregister events added during
11349: * the interval the lock was held have been completed.
11350: */
11351: void netdev_run_todo(void)
11352: {
11353: struct net_device *dev, *tmp;
11354: struct list_head list;
11355: int cnt;
11356: #ifdef CONFIG_LOCKDEP
11357: struct list_head unlink_list;
11358:
11359: list_replace_init(&net_unlink_list, &unlink_list);
11360:
11361: while (!list_empty(&unlink_list)) {
11362: dev = list_first_entry(&unlink_list, struct net_device,
11363: unlink_list);
11364: list_del_init(&dev->unlink_list);
11365: dev->nested_level = dev->lower_level - 1;
11366: }
11367: #endif
11368:
11369: /* Snapshot list, allow later requests */
11370: list_replace_init(&net_todo_list, &list);
11371:
11372: __rtnl_unlock();
11373:
11374: /* Wait for rcu callbacks to finish before next phase */
11375: if (!list_empty(&list))
11376: rcu_barrier();
11377:
11378: list_for_each_entry_safe(dev, tmp, &list, todo_list) {
11379: if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {
11380: netdev_WARN(dev, "run_todo but not unregistering\n");
11381: list_del(&dev->todo_list);
11382: continue;
11383: }
11384:
11385: netdev_lock(dev);
11386: WRITE_ONCE(dev->reg_state, NETREG_UNREGISTERED);
11387: netdev_unlock(dev);
11388: linkwatch_sync_dev(dev);
11389: }
11390:
11391: cnt = 0;
11392: while (!list_empty(&list)) {
11393: dev = netdev_wait_allrefs_any(&list);
11394: list_del(&dev->todo_list);
11395:
11396: /* paranoia */
11397: BUG_ON(netdev_refcnt_read(dev) != 1);
11398: BUG_ON(!list_empty(&dev->ptype_all));
11399: BUG_ON(!list_empty(&dev->ptype_specific));
11400: WARN_ON(rcu_access_pointer(dev->ip_ptr));
11401: WARN_ON(rcu_access_pointer(dev->ip6_ptr));
11402:
11403: netdev_do_free_pcpu_stats(dev);
11404: if (dev->priv_destructor)
11405: dev->priv_destructor(dev);
11406: if (dev->needs_free_netdev)
11407: free_netdev(dev);
11408:
11409: cnt++;
11410:
11411: /* Free network device */
11412: kobject_put(&dev->dev.kobj);
11413: }
11414: if (cnt && atomic_sub_and_test(cnt, &dev_unreg_count))
11415: wake_up(&netdev_unregistering_wq);
11416: }
SourceFile:net/core/dev.c]
|
| 91/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 59275
output: 42
thoughts: 1032
Thoughts: **Analyzing a Potential RTNL Mutex Lockdep Issue**
Okay, let's break this down. It seems I'm staring at a lockdep issue involving the `rtnl_mutex`. I see that `netdev_run_todo` calls `__rtnl_unlock()`, which, in turn, calls `mutex_unlock(&rtnl_mutex)`. Logically, I'd expect that once `mutex_unlock` is called, lockdep *should* release the `rtnl_mutex` from the "held locks" list. I'm puzzled because I'm seeing that a `kworker` thread, specifically `kworker/u4:4`, *still* appears to hold the `rtnl_mutex` according to lockdep, even after the unlock.
My initial thought was maybe `__rtnl_unlock` wasn't properly signaling to lockdep but the code explicitly calls `mutex_unlock`. So, that can't be it.
Then I checked where `kworker/u4:4` is blocked and I think I've found it: `synchronize_rcu_expedited`. Digging deeper, it appears this is called within a sequence that begins with `linkwatch_event` -> `__linkwatch_run_queue` -> `linkwatch_do_dev` -> `dev_deactivate` -> `dev_deactivate_many` -> `synchronize_net()` -> `synchronize_rcu_expedited()`. Crucially, `linkwatch_event` calls `rtnl_lock()` *before* calling `__linkwatch_run_queue`, which in turn calls `synchronize_net()`. So, the `kworker` is acquiring the `rtnl_mutex` *before* the call to `synchronize_rcu_expedited()`. This makes sense. It's holding the lock, then getting blocked in the RCU synchronization.
But now, here's the kicker: Lockdep also shows that `syz-executor/5806` *also* holds the `rtnl_mutex`. This is where it's starting to make less sense. I see that `syz-executor/5806` is calling `rtnl_nets_lock`. However, `rtnl_nets_lock` calls `rtnl_lock()` and if it's *blocked* in `rtnl_lock()`, it shouldn't have the lock already held. The lockdep output is confusing because it shows THREE lines for `#0` for the same lock. But that means it is ONE lock acquired at `rtnl_newlink` -> `rtnl_nets_lock` -> `rtnl_lock`. If it shows in the "held locks" list, it *must* have acquired the lock.
But then, I also have `kworker/u4:4` with the same mutex, in the held locks list.
The real problem is: How can two tasks simultaneously hold the *same* mutex? Unless this isn't a mutex at all. So, I have to go back and check the definition of `rtnl_mutex`.
|
| 92/2 |
2026/05/25 23:50 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnl_mutex]
Results: map[SourceCode: 76: static DEFINE_MUTEX(rtnl_mutex);
SourceFile:net/core/rtnetlink.c]
|
| 93/2 |
2026/05/25 23:50 |
llm |
expert |
0m
Model:
Tokens:
input: 61072
output: 43
thoughts: 2522
Thoughts: **Analyzing a Potential Kernel Deadlock**
Okay, let's break this down. First thought, `DEFINE_MUTEX`. Now I see `syz-executor/5806` is holding `rtnl_mutex` and `kworker/u4:4` is blocked in `rtnl_lock()`. Wait a second... I need to look at the `kworker/u4:4` stack trace again to confirm. The stack trace shows `kworker/u4:4` holds `rcu_state.exp_mutex`. But, if it were blocked in `rtnl_lock()`, it *shouldn't* hold `rcu_state.exp_mutex` because that's acquired *after* the `rtnl_lock()`. Thus, `kworker/u4:4` must have *already* acquired `rtnl_mutex` before calling `synchronize_rcu_expedited()`. But how can `syz-executor/5806` also hold `rtnl_mutex`?
Let's look at the lockdep output for `syz-executor/5806`. It seems the call stack includes `rtnl_nets_lock`, and this is where it acquires `rtnl_mutex`. If `rtnl_newlink` uses `rtnl_nets_lock()` that calls `rtnl_lock()` *again*, which would cause deadlock since it's not recursive. But, wait... `rtnl_newlink` is registered with `RTNL_FLAG_DOIT_PERNET` which means `RTNL_FLAG_DOIT_UNLOCKED`. This means that `rtnetlink_rcv_msg` doesn't call `rtnl_lock()` first.
If `rtnetlink_rcv_msg` *doesn't* call `rtnl_lock()` first, `syz-executor/5806` acquires `rtnl_mutex` inside `rtnl_nets_lock()` only. But then it seems, `kworker/u4:4` cannot be holding the same `rtnl_mutex`. Wait! Perhaps `syz-executor/5806` is blocked waiting for `rtnl_mutex` to be available. But, if it's blocked, why is lockdep showing that it *holds* the lock? Let's rule out any issues with the address. No, the address for `rtnl_mutex` is the same in both outputs.
Wait, it looks like `syz-executor/5806` isn't blocked. The bug report only says `khidpd` and `syz.0.54` are blocked. Therefore, `syz-executor/5806` is just another task running or runnable. It might have *just* acquired the lock and, if that's the case, then, how is `kworker/u4:4` also holding the same mutex? That doesn't make any sense unless there is a mutex corruption or the lockdep output is wrong. I need to re-examine the lockdep output.
Okay, is it possible `syz-executor/5806` is blocked in `rtnl_lock()` and lockdep is showing the lock it is trying to acquire? No, lockdep prints `p->held_locks`. Okay... *mutex_lock* calls *mutex_lock_nested*. This calls `__mutex_lock_common` that calls `mutex_acquire`. `mutex_acquire` adds the lock to `p->held_locks` *before* the task actually waits! Yes, that's it! If a task is blocked in `mutex_lock`, lockdep *will* show it as holding the lock!
Aha! `syz-executor/5806` is blocked in `rtnl_lock()` waiting for `rtnl_mutex`. `syz.0.54/5547` is blocked in `mutex_lock(&hdev->lock)`. `khidpd_000d2f19/5507` is blocked in `cancel_work_sync(&session->dev_init)`. It is waiting for `kworker/0:3`. This `dev_init` then calls `hid_add_device`, which is what's probing the driver, which probably calls `rtnl_lock()`.
Now let's check what's up with the other worker, `kworker/u4:4`, it holds `rtnl_mutex` and it's blocked in `synchronize_rcu_expedited()`. This will wait for an RCU reader to finish. So, who is the RCU reader? If `k
|
| 94/2 |
2026/05/25 23:51 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/hid/hid-core.c IncludeLines:true Name:hid_add_device]
Results: map[SourceCode:2854: int hid_add_device(struct hid_device *hdev)
2855: {
2856: static atomic_t id = ATOMIC_INIT(0);
2857: int ret;
2858:
2859: if (WARN_ON(hdev->status & HID_STAT_ADDED))
2860: return -EBUSY;
2861:
2862: hdev->quirks = hid_lookup_quirk(hdev);
2863:
2864: /* we need to kill them here, otherwise they will stay allocated to
2865: * wait for coming driver */
2866: if (hid_ignore(hdev))
2867: return -ENODEV;
2868:
2869: /*
2870: * Check for the mandatory transport channel.
2871: */
2872: if (!hdev->ll_driver->raw_request) {
2873: hid_err(hdev, "transport driver missing .raw_request()\n");
2874: return -EINVAL;
2875: }
2876:
2877: /*
2878: * Read the device report descriptor once and use as template
2879: * for the driver-specific modifications.
2880: */
2881: ret = hdev->ll_driver->parse(hdev);
2882: if (ret)
2883: return ret;
2884: if (!hdev->dev_rdesc)
2885: return -ENODEV;
2886:
2887: /*
2888: * Scan generic devices for group information
2889: */
2890: if (hid_ignore_special_drivers) {
2891: hdev->group = HID_GROUP_GENERIC;
2892: } else if (!hdev->group &&
2893: !(hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)) {
2894: ret = hid_scan_report(hdev);
2895: if (ret)
2896: hid_warn(hdev, "bad device descriptor (%d)\n", ret);
2897: }
2898:
2899: hdev->id = atomic_inc_return(&id);
2900:
2901: /* XXX hack, any other cleaner solution after the driver core
2902: * is converted to allow more than 20 bytes as the device name? */
2903: dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
2904: hdev->vendor, hdev->product, hdev->id);
2905:
2906: hid_debug_register(hdev, dev_name(&hdev->dev));
2907: ret = device_add(&hdev->dev);
2908: if (!ret)
2909: hdev->status |= HID_STAT_ADDED;
2910: else
2911: hid_debug_unregister(hdev);
2912:
2913: return ret;
2914: }
SourceFile:drivers/hid/hid-core.c]
|
| 95/2 |
2026/05/25 23:51 |
llm |
expert |
0m
Model:
Tokens:
input: 65579
output: 20
thoughts: 86
Thoughts: **Analyzing the Device Addition Process**
Okay, so `device_add` is the starting point, that's where everything kicks off. First, we're probing the driver – standard operating procedure. Now, the interesting part: if the driver turns out to be a network driver (and this seems to be the case with some USB network adapters that masquerade as HID devices), it *might* call `register_netdev`. That's a key detail to watch for.
The reproducer provides some vital clues, specifically `vendor: 0xd` and `product: 0x2f19`. This suggests a targeted device, and that rings a bell. My next step has to be checking whether there's a specific HID driver explicitly written for *this* vendor/product ID combination. That's where I need to focus my investigation.
|
| 96/2 |
2026/05/25 23:51 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h-12424-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h:12425:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_2_offset.h-12426-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h-11533-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h:11534:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_4_offset.h-11535-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h-12289-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h:12290:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_5_offset.h-12291-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h-13020-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h:13021:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_1_6_offset.h-13022-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h-11698-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h:11699:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_0_offset.h-11700-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h-11707-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h:11708:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_2_1_offset.h-11709-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h-10432-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h:10433:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_0_offset.h-10434-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h-10411-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h:10412:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_5_1_offset.h-10413-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h-12509-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h:12510:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_3_6_0_offset.h-12511-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h-11888-#define regPANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h:11889:#define regBL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dcn/dcn_4_1_0_offset.h-11890-#define regBL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h-7167-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h:7168:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_3_1_4_offset.h-7169-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h-85-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h:86:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_0_offset.h-87-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h-72-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h:73:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_2_offset.h-74-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h-89-#define regPWRSEQ0_PANEL_PWRSEQ_REF_DIV1_BASE_IDX 2
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h:90:#define regPWRSEQ0_BL_PWM_CNTL 0x2f19
drivers/gpu/drm/amd/include/asic_reg/dpcs/dpcs_4_2_3_offset.h-91-#define regPWRSEQ0_BL_PWM_CNTL_BASE_IDX 2
--
sound/soc/codecs/rt715-sdca-sdw.h=13=static const struct reg_default rt715_reg_defaults_sdca[] = {
--
sound/soc/codecs/rt715-sdca-sdw.h-66- { 0x2f18, 0x00 },
sound/soc/codecs/rt715-sdca-sdw.h:67: { 0x2f19, 0x03 },
sound/soc/codecs/rt715-sdca-sdw.h-68- { 0x2f1a, 0x00 },
]
|
| 97/2 |
2026/05/25 23:51 |
llm |
expert |
0m
Model:
Tokens:
input: 68457
output: 45
thoughts: 900
Thoughts: **Deadlock Analysis in Bluetooth HIDP Driver**
Okay, so `0x2f19` is just a garbage value, that's not the key. The first thing that comes to mind is the `hid-generic` driver. I need to be sure that it is not causing the problem. Does that one need `rtnl_mutex`? Nope, it doesn't. And I think I'm safe there. What about the report descriptor `22 ad 35`? Is it triggering some unexpected network interface creation in `hid-generic`? It's too short. That seems unlikely.
Alright, what about `hidp_session_dev_add`? Does *that* call `input_register_device`? Yep, it does. Okay, does that method acquire `rtnl_mutex`? No, that also doesn't seem to be the problem.
Wait, `kworker/0:3` is executing `dev_init`, which means it's initializing a new device. Is it possible it is blocked on `hdev->lock`? We should check `hidp_session_dev_add`. It seems like that's where the problem is. Wait, let's go back and check the function again. Does it acquire `hdev->lock`? It doesn't explicitly. But what about the functions that it calls?
Aha, `syz.0.54` is blocked on `hdev->lock` in `l2cap_chan_connect`. We know `khidpd` holds `hdev->lock` and is blocked on `cancel_work_sync(&session->dev_init)`. I think this is where the deadlock occurs.
So, if `kworker/0:3` is executing `session->dev_init`, and it tries to acquire `hdev->lock`, it *will* deadlock! That's it. We have a concurrency issue.
Does `hidp_session_dev_add` acquire `hdev->lock`? Let me examine the code again. Okay, it calls `hid_add_device`. Let's check this function. What about `hid_add_device`? `hid_add_device` in turn calls `device_add`, which should probe the drivers. The probe should then call `hid_hw_start`, and the driver probe should then call `hidp_start`. However, `hidp_start` simply returns 0. So no luck there. Does `hid_add_device` might call `hid_hw_open`? No, that's only called when a user space app accesses the device, e.g. using evdev.
Right, back to `hidp_session_dev_add` again. Does *that* acquire `hdev->lock`?
Okay, wait, here's what's happening in `hidp_session_dev_work`:
`hidp_session_dev_work` is the work function for `dev_init`. It calls `hidp_session_dev_add`. If that fails, it calls `hidp_session_terminate`. But if `hidp_session_dev_add` fails, then the deadlock shouldn't happen, as the device will be torn down. I've found it! We have a classic deadlock situation where `khidpd` holding `hdev->lock` is blocking `kworker/0:3` which needs the same lock. It's a race between `kworker/0:3` and `khidpd`.
|
| 98/2 |
2026/05/25 23:51 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_session_terminate]
Results: map[SourceCode:1071: /*
1072: * Terminate session thread
1073: * Wake up session thread and notify it to stop. This is asynchronous and
1074: * returns immediately. Call this whenever a runtime error occurs and you want
1075: * the session to stop.
1076: * Note: wake_up_interruptible() performs any necessary memory-barriers for us.
1077: */
1078: static void hidp_session_terminate(struct hidp_session *session)
1079: {
1080: atomic_inc(&session->terminate);
1081: /*
1082: * See the comment preceding the call to wait_woken()
1083: * in hidp_session_run().
1084: */
1085: wake_up_interruptible(&hidp_session_wq);
1086: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 99/2 |
2026/05/25 23:51 |
llm |
expert |
0m
Model:
Tokens:
input: 70442
output: 18
thoughts: 4279
Thoughts: **Deadlock Analysis: hidp_session_sem and workqueue issues**
Okay, so here's the thought process: I'm staring at this deadlock and trying to figure out what's going on. `hidp_session_terminate` doesn't grab `hdev->lock`, which is a good starting point. The crucial thing is the uevent triggered by `hid_add_device`. I was wondering if this uevent could launch something that would block.
I was originally thinking that `kworker/0:3` was the issue, but it turned out not to be the case. I initially thought it could be blocked on `hdev->lock` or potentially `rtnl_mutex`. Since I know `hid_add_device` calls a uevent, perhaps an input registration calls the rtnl_lock? But that seemed unlikely, especially since the uevent processing is often asynchronous.
But I was looking at the stack trace and saw that `kworker/0:3` was actually executing `session->dev_init`. I noted that `kworker/u4:4` was blocked in `synchronize_rcu_expedited`. This made me focus on the RCU-related blocking.
So then, I found that `syz-executor/5806` was holding `rtnl_mutex`, and `rtnetlink_rcv_msg` calls `rcu_read_lock`. I figured if `RTNL_FLAG_DOIT_UNLOCKED` is set, it calls `rcu_read_unlock()` before calling `doit`. Now the question was who's holding the RCU lock? I checked `khidpd` and it wasn't holding it directly. I knew that it could be blocked in `__cancel_work_sync` which waits for the `dev_init` work to complete. `dev_init` is `hidp_session_dev_work`.
I checked whether `hidp_session_dev_add` could be blocking on `hdev->lock`. And now I realized `hid_add_device` calls `device_add`, which calls `kobject_uevent`. This led me to think about `call_usermodehelper`, which *can* block, waiting for a userspace process (like `udevd`) to finish. However, for uevents this should be asynchronous (`UMH_NO_WAIT`).
I thought `hid_add_device` could potentially block on `rtnl_mutex`, but `hidp_session_dev_add` does not register a network device.
Here's the key. I noticed that all `syz` tasks are holding `hidp_session_sem`. `khidpd` is holding it for WRITE. I finally realized that `khidpd` must be the one *holding* the semaphore for WRITE! Since `khidpd` holds it for WRITE, all other tasks are blocked trying to acquire it for READ! I saw that lockdep correctly shows the "contending" `hidp_session_sem` lock.
I realized that `khidpd` is blocked in `cancel_work_sync(&session->dev_init)`. It holds `hidp_session_sem` (WRITE) and the main `hdev->lock`, while the other threads are waiting for this work to finish! I realize this `dev_init` work is preventing progress. I tried to figure out why this work is not finishing. I knew this would cause a deadlock, and that `hid_add_device` could potentially block. And I had a thought on the `rtnl_mutex` for a network device. I still believe `hidp_session_dev_work` is the source. The report descriptor from the attacker looks like a "normal" report descriptor. I still thought of it as potentially creating a network device.
I considered whether a specific report descriptor would cause a HID driver to register a network device, but that seemed unlikely. I then thought, maybe `hid-generic` would call a network driver. But, `hid-generic` only registers input devices. And I was back to the `rtnl_mutex` which didn't feel right. I went through the probing and startup, but nothing looked problematic. I checked for anything suspicious in a specific hid driver but found nothing. Now I know that `khidpd` is blocked because of the semaphore!
|
| 100/2 |
2026/05/25 23:52 |
tool |
grepper |
0m
Args: map[Expression:register_netdev]
Results: map[Output:
Full output is too long, showing 500 out of 9378 lines.
Use more precise expression if possible.
[Documentation/arch/powerpc/eeh-pci-error-recovery.rst=197=The following sequence is an example of the pcnet32 device driver::
--
Documentation/arch/powerpc/eeh-pci-error-recovery.rst-227- calls
Documentation/arch/powerpc/eeh-pci-error-recovery.rst:228: unregister_netdev() // in /net/core/dev.c
Documentation/arch/powerpc/eeh-pci-error-recovery.rst-229- {
--
Documentation/arch/powerpc/eeh-pci-error-recovery.rst=249=which calls struct pci_driver->remove() which is pcnet32_remove_one()
Documentation/arch/powerpc/eeh-pci-error-recovery.rst:250:which calls unregister_netdev() (in net/core/dev.c)
Documentation/arch/powerpc/eeh-pci-error-recovery.rst-251-which calls dev_close() (in net/core/dev.c)
--
Documentation/driver-api/driver-model/devres.rst=380=NET
--
Documentation/driver-api/driver-model/devres.rst-382- devm_alloc_etherdev_mqs()
Documentation/driver-api/driver-model/devres.rst:383: devm_register_netdev()
Documentation/driver-api/driver-model/devres.rst-384-
--
Documentation/networking/driver.rst=28=Auto-close
--
Documentation/networking/driver.rst-30-
Documentation/networking/driver.rst:31:The ndo_stop routine will be called by unregister_netdevice
Documentation/networking/driver.rst-32-if device is still UP.
--
Documentation/networking/multiqueue.rst=24=device is still operational. netdev->queue_lock is still used when the device
Documentation/networking/multiqueue.rst:25:comes online or when it's completely shut down (unregister_netdev(), etc.).
Documentation/networking/multiqueue.rst-26-
--
Documentation/networking/netdevices.rst=27=First group can be used in normal contexts where ``rtnl_lock`` is not already
Documentation/networking/netdevices.rst:28:held: register_netdev(), unregister_netdev().
Documentation/networking/netdevices.rst-29-Second group can be used when ``rtnl_lock`` is already held:
Documentation/networking/netdevices.rst:30:register_netdevice(), unregister_netdevice(), free_netdevice().
Documentation/networking/netdevices.rst-31-
--
Documentation/networking/netdevices.rst=38=In that case the struct net_device registration is done using
Documentation/networking/netdevices.rst:39:the register_netdev(), and unregister_netdev() functions:
Documentation/networking/netdevices.rst-40-
--
Documentation/networking/netdevices.rst-52-
Documentation/networking/netdevices.rst:53: /* ... do all device setup before calling register_netdev() ...
Documentation/networking/netdevices.rst-54- */
Documentation/networking/netdevices.rst-55-
Documentation/networking/netdevices.rst:56: err = register_netdev(dev);
Documentation/networking/netdevices.rst-57- if (err)
--
Documentation/networking/netdevices.rst-69- {
Documentation/networking/netdevices.rst:70: unregister_netdev(dev);
Documentation/networking/netdevices.rst-71- free_netdev(dev);
--
Documentation/networking/netdevices.rst-73-
Documentation/networking/netdevices.rst:74:Note that after calling register_netdev() the device is visible in the system.
Documentation/networking/netdevices.rst-75-Users can open it and start sending / receiving traffic immediately,
--
Documentation/networking/netdevices.rst=77=registration.
Documentation/networking/netdevices.rst-78-
Documentation/networking/netdevices.rst:79:unregister_netdev() closes the device and waits for all users to be done
Documentation/networking/netdevices.rst-80-with it. The memory of struct net_device itself may still be referenced
Documentation/networking/netdevices.rst=81=by sysfs but all operations on that device will fail.
Documentation/networking/netdevices.rst-82-
Documentation/networking/netdevices.rst:83:free_netdev() can be called after unregister_netdev() returns on when
Documentation/networking/netdevices.rst:84:register_netdev() failed.
Documentation/networking/netdevices.rst-85-
--
Documentation/networking/netdevices.rst=94=Example flow of netdev handling under ``rtnl_lock``:
--
Documentation/networking/netdevices.rst-133-
Documentation/networking/netdevices.rst:134: err = register_netdevice(dev);
Documentation/networking/netdevices.rst-135- if (err)
Documentation/networking/netdevices.rst:136: /* register_netdevice() calls destructor on failure */
Documentation/networking/netdevices.rst-137- goto err_free_dev;
Documentation/networking/netdevices.rst-138-
Documentation/networking/netdevices.rst:139: /* If anything fails now unregister_netdevice() (or unregister_netdev())
Documentation/networking/netdevices.rst-140- * will take care of calling my_destructor and free_netdev().
--
Documentation/networking/netdevices.rst=152=If struct net_device.priv_destructor is set it will be called by the core
Documentation/networking/netdevices.rst:153:some time after unregister_netdevice(), it will also be called if
Documentation/networking/netdevices.rst:154:register_netdevice() fails. The callback may be invoked with or without
Documentation/networking/netdevices.rst-155-``rtnl_lock`` held.
--
Documentation/networking/netdevices.rst=160=Setting struct net_device.needs_free_netdev makes core call free_netdevice()
Documentation/networking/netdevices.rst:161:automatically after unregister_netdevice() when all references to the device
Documentation/networking/netdevices.rst:162:are gone. It only takes effect after a successful call to register_netdevice()
Documentation/networking/netdevices.rst:163:so if register_netdevice() fails driver is responsible for calling
Documentation/networking/netdevices.rst-164-free_netdev().
Documentation/networking/netdevices.rst-165-
Documentation/networking/netdevices.rst:166:free_netdev() is safe to call on error paths right after unregister_netdevice()
Documentation/networking/netdevices.rst:167:or when register_netdevice() fails. Parts of netdev (de)registration process
Documentation/networking/netdevices.rst-168-happen after ``rtnl_lock`` is released, therefore in those cases free_netdev()
--
Documentation/networking/switchdev.rst=85=On switchdev driver initialization, the driver will allocate and register a
Documentation/networking/switchdev.rst:86:struct net_device (using register_netdev()) for each enumerated physical switch
Documentation/networking/switchdev.rst-87-port, called the port netdev. A port netdev is the software representation of
--
Documentation/networking/xfrm_device.rst=93=Flow
--
Documentation/networking/xfrm_device.rst-95-
Documentation/networking/xfrm_device.rst:96:At probe time and before the call to register_netdev(), the driver should
Documentation/networking/xfrm_device.rst-97-set up local data structures and XFRM callbacks, and set the feature bits.
--
arch/m68k/emu/nfeth.c=185=static struct net_device * __init nfeth_probe(int unit)
--
arch/m68k/emu/nfeth.c-207-
arch/m68k/emu/nfeth.c:208: err = register_netdev(dev);
arch/m68k/emu/nfeth.c-209- if (err) {
--
arch/m68k/emu/nfeth.c=251=static void __exit nfeth_cleanup(void)
--
arch/m68k/emu/nfeth.c-256- if (nfeth_dev[i]) {
arch/m68k/emu/nfeth.c:257: unregister_netdev(nfeth_dev[i]);
arch/m68k/emu/nfeth.c-258- free_netdev(nfeth_dev[i]);
--
arch/um/drivers/vector_kern.c=786=static int vector_remove(int n, char **error_out)
--
arch/um/drivers/vector_kern.c-798- return -EBUSY;
arch/um/drivers/vector_kern.c:799: unregister_netdev(dev);
arch/um/drivers/vector_kern.c-800- platform_device_unregister(&vec_d->pdev);
--
arch/um/drivers/vector_kern.c=1579=static void vector_eth_configure(
--
arch/um/drivers/vector_kern.c-1605- /* If this name ends up conflicting with an existing registered
arch/um/drivers/vector_kern.c:1606: * netdevice, that is OK, register_netdev{,ice}() will notice this
arch/um/drivers/vector_kern.c-1607- * and fail.
--
arch/um/drivers/vector_kern.c-1654- rtnl_lock();
arch/um/drivers/vector_kern.c:1655: err = register_netdevice(dev);
arch/um/drivers/vector_kern.c-1656- rtnl_unlock();
--
arch/xtensa/platforms/iss/network.c=478=static void iss_net_configure(int index, char *init)
--
arch/xtensa/platforms/iss/network.c-499- * If this name ends up conflicting with an existing registered
arch/xtensa/platforms/iss/network.c:500: * netdevice, that is OK, register_netdev{,ice}() will notice this
arch/xtensa/platforms/iss/network.c-501- * and fail.
--
arch/xtensa/platforms/iss/network.c-538- rtnl_lock();
arch/xtensa/platforms/iss/network.c:539: if (register_netdevice(dev)) {
arch/xtensa/platforms/iss/network.c-540- rtnl_unlock();
--
drivers/firewire/net.c=1432=static int fwnet_probe(struct fw_unit *unit,
--
drivers/firewire/net.c-1495-
drivers/firewire/net.c:1496: ret = register_netdev(net);
drivers/firewire/net.c-1497- if (ret)
--
drivers/firewire/net.c-1505- if (ret && allocated_netdev) {
drivers/firewire/net.c:1506: unregister_netdev(net);
drivers/firewire/net.c-1507- list_del(&dev->dev_link);
--
drivers/firewire/net.c=1552=static void fwnet_remove(struct fw_unit *unit)
--
drivers/firewire/net.c-1565- if (list_empty(&dev->peer_list)) {
drivers/firewire/net.c:1566: unregister_netdev(net);
drivers/firewire/net.c-1567-
--
drivers/hsi/clients/ssi_protocol.c=1073=static int ssi_protocol_probe(struct device *dev)
--
drivers/hsi/clients/ssi_protocol.c-1127- netif_carrier_off(ssi->netdev);
drivers/hsi/clients/ssi_protocol.c:1128: err = register_netdev(ssi->netdev);
drivers/hsi/clients/ssi_protocol.c-1129- if (err < 0) {
--
drivers/hsi/clients/ssi_protocol.c=1150=static int ssi_protocol_remove(struct device *dev)
--
drivers/hsi/clients/ssi_protocol.c-1155- list_del(&ssi->link);
drivers/hsi/clients/ssi_protocol.c:1156: unregister_netdev(ssi->netdev);
drivers/hsi/clients/ssi_protocol.c-1157- ssip_free_cmds(ssi);
--
drivers/infiniband/core/cma.c=5445=static int __init cma_init(void)
--
drivers/infiniband/core/cma.c-5470- ib_sa_register_client(&sa_client);
drivers/infiniband/core/cma.c:5471: register_netdevice_notifier(&cma_nb);
drivers/infiniband/core/cma.c-5472- register_netevent_notifier(&cma_netevent_cb);
--
drivers/infiniband/core/cma.c-5487- unregister_netevent_notifier(&cma_netevent_cb);
drivers/infiniband/core/cma.c:5488: unregister_netdevice_notifier(&cma_nb);
drivers/infiniband/core/cma.c-5489- ib_sa_unregister_client(&sa_client);
--
drivers/infiniband/core/cma.c=5496=static void __exit cma_cleanup(void)
--
drivers/infiniband/core/cma.c-5500- unregister_netevent_notifier(&cma_netevent_cb);
drivers/infiniband/core/cma.c:5501: unregister_netdevice_notifier(&cma_nb);
drivers/infiniband/core/cma.c-5502- ib_sa_unregister_client(&sa_client);
--
drivers/infiniband/core/device.c=2977=static int __init ib_core_init(void)
--
drivers/infiniband/core/device.c-3047-
drivers/infiniband/core/device.c:3048: register_netdevice_notifier(&nb_netdevice);
drivers/infiniband/core/device.c-3049-
--
drivers/infiniband/core/device.c=3077=static void __exit ib_core_cleanup(void)
drivers/infiniband/core/device.c-3078-{
drivers/infiniband/core/device.c:3079: unregister_netdevice_notifier(&nb_netdevice);
drivers/infiniband/core/device.c-3080- roce_gid_mgmt_cleanup();
--
drivers/infiniband/core/roce_gid_mgmt.c=919=int __init roce_gid_mgmt_init(void)
--
drivers/infiniband/core/roce_gid_mgmt.c-932- */
drivers/infiniband/core/roce_gid_mgmt.c:933: register_netdevice_notifier(&nb_netdevice);
drivers/infiniband/core/roce_gid_mgmt.c-934-
--
drivers/infiniband/core/roce_gid_mgmt.c=938=void __exit roce_gid_mgmt_cleanup(void)
--
drivers/infiniband/core/roce_gid_mgmt.c-942- unregister_inetaddr_notifier(&nb_inetaddr);
drivers/infiniband/core/roce_gid_mgmt.c:943: unregister_netdevice_notifier(&nb_netdevice);
drivers/infiniband/core/roce_gid_mgmt.c-944- /* Ensure all gid deletion tasks complete before we go down,
--
drivers/infiniband/hw/bnxt_re/main.c=509=static struct bnxt_ulp_ops bnxt_re_ulp_ops = {
--
drivers/infiniband/hw/bnxt_re/main.c-516-
drivers/infiniband/hw/bnxt_re/main.c:517:static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev)
drivers/infiniband/hw/bnxt_re/main.c-518-{
--
drivers/infiniband/hw/bnxt_re/main.c=2075=static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 op_type)
--
drivers/infiniband/hw/bnxt_re/main.c-2085- /* Registered a new RoCE device instance to netdev */
drivers/infiniband/hw/bnxt_re/main.c:2086: rc = bnxt_re_register_netdev(rdev);
drivers/infiniband/hw/bnxt_re/main.c-2087- if (rc) {
--
drivers/infiniband/hw/erdma/erdma_main.c=86=static int erdma_device_register(struct erdma_dev *dev)
--
drivers/infiniband/hw/erdma/erdma_main.c-105- dev->netdev_nb.notifier_call = erdma_netdev_event;
drivers/infiniband/hw/erdma/erdma_main.c:106: ret = register_netdevice_notifier(&dev->netdev_nb);
drivers/infiniband/hw/erdma/erdma_main.c-107- if (ret) {
--
drivers/infiniband/hw/erdma/erdma_main.c=616=static void erdma_ib_device_remove(struct pci_dev *pdev)
--
drivers/infiniband/hw/erdma/erdma_main.c-619-
drivers/infiniband/hw/erdma/erdma_main.c:620: unregister_netdevice_notifier(&dev->netdev_nb);
drivers/infiniband/hw/erdma/erdma_main.c-621- ib_unregister_device(&dev->ibdev);
--
drivers/infiniband/hw/hns/hns_roce_main.c=616=static void hns_roce_unregister_device(struct hns_roce_dev *hr_dev)
--
drivers/infiniband/hw/hns/hns_roce_main.c-620- hr_dev->active = false;
drivers/infiniband/hw/hns/hns_roce_main.c:621: unregister_netdevice_notifier(&iboe->nb);
drivers/infiniband/hw/hns/hns_roce_main.c-622- ib_unregister_device(&hr_dev->ib_dev);
--
drivers/infiniband/hw/hns/hns_roce_main.c=712=static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
--
drivers/infiniband/hw/hns/hns_roce_main.c-778- iboe->nb.notifier_call = hns_roce_netdev_event;
drivers/infiniband/hw/hns/hns_roce_main.c:779: ret = register_netdevice_notifier(&iboe->nb);
drivers/infiniband/hw/hns/hns_roce_main.c-780- if (ret) {
drivers/infiniband/hw/hns/hns_roce_main.c:781: dev_err(dev, "register_netdevice_notifier failed!\n");
drivers/infiniband/hw/hns/hns_roce_main.c-782- goto error_failed_setup_mtu_mac;
--
drivers/infiniband/hw/irdma/main.c=25=static void irdma_register_notifiers(void)
--
drivers/infiniband/hw/irdma/main.c-29- register_netevent_notifier(&irdma_net_notifier);
drivers/infiniband/hw/irdma/main.c:30: register_netdevice_notifier(&irdma_netdevice_notifier);
drivers/infiniband/hw/irdma/main.c-31-}
--
drivers/infiniband/hw/irdma/main.c=33=static void irdma_unregister_notifiers(void)
--
drivers/infiniband/hw/irdma/main.c-37- unregister_inet6addr_notifier(&irdma_inetaddr6_notifier);
drivers/infiniband/hw/irdma/main.c:38: unregister_netdevice_notifier(&irdma_netdevice_notifier);
drivers/infiniband/hw/irdma/main.c-39-}
--
drivers/infiniband/hw/mana/device.c=100=static int mana_ib_probe(struct auxiliary_device *adev,
--
drivers/infiniband/hw/mana/device.c-141- dev->nb.notifier_call = mana_ib_netdev_event;
drivers/infiniband/hw/mana/device.c:142: ret = register_netdevice_notifier(&dev->nb);
drivers/infiniband/hw/mana/device.c-143- if (ret) {
--
drivers/infiniband/hw/mana/device.c-209- if (mana_ib_is_rnic(dev))
drivers/infiniband/hw/mana/device.c:210: unregister_netdevice_notifier(&dev->nb);
drivers/infiniband/hw/mana/device.c-211-free_ib_device:
--
drivers/infiniband/hw/mana/device.c=217=static void mana_ib_remove(struct auxiliary_device *adev)
--
drivers/infiniband/hw/mana/device.c-225- mana_ib_destroy_eqs(dev);
drivers/infiniband/hw/mana/device.c:226: unregister_netdevice_notifier(&dev->nb);
drivers/infiniband/hw/mana/device.c-227- }
--
drivers/infiniband/hw/mlx4/main.c=2613=static int mlx4_ib_probe(struct auxiliary_device *adev,
--
drivers/infiniband/hw/mlx4/main.c-2829- iboe->nb.notifier_call = mlx4_ib_netdev_event;
drivers/infiniband/hw/mlx4/main.c:2830: err = register_netdevice_notifier(&iboe->nb);
drivers/infiniband/hw/mlx4/main.c-2831- if (err) {
--
drivers/infiniband/hw/mlx4/main.c-2869- if (ibdev->iboe.nb.notifier_call) {
drivers/infiniband/hw/mlx4/main.c:2870: if (unregister_netdevice_notifier(&ibdev->iboe.nb))
drivers/infiniband/hw/mlx4/main.c-2871- pr_warn("failure unregistering notifier\n");
--
drivers/infiniband/hw/mlx4/main.c=2977=static void mlx4_ib_remove(struct auxiliary_device *adev)
--
drivers/infiniband/hw/mlx4/main.c-2992- if (ibdev->iboe.nb.notifier_call) {
drivers/infiniband/hw/mlx4/main.c:2993: if (unregister_netdevice_notifier(&ibdev->iboe.nb))
drivers/infiniband/hw/mlx4/main.c-2994- pr_warn("failure unregistering notifier\n");
--
drivers/infiniband/hw/mlx5/main.c=3349=static void mlx5_netdev_notifier_register(struct mlx5_roce *roce,
--
drivers/infiniband/hw/mlx5/main.c-3357- roce->nb.notifier_call = mlx5_netdev_event;
drivers/infiniband/hw/mlx5/main.c:3358: err = register_netdevice_notifier_dev_net(netdev, &roce->nb, &roce->nn);
drivers/infiniband/hw/mlx5/main.c-3359- WARN_ON(err);
--
drivers/infiniband/hw/mlx5/main.c=3362=static void mlx5_netdev_notifier_unregister(struct mlx5_roce *roce)
--
drivers/infiniband/hw/mlx5/main.c-3365- return;
drivers/infiniband/hw/mlx5/main.c:3366: unregister_netdevice_notifier_dev_net(roce->tracking_netdev, &roce->nb,
drivers/infiniband/hw/mlx5/main.c-3367- &roce->nn);
--
drivers/infiniband/hw/usnic/usnic_ib_main.c=659=static int __init usnic_ib_init(void)
--
drivers/infiniband/hw/usnic/usnic_ib_main.c-670-
drivers/infiniband/hw/usnic/usnic_ib_main.c:671: err = register_netdevice_notifier(&usnic_ib_netdevice_notifier);
drivers/infiniband/hw/usnic/usnic_ib_main.c-672- if (err) {
--
drivers/infiniband/hw/usnic/usnic_ib_main.c-695-out_unreg_netdev_notifier:
drivers/infiniband/hw/usnic/usnic_ib_main.c:696: unregister_netdevice_notifier(&usnic_ib_netdevice_notifier);
drivers/infiniband/hw/usnic/usnic_ib_main.c-697-out_pci_unreg:
--
drivers/infiniband/hw/usnic/usnic_ib_main.c=704=static void __exit usnic_ib_destroy(void)
--
drivers/infiniband/hw/usnic/usnic_ib_main.c-709- unregister_inetaddr_notifier(&usnic_ib_inetaddr_notifier);
drivers/infiniband/hw/usnic/usnic_ib_main.c:710: unregister_netdevice_notifier(&usnic_ib_netdevice_notifier);
drivers/infiniband/hw/usnic/usnic_ib_main.c-711- pci_unregister_driver(&usnic_ib_pci_driver);
--
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c=767=static int pvrdma_pci_probe(struct pci_dev *pdev,
--
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c-1019- dev->nb_netdev.notifier_call = pvrdma_netdevice_event;
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c:1020: ret = register_netdevice_notifier(&dev->nb_netdev);
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c-1021- if (ret) {
--
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c=1067=static void pvrdma_pci_remove(struct pci_dev *pdev)
--
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c-1075-
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c:1076: unregister_netdevice_notifier(&dev->nb_netdev);
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c-1077- dev->nb_netdev.notifier_call = NULL;
--
drivers/infiniband/sw/rxe/rxe_net.c=678=void rxe_net_exit(void)
--
drivers/infiniband/sw/rxe/rxe_net.c-681- rxe_release_udp_tunnel(recv_sockets.sk4);
drivers/infiniband/sw/rxe/rxe_net.c:682: unregister_netdevice_notifier(&rxe_net_notifier);
drivers/infiniband/sw/rxe/rxe_net.c-683-}
--
drivers/infiniband/sw/rxe/rxe_net.c=685=int rxe_net_init(void)
--
drivers/infiniband/sw/rxe/rxe_net.c-696- goto err_out;
drivers/infiniband/sw/rxe/rxe_net.c:697: err = register_netdevice_notifier(&rxe_net_notifier);
drivers/infiniband/sw/rxe/rxe_net.c-698- if (err) {
--
drivers/infiniband/sw/siw/siw_main.c=446=static __init int siw_init_module(void)
--
drivers/infiniband/sw/siw/siw_main.c-469-
drivers/infiniband/sw/siw/siw_main.c:470: rv = register_netdevice_notifier(&siw_netdev_nb);
drivers/infiniband/sw/siw/siw_main.c-471- if (rv)
--
drivers/infiniband/sw/siw/siw_main.c=490=static void __exit siw_exit_module(void)
--
drivers/infiniband/sw/siw/siw_main.c-493-
drivers/infiniband/sw/siw/siw_main.c:494: unregister_netdevice_notifier(&siw_netdev_nb);
drivers/infiniband/sw/siw/siw_main.c-495- rdma_link_unregister(&siw_link_ops);
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=1827=static int ipoib_dev_init(struct net_device *dev)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-1895-/*
drivers/infiniband/ulp/ipoib/ipoib_main.c:1896: * This must be called before doing an unregister_netdev on a parent device to
drivers/infiniband/ulp/ipoib/ipoib_main.c-1897- * shutdown the IB event handler.
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2175=void ipoib_setup_common(struct net_device *dev)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2196- /*
drivers/infiniband/ulp/ipoib/ipoib_main.c:2197: * unregister_netdev always frees the netdev, we use this mode
drivers/infiniband/ulp/ipoib/ipoib_main.c-2198- * consistently to unify all the various unregister paths, including
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2246=int ipoib_intf_init(struct ib_device *hca, u32 port, const char *name,
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2288- /*
drivers/infiniband/ulp/ipoib/ipoib_main.c:2289: * Only the child register_netdev flows can handle priv_destructor
drivers/infiniband/ulp/ipoib/ipoib_main.c-2290- * being set, so we force it to NULL here and handle manually until it
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2305=struct net_device *ipoib_intf_alloc(struct ib_device *hca, u32 port,
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2322- * Upon success the caller must ensure ipoib_intf_free is called or
drivers/infiniband/ulp/ipoib/ipoib_main.c:2323: * register_netdevice succeed'd and priv_destructor is set to
drivers/infiniband/ulp/ipoib/ipoib_main.c-2324- * ipoib_intf_free.
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2329=void ipoib_intf_free(struct net_device *dev)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2338- /*
drivers/infiniband/ulp/ipoib/ipoib_main.c:2339: * There are some error flows around register_netdev failing that may
drivers/infiniband/ulp/ipoib/ipoib_main.c-2340- * attempt to call priv_destructor twice, prevent that from happening.
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2554=static struct net_device *ipoib_add_port(const char *format,
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2579-
drivers/infiniband/ulp/ipoib/ipoib_main.c:2580: result = register_netdev(ndev);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2581- if (result) {
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2600- /*
drivers/infiniband/ulp/ipoib/ipoib_main.c:2601: * We cannot set priv_destructor before register_netdev because we
drivers/infiniband/ulp/ipoib/ipoib_main.c-2602- * need priv to be always valid during the error flow to execute
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2624- ipoib_parent_unregister_pre(ndev);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2625: unregister_netdev(ndev);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2626- return ERR_PTR(-ENOMEM);
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2663=static void ipoib_remove_one(struct ib_device *device, void *client_data)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2676- list)
drivers/infiniband/ulp/ipoib/ipoib_main.c:2677: unregister_netdevice_queue(cpriv->dev, &head);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2678- netdev_unlock(priv->dev);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2679: unregister_netdevice_queue(priv->dev, &head);
drivers/infiniband/ulp/ipoib/ipoib_main.c:2680: unregister_netdevice_many(&head);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2681-
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2694=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2744-#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
drivers/infiniband/ulp/ipoib/ipoib_main.c:2745: register_netdevice_notifier(&ipoib_netdev_notifier);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2746-#endif
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2762=static void __exit ipoib_cleanup_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2764-#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
drivers/infiniband/ulp/ipoib/ipoib_main.c:2765: unregister_netdevice_notifier(&ipoib_netdev_notifier);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2766-#endif
--
drivers/infiniband/ulp/ipoib/ipoib_netlink.c=100=static int ipoib_new_child_link(struct net_device *dev,
--
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-145- if (err) {
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:146: unregister_netdevice(dev);
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-147- return err;
--
drivers/infiniband/ulp/ipoib/ipoib_netlink.c=154=static void ipoib_del_child_link(struct net_device *dev, struct list_head *head)
--
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-160-
drivers/infiniband/ulp/ipoib/ipoib_netlink.c:161: unregister_netdevice_queue(dev, head);
drivers/infiniband/ulp/ipoib/ipoib_netlink.c-162-}
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c=97=int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-104- /*
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:105: * We do not need to touch priv if register_netdevice fails, so just
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-106- * always use this flow.
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-131-
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:132: result = register_netdevice(ndev);
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-133- if (result) {
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-136- /*
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:137: * register_netdevice sometimes calls priv_destructor,
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-138- * sometimes not. Make sure it was done.
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-158-sysfs_failed:
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:159: unregister_netdevice(priv->dev);
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-160- return -ENOMEM;
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c=215=struct ipoib_vlan_delete_work {
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-221- * sysfs callbacks of a netdevice cannot obtain the rtnl lock as
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:222: * unregister_netdev ultimately deletes the sysfs files while holding the rtnl
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-223- * lock. This deadlocks the system.
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-225- * A callback can use rtnl_trylock to avoid the deadlock but it cannot call
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:226: * unregister_netdev as that internally takes and releases the rtnl_lock. So
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-227- * instead we find the netdev to unregister and then do the actual unregister
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c=230=static void ipoib_vlan_delete_task(struct work_struct *work)
--
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-243- ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);
drivers/infiniband/ulp/ipoib/ipoib_vlan.c:244: unregister_netdevice(dev);
drivers/infiniband/ulp/ipoib/ipoib_vlan.c-245- }
--
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c=322=struct opa_vnic_adapter *opa_vnic_add_netdev(struct ib_device *ibdev,
--
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c-367-
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c:368: rc = register_netdev(netdev);
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c-369- if (rc)
--
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c=388=void opa_vnic_rem_netdev(struct opa_vnic_adapter *adapter)
--
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c-393- v_info("removing\n");
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c:394: unregister_netdev(netdev);
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c-395- opa_vnic_release_mac_tbl(adapter);
--
drivers/leds/trigger/ledtrig-netdev.c=697=static int netdev_trig_activate(struct led_classdev *led_cdev)
--
drivers/leds/trigger/ledtrig-netdev.c-741-
drivers/leds/trigger/ledtrig-netdev.c:742: rc = register_netdevice_notifier(&trigger_data->notifier);
drivers/leds/trigger/ledtrig-netdev.c-743- if (rc)
--
drivers/leds/trigger/ledtrig-netdev.c=749=static void netdev_trig_deactivate(struct led_classdev *led_cdev)
--
drivers/leds/trigger/ledtrig-netdev.c-752-
drivers/leds/trigger/ledtrig-netdev.c:753: unregister_netdevice_notifier(&trigger_data->notifier);
drivers/leds/trigger/ledtrig-netdev.c-754-
--
drivers/media/dvb-core/dvb_net.c=1344=static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
--
drivers/media/dvb-core/dvb_net.c-1389-
drivers/media/dvb-core/dvb_net.c:1390: if ((result = register_netdev(net)) < 0) {
drivers/media/dvb-core/dvb_net.c-1391- dvbnet->device[if_num] = NULL;
--
drivers/media/dvb-core/dvb_net.c=1400=static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num)
--
drivers/media/dvb-core/dvb_net.c-1414- pr_info("removed network interface %s\n", net->name);
drivers/media/dvb-core/dvb_net.c:1415: unregister_netdev(net);
drivers/media/dvb-core/dvb_net.c-1416- dvbnet->state[num]=0;
--
drivers/message/fusion/mptlan.c=1306=mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum)
--
drivers/message/fusion/mptlan.c-1377-
drivers/message/fusion/mptlan.c:1378: if (register_netdev(dev) != 0) {
drivers/message/fusion/mptlan.c-1379- free_netdev(dev);
]
]
|
| 101/2 |
2026/05/25 23:52 |
llm |
expert |
0m
Model:
Tokens:
input: 87496
output: 45
thoughts: 865
Thoughts: **Debugging a Potential Deadlock in HID over Bluetooth**
Okay, so I'm trying to figure out why this `cancel_work_sync` is taking so long. My initial thought was that maybe `register_netdevice_notifier` was the culprit, but then I realized that `hid_add_device` doesn't call `register_netdev`. That's a red herring. The problem isn't related to the network device registration at all.
Let's look at `kworker/0:3`. It could be blocked on `rtnl_mutex`, but what if it's blocked on `hdev->lock` instead? I need to review what `hidp_session_dev_add` does. Okay, it either calls `hid_add_device` or `input_register_device`. Let's step through `hid_add_device`: it triggers `hid_device_probe`, which calls `hid_hw_start`. But `hidp_start` does nothing. Okay, so that branch isn't it.
I was about to look into whether `hid_hw_open` gets called, but I remember it's called when the input device is opened. What if `hid_add_device` calls `device_add`? Aha, `device_add` might trigger `udev` to load a driver. But `hid-generic` is already loaded, so it’s likely not that. The `hid-generic` probe calls `hid_parse` and `hid_hw_start`. Still, no blocking calls there. Wait, `hid_add_device` might call `input_register_device` if the HID driver registers an input device. But `input_register_device` doesn't seem to acquire `hdev->lock` or `rtnl_mutex` either.
So now, let's explore another possibility. What if `kworker/0:3` is *not* executing `hidp_session_dev_work` anymore, but `khidpd` is blocked in `cancel_work_sync` because it is waiting for `hidp_session_dev_work` to finish, but `hidp_session_dev_work` is blocked? I see in the code that if `hidp_session_dev_add` returns an error, it calls `hidp_session_terminate`, which calls `wake_up_interruptible(&hidp_session_wq)`. And that doesn't block. So, it *should* finish quickly.
Why would `cancel_work_sync` block for 143 seconds then? The only logical explanation is that `hidp_session_dev_work` *is* blocked. What could it be blocked on? Wait! `hid_add_device` calls `device_add`, which acquires `device_lock(dev->parent)`. The parent of `session->hid` is `&session->conn->hcon->dev`. Okay, I need to check how `session->hid` is actually created and figure out what locks are being held there. I need to trace this back. This could be where the deadlock is, if `device_lock` is held by something else.
|
| 102/2 |
2026/05/25 23:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_setup_hid]
Results: map[SourceCode: 752: /* This function sets up the hid device. It does not add it
753: to the HID system. That is done in hidp_add_connection(). */
754: static int hidp_setup_hid(struct hidp_session *session,
755: const struct hidp_connadd_req *req)
756: {
757: struct hid_device *hid;
758: int err;
759:
760: session->rd_data = memdup_user(req->rd_data, req->rd_size);
761: if (IS_ERR(session->rd_data))
762: return PTR_ERR(session->rd_data);
763:
764: session->rd_size = req->rd_size;
765:
766: hid = hid_allocate_device();
767: if (IS_ERR(hid)) {
768: err = PTR_ERR(hid);
769: goto fault;
770: }
771:
772: session->hid = hid;
773:
774: hid->driver_data = session;
775:
776: hid->bus = BUS_BLUETOOTH;
777: hid->vendor = req->vendor;
778: hid->product = req->product;
779: hid->version = req->version;
780: hid->country = req->country;
781:
782: strscpy(hid->name, req->name, sizeof(hid->name));
783:
784: snprintf(hid->phys, sizeof(hid->phys), "%pMR",
785: &l2cap_pi(session->ctrl_sock->sk)->chan->src);
786:
787: /* NOTE: Some device modules depend on the dst address being stored in
788: * uniq. Please be aware of this before making changes to this behavior.
789: */
790: snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
791: &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
792:
793: hid->dev.parent = &session->conn->hcon->dev;
794: hid->ll_driver = &hidp_hid_driver;
795:
796: /* True if device is blocked in drivers/hid/hid-quirks.c */
797: if (hid_ignore(hid)) {
798: hid_destroy_device(session->hid);
799: session->hid = NULL;
800: return -ENODEV;
801: }
802:
803: return 0;
804:
805: fault:
806: kfree(session->rd_data);
807: session->rd_data = NULL;
808:
809: return err;
810: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 103/2 |
2026/05/25 23:52 |
llm |
expert |
0m
Model:
Tokens:
input: 89989
output: 44
thoughts: 1436
Thoughts: **Deadlock Analysis: Unraveling the RCU Hold**
Okay, let's break this down. My initial thought was about how `device_add` might interact with locks, specifically `device_lock`. I quickly dismissed concerns around `device_lock`'s interaction with parent devices, and then whether `hid_add_device` blocks on its own lock. That led me to consider `kworker/0:3` and `linkwatch_work`, but the context didn't seem right. My attention then shifted to a potential `hidp_session` and `dev_init` scenario, where a `kobject_uevent` call within `hid_add_device` could cause a block in user space via `call_usermodehelper`. That line of reasoning also didn't quite fit the pattern I was seeing with the syzkaller reports.
Now, let's re-evaluate. The core of this appears to be a deadlock involving `syz-executor/5806` and `kworker/u4:4`. I've established that `syz-executor/5806` is blocked in `rtnl_lock()`, while `kworker/u4:4` holds `rtnl_mutex`, and this is linked to `synchronize_rcu_expedited()`. I traced the execution flow through `rtnl_newlink` (called without the `rtnl_mutex` held), then `rtnl_nets_lock()`, and ultimately ending with `rtnl_lock()` which `syz-executor/5806` is trying to obtain.
So, the critical piece here is why `synchronize_rcu_expedited()` is blocked. The answer is that *someone* is holding `rcu_read_lock()`.
I initially hypothesized that it might be `syz-executor/5806` itself, but the code in `rtnetlink_rcv_msg` calls `rcu_read_unlock()` *before* invoking the relevant `doit` function, so I'm sure that isn't the problem. My investigation was thus to identify any other threads holding the `rcu_read_lock()` such as `khidpd`. However, my investigation showed that the associated locking mechanisms in `l2cap_unregister_user`, `l2cap_sock_connect` and in `kworker/0:3` don't seem to present an issue.
However, if `kworker/0:3` is executing `hidp_session_dev_work` which in turn calls `hid_add_device` and `kobject_uevent`, then `netlink_broadcast` might be called. And, Bingo! `netlink_broadcast` *does* call `rcu_read_lock()`.
**Therefore, the deadlock is highly likely caused by `kworker/0:3` holding `rcu_read_lock()` within `netlink_broadcast`, while `syz-executor/5806` is blocked waiting for `rtnl_mutex`.** Now, let's focus on verifying that path and see what's happening within `netlink_broadcast`.
|
| 104/2 |
2026/05/25 23:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/af_netlink.c IncludeLines:true Name:netlink_broadcast]
Results: map[SourceCode:1543: int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 portid,
1544: u32 group, gfp_t allocation)
1545: {
1546: return netlink_broadcast_filtered(ssk, skb, portid, group, allocation,
1547: NULL, NULL);
1548: }
SourceFile:net/netlink/af_netlink.c]
|
| 105/2 |
2026/05/25 23:52 |
llm |
expert |
0m
Model:
Tokens:
input: 92297
output: 46
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Diving into `netlink_broadcast_filtered`**
Alright, time to dig into this. I'm hitting a point where I need to refresh my understanding, or at least verify my assumptions, about how Netlink broadcasts are handled in this specific scenario. The name `netlink_broadcast_filtered` immediately suggests a level of sophistication beyond a simple, unfiltered broadcast, so that's the starting point. I need to be sure I understand the filtering mechanism involved here. Is it based on groups? PID? Perhaps message type? I'm anticipating there's some nuance involved in how the messages are being selectively distributed. I'm going to have to check its implementation to be certain what it is doing.
|
| 106/2 |
2026/05/25 23:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/af_netlink.c IncludeLines:true Name:netlink_broadcast_filtered]
Results: map[SourceCode:1491: int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb,
1492: u32 portid,
1493: u32 group, gfp_t allocation,
1494: netlink_filter_fn filter,
1495: void *filter_data)
1496: {
1497: struct net *net = sock_net(ssk);
1498: struct netlink_broadcast_data info;
1499: struct sock *sk;
1500:
1501: skb = netlink_trim(skb, allocation);
1502:
1503: info.exclude_sk = ssk;
1504: info.net = net;
1505: info.portid = portid;
1506: info.group = group;
1507: info.failure = 0;
1508: info.delivery_failure = 0;
1509: info.congested = 0;
1510: info.delivered = 0;
1511: info.allocation = allocation;
1512: info.skb = skb;
1513: info.skb2 = NULL;
1514: info.tx_filter = filter;
1515: info.tx_data = filter_data;
1516:
1517: /* While we sleep in clone, do not allow to change socket list */
1518:
1519: netlink_lock_table();
1520:
1521: sk_for_each_bound(sk, &nl_table[ssk->sk_protocol].mc_list)
1522: do_one_broadcast(sk, &info);
1523:
1524: consume_skb(skb);
1525:
1526: netlink_unlock_table();
1527:
1528: if (info.delivery_failure) {
1529: kfree_skb(info.skb2);
1530: return -ENOBUFS;
1531: }
1532: consume_skb(info.skb2);
1533:
1534: if (info.delivered) {
1535: if (info.congested && gfpflags_allow_blocking(allocation))
1536: yield();
1537: return 0;
1538: }
1539: return -ESRCH;
1540: }
SourceFile:net/netlink/af_netlink.c]
|
| 107/2 |
2026/05/25 23:52 |
llm |
expert |
0m
Model:
Tokens:
input: 93265
output: 43
thoughts: 1377
Thoughts: **Analyzing a Potential RCU Stall**
Okay, so I'm diving into this bug report and trying to understand why `synchronize_rcu_expedited()` is blocked. The initial clue is that it seems like a task is in an RCU read-side critical section, or is preempted within one. I see that it calls `netlink_lock_table()` which is a `read_lock(&nl_table_lock)`. But crucially, it *doesn't* call `rcu_read_lock()`. That's important.
The first potential scenario I considered was if `kworker/0:3` was blocked inside of `rtnl_lock()`. If that were the case it would *not* be holding `rcu_read_lock()`. Furthermore, if `kworker/0:3` was stuck in `rtnl_lock()` that would mean `khidpd` would be blocked in `cancel_work_sync`, and `syz.0.54` would be blocked in `mutex_lock(&hdev->lock)`. So then I had to ask, if no one is holding `rcu_read_lock()`, why is `synchronize_rcu_expedited()` blocked?
I shifted to considering that maybe the block isn't related to `rcu_read_lock()` at all. Perhaps it's waiting for a CPU to become quiescent, but something's preventing that. The NMI backtrace for CPU 0 shows `khungtaskd`, but that's not a hardlockup or softlockup - that's a red herring. It's a hung task report, with tasks in the `D` state for 143 seconds. That implies a real issue with blocking.
The core of the problem here is a task being preempted while holding `rcu_read_lock()`. Since `PREEMPT(full)` is enabled, that's entirely possible. If the preempted task is blocked on a mutex, it cannot resume, thus, it is a potential cause for `synchronize_rcu_expedited()` to be waiting. However, a task should *never* sleep while holding `rcu_read_lock()`. That would be a bug, and `lockdep` or `might_sleep()` should catch it. However, with preemptible RCU, preemption *is* allowed.
So, could it be a kernel bug? Maybe a task is inadvertently sleeping within an `rcu_read_lock()`? Let's check `syz-executor/5806` to make sure it's not the cause. It calls `rcu_read_lock()` from `rtnetlink_rcv_msg`. If `RTNL_FLAG_DOIT_UNLOCKED` is set, it calls `rcu_read_unlock()` *before* calling the function `doit`. So, `rtnetlink_rcv_msg` is *not* sleeping inside the read lock.
What about other relevant tasks? `khidpd` calls `cancel_work_sync(&session->dev_init)` and `syz.0.54` calls `hci_dev_lock(hdev)`, neither of these call `rcu_read_lock()`.
Okay, the question is now narrowed. Could `kworker/u4:4` be blocked in `synchronize_rcu_expedited()` because `syz-executor/5806` is holding `rcu_read_lock()`? And that is most likely tied to what happens at `rtnl_newlink+0x8db/0x1c70`. I need to verify: does `rtnl_newlink` call `rcu_read_lock()` somewhere? Let's check `rtnl_newlink`.
|
| 108/2 |
2026/05/25 23:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c IncludeLines:true Name:rtnl_newlink]
Results: map[SourceCode:3944: static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3945: struct netlink_ext_ack *extack)
3946: {
3947: struct net *tgt_net, *link_net = NULL, *peer_net = NULL;
3948: struct nlattr **tb, **linkinfo, **data = NULL;
3949: struct rtnl_link_ops *ops = NULL;
3950: struct rtnl_newlink_tbs *tbs;
3951: struct rtnl_nets rtnl_nets;
3952: int ops_srcu_index;
3953: int ret;
3954:
3955: tbs = kmalloc(sizeof(*tbs), GFP_KERNEL);
3956: if (!tbs)
3957: return -ENOMEM;
3958:
3959: tb = tbs->tb;
3960: ret = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg), tb,
3961: IFLA_MAX, ifla_policy, extack);
3962: if (ret < 0)
3963: goto free;
3964:
3965: ret = rtnl_ensure_unique_netns(tb, extack, false);
3966: if (ret < 0)
3967: goto free;
3968:
3969: linkinfo = tbs->linkinfo;
3970: if (tb[IFLA_LINKINFO]) {
3971: ret = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
3972: tb[IFLA_LINKINFO],
3973: ifla_info_policy, NULL);
3974: if (ret < 0)
3975: goto free;
3976: } else {
3977: memset(linkinfo, 0, sizeof(tbs->linkinfo));
3978: }
3979:
3980: if (linkinfo[IFLA_INFO_KIND]) {
3981: char kind[MODULE_NAME_LEN];
3982:
3983: nla_strscpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
3984: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
3985: #ifdef CONFIG_MODULES
3986: if (!ops) {
3987: request_module("rtnl-link-%s", kind);
3988: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
3989: }
3990: #endif
3991: }
3992:
3993: rtnl_nets_init(&rtnl_nets);
3994:
3995: if (ops) {
3996: if (ops->maxtype > RTNL_MAX_TYPE) {
3997: ret = -EINVAL;
3998: goto put_ops;
3999: }
4000:
4001: if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
4002: ret = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
4003: linkinfo[IFLA_INFO_DATA],
4004: ops->policy, extack);
4005: if (ret < 0)
4006: goto put_ops;
4007:
4008: data = tbs->attr;
4009: }
4010:
4011: if (ops->validate) {
4012: ret = ops->validate(tb, data, extack);
4013: if (ret < 0)
4014: goto put_ops;
4015: }
4016:
4017: if (ops->peer_type) {
4018: peer_net = rtnl_get_peer_net(ops, tb, data, extack);
4019: if (IS_ERR(peer_net)) {
4020: ret = PTR_ERR(peer_net);
4021: goto put_ops;
4022: }
4023: if (peer_net)
4024: rtnl_nets_add(&rtnl_nets, peer_net);
4025: }
4026: }
4027:
4028: tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN);
4029: if (IS_ERR(tgt_net)) {
4030: ret = PTR_ERR(tgt_net);
4031: goto put_net;
4032: }
4033:
4034: rtnl_nets_add(&rtnl_nets, tgt_net);
4035:
4036: if (tb[IFLA_LINK_NETNSID]) {
4037: int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
4038:
4039: link_net = get_net_ns_by_id(tgt_net, id);
4040: if (!link_net) {
4041: NL_SET_ERR_MSG(extack, "Unknown network namespace id");
4042: ret = -EINVAL;
4043: goto put_net;
4044: }
4045:
4046: rtnl_nets_add(&rtnl_nets, link_net);
4047:
4048: if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) {
4049: ret = -EPERM;
4050: goto put_net;
4051: }
4052: }
4053:
4054: rtnl_nets_lock(&rtnl_nets);
4055: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
4056: rtnl_nets_unlock(&rtnl_nets);
4057:
4058: put_net:
4059: rtnl_nets_destroy(&rtnl_nets);
4060: put_ops:
4061: if (ops)
4062: rtnl_link_ops_put(ops, ops_srcu_index);
4063: free:
4064: kfree(tbs);
4065: return ret;
4066: }
SourceFile:net/core/rtnetlink.c]
|
| 109/2 |
2026/05/25 23:52 |
llm |
expert |
0m
Model:
Tokens:
input: 97420
output: 47
thoughts: 1395
Thoughts: **RCU Conundrum**
Okay, so I'm diving into this RCU deadlock issue, and here's what I'm thinking. Initially, `get_net_ns_by_id` jumps out as a potential culprit because it uses `rcu_read_lock()` and releases it. However, it's not the only function that might call `rcu_read_lock()`. I need to ensure no other function, such as `rtnl_get_peer_net`, is also acquiring this lock. It turns out this function doesn't.
Now, my mind shifts to the scenario described by the bug report: `syz-executor/5806` isn't holding `rcu_read_lock()`. Instead, `kworker/u4:4` seems to be blocked in `synchronize_rcu_expedited()`, potentially because another worker, like `kworker/0:3`, is holding the lock. I start to think about what `kworker/0:3` might be doing to hold the lock. `hid_add_device` is called by `kworker/0:3` does NOT hold `rcu_read_lock()`.
The bug report mentions `khidpd` being blocked in `__cancel_work_sync`. I'm starting to trace the function call stack: `hidp_session_dev_work` -> `hidp_session_dev_add` -> `hid_add_device` -> `device_add` -> `kobject_uevent` -> `netlink_broadcast` -> `netlink_broadcast_filtered` -> `netlink_lock_table()`, which uses `read_lock(&nl_table_lock)`. Now, I remember that `read_lock` disables preemption! So, if `kworker/0:3` is blocked inside the `read_lock` of `nl_table_lock`, it is NOT holding `rcu_read_lock()`. Crucially, if that's the case, who's holding the `write_lock` for `nl_table_lock`? But, it turns out, no one is!
Okay, new thought, what if `kworker/0:3` is blocked in `rtnl_lock()`? If that's the case, it's waiting for `rtnl_mutex`, and who holds that? Bingo, `kworker/u4:4` holds `rtnl_mutex`! And `kworker/u4:4` is blocked in `synchronize_rcu_expedited()`. Why is this the case? The lockdep output from the bug report says `khungtaskd` is holding `rcu_read_lock()`! That explains why `synchronize_rcu_expedited()` is blocked, because it's waiting for all RCU callbacks to finish, and `khungtaskd` holds `rcu_read_lock()`.
However, `khungtaskd` only runs when a task has been hung for a long time (143 seconds). So, the `synchronize_rcu_expedited` function must have been blocked *before* `khungtaskd` ran. That means, someone *else* must have been holding `rcu_read_lock()` to cause the block!
Let's look at `syz.0.54` stack trace, and see what the trace indicates. The stack trace points toward `l2cap_sock_connect` and `l2cap_chan_connect`. I'll have to investigate if either of these functions is holding `rcu_read_lock()`.
|
| 110/2 |
2026/05/25 23:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_sock.c IncludeLines:true Name:l2cap_sock_connect]
Results: map[SourceCode: 181: static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
182: int alen, int flags)
183: {
184: struct sock *sk = sock->sk;
185: struct l2cap_chan *chan = l2cap_pi(sk)->chan;
186: struct sockaddr_l2 la;
187: int len, err = 0;
188: bool zapped;
189:
190: BT_DBG("sk %p", sk);
191:
192: lock_sock(sk);
193: zapped = sock_flag(sk, SOCK_ZAPPED);
194: release_sock(sk);
195:
196: if (zapped)
197: return -EINVAL;
198:
199: if (!addr || alen < offsetofend(struct sockaddr, sa_family) ||
200: addr->sa_family != AF_BLUETOOTH)
201: return -EINVAL;
202:
203: memset(&la, 0, sizeof(la));
204: len = min_t(unsigned int, sizeof(la), alen);
205: memcpy(&la, addr, len);
206:
207: if (la.l2_cid && la.l2_psm)
208: return -EINVAL;
209:
210: if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
211: return -EINVAL;
212:
213: /* Check that the socket wasn't bound to something that
214: * conflicts with the address given to connect(). If chan->src
215: * is BDADDR_ANY it means bind() was never used, in which case
216: * chan->src_type and la.l2_bdaddr_type do not need to match.
217: */
218: if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
219: bdaddr_type_is_le(la.l2_bdaddr_type)) {
220: /* Old user space versions will try to incorrectly bind
221: * the ATT socket using BDADDR_BREDR. We need to accept
222: * this and fix up the source address type only when
223: * both the source CID and destination CID indicate
224: * ATT. Anything else is an invalid combination.
225: */
226: if (chan->scid != L2CAP_CID_ATT ||
227: la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
228: return -EINVAL;
229:
230: /* We don't have the hdev available here to make a
231: * better decision on random vs public, but since all
232: * user space versions that exhibit this issue anyway do
233: * not support random local addresses assuming public
234: * here is good enough.
235: */
236: chan->src_type = BDADDR_LE_PUBLIC;
237: }
238:
239: if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
240: return -EINVAL;
241:
242: if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
243: /* We only allow ATT user space socket */
244: if (la.l2_cid &&
245: la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
246: return -EINVAL;
247: }
248:
249: /* Use L2CAP_MODE_LE_FLOWCTL (CoC) in case of LE address and
250: * L2CAP_MODE_EXT_FLOWCTL (ECRED) has not been set.
251: */
252: if (chan->psm && bdaddr_type_is_le(chan->src_type) &&
253: chan->mode != L2CAP_MODE_EXT_FLOWCTL)
254: chan->mode = L2CAP_MODE_LE_FLOWCTL;
255:
256: err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
257: &la.l2_bdaddr, la.l2_bdaddr_type,
258: sk->sk_sndtimeo);
259: if (err)
260: return err;
261:
262: lock_sock(sk);
263:
264: err = bt_sock_wait_state(sk, BT_CONNECTED,
265: sock_sndtimeo(sk, flags & O_NONBLOCK));
266:
267: release_sock(sk);
268:
269: return err;
270: }
SourceFile:net/bluetooth/l2cap_sock.c]
|
| 111/2 |
2026/05/25 23:53 |
llm |
expert |
0m
Model:
Tokens:
input: 101152
output: 43
thoughts: 3055
Thoughts: **Analyzing the Reported Deadlock: A Deep Dive**
Okay, so the initial thought was to look for `rcu_read_lock()` issues, but that doesn't seem to be the immediate problem. The bug report points to a situation where a thread is blocked on `hdev->lock`, but it's *blocked*, not holding it. Then, a thread (`syz-executor/5806`) is holding `rtnl_mutex`. If that's the case, then something else (`kworker/u4:4`) should be blocked on it. Hmm, the lockdep output for `kworker/u4:4` is weird. It *also* appears to be holding `rcu_state.exp_mutex`. If it holds `rcu_state.exp_mutex`, it *must* have acquired `rtnl_mutex` first because `synchronize_rcu_expedited` is called *after* acquiring `rtnl_mutex` within `linkwatch_event`. So, `kworker/u4:4` DEFINITELY holds `rtnl_mutex`! That makes `syz-executor/5806` being blocked on `rtnl_mutex` even more likely. If `syz-executor/5806` is blocked, it's not actually *holding* the `rtnl_mutex`, the deadlock detection system is showing it as "held" because it's in the `p->held_locks` list. That makes sense; `mutex_acquire` adds it *before* blocking.
So, `kworker/u4:4` holds `rtnl_mutex` and `rcu_state.exp_mutex` and is blocked. But why is `synchronize_rcu_expedited()` blocked? The implication is that `rcu_read_lock()` is *held* somewhere. But *who* is holding `rcu_read_lock()`? Let's trace this. If `kworker/0:3` is executing `hidp_session_dev_work` -> `hidp_session_dev_add` -> `hid_add_device` -> `device_add` -> `kobject_uevent` -> `netlink_broadcast`, does `netlink_broadcast` hold the lock? No, that calls `netlink_lock_table()`, a read lock which disables preemption! If preemption is disabled, then RCU sees this as a critical section. If `kworker/0:3` is blocked, but not spinning, it doesn't have preemption disabled. If it *were* spinning, it *would* disable preemption. If it's *blocked*, it's *not* in a critical section, so that's a dead end.
Okay, who *could* be holding `write_lock(&nl_table_lock)`? `netlink_table_grab` acquires the `write_lock`. But `netlink_create`, `netlink_release`, `netlink_bind`, and `netlink_unbind` call `netlink_table_grab`. If `syz-executor/5806` is calling `rtnl_newlink`, it doesn't. And what if another syz-executor is blocked in `netlink_bind`? `netlink_bind` doesn't block while holding `nl_table_lock`. Is *anywhere* holding `write_lock(&nl_table_lock)`? No. If `kworker/0:3` *were* spinning, it'd cause a hard/soft lockup, but the report only shows a hung task (143 seconds). So, it's blocked, *not* spinning. It's blocked in the `D` state, which means preemption is *not* disabled, which means it is NOT holding an RCU read-side critical section.
So, *who* is holding `rcu_read_lock()`? `synchronize_rcu_expedited()` is sending IPIs to other CPUs, and if any CPU is stuck with interrupts disabled, it'll hang. The NMI backtrace shows `khungtaskd` on CPU 0. Are the other CPUs stuck? The report *only* shows CPU 0. If another CPU was stuck, it should be in the backtrace. The VM has 2 CPUs typically. If CPU 1 is stuck, it should be in the NMI backtrace, but it's not.
What if `synchronize_rcu_expedited()` is blocked because of a workqueue dependency? `synchronize_rcu_expedited()` *does* use workqueues. If `rcu_gp_wq` is blocked
|
| 112/2 |
2026/05/25 23:53 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_request]
Results: map[SourceCode: SourceFile:]
|
| 113/2 |
2026/05/25 23:53 |
llm |
expert |
0m
Model:
Tokens:
input: 105296
output: 45
thoughts: 13
Thoughts: Okay, here's the summary, rendered as a first-person thought process, assuming the user is an expert.
**The HIDP Raw Request - Familiar Territory**
Ah, right, it's `hidp_raw_request`. Straight away, I recognize that. This is the low-level stuff, the direct interaction with the HID protocol. No abstractions here, just the raw data packet. I'm immediately thinking about the implications. What kind of HID device are we dealing with? Keyboard? Mouse? Something more exotic? Is this a standard request or something proprietary? Context is going to be crucial here. Need to check the report ID, the usage page and ID, the report size... all the usual suspects. I’m already mentally parsing the structure. This could be a simple event, a complex configuration packet, or even a firmware update in progress. My experience tells me to look for error flags, checksums... things that might indicate a problem or an anomaly. It's time to dig deeper and decode this thing.
|
| 114/2 |
2026/05/25 23:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_raw_request]
Results: map[SourceCode: 392: static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
393: __u8 *buf, size_t len, unsigned char rtype,
394: int reqtype)
395: {
396: switch (reqtype) {
397: case HID_REQ_GET_REPORT:
398: return hidp_get_raw_report(hid, reportnum, buf, len, rtype);
399: case HID_REQ_SET_REPORT:
400: return hidp_set_raw_report(hid, reportnum, buf, len, rtype);
401: default:
402: return -EIO;
403: }
404: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 115/2 |
2026/05/25 23:53 |
llm |
expert |
0m
Model:
Tokens:
input: 105814
output: 47
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, and rendered in the first person:
**Diving into the HID Report Acquisition**
Alright, time to get down to brass tacks. Let's see... the core issue at hand likely involves how we're actually *getting* the raw HID reports. I need to refresh my memory and pinpoint the function responsible. Let me start by focusing on `hidp_get_raw_report`. That's where the rubber meets the road, where the data is actually being pulled from the device. I need to meticulously verify its implementation and usage. Is it error-handling appropriately? Are the buffers correctly sized? Are we missing any crucial steps in the process? This will likely be the source.
|
| 116/2 |
2026/05/25 23:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/core.c IncludeLines:true Name:hidp_get_raw_report]
Results: map[SourceCode: 233: static int hidp_get_raw_report(struct hid_device *hid,
234: unsigned char report_number,
235: unsigned char *data, size_t count,
236: unsigned char report_type)
237: {
238: struct hidp_session *session = hid->driver_data;
239: struct sk_buff *skb;
240: size_t len;
241: int numbered_reports = hid->report_enum[report_type].numbered;
242: int ret;
243:
244: if (atomic_read(&session->terminate))
245: return -EIO;
246:
247: switch (report_type) {
248: case HID_FEATURE_REPORT:
249: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
250: break;
251: case HID_INPUT_REPORT:
252: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
253: break;
254: case HID_OUTPUT_REPORT:
255: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
256: break;
257: default:
258: return -EINVAL;
259: }
260:
261: if (mutex_lock_interruptible(&session->report_mutex))
262: return -ERESTARTSYS;
263:
264: /* Set up our wait, and send the report request to the device. */
265: session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK;
266: session->waiting_report_number = numbered_reports ? report_number : -1;
267: set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
268: data[0] = report_number;
269: ret = hidp_send_ctrl_message(session, report_type, data, 1);
270: if (ret < 0)
271: goto err;
272:
273: /* Wait for the return of the report. The returned report
274: gets put in session->report_return. */
275: while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
276: !atomic_read(&session->terminate)) {
277: int res;
278:
279: res = wait_event_interruptible_timeout(session->report_queue,
280: !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)
281: || atomic_read(&session->terminate),
282: 5*HZ);
283: if (res == 0) {
284: /* timeout */
285: ret = -EIO;
286: goto err;
287: }
288: if (res < 0) {
289: /* signal */
290: ret = -ERESTARTSYS;
291: goto err;
292: }
293: }
294:
295: skb = session->report_return;
296: if (skb) {
297: len = skb->len < count ? skb->len : count;
298: memcpy(data, skb->data, len);
299:
300: kfree_skb(skb);
301: session->report_return = NULL;
302: } else {
303: /* Device returned a HANDSHAKE, indicating protocol error. */
304: len = -EIO;
305: }
306:
307: clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
308: mutex_unlock(&session->report_mutex);
309:
310: return len;
311:
312: err:
313: clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
314: mutex_unlock(&session->report_mutex);
315: return ret;
316: }
SourceFile:net/bluetooth/hidp/core.c]
|
| 117/2 |
2026/05/25 23:53 |
llm |
expert |
0m
Model:
Tokens:
input: 107322
output: 45
thoughts: 2042
Thoughts: **Debugging a Kernel Lockup**
Okay, so I'm looking at this kernel deadlock. The main thread, `khidpd`, is blocked in `cancel_work_sync`, and a syz-executor is blocked acquiring a mutex. The key is that `khidpd` is blocked for a long time - 143 seconds. I've got to figure out what's causing this.
First, I think about what `hidp_get_raw_report` does. It waits on a device response, but the timeout is only 5 seconds. So that's not it.
Then, I trace the execution path from `hid_add_device` which calls `device_add` to `netlink_broadcast`. That path potentially calls `netlink_lock_table()`, so perhaps I'm blocking on a netlink lock. But, that lock is only held for reading which should be safe.
I wonder if a `kworker` thread is involved. I consider a scenario where `kworker/0:3` is blocked in `rtnl_lock()`, triggered by a uevent after `hid_add_device`. But `udevd` is userspace, and even if a driver probes, `input_register_device` doesn't block on `rtnl_mutex`.
Okay, what if `khidpd` is blocked *because* `hidp_session_dev_work` is running and holding a lock? I check `hidp_session_dev_add`. It *could* call `hid_add_device` or `input_register_device` but neither of those seem to block on `hdev->lock`.
Wait, what if `khidpd` is stuck waiting for `hidp_session_terminate`? No.
Let's look at the bug report again. `khidpd` is blocked on `cancel_work_sync` and `syz.0.54` is blocked in `mutex_lock(&hdev->lock)`. We've also got `syz-executor/5806` holding `rtnl_mutex` and `kworker/u4:4` holding `rtnl_mutex` and blocked in `synchronize_rcu_expedited`.
Aha! `syz-executor/5806` is *also* blocked in `rtnl_newlink`, specifically `rtnl_nets_lock`. `rtnl_nets_lock` calls `rtnl_lock()`.
So, `kworker/u4:4` is holding `rtnl_mutex` and blocking `syz-executor/5806`. It seems like `kworker/u4:4` is blocked in `synchronize_rcu_expedited()`. It means someone is holding `rcu_read_lock()` so that function is blocked. Now, I am thinking that `khidpd` which holds `hdev->lock` is blocked due to the work in `session->dev_init` that is running by `kworker/0:3`. That `dev_init` may be blocking `hid_add_device`, which calls `device_add`, which calls `kobject_uevent`, and `netlink_broadcast`. `netlink_broadcast` calls `netlink_lock_table()` which performs a `read_lock(&nl_table_lock)`. That is fine. Then, it iterates the multicast list and sends to sockets. I have to check `do_one_broadcast`.
Is it blocking? I remember that it *is* in that part of the code!
|
| 118/2 |
2026/05/25 23:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/af_netlink.c IncludeLines:true Name:do_one_broadcast]
Results: map[SourceCode:1411: static void do_one_broadcast(struct sock *sk,
1412: struct netlink_broadcast_data *p)
1413: {
1414: struct netlink_sock *nlk = nlk_sk(sk);
1415: int val;
1416:
1417: if (p->exclude_sk == sk)
1418: return;
1419:
1420: if (nlk->portid == p->portid || p->group - 1 >= nlk->ngroups ||
1421: !test_bit(p->group - 1, nlk->groups))
1422: return;
1423:
1424: if (!net_eq(sock_net(sk), p->net)) {
1425: if (!nlk_test_bit(LISTEN_ALL_NSID, sk))
1426: return;
1427:
1428: if (!peernet_has_id(sock_net(sk), p->net))
1429: return;
1430:
1431: if (!file_ns_capable(sk->sk_socket->file, p->net->user_ns,
1432: CAP_NET_BROADCAST))
1433: return;
1434: }
1435:
1436: if (p->failure) {
1437: netlink_overrun(sk);
1438: return;
1439: }
1440:
1441: sock_hold(sk);
1442: if (p->skb2 == NULL) {
1443: if (skb_shared(p->skb)) {
1444: p->skb2 = skb_clone(p->skb, p->allocation);
1445: } else {
1446: p->skb2 = skb_get(p->skb);
1447: /*
1448: * skb ownership may have been set when
1449: * delivered to a previous socket.
1450: */
1451: skb_orphan(p->skb2);
1452: }
1453: }
1454: if (p->skb2 == NULL) {
1455: netlink_overrun(sk);
1456: /* Clone failed. Notify ALL listeners. */
1457: p->failure = 1;
1458: if (nlk_test_bit(BROADCAST_SEND_ERROR, sk))
1459: p->delivery_failure = 1;
1460: goto out;
1461: }
1462:
1463: if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) {
1464: kfree_skb(p->skb2);
1465: p->skb2 = NULL;
1466: goto out;
1467: }
1468:
1469: if (sk_filter(sk, p->skb2)) {
1470: kfree_skb(p->skb2);
1471: p->skb2 = NULL;
1472: goto out;
1473: }
1474: NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net);
1475: if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED)
1476: NETLINK_CB(p->skb2).nsid_is_set = true;
1477: val = netlink_broadcast_deliver(sk, p->skb2);
1478: if (val < 0) {
1479: netlink_overrun(sk);
1480: if (nlk_test_bit(BROADCAST_SEND_ERROR, sk))
1481: p->delivery_failure = 1;
1482: } else {
1483: p->congested |= val;
1484: p->delivered = 1;
1485: p->skb2 = NULL;
1486: }
1487: out:
1488: sock_put(sk);
1489: }
SourceFile:net/netlink/af_netlink.c]
|
| 119/2 |
2026/05/25 23:53 |
llm |
expert |
6m
Model:
Tokens:
input: 111420
output: 66
thoughts: 62910
|
| 120/2 |
2026/05/26 00:00 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 121/2 |
2026/05/26 00:00 |
llm |
expert |
0m
Model:
Tokens:
input: 174460
output: 45
thoughts: 1606
Thoughts: **Deadlock Analysis: A Deep Dive**
Alright, let's break this down. We've got a classic deadlock scenario involving `hdev->lock`, `hidp_session_sem`, `rtnl_mutex`, and `rcu_state.exp_mutex`. The key players are `khidpd`, `syz.0.54`, `kworker/u4:4`, and `syz-executor/5806`, each snagged on a different lock.
My initial thought was that `hidp_session_dev_work` was the linchpin, and if it were blocked, everything would grind to a halt. The question was *why* is it blocked? Was it hung up in `hid_add_device`, perhaps waiting on `rtnl_mutex`, or something more subtle? The immediate possibility of an RCU issue was quickly dismissed, as `hidp_session_dev_work` doesn't hold `rcu_read_lock()`.
Then, I shifted focus to `khidpd` and that `cancel_work_sync(&session->dev_init)` call. If the work is running, `cancel_work_sync` *waits* for it. The suspicion then fell on `hidp_session_dev_work`, did it call `hidp_session_dev_add`, which then calls `hid_add_device` and then a driver probe? The answer, at first, seemed to indicate `hidp_start` would not be the issue as it does not acquire the `hidp_session_sem`. However, `hid_add_device` calls `hid_hw_open` which in turn calls `hidp_open`. Still no `hidp_session_sem`. I moved on to `hid_add_device` calls `hid_hw_request`. This does call `hidp_raw_request` which does call `hidp_get_raw_report`, but with a timeout, so not a likely culprit either.
Then I considered `hidp_session_dev_work` being blocked on `hdev->lock`. This led me to think about udev events. If `hid_add_device` triggered a uevent, and `udevd` was trying to do something with the Bluetooth device, and `udevd` calling `ioctl(HCIDEVUP)` which then acquires `hdev->lock`! But no, `kobject_uevent` uses `UMH_NO_WAIT`, so `udevd` is not blocked. *Unless* `bus_probe_device` blocks!
Then a familiar issue clicked, a known case: `l2cap_unregister_user` holding `hdev->lock` and calling `user->remove` which for HIDP is `hidp_session_remove`. And now we're back to `cancel_work_sync(&session->dev_init)`. If `session->dev_init` is running, it could try to acquire `hdev->lock`. Is it possible that `hidp_send_ctrl_message` is the cause? No, that doesn't acquire `hdev->lock`.
Then I backtracked again, to the original question: What if `khidpd` is blocked in `cancel_work_sync` because of a workqueue deadlock? The `session->dev_init` work is queued on `system_wq`, so if `system_wq` is blocked that could explain the problem.
Regardless of the exact execution path, the end result is a deadlock, and ultimately a denial of service. The question then becomes the attack surface.
The good news is that this isn't a privilege escalation or memory corruption issue. However, the bad news is that it *is* accessible from unprivileged processes...or is it? My initial thought was that `socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)` and `ioctl(HIDPCONNADD)` might be to blame. Wait a minute, I need to check `hidp_sock_ioctl`. And yes, `ioctl(HIDPCONNADD)` *requires* `CAP_NET_ADMIN`. So, not accessible from a truly unprivileged process. *Unless* it's in a user namespace where `CAP_NET_ADMIN` is available. Let me double check if `hidp_sock_ioctl` uses `capable()` or `ns_capable()`... *It uses `capable()`*. That confirms it, so it's *not* accessible from a user namespace either. I'm going to double-check that `hidp_sock_ioctl` function again to be sure.
|
| 122/2 |
2026/05/26 00:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/sock.c IncludeLines:true Name:hidp_sock_ioctl]
Results: map[SourceCode: 126: static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
127: {
128: return do_hidp_sock_ioctl(sock, cmd, (void __user *)arg);
129: }
SourceFile:net/bluetooth/hidp/sock.c]
|
| 123/2 |
2026/05/26 00:01 |
llm |
expert |
0m
Model:
Tokens:
input: 177217
output: 47
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, presented as a first-person summary:
**Digging into `do_hidp_sock_ioctl`**
Alright, time to dive into this. The mental checklist starts with `do_hidp_sock_ioctl`. I remember seeing that function pop up in the logs. It's the point of interest here, where the low level data control occurs. I need to get in there and see what’s actually happening. What kind of IOCTL requests is this function handling? Are there any obvious areas for optimization, or potential security vulnerabilities? What's the context? What exactly is being controlled here? The core issue is probably buried within this function, so this is my first stop. Let's see what we're working with.
|
| 124/2 |
2026/05/26 00:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/hidp/sock.c IncludeLines:true Name:do_hidp_sock_ioctl]
Results: map[SourceCode: 50: static int do_hidp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp)
51: {
52: struct hidp_connadd_req ca;
53: struct hidp_conndel_req cd;
54: struct hidp_connlist_req cl;
55: struct hidp_conninfo ci;
56: struct socket *csock;
57: struct socket *isock;
58: int err;
59:
60: BT_DBG("cmd %x arg %p", cmd, argp);
61:
62: switch (cmd) {
63: case HIDPCONNADD:
64: if (!capable(CAP_NET_ADMIN))
65: return -EPERM;
66:
67: if (copy_from_user(&ca, argp, sizeof(ca)))
68: return -EFAULT;
69:
70: csock = sockfd_lookup(ca.ctrl_sock, &err);
71: if (!csock)
72: return err;
73:
74: isock = sockfd_lookup(ca.intr_sock, &err);
75: if (!isock) {
76: sockfd_put(csock);
77: return err;
78: }
79: ca.name[sizeof(ca.name)-1] = 0;
80:
81: err = hidp_connection_add(&ca, csock, isock);
82: if (!err && copy_to_user(argp, &ca, sizeof(ca)))
83: err = -EFAULT;
84:
85: sockfd_put(csock);
86: sockfd_put(isock);
87:
88: return err;
89:
90: case HIDPCONNDEL:
91: if (!capable(CAP_NET_ADMIN))
92: return -EPERM;
93:
94: if (copy_from_user(&cd, argp, sizeof(cd)))
95: return -EFAULT;
96:
97: return hidp_connection_del(&cd);
98:
99: case HIDPGETCONNLIST:
100: if (copy_from_user(&cl, argp, sizeof(cl)))
101: return -EFAULT;
102:
103: if (cl.cnum <= 0)
104: return -EINVAL;
105:
106: err = hidp_get_connlist(&cl);
107: if (!err && copy_to_user(argp, &cl, sizeof(cl)))
108: return -EFAULT;
109:
110: return err;
111:
112: case HIDPGETCONNINFO:
113: if (copy_from_user(&ci, argp, sizeof(ci)))
114: return -EFAULT;
115:
116: err = hidp_get_conninfo(&ci);
117: if (!err && copy_to_user(argp, &ci, sizeof(ci)))
118: return -EFAULT;
119:
120: return err;
121: }
122:
123: return -EINVAL;
124: }
SourceFile:net/bluetooth/hidp/sock.c]
|
| 125/2 |
2026/05/26 00:01 |
llm |
expert |
0m
Model:
Tokens:
input: 178459
output: 48
thoughts: 5255
Thoughts: **Deadlock Deep Dive: A Bluetooth Nightmare**
Alright, let's break this down. Initial assessment? This deadlock isn't exploitable for privilege escalation by regular users because it hinges on global `CAP_NET_ADMIN`. This is a crucial detail, blocking exploitation from unprivileged processes and user namespaces. The syzkaller reproducer uses `syz_init_net_socket`, which runs with root privileges. So, no dice for standard users.
The path to trigger this appears to be through `hidp_session_remove`, which is called when an L2CAP connection closes or `HIDPCONNDEL` is invoked. A HIDP session necessitates `HIDPCONNADD`, which needs `CAP_NET_ADMIN`. This confirms the initial impression: unprivileged users, even in user namespaces, are locked out.
Essentially, this is a Denial of Service (DoS) vulnerability; the kernel gets hung, locking up network and Bluetooth functions. It does NOT enable privilege escalation, and doesn't rely on being in a user namespace. It requires an outside trigger, but is not Remote.
Now, the plot thickens. Could a remote Bluetooth device trigger this? If a remote device connects to the host, `bluetoothd` on Linux usually calls `HIDPCONNADD` as root, opening the door. If the remote device then disconnects—or sends a specific packet sequence—could this kick off the deadlock? `bluetoothd` *must* call `HIDPCONNADD` on the host side, so a remote attacker can trigger this.
The crucial sequence seems to be: 1) malicious device connects; 2) `bluetoothd` calls `HIDPCONNADD`; 3) remote device disconnects, triggering `l2cap_unregister_user` and then `hidp_session_remove`. This means `RemoteTrigger` is now looking like a "True."
Standard desktop environments running `bluetoothd` will accept incoming HID connections from paired devices. Even if the device is not paired, a user may be prompted for paring first, but this is enough to trigger the bug. This turns it into a remotely-triggerable DoS via Bluetooth.
Let's look at the deadlock again. The key threads are: `khidpd` is blocked in `cancel_work_sync(&session->dev_init)`, and `syz.0.54` (the syzkaller executor) is blocked in `mutex_lock(&hdev->lock)`. It’s `khidpd` holding `hdev->lock` where the trouble begins. It gets the lock during `l2cap_unregister_user`. `l2cap_unregister_user` calls `hidp_session_remove` which calls `cancel_work_sync`. If `session->dev_init` needs `hdev->lock` to complete, we've got a deadlock!
It looks like the `dev_init` work is blocked because `hidp_session_dev_work` is the work. Then `hid_add_device`, `device_add`, which adds the device to the bus, which needs to be unlocked.
The lock is at `l2cap_unregister_user` which means the HIDP side is holding the lock. This is the crucial point for a direct deadlock between the bluetooth daemon, and the test code.
Now the question is WHY is this `dev_init` work blocked in the first place? It's blocked in the middle of `cancel_work_sync`.
The report descriptor includes rdsize is `0xfea4` (65188 bytes) but rddata only has 3 bytes. This makes the `hid_parse_report` function take an extremely long time (143s). We're going to use this custom report descriptor.
Then, the bluetooth kernel code goes to the generic driver (because this is a "new" device). If `kworker/0:3` is executing `hidp_session_dev_work`, it could be blocked on `hdev->lock`.
The hypothesis is that `hidp_session_dev_work` is being slow.
|
| 126/2 |
2026/05/26 00:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_sock.c IncludeLines:true Name:l2cap_sock_sendmsg]
Results: map[SourceCode:1104: static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg,
1105: size_t len)
1106: {
1107: struct sock *sk = sock->sk;
1108: struct l2cap_chan *chan = l2cap_pi(sk)->chan;
1109: struct sockcm_cookie sockc;
1110: int err;
1111:
1112: BT_DBG("sock %p, sk %p", sock, sk);
1113:
1114: err = sock_error(sk);
1115: if (err)
1116: return err;
1117:
1118: if (msg->msg_flags & MSG_OOB)
1119: return -EOPNOTSUPP;
1120:
1121: if (sk->sk_state != BT_CONNECTED)
1122: return -ENOTCONN;
1123:
1124: hci_sockcm_init(&sockc, sk);
1125:
1126: if (msg->msg_controllen) {
1127: err = sock_cmsg_send(sk, msg, &sockc);
1128: if (err)
1129: return err;
1130: }
1131:
1132: lock_sock(sk);
1133: err = bt_sock_wait_ready(sk, msg->msg_flags);
1134: release_sock(sk);
1135: if (err)
1136: return err;
1137:
1138: l2cap_chan_lock(chan);
1139: err = l2cap_chan_send(chan, msg, len, &sockc);
1140: l2cap_chan_unlock(chan);
1141:
1142: return err;
1143: }
SourceFile:net/bluetooth/l2cap_sock.c]
|
| 127/2 |
2026/05/26 00:02 |
llm |
expert |
0m
Model:
Tokens:
input: 185228
output: 47
thoughts: 44
Thoughts: Here's my analysis of what's happening, keeping in mind my deep understanding of the intricacies of L2CAP and Bluetooth communication:
**Investigating the Data Transmission Path**
Okay, it seems we're dealing with a call to `l2cap_chan_send`. This immediately triggers a set of questions in my mind. The critical piece here, and something I need to quickly ascertain, is whether `l2cap_chan_send` manages the device lock, specifically `hdev->lock`. This is *essential* for understanding potential concurrency issues and ensuring proper synchronization, especially within the Bluetooth stack. Without knowing if the device lock is acquired and released correctly within that function, I can't be confident in the data's safe journey. So, let's dive into the code and verify how `l2cap_chan_send` is implemented. I need to trace its execution path to determine the lock's role.
|
| 128/2 |
2026/05/26 00:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bluetooth/l2cap_core.c IncludeLines:true Name:l2cap_chan_send]
Results: map[SourceCode:2544: int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
2545: const struct sockcm_cookie *sockc)
2546: {
2547: struct sk_buff *skb;
2548: int err;
2549: struct sk_buff_head seg_queue;
2550:
2551: if (!chan->conn)
2552: return -ENOTCONN;
2553:
2554: /* Connectionless channel */
2555: if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
2556: skb = l2cap_create_connless_pdu(chan, msg, len);
2557: if (IS_ERR(skb))
2558: return PTR_ERR(skb);
2559:
2560: l2cap_tx_timestamp(skb, sockc, len);
2561:
2562: l2cap_do_send(chan, skb);
2563: return len;
2564: }
2565:
2566: switch (chan->mode) {
2567: case L2CAP_MODE_LE_FLOWCTL:
2568: case L2CAP_MODE_EXT_FLOWCTL:
2569: /* Check outgoing MTU */
2570: if (len > chan->omtu)
2571: return -EMSGSIZE;
2572:
2573: __skb_queue_head_init(&seg_queue);
2574:
2575: err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
2576:
2577: if (chan->state != BT_CONNECTED) {
2578: __skb_queue_purge(&seg_queue);
2579: err = -ENOTCONN;
2580: }
2581:
2582: if (err)
2583: return err;
2584:
2585: l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
2586:
2587: skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
2588:
2589: l2cap_le_flowctl_send(chan);
2590:
2591: if (!chan->tx_credits)
2592: chan->ops->suspend(chan);
2593:
2594: err = len;
2595:
2596: break;
2597:
2598: case L2CAP_MODE_BASIC:
2599: /* Check outgoing MTU */
2600: if (len > chan->omtu)
2601: return -EMSGSIZE;
2602:
2603: /* Create a basic PDU */
2604: skb = l2cap_create_basic_pdu(chan, msg, len);
2605: if (IS_ERR(skb))
2606: return PTR_ERR(skb);
2607:
2608: l2cap_tx_timestamp(skb, sockc, len);
2609:
2610: l2cap_do_send(chan, skb);
2611: err = len;
2612: break;
2613:
2614: case L2CAP_MODE_ERTM:
2615: case L2CAP_MODE_STREAMING:
2616: /* Check outgoing MTU */
2617: if (len > chan->omtu) {
2618: err = -EMSGSIZE;
2619: break;
2620: }
2621:
2622: __skb_queue_head_init(&seg_queue);
2623:
2624: /* Do segmentation before calling in to the state machine,
2625: * since it's possible to block while waiting for memory
2626: * allocation.
2627: */
2628: err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
2629:
2630: if (err)
2631: break;
2632:
2633: if (chan->mode == L2CAP_MODE_ERTM) {
2634: /* TODO: ERTM mode timestamping */
2635: l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST);
2636: } else {
2637: l2cap_tx_timestamp_seg(&seg_queue, sockc, len);
2638: l2cap_streaming_send(chan, &seg_queue);
2639: }
2640:
2641: err = len;
2642:
2643: /* If the skbs were not queued for sending, they'll still be in
2644: * seg_queue and need to be purged.
2645: */
2646: __skb_queue_purge(&seg_queue);
2647: break;
2648:
2649: default:
2650: BT_DBG("bad state %1.1x", chan->mode);
2651: err = -EBADFD;
2652: }
2653:
2654: return err;
2655: }
SourceFile:net/bluetooth/l2cap_core.c]
|
| 129/2 |
2026/05/26 00:02 |
llm |
expert |
5m
Model:
Tokens:
input: 187096
output: 985
thoughts: 62914
|
| 130/1 |
2026/05/26 00:07 |
action |
format-explanation |
0m
Results: map[Explanation:The bug report describes a hung task situation where multiple threads are
blocked, leading to a system deadlock.
### Analysis of the Deadlock
1. **`khidpd` (PID 5507)** is executing `hidp_session_thread` and calls
`l2cap_unregister_user`.
- It acquires `hdev->lock` inside `l2cap_unregister_user`.
- It then calls the user's `remove` callback, which is `hidp_session_remove`.
- `hidp_session_remove` acquires `hidp_session_sem` for WRITE.
- It then calls `cancel_work_sync(&session->dev_init)` and blocks, waiting
for the `dev_init` work (`hidp_session_dev_work`) to finish.
2. **`syz.0.54` (PID 5547)** is executing `l2cap_sock_connect` ->
`l2cap_chan_connect`.
- It attempts to acquire `hdev->lock` but is blocked because `khidpd` is
already holding it.
3. **`syz.1.55` to `syz.7.61`** are executing `hidp_session_find`.
- They attempt to acquire `hidp_session_sem` for READ but are blocked because
`khidpd` is holding it for WRITE.
4. **`kworker/0:3`** is executing the `dev_init` work (`hidp_session_dev_work`).
- This work calls `hidp_session_dev_add`, which registers the HID device
(`hid_add_device`).
- Device registration triggers driver probing and uevents. If any part of
this device registration path attempts to acquire `hdev->lock` (e.g., via
some indirect L2CAP or HCI interaction) or is blocked behind another lock
(like `rtnl_mutex` which is currently held by `kworker/u4:4` and heavily
contended), it will deadlock because `khidpd` is holding `hdev->lock` and
waiting for this exact work to finish.
This is a classic ABBA deadlock or workqueue synchronization hang where a thread
holding a lock (`hdev->lock`) waits for a work item to finish, but the work item
requires the same lock (or is blocked behind something else that requires it) to
complete.
### Security Impact Assessment
- **Exploitable:** False. This is a synchronization issue (deadlock) resulting
in threads hanging in the `D` state. It does not lead to memory corruption,
use-after-free, or out-of-bounds accesses that could be leveraged for privilege
escalation or arbitrary code execution.
- **Denial Of Service:** True. The deadlock permanently blocks critical
Bluetooth locks (`hdev->lock` and `hidp_session_sem`), which will hang the
Bluetooth subsystem and any processes attempting to interact with it,
effectively causing a local denial of service.
- **Accessible From Unprivileged Processes:** False. The bug is triggered by
creating a HIDP session via the `HIDPCONNADD` ioctl on a Bluetooth socket. The
`do_hidp_sock_ioctl` function explicitly checks for global `CAP_NET_ADMIN` using
`capable(CAP_NET_ADMIN)`. Therefore, a normal unprivileged user cannot reach the
vulnerable code path.
- **Accessible From User Namespaces:** False. Because the code uses
`capable(CAP_NET_ADMIN)` instead of `ns_capable()`, the capability must be held
in the initial (global) user namespace. Having `CAP_NET_ADMIN` inside a
restricted user namespace is not sufficient to bypass this check.
- **Remote Trigger:** True. While a local unprivileged user cannot trigger this
via ioctls, a remote attacker acting as a malicious Bluetooth HID device can.
When a paired remote HID device connects, the host's `bluetoothd` (running as
root with `CAP_NET_ADMIN`) automatically accepts the connection and issues the
`HIDPCONNADD` ioctl. If the remote device then immediately drops the L2CAP
connection at the right moment while the `dev_init` work is still running, it
will invoke `hidp_session_remove` and trigger the deadlock remotely.
- **Peripheral Trigger:** False. This is triggered via Bluetooth network
protocols, not by physically plugging in a USB or similar peripheral device.
- **VM Guest/Host Trigger:** False. This is specific to the Bluetooth subsystem
and not related to virtualization boundaries or paravirtualized devices.]
|