| 0/0 |
2026/08/04 01:44 |
flow |
assessment-security |
31m
Results: map[DenialOfService:false Explanation:The bug is a `WARN_ONCE` triggered in `check_sdata_in_driver` when
`drv_set_bitrate_mask` is called on a mac80211 sub-interface that is not "in the
driver" (i.e., the `IEEE80211_SDATA_IN_DRIVER` flag is not set).
This state can be reached when an `AP_VLAN` interface is created and brought up.
For `AP_VLAN` interfaces, `ieee80211_do_open` sets the `SDATA_STATE_RUNNING` bit
but intentionally skips calling `drv_add_interface`, meaning
`IEEE80211_SDATA_IN_DRIVER` is never set. If a user then issues a `SIOCSIWRATE`
ioctl or `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command on this `AP_VLAN`
interface, the kernel checks if the interface is running
(`ieee80211_sdata_running` returns true) and proceeds to call
`drv_set_bitrate_mask`. This function expects the interface to be in the driver
and triggers the `WARN_ONCE` via `check_sdata_in_driver`.
**Exploitable: False**
The condition is gracefully handled by returning `-EIO` immediately after the
warning:
```c
if (!check_sdata_in_driver(sdata))
return -EIO;
```
There is no memory corruption, out-of-bounds access, or information leak.
**Denial of Service: False**
The warning does not crash the kernel (unless `panic_on_warn` is enabled, which
is not the case for standard production builds). The control flow properly
handles the unexpected state by returning an error code, so it does not lead to
deadlocks, hangs, or resource leaks.
**Accessible From Unprivileged Processes: False**
Triggering this requires `CAP_NET_ADMIN` to issue the `SIOCSIWRATE` ioctl or the
`NL80211_CMD_SET_TX_BITRATE_MASK` netlink command, as well as to create the
`AP_VLAN` interface. An unprivileged user in the initial namespace cannot
perform these actions.
**Accessible From User Namespaces: True**
A user with `CAP_NET_ADMIN` in a user/network namespace can create a virtual
`mac80211_hwsim` radio (which supports `AP_VLAN` if `AP` mode is supported),
create an `AP_VLAN` interface, bring it up, and send the netlink command to
trigger the warning. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/04 01:44 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/04 01:44 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/ad1d40b15882d85c62a8b234eb41ff6b656fee33]
|
| 3/1 |
2026/08/04 01:45 |
action |
kernel-builder |
13m
Results: map[KernelObj:/app/workdir/cache/build/2a5ed0259b9464e99c5f84f034a631d0b72f35ff]
|
| 4/1 |
2026/08/04 01:59 |
action |
codesearch-prepare |
9m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/04 02:08 |
agent |
expert |
6m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true 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 grepping
".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, elevated privileges, or an information leak.
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 completely 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 transfers, that 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 exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
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 Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
wlan1: Failed check-sdata-in-driver check, flags: 0x0
WARNING: net/mac80211/driver-ops.h:884 at drv_set_bitrate_mask net/mac80211/driver-ops.h:884 [inline], CPU#0: syz.1.2223/14766
WARNING: net/mac80211/driver-ops.h:884 at ieee80211_set_bitrate_mask+0x1034/0x1320 net/mac80211/cfg.c:4134, CPU#0: syz.1.2223/14766
Modules linked in:
CPU: 0 UID: 0 PID: 14766 Comm: syz.1.2223 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
RIP: 0010:drv_set_bitrate_mask net/mac80211/driver-ops.h:884 [inline]
RIP: 0010:ieee80211_set_bitrate_mask+0x1084/0x1320 net/mac80211/cfg.c:4134
Code: 20 01 00 00 48 85 c0 49 0f 44 ee 48 b8 00 00 00 00 00 fc ff df 0f b6 04 03 84 c0 0f 85 3b 01 00 00 41 8b 17 4c 89 ef 48 89 ee <67> 48 0f b9 3a e9 c3 fd ff ff e8 fd 3c 82 f6 90 0f 0b 90 e9 41 fe
RSP: 0018:ffffc90004317778 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 1ffff1100ac42b25 RCX: dffffc0000000000
RDX: 0000000000000000 RSI: ffff888056214120 RDI: ffffffff9069c4f0
RBP: ffff888056214120 R08: ffff888020f70faf R09: 1ffff110041ee1f5
R10: dffffc0000000000 R11: ffffed10041ee1f6 R12: ffff888020f70740
R13: ffffffff9069c4f0 R14: ffff888056215938 R15: ffff888056215928
FS: 00007f2c3f1916c0(0000) GS:ffff888124f50000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005555809f54e8 CR3: 0000000025bcc000 CR4: 00000000003526f0
DR0: 0000200000000300 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600
Call Trace:
<TASK>
rdev_set_bitrate_mask net/wireless/rdev-ops.h:683 [inline]
cfg80211_wext_siwrate+0x78d/0x9d0 net/wireless/wext-compat.c:1238
ioctl_standard_call+0xc5/0x160 net/wireless/wext-core.c:1042
wireless_process_ioctl net/wireless/wext-core.c:-1 [inline]
wext_ioctl_dispatch+0xee/0x410 net/wireless/wext-core.c:1013
wext_handle_ioctl+0x110/0x1d0 net/wireless/wext-core.c:1074
sock_ioctl+0x159/0x7e0 net/socket.c:1353
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f2c4139e019
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f2c3f191028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f2c41626270 RCX: 00007f2c4139e019
RDX: 0000200000000040 RSI: 0000000000008b20 RDI: 000000000000000c
RBP: 00007f2c4143500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f2c41626308 R14: 00007f2c41626270 R15: 00007ffd4f05a2f8
</TASK>
----------------
Code disassembly (best guess):
0: 20 01 and %al,(%rcx)
2: 00 00 add %al,(%rax)
4: 48 85 c0 test %rax,%rax
7: 49 0f 44 ee cmove %r14,%rbp
b: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
12: fc ff df
15: 0f b6 04 03 movzbl (%rbx,%rax,1),%eax
19: 84 c0 test %al,%al
1b: 0f 85 3b 01 00 00 jne 0x15c
21: 41 8b 17 mov (%r15),%edx
24: 4c 89 ef mov %r13,%rdi
27: 48 89 ee mov %rbp,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: e9 c3 fd ff ff jmp 0xfffffdf7
34: e8 fd 3c 82 f6 call 0xf6823d36
39: 90 nop
3a: 0f 0b ud2
3c: 90 nop
3d: e9 .byte 0xe9
3e: 41 rex.B
3f: fe .byte 0xfe
Reply: The bug is a `WARN_ONCE` triggered in `check_sdata_in_driver` when `drv_set_bitrate_mask` is called on a mac80211 sub-interface that is not "in the driver" (i.e., the `IEEE80211_SDATA_IN_DRIVER` flag is not set).
This state can be reached when an `AP_VLAN` interface is created and brought up. For `AP_VLAN` interfaces, `ieee80211_do_open` sets the `SDATA_STATE_RUNNING` bit but intentionally skips calling `drv_add_interface`, meaning `IEEE80211_SDATA_IN_DRIVER` is never set. If a user then issues a `SIOCSIWRATE` ioctl or `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command on this `AP_VLAN` interface, the kernel checks if the interface is running (`ieee80211_sdata_running` returns true) and proceeds to call `drv_set_bitrate_mask`. This function expects the interface to be in the driver and triggers the `WARN_ONCE` via `check_sdata_in_driver`.
**Exploitable: False**
The condition is gracefully handled by returning `-EIO` immediately after the warning:
```c
if (!check_sdata_in_driver(sdata))
return -EIO;
```
There is no memory corruption, out-of-bounds access, or information leak.
**Denial of Service: False**
The warning does not crash the kernel (unless `panic_on_warn` is enabled, which is not the case for standard production builds). The control flow properly handles the unexpected state by returning an error code, so it does not lead to deadlocks, hangs, or resource leaks.
**Accessible From Unprivileged Processes: False**
Triggering this requires `CAP_NET_ADMIN` to issue the `SIOCSIWRATE` ioctl or the `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command, as well as to create the `AP_VLAN` interface. An unprivileged user in the initial namespace cannot perform these actions.
**Accessible From User Namespaces: True**
A user with `CAP_NET_ADMIN` in a user/network namespace can create a virtual `mac80211_hwsim` radio (which supports `AP_VLAN` if `AP` mode is supported), create an `AP_VLAN` interface, bring it up, and send the netlink command to trigger the warning.
|
| 6/2 |
2026/08/04 02:08 |
llm |
expert |
0m
Model:
Tokens:
input: 8217
output: 42
thoughts: 155
Thoughts: **Analyzing a Kernel Bug Report**
Okay, so I've got this kernel bug report here. The initial lines jump out: `WARNING: net/mac80211/driver-ops.h:884 at drv_set_bitrate_mask net/mac80211/driver-ops.h:884 [inline]` and `WARNING: net/mac80211/driver-ops.h:884 at ieee80211_set_bitrate_mask+0x1034/0x1320 net/mac80211/cfg.c:4134`. It's pretty clear where the problem *might* be stemming from.
Given the error messages pinpointing `net/mac80211/driver-ops.h` and specifically line 884, that's where I need to start. I'll need to immediately pull up that file and focus on the code surrounding that line, paying close attention to the `drv_set_bitrate_mask` function, as well as the callstack information to `ieee80211_set_bitrate_mask`. This should provide some solid context into what's happening. The inline nature of the function could be playing a role, I'll keep that in mind. Let's see what's actually going on in that section of the driver.
|
| 7/2 |
2026/08/04 02:08 |
tool |
read-file |
0m
Args: map[File:net/mac80211/driver-ops.h FirstLine:870 LineCount:30]
Results: map[Contents: 870: trace_drv_return_bool(local, ret);
871:
872: return ret;
873: }
874:
875: static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
876: struct ieee80211_sub_if_data *sdata,
877: const struct cfg80211_bitrate_mask *mask)
878: {
879: int ret = -EOPNOTSUPP;
880:
881: might_sleep();
882: lockdep_assert_wiphy(local->hw.wiphy);
883:
884: if (!check_sdata_in_driver(sdata))
885: return -EIO;
886:
887: trace_drv_set_bitrate_mask(local, sdata, mask);
888: if (local->ops->set_bitrate_mask)
889: ret = local->ops->set_bitrate_mask(&local->hw,
890: &sdata->vif, mask);
891: trace_drv_return_int(local, ret);
892:
893: return ret;
894: }
895:
896: static inline void drv_set_rekey_data(struct ieee80211_local *local,
897: struct ieee80211_sub_if_data *sdata,
898: struct cfg80211_gtk_rekey_data *data)
899: {
]
|
| 8/2 |
2026/08/04 02:08 |
llm |
expert |
0m
Model:
Tokens:
input: 9215
output: 46
thoughts: 35
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the code:
**Diving into the Warning Trigger**
Right, the warning's popped up. My instinct is to trace it back to the source. The text says the trigger for this warning is specifically the call to `check_sdata_in_driver(sdata)`. So, that's where I need to focus. I'm going to start by getting a clear picture of what's happening within that function. I need to understand what it's checking and what conditions are leading to this warning being raised. Time to dissect `check_sdata_in_driver`. Let's see what that function is up to.
|
| 9/2 |
2026/08/04 02:08 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/mac80211/driver-ops.h Name:check_sdata_in_driver]
Results: map[SourceCode: SourceFile:]
|
| 10/2 |
2026/08/04 02:08 |
llm |
expert |
0m
Model:
Tokens:
input: 9471
output: 37
thoughts: 32
Thoughts: Okay, here's my interpretation of that thought process, as if it were my own:
**Investigating a Driver Operations Function**
Alright, I need to track down the implementation or usage of the `check_sdata_in_driver` function. The logical starting point for a function like that, especially considering the `net/mac80211` context, is the driver operations header file. So, I'll head straight for `net/mac80211/driver-ops.h`. That's where I'd expect to find the function declaration, at least, and potentially comments that give me a clue about its purpose and how it's intended to be used by the drivers. Time to dive in and see what's what!
|
| 11/2 |
2026/08/04 02:08 |
tool |
grepper |
0m
Args: map[Expression:check_sdata_in_driver PathPrefix:net/mac80211/]
Results: map[Output:net/mac80211/cfg.c=5921=static int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
--
net/mac80211/cfg.c-5930-
net/mac80211/cfg.c:5931: if (!check_sdata_in_driver(sdata))
net/mac80211/cfg.c-5932- return -EIO;
--
net/mac80211/driver-ops.c=90=int drv_change_interface(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-98-
net/mac80211/driver-ops.c:99: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-100- return -EIO;
--
net/mac80211/driver-ops.c=108=void drv_remove_interface(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-113-
net/mac80211/driver-ops.c:114: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-115- return;
--
net/mac80211/driver-ops.c=134=int drv_sta_state(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-145- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.c:146: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-147- return -EIO;
--
net/mac80211/driver-ops.c=170=int drv_sta_set_txpwr(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-179- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.c:180: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-181- return -EIO;
--
net/mac80211/driver-ops.c=191=void drv_link_sta_rc_update(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-196- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.c:197: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-198- return;
--
net/mac80211/driver-ops.c=212=int drv_conf_tx(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-221-
net/mac80211/driver-ops.c:222: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-223- return -EIO;
--
net/mac80211/driver-ops.c=247=u64 drv_get_tsf(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-254-
net/mac80211/driver-ops.c:255: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-256- return ret;
--
net/mac80211/driver-ops.c=265=void drv_set_tsf(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-271-
net/mac80211/driver-ops.c:272: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-273- return;
--
net/mac80211/driver-ops.c=281=void drv_offset_tsf(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-287-
net/mac80211/driver-ops.c:288: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-289- return;
--
net/mac80211/driver-ops.c=297=void drv_reset_tsf(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-302-
net/mac80211/driver-ops.c:303: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-304- return;
--
net/mac80211/driver-ops.c=312=int drv_assign_vif_chanctx(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-333-
net/mac80211/driver-ops.c:334: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-335- return -EIO;
--
net/mac80211/driver-ops.c=353=void drv_unassign_vif_chanctx(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-365-
net/mac80211/driver-ops.c:366: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-367- return;
--
net/mac80211/driver-ops.c=437=int drv_ampdu_action(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-446- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.c:447: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-448- return -EIO;
--
net/mac80211/driver-ops.c=460=void drv_link_info_changed(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-486-
net/mac80211/driver-ops.c:487: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-488- return;
--
net/mac80211/driver-ops.c=503=int drv_set_key(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-514- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.c:515: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-516- return -EIO;
--
net/mac80211/driver-ops.c=531=int drv_change_vif_links(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-544-
net/mac80211/driver-ops.c:545: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-546- return -EIO;
--
net/mac80211/driver-ops.c=581=int drv_change_sta_links(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-595-
net/mac80211/driver-ops.c:596: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.c-597- return -EIO;
--
net/mac80211/driver-ops.h-15-
net/mac80211/driver-ops.h:16:#define check_sdata_in_driver(sdata) ({ \
net/mac80211/driver-ops.h-17- WARN_ONCE(!sdata->local->reconfig_failure && \
--
net/mac80211/driver-ops.h=161=static inline void drv_vif_cfg_changed(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-167-
net/mac80211/driver-ops.h:168: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-169- return;
--
net/mac80211/driver-ops.h=249=static inline void drv_update_tkip_key(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-260- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:261: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-262- return;
--
net/mac80211/driver-ops.h=271=static inline int drv_hw_scan(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-279-
net/mac80211/driver-ops.h:280: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-281- return -EIO;
--
net/mac80211/driver-ops.h=289=static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-294-
net/mac80211/driver-ops.h:295: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-296- return;
--
net/mac80211/driver-ops.h=304=drv_sched_scan_start(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-313-
net/mac80211/driver-ops.h:314: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-315- return -EIO;
--
net/mac80211/driver-ops.h=324=static inline int drv_sched_scan_stop(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-331-
net/mac80211/driver-ops.h:332: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-333- return -EIO;
--
net/mac80211/driver-ops.h=440=static inline void drv_sta_notify(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-445- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:446: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-447- return;
--
net/mac80211/driver-ops.h=455=static inline int drv_sta_add(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-464- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:465: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-466- return -EIO;
--
net/mac80211/driver-ops.h=477=static inline void drv_sta_remove(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-484- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:485: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-486- return;
--
net/mac80211/driver-ops.h=496=static inline void drv_vif_add_debugfs(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-505- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:506: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-507- return;
--
net/mac80211/driver-ops.h=513=static inline void drv_link_add_debugfs(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-521- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:522: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-523- return;
--
net/mac80211/driver-ops.h=530=static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-538- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:539: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-540- return;
--
net/mac80211/driver-ops.h=547=static inline void drv_link_sta_add_debugfs(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-555- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:556: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-557- return;
--
net/mac80211/driver-ops.h=571=static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-578- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:579: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-580- return;
--
net/mac80211/driver-ops.h=605=static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-609- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:610: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-611- return;
--
net/mac80211/driver-ops.h=620=static inline void drv_sta_statistics(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-628- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:629: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-630- return;
--
net/mac80211/driver-ops.h=638=static inline void drv_link_sta_statistics(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-646- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:647: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-648- return;
--
net/mac80211/driver-ops.h=717=static inline void drv_flush(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-728-
net/mac80211/driver-ops.h:729: if (sdata && !check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-730- return;
--
net/mac80211/driver-ops.h=738=static inline void drv_flush_sta(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-746-
net/mac80211/driver-ops.h:747: if (sdata && !check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-748- return;
--
net/mac80211/driver-ops.h=875=static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-883-
net/mac80211/driver-ops.h:884: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-885- return -EIO;
--
net/mac80211/driver-ops.h=896=static inline void drv_set_rekey_data(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-902-
net/mac80211/driver-ops.h:903: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-904- return;
--
net/mac80211/driver-ops.h=955=static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-961-
net/mac80211/driver-ops.h:962: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-963- return;
--
net/mac80211/driver-ops.h=974=static inline void drv_mgd_complete_tx(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-980-
net/mac80211/driver-ops.h:981: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-982- return;
--
net/mac80211/driver-ops.h=994=drv_mgd_protect_tdls_discover(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1000-
net/mac80211/driver-ops.h:1001: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1002- return;
--
net/mac80211/driver-ops.h=1075=static inline int drv_start_ap(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1083-
net/mac80211/driver-ops.h:1084: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1085- return -EIO;
--
net/mac80211/driver-ops.h=1094=static inline void drv_stop_ap(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1100-
net/mac80211/driver-ops.h:1101: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1102- return;
--
net/mac80211/driver-ops.h=1124=drv_set_default_unicast_key(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1130-
net/mac80211/driver-ops.h:1131: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1132- return;
--
net/mac80211/driver-ops.h=1172=drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/driver-ops.h-1180-
net/mac80211/driver-ops.h:1181: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1182- return -EIO;
--
net/mac80211/driver-ops.h=1196=drv_post_channel_switch(struct ieee80211_link_data *link)
--
net/mac80211/driver-ops.h-1204-
net/mac80211/driver-ops.h:1205: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1206- return -EIO;
--
net/mac80211/driver-ops.h=1220=drv_abort_channel_switch(struct ieee80211_link_data *link)
--
net/mac80211/driver-ops.h-1227-
net/mac80211/driver-ops.h:1228: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1229- return;
--
net/mac80211/driver-ops.h=1242=drv_channel_switch_rx_beacon(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/driver-ops.h-1249-
net/mac80211/driver-ops.h:1250: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1251- return;
--
net/mac80211/driver-ops.h=1262=static inline int drv_join_ibss(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1268- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1269: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1270- return -EIO;
--
net/mac80211/driver-ops.h=1279=static inline void drv_leave_ibss(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1283- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1284: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1285- return;
--
net/mac80211/driver-ops.h=1325=drv_tdls_channel_switch(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1334- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1335: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1336- return -EIO;
--
net/mac80211/driver-ops.h=1350=drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1355- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1356: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1357- return;
--
net/mac80211/driver-ops.h=1379=static inline void drv_wake_tx_queue(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1389-
net/mac80211/driver-ops.h:1390: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1391- return;
--
net/mac80211/driver-ops.h=1415=drv_get_ftm_responder_stats(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1422- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1423: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1424- return -EIO;
--
net/mac80211/driver-ops.h=1435=static inline int drv_start_pmsr(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1442- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1443: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1444- return -EIO;
--
net/mac80211/driver-ops.h=1455=static inline void drv_abort_pmsr(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1462- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1463: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1464- return;
--
net/mac80211/driver-ops.h=1471=static inline int drv_start_nan(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1478- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1479: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1480-
--
net/mac80211/driver-ops.h=1487=static inline void drv_stop_nan(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1491- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1492: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1493-
--
net/mac80211/driver-ops.h=1499=static inline int drv_nan_change_conf(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1507- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1508: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1509-
--
net/mac80211/driver-ops.h=1521=static inline int drv_add_nan_func(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1528- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1529: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1530-
--
net/mac80211/driver-ops.h=1541=static inline void drv_del_nan_func(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1546- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1547: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1548-
--
net/mac80211/driver-ops.h=1585=static inline void drv_update_vif_offload(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1589- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1590: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1591-
--
net/mac80211/driver-ops.h=1600=static inline void drv_sta_set_4addr(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1607- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1608: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1609- return;
--
net/mac80211/driver-ops.h=1617=static inline void drv_sta_set_decap_offload(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1625- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1626: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1627- return;
--
net/mac80211/driver-ops.h=1636=static inline void drv_add_twt_setup(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1645-
net/mac80211/driver-ops.h:1646: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1647- return;
--
net/mac80211/driver-ops.h=1656=static inline void drv_twt_teardown_request(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1662- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1663: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1664- return;
--
net/mac80211/driver-ops.h=1674=static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1682- sdata = get_bss_sdata(sdata);
net/mac80211/driver-ops.h:1683: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1684- return -EIO;
--
net/mac80211/driver-ops.h=1715=static inline bool drv_can_activate_links(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1722-
net/mac80211/driver-ops.h:1723: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1724- return false;
--
net/mac80211/driver-ops.h=1745=drv_can_neg_ttlm(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1751- might_sleep();
net/mac80211/driver-ops.h:1752: if (!check_sdata_in_driver(sdata))
net/mac80211/driver-ops.h-1753- return -EIO;
--
net/mac80211/driver-ops.h=1797=drv_nan_peer_sched_changed(struct ieee80211_local *local,
--
net/mac80211/driver-ops.h-1804- lockdep_assert_wiphy(local->hw.wiphy);
net/mac80211/driver-ops.h:1805: check_sdata_in_driver(sdata);
net/mac80211/driver-ops.h-1806-
--
net/mac80211/main.c=336=void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/main.c-361-
net/mac80211/main.c:362: if (!check_sdata_in_driver(sdata))
net/mac80211/main.c-363- return;
--
net/mac80211/main.c=402=void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/main.c-423-
net/mac80211/main.c:424: if (!check_sdata_in_driver(sdata))
net/mac80211/main.c-425- return;
]
|
| 12/2 |
2026/08/04 02:08 |
llm |
expert |
0m
Model:
Tokens:
input: 19721
output: 41
thoughts: 40
Thoughts: Here's a summary of my thought process:
**Diving into the Driver Operations**
Alright, let's get down to brass tacks. I need to understand how this particular feature is being handled at the lowest level. To do that, I'm heading directly into the heart of the mac80211 driver. Specifically, I'm zeroing in on `driver-ops.h`. I recall that's where a lot of the interface definitions reside, the contracts between the core mac80211 stack and the hardware-specific driver implementations. I figure around line 15 is where I should find the `check_sdata_in_driver` function defined. From there, I can analyze how the driver interacts with the shared data structures in this section.
|
| 13/2 |
2026/08/04 02:08 |
tool |
read-file |
0m
Args: map[File:net/mac80211/driver-ops.h FirstLine:10 LineCount:30]
Results: map[Contents: 10:
11: #include <linux/fips.h>
12: #include <net/mac80211.h>
13: #include "ieee80211_i.h"
14: #include "trace.h"
15:
16: #define check_sdata_in_driver(sdata) ({ \
17: WARN_ONCE(!sdata->local->reconfig_failure && \
18: !(sdata->flags & IEEE80211_SDATA_IN_DRIVER), \
19: "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", \
20: sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); \
21: !!(sdata->flags & IEEE80211_SDATA_IN_DRIVER); \
22: })
23:
24: static inline struct ieee80211_sub_if_data *
25: get_bss_sdata(struct ieee80211_sub_if_data *sdata)
26: {
27: if (sdata && sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
28: sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
29: u.ap);
30:
31: return sdata;
32: }
33:
34: static inline void drv_tx(struct ieee80211_local *local,
35: struct ieee80211_tx_control *control,
36: struct sk_buff *skb)
37: {
38: local->ops->tx(&local->hw, control, skb);
39: }
]
|
| 14/2 |
2026/08/04 02:08 |
llm |
expert |
0m
Model:
Tokens:
input: 20512
output: 39
thoughts: 392
Thoughts: **Debugging a MAC80211 Bitrate Mask Issue**
Okay, so I'm seeing this `WARN_ONCE` fire in the `wlan1` interface, which is using the `check_sdata_in_driver` macro. Right, that macro checks the `sdata->flags` and, specifically, if the `IEEE80211_SDATA_IN_DRIVER` bit is set. The warning's telling me that bit isn't set, and the `sdata->flags` are `0x0`, which is as unset as can be. The warning originates from `drv_set_bitrate_mask` in `net/mac80211/driver-ops.h` on line 884 (inline).
The call trace indicates the issue is originating in `cfg80211_wext_siwrate`, going through the usual WEXT ioctl path: `ioctl_standard_call`, `wireless_process_ioctl`, `wext_ioctl_dispatch`, `wext_handle_ioctl`, and finally `sock_ioctl`. So, this is likely related to a WEXT ioctl manipulating bitrate masks. Looks like my next step is to examine `cfg80211_wext_siwrate` in `net/wireless/wext-compat.c` to understand *why* it's calling `drv_set_bitrate_mask` with an `sdata` that's not properly marked as being in the driver context. I need to figure out the context of this call and, more importantly, *why* the driver is being asked to modify bitrate masks when it doesn't appear to be set up correctly to do so.
|
| 15/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/wireless/wext-compat.c FirstLine:1200 LineCount:50]
Results: map[Contents:1200: memset(&mask, 0, sizeof(mask));
1201: fixed = 0;
1202: maxrate = (u32)-1;
1203:
1204: if (rate->value < 0) {
1205: /* nothing */
1206: } else if (rate->fixed) {
1207: fixed = rate->value / 100000;
1208: } else {
1209: maxrate = rate->value / 100000;
1210: }
1211:
1212: for (band = 0; band < NUM_NL80211_BANDS; band++) {
1213: sband = wdev->wiphy->bands[band];
1214: if (sband == NULL)
1215: continue;
1216: for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
1217: struct ieee80211_rate *srate = &sband->bitrates[ridx];
1218: if (fixed == srate->bitrate) {
1219: mask.control[band].legacy = 1 << ridx;
1220: match = true;
1221: break;
1222: }
1223: if (srate->bitrate <= maxrate) {
1224: mask.control[band].legacy |= 1 << ridx;
1225: match = true;
1226: }
1227: }
1228: }
1229:
1230: if (!match)
1231: return -EINVAL;
1232:
1233: guard(wiphy)(&rdev->wiphy);
1234:
1235: if (dev->ieee80211_ptr->valid_links)
1236: return -EOPNOTSUPP;
1237:
1238: return rdev_set_bitrate_mask(rdev, dev, 0, NULL, &mask);
1239: }
1240:
1241: static int cfg80211_wext_giwrate(struct net_device *dev,
1242: struct iw_request_info *info,
1243: union iwreq_data *wrqu, char *extra)
1244: {
1245: struct iw_param *rate = &wrqu->bitrate;
1246: struct wireless_dev *wdev = dev->ieee80211_ptr;
1247: struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1248: struct station_info sinfo = {};
1249: u8 addr[ETH_ALEN];
]
|
| 16/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 22114
output: 38
thoughts: 71
Thoughts: Here's my understanding of the situation, presented from my perspective as an expert:
**Investigating Bitrate Mask Configuration in cfg80211**
Okay, so `cfg80211_wext_siwrate` is the entry point, and it's calling `rdev_set_bitrate_mask`. My immediate thought is, "Right, setting the bitrate mask – that makes sense." I know that function is responsible for adjusting the supported data rates for a wireless device. The call signature itself is pretty standard: radio device (`rdev`), a device context (`dev`), some zero values (likely for flags or rate indexes that aren't used in this specific call), and then, importantly, the address of the bitmask itself (`&mask`). That gives me a clue about where the configuration data is stored, and it should allow me to go and understand how the underlying hardware is affected.
Now, to really understand what's happening, I need to dig into the definition of `rdev_set_bitrate_mask`. I'll pull up the code in `net/wireless/rdev-ops.h` to see exactly what this `rdev_set_bitrate_mask` function does. I want to see how the bitmask is interpreted, how it interacts with the radio device's internal state, and how any underlying hardware registers are being manipulated. I expect to see the bits in the `mask` being applied to internal data structures within the radio device representation (`rdev`), which should then translate into the driver communicating the configured rates to the hardware during association, scanning, etc. My focus is on the mechanism of how this `mask` is transferred and its effect on the radio device's operating parameters.
|
| 17/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/wireless/rdev-ops.h FirstLine:670 LineCount:30]
Results: map[Contents: 670: trace_rdev_return_int(&rdev->wiphy, ret);
671: return ret;
672: }
673: #endif
674:
675: static inline int
676: rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev,
677: struct net_device *dev, unsigned int link_id,
678: const u8 *peer,
679: const struct cfg80211_bitrate_mask *mask)
680: {
681: int ret;
682: trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, link_id, peer, mask);
683: ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, link_id,
684: peer, mask);
685: trace_rdev_return_int(&rdev->wiphy, ret);
686: return ret;
687: }
688:
689: static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev,
690: struct net_device *netdev, int idx,
691: struct survey_info *info)
692: {
693: int ret;
694: trace_rdev_dump_survey(&rdev->wiphy, netdev, idx);
695: ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info);
696: if (ret < 0)
697: trace_rdev_return_int(&rdev->wiphy, ret);
698: else
699: trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info);
]
|
| 18/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 23128
output: 38
thoughts: 71
Thoughts: **Analyzing the Bitrate Mask Configuration**
Okay, so I'm diving into this `set_bitrate_mask` function call. Looks like we're working with bitrate settings. The call originates from `rdev->ops->set_bitrate_mask`, and the immediate thought is to trace where `rdev->ops` is defined. Knowing this is in the mac80211 context, I can safely assume that `rdev->ops` points to `mac80211_config_ops`. Now, to get the specifics of the actual function, my next step is to examine the definition of `set_bitrate_mask` within `mac80211_config_ops`. Let me check `net/mac80211/cfg.c`. That should provide the details I need on how the bitrate mask is being handled within the mac80211 framework. I need to understand the exact implementation in that file to fully grasp what's going on here.
|
| 19/2 |
2026/08/04 02:09 |
tool |
grepper |
0m
Args: map[Expression:set_bitrate_mask PathPrefix:net/mac80211/cfg.c]
Results: map[Output:net/mac80211/cfg.c=4080=static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
--
net/mac80211/cfg.c-4102-
net/mac80211/cfg.c:4103:static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
net/mac80211/cfg.c-4104- struct net_device *dev,
--
net/mac80211/cfg.c-4133- if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
net/mac80211/cfg.c:4134: ret = drv_set_bitrate_mask(local, sdata, mask);
net/mac80211/cfg.c-4135- if (ret)
--
net/mac80211/cfg.c=5991=const struct cfg80211_ops mac80211_config_ops = {
--
net/mac80211/cfg.c-6050- .set_power_mgmt = ieee80211_set_power_mgmt,
net/mac80211/cfg.c:6051: .set_bitrate_mask = ieee80211_set_bitrate_mask,
net/mac80211/cfg.c-6052- .remain_on_channel = ieee80211_remain_on_channel,
]
|
| 20/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 23884
output: 41
thoughts: 72
Thoughts: Here's my analysis of that code snippet:
**Diving into Bitrate Mask Configuration**
Okay, so I see we're dealing with bitrate masks here. Specifically, `ieee80211_set_bitrate_mask` is the entry point. That's good, it's where the user or system likely interacts with setting or changing the allowable data rates for a wireless interface. My initial thought is to trace the flow of control and understand the underlying driver interaction.
The important function here is `drv_set_bitrate_mask`, which is being called from `ieee80211_set_bitrate_mask`. This suggests a clear separation of concerns, with `ieee80211_set_bitrate_mask` likely handling the mac80211 layer logic and the driver function doing the actual hardware configuration. The `local`, `sdata`, and `mask` arguments are pretty standard and I can infer their meaning immediately: `local` likely points to the mac80211 private data structure, `sdata` is the station data, and `mask` is the new bitrate mask.
My next step is to examine the contents of `ieee80211_set_bitrate_mask` from `net/mac80211/cfg.c`. I need to see exactly how mac80211 is interpreting the bitrate mask, any validation it performs, and how it translates the user-provided mask into something the driver function can understand. I'll be looking for things like rate scaling, rate limiting, and other possible bit manipulations that might be happening before the final call to the driver. This is where the magic happens and understanding it is critical to proper interface behavior.
|
| 21/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/mac80211/cfg.c FirstLine:4100 LineCount:50]
Results: map[Contents:4100: return 0;
4101: }
4102:
4103: static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
4104: struct net_device *dev,
4105: unsigned int link_id,
4106: const u8 *addr,
4107: const struct cfg80211_bitrate_mask *mask)
4108: {
4109: struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4110: struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
4111: int i, ret;
4112:
4113: if (!ieee80211_sdata_running(sdata))
4114: return -ENETDOWN;
4115:
4116: /*
4117: * If active validate the setting and reject it if it doesn't leave
4118: * at least one basic rate usable, since we really have to be able
4119: * to send something, and if we're an AP we have to be able to do
4120: * so at a basic rate so that all clients can receive it.
4121: */
4122: if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) &&
4123: sdata->vif.bss_conf.chanreq.oper.chan) {
4124: u32 basic_rates = sdata->vif.bss_conf.basic_rates;
4125: enum nl80211_band band;
4126:
4127: band = sdata->vif.bss_conf.chanreq.oper.chan->band;
4128:
4129: if (!(mask->control[band].legacy & basic_rates))
4130: return -EINVAL;
4131: }
4132:
4133: if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
4134: ret = drv_set_bitrate_mask(local, sdata, mask);
4135: if (ret)
4136: return ret;
4137: }
4138:
4139: for (i = 0; i < NUM_NL80211_BANDS; i++) {
4140: struct ieee80211_supported_band *sband = wiphy->bands[i];
4141: int j;
4142:
4143: sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
4144: memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
4145: sizeof(mask->control[i].ht_mcs));
4146: memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
4147: mask->control[i].vht_mcs,
4148: sizeof(mask->control[i].vht_mcs));
4149:
]
|
| 22/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 25287
output: 48
thoughts: 98
Thoughts: **Investigating a Discrepancy in Interface State**
Okay, here's what's happening. I've got a warning being triggered, and the initial culprit seems to be the fact that `sdata->flags & IEEE80211_SDATA_IN_DRIVER` is evaluating to false. Now, that's not what I'd expect given that `ieee80211_sdata_running(sdata)` is returning true. From mac80211's point of view, the interface *is* running, but apparently, it's not considered to be actively "in the driver's hands," which is definitely a point of concern. So, I need to understand what `ieee80211_sdata_running` actually checks for, to get to the root of this disconnect. Let's dive into the function's implementation and see where the interface's perceived operational status diverges from its internal driver state. This difference is key to understanding the warning, and ultimately, to fixing the underlying issue.
|
| 23/2 |
2026/08/04 02:09 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac80211/cfg.c Name:ieee80211_sdata_running]
Results: map[SourceCode:1970: static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
1971: {
1972: return test_bit(SDATA_STATE_RUNNING, &sdata->state);
1973: }
SourceFile:net/mac80211/ieee80211_i.h]
|
| 24/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 25787
output: 42
thoughts: 52
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Debugging a Network Data Flow Discrepancy**
Right, so I'm seeing `SDATA_STATE_RUNNING` active, which indicates that the data path should be actively processing. But I'm not seeing `IEEE80211_SDATA_IN_DRIVER` set. This is a red flag. Typically, when data is running, we expect the driver to be involved, represented by that flag. The driver *should* be handling the data.
The core of the problem then comes down to where/when is `IEEE80211_SDATA_IN_DRIVER` supposed to be set? It *should* be set at a specific point within the driver's execution path. Now, I need to trace back through the relevant code, looking for where that flag is manipulated. I have a hunch it's either not being set correctly, or it's being cleared prematurely. It is going to be a classic race condition in the code that I need to debug. I need to figure out the exact sequence of events that should lead to that flag being set, and pinpoint the exact point where it's supposed to happen. Then I'll be able to compare that with how the code's actually operating, and hopefully identify the source of the mismatch. Time to dig into the driver's code...
|
| 25/2 |
2026/08/04 02:09 |
tool |
grepper |
0m
Args: map[Expression:IEEE80211_SDATA_IN_DRIVER PathPrefix:net/mac80211/]
Results: map[Output:net/mac80211/cfg.c=50=static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/cfg.c-90- if (sdata->vif.bss_conf.mu_mimo_owner &&
net/mac80211/cfg.c:91: sdata->flags & IEEE80211_SDATA_IN_DRIVER)
net/mac80211/cfg.c-92- ieee80211_link_info_change_notify(sdata, &sdata->deflink,
--
net/mac80211/cfg.c=3835=static int ieee80211_get_tx_power(struct wiphy *wiphy,
--
net/mac80211/cfg.c-3845- if (local->ops->get_txpower &&
net/mac80211/cfg.c:3846: (sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/cfg.c-3847- return drv_get_txpower(local, sdata, link_id, dbm);
--
net/mac80211/cfg.c=3875=static int ieee80211_testmode_cmd(struct wiphy *wiphy,
--
net/mac80211/cfg.c-3888- sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
net/mac80211/cfg.c:3889: if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
net/mac80211/cfg.c-3890- vif = &sdata->vif;
--
net/mac80211/debugfs_netdev.c=1030=void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/debugfs_netdev.c-1035-
net/mac80211/debugfs_netdev.c:1036: if (sdata->flags & IEEE80211_SDATA_IN_DRIVER) {
net/mac80211/debugfs_netdev.c-1037- drv_vif_add_debugfs(sdata->local, sdata);
--
net/mac80211/driver-ops.c=57=int drv_add_interface(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-78-
net/mac80211/driver-ops.c:79: if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) {
net/mac80211/driver-ops.c:80: sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
net/mac80211/driver-ops.c-81-
--
net/mac80211/driver-ops.c=108=void drv_remove_interface(struct ieee80211_local *local,
--
net/mac80211/driver-ops.c-116-
net/mac80211/driver-ops.c:117: sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
net/mac80211/driver-ops.c-118-
--
net/mac80211/driver-ops.h-17- WARN_ONCE(!sdata->local->reconfig_failure && \
net/mac80211/driver-ops.h:18: !(sdata->flags & IEEE80211_SDATA_IN_DRIVER), \
net/mac80211/driver-ops.h-19- "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", \
net/mac80211/driver-ops.h-20- sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); \
net/mac80211/driver-ops.h:21: !!(sdata->flags & IEEE80211_SDATA_IN_DRIVER); \
net/mac80211/driver-ops.h-22-})
--
net/mac80211/ieee80211_i.h=770=struct ieee80211_if_mesh {
--
net/mac80211/ieee80211_i.h-866- * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
net/mac80211/ieee80211_i.h:867: * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
net/mac80211/ieee80211_i.h-868- * @IEEE80211_SDATA_DISCONNECT_HW_RESTART: Disconnect after hardware restart
--
net/mac80211/ieee80211_i.h=871=enum ieee80211_sub_if_data_flags {
--
net/mac80211/ieee80211_i.h-874- IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
net/mac80211/ieee80211_i.h:875: IEEE80211_SDATA_IN_DRIVER = BIT(5),
net/mac80211/ieee80211_i.h-876- IEEE80211_SDATA_DISCONNECT_HW_RESTART = BIT(6),
--
net/mac80211/util.c=745=static void __iterate_interfaces(struct ieee80211_local *local,
--
net/mac80211/util.c-768- if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
net/mac80211/util.c:769: active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-770- continue;
net/mac80211/util.c-771- if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
net/mac80211/util.c:772: !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-773- continue;
--
net/mac80211/util.c-783- (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
net/mac80211/util.c:784: sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-785- iterator(data, sdata->vif.addr, &sdata->vif);
--
net/mac80211/util.c=818=__ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
--
net/mac80211/util.c-849- if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
net/mac80211/util.c:850: active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-851- continue;
net/mac80211/util.c-852- if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
net/mac80211/util.c:853: !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-854- continue;
--
net/mac80211/util.c-860- (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
net/mac80211/util.c:861: monitor->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-862- return &monitor->vif;
--
net/mac80211/util.c=921=struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
--
net/mac80211/util.c-925- if (!ieee80211_sdata_running(sdata) ||
net/mac80211/util.c:926: !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
net/mac80211/util.c-927- return NULL;
--
net/mac80211/util.c=1673=static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
--
net/mac80211/util.c-1702- list_for_each_entry(sdata, &local->interfaces, list)
net/mac80211/util.c:1703: sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
net/mac80211/util.c-1704-
]
|
| 26/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 28837
output: 39
thoughts: 422
Thoughts: **Analyzing a Potential Driver State Issue**
Okay, so I'm digging into this warning I'm seeing. It looks like `WARN_ONCE` is triggered when `reconfig_failure` is false *and* `IEEE80211_SDATA_IN_DRIVER` is also false. This is a red flag. I know `IEEE80211_SDATA_IN_DRIVER` is set in `drv_add_interface` and cleared in `drv_remove_interface`, and it’s *also* cleared in `ieee80211_handle_reconfig_failure`. Therefore, if the warning is firing, it means that either `drv_add_interface` hasn't been called, or `drv_remove_interface` *has* been called.
Now, let's trace back how we got here to understand how that could even happen. My focus is on the `cfg80211_wext_siwrate` function because that's where the code appears to be blowing up. It looks like it gets invoked via a standard `SIOCSIWRATE` ioctl, going through `wext_handle_ioctl`, `wext_ioctl_dispatch`, `wireless_process_ioctl`, and finally `ioctl_standard_call`. The device in question is `dev`, and it retrieves its wireless device context with `dev->ieee80211_ptr`. Then, it gets the regulatory device (`rdev`) via `wiphy_to_rdev`. Ultimately, it calls `rdev_set_bitrate_mask`.
This is where I'm starting to get concerned, because I'm seeing that `cfg80211_wext_siwrate` uses `dev->ieee80211_ptr->valid_links`. My immediate thought is, is there a check for the device being UP? I've seen ioctls work on devices regardless of link state, so I need to check the function for a `netif_running(dev)` check. If there's no check, then this could indicate a flaw in handling interface initialization, or removal, in relation to `SIOCSIWRATE`.
|
| 27/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/wireless/wext-compat.c FirstLine:1180 LineCount:50]
Results: map[Contents:1180:
1181: return 0;
1182: }
1183:
1184: static int cfg80211_wext_siwrate(struct net_device *dev,
1185: struct iw_request_info *info,
1186: union iwreq_data *wrqu, char *extra)
1187: {
1188: struct iw_param *rate = &wrqu->bitrate;
1189: struct wireless_dev *wdev = dev->ieee80211_ptr;
1190: struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1191: struct cfg80211_bitrate_mask mask;
1192: u32 fixed, maxrate;
1193: struct ieee80211_supported_band *sband;
1194: bool match = false;
1195: int band, ridx;
1196:
1197: if (!rdev->ops->set_bitrate_mask)
1198: return -EOPNOTSUPP;
1199:
1200: memset(&mask, 0, sizeof(mask));
1201: fixed = 0;
1202: maxrate = (u32)-1;
1203:
1204: if (rate->value < 0) {
1205: /* nothing */
1206: } else if (rate->fixed) {
1207: fixed = rate->value / 100000;
1208: } else {
1209: maxrate = rate->value / 100000;
1210: }
1211:
1212: for (band = 0; band < NUM_NL80211_BANDS; band++) {
1213: sband = wdev->wiphy->bands[band];
1214: if (sband == NULL)
1215: continue;
1216: for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
1217: struct ieee80211_rate *srate = &sband->bitrates[ridx];
1218: if (fixed == srate->bitrate) {
1219: mask.control[band].legacy = 1 << ridx;
1220: match = true;
1221: break;
1222: }
1223: if (srate->bitrate <= maxrate) {
1224: mask.control[band].legacy |= 1 << ridx;
1225: match = true;
1226: }
1227: }
1228: }
1229:
]
|
| 28/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 30563
output: 40
thoughts: 167
Thoughts: **Analyzing the Missing Network Interface Check**
Okay, so I'm digging into this `cfg80211_wext_siwrate` function, and I've noticed something that's making me a bit uneasy. There's no explicit check in the code to verify if the network interface device (`dev`) is actually running, specifically using `netif_running(dev)`. That seems a little...unusual, doesn't it? I usually expect to see that safeguard.
But, hold on a second... let's rewind and investigate further. Looking deeper, I see that the `ieee80211_set_bitrate_mask` function, which is located in `net/mac80211/cfg.c`, *does* perform a check. It calls `ieee80211_sdata_running(sdata)` before doing anything:
```c
if (!ieee80211_sdata_running(sdata))
return -ENETDOWN;
```
Interesting. That *means* `ieee80211_sdata_running(sdata)` must evaluate to `true` at that point in the code's execution. And what does `ieee80211_sdata_running(sdata)` actually do? Ah, it checks for the `SDATA_STATE_RUNNING` state. Okay, so now I'm thinking, when *is* `SDATA_STATE_RUNNING` set? I need to track down exactly where and when that state flag is enabled to understand how this whole system ensures the network interface is active before modifying the rate mask. I'll need to go hunting for the state machine logic now.
|
| 29/2 |
2026/08/04 02:09 |
tool |
grepper |
0m
Args: map[Expression:set_bit\(SDATA_STATE_RUNNING PathPrefix:net/mac80211/]
Results: map[Output:net/mac80211/iface.c=1228=int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
--
net/mac80211/iface.c-1274-
net/mac80211/iface.c:1275: set_bit(SDATA_STATE_RUNNING, &sdata->state);
net/mac80211/iface.c-1276-
--
net/mac80211/iface.c=1365=int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
--
net/mac80211/iface.c-1599-
net/mac80211/iface.c:1600: set_bit(SDATA_STATE_RUNNING, &sdata->state);
net/mac80211/iface.c-1601-
]
|
| 30/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 31418
output: 42
thoughts: 34
Thoughts: Okay, let's dive into this. Here's my take on those thoughts:
**Dissecting `ieee80211_do_open`**
Alright, I'm thinking, "Let's crack open `ieee80211_do_open` and see what it's really doing." My attention's laser-focused on that specific function call within the `net/mac80211/iface.c` file. It's a key entry point, likely handling a crucial aspect of interface initialization or bring-up within the mac80211 stack. I immediately want to understand its role in the overall architecture. What resources does it allocate? What other functions does it call? What state transitions does it manage? I'm anticipating this function will involve registering the interface, setting up data structures, and possibly enabling hardware. It should be an interesting piece to dissect.
|
| 31/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1580 LineCount:50]
Results: map[Contents:1580: }
1581:
1582: /*
1583: * set_multicast_list will be invoked by the networking core
1584: * which will check whether any increments here were done in
1585: * error and sync them down to the hardware as filter flags.
1586: */
1587: if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
1588: atomic_inc(&local->iff_allmultis);
1589:
1590: if (coming_up)
1591: local->open_count++;
1592:
1593: if (local->open_count == 1)
1594: ieee80211_hw_conf_init(local);
1595: else if (hw_reconf_flags)
1596: ieee80211_hw_config(local, -1, hw_reconf_flags);
1597:
1598: ieee80211_recalc_ps(local);
1599:
1600: set_bit(SDATA_STATE_RUNNING, &sdata->state);
1601:
1602: return 0;
1603: err_del_interface:
1604: drv_remove_interface(local, sdata);
1605: err_stop:
1606: if (!local->open_count)
1607: drv_stop(local, false);
1608: if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
1609: RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL);
1610: if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
1611: list_del(&sdata->u.vlan.list);
1612: /* Might not be initialized yet, but it is harmless */
1613: sdata->bss = NULL;
1614: return res;
1615: }
1616:
1617: static void ieee80211_if_setup(struct net_device *dev)
1618: {
1619: ether_setup(dev);
1620: dev->priv_flags &= ~IFF_TX_SKB_SHARING;
1621: dev->priv_flags |= IFF_NO_QUEUE;
1622: dev->netdev_ops = &ieee80211_dataif_ops;
1623: dev->needs_free_netdev = true;
1624: }
1625:
1626: static void ieee80211_iface_process_skb(struct ieee80211_local *local,
1627: struct ieee80211_sub_if_data *sdata,
1628: struct sk_buff *skb)
1629: {
]
|
| 32/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 32532
output: 42
thoughts: 28
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Tracing `drv_add_interface` in `ieee80211_do_open`**
Okay, so I'm digging into the 802.11 stack again. I need to understand the control flow, specifically how a new virtual interface is brought up. Right now, I'm thinking about where `drv_add_interface` is actually invoked. It's a key function for associating the driver's representation of a virtual interface with the underlying hardware. Since I'm focusing on the initialization path, my initial thought is to trace where it is called within the `ieee80211_do_open` context. That function is pretty central when setting up an interface, so that seems like a logical starting point. I'll need to double check how the driver's `ops` are setup, which likely gets initiated around the device registration/probe stage. I know the `ieee80211_do_open` routine is the entry point for the interface coming online, therefore finding the function call from there will provide a high level overview of the overall process.
|
| 33/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1500 LineCount:50]
Results: map[Contents:1500: hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
1501: }
1502: }
1503:
1504: local->monitors++;
1505:
1506: ieee80211_adjust_monitor_flags(sdata, 1);
1507: ieee80211_configure_filter(local);
1508: ieee80211_recalc_offload(local);
1509: ieee80211_recalc_idle(local);
1510:
1511: netif_carrier_on(dev);
1512: list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list);
1513: break;
1514: case NL80211_IFTYPE_AP:
1515: sdata->bss = &sdata->u.ap;
1516: fallthrough;
1517: default:
1518: if (coming_up) {
1519: ieee80211_del_virtual_monitor(local);
1520: ieee80211_set_sdata_offload_flags(sdata);
1521:
1522: res = drv_add_interface(local, sdata);
1523: if (res)
1524: goto err_stop;
1525:
1526: ieee80211_set_vif_encap_ops(sdata);
1527: res = ieee80211_check_queues(sdata,
1528: ieee80211_vif_type_p2p(&sdata->vif));
1529: if (res)
1530: goto err_del_interface;
1531: }
1532:
1533: if (sdata->vif.type == NL80211_IFTYPE_AP) {
1534: local->fif_pspoll++;
1535: local->fif_probe_req++;
1536:
1537: ieee80211_configure_filter(local);
1538: } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
1539: local->fif_probe_req++;
1540: }
1541:
1542: if (sdata->vif.probe_req_reg)
1543: drv_config_iface_filter(local, sdata,
1544: FIF_PROBE_REQ,
1545: FIF_PROBE_REQ);
1546:
1547: if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
1548: sdata->vif.type != NL80211_IFTYPE_NAN &&
1549: sdata->vif.type != NL80211_IFTYPE_PD)
]
|
| 34/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 33719
output: 42
thoughts: 190
Thoughts: **My Analysis of Driver Initialization and State Transitions**
Okay, so I'm tracing the initialization path here, focusing on how the driver interface is established and its operational state managed. It looks like `drv_add_interface` is triggered when `coming_up` is true – that makes perfect sense. My concern now is what happens when it's *not* true. I need to understand all possible scenarios.
Looking deeper, `ieee80211_do_open` is the key function; it seems to be the entry point from both `ieee80211_open` and `ieee80211_resume`. Specifically, `ieee80211_open` sets `coming_up` to true, so we definitely hit `drv_add_interface`. This call is crucial, because that's where `IEEE80211_SDATA_IN_DRIVER` gets set, which is expected. Followed by setting `SDATA_STATE_RUNNING`, which is precisely the behavior I would expect.
My next step is to understand how the system transitions out of this state. Where does `SDATA_STATE_RUNNING` get cleared? I see it happens in `ieee80211_do_stop` within `net/mac80211/iface.c`. This makes perfect sense; stopping the interface would mean dropping the running state. I'll need to review the conditions that lead to `ieee80211_do_stop` to have a complete picture.
|
| 35/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1680 LineCount:50]
Results: map[Contents:1680: lockdep_is_held(&local->hw.wiphy->mtx));
1681: if (link_sta)
1682: ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
1683: link_sta, chanwidth,
1684: status->band);
1685: break;
1686: }
1687: default:
1688: WARN_ON(1);
1689: break;
1690: }
1691: } else if (ieee80211_is_action(mgmt->frame_control) &&
1692: mgmt->u.action.category == WLAN_CATEGORY_VHT) {
1693: switch (mgmt->u.action.action_code) {
1694: case WLAN_VHT_ACTION_OPMODE_NOTIF: {
1695: struct ieee80211_rx_status *status;
1696: enum nl80211_band band;
1697: struct sta_info *sta;
1698: u8 opmode;
1699:
1700: status = IEEE80211_SKB_RXCB(skb);
1701: band = status->band;
1702: opmode = mgmt->u.action.vht_opmode_notif.operating_mode;
1703:
1704: sta = sta_info_get_bss(sdata, mgmt->sa);
1705:
1706: if (sta)
1707: ieee80211_vht_handle_opmode(sdata,
1708: &sta->deflink,
1709: opmode, band);
1710:
1711: break;
1712: }
1713: case WLAN_VHT_ACTION_GROUPID_MGMT:
1714: ieee80211_process_mu_groups(sdata, &sdata->deflink,
1715: mgmt);
1716: break;
1717: default:
1718: WARN_ON(1);
1719: break;
1720: }
1721: } else if (ieee80211_is_action(mgmt->frame_control) &&
1722: mgmt->u.action.category == WLAN_CATEGORY_S1G) {
1723: switch (mgmt->u.action.action_code) {
1724: case WLAN_S1G_TWT_TEARDOWN:
1725: case WLAN_S1G_TWT_SETUP:
1726: ieee80211_s1g_rx_twt_action(sdata, skb);
1727: break;
1728: default:
1729: break;
]
|
| 36/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 35100
output: 40
thoughts: 19
Thoughts: Here's my thought process, summarized as you requested:
**Finding the Right Function**
Okay, I need to get moving on this. First things first: I've got to locate the function `ieee80211_do_stop`. That's the key starting point. I know what the task is, and I need to hone in on the function that's central to its execution. Let's make sure I'm zeroing in on the correct function!
|
| 37/2 |
2026/08/04 02:09 |
tool |
grepper |
0m
Args: map[Expression:ieee80211_do_stop PathPrefix:net/mac80211/]
Results: map[Output:net/mac80211/cfg.c=96=static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/cfg.c-149- /*
net/mac80211/cfg.c:150: * Because the interface is down, ieee80211_do_stop
net/mac80211/cfg.c-151- * and ieee80211_do_open take care of "everything"
--
net/mac80211/iface.c=461=static int ieee80211_open(struct net_device *dev)
--
net/mac80211/iface.c-478-
net/mac80211/iface.c:479:static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
net/mac80211/iface.c-480-{
--
net/mac80211/iface.c=845=static int ieee80211_stop(struct net_device *dev)
--
net/mac80211/iface.c-867-
net/mac80211/iface.c:868: ieee80211_do_stop(sdata, true);
net/mac80211/iface.c-869-
--
net/mac80211/iface.c=1980=static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
--
net/mac80211/iface.c-2044- /* do_stop will synchronize_rcu() first thing */
net/mac80211/iface.c:2045: ieee80211_do_stop(sdata, false);
net/mac80211/iface.c-2046-
--
net/mac80211/iface.c=2440=void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata)
--
net/mac80211/iface.c-2443- return;
net/mac80211/iface.c:2444: ieee80211_do_stop(sdata, true);
net/mac80211/iface.c-2445-}
]
|
| 38/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 35964
output: 41
thoughts: 34
Thoughts: Okay, let's dive into this. Here's my take on those thoughts:
**Dissecting `ieee80211_do_stop`**
Alright, I'm thinking, "Let's crack open `ieee80211_do_stop`." Time to head over to `net/mac80211/iface.c` – that's where the interesting stuff usually happens with interface management in mac80211. I need to understand exactly what this function does. My expert eye is already scanning, thinking about the implications of stopping an interface: What state transitions are involved? What resources are being freed? Are there any subtle race conditions I should be looking out for? What about potential error conditions? This isn't just a simple stop; it's a careful dance that has to be done correctly to maintain system integrity. I'll need to consider how the function interacts with the hardware, the regulatory domain, and any pending traffic or connections. A good understanding of this function is fundamental to debugging and extending mac80211. Let's see what we've got.
|
| 39/2 |
2026/08/04 02:09 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:479 LineCount:50]
Results: map[Contents: 479: static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
480: {
481: struct ieee80211_local *local = sdata->local;
482: struct ieee80211_sub_if_data *iter;
483: unsigned long flags;
484: struct sk_buff_head freeq;
485: struct sk_buff *skb, *tmp;
486: u32 hw_reconf_flags = 0;
487: int i, flushed;
488: struct ps_data *ps;
489: struct cfg80211_chan_def chandef;
490: bool cancel_scan;
491: struct cfg80211_nan_func *func;
492:
493: lockdep_assert_wiphy(local->hw.wiphy);
494:
495: clear_bit(SDATA_STATE_RUNNING, &sdata->state);
496: synchronize_rcu(); /* flush _ieee80211_wake_txqs() */
497:
498: cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
499: if (cancel_scan)
500: ieee80211_scan_cancel(local);
501:
502: ieee80211_roc_purge(local, sdata);
503:
504: switch (sdata->vif.type) {
505: case NL80211_IFTYPE_STATION:
506: ieee80211_mgd_stop(sdata);
507: break;
508: case NL80211_IFTYPE_ADHOC:
509: ieee80211_ibss_stop(sdata);
510: break;
511: case NL80211_IFTYPE_MONITOR:
512: list_del_rcu(&sdata->u.mntr.list);
513: break;
514: case NL80211_IFTYPE_AP_VLAN:
515: ieee80211_apvlan_link_clear(sdata);
516: break;
517: default:
518: break;
519: }
520:
521: /*
522: * Remove all stations associated with this interface.
523: *
524: * This must be done before calling ops->remove_interface()
525: * because otherwise we can later invoke ops->sta_notify()
526: * whenever the STAs are removed, and that invalidates driver
527: * assumptions about always getting a vif pointer that is valid
528: * (because if we remove a STA after ops->remove_interface()
]
|
| 40/2 |
2026/08/04 02:09 |
llm |
expert |
0m
Model:
Tokens:
input: 37142
output: 41
thoughts: 162
Thoughts: **Analyzing the Stop Sequence**
Okay, so I'm thinking about how the system handles stopping a wireless interface. Specifically, I'm focused on `ieee80211_do_stop` and the `SDATA_STATE_RUNNING` flag. The first thing that jumps out is that `ieee80211_do_stop` immediately clears `SDATA_STATE_RUNNING` at the top of the function. That's the critical point here.
So, if `SDATA_STATE_RUNNING` is still set, it means either `ieee80211_do_stop` hasn't been called yet, or it *is* running but hasn't gotten past that first crucial line where it clears the flag. This is important to remember because of the order of operations.
Now, I recall that `drv_remove_interface` gets called *after* `SDATA_STATE_RUNNING` is cleared within `ieee80211_do_stop`. The next logical step is to pinpoint precisely where that `drv_remove_interface` call occurs within the stop procedure. Need to dig into that specific call within the code and see what might be happening.
|
| 41/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:700 LineCount:50]
Results: map[Contents: 700: }
701: }
702: }
703: spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
704:
705: /* ... and perform actual reclamation with interrupts enabled. */
706: skb_queue_walk_safe(&freeq, skb, tmp) {
707: __skb_unlink(skb, &freeq);
708: ieee80211_free_txskb(&local->hw, skb);
709: }
710:
711: if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
712: ieee80211_txq_remove_vlan(local, sdata);
713:
714: if (sdata->vif.txq)
715: ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq));
716:
717: if (sdata->vif.txq_mgmt)
718: ieee80211_txq_purge(sdata->local,
719: to_txq_info(sdata->vif.txq_mgmt));
720:
721: sdata->bss = NULL;
722:
723: if (local->open_count == 0)
724: ieee80211_clear_tx_pending(local);
725:
726: sdata->vif.bss_conf.beacon_int = 0;
727:
728: /*
729: * If the interface goes down while suspended, presumably because
730: * the device was unplugged and that happens before our resume,
731: * then the driver is already unconfigured and the remainder of
732: * this function isn't needed.
733: * XXX: what about WoWLAN? If the device has software state, e.g.
734: * memory allocated, it might expect teardown commands from
735: * mac80211 here?
736: */
737: if (local->suspended) {
738: WARN_ON(local->wowlan);
739: WARN_ON(rcu_access_pointer(local->monitor_sdata));
740: return;
741: }
742:
743: switch (sdata->vif.type) {
744: case NL80211_IFTYPE_AP_VLAN:
745: break;
746: case NL80211_IFTYPE_MONITOR:
747: if (local->virt_monitors == 0)
748: ieee80211_del_virtual_monitor(local);
749:
]
|
| 42/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 38445
output: 41
thoughts: 18
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in the field and diving into the `ieee80211_do_stop` function:
**Diving into `ieee80211_do_stop`**
Alright, let's keep going. We're in `ieee80211_do_stop`, and I need to really understand the implications of this function. This is critical for graceful device shutdown, and understanding how we transition back to a "stopped" state after having been in operation. I'm focusing on ensuring resources are cleanly released, hardware is properly configured for being inactive and making certain there are no lingering state issues that could cause problems later when we reboot or re-initialize. I'm thinking about things like pending frames, timers, and any hardware registers that need to be reset. I need to make sure this is rock-solid. A sloppy stop implementation can lead to instability, resource leaks, or even device lockups, so I want to make certain that we properly synchronize and shut down everything in the right order. This function is essential to system stability.
|
| 43/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:750 LineCount:50]
Results: map[Contents: 750: ieee80211_recalc_idle(local);
751: ieee80211_recalc_offload(local);
752:
753: if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
754: !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
755: break;
756:
757: ieee80211_link_release_channel(&sdata->deflink);
758: fallthrough;
759: default:
760: if (!going_down)
761: break;
762: drv_remove_interface(local, sdata);
763:
764: /* Clear private driver data to prevent reuse */
765: memset(sdata->vif.drv_priv, 0, local->hw.vif_data_size);
766: }
767:
768: ieee80211_recalc_ps(local);
769:
770: if (cancel_scan)
771: wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
772:
773: if (local->open_count == 0) {
774: ieee80211_stop_device(local, false);
775:
776: /* no reconfiguring after stop! */
777: return;
778: }
779:
780: /* do after stop to avoid reconfiguring when we stop anyway */
781: ieee80211_configure_filter(local);
782: ieee80211_hw_config(local, -1, hw_reconf_flags);
783:
784: /* Passing NULL means an interface is picked for configuration */
785: if (local->virt_monitors == local->open_count)
786: ieee80211_add_virtual_monitor(local, NULL);
787: }
788:
789: void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
790: {
791: struct ieee80211_sub_if_data *tx_sdata;
792: struct ieee80211_bss_conf *link_conf, *tx_bss_conf;
793: struct ieee80211_link_data *tx_link, *link;
794: unsigned int link_id;
795:
796: lockdep_assert_wiphy(sdata->local->hw.wiphy);
797:
798: /* Check if any of the links of current sdata is an MBSSID. */
799: for_each_vif_active_link(&sdata->vif, link_conf, link_id) {
]
|
| 44/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 39620
output: 41
thoughts: 195
Thoughts: **Debugging a Wireless Driver State Discrepancy**
Okay, so I'm looking at this code and something doesn't quite add up. It looks like `drv_remove_interface` is called within `ieee80211_do_stop`, but only if the `going_down` flag is set. The confusing part is that `ieee80211_do_stop` immediately clears `SDATA_STATE_RUNNING`. So, if `SDATA_STATE_RUNNING` *is* set, logically `ieee80211_do_stop` can't have been called yet, right? Which begs the question... why is `IEEE80211_SDATA_IN_DRIVER` false in this scenario? This indicates there's a disconnect.
Let me trace back through where `IEEE80211_SDATA_IN_DRIVER` is actually cleared to see if I can find the source of the issue. A quick scan reveals two possible locations: `drv_remove_interface` (which, as we've seen, is conditionally called within `ieee80211_do_stop`) and `ieee80211_handle_reconfig_failure`. I'm leaning toward the latter being the more likely culprit. I'll need to dive into the specifics of `ieee80211_handle_reconfig_failure` and see what's happening there to better understand what this function does and why it may be clearing this state bit when it shouldn't.
|
| 45/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/util.c FirstLine:1670 LineCount:50]
Results: map[Contents:1670: }
1671: }
1672:
1673: static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
1674: {
1675: struct ieee80211_sub_if_data *sdata;
1676: struct ieee80211_chanctx *ctx;
1677:
1678: lockdep_assert_wiphy(local->hw.wiphy);
1679:
1680: /*
1681: * We get here if during resume the device can't be restarted properly.
1682: * We might also get here if this happens during HW reset, which is a
1683: * slightly different situation and we need to drop all connections in
1684: * the latter case.
1685: *
1686: * Ask cfg80211 to turn off all interfaces, this will result in more
1687: * warnings but at least we'll then get into a clean stopped state.
1688: */
1689:
1690: local->resuming = false;
1691: local->suspended = false;
1692: local->in_reconfig = false;
1693: local->reconfig_failure = true;
1694:
1695: ieee80211_flush_completed_scan(local, true);
1696:
1697: /* scheduled scan clearly can't be running any more, but tell
1698: * cfg80211 and clear local state
1699: */
1700: ieee80211_sched_scan_end(local);
1701:
1702: list_for_each_entry(sdata, &local->interfaces, list)
1703: sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
1704:
1705: /* Mark channel contexts as not being in the driver any more to avoid
1706: * removing them from the driver during the shutdown process...
1707: */
1708: list_for_each_entry(ctx, &local->chanctx_list, list)
1709: ctx->driver_present = false;
1710: }
1711:
1712: static void ieee80211_assign_chanctx(struct ieee80211_local *local,
1713: struct ieee80211_sub_if_data *sdata,
1714: struct ieee80211_link_data *link)
1715: {
1716: struct ieee80211_chanctx_conf *conf;
1717: struct ieee80211_chanctx *ctx;
1718:
1719: lockdep_assert_wiphy(local->hw.wiphy);
]
|
| 46/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 41019
output: 23
thoughts: 298
Thoughts: **Analyzing the `check_sdata_in_driver` Macro**
Okay, so if `reconfig_failure` is true, the `WARN_ONCE` within `check_sdata_in_driver(sdata)` *shouldn't* trigger. I'm looking at this macro definition:
```c
#define check_sdata_in_driver(sdata) ({ \
WARN_ONCE(!sdata->local->reconfig_failure && \
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), \
"%s: Failed check-sdata-in-driver check, flags: 0x%x\n", \
sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); \
!!(sdata->flags & IEEE80211_SDATA_IN_DRIVER); \
})
```
The logic is pretty clear: it only warns if `reconfig_failure` is *false* AND `IEEE80211_SDATA_IN_DRIVER` is *also* false. But the warning *is* being triggered. So, that tells me that `reconfig_failure` *must* be false. That means, to reach the WARN_ONCE call, that the condition `!sdata->local->reconfig_failure && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)` is true.
Since `reconfig_failure` is false, and that part of the condition must be true, that also implies that `IEEE80211_SDATA_IN_DRIVER` must also be false.
My next step is to figure out why this is. I need to know where this flag might be set and cleared, as that will give me my answer. I'll need to re-examine the `drv_remove_interface` code, and any other places where this flag is manipulated.
|
| 47/2 |
2026/08/04 02:10 |
tool |
codesearch-find-references |
0m
Args: map[Name:drv_remove_interface]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__ieee80211_suspend SourceFile:net/mac80211/pm.c SourceLine:164] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:_ieee80211_change_mac SourceFile:net/mac80211/iface.c SourceLine:294] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_add_virtual_monitor SourceFile:net/mac80211/iface.c SourceLine:1288] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_del_virtual_monitor SourceFile:net/mac80211/iface.c SourceLine:1350] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_do_open SourceFile:net/mac80211/iface.c SourceLine:1604] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_do_stop SourceFile:net/mac80211/iface.c SourceLine:762] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_reconfig SourceFile:net/mac80211/util.c SourceLine:2030]] TruncatedOutput:false]
|
| 48/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 42239
output: 41
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:
**Diving into the MAC Address Change Function**
Alright, time to get to the core of this. My mind immediately jumps to `_ieee80211_change_mac`. That's the function I need to examine. Let's see what's happening under the hood there. I know from experience that changes to the MAC address within the 802.11 framework are critical and often involved in security and configuration. I need to understand how the driver handles that, and what implications a change will have. This is likely where the real magic or potential problems lie, so I'm focusing my attention there. Let's dig in and see.
|
| 49/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:270 LineCount:50]
Results: map[Contents: 270: {
271: struct ieee80211_local *local = sdata->local;
272: struct sockaddr *sa = addr;
273: bool check_dup = true;
274: bool live = false;
275: int ret;
276:
277: if (ieee80211_sdata_running(sdata)) {
278: ret = ieee80211_can_powered_addr_change(sdata);
279: if (ret)
280: return ret;
281:
282: live = true;
283: }
284:
285: if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
286: !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
287: check_dup = false;
288:
289: ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
290: if (ret)
291: return ret;
292:
293: if (live)
294: drv_remove_interface(local, sdata);
295: ret = eth_mac_addr(sdata->dev, sa);
296:
297: if (ret == 0) {
298: memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
299: ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
300: }
301:
302: /* Regardless of eth_mac_addr() return we still want to add the
303: * interface back. This should not fail...
304: */
305: if (live)
306: WARN_ON(drv_add_interface(local, sdata));
307:
308: return ret;
309: }
310:
311: static int ieee80211_change_mac(struct net_device *dev, void *addr)
312: {
313: struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
314: struct ieee80211_local *local = sdata->local;
315:
316: /*
317: * This happens during unregistration if there's a bond device
318: * active (maybe other cases?) and we must get removed from it.
319: * But we really don't care anymore if it's not registered now.
]
|
| 50/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 43266
output: 41
thoughts: 118
Thoughts: **Analyzing the MAC Address Change Process**
Okay, so I'm looking at how the MAC address is changed within the `ieee80211` framework. It appears the process is a bit convoluted. First, `drv_remove_interface` is called. That makes sense – you need to detach the existing interface. Then, `eth_mac_addr` gets involved, which I assume is for the actual MAC address manipulation. Finally, `drv_add_interface` is called, presumably to bring the interface back up with the new address.
A flag seems to be involved. Let's see... If `drv_add_interface` fails, there's a warning, but crucially, the `IEEE80211_SDATA_IN_DRIVER` flag remains false. That seems like a potential issue – a partial failure state. Now, hang on a second... I remember `drv_add_interface` sets `IEEE80211_SDATA_IN_DRIVER` *before* calling the driver. This is important to note and potentially creates some timing issues. I need to review `drv_add_interface` more closely to be certain of the implications.
|
| 51/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/driver-ops.c FirstLine:57 LineCount:50]
Results: map[Contents: 57: int drv_add_interface(struct ieee80211_local *local,
58: struct ieee80211_sub_if_data *sdata)
59: {
60: int ret;
61:
62: might_sleep();
63: lockdep_assert_wiphy(local->hw.wiphy);
64:
65: if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
66: (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
67: !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
68: !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
69: !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
70: return -EINVAL;
71:
72: trace_drv_add_interface(local, sdata);
73: ret = local->ops->add_interface(&local->hw, &sdata->vif);
74: trace_drv_return_int(local, ret);
75:
76: if (ret)
77: return ret;
78:
79: if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) {
80: sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
81:
82: drv_vif_add_debugfs(local, sdata);
83: /* initially vif is not MLD */
84: ieee80211_link_debugfs_drv_add(&sdata->deflink);
85: }
86:
87: return 0;
88: }
89:
90: int drv_change_interface(struct ieee80211_local *local,
91: struct ieee80211_sub_if_data *sdata,
92: enum nl80211_iftype type, bool p2p)
93: {
94: int ret;
95:
96: might_sleep();
97: lockdep_assert_wiphy(local->hw.wiphy);
98:
99: if (!check_sdata_in_driver(sdata))
100: return -EIO;
101:
102: trace_drv_change_interface(local, sdata, type, p2p);
103: ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
104: trace_drv_return_int(local, ret);
105: return ret;
106: }
]
|
| 52/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 44496
output: 41
thoughts: 643
Thoughts: **Analyzing Driver Interface Management and State Transitions**
Okay, let's break this down. My initial observation is that `drv_add_interface` strategically sets the `IEEE80211_SDATA_IN_DRIVER` flag *after* a successful `local->ops->add_interface` call. If `add_interface` fails, `drv_add_interface` immediately returns the error code (`ret`), and `IEEE80211_SDATA_IN_DRIVER` is *not* set. That's the critical point I'm focusing on. Now, I need to see where `drv_add_interface` is actually used and how the resulting return value is handled.
First, I examined `ieee80211_do_open`. I see that `drv_add_interface` is called and if it fails, the code jumps to `err_stop`. In `err_stop`, there's a conditional `drv_stop`, some interface cleanup and ultimately the return of the error code. The important thing I realize is that if `drv_add_interface` *does* fail here, the code never sets `SDATA_STATE_RUNNING`. This is because `set_bit(SDATA_STATE_RUNNING, &sdata->state)` comes *after* the call to `drv_add_interface` in `ieee80211_do_open`. So, the failure of `drv_add_interface` results in the `SDATA_STATE_RUNNING` bit *not* being set.
Next, I looked at `_ieee80211_change_mac`. Here, `drv_add_interface` is called within a `WARN_ON` block if the interface is live. If this call to `drv_add_interface` *fails*, `IEEE80211_SDATA_IN_DRIVER` will *not* be set. However, and this is crucial, it looks like `SDATA_STATE_RUNNING` *is* still set! The function doesn't seem to clear that state bit in the failure path. This is starting to look like a possible path to the state where `SDATA_STATE_RUNNING` is true, but `IEEE80211_SDATA_IN_DRIVER` is false.
Now I need to broaden my search. Are there other places where `drv_remove_interface` is called without subsequently clearing `SDATA_STATE_RUNNING`? I'll look into `ieee80211_reconfig`, specifically within `net/mac80211/util.c` to see if there are other potential sources.
|
| 53/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/util.c FirstLine:2000 LineCount:50]
Results: map[Contents:2000: }
2001:
2002: list_for_each_entry(sdata, &local->interfaces, list) {
2003: if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
2004: !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
2005: continue;
2006: /* These vifs can't be added before NAN was started */
2007: if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
2008: continue;
2009: if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
2010: ieee80211_sdata_running(sdata)) {
2011: res = drv_add_interface(local, sdata);
2012: if (WARN_ON(res))
2013: break;
2014: }
2015: }
2016:
2017: /* If adding any of the interfaces failed above, roll back and
2018: * report failure.
2019: */
2020: if (res) {
2021: list_for_each_entry_continue_reverse(sdata, &local->interfaces,
2022: list) {
2023: if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
2024: !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
2025: continue;
2026: if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
2027: continue;
2028: if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
2029: ieee80211_sdata_running(sdata))
2030: drv_remove_interface(local, sdata);
2031: }
2032: ieee80211_handle_reconfig_failure(local);
2033: return res;
2034: }
2035:
2036: /* add channel contexts */
2037: list_for_each_entry(ctx, &local->chanctx_list, list)
2038: if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
2039: WARN_ON(drv_add_chanctx(local, ctx));
2040:
2041: sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
2042: if (sdata && ieee80211_sdata_running(sdata))
2043: ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
2044:
2045: /* reconfigure hardware */
2046: ieee80211_hw_config(local, -1, IEEE80211_CONF_CHANGE_LISTEN_INTERVAL |
2047: IEEE80211_CONF_CHANGE_MONITOR |
2048: IEEE80211_CONF_CHANGE_PS |
2049: IEEE80211_CONF_CHANGE_RETRY_LIMITS |
]
|
| 54/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 46721
output: 42
thoughts: 567
Thoughts: **Analyzing a Potential Driver Warning**
Okay, let's break this down. My initial thought is about the `ieee80211_reconfig` function. If `drv_add_interface` fails within it, we know a rollback happens and `ieee80211_handle_reconfig_failure(local)` is called. That, in turn, sets `reconfig_failure` to true. This would then influence the behavior of `check_sdata_in_driver`. Essentially, if `reconfig_failure` becomes true, `check_sdata_in_driver` should *not* issue a warning.
The likely culprit seems to be the `_ieee80211_change_mac` path. I'm going to revisit that function. It gets called from `ieee80211_change_mac`, which is, essentially, the `ndo_set_mac_address` function for the network device. Aha, the user calls `SIOCSIFHWADDR` to change the MAC address. Now I'm seeing a possible issue. If `drv_add_interface` fails, then `IEEE80211_SDATA_IN_DRIVER` isn't set, but `SDATA_STATE_RUNNING` *is* still set.
So, the user calls `SIOCSIWRATE` ioctl. This triggers `cfg80211_wext_siwrate`, which in turn calls `rdev_set_bitrate_mask`. This flows into `ieee80211_set_bitrate_mask`, which correctly checks `ieee80211_sdata_running(sdata)` (that should return true). Then it calls `drv_set_bitrate_mask(local, sdata, mask)`. Inside this, `drv_set_bitrate_mask` calls `check_sdata_in_driver(sdata)`.
Here's the problem: `check_sdata_in_driver` sees that `sdata->flags & IEEE80211_SDATA_IN_DRIVER` is false, and `reconfig_failure` is also false, which should trigger a `WARN_ONCE`.
But wait. If `drv_add_interface` *fails* within `_ieee80211_change_mac`, it also triggers a `WARN_ON(drv_add_interface(local, sdata))`. This would create a separate warning *first*. Therefore, there is another potential path.
I'll check `ieee80211_add_virtual_monitor`. I need to broaden my search.
|
| 55/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1250 LineCount:50]
Results: map[Contents:1250: sdata->vif.type = NL80211_IFTYPE_MONITOR;
1251: snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
1252: wiphy_name(local->hw.wiphy));
1253: sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
1254: sdata->wdev.wiphy = local->hw.wiphy;
1255:
1256: ieee80211_sdata_init(local, sdata);
1257:
1258: ieee80211_set_default_queues(sdata);
1259:
1260: if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
1261: ret = drv_add_interface(local, sdata);
1262: if (WARN_ON(ret)) {
1263: /* ok .. stupid driver, it asked for this! */
1264: kfree(sdata);
1265: return ret;
1266: }
1267: }
1268:
1269: ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
1270: if (ret) {
1271: kfree(sdata);
1272: return ret;
1273: }
1274:
1275: set_bit(SDATA_STATE_RUNNING, &sdata->state);
1276:
1277: mutex_lock(&local->iflist_mtx);
1278: rcu_assign_pointer(local->monitor_sdata, sdata);
1279: mutex_unlock(&local->iflist_mtx);
1280:
1281: ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chanreq,
1282: IEEE80211_CHANCTX_EXCLUSIVE);
1283: if (ret) {
1284: mutex_lock(&local->iflist_mtx);
1285: RCU_INIT_POINTER(local->monitor_sdata, NULL);
1286: mutex_unlock(&local->iflist_mtx);
1287: synchronize_net();
1288: drv_remove_interface(local, sdata);
1289: clear_bit(SDATA_STATE_RUNNING, &sdata->state);
1290: kfree(sdata);
1291: return ret;
1292: }
1293:
1294: skb_queue_head_init(&sdata->skb_queue);
1295: skb_queue_head_init(&sdata->status_queue);
1296: wiphy_work_init(&sdata->work, ieee80211_iface_work);
1297:
1298: configure_monitor:
1299: /* Copy in the MU-MIMO configuration if set */
]
|
| 56/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 48786
output: 42
thoughts: 104
Thoughts: **Analyzing Virtual Monitor Interface Removal**
Okay, so I'm looking at the virtual monitor interface code, specifically how it's cleaned up when things go wrong. In `ieee80211_add_virtual_monitor`, if `ieee80211_link_use_channel` has a problem and fails, the system thoughtfully calls `drv_remove_interface`. After that we're removing `SDATA_STATE_RUNNING` bit from the state of the soft device, and finally `kfree(sdata)` cleans up the soft device. That makes sense, pretty standard cleanup, making sure the driver and the soft device are properly handled when the channel can't be linked. The `SDATA_STATE_RUNNING` bit is definitely cleared in this failure path.
Now, my mind goes to the opposite side of the coin: what about the *del* function, `ieee80211_del_virtual_monitor`? It's crucial to understand the symmetrical handling to guarantee no resources are leaked and everything's properly de-initialized. That's the next logical step in my analysis. I need to make sure the same cleanup logic is present, or if there's a different way of going about the shutdown.
|
| 57/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1330 LineCount:50]
Results: map[Contents:1330:
1331: if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
1332: return;
1333:
1334: ASSERT_RTNL();
1335: lockdep_assert_wiphy(local->hw.wiphy);
1336:
1337: mutex_lock(&local->iflist_mtx);
1338:
1339: sdata = rcu_dereference_protected(local->monitor_sdata,
1340: lockdep_is_held(&local->iflist_mtx));
1341: if (!sdata) {
1342: mutex_unlock(&local->iflist_mtx);
1343: return;
1344: }
1345:
1346: clear_bit(SDATA_STATE_RUNNING, &sdata->state);
1347: ieee80211_link_release_channel(&sdata->deflink);
1348:
1349: if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
1350: drv_remove_interface(local, sdata);
1351:
1352: RCU_INIT_POINTER(local->monitor_sdata, NULL);
1353: mutex_unlock(&local->iflist_mtx);
1354:
1355: synchronize_net();
1356:
1357: kfree(sdata);
1358: }
1359:
1360: /*
1361: * NOTE: Be very careful when changing this function, it must NOT return
1362: * an error on interface type changes that have been pre-checked, so most
1363: * checks should be in ieee80211_check_concurrent_iface.
1364: */
1365: int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
1366: {
1367: struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
1368: struct net_device *dev = wdev->netdev;
1369: struct ieee80211_local *local = sdata->local;
1370: u64 changed = 0;
1371: int res;
1372: u32 hw_reconf_flags = 0;
1373:
1374: lockdep_assert_wiphy(local->hw.wiphy);
1375:
1376: switch (sdata->vif.type) {
1377: case NL80211_IFTYPE_AP_VLAN: {
1378: struct ieee80211_sub_if_data *master;
1379:
]
|
| 58/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 50028
output: 42
thoughts: 216
Thoughts: **Analyzing the 802.11 Virtual Monitor Deletion**
Okay, so `ieee80211_del_virtual_monitor` first clears `SDATA_STATE_RUNNING`. My mind is racing a bit here. I'm wondering about a potential race condition. What if `ieee80211_sdata_running` is still true, but `IEEE80211_SDATA_IN_DRIVER` is false? That sounds like a situation where the interface isn't meant to be actively managed by the driver. I'm thinking of interface types that the driver doesn't natively handle, and my first thought is `NL80211_IFTYPE_AP_VLAN`.
Now, if I recall correctly, in `drv_add_interface`, there's a guard against adding this type:
```c
if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || ...))
return -EINVAL;
```
Right, so there's a `WARN_ON` check in `drv_add_interface` that prevents adding interfaces of the `NL80211_IFTYPE_AP_VLAN` type! This strongly suggests that these interfaces are explicitly *not* meant to be managed directly by the driver in the typical way. The question now becomes: how is the running state handled in this case? I need to check `ieee80211_do_open` and how it handles `NL80211_IFTYPE_AP_VLAN` to see how it's handled.
|
| 59/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1375 LineCount:50]
Results: map[Contents:1375:
1376: switch (sdata->vif.type) {
1377: case NL80211_IFTYPE_AP_VLAN: {
1378: struct ieee80211_sub_if_data *master;
1379:
1380: if (!sdata->bss)
1381: return -ENOLINK;
1382:
1383: list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
1384:
1385: master = container_of(sdata->bss,
1386: struct ieee80211_sub_if_data, u.ap);
1387: sdata->control_port_protocol =
1388: master->control_port_protocol;
1389: sdata->control_port_no_encrypt =
1390: master->control_port_no_encrypt;
1391: sdata->control_port_over_nl80211 =
1392: master->control_port_over_nl80211;
1393: sdata->control_port_no_preauth =
1394: master->control_port_no_preauth;
1395: sdata->vif.cab_queue = master->vif.cab_queue;
1396: memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
1397: sizeof(sdata->vif.hw_queue));
1398: sdata->vif.bss_conf.chanreq = master->vif.bss_conf.chanreq;
1399:
1400: sdata->crypto_tx_tailroom_needed_cnt +=
1401: master->crypto_tx_tailroom_needed_cnt;
1402:
1403: ieee80211_apvlan_link_setup(sdata);
1404:
1405: break;
1406: }
1407: case NL80211_IFTYPE_AP:
1408: case NL80211_IFTYPE_MESH_POINT:
1409: case NL80211_IFTYPE_STATION:
1410: case NL80211_IFTYPE_MONITOR:
1411: case NL80211_IFTYPE_ADHOC:
1412: case NL80211_IFTYPE_P2P_DEVICE:
1413: case NL80211_IFTYPE_OCB:
1414: case NL80211_IFTYPE_NAN:
1415: case NL80211_IFTYPE_PD:
1416: /* no special treatment */
1417: break;
1418: case NL80211_IFTYPE_NAN_DATA:
1419: if (WARN_ON(!rcu_access_pointer(sdata->u.nan_data.nmi)))
1420: return -ENOLINK;
1421: break;
1422: case NL80211_IFTYPE_UNSPECIFIED:
1423: case NUM_NL80211_IFTYPES:
1424: case NL80211_IFTYPE_P2P_CLIENT:
]
|
| 60/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 51568
output: 42
thoughts: 427
Thoughts: **Analyzing the VLAN AP Interface Setup**
Okay, so I'm looking at this code path for `NL80211_IFTYPE_AP_VLAN` and it seems like it breaks down pretty quickly. I see that the code goes into this `switch` statement, and based on the interface type it's supposed to handle different cases. Now, the interesting bit here is that when `sdata->vif.type` is set to `NL80211_IFTYPE_AP_VLAN`, it actually falls into the `default` case, which seems odd.
Now, in the `default` case, it calls `drv_add_interface`. I know this function well. But there's a problem: inside `drv_add_interface`, there's a `WARN_ON` check. It's explicitly checking if `sdata->vif.type` is `NL80211_IFTYPE_AP_VLAN`. And, if it is, the function immediately returns `-EINVAL`. So, there's an immediate conflict there. The code *intends* to handle VLAN APs, but the driver immediately rejects them with an error.
Wait! There's also `ieee80211_do_open`, and I need to check line 1515 and around there. I'm recalling something similar happening there, it seems like a potential place for another issue. This seems like a subtle but definite bug, specifically with how the `AP_VLAN` interface type is handled and passed to the driver. I need to dig deeper into the code around that area to confirm my suspicions and verify where this specific interface type is supported/used.
|
| 61/2 |
2026/08/04 02:10 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1515 LineCount:50]
Results: map[Contents:1515: sdata->bss = &sdata->u.ap;
1516: fallthrough;
1517: default:
1518: if (coming_up) {
1519: ieee80211_del_virtual_monitor(local);
1520: ieee80211_set_sdata_offload_flags(sdata);
1521:
1522: res = drv_add_interface(local, sdata);
1523: if (res)
1524: goto err_stop;
1525:
1526: ieee80211_set_vif_encap_ops(sdata);
1527: res = ieee80211_check_queues(sdata,
1528: ieee80211_vif_type_p2p(&sdata->vif));
1529: if (res)
1530: goto err_del_interface;
1531: }
1532:
1533: if (sdata->vif.type == NL80211_IFTYPE_AP) {
1534: local->fif_pspoll++;
1535: local->fif_probe_req++;
1536:
1537: ieee80211_configure_filter(local);
1538: } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
1539: local->fif_probe_req++;
1540: }
1541:
1542: if (sdata->vif.probe_req_reg)
1543: drv_config_iface_filter(local, sdata,
1544: FIF_PROBE_REQ,
1545: FIF_PROBE_REQ);
1546:
1547: if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
1548: sdata->vif.type != NL80211_IFTYPE_NAN &&
1549: sdata->vif.type != NL80211_IFTYPE_PD)
1550: changed |= ieee80211_reset_erp_info(sdata);
1551: ieee80211_link_info_change_notify(sdata, &sdata->deflink,
1552: changed);
1553:
1554: switch (sdata->vif.type) {
1555: case NL80211_IFTYPE_STATION:
1556: case NL80211_IFTYPE_ADHOC:
1557: case NL80211_IFTYPE_AP:
1558: case NL80211_IFTYPE_MESH_POINT:
1559: case NL80211_IFTYPE_OCB:
1560: case NL80211_IFTYPE_NAN_DATA:
1561: netif_carrier_off(dev);
1562: break;
1563: case NL80211_IFTYPE_P2P_DEVICE:
1564: case NL80211_IFTYPE_NAN:
]
|
| 62/2 |
2026/08/04 02:10 |
llm |
expert |
0m
Model:
Tokens:
input: 53307
output: 42
thoughts: 73
Thoughts: Here's my summary, as the expert:
**Investigating a VLAN AP Context**
Okay, so I'm digging into this `NL80211_IFTYPE_AP_VLAN` interface type and something's bugging me. It looks like the handling in `ieee80211_do_open` happens *before* the main switch statement on line 1480. I need to confirm this. I’m pretty sure I need to focus on where the interface type is handled, which I suspect is around line 1470 in that function. I'll take a closer look at the call to `ieee80211_do_open` to confirm my suspicion.
|
| 63/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/mac80211/iface.c FirstLine:1460 LineCount:50]
Results: map[Contents:1460: }
1461: }
1462:
1463: sdata->vif.addr_valid = sdata->vif.type != NL80211_IFTYPE_MONITOR ||
1464: (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE);
1465: switch (sdata->vif.type) {
1466: case NL80211_IFTYPE_AP_VLAN:
1467: /* no need to tell driver, but set carrier and chanctx */
1468: if (sdata->bss->active) {
1469: struct ieee80211_link_data *link;
1470:
1471: for_each_link_data(sdata, link) {
1472: ieee80211_link_vlan_copy_chanctx(link);
1473: }
1474:
1475: netif_carrier_on(dev);
1476: ieee80211_set_vif_encap_ops(sdata);
1477: } else {
1478: netif_carrier_off(dev);
1479: }
1480: break;
1481: case NL80211_IFTYPE_MONITOR:
1482: if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
1483: ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
1484: res = drv_add_interface(local, sdata);
1485: if (res)
1486: goto err_stop;
1487: } else {
1488: /* add/configure if there is no non-monitor interface */
1489: if (local->virt_monitors == local->open_count) {
1490: res = ieee80211_add_virtual_monitor(local, sdata);
1491: if (res)
1492: goto err_stop;
1493: }
1494:
1495: local->virt_monitors++;
1496:
1497: /* must be before the call to ieee80211_configure_filter */
1498: if (local->virt_monitors == 1) {
1499: local->hw.conf.flags |= IEEE80211_CONF_MONITOR;
1500: hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
1501: }
1502: }
1503:
1504: local->monitors++;
1505:
1506: ieee80211_adjust_monitor_flags(sdata, 1);
1507: ieee80211_configure_filter(local);
1508: ieee80211_recalc_offload(local);
1509: ieee80211_recalc_idle(local);
]
|
| 64/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 54437
output: 39
thoughts: 952
Thoughts: **Analyzing the AP_VLAN Interface Initialization and Bitrate Masking**
Okay, so I've been digging into the `AP_VLAN` interface type and how it interacts with the wireless stack. My initial observation is that, for `NL80211_IFTYPE_AP_VLAN`, the interface "breaks out" of the typical driver initialization flow, bypassing the `drv_add_interface` call, and skipping setting the `IEEE80211_SDATA_IN_DRIVER` flag. However, it does reach `ieee80211_do_open` and sets the `SDATA_STATE_RUNNING` bit.
This immediately got me thinking about the implications. If `SDATA_STATE_RUNNING` is set but `IEEE80211_SDATA_IN_DRIVER` is not, what happens when we try to interact with this interface? I decided to trace the execution path of a bitrate mask change, triggered by `cfg80211_wext_siwrate`. This function calls `rdev_set_bitrate_mask`, which, in turn, calls `ieee80211_set_bitrate_mask`. This function checks if the interface is running via `ieee80211_sdata_running(sdata)`, which returns true because `SDATA_STATE_RUNNING` is set. Then, it calls `drv_set_bitrate_mask`, which, in turn, calls `check_sdata_in_driver`. Here's where it gets interesting: `check_sdata_in_driver` checks `sdata->flags & IEEE80211_SDATA_IN_DRIVER`, which we know is false for `AP_VLAN`, and triggers the `WARN_ONCE`.
My next question was whether `cfg80211_wext_siwrate` could even be called on an `AP_VLAN` interface. Considering that `hostapd` creates these for dynamic VLANs, and that they are standard netdevs, then yes, `SIOCSIWRATE` *can* be called on them. I then went to check if `ieee80211_set_bitrate_mask` had any special handling for `AP_VLAN` interfaces. I confirmed that it doesn't. This means that, if a user calls `SIOCSIWRATE` on an `AP_VLAN` interface, the warning will be triggered.
Now, my mind shifts to security. Is `SIOCSIWRATE` accessible to unprivileged users? I need to check `sock_ioctl` and `wext_handle_ioctl` to verify that assumption.
|
| 65/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/wireless/wext-core.c FirstLine:1060 LineCount:50]
Results: map[Contents:1060: }
1061:
1062:
1063: int wext_handle_ioctl(struct net *net, unsigned int cmd, void __user *arg)
1064: {
1065: struct iw_request_info info = { .cmd = cmd, .flags = 0 };
1066: struct iwreq iwr;
1067: int ret;
1068:
1069: if (copy_from_user(&iwr, arg, sizeof(iwr)))
1070: return -EFAULT;
1071:
1072: iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0;
1073:
1074: ret = wext_ioctl_dispatch(net, &iwr, cmd, &info,
1075: ioctl_standard_call,
1076: ioctl_private_call);
1077: if (ret >= 0 &&
1078: IW_IS_GET(cmd) &&
1079: copy_to_user(arg, &iwr, sizeof(struct iwreq)))
1080: return -EFAULT;
1081:
1082: return ret;
1083: }
1084:
1085: #ifdef CONFIG_COMPAT
1086: static int compat_standard_call(struct net_device *dev,
1087: struct iwreq *iwr,
1088: unsigned int cmd,
1089: struct iw_request_info *info,
1090: iw_handler handler)
1091: {
1092: const struct iw_ioctl_description *descr;
1093: struct compat_iw_point *iwp_compat;
1094: struct iw_point iwp;
1095: int err;
1096:
1097: descr = standard_ioctl + IW_IOCTL_IDX(cmd);
1098:
1099: if (descr->header_type != IW_HEADER_TYPE_POINT)
1100: return ioctl_standard_call(dev, iwr, cmd, info, handler);
1101:
1102: iwp_compat = (struct compat_iw_point *) &iwr->u.data;
1103:
1104: /* struct iw_point has a 32bit hole on 64bit arches. */
1105: memset(&iwp, 0, sizeof(iwp));
1106:
1107: iwp.pointer = compat_ptr(iwp_compat->pointer);
1108: iwp.length = iwp_compat->length;
1109: iwp.flags = iwp_compat->flags;
]
|
| 66/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 56788
output: 38
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Diving into `wext_ioctl_dispatch`**
Alright, time to get my hands dirty. My first instinct is to check `wext_ioctl_dispatch`. I need to understand exactly how the wireless extensions (wext) ioctl calls are handled at this level. I'm thinking about the control flow here: where's the entry point? What are the key data structures involved? Is this the central dispatch function, or is there a more granular layer I need to consider? I'm anticipating potential areas of interest immediately: error handling, security considerations (especially given the nature of wireless configuration), and the general efficiency of the call routing. Time to dig in and see how the kernel is really handling these wireless configuration requests.
|
| 67/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/wireless/wext-core.c FirstLine:970 LineCount:50]
Results: map[Contents: 970:
971: /* Basic check */
972: if (!netif_device_present(dev))
973: return -ENODEV;
974:
975: /* New driver API : try to find the handler */
976: handler = get_handler(dev, cmd);
977: if (handler) {
978: /* Standard and private are not the same */
979: if (cmd < SIOCIWFIRSTPRIV)
980: return standard(dev, iwr, cmd, info, handler);
981: else if (private)
982: return private(dev, iwr, cmd, info, handler);
983: }
984: return -EOPNOTSUPP;
985: }
986:
987: /* If command is `set a parameter', or `get the encoding parameters',
988: * check if the user has the right to do it.
989: */
990: static int wext_permission_check(unsigned int cmd)
991: {
992: if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE ||
993: cmd == SIOCGIWENCODEEXT) &&
994: !capable(CAP_NET_ADMIN))
995: return -EPERM;
996:
997: return 0;
998: }
999:
1000: /* entry point from dev ioctl */
1001: static int wext_ioctl_dispatch(struct net *net, struct iwreq *iwr,
1002: unsigned int cmd, struct iw_request_info *info,
1003: wext_ioctl_func standard,
1004: wext_ioctl_func private)
1005: {
1006: int ret = wext_permission_check(cmd);
1007:
1008: if (ret)
1009: return ret;
1010:
1011: dev_load(net, iwr->ifr_name);
1012: rtnl_lock();
1013: ret = wireless_process_ioctl(net, iwr, cmd, info, standard, private);
1014: rtnl_unlock();
1015:
1016: return ret;
1017: }
1018:
1019: /*
]
|
| 68/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 57714
output: 41
thoughts: 211
Thoughts: **Analyzing Wireless Rate Limiting Permissions**
Okay, so I'm digging into how the kernel handles permissions for setting wireless rates. My initial thought is focused on `wext_permission_check`. It looks like this function uses `capable(CAP_NET_ADMIN)` when dealing with `IW_IS_SET(cmd)`. Right, and `SIOCSIWRATE` is a SET command, so that's the trigger. This naturally implies `CAP_NET_ADMIN` is required.
Now, a quick sanity check: is it `capable` or `ns_capable`? Ah, it’s using `capable(CAP_NET_ADMIN)`. This is key. That means this action demands `CAP_NET_ADMIN` within the *initial* user namespace. Wait a minute… looking closer, I see `capable` actually checks `ns_capable(&init_user_ns, cap)`. That solidifies it: this operation is NOT going to be accessible from an unprivileged process. And, very importantly, it's also NOT accessible from within a user namespace. It’s pretty tightly locked down.
But I should broaden my view, right? Is `SIOCSIWRATE` the *only* way this can be triggered? Let’s not jump to conclusions. What about the newer nl80211 interface? Specifically, what about `NL80211_CMD_SET_BITRATE_MASK`? I'd better double-check the `nl80211_set_bitrate_mask` function in `net/wireless/nl80211.c` to be sure. It's best to verify that these permissions are consistent across both interfaces.
|
| 69/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/wireless/nl80211.c FirstLine:5850 LineCount:50]
Results: map[Contents:5850: break;
5851: case IEEE80211_VHT_MCS_SUPPORT_0_7:
5852: mcs_mask = 0x00FF;
5853: break;
5854: case IEEE80211_VHT_MCS_SUPPORT_0_8:
5855: mcs_mask = 0x01FF;
5856: break;
5857: case IEEE80211_VHT_MCS_SUPPORT_0_9:
5858: mcs_mask = 0x03FF;
5859: break;
5860: default:
5861: break;
5862: }
5863:
5864: return mcs_mask;
5865: }
5866:
5867: static void vht_build_mcs_mask(u16 vht_mcs_map,
5868: u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
5869: {
5870: u8 nss;
5871:
5872: for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
5873: vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
5874: vht_mcs_map >>= 2;
5875: }
5876: }
5877:
5878: static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
5879: struct nl80211_txrate_vht *txrate,
5880: u16 mcs[NL80211_VHT_NSS_MAX])
5881: {
5882: u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
5883: u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
5884: u8 i;
5885:
5886: if (!sband->vht_cap.vht_supported)
5887: return false;
5888:
5889: memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
5890:
5891: /* Build vht_mcs_mask from VHT capabilities */
5892: vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
5893:
5894: for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
5895: if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
5896: mcs[i] = txrate->mcs[i];
5897: else
5898: return false;
5899: }
]
|
| 70/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 59221
output: 45
thoughts: 19
Thoughts: Here's my thought process, as if I were considering this myself:
**Tracing Down `nl80211_set_bitrate_mask`**
Okay, so I need to find the definition and usage of `nl80211_set_bitrate_mask`. My first move, as always, is to fire up my trusty code browser and search for that exact symbol. I'll need to remember to check both kernel source and, since it has `nl80211` in the name, probably the userspace nl80211 headers and libraries too.
Given its name, it probably manipulates some sort of bitmask related to data rates, which is typically tied into the wireless configuration. I'll expect to see it called as part of a Netlink message handling sequence, likely related to setting or configuring the wireless interface's capabilities or the supported rates. I'll need to look for Netlink message handler functions within the nl80211 subsystem. Specifically, I'll be looking for handlers that deal with configuration parameters, rates, or even potentially regulatory domain settings as they influence available rates.
Once I find its definition, I'll pay close attention to the data types it operates on. What data structure is being used for the bitmask? What is the meaning of each bit in the mask? I'll also be interested in where and how the mask is actually used within the kernel’s wireless stack. Is it passed down to hardware drivers? Is it used to determine which rates are advertised to other wireless devices? Is there any rate negotiation logic based on this mask?
I'll also keep in mind that I might need to consider device-specific considerations. Depending on the hardware, the interpretation of the bitrate mask can differ. So, I may need to look for architecture-specific code or driver-specific adaptations that relate to the bitrate mask. And, naturally, I'll want to check for any error handling related to setting or checking the bitmask; I'm expecting some validation steps or checks around that bitmask, because messing with rates can quickly render the device incapable of communicating.
Finally, I'll try to get a broad understanding of the function's place within the overall wireless stack architecture and how it integrates with other components. Understanding the bigger picture helps with predicting how changes to `nl80211_set_bitrate_mask` can affect the overall behavior of the system.
|
| 71/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Error:
Args: map[Expression:nl80211_set_bitrate_mask PathPrefix:net/wireless/nl80211.c]
Results:
|
| 72/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 59814
output: 47
thoughts: 37
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of wireless networking code:
**Diving into the Bitrate Controls**
Alright, so I need to find where the `NL80211_CMD_SET_BITRATE_MASK` command is handled. It's time to dig into the heart of the netlink interface, specifically within the wireless subsystem, and my intuition suggests that `net/wireless/nl80211.c` is the most logical place to start looking. I'm going to run a search for that constant within that file. This likely controls the mechanism for setting the permissible data rates on a wireless interface, so understanding its implementation is key for troubleshooting or implementing advanced features. Let's see what we can find.
|
| 73/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Error:
Args: map[Expression:NL80211_CMD_SET_BITRATE_MASK PathPrefix:net/wireless/nl80211.c]
Results:
|
| 74/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 60081
output: 49
thoughts: 24
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Decoding the Wireless Command**
Okay, let me just double-check that this is indeed the right command. Hmm, let me see... Yes, there it is! Confirmed. It's definitely `NL80211_CMD_SET_TX_BITRATE_MASK`. Alright, good. We're on the right track then. Now I can proceed with the analysis of its implications. This command is critical for controlling transmission rates, which is, of course, absolutely crucial to performance and efficiency. Time to consider the next step.
|
| 75/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Args: map[Expression:NL80211_CMD_SET_TX_BITRATE_MASK PathPrefix:net/wireless/nl80211.c]
Results: map[Output:net/wireless/nl80211.c=19547=static const struct genl_small_ops nl80211_small_ops[] = {
--
net/wireless/nl80211.c-19924- {
net/wireless/nl80211.c:19925: .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
net/wireless/nl80211.c-19926- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
]
|
| 76/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 60459
output: 42
thoughts: 26
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the NL80211_CMD_SET_TX_BITRATE_MASK Handler**
Okay, so I'm diving into the code. I need to understand how the system manages that `NL80211_CMD_SET_TX_BITRATE_MASK` command. Specifically, I want to trace the execution path and identify the function responsible for processing it. Knowing this handler is crucial for understanding how the driver configures transmit rate masks. I need to understand how the mask is validated, applied, and perhaps, how it interacts with other rate control mechanisms. This isn't just about finding the function's name; it's about grasping the underlying logic and potential interactions with other parts of the wireless stack. My focus here is going to be tracing the specific command and how it affects the hardware.
|
| 77/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/wireless/nl80211.c FirstLine:19920 LineCount:20]
Results: map[Contents:19920: .doit = nl80211_cancel_remain_on_channel,
19921: .flags = GENL_UNS_ADMIN_PERM,
19922: .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
19923: },
19924: {
19925: .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
19926: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
19927: .doit = nl80211_set_tx_bitrate_mask,
19928: .flags = GENL_UNS_ADMIN_PERM,
19929: .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV |
19930: NL80211_FLAG_MLO_VALID_LINK_ID),
19931: },
19932: {
19933: .cmd = NL80211_CMD_REGISTER_FRAME,
19934: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
19935: .doit = nl80211_register_mgmt,
19936: .flags = GENL_UNS_ADMIN_PERM,
19937: .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV),
19938: },
19939: {
]
|
| 78/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 61166
output: 39
thoughts: 90
Thoughts: **Understanding the Bitrate Mask Restriction**
Okay, so I'm looking at how we manage the transmit bitrate mask, and the function `nl80211_set_tx_bitrate_mask` is the key player here. It seems straightforward, but I need to understand the implications of its security requirements.
The critical piece is that it's flagged with `.flags = GENL_UNS_ADMIN_PERM`. Now, that specific flag – `GENL_UNS_ADMIN_PERM` – is the part that demands further investigation. What exactly does *that* mean in terms of user permissions?
Aha, it turns out that `GENL_UNS_ADMIN_PERM` translates to requiring `CAP_NET_ADMIN` within the user namespace. This is important. So, for the user to be able to set this bitrate mask, they're going to need that particular capability. This means I'll need to double-check that definition in the code to ensure I haven't missed anything crucial.
|
| 79/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Args: map[Expression:GENL_UNS_ADMIN_PERM PathPrefix:include/net/genetlink.h]
Results: map[Output:include/net/genetlink.h=173=enum genl_validate_flags {
--
include/net/genetlink.h-182- * @internal_flags: flags used by the family
include/net/genetlink.h:183: * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM)
include/net/genetlink.h-184- * @validate: validation flags from enum genl_validate_flags
--
include/net/genetlink.h=191=struct genl_small_ops {
--
include/net/genetlink.h-203- * @internal_flags: flags used by the family
include/net/genetlink.h:204: * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM)
include/net/genetlink.h-205- * @maxattr: maximum number of attributes supported
--
include/net/genetlink.h=213=struct genl_ops {
--
include/net/genetlink.h-231- * @internal_flags: flags used by the family
include/net/genetlink.h:232: * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM)
include/net/genetlink.h-233- * @validate: validation flags from enum genl_validate_flags
]
|
| 80/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 61898
output: 34
thoughts: 20
Thoughts: Okay, let's dive into this. Here's my thought process on tracking down that definition:
**Pinpointing the Source of `GENL_UNS_ADMIN_PERM`**
Alright, my immediate thought is to trace the definition of `GENL_UNS_ADMIN_PERM`. I need to figure out exactly where this constant, macro, or variable is declared. Is it in a header file? A configuration script? Maybe even hardcoded somewhere, though I sincerely hope not. My instinct is to start with a broad search of the codebase. A simple `grep` or equivalent within the project directory should be my first step. I'm assuming this is a project with a defined build system, so knowing the file structure is key, and I’ll have to tailor my search accordingly. From there, I'll be able to work backward. Once I find it, I can identify where the value originates.
|
| 81/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Args: map[Expression:#define GENL_UNS_ADMIN_PERM PathPrefix:include/]
Results: map[Output:include/uapi/linux/genetlink.h=13=struct genlmsghdr {
--
include/uapi/linux/genetlink.h-24-#define GENL_CMD_CAP_HASPOL 0x08
include/uapi/linux/genetlink.h:25:#define GENL_UNS_ADMIN_PERM 0x10
include/uapi/linux/genetlink.h-26-
]
|
| 82/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 62266
output: 40
thoughts: 32
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating GENL_UNS_ADMIN_PERM in genetlink.c**
Alright, time to dig into this `GENL_UNS_ADMIN_PERM` macro. I need to refresh my memory on its usage within the `genetlink.c` code. It's crucial for understanding access control and permission models within that framework, so let's start by quickly tracing its occurrences within that specific file. I want to see precisely where it's employed. What functions, structures, or contexts rely on this permission check? Understanding the implementation context is paramount. Are we dealing with security constraints, configuration parameters, or something else entirely? A quick scan of `net/netlink/genetlink.c` will give me the contextual foundation I need. Let's get to it.
|
| 83/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Args: map[Expression:GENL_UNS_ADMIN_PERM PathPrefix:net/netlink/genetlink.c]
Results: map[Output:net/netlink/genetlink.c=1155=static int genl_family_rcv_msg(const struct genl_family *family,
--
net/netlink/genetlink.c-1185-
net/netlink/genetlink.c:1186: if ((op.flags & GENL_UNS_ADMIN_PERM) &&
net/netlink/genetlink.c-1187- !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
]
|
| 84/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 62676
output: 41
thoughts: 372
Thoughts: **Analyzing a Potential Vulnerability**
Okay, so I'm seeing `netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)` used, which immediately flags something. It requires `CAP_NET_ADMIN` within the *network namespace's* user namespace. That's key, because that implies it *is* accessible from a user namespace! Hmm... if an unprivileged user creates both a user namespace and a network namespace, they're granted `CAP_NET_ADMIN` within that very user namespace.
Now, the natural extension is, can they leverage this? If they get `CAP_NET_ADMIN`, could they create a virtual WiFi device, like `mac80211_hwsim`, or even use a physical one if it's been moved into their network namespace? Wait a minute... Can an unprivileged user even create the `mac80211_hwsim` device directly? No, creating that requires `CAP_NET_ADMIN` in the *initial* namespace or it is set as a module parameter. So, that avenue is likely blocked.
However, if they have their own user namespace, could they trigger this vulnerability on an *existing* WiFi device? If they move a WiFi device into their network namespace, they effectively have `CAP_NET_ADMIN` over that device, so there's potential there. The question becomes, can they exploit this via a call such as `SIOCSIWRATE`? But, hold on a second! `SIOCSIWRATE` relies on `wext_permission_check`, which uses `capable(CAP_NET_ADMIN)`. `capable` checks the initial user namespace. So, `SIOCSIWRATE` itself is likely NOT accessible from their user namespace. Good.
Okay, but what about Netlink? Wait, I see `NL80211_CMD_SET_TX_BITRATE_MASK` uses `GENL_UNS_ADMIN_PERM`. And `GENL_UNS_ADMIN_PERM` *is* accessible from user namespaces! Bingo! That's the vector we need to investigate. So, the crucial question now is, can `nl80211_set_tx_bitrate_mask` trigger the same vulnerability? That's the next step to analyze.
|
| 85/2 |
2026/08/04 02:11 |
tool |
read-file |
0m
Args: map[File:net/wireless/nl80211.c FirstLine:5900 LineCount:50]
Results: map[Contents:5900:
5901: return true;
5902: }
5903:
5904: static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map)
5905: {
5906: switch (he_mcs_map) {
5907: case IEEE80211_HE_MCS_NOT_SUPPORTED:
5908: return 0;
5909: case IEEE80211_HE_MCS_SUPPORT_0_7:
5910: return 0x00FF;
5911: case IEEE80211_HE_MCS_SUPPORT_0_9:
5912: return 0x03FF;
5913: case IEEE80211_HE_MCS_SUPPORT_0_11:
5914: return 0xFFF;
5915: default:
5916: break;
5917: }
5918: return 0;
5919: }
5920:
5921: static void he_build_mcs_mask(u16 he_mcs_map,
5922: u16 he_mcs_mask[NL80211_HE_NSS_MAX])
5923: {
5924: u8 nss;
5925:
5926: for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) {
5927: he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map & 0x03);
5928: he_mcs_map >>= 2;
5929: }
5930: }
5931:
5932: static u16 he_get_txmcsmap(struct genl_info *info, unsigned int link_id,
5933: const struct ieee80211_sta_he_cap *he_cap)
5934: {
5935: struct net_device *dev = info->user_ptr[1];
5936: struct wireless_dev *wdev = dev->ieee80211_ptr;
5937: struct cfg80211_chan_def *chandef;
5938: __le16 tx_mcs;
5939:
5940: chandef = wdev_chandef(wdev, link_id);
5941: if (!chandef) {
5942: /*
5943: * This is probably broken, but we never maintained
5944: * a chandef in these cases, so it always was.
5945: */
5946: return le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
5947: }
5948:
5949: switch (chandef->width) {
]
|
| 86/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 64412
output: 47
thoughts: 36
Thoughts: Okay, let's see. My current task is to understand how the system configures the transmit bitrate mask for wireless devices. Since I'm working with the kernel, and the likely module is related to network wireless functions, I need to pinpoint the relevant function. Let's start with `nl80211_set_tx_bitrate_mask`. That function name suggests a good starting point. Now, where is it defined? Knowing how kernel modules are structured, `net/wireless/nl80211.c` is the most probable location. That seems like the right file, given the naming convention and purpose. I'll start by checking there.
|
| 87/2 |
2026/08/04 02:11 |
tool |
grepper |
0m
Args: map[Expression:nl80211_set_tx_bitrate_mask PathPrefix:net/wireless/nl80211.c]
Results: map[Output:net/wireless/nl80211.c=14505=static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
--
net/wireless/nl80211.c-14522-
net/wireless/nl80211.c:14523:static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
net/wireless/nl80211.c-14524- struct genl_info *info)
--
net/wireless/nl80211.c=19547=static const struct genl_small_ops nl80211_small_ops[] = {
--
net/wireless/nl80211.c-19926- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/wireless/nl80211.c:19927: .doit = nl80211_set_tx_bitrate_mask,
net/wireless/nl80211.c-19928- .flags = GENL_UNS_ADMIN_PERM,
]
|
| 88/2 |
2026/08/04 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 64960
output: 42
thoughts: 21
Thoughts: Okay, let's dive into this. Here's my take on `nl80211_set_tx_bitrate_mask`:
**Diving into nl80211_set_tx_bitrate_mask**
Alright, time to get my hands dirty. I'm going to start by taking a close look at this `nl80211_set_tx_bitrate_mask` function. I know this likely deals with configuring the transmission rate, but the specifics are where the devil is. I need to understand how it's interacting with the nl80211 interface, and, importantly, which structures and data types are in play. I'm particularly interested in how the bitrate mask is represented – is it a bitmask? A set of values? How granular is the control? I need to understand the implications of setting this mask for the overall performance and reliability of the wireless link. I'll be looking for potential edge cases, any error handling, and how it interacts with other configuration parameters within the nl80211 framework. I'll need to remember to focus on the interplay between the userspace requests, and the kernel side processing. I'll also be considering the potential impact on power management.
|
| 89/2 |
2026/08/04 02:12 |
tool |
read-file |
0m
Args: map[File:net/wireless/nl80211.c FirstLine:14523 LineCount:50]
Results: map[Contents:14523: static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
14524: struct genl_info *info)
14525: {
14526: struct cfg80211_bitrate_mask mask;
14527: unsigned int link_id = nl80211_link_id(info->attrs);
14528: struct cfg80211_registered_device *rdev = info->user_ptr[0];
14529: struct net_device *dev = info->user_ptr[1];
14530: int err;
14531:
14532: if (!rdev->ops->set_bitrate_mask)
14533: return -EOPNOTSUPP;
14534:
14535: err = nl80211_parse_tx_bitrate_mask(info, info->attrs,
14536: NL80211_ATTR_TX_RATES, &mask,
14537: dev, true, link_id);
14538: if (err)
14539: return err;
14540:
14541: return rdev_set_bitrate_mask(rdev, dev, link_id, NULL, &mask);
14542: }
14543:
14544: static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
14545: {
14546: struct cfg80211_registered_device *rdev = info->user_ptr[0];
14547: struct wireless_dev *wdev = info->user_ptr[1];
14548: u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
14549:
14550: if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
14551: return -EINVAL;
14552:
14553: if (info->attrs[NL80211_ATTR_FRAME_TYPE])
14554: frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
14555:
14556: switch (wdev->iftype) {
14557: case NL80211_IFTYPE_STATION:
14558: case NL80211_IFTYPE_ADHOC:
14559: case NL80211_IFTYPE_P2P_CLIENT:
14560: case NL80211_IFTYPE_AP:
14561: case NL80211_IFTYPE_AP_VLAN:
14562: case NL80211_IFTYPE_MESH_POINT:
14563: case NL80211_IFTYPE_P2P_GO:
14564: case NL80211_IFTYPE_P2P_DEVICE:
14565: break;
14566: case NL80211_IFTYPE_NAN:
14567: case NL80211_IFTYPE_NAN_DATA:
14568: if (!wiphy_ext_feature_isset(wdev->wiphy,
14569: NL80211_EXT_FEATURE_SECURE_NAN) &&
14570: !(wdev->wiphy->nan_capa.flags &
14571: WIPHY_NAN_FLAGS_USERSPACE_DE))
14572: return -EOPNOTSUPP;
]
|
| 90/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 66328
output: 42
thoughts: 263
Thoughts: **Vulnerability Analysis: nl80211_set_tx_bitrate_mask**
Okay, so `nl80211_set_tx_bitrate_mask` calls `rdev_set_bitrate_mask` – interesting. The key here is that it *doesn't* validate the interface state. No check for it being UP, and critically, no check if it's an `AP_VLAN` interface. This is where things get interesting from a privilege escalation perspective.
So, the attack vector... an unprivileged user can leverage user and network namespaces. They could craft a scenario using a `mac80211_hwsim` device... hold on, `mac80211_hwsim` needs `CAP_NET_ADMIN` in the *initial* namespace. That's a blocker. But wait! What if they *inherit* a physical wifi device that was moved into their network namespace? Bingo! If they have a device in their netns, then they can create an `AP_VLAN` interface on it!
But, I need to be careful; creating an `AP_VLAN` interface *also* needs `CAP_NET_ADMIN` in the network namespace. That's a critical point to verify. Let's dig into `nl80211_new_interface` and see what permissions it checks for `AP_VLAN` creation. Gotta make sure they *can* actually create that vlan.
|
| 91/2 |
2026/08/04 02:12 |
tool |
grepper |
0m
Args: map[Expression:nl80211_new_interface PathPrefix:net/wireless/nl80211.c]
Results: map[Output:net/wireless/nl80211.c=5044=static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
--
net/wireless/nl80211.c-5118-
net/wireless/nl80211.c:5119:static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
net/wireless/nl80211.c-5120-{
--
net/wireless/nl80211.c-5221-
net/wireless/nl80211.c:5222:static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
net/wireless/nl80211.c-5223-{
--
net/wireless/nl80211.c-5230-
net/wireless/nl80211.c:5231: return _nl80211_new_interface(skb, info);
net/wireless/nl80211.c-5232-}
--
net/wireless/nl80211.c=19547=static const struct genl_small_ops nl80211_small_ops[] = {
--
net/wireless/nl80211.c-19572- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/wireless/nl80211.c:19573: .doit = nl80211_new_interface,
net/wireless/nl80211.c-19574- .flags = GENL_UNS_ADMIN_PERM,
]
|
| 92/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 67432
output: 26
thoughts: 308
Thoughts: **Analyzing the Security Implications of `nl80211_new_interface`**
Okay, so `nl80211_new_interface` sets `.flags = GENL_UNS_ADMIN_PERM`. Right, that means a user *with* a Wi-Fi device in their network namespace can potentially create an `AP_VLAN` interface. Hmm, but hold on, does the user *need* a physical Wi-Fi device to begin with? It's typically in the initial network namespace, isn't it? Can an unprivileged user even *move* a Wi-Fi device into their own namespace? No, no they can't. That requires `CAP_NET_ADMIN` in the original namespace, which they won't have.
So, even if they create a user namespace and the system uses something like `mac80211_hwsim`, they still wouldn't have the device, correct? That would be useful. This looks like the bug isn't exploitable from unprivileged processes or user namespaces, unless there's a Wi-Fi device *already* within a user namespace, which is not the standard configuration.
But wait, what about `SIOCSIWRATE` in the initial namespace? Can that be used to trigger something? Nope. `SIOCSIWRATE` also needs `CAP_NET_ADMIN` in the initial namespace.
Alright, so the original plan is not looking fruitful, is there any other way to trigger `drv_set_bitrate_mask`? Gotta check all the callers of `drv_set_bitrate_mask` to be sure.
|
| 93/2 |
2026/08/04 02:12 |
tool |
codesearch-find-references |
0m
Args: map[Name:drv_set_bitrate_mask]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee80211_set_bitrate_mask SourceFile:net/mac80211/cfg.c SourceLine:4134]] TruncatedOutput:false]
|
| 94/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 68200
output: 27
thoughts: 111
Thoughts: **Analyzing the Bitrate Mask Setter**
Okay, so I'm tracing the usage of `ieee80211_set_bitrate_mask`. My initial assessment is that it's only called directly by `ieee80211_set_bitrate_mask` itself. That gives me a good starting point. Now, to understand *why* it's being used, I need to know where *it* is called from.
Based on the code, `ieee80211_set_bitrate_mask` is called from two key places. First, there's `rdev_set_bitrate_mask`, which gets the `set_bitrate_mask` via the `rdev->ops` pointer. Second, it's called from `mac80211_config_ops.set_bitrate_mask`.
Now, the real question is, who's calling `rdev_set_bitrate_mask`? That's where I need to dig next to fully understand the flow and context of this bitrate mask configuration. I'll need to examine the callers of `rdev_set_bitrate_mask`.
|
| 95/2 |
2026/08/04 02:12 |
tool |
codesearch-find-references |
0m
Args: map[Name:rdev_set_bitrate_mask]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cfg80211_wext_siwrate SourceFile:net/wireless/wext-compat.c SourceLine:1238] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nl80211_set_tx_bitrate_mask SourceFile:net/wireless/nl80211.c SourceLine:14541]] TruncatedOutput:false]
|
| 96/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 68742
output: 46
thoughts: 944
Thoughts: **Analyzing the Potential for Denial of Service**
Okay, let's break this down. My initial thought is to look at the vulnerable code paths: `cfg80211_wext_siwrate` and `nl80211_set_tx_bitrate_mask`. Both of these require `CAP_NET_ADMIN`. Since unprivileged users can't typically get a Wi-Fi device in their user namespace, the vulnerability seems inaccessible to them. So, the direct route is blocked.
The idea of a malicious USB Wi-Fi dongle popped into my head. Could that be a vector? Even if a user plugs in a bad dongle, they'd still need `CAP_NET_ADMIN` to actually use `SIOCSIWRATE` or `NL80211_CMD_SET_TX_BITRATE_MASK`. The dongle itself isn't the trigger; it's the user-space command. And, no, the peripheral can't trigger it by sending frames as `drv_set_bitrate_mask` is only called from the configuration path.
Now, the potential impact: a `WARNING` in `net/mac80211/driver-ops.h`. Looking at the code, it returns `-EIO` if `!check_sdata_in_driver(sdata)`. No crash, no memory corruption. The question becomes: could this be a denial-of-service? A `WARNING` prints a stack trace, which is undesirable. In a production environment with `panic_on_warn` set, it could lead to a crash, which is a DoS. But in most cases, a WARNING is just that: a warning. However, I know syzkaller reports even warnings as bugs.
The prompt specifically asks about the potential for DoS in the context of `WARNING`s that *don't* cause crashes in production. The prompt suggests thinking about the consequences of the violated kernel assumptions. Here, the condition is handled with a proper return: `if (!check_sdata_in_driver(sdata)) return -EIO;`. So, the control flow handles the unexpected condition. Therefore, based on the prompt's instructions, this condition does *not* result in a denial of service.
Just to be absolutely sure, let's retrace the potential trigger paths. Can an unprivileged user even trigger `cfg80211_wext_siwrate`? `wext_permission_check` requires `CAP_NET_ADMIN`. So, no. And what about `nl80211_set_tx_bitrate_mask`? That needs `GENL_UNS_ADMIN_PERM`, which means `CAP_NET_ADMIN` in the network namespace. A user *could* create a user namespace and a network namespace, and thus *could* have `CAP_NET_ADMIN` in that network namespace. But, importantly, no Wi-Fi devices would be in that network namespace by default. Unless a system admin explicitly moved a Wi-Fi device into that namespace. I wonder, could the user create a virtual Wi-Fi device, like `mac80211_hwsim`, in their own network namespace? I need to check if an unprivileged user can even create `mac80211_hwsim`. It uses netlink to create radios: `HWSIM_CMD_NEW_RADIO`.
|
| 97/2 |
2026/08/04 02:12 |
tool |
grepper |
0m
Error:
Args: map[Expression:HWSIM_CMD_NEW_RADIO PathPrefix:drivers/net/wireless/mac80211_hwsim.c]
Results:
|
| 98/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 70483
output: 35
thoughts: 39
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of a wireless driver:
**Locating the HWSIM Command**
Alright, I need to figure out where `HWSIM_CMD_NEW_RADIO` is defined and used. My first instinct, given the context, is that it's likely part of the hardware simulation or emulation layer within the Linux kernel, specifically related to the mac80211 wireless stack. So, I'll start by searching within the `drivers/net/wireless/` directory, and the filename that jumps out is `mac80211_hwsim.c` or something analogous. That seems like the logical place to find this command, and any related structures or functions. Let's see what we turn up.
|
| 99/2 |
2026/08/04 02:12 |
tool |
grepper |
0m
Args: map[Expression:HWSIM_CMD_NEW_RADIO PathPrefix:drivers/net/wireless/]
Results: map[Output:drivers/net/wireless/virtual/mac80211_hwsim.h=23=enum hwsim_tx_control_flags {
--
drivers/net/wireless/virtual/mac80211_hwsim.h-66- * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
drivers/net/wireless/virtual/mac80211_hwsim.h:67: * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
drivers/net/wireless/virtual/mac80211_hwsim.h-68- * returns the radio ID (>= 0) or negative on errors, if successful
--
drivers/net/wireless/virtual/mac80211_hwsim.h=91=enum hwsim_commands {
--
drivers/net/wireless/virtual/mac80211_hwsim.h-95- HWSIM_CMD_TX_INFO_FRAME,
drivers/net/wireless/virtual/mac80211_hwsim.h:96: HWSIM_CMD_NEW_RADIO,
drivers/net/wireless/virtual/mac80211_hwsim.h-97- HWSIM_CMD_DEL_RADIO,
--
drivers/net/wireless/virtual/mac80211_hwsim.h-107-
drivers/net/wireless/virtual/mac80211_hwsim.h:108:#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO
drivers/net/wireless/virtual/mac80211_hwsim.h-109-#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO
--
drivers/net/wireless/virtual/mac80211_hwsim.h-128- * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
drivers/net/wireless/virtual/mac80211_hwsim.h:129: * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_NEW_RADIO
drivers/net/wireless/virtual/mac80211_hwsim.h-130- * command giving the number of channels supported by the new radio
--
drivers/net/wireless/virtual/mac80211_hwsim.h-137- * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag)
drivers/net/wireless/virtual/mac80211_hwsim.h:138: * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_NEW_RADIO
drivers/net/wireless/virtual/mac80211_hwsim.h-139- * command to force use of channel contexts even when only a
drivers/net/wireless/virtual/mac80211_hwsim.h-140- * single channel is supported
drivers/net/wireless/virtual/mac80211_hwsim.h:141: * @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_NEW_RADIO
drivers/net/wireless/virtual/mac80211_hwsim.h-142- * command to force radio removal when process that created the radio dies
--
drivers/net/wireless/virtual/mac80211_hwsim.h-153- * the new radio
drivers/net/wireless/virtual/mac80211_hwsim.h:154: * @HWSIM_ATTR_PMSR_SUPPORT: nested attribute used with %HWSIM_CMD_NEW_RADIO
drivers/net/wireless/virtual/mac80211_hwsim.h-155- * to provide peer measurement capabilities. (nl80211_peer_measurement_attrs)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=4467=static void hwsim_mcast_new_radio(int id, struct genl_info *info,
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-4477- data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
drivers/net/wireless/virtual/mac80211_hwsim_main.c:4478: HWSIM_CMD_NEW_RADIO);
drivers/net/wireless/virtual/mac80211_hwsim_main.c-4479- if (!data)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=7052=static const struct genl_small_ops hwsim_ops[] = {
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7069- {
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7070: .cmd = HWSIM_CMD_NEW_RADIO,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7071- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
]
|
| 100/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 72062
output: 50
thoughts: 22
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `HWSIM_CMD_NEW_RADIO` and `hwsim_ops`**
Okay, so the task at hand involves examining `hwsim_ops`, specifically in relation to `HWSIM_CMD_NEW_RADIO`. My initial instinct is to go directly to the relevant code and analyze the implementation. It's crucial to understand how a new radio is brought online within the hardware simulation environment. I need to figure out what functions are called, the data structures involved, and the overall control flow. What's the interface? What kind of parameters are passed? Are there any preconditions that need to be met before this command is executed successfully? I need to look for potential areas of concern, bottlenecks, or any obvious inefficiencies. Furthermore, how does this command interact with other parts of the simulation infrastructure? Are there any dependencies I should be aware of, perhaps with device initialization or resource allocation? Time to start digging through the source...
|
| 101/2 |
2026/08/04 02:12 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:7060 LineCount:20]
Results: map[Contents:7060: .cmd = HWSIM_CMD_FRAME,
7061: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7062: .doit = hwsim_cloned_frame_received_nl,
7063: },
7064: {
7065: .cmd = HWSIM_CMD_TX_INFO_FRAME,
7066: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7067: .doit = hwsim_tx_info_frame_received_nl,
7068: },
7069: {
7070: .cmd = HWSIM_CMD_NEW_RADIO,
7071: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7072: .doit = hwsim_new_radio_nl,
7073: .flags = GENL_UNS_ADMIN_PERM,
7074: },
7075: {
7076: .cmd = HWSIM_CMD_DEL_RADIO,
7077: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7078: .doit = hwsim_del_radio_nl,
7079: .flags = GENL_UNS_ADMIN_PERM,
]
|
| 102/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 72744
output: 50
thoughts: 221
Thoughts: **Unpacking the `HWSIM_CMD_NEW_RADIO` Security Implications**
Okay, so I'm digging into this `HWSIM_CMD_NEW_RADIO` thing, and it looks like the `.flags` field is set to `GENL_UNS_ADMIN_PERM`. Right, that's interesting. So an *unprivileged* user, with a little namespace magic – creating their own user and network namespaces – could potentially spin up a `mac80211_hwsim` radio. That immediately raises a flag.
But hold on, `mac80211_hwsim` is a kernel module, isn't it? So, the whole thing hinges on whether the module is actually loaded. If it *is* loaded, then yes, my initial thought is correct, a non-privileged user can do this.
Now, the question is: is `mac80211_hwsim` loaded by default on a typical system? I'm pretty sure it's not. But then I remember Syzkaller's fuzzing strategy... it loads a bunch of modules. If Syzkaller's in play, then the module *is* loaded, and the potential for a privilege escalation via `mac80211_hwsim` is very real.
Wait, back up! `GENL_UNS_ADMIN_PERM`... that implies they need `CAP_NET_ADMIN` *within the specific namespace* where the Netlink socket is created, right? So we're not talking about full system privileges, but rather permissions within that isolated namespace.
But the key point is the namespace. This also makes me wonder... where does `mac80211_hwsim` actually create these radios? In the initial network namespace, or the namespace where the Netlink socket is? Let's trace this back to `hwsim_new_radio_nl` and dig deeper. This nuance will be critical for understanding the attack surface here.
|
| 103/2 |
2026/08/04 02:12 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:4485 LineCount:50]
Results: map[Contents:4485: genlmsg_end(mcast_skb, data);
4486:
4487: hwsim_mcast_config_msg(mcast_skb, info);
4488: return;
4489:
4490: out_err:
4491: nlmsg_free(mcast_skb);
4492: }
4493:
4494: static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
4495: {
4496: .types_mask = BIT(NL80211_IFTYPE_STATION) |
4497: BIT(NL80211_IFTYPE_P2P_CLIENT),
4498: .he_cap = {
4499: .has_he = true,
4500: .he_cap_elem = {
4501: .mac_cap_info[0] =
4502: IEEE80211_HE_MAC_CAP0_HTC_HE,
4503: .mac_cap_info[1] =
4504: IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
4505: IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
4506: .mac_cap_info[2] =
4507: IEEE80211_HE_MAC_CAP2_BSR |
4508: IEEE80211_HE_MAC_CAP2_MU_CASCADING |
4509: IEEE80211_HE_MAC_CAP2_ACK_EN,
4510: .mac_cap_info[3] =
4511: IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
4512: IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
4513: .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
4514: .phy_cap_info[0] =
4515: IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G,
4516: .phy_cap_info[1] =
4517: IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
4518: IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
4519: IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
4520: IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
4521: .phy_cap_info[2] =
4522: IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
4523: IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
4524: IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
4525: IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
4526: IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
4527:
4528: /* Leave all the other PHY capability bytes
4529: * unset, as DCM, beam forming, RU and PPE
4530: * threshold information are not supported
4531: */
4532: },
4533: .he_mcs_nss_supp = {
4534: .rx_mcs_80 = cpu_to_le16(0xfffa),
]
|
| 104/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 74543
output: 49
thoughts: 14
Thoughts: Here's my thought process, summarized as you requested:
**Navigating the Codebase: The Hunt for `hwsim_new_radio_nl`**
Alright, let's get down to brass tacks. I need to locate the definition or usage of `hwsim_new_radio_nl`. That's the core of what I need to address. This suggests a piece of code that interacts with the netlink subsystem, specifically dealing with a "new radio" concept within the hardware simulation framework, `hwsim`. Likely, this is where a driver or an emulation component registers or configures something related to a simulated radio device via Netlink. Now, the next question is where do I begin the search? Given my familiarity with the structure, I'll start by checking the relevant directories: the source related to the kernel, netlink protocol, or the relevant driver within `hwsim`. I'll likely use `grep` or `find` with suitable wildcard patterns to pinpoint the exact location. I need to be as efficient as possible. The goal is to quickly understand how this function is used and what it interacts with.
|
| 105/2 |
2026/08/04 02:12 |
tool |
grepper |
0m
Args: map[Expression:hwsim_new_radio_nl PathPrefix:drivers/net/wireless/virtual/mac80211_hwsim_main.c]
Results: map[Output:drivers/net/wireless/virtual/mac80211_hwsim_main.c=6713=static int parse_pmsr_capa(const struct nlattr *pmsr_capa, struct cfg80211_pmsr_capabilities *out,
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6751-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:6752:static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6753-{
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=7052=static const struct genl_small_ops hwsim_ops[] = {
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7071- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7072: .doit = hwsim_new_radio_nl,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7073- .flags = GENL_UNS_ADMIN_PERM,
]
|
| 106/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 75220
output: 50
thoughts: 14
Thoughts: Okay, let's dive into `hwsim_new_radio_nl`.
**Diving into `hwsim_new_radio_nl`**
Alright, so I'm about to look at `hwsim_new_radio_nl`. Immediately, my expert brain is already running through potential implications. Is this a new Netlink interface for hardware simulation? Are we talking about emulating radio interfaces? Or perhaps it's related to a new radio stack implementation within the hardware simulator? Given the naming convention, it suggests a Netlink focus. That instantly triggers a cascade of questions: what specific Netlink message types are being used? Are there custom attributes defined? Is it using existing Netlink families, or is this a completely new implementation? I'm hoping it's well-documented and follows best practices, especially considering the potential for hardware interaction. My experience tells me that these types of interfaces are prone to subtle bugs and performance bottlenecks, so I’ll be paying close attention to error handling and efficiency. I’m eager to get a handle on the architecture here – how does the Netlink interface fit into the overall simulation framework? Let's get to it.
|
| 107/2 |
2026/08/04 02:12 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:6752 LineCount:50]
Results: map[Contents:6752: static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
6753: {
6754: struct hwsim_new_radio_params param = { 0 };
6755: const char *hwname = NULL;
6756: int ret;
6757:
6758: param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
6759: param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
6760: param.nan_device = info->attrs[HWSIM_ATTR_SUPPORT_NAN_DEVICE];
6761: param.channels = channels;
6762: param.destroy_on_close =
6763: info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE];
6764:
6765: if (info->attrs[HWSIM_ATTR_CHANNELS])
6766: param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
6767:
6768: if (param.channels < 1) {
6769: GENL_SET_ERR_MSG(info, "must have at least one channel");
6770: return -EINVAL;
6771: }
6772:
6773: if (info->attrs[HWSIM_ATTR_NO_VIF])
6774: param.no_vif = true;
6775:
6776: if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
6777: param.use_chanctx = true;
6778: else
6779: param.use_chanctx = (param.channels > 1);
6780:
6781: if (info->attrs[HWSIM_ATTR_MULTI_RADIO])
6782: param.multi_radio = true;
6783:
6784: if (info->attrs[HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR])
6785: param.background_radar = true;
6786:
6787: if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
6788: param.reg_alpha2 =
6789: nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
6790:
6791: if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
6792: u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
6793:
6794: if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
6795: return -EINVAL;
6796:
6797: idx = array_index_nospec(idx,
6798: ARRAY_SIZE(hwsim_world_regdom_custom));
6799: param.regd = hwsim_world_regdom_custom[idx];
6800: }
6801:
]
|
| 108/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 76409
output: 50
thoughts: 26
Thoughts: Okay, here's my interpretation of that:
**My Perspective on Radio Initialization in the Hardware Simulator**
Alright, so the `hwsim_new_radio_nl` function, which I recognize as part of that network namespace interface for the hardware simulator, is clearly the entry point for creating a new simulated radio. It's essentially calling the `mac80211_hwsim_new_radio` function. That's a pretty standard pattern – a network namespace specific handler that then calls the core radio creation function within the mac80211 hardware simulator framework. I would expect `mac80211_hwsim_new_radio` to be the heart of it, responsible for all the underlying setup: allocating the radio structure, initializing its hardware emulation, registering the device with mac80211, and probably setting up any internal data structures needed for the simulation. My first focus would be to check out the arguments passed and see if there are additional parameters being passed that influence radio creation; it is important to understand the capabilities being emulated. From there, I'd want to trace through the `mac80211_hwsim_new_radio` function itself to see exactly how the radio is being set up. This will show me where the hardware emulation is actually happening, what default configurations are being used, and any points of customization or extension. The namespace specific handler is only setting up the basic environment for the radio, while the latter command is where the actual allocation and configuration will occur.
|
| 109/2 |
2026/08/04 02:12 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:6800 LineCount:50]
Results: map[Contents:6800: }
6801:
6802: if (info->attrs[HWSIM_ATTR_PERM_ADDR]) {
6803: if (!is_valid_ether_addr(
6804: nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]))) {
6805: GENL_SET_ERR_MSG(info,"MAC is no valid source addr");
6806: NL_SET_BAD_ATTR(info->extack,
6807: info->attrs[HWSIM_ATTR_PERM_ADDR]);
6808: return -EINVAL;
6809: }
6810:
6811: param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]);
6812: }
6813:
6814: if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) {
6815: param.iftypes =
6816: nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]);
6817:
6818: if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) {
6819: NL_SET_ERR_MSG_ATTR(info->extack,
6820: info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT],
6821: "cannot support more iftypes than kernel");
6822: return -EINVAL;
6823: }
6824: } else {
6825: param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
6826: }
6827:
6828: /* ensure both flag and iftype support is honored */
6829: if (param.p2p_device ||
6830: param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
6831: param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
6832: param.p2p_device = true;
6833: }
6834:
6835: if (param.nan_device) {
6836: if (param.multi_radio) {
6837: NL_SET_ERR_MSG(info->extack,
6838: "NAN is not supported on multi-radio wiphys");
6839: return -EINVAL;
6840: }
6841: param.iftypes |= BIT(NL80211_IFTYPE_NAN) |
6842: BIT(NL80211_IFTYPE_NAN_DATA);
6843: }
6844:
6845: if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) {
6846: u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
6847:
6848: param.ciphers =
6849: nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
]
|
| 110/2 |
2026/08/04 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 77668
output: 50
thoughts: 23
Thoughts: Here's my thought process, summarized as you requested:
**Debugging mac80211_hwsim_new_radio**
Okay, time to dive in. My immediate thought is, "Is `mac80211_hwsim_new_radio` actually being called?" That's the crucial first step. Before I start chasing down what's *happening* within that function, I need to confirm that it's even being executed in the first place. I need to establish the baseline: is this even the code path my system is taking? If it isn't, I'm wasting my time. Time to fire up the debugger and get some evidence.
|
| 111/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:6850 LineCount:50]
Results: map[Contents:6850:
6851: if (len % sizeof(u32)) {
6852: NL_SET_ERR_MSG_ATTR(info->extack,
6853: info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
6854: "bad cipher list length");
6855: return -EINVAL;
6856: }
6857:
6858: param.n_ciphers = len / sizeof(u32);
6859:
6860: if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) {
6861: NL_SET_ERR_MSG_ATTR(info->extack,
6862: info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
6863: "too many ciphers specified");
6864: return -EINVAL;
6865: }
6866:
6867: if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) {
6868: NL_SET_ERR_MSG_ATTR(info->extack,
6869: info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
6870: "unsupported ciphers specified");
6871: return -EINVAL;
6872: }
6873: }
6874:
6875: param.mlo = info->attrs[HWSIM_ATTR_MLO_SUPPORT];
6876:
6877: if (param.mlo || param.multi_radio)
6878: param.use_chanctx = true;
6879:
6880: if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
6881: hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]),
6882: nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
6883: GFP_KERNEL);
6884: if (!hwname)
6885: return -ENOMEM;
6886: param.hwname = hwname;
6887: }
6888:
6889: if (info->attrs[HWSIM_ATTR_PMSR_SUPPORT]) {
6890: struct cfg80211_pmsr_capabilities *pmsr_capa;
6891:
6892: pmsr_capa = kzalloc_obj(*pmsr_capa);
6893: if (!pmsr_capa) {
6894: ret = -ENOMEM;
6895: goto out_free;
6896: }
6897: param.pmsr_capa = pmsr_capa;
6898:
6899: ret = parse_pmsr_capa(info->attrs[HWSIM_ATTR_PMSR_SUPPORT], pmsr_capa, info);
]
|
| 112/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 78727
output: 50
thoughts: 10
Thoughts: Okay, I'm ready to dive into this. Let's see how this goes.
**Analyzing the Function's Context**
Alright, I need to understand this function fully. I've got the initial snippet, but that's not enough to draw any firm conclusions. Before I can properly assess its performance, efficiency, or potential issues, I absolutely have to see the *rest* of the function. What are the inputs, the other internal calculations, and crucially, what's the ultimate output? Understanding the complete picture is paramount here. This fragment alone gives me a very limited perspective, and I'm itching to get the whole context. I need to see the entire implementation to start forming meaningful insights.
|
| 113/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:6900 LineCount:50]
Results: map[Contents:6900: if (ret)
6901: goto out_free;
6902: }
6903:
6904: ret = mac80211_hwsim_new_radio(info, ¶m);
6905:
6906: out_free:
6907: kfree(hwname);
6908: kfree(param.pmsr_capa);
6909: return ret;
6910: }
6911:
6912: static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
6913: {
6914: struct mac80211_hwsim_data *data;
6915: s64 idx = -1;
6916: const char *hwname = NULL;
6917:
6918: if (info->attrs[HWSIM_ATTR_RADIO_ID]) {
6919: idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
6920: } else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
6921: hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]),
6922: nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
6923: GFP_KERNEL);
6924: if (!hwname)
6925: return -ENOMEM;
6926: } else
6927: return -EINVAL;
6928:
6929: spin_lock_bh(&hwsim_radio_lock);
6930: list_for_each_entry(data, &hwsim_radios, list) {
6931: if (idx >= 0) {
6932: if (data->idx != idx)
6933: continue;
6934: } else {
6935: if (!hwname ||
6936: strcmp(hwname, wiphy_name(data->hw->wiphy)))
6937: continue;
6938: }
6939:
6940: if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
6941: continue;
6942:
6943: list_del(&data->list);
6944: rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
6945: hwsim_rht_params);
6946: hwsim_radios_generation++;
6947: spin_unlock_bh(&hwsim_radio_lock);
6948: mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
6949: info);
]
|
| 114/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 79747
output: 50
thoughts: 51
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Investigating Radio Creation in the Network Namespace**
Alright, so `hwsim_new_radio_nl` is responsible for creating a new radio instance, right? My immediate question is, where exactly does this new radio reside in the network namespace hierarchy? Is it automatically bound to the same namespace as the netlink socket that's triggering this operation? The intuitive answer, and the one I need to verify, is yes, but I need to confirm.
The logical next step is to dive into `mac80211_hwsim_new_radio`. That function likely handles the core initialization and configuration of the virtual radio interface. I'm expecting to find the answer buried somewhere in the parameter passing, calls to namespace setup functions (if any), or maybe even hints in its relationship with the netlink socket. I'll be looking specifically for any actions taken that tie the radio's "birth" to a specific namespace or inherit it from the caller. Time to see what's under the hood.
|
| 115/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:3800 LineCount:50]
Results: map[Contents:3800: goto out_free;
3801: }
3802:
3803: pmsr = nla_nest_start(skb, HWSIM_ATTR_PMSR_REQUEST);
3804: if (!pmsr) {
3805: err = -ENOMEM;
3806: goto out_free;
3807: }
3808:
3809: err = mac80211_hwsim_send_pmsr_request(skb, request);
3810: if (err)
3811: goto out_free;
3812:
3813: nla_nest_end(skb, pmsr);
3814:
3815: genlmsg_end(skb, msg_head);
3816: if (hwsim_virtio_enabled)
3817: hwsim_tx_virtio(data, skb);
3818: else
3819: hwsim_unicast_netgroup(data, skb, _portid);
3820:
3821: data->pmsr_request = request;
3822: data->pmsr_request_wdev = ieee80211_vif_to_wdev(vif);
3823:
3824: out_free:
3825: if (err && skb)
3826: nlmsg_free(skb);
3827:
3828: mutex_unlock(&data->mutex);
3829: return err;
3830: }
3831:
3832: static void mac80211_hwsim_abort_pmsr(struct ieee80211_hw *hw,
3833: struct ieee80211_vif *vif,
3834: struct cfg80211_pmsr_request *request)
3835: {
3836: struct mac80211_hwsim_data *data;
3837: struct sk_buff *skb = NULL;
3838: struct nlattr *pmsr;
3839: void *msg_head;
3840: u32 _portid;
3841: int err = 0;
3842:
3843: data = hw->priv;
3844: _portid = READ_ONCE(data->wmediumd);
3845: if (!_portid && !hwsim_virtio_enabled)
3846: return;
3847:
3848: mutex_lock(&data->mutex);
3849:
]
|
| 116/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 80828
output: 54
thoughts: 21
Thoughts: Here's my thought process, summarized as you requested:
**Navigating the Codebase**
Okay, hold on a second. I need to get my bearings here. My primary objective at the moment is to locate the function `mac80211_hwsim_new_radio`. This is a critical function in this context, and I need to understand its role and how it's being used. It's the lynchpin I'm chasing. Gotta find it and understand its context before I can move forward.
|
| 117/2 |
2026/08/04 02:13 |
tool |
grepper |
0m
Args: map[Expression:mac80211_hwsim_new_radio PathPrefix:drivers/net/wireless/virtual/mac80211_hwsim_main.c]
Results: map[Output:drivers/net/wireless/virtual/mac80211_hwsim_main.c=5638=static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = {
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-5649-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:5650:static int mac80211_hwsim_new_radio(struct genl_info *info,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-5651- struct hwsim_new_radio_params *param)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=6752=static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6903-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:6904: ret = mac80211_hwsim_new_radio(info, ¶m);
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6905-
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=7457=static int __init init_mac80211_hwsim(void)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7570-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7571: err = mac80211_hwsim_new_radio(NULL, ¶m);
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7572- if (err < 0)
]
|
| 118/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 81540
output: 50
thoughts: 19
Thoughts: Here's a first-person summary, tailored for an expert audience, of the thought process suggested by "Let's check `mac80211_hwsim_new_radio`":
**Diving into `mac80211_hwsim_new_radio`**
Alright, time to dig into `mac80211_hwsim_new_radio`. My first thought is: why are we focusing on this specific function? Is there a particular issue or investigation underway related to hardware simulation within the mac80211 framework? Is there a recent patch or report that triggered this? I want to understand the context.
My expertise tells me this function is likely responsible for initializing a new "radio" – a simulated wireless device, essentially – within the `hwsim` module. It's the building block, the foundation for emulating wireless interfaces within the kernel. I immediately start mentally mapping the expected call stack. Where does this get invoked? Probably during module loading, device creation, or perhaps even dynamically when a new simulated hardware instance is requested.
I'm thinking about the data structures involved: What arguments does it accept? What data is being passed in? This will provide the key to understanding the parameters that control the simulated hardware behavior. What specific features and capabilities are being configured? What does it return? A pointer to a new radio instance, presumably. What happens if creation fails? Error handling is critical here.
I anticipate the core functionality will involve allocating memory for the radio structure, potentially initializing related subsystems and data structures, and perhaps linking this new radio instance into a global or module-level list of simulated radios. I'm keen to examine how the hardware aspects are abstracted. Are we dealing with standard mac80211 structures and callbacks, or are there `hwsim`-specific extensions here? What hooks are used to implement RX/TX path handling, and which registers get mapped. Also, I would also like to confirm the supported firmware features.
I want to see how the virtual hardware interacts with the mac80211 stack. Is there direct memory access or the use of callbacks? What resources are being simulated: frequency plans, antenna diversity, and channel selection? Understanding how `hwsim` maps these configurations to the real world is vital, especially when it comes to testing mac80211's more advanced features. This would tell me how deep the simulation goes.
In short, I'm expecting a critical point of entry for understanding and potentially debugging the hardware simulation aspects of mac80211, and I'm eager to get a clearer picture of its inner workings.
|
| 119/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:5650 LineCount:50]
Results: map[Contents:5650: static int mac80211_hwsim_new_radio(struct genl_info *info,
5651: struct hwsim_new_radio_params *param)
5652: {
5653: int err;
5654: u8 addr[ETH_ALEN];
5655: struct mac80211_hwsim_data *data;
5656: struct ieee80211_hw *hw;
5657: enum nl80211_band band;
5658: const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
5659: struct net *net;
5660: int idx, i;
5661: int n_limits = 0;
5662: int n_bands = 0;
5663:
5664: if (WARN_ON(param->channels > 1 && !param->use_chanctx))
5665: return -EINVAL;
5666:
5667: spin_lock_bh(&hwsim_radio_lock);
5668: idx = hwsim_radio_idx++;
5669: spin_unlock_bh(&hwsim_radio_lock);
5670:
5671: if (param->mlo)
5672: ops = &mac80211_hwsim_mlo_ops;
5673: else if (param->use_chanctx)
5674: ops = &mac80211_hwsim_mchan_ops;
5675: hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname);
5676: if (!hw) {
5677: pr_debug("mac80211_hwsim: ieee80211_alloc_hw failed\n");
5678: err = -ENOMEM;
5679: goto failed;
5680: }
5681:
5682: /* ieee80211_alloc_hw_nm may have used a default name */
5683: param->hwname = wiphy_name(hw->wiphy);
5684:
5685: if (info)
5686: net = genl_info_net(info);
5687: else
5688: net = &init_net;
5689: wiphy_net_set(hw->wiphy, net);
5690:
5691: data = hw->priv;
5692: data->hw = hw;
5693:
5694: data->dev = device_create(&hwsim_class, NULL, 0, hw, "hwsim%d", idx);
5695: if (IS_ERR(data->dev)) {
5696: printk(KERN_DEBUG
5697: "mac80211_hwsim: device_create failed (%ld)\n",
5698: PTR_ERR(data->dev));
5699: err = -ENOMEM;
]
|
| 120/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 83018
output: 41
thoughts: 708
Thoughts: **Vulnerability Discovery: Exploiting Network Namespaces and mac80211_hwsim**
Okay, so here's what I've got. The critical piece is `wiphy_net_set(hw->wiphy, net);` where `net` is the network namespace of the netlink socket. This means a non-privileged user can create a `mac80211_hwsim` radio *within their own network namespace*. And, crucially, they'd have `CAP_NET_ADMIN` *within that namespac*.
This is huge! With `CAP_NET_ADMIN`, they could then create an `AP_VLAN` interface on that simulated radio. Now, initially I thought `SIOCSIWRATE` might be the trigger, but that requires `CAP_NET_ADMIN` in the *initial* namespace, so that's a dead end. However, `NL80211_CMD_SET_TX_BITRATE_MASK` uses `GENL_UNS_ADMIN_PERM`, which only needs `CAP_NET_ADMIN` within the *network namespace*.
This is where it gets interesting... The chain is now clear:
1. A user can create both a user and network namespace. No privileges needed, just unprivileged execution
2. Inside their user namespace, they have `CAP_NET_ADMIN`.
3. They send `HWSIM_CMD_NEW_RADIO` via netlink to craft a `mac80211_hwsim` radio. This requires `GENL_UNS_ADMIN_PERM`, which they have because of the `CAP_NET_ADMIN` in their user namespace. The radio gets created in *their* network namespace. Perfect.
4. Then, they create an `AP_VLAN` interface on the radio using `NL80211_CMD_NEW_INTERFACE`. Again, `GENL_UNS_ADMIN_PERM` is all they need, which they have.
5. Bring that interface UP. This sets `SDATA_STATE_RUNNING` on the interface, but crucially, it *doesn't* set `IEEE80211_SDATA_IN_DRIVER` because it's an `AP_VLAN`.
6. Finally, they send `NL80211_CMD_SET_TX_BITRATE_MASK` on the `AP_VLAN` interface. This also only needs `GENL_UNS_ADMIN_PERM`, which they have.
7. This cascades down to `nl80211_set_tx_bitrate_mask` -> `rdev_set_bitrate_mask` -> `ieee80211_set_bitrate_mask` -> checks `ieee80211_sdata_running(sdata)` (which is TRUE) -> then calls `drv_set_bitrate_mask` -> then `check_sdata_in_driver(sdata)`.
8. The key is that the code checks for `sdata->flags & IEEE80211_SDATA_IN_DRIVER`, which is *false* due to `AP_VLAN`. And boom, that triggers a `WARN_ONCE`.
Now, I'm just quickly double-checking the code for `NL80211_CMD_NEW_INTERFACE` to confirm we can actually create an `AP_VLAN` this way. That needs verification, but I'm pretty confident this is a viable path to exploit this bug from a user namespace.
|
| 121/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:net/wireless/nl80211.c FirstLine:5119 LineCount:50]
Results: map[Contents:5119: static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
5120: {
5121: struct cfg80211_registered_device *rdev = info->user_ptr[0];
5122: struct vif_params params;
5123: struct wireless_dev *wdev;
5124: struct sk_buff *msg;
5125: u32 radio_mask;
5126: int err;
5127: enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
5128:
5129: memset(¶ms, 0, sizeof(params));
5130:
5131: if (!info->attrs[NL80211_ATTR_IFNAME])
5132: return -EINVAL;
5133:
5134: if (info->attrs[NL80211_ATTR_IFTYPE])
5135: type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
5136:
5137: if (!rdev->ops->add_virtual_intf)
5138: return -EOPNOTSUPP;
5139:
5140: if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
5141: type == NL80211_IFTYPE_PD ||
5142: rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
5143: info->attrs[NL80211_ATTR_MAC]) {
5144: nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
5145: ETH_ALEN);
5146: if (!is_valid_ether_addr(params.macaddr))
5147: return -EADDRNOTAVAIL;
5148: }
5149:
5150: if (info->attrs[NL80211_ATTR_4ADDR]) {
5151: params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
5152: err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
5153: if (err)
5154: return err;
5155: }
5156:
5157: if (!cfg80211_iftype_allowed(&rdev->wiphy, type, params.use_4addr, 0))
5158: return -EOPNOTSUPP;
5159:
5160: err = nl80211_parse_mon_options(rdev, type, info, ¶ms);
5161: if (err < 0)
5162: return err;
5163:
5164: err = nl80211_parse_vif_radio_mask(info, &radio_mask);
5165: if (err < 0)
5166: return err;
5167:
5168: msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
]
|
| 122/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 85487
output: 50
thoughts: 92
Thoughts: **Analyzing Interface Type Capabilities**
Okay, so I'm diving into how this system handles interface types. My immediate thought is to check if the proposed interface type is permitted. The code uses `cfg80211_iftype_allowed` to determine this, considering the Wi-Fi device's capabilities, the requested interface type (`type`), whether 4-address frames are in use (`params.use_4addr`), and some other flags. Now, the crucial question is: does `mac80211_hwsim`, which I know is a hardware simulation module, actually *allow* the `NL80211_IFTYPE_AP_VLAN` interface type? I need to verify its specific configurations. To do that, I need to look for a specific function or structure within `mac80211_hwsim` that defines these extended capabilities – likely something named along the lines of `mac80211_hwsim_iftypes_ext_capa` or something similar, where it explicitly lists supported interface types.
|
| 123/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:5630 LineCount:20]
Results: map[Contents:5630: };
5631:
5632: #define MAC80211_HWSIM_MLD_CAPA_OPS \
5633: FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
5634: IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
5635: FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS, \
5636: IEEE80211_MLD_MAX_NUM_LINKS - 1)
5637:
5638: static const struct wiphy_iftype_ext_capab mac80211_hwsim_iftypes_ext_capa[] = {
5639: {
5640: .iftype = NL80211_IFTYPE_AP,
5641: .extended_capabilities = iftypes_ext_capa_ap,
5642: .extended_capabilities_mask = iftypes_ext_capa_ap,
5643: .extended_capabilities_len = sizeof(iftypes_ext_capa_ap),
5644: .eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP |
5645: IEEE80211_EML_CAP_EMLMR_SUPPORT,
5646: .mld_capa_and_ops = MAC80211_HWSIM_MLD_CAPA_OPS,
5647: },
5648: };
5649:
]
|
| 124/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 86326
output: 51
thoughts: 114
Thoughts: **Analyzing Interface Type Support in the Hardware Simulator**
Okay, so I'm digging into how interface types are handled in this hardware simulator, specifically the `hwsim_new_radio_nl` function. It looks like the `param.iftypes` variable is getting populated right there, which is good to know. The crucial part is this conditional, `if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT])`. If the `HWSIM_ATTR_IFTYPE_SUPPORT` attribute is present in the provided information (`info`), then we use `nla_get_u32` to grab the value and assign it to `param.iftypes`. That makes sense. However, if that attribute isn't provided, what happens? Ah, it defaults! That's the key. So, I need to check how `param.iftypes` gets initialized when that attribute is *not* present. I'm guessing it defaults to some predefined mask. Let's see what `HWSIM_IFTYPE_SUPPORT_MASK` actually represents. That's the next step; understanding the implications of that mask is central to understanding the supported interface types.
|
| 125/2 |
2026/08/04 02:13 |
tool |
grepper |
0m
Args: map[Expression:HWSIM_IFTYPE_SUPPORT_MASK PathPrefix:drivers/net/wireless/virtual/mac80211_hwsim_main.c]
Results: map[Output:drivers/net/wireless/virtual/mac80211_hwsim_main.c=5584=static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-5613-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:5614:#define HWSIM_IFTYPE_SUPPORT_MASK \
drivers/net/wireless/virtual/mac80211_hwsim_main.c-5615- (BIT(NL80211_IFTYPE_STATION) | \
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=6752=static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6817-
drivers/net/wireless/virtual/mac80211_hwsim_main.c:6818: if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) {
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6819- NL_SET_ERR_MSG_ATTR(info->extack,
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6824- } else {
drivers/net/wireless/virtual/mac80211_hwsim_main.c:6825: param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
drivers/net/wireless/virtual/mac80211_hwsim_main.c-6826- }
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c=7457=static int __init init_mac80211_hwsim(void)
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7566- param.use_chanctx = channels > 1 || mlo || multi_radio;
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7567: param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7568- if (param.p2p_device)
]
|
| 126/2 |
2026/08/04 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 87401
output: 50
thoughts: 19
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `HWSIM_IFTYPE_SUPPORT_MASK`**
Okay, let's dissect this. I need to understand what specific interface types are supported by this `HWSIM_IFTYPE_SUPPORT_MASK`. This usually means I'm looking at a bitmask, and each bit likely corresponds to a particular interface type. I'll need to know which bit indicates which interface, and what that implies in terms of hardware support or simulation configuration. I need to determine how various features and functionalities are allocated in the underlying network hardware for the simulation. Knowing the components assigned to each function is the goal. I should probably start by reviewing any associated header files or documentation. Is it a well-defined enumeration, or a more obscure implementation that may require deeper understanding. My intuition is that this is the crux of the configuration.
|
| 127/2 |
2026/08/04 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/net/wireless/virtual/mac80211_hwsim_main.c FirstLine:5610 LineCount:20]
Results: map[Contents:5610: BIT(NL80211_IFTYPE_AP) | \
5611: BIT(NL80211_IFTYPE_P2P_GO) | \
5612: HWSIM_MESH_BIT)
5613:
5614: #define HWSIM_IFTYPE_SUPPORT_MASK \
5615: (BIT(NL80211_IFTYPE_STATION) | \
5616: BIT(NL80211_IFTYPE_AP) | \
5617: BIT(NL80211_IFTYPE_P2P_CLIENT) | \
5618: BIT(NL80211_IFTYPE_P2P_GO) | \
5619: BIT(NL80211_IFTYPE_ADHOC) | \
5620: BIT(NL80211_IFTYPE_MESH_POINT) | \
5621: BIT(NL80211_IFTYPE_OCB))
5622:
5623: static const u8 iftypes_ext_capa_ap[] = {
5624: [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
5625: [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
5626: [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
5627: WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
5628: [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
5629: [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT,
]
|
| 128/2 |
2026/08/04 02:13 |
llm |
expert |
1m
Model:
Tokens:
input: 88140
output: 36
thoughts: 101
Thoughts: **Investigating AP VLAN Interface Type Support**
Okay, hold on a second. I'm seeing something that doesn't quite add up. The `NL80211_IFTYPE_AP_VLAN` interface type *isn't* present in `HWSIM_IFTYPE_SUPPORT_MASK`. That's a red flag. I need to understand if this is handled correctly. My gut feeling tells me that if the underlying `NL80211_IFTYPE_AP` type is supported, then maybe, just maybe, `cfg80211_iftype_allowed` is letting `NL80211_IFTYPE_AP_VLAN` through anyway. This deserves a closer look. Time to dive into the code and verify what `cfg80211_iftype_allowed` is actually doing in this scenario. I need to confirm whether this is as expected or not.
|
| 129/2 |
2026/08/04 02:14 |
tool |
read-file |
0m
Args: map[File:net/wireless/util.c FirstLine:1410 LineCount:30]
Results: map[Contents:1410: [1] = 3850,
1411: [2] = 7700,
1412: [3] = 9625,
1413: [4] = 11550,
1414: [5] = 12512, /* 1251.25 mbps */
1415: [6] = 13475,
1416: [7] = 15400,
1417: [8] = 19250,
1418: [9] = 23100,
1419: [10] = 25025,
1420: [11] = 26950,
1421: [12] = 30800,
1422: [13] = 38500,
1423: [14] = 46200,
1424: [15] = 50050,
1425: [16] = 53900,
1426: [17] = 57750,
1427: [18] = 69300,
1428: [19] = 75075,
1429: [20] = 80850,
1430: };
1431:
1432: if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
1433: return 0;
1434:
1435: return __mcs2bitrate[rate->mcs] * rate->n_bonded_ch;
1436: }
1437:
1438: static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
1439: {
]
|
| 130/2 |
2026/08/04 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 89014
output: 35
thoughts: 19
Thoughts: Here's my thought process, summarized as you requested:
**Navigating the Wireless Landscape**
Okay, hold on a second. I need to zero in on something specific, a function that's critical to understanding the configuration of wireless interfaces. What was it again? Ah, yes, it's `cfg80211_iftype_allowed`. I need to track down exactly where that function lives, what it does, and how it interacts with the broader framework. My immediate task is to locate that function. That's the key to unlocking this section of the code. Let's see if I can find it quickly using my usual methods.
|
| 131/2 |
2026/08/04 02:14 |
tool |
grepper |
0m
Args: map[Expression:cfg80211_iftype_allowed PathPrefix:net/wireless/]
Results: map[Output:net/wireless/core.c=1704=static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
--
net/wireless/core.c-1823- case NETDEV_PRE_UP:
net/wireless/core.c:1824: if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
net/wireless/core.c-1825- wdev->use_4addr, 0))
--
net/wireless/nl80211.c=2949=static int nl80211_put_nan_phy_cap(struct wiphy *wiphy, struct sk_buff *msg)
--
net/wireless/nl80211.c-2955-
net/wireless/nl80211.c:2956: if (!cfg80211_iftype_allowed(wiphy, NL80211_IFTYPE_NAN_DATA, false, 0))
net/wireless/nl80211.c-2957- return 0;
--
net/wireless/nl80211.c=5119=static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
--
net/wireless/nl80211.c-5156-
net/wireless/nl80211.c:5157: if (!cfg80211_iftype_allowed(&rdev->wiphy, type, params.use_4addr, 0))
net/wireless/nl80211.c-5158- return -EOPNOTSUPP;
--
net/wireless/util.c=2475=int cfg80211_iter_combinations(struct wiphy *wiphy,
--
net/wireless/util.c-2518- if (params->iftype_num[iftype] > 0 &&
net/wireless/util.c:2519: !cfg80211_iftype_allowed(wiphy, iftype, 0, 1))
net/wireless/util.c-2520- used_iftypes |= BIT(iftype);
--
net/wireless/util.c-2545- for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
net/wireless/util.c:2546: if (cfg80211_iftype_allowed(wiphy, iftype, 0, 1))
net/wireless/util.c-2547- continue;
--
net/wireless/util.c=2937=EXPORT_SYMBOL(ieee80211_get_vht_max_nss);
net/wireless/util.c-2938-
net/wireless/util.c:2939:bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype,
net/wireless/util.c-2940- bool is_4addr, u8 check_swif)
--
net/wireless/util.c-2959-}
net/wireless/util.c:2960:EXPORT_SYMBOL(cfg80211_iftype_allowed);
net/wireless/util.c-2961-
]
|
| 132/2 |
2026/08/04 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 90077
output: 36
thoughts: 17
Thoughts: Okay, let's dive into this. Here's my take on `cfg80211_iftype_allowed`, as if it's currently bouncing around in my own head:
**Analyzing `cfg80211_iftype_allowed`**
Alright, time to get into `cfg80211_iftype_allowed`. I need to figure out what it's really doing, and why it's important within the larger context of cfg80211 and the wireless stack. My initial hunch is that this is a critical gatekeeper – a function that decides whether a particular interface type (like station, AP, monitor, etc.) is even *permitted* on a given wireless device. I'd wager it's probably checking the hardware capabilities, firmware limitations, perhaps regulatory restrictions, and/or the state of the software configuration to make that determination.
I need to understand the inputs. It's almost certainly taking the wireless device's details as an input, and the intended interface type. It's likely returning a boolean or an error code indicating the allowed/disallowed status. I'll need to see how the allowed status is derived, because this is where the interesting logic will reside.
I'm thinking this function will be called during interface creation or mode changes. So I'll need to trace its use within the relevant cfg80211 framework code, focusing on the points where the requested iftype is set for the device. Is it a straightforward check, or does it involve looking at other factors? Are there any hooks or extensibility points for custom interface types or specific hardware configurations? This is a key area of potential vendor customization, which I always need to keep in mind.
I'm also curious about the error handling. What kind of errors are returned if the iftype is not allowed? Does it provide any helpful context or reasons? And how does the calling code handle those errors? I want to see if the interface change can be gracefully rejected, or the caller needs to handle the situation somehow.
I'll be looking for possible security implications too. Is there any way this function could be bypassed or exploited to create an interface type that shouldn't be allowed? Are there any potential vulnerabilities related to incorrect hardware capability detection? What does it do in case of missing firmware?
Ultimately, my goal is to grasp the core mechanics, identify any potential trouble spots or unexpected behavior, and understand how this function contributes to a robust and secure wireless system.
|
| 133/2 |
2026/08/04 02:14 |
tool |
read-file |
0m
Args: map[File:net/wireless/util.c FirstLine:2939 LineCount:30]
Results: map[Contents:2939: bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype,
2940: bool is_4addr, u8 check_swif)
2941:
2942: {
2943: bool is_vlan = iftype == NL80211_IFTYPE_AP_VLAN;
2944:
2945: switch (check_swif) {
2946: case 0:
2947: if (is_vlan && is_4addr)
2948: return wiphy->flags & WIPHY_FLAG_4ADDR_AP;
2949: return wiphy->interface_modes & BIT(iftype);
2950: case 1:
2951: if (!(wiphy->software_iftypes & BIT(iftype)) && is_vlan)
2952: return wiphy->flags & WIPHY_FLAG_4ADDR_AP;
2953: return wiphy->software_iftypes & BIT(iftype);
2954: default:
2955: break;
2956: }
2957:
2958: return false;
2959: }
2960: EXPORT_SYMBOL(cfg80211_iftype_allowed);
2961:
2962: void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id)
2963: {
2964: struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
2965:
2966: lockdep_assert_wiphy(wdev->wiphy);
2967:
2968: switch (wdev->iftype) {
]
|
| 134/2 |
2026/08/04 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 91161
output: 47
thoughts: 426
Thoughts: **Investigating Interface Type Support in mac80211_hwsim**
Okay, so I'm digging into how `mac80211_hwsim` handles interface types. The `cfg80211_iftype_allowed` function is the key here, it checks if a given interface type, `iftype`, is supported by the hardware by comparing it against `wiphy->interface_modes`. With `mac80211_hwsim`, `interface_modes` gets its initial value from `param.iftypes` during the `mac80211_hwsim_new_radio` call. Hmm, a quick review reveals that, by default, `param.iftypes` *doesn't* include `NL80211_IFTYPE_AP_VLAN`.
My immediate question is: can the user somehow specify `NL80211_IFTYPE_AP_VLAN` as supported? Let's trace it back. I should look at `hwsim_new_radio_nl` function.
The code checks if the `HWSIM_ATTR_IFTYPE_SUPPORT` attribute is present. If it is, the value is assigned to `param.iftypes`. A quick check of the relevant lines shows the code validates the supplied types, ensuring they don't exceed the `HWSIM_IFTYPE_SUPPORT_MASK`. Since `NL80211_IFTYPE_AP_VLAN` isn't in `HWSIM_IFTYPE_SUPPORT_MASK`, the user *cannot* specify it directly through this attribute. This is quite an important constraint.
If `NL80211_IFTYPE_AP_VLAN` is *not* in `interface_modes`, then `cfg80211_iftype_allowed` will correctly return false. Makes sense.
I have to wonder, is `NL80211_IFTYPE_AP_VLAN` being added to `interface_modes` somewhere else later? That's the next avenue to explore.
|
| 135/2 |
2026/08/04 02:15 |
tool |
grepper |
0m
Args: map[Expression:interface_modes.*NL80211_IFTYPE_AP_VLAN PathPrefix:net/mac80211/]
Results: map[Output:net/mac80211/main.c=1138=int ieee80211_register_hw(struct ieee80211_hw *hw)
--
net/mac80211/main.c-1371- !ieee80211_hw_check(&local->hw, SW_CRYPTO_CONTROL)) {
net/mac80211/main.c:1372: hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
net/mac80211/main.c-1373- hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
]
|
| 136/2 |
2026/08/04 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 92280
output: 66
thoughts: 2600
Thoughts: **Analysis of the IEEE 802.11 AP VLAN Bitrate Mask Warning**
Okay, so here's what I've got after going through this. `ieee80211_register_hw` will add support for `NL80211_IFTYPE_AP_VLAN` if the hardware supports regular AP mode, and luckily, `mac80211_hwsim` does, so we can expect AP VLAN interfaces to work fine in that context. My investigation focuses on an observed `WARN_ONCE` in the kernel related to setting the bitrate mask on an AP VLAN interface.
Here's the trigger path: An unprivileged user creates a user and network namespace. They then create a `mac80211_hwsim` radio using a netlink command. Then, they create an `AP_VLAN` interface on the radio using another netlink command, then bring it up. Because it's an `AP_VLAN` interface, the interface state becomes running, but `IEEE80211_SDATA_IN_DRIVER` doesn't get set. Then the user sends a `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command which will trigger a call to the driver. When the `drv_set_bitrate_mask` function is called, the code checks `sdata->flags & IEEE80211_SDATA_IN_DRIVER`, which is false. This triggers the warning.
So, while this can't be triggered by an unprivileged process, and will not cause a DoS, it is accessible from user namespaces. It's essentially a benign warning, since the unexpected condition will result in an early return with the error `-EIO`. There's no memory corruption, no information leak, and no privilege escalation. Other potential trigger paths, such as failure in `drv_add_interface` during interface setup or changing the MAC address, won't trigger the warning under these circumstances.
Therefore, my conclusion is:
- Exploitable: False
- DenialOfService: False
- Accessible From Unprivileged Processes: False
- Accessible From User Namespaces: True
|
| 137/2 |
2026/08/04 02:15 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 138/2 |
2026/08/04 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 95488
output: 593
thoughts: 556
Thoughts: **My Analysis of the mac80211 Bug**
Okay, the analysis is complete, and it's time to document my reasoning. The issue boils down to a `WARN_ONCE` triggered within `check_sdata_in_driver`. The root cause is a call to `drv_set_bitrate_mask` on a mac80211 sub-interface that isn't properly "in the driver" - meaning the `IEEE80211_SDATA_IN_DRIVER` flag hasn't been set.
Specifically, this manifests when an `AP_VLAN` interface is created and activated. The `ieee80211_do_open` function sets the `SDATA_STATE_RUNNING` bit for these interfaces, but intentionally avoids calling `drv_add_interface`, leaving `IEEE80211_SDATA_IN_DRIVER` unset. Later, if a user attempts to manipulate the bitrate mask using either the `SIOCSIWRATE` ioctl or the `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command on this `AP_VLAN` interface, the code checks if the interface is running (which it is) and then calls `drv_set_bitrate_mask`. This function, however, assumes the interface is already integrated into the driver, and so, the `check_sdata_in_driver` function triggers the `WARN_ONCE`.
Now, regarding the impact. The good news is, it's not exploitable in any traditional sense. The code handles the unexpected situation gracefully by returning `-EIO` immediately after the warning. There's no memory corruption, no out-of-bounds access, and no information leak, so there's no way to hijack the system. Also, it's not a denial of service vulnerability in the normal sense. While the `WARN_ONCE` is concerning, it doesn't crash the kernel in standard configurations – unless, of course, `panic_on_warn` is enabled, which is unusual for production builds. The control flow simply returns an error code, which is correct behavior.
Access isn't a significant concern. The vulnerability requires `CAP_NET_ADMIN` privileges to create the `AP_VLAN` interface and execute the ioctl or netlink command. This mitigates the risk from unprivileged processes. However, a user *with* `CAP_NET_ADMIN` privileges *within a user or network namespace* *can* trigger this warning. By creating a virtual `mac80211_hwsim` radio (assuming the AP mode is supported, of course), they can create and activate an `AP_VLAN` interface and issue the appropriate command to generate the warning.
|
| 139/1 |
2026/08/04 02:15 |
action |
format-explanation |
0m
Results: map[Explanation:The bug is a `WARN_ONCE` triggered in `check_sdata_in_driver` when
`drv_set_bitrate_mask` is called on a mac80211 sub-interface that is not "in the
driver" (i.e., the `IEEE80211_SDATA_IN_DRIVER` flag is not set).
This state can be reached when an `AP_VLAN` interface is created and brought up.
For `AP_VLAN` interfaces, `ieee80211_do_open` sets the `SDATA_STATE_RUNNING` bit
but intentionally skips calling `drv_add_interface`, meaning
`IEEE80211_SDATA_IN_DRIVER` is never set. If a user then issues a `SIOCSIWRATE`
ioctl or `NL80211_CMD_SET_TX_BITRATE_MASK` netlink command on this `AP_VLAN`
interface, the kernel checks if the interface is running
(`ieee80211_sdata_running` returns true) and proceeds to call
`drv_set_bitrate_mask`. This function expects the interface to be in the driver
and triggers the `WARN_ONCE` via `check_sdata_in_driver`.
**Exploitable: False**
The condition is gracefully handled by returning `-EIO` immediately after the
warning:
```c
if (!check_sdata_in_driver(sdata))
return -EIO;
```
There is no memory corruption, out-of-bounds access, or information leak.
**Denial of Service: False**
The warning does not crash the kernel (unless `panic_on_warn` is enabled, which
is not the case for standard production builds). The control flow properly
handles the unexpected state by returning an error code, so it does not lead to
deadlocks, hangs, or resource leaks.
**Accessible From Unprivileged Processes: False**
Triggering this requires `CAP_NET_ADMIN` to issue the `SIOCSIWRATE` ioctl or the
`NL80211_CMD_SET_TX_BITRATE_MASK` netlink command, as well as to create the
`AP_VLAN` interface. An unprivileged user in the initial namespace cannot
perform these actions.
**Accessible From User Namespaces: True**
A user with `CAP_NET_ADMIN` in a user/network namespace can create a virtual
`mac80211_hwsim` radio (which supports `AP_VLAN` if `AP` mode is supported),
create an `AP_VLAN` interface, bring it up, and send the netlink command to
trigger the warning.]
|