| 0/0 |
2026/05/16 06:56 |
flow |
assessment-security |
39m
Results: map[DenialOfService:false Explanation:**Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is a race condition in the `aql_enable_write` function located in
`net/mac80211/debugfs.c`. This function handles writes to the `aql_enable`
debugfs file, which toggles the `aql_disable` static key.
The code reads the current state of the static key (`aql_disabled =
static_key_false(&aql_disable.key);`) and then conditionally increments or
decrements it based on the user input ('0' or '1'). If multiple threads
concurrently write '1' to this file, they can both read `aql_disabled` as `true`
and subsequently both call `static_branch_dec(&aql_disable)`. The second call
attempts to decrement the static key's reference count below 0, which triggers a
`WARN_ON_ONCE(val == 0)` in `__static_key_slow_dec_cpuslocked` (in
`kernel/jump_label.c`).
**2. Exploitability & Denial of Service:**
The bug is **not exploitable** and does **not cause a Denial of Service (DoS)**.
When the `WARN_ON_ONCE(val == 0)` is triggered in
`__static_key_slow_dec_cpuslocked`, the function immediately returns without
actually decrementing the atomic counter. This prevents any integer underflow or
memory corruption. The kernel continues to execute normally. The only
consequence is a kernel warning in the logs and a potential desynchronization of
the static key's state (requiring the user to write to the debugfs file again to
correct it), which is purely a minor functional glitch in a debug interface.
**3. Accessibility (Unprivileged & User Namespaces):**
The bug is **not accessible to unprivileged users** or from **user namespaces**.
The `aql_enable` file is exposed via debugfs (`/sys/kernel/debug/...`) and is
explicitly created with `0600` permissions (`DEBUGFS_ADD_MODE(aql_enable,
0600)`). Debugfs is a root-only interface that cannot be mounted or accessed by
unprivileged users, nor can it be mounted inside unprivileged user namespaces.
The syzkaller report confirms this by showing `UID: 0`, indicating it was
triggered by the root user.
**4. Other Triggers:**
The bug is strictly triggered by a local root user writing to a specific debugfs
file. It cannot be triggered via network traffic (Ethernet or other remote
vectors), malicious filesystems, peripheral devices, or from VM guest/host
contexts. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/16 06:56 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/16 06:56 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/656999a5500966ef94900a70fd38eebe6ff83d6f]
|
| 3/1 |
2026/05/16 06:57 |
action |
kernel-builder |
24m
Results: map[KernelObj:/app/workdir/cache/build/278bff1c04cf4da88289c37ad7c362bec9eb24f7]
|
| 4/1 |
2026/05/16 07:21 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/16 07:34 |
agent |
expert |
1m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction:
You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by greeping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Don't make assumptions; verify them with source code access. Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completly wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control tranfers, that's also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitabability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidetial Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
val == 0
WARNING: kernel/jump_label.c:311 at __static_key_slow_dec_cpuslocked.part.0+0x107/0x120 kernel/jump_label.c:311, CPU#0: syz.1.3155/20288
Modules linked in:
CPU: 0 UID: 0 PID: 20288 Comm: syz.1.3155 Tainted: G U L syzkaller #0 PREEMPT(full)
Tainted: [U]=USER, [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/24/2026
RIP: 0010:__static_key_slow_dec_cpuslocked.part.0+0x107/0x120 kernel/jump_label.c:311
Code: f2 c9 ff 5b 5d c3 cc cc cc cc e8 54 f2 c9 ff 48 89 df e8 ac f9 ff ff eb ad e8 45 f2 c9 ff 90 0f 0b 90 eb a2 e8 3a f2 c9 ff 90 <0f> 0b 90 eb 97 48 89 df e8 5c 4b 33 00 e9 36 ff ff ff 0f 1f 80 00
RSP: 0018:ffffc9000b9f7c10 EFLAGS: 00010293
RAX: 0000000000000000 RBX: ffffffff9b3e5d40 RCX: ffffffff823c57b4
RDX: ffff8880285a0000 RSI: ffffffff823c5846 RDI: ffff8880285a0000
RBP: 0000000000000000 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 000000000000000a
R13: 1ffff9200173ef88 R14: 0000000000000001 R15: ffffc9000b9f7e98
FS: 00007f530dd726c0(0000) GS:ffff8881245e3000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000001140 CR3: 000000007cc4a000 CR4: 00000000003526f0
Call Trace:
<TASK>
__static_key_slow_dec_cpuslocked kernel/jump_label.c:297 [inline]
__static_key_slow_dec kernel/jump_label.c:321 [inline]
static_key_slow_dec+0x7c/0xc0 kernel/jump_label.c:336
aql_enable_write+0x2b2/0x310 net/mac80211/debugfs.c:343
short_proxy_write+0x133/0x1a0 fs/debugfs/file.c:383
vfs_write+0x2aa/0x1070 fs/read_write.c:684
ksys_pwrite64 fs/read_write.c:793 [inline]
__do_sys_pwrite64 fs/read_write.c:801 [inline]
__se_sys_pwrite64 fs/read_write.c:798 [inline]
__x64_sys_pwrite64+0x1eb/0x250 fs/read_write.c:798
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xc9/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f530cf9aeb9
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:00007f530dd72028 EFLAGS: 00000246 ORIG_RAX: 0000000000000012
RAX: ffffffffffffffda RBX: 00007f530d215fa0 RCX: 00007f530cf9aeb9
RDX: 0000000000000003 RSI: 0000000000000000 RDI: 0000000000000010
RBP: 00007f530d008c1f R08: 0000000000000000 R09: 0000000000000000
R10: 4200000000000005 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f530d216038 R14: 00007f530d215fa0 R15: 00007ffde89fb978
</TASK>
Reply: **Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is a race condition in the `aql_enable_write` function located in `net/mac80211/debugfs.c`. This function handles writes to the `aql_enable` debugfs file, which toggles the `aql_disable` static key.
The code reads the current state of the static key (`aql_disabled = static_key_false(&aql_disable.key);`) and then conditionally increments or decrements it based on the user input ('0' or '1'). If multiple threads concurrently write '1' to this file, they can both read `aql_disabled` as `true` and subsequently both call `static_branch_dec(&aql_disable)`. The second call attempts to decrement the static key's reference count below 0, which triggers a `WARN_ON_ONCE(val == 0)` in `__static_key_slow_dec_cpuslocked` (in `kernel/jump_label.c`).
**2. Exploitability & Denial of Service:**
The bug is **not exploitable** and does **not cause a Denial of Service (DoS)**.
When the `WARN_ON_ONCE(val == 0)` is triggered in `__static_key_slow_dec_cpuslocked`, the function immediately returns without actually decrementing the atomic counter. This prevents any integer underflow or memory corruption. The kernel continues to execute normally. The only consequence is a kernel warning in the logs and a potential desynchronization of the static key's state (requiring the user to write to the debugfs file again to correct it), which is purely a minor functional glitch in a debug interface.
**3. Accessibility (Unprivileged & User Namespaces):**
The bug is **not accessible to unprivileged users** or from **user namespaces**.
The `aql_enable` file is exposed via debugfs (`/sys/kernel/debug/...`) and is explicitly created with `0600` permissions (`DEBUGFS_ADD_MODE(aql_enable, 0600)`). Debugfs is a root-only interface that cannot be mounted or accessed by unprivileged users, nor can it be mounted inside unprivileged user namespaces. The syzkaller report confirms this by showing `UID: 0`, indicating it was triggered by the root user.
**4. Other Triggers:**
The bug is strictly triggered by a local root user writing to a specific debugfs file. It cannot be triggered via network traffic (Ethernet or other remote vectors), malicious filesystems, peripheral devices, or from VM guest/host contexts.
|
| 6/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 6146
output: 48
thoughts: 186
Thoughts: **Investigating a Kernel Bug Report**
Okay, so I've got this kernel bug report flagging a warning. The core of it seems to be in `kernel/jump_label.c`, specifically at line 311 within the `__static_key_slow_dec_cpuslocked.part.0` function. Intriguingly, this warning is *triggered* by a call to `aql_enable_write` within `net/mac80211/debugfs.c`, line 343, which immediately makes me think about potential concurrency issues or race conditions in the mac80211 debugging interface.
The call stack confirms this flow: we have `__static_key_slow_dec`, which leads to `static_key_slow_dec`, then *that* triggers `aql_enable_write`, all the way down to `short_proxy_write`, `vfs_write`, and ultimately `ksys_pwrite64`. This is pretty standard for a write to a debugfs entry, but the `static_key_slow_dec` is what's raising the flag. I need to take a closer look at `aql_enable_write` within that `net/mac80211/debugfs.c` file to really understand *why* this decrement is causing a warning. I'm guessing there's either a double decrement, or an improper synchronization with the jump label that's getting triggered during the write. Time to dive into the code.
|
| 7/2 |
2026/05/16 07:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac80211/debugfs.c IncludeLines:true Name:aql_enable_write]
Results: map[SourceCode: 320: static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
321: size_t count, loff_t *ppos)
322: {
323: bool aql_disabled = static_key_false(&aql_disable.key);
324: char buf[3];
325: size_t len;
326:
327: if (count > sizeof(buf))
328: return -EINVAL;
329:
330: if (copy_from_user(buf, user_buf, count))
331: return -EFAULT;
332:
333: buf[sizeof(buf) - 1] = '\0';
334: len = strlen(buf);
335: if (len > 0 && buf[len - 1] == '\n')
336: buf[len - 1] = 0;
337:
338: if (buf[0] == '0' && buf[1] == '\0') {
339: if (!aql_disabled)
340: static_branch_inc(&aql_disable);
341: } else if (buf[0] == '1' && buf[1] == '\0') {
342: if (aql_disabled)
343: static_branch_dec(&aql_disable);
344: } else {
345: return -EINVAL;
346: }
347:
348: return count;
349: }
SourceFile:net/mac80211/debugfs.c]
|
| 8/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 7195
output: 48
thoughts: 458
Thoughts: **Analyzing the Race Condition in `aql_enable_write`**
Okay, so the bug is definitely within `aql_enable_write`. My initial instinct is to look at the access permissions of debugfs since it's writing to a debugfs file. That screams privilege escalation potential, but then I remember debugfs is typically restricted to root β usually mounted at `/sys/kernel/debug` with mode 0700. So, it should be protected by CAP_SYS_ADMIN, right? Let me double-check... Yes, it appears debugfs is off-limits to unprivileged users, as expected. User namespaces are out too since debugfs isn't mountable there.
The warning in `__static_key_slow_dec_cpuslocked` is the tell. It stems from the `val == 0` condition. The code snippet makes it clear: it's all about that `aql_disabled` boolean and the `static_branch_dec` calls. I can see the race immediately. Multiple threads could be reading `aql_disabled`, then decrementing it simultaneously. Here's the scenario:
Thread A reads `aql_disabled = true`.
Thread B reads `aql_disabled = true`.
Thread A calls `static_branch_dec(&aql_disable)`.
Thread B calls `static_branch_dec(&aql_disable)`.
That second call to `static_branch_dec` is the killer! It'll decrement the static key below zero, triggering that warning. So, we're dealing with a simple integer underflow issue.
The question is, is this exploitable in any practical sense? We are dealing with a kernel warning, so exploitation possibilities are limited. Let me take a look at the `__static_key_slow_dec_cpuslocked` function in `kernel/jump_label.c` to see if there's any follow-up code that's impacted by the underflow.
|
| 9/2 |
2026/05/16 07:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/jump_label.c IncludeLines:true Name:__static_key_slow_dec_cpuslocked]
Results: map[SourceCode: 292: static void __static_key_slow_dec_cpuslocked(struct static_key *key)
293: {
294: lockdep_assert_cpus_held();
295: int val;
296:
297: if (static_key_dec_not_one(key))
298: return;
299:
300: guard(mutex)(&jump_label_mutex);
301: val = atomic_read(&key->enabled);
302: /*
303: * It should be impossible to observe -1 with jump_label_mutex held,
304: * see static_key_slow_inc_cpuslocked().
305: */
306: if (WARN_ON_ONCE(val == -1))
307: return;
308: /*
309: * Cannot already be 0, something went sideways.
310: */
311: if (WARN_ON_ONCE(val == 0))
312: return;
313:
314: if (atomic_dec_and_test(&key->enabled))
315: jump_label_update(key);
316: }
SourceFile:kernel/jump_label.c]
|
| 10/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 8503
output: 56
thoughts: 307
Thoughts: **Analyzing the Vulnerability Report**
Okay, let's break this down. If `val` is zero, the function just returns without decrementing. That prevents any underflow, which is good. All it does is print a warning. So, it's not directly exploitable, and it certainly won't cause a denial of service; the warning is annoying, but it just returns.
The key thing is it's in debugfs. Debugfs isn't generally accessible to unprivileged users or within user namespaces. I'd better double-check that though. Typically, debugfs is mounted at `/sys/kernel/debug` with permissions of 0700, that is, root-only. Even in unusual cases with relaxed permissions, it's still considered a privileged root interface. The syzkaller report clearly indicates `UID: 0`, so it was root that triggered it. This looks like a simple race condition in a debugfs write handler.
Alright, time to fill out the results. Definitely not exploitable. Definitely not a denial of service. Not unprivileged access, not in a user namespace. No VM guest or host trigger. It's not network-triggered, remote, peripheral, or filesystem-triggered either.
Wait a second⦠is `aql_enable_write` exposed anywhere *else*? No, it's definitely a debugfs file. OK, I'm comfortable with the analysis.
|
| 11/2 |
2026/05/16 07:34 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac80211/debugfs.c IncludeSnippetLines:5 Name:aql_enable_write SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 12/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 9186
output: 20
thoughts: 49
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Debugging a Naming Convention**
Hmm, I seem to recall using `aql_enable_write` as the function name. A static function, at that. Now, let me retrace my steps and see where that's actually being used. I need to understand the implications of this name. It's a static function, so its scope is limited to its compilation unit, which might influence its usage patterns. Okay, the logical place to start looking for this would be within the `net/mac80211/debugfs.c` file. Time to dig in and see how I implemented it. Hopefully, the calling context provides some immediate clues.
|
| 13/2 |
2026/05/16 07:34 |
tool |
grepper |
0m
Args: map[Expression:aql_enable_write]
Results: map[Output:net/mac80211/debugfs.c=308=static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
--
net/mac80211/debugfs.c-319-
net/mac80211/debugfs.c:320:static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
net/mac80211/debugfs.c-321- size_t count, loff_t *ppos)
--
net/mac80211/debugfs.c=351=static const struct debugfs_short_fops aql_enable_ops = {
net/mac80211/debugfs.c:352: .write = aql_enable_write,
net/mac80211/debugfs.c-353- .read = aql_enable_read,
]
|
| 14/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 9665
output: 20
thoughts: 13
Thoughts: Okay, here's my summary, taking on the persona you requested:
**Assessing the Context of `aql_enable_ops`**
Alright, let's break this down. My mind immediately flags `aql_enable_ops`. That means I need to think about *where* this is used, the typical implications. If it's something like an enable function, I'm thinking about it probably being used within some sort of context or feature flag mechanism. I'm going to consider what parts of a system this impacts. What type of "ops" is being controlled. I can immediately see the need for checking its dependencies. Is this a new feature, a legacy one, or a modification? Understanding the role of `aql_enable_ops` is critical. Knowing how it's integrated tells me about its functionality within the system. Is it the key? Or does it merely gate functionality?
|
| 15/2 |
2026/05/16 07:34 |
tool |
grepper |
0m
Args: map[Expression:aql_enable_ops]
Results: map[Output:net/mac80211/debugfs.c=320=static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
--
net/mac80211/debugfs.c-350-
net/mac80211/debugfs.c:351:static const struct debugfs_short_fops aql_enable_ops = {
net/mac80211/debugfs.c-352- .write = aql_enable_write,
]
|
| 16/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 10034
output: 48
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Tracing the Usage of `aql_enable_ops`**
Hmm, so I'm thinking about the code base and a specific function, `aql_enable_ops`, has popped into my head. It's a key piece in this, so my immediate thought is to trace where it is used. I need to refresh my memory and understand the call graph surrounding it. Where in the framework is this thing being invoked? Is it tied to a specific driver, a particular feature, or is it more fundamental? I need to quickly determine its dependencies and its role in the overall execution flow. I need to map the use cases out clearly to avoid any missteps in my next evaluation.
|
| 17/2 |
2026/05/16 07:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac80211/debugfs.c IncludeLines:true Name:aql_enable_ops]
Results: map[SourceCode: 351: static const struct debugfs_short_fops aql_enable_ops = {
352: .write = aql_enable_write,
353: .read = aql_enable_read,
354: .llseek = default_llseek,
355: };
SourceFile:net/mac80211/debugfs.c]
|
| 18/2 |
2026/05/16 07:34 |
llm |
expert |
0m
Model:
Tokens:
input: 10377
output: 20
thoughts: 20
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Grep Hunt: Another Pass on `aql_enable_ops`**
Okay, time to circle back. I'm going to run a fresh `grep` through the codebase, specifically looking again for instances of `aql_enable_ops`. I'm double-checking because, you know how it is, these things can be buried in a conditional or a macro definition, and it's easy to overlook something the first time around. Need to be absolutely certain I didn't miss any potential interactions, dependencies, or usage scenarios related to that particular function or variable, especially given the context of what we're investigating. Gotta ensure I haven't overlooked any crucial pieces in the broader picture.
|
| 19/2 |
2026/05/16 07:35 |
tool |
grepper |
0m
Args: map[Expression:aql_enable_ops]
Results: map[Output:net/mac80211/debugfs.c=320=static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
--
net/mac80211/debugfs.c-350-
net/mac80211/debugfs.c:351:static const struct debugfs_short_fops aql_enable_ops = {
net/mac80211/debugfs.c-352- .write = aql_enable_write,
]
|
| 20/2 |
2026/05/16 07:35 |
llm |
expert |
0m
Model:
Tokens:
input: 10727
output: 28
thoughts: 41
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Suspected Unused Function**
Hmm, this piece of code isn't being directly called? That seems unlikely, given its purpose. I'd expect it to be used somewhere. Okay, let's take a look. If it *is* used, a good place to start would be in the debugfs code, particularly within the MAC80211 subsystem. Specifically, my hunch is that it might be hooked into some debugging interface. I'll need to check the relevant debugfs implementation. Let me jump into `net/mac80211/debugfs.c` and zero in on that section around line 351, that should be a useful starting point. Hopefully, the implementation there will shed some light.
|
| 21/2 |
2026/05/16 07:35 |
tool |
read-file |
0m
Args: map[File:net/mac80211/debugfs.c]
Results: map[Contents:// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211 debugfs for wireless PHYs
*
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2018 - 2019, 2021-2025 Intel Corporation
*/
#include <linux/debugfs.h>
#include <linux/rtnetlink.h>
#include <linux/vmalloc.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
#include "debugfs.h"
#define DEBUGFS_FORMAT_BUFFER_SIZE 100
int mac80211_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
{
va_list args;
char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
int res;
va_start(args, fmt);
res = vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct ieee80211_local *local = file->private_data; \
\
return mac80211_format_buffer(userbuf, count, ppos, \
fmt "\n", ##value); \
}
#define DEBUGFS_READONLY_FILE_OPS(name) \
static const struct debugfs_short_fops name## _ops = { \
.read = name## _read, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
DEBUGFS_READONLY_FILE_FN(name, fmt, value) \
DEBUGFS_READONLY_FILE_OPS(name)
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, phyd, local, &name## _ops)
#define DEBUGFS_ADD_MODE(name, mode) \
debugfs_create_file(#name, mode, phyd, local, &name## _ops);
DEBUGFS_READONLY_FILE(hw_conf, "%x",
local->hw.conf.flags);
DEBUGFS_READONLY_FILE(user_power, "%d",
local->user_power_level);
DEBUGFS_READONLY_FILE(power, "%d",
local->hw.conf.power_level);
DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
local->total_ps_buffered);
DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
local->wep_iv & 0xffffff);
DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
static ssize_t aqm_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
struct fq *fq = &local->fq;
char buf[200];
int len = 0;
spin_lock_bh(&local->fq.lock);
len = scnprintf(buf, sizeof(buf),
"access name value\n"
"R fq_flows_cnt %u\n"
"R fq_backlog %u\n"
"R fq_overlimit %u\n"
"R fq_overmemory %u\n"
"R fq_collisions %u\n"
"R fq_memory_usage %u\n"
"RW fq_memory_limit %u\n"
"RW fq_limit %u\n"
"RW fq_quantum %u\n",
fq->flows_cnt,
fq->backlog,
fq->overmemory,
fq->overlimit,
fq->collisions,
fq->memory_usage,
fq->memory_limit,
fq->limit,
fq->quantum);
spin_unlock_bh(&local->fq.lock);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static ssize_t aqm_write(struct file *file,
const char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[100];
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
if (count && buf[count - 1] == '\n')
buf[count - 1] = '\0';
else
buf[count] = '\0';
if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
return count;
else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1)
return count;
else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1)
return count;
return -EINVAL;
}
static const struct debugfs_short_fops aqm_ops = {
.write = aqm_write,
.read = aqm_read,
.llseek = default_llseek,
};
static ssize_t airtime_flags_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[128] = {}, *pos, *end;
pos = buf;
end = pos + sizeof(buf) - 1;
if (local->airtime_flags & AIRTIME_USE_TX)
pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n",
AIRTIME_USE_TX);
if (local->airtime_flags & AIRTIME_USE_RX)
pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n",
AIRTIME_USE_RX);
return simple_read_from_buffer(user_buf, count, ppos, buf,
strlen(buf));
}
static ssize_t airtime_flags_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[16];
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
if (count && buf[count - 1] == '\n')
buf[count - 1] = '\0';
else
buf[count] = '\0';
if (kstrtou16(buf, 0, &local->airtime_flags))
return -EINVAL;
return count;
}
static const struct debugfs_short_fops airtime_flags_ops = {
.write = airtime_flags_write,
.read = airtime_flags_read,
.llseek = default_llseek,
};
static ssize_t aql_pending_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[400];
int len = 0;
len = scnprintf(buf, sizeof(buf),
"AC AQL pending\n"
"VO %u us\n"
"VI %u us\n"
"BE %u us\n"
"BK %u us\n"
"total %u us\n",
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
atomic_read(&local->aql_total_pending_airtime));
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static const struct debugfs_short_fops aql_pending_ops = {
.read = aql_pending_read,
.llseek = default_llseek,
};
static ssize_t aql_txq_limit_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[400];
int len = 0;
len = scnprintf(buf, sizeof(buf),
"AC AQL limit low AQL limit high\n"
"VO %u %u\n"
"VI %u %u\n"
"BE %u %u\n"
"BK %u %u\n",
local->aql_txq_limit_low[IEEE80211_AC_VO],
local->aql_txq_limit_high[IEEE80211_AC_VO],
local->aql_txq_limit_low[IEEE80211_AC_VI],
local->aql_txq_limit_high[IEEE80211_AC_VI],
local->aql_txq_limit_low[IEEE80211_AC_BE],
local->aql_txq_limit_high[IEEE80211_AC_BE],
local->aql_txq_limit_low[IEEE80211_AC_BK],
local->aql_txq_limit_high[IEEE80211_AC_BK]);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static ssize_t aql_txq_limit_write(struct file *file,
const char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[100];
u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
struct sta_info *sta;
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
if (count && buf[count - 1] == '\n')
buf[count - 1] = '\0';
else
buf[count] = '\0';
if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
return -EINVAL;
if (ac >= IEEE80211_NUM_ACS)
return -EINVAL;
q_limit_low_old = local->aql_txq_limit_low[ac];
q_limit_high_old = local->aql_txq_limit_high[ac];
guard(wiphy)(local->hw.wiphy);
local->aql_txq_limit_low[ac] = q_limit_low;
local->aql_txq_limit_high[ac] = q_limit_high;
list_for_each_entry(sta, &local->sta_list, list) {
/* If a sta has customized queue limits, keep it */
if (sta->airtime[ac].aql_limit_low == q_limit_low_old &&
sta->airtime[ac].aql_limit_high == q_limit_high_old) {
sta->airtime[ac].aql_limit_low = q_limit_low;
sta->airtime[ac].aql_limit_high = q_limit_high;
}
}
return count;
}
static const struct debugfs_short_fops aql_txq_limit_ops = {
.write = aql_txq_limit_write,
.read = aql_txq_limit_read,
.llseek = default_llseek,
};
static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[3];
int len;
len = scnprintf(buf, sizeof(buf), "%d\n",
!static_key_false(&aql_disable.key));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
bool aql_disabled = static_key_false(&aql_disable.key);
char buf[3];
size_t len;
if (count > sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = 0;
if (buf[0] == '0' && buf[1] == '\0') {
if (!aql_disabled)
static_branch_inc(&aql_disable);
} else if (buf[0] == '1' && buf[1] == '\0') {
if (aql_disabled)
static_branch_dec(&aql_disable);
} else {
return -EINVAL;
}
return count;
}
static const struct debugfs_short_fops aql_enable_ops = {
.write = aql_enable_write,
.read = aql_enable_read,
.llseek = default_llseek,
};
static ssize_t force_tx_status_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[3];
int len = 0;
len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static ssize_t force_tx_status_write(struct file *file,
const char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[3];
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
if (count && buf[count - 1] == '\n')
buf[count - 1] = '\0';
else
buf[count] = '\0';
if (buf[0] == '0' && buf[1] == '\0')
local->force_tx_status = 0;
else if (buf[0] == '1' && buf[1] == '\0')
local->force_tx_status = 1;
else
return -EINVAL;
return count;
}
static const struct debugfs_short_fops force_tx_status_ops = {
.write = force_tx_status_write,
.read = force_tx_status_read,
.llseek = default_llseek,
};
#ifdef CONFIG_PM
static ssize_t reset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
int ret;
rtnl_lock();
wiphy_lock(local->hw.wiphy);
__ieee80211_suspend(&local->hw, NULL);
ret = __ieee80211_resume(&local->hw);
wiphy_unlock(local->hw.wiphy);
if (ret)
cfg80211_shutdown_all_interfaces(local->hw.wiphy);
rtnl_unlock();
return count;
}
static const struct debugfs_short_fops reset_ops = {
.write = reset_write,
.llseek = noop_llseek,
};
#endif
static const char *hw_flag_names[] = {
#define FLAG(F) [IEEE80211_HW_##F] = #F
FLAG(HAS_RATE_CONTROL),
FLAG(RX_INCLUDES_FCS),
FLAG(HOST_BROADCAST_PS_BUFFERING),
FLAG(SIGNAL_UNSPEC),
FLAG(SIGNAL_DBM),
FLAG(NEED_DTIM_BEFORE_ASSOC),
FLAG(SPECTRUM_MGMT),
FLAG(AMPDU_AGGREGATION),
FLAG(SUPPORTS_PS),
FLAG(PS_NULLFUNC_STACK),
FLAG(SUPPORTS_DYNAMIC_PS),
FLAG(MFP_CAPABLE),
FLAG(WANT_MONITOR_VIF),
FLAG(NO_VIRTUAL_MONITOR),
FLAG(NO_AUTO_VIF),
FLAG(SW_CRYPTO_CONTROL),
FLAG(SUPPORT_FAST_XMIT),
FLAG(REPORTS_TX_ACK_STATUS),
FLAG(CONNECTION_MONITOR),
FLAG(QUEUE_CONTROL),
FLAG(SUPPORTS_PER_STA_GTK),
FLAG(AP_LINK_PS),
FLAG(TX_AMPDU_SETUP_IN_HW),
FLAG(SUPPORTS_RC_TABLE),
FLAG(P2P_DEV_ADDR_FOR_INTF),
FLAG(TIMING_BEACON_ONLY),
FLAG(SUPPORTS_HT_CCK_RATES),
FLAG(CHANCTX_STA_CSA),
FLAG(SUPPORTS_CLONED_SKBS),
FLAG(SINGLE_SCAN_ON_ALL_BANDS),
FLAG(TDLS_WIDER_BW),
FLAG(SUPPORTS_AMSDU_IN_AMPDU),
FLAG(BEACON_TX_STATUS),
FLAG(NEEDS_UNIQUE_STA_ADDR),
FLAG(SUPPORTS_REORDERING_BUFFER),
FLAG(USES_RSS),
FLAG(TX_AMSDU),
FLAG(TX_FRAG_LIST),
FLAG(REPORTS_LOW_ACK),
FLAG(SUPPORTS_TX_FRAG),
FLAG(SUPPORTS_TDLS_BUFFER_STA),
FLAG(DOESNT_SUPPORT_QOS_NDP),
FLAG(BUFF_MMPDU_TXQ),
FLAG(SUPPORTS_VHT_EXT_NSS_BW),
FLAG(STA_MMPDU_TXQ),
FLAG(TX_STATUS_NO_AMPDU_LEN),
FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
FLAG(AMPDU_KEYBORDER_SUPPORT),
FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
FLAG(SUPPORTS_CONC_MON_RX_DECAP),
FLAG(DETECTS_COLOR_COLLISION),
FLAG(MLO_MCAST_MULTI_LINK_TX),
FLAG(DISALLOW_PUNCTURING),
FLAG(HANDLES_QUIET_CSA),
FLAG(STRICT),
#undef FLAG
};
static ssize_t hwflags_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS;
char *buf = kzalloc(bufsz, GFP_KERNEL);
char *pos = buf, *end = buf + bufsz - 1;
ssize_t rv;
int i;
if (!buf)
return -ENOMEM;
/* fail compilation if somebody adds or removes
* a flag without updating the name array above
*/
BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS);
for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) {
if (test_bit(i, local->hw.flags))
pos += scnprintf(pos, end - pos, "%s\n",
hw_flag_names[i]);
}
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf);
return rv;
}
static ssize_t hwflags_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[100];
int val;
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
if (count && buf[count - 1] == '\n')
buf[count - 1] = '\0';
else
buf[count] = '\0';
if (sscanf(buf, "strict=%d", &val) == 1) {
switch (val) {
case 0:
ieee80211_hw_set(&local->hw, STRICT);
return count;
case 1:
__clear_bit(IEEE80211_HW_STRICT, local->hw.flags);
return count;
default:
return -EINVAL;
}
}
return -EINVAL;
}
static const struct file_operations hwflags_ops = {
.open = simple_open,
.read = hwflags_read,
.write = hwflags_write,
};
static ssize_t misc_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
/* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
char *buf;
char *pos, *end;
ssize_t rv;
int i;
int ln;
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pos = buf;
end = buf + bufsz - 1;
pos += scnprintf(pos, end - pos, "pending:\n");
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
ln = skb_queue_len(&local->pending[i]);
pos += scnprintf(pos, end - pos, "[%i] %d\n",
i, ln);
}
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf);
return rv;
}
static ssize_t queues_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
unsigned long flags;
char buf[IEEE80211_MAX_QUEUES * 20];
int q, res = 0;
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
for (q = 0; q < local->hw.queues; q++)
res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
local->queue_stop_reasons[q],
skb_queue_len(&local->pending[q]));
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
return simple_read_from_buffer(user_buf, count, ppos, buf, res);
}
DEBUGFS_READONLY_FILE_OPS(queues);
DEBUGFS_READONLY_FILE_OPS(misc);
/* statistics stuff */
static ssize_t format_devstat_counter(struct ieee80211_local *local,
char __user *userbuf,
size_t count, loff_t *ppos,
int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
int buflen))
{
struct ieee80211_low_level_stats stats;
char buf[20];
int res;
wiphy_lock(local->hw.wiphy);
res = drv_get_stats(local, &stats);
wiphy_unlock(local->hw.wiphy);
if (res)
return res;
res = printvalue(&stats, buf, sizeof(buf));
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
#define DEBUGFS_DEVSTATS_FILE(name) \
static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
char *buf, int buflen) \
{ \
return scnprintf(buf, buflen, "%u\n", stats->name); \
} \
static ssize_t stats_ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
return format_devstat_counter(file->private_data, \
userbuf, \
count, \
ppos, \
print_devstats_##name); \
} \
\
static const struct debugfs_short_fops stats_ ##name## _ops = { \
.read = stats_ ##name## _read, \
.llseek = generic_file_llseek, \
};
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
#define DEBUGFS_STATS_ADD(name) \
debugfs_create_u32(#name, 0400, statsd, &local->name);
#endif
#define DEBUGFS_DEVSTATS_ADD(name) \
debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);
void debugfs_hw_add(struct ieee80211_local *local)
{
struct dentry *phyd = local->hw.wiphy->debugfsdir;
struct dentry *statsd;
if (!phyd)
return;
local->debugfs.keys = debugfs_create_dir("keys", phyd);
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(rate_ctrl_alg);
DEBUGFS_ADD(queues);
DEBUGFS_ADD(misc);
#ifdef CONFIG_PM
DEBUGFS_ADD_MODE(reset, 0200);
#endif
DEBUGFS_ADD_MODE(hwflags, 0600);
DEBUGFS_ADD(user_power);
DEBUGFS_ADD(power);
DEBUGFS_ADD(hw_conf);
DEBUGFS_ADD_MODE(force_tx_status, 0600);
DEBUGFS_ADD_MODE(aql_enable, 0600);
DEBUGFS_ADD(aql_pending);
DEBUGFS_ADD_MODE(aqm, 0600);
DEBUGFS_ADD_MODE(airtime_flags, 0600);
DEBUGFS_ADD(aql_txq_limit);
debugfs_create_u32("aql_threshold", 0600,
phyd, &local->aql_threshold);
statsd = debugfs_create_dir("statistics", phyd);
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount);
DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount);
DEBUGFS_STATS_ADD(dot11FailedCount);
DEBUGFS_STATS_ADD(dot11RetryCount);
DEBUGFS_STATS_ADD(dot11MultipleRetryCount);
DEBUGFS_STATS_ADD(dot11FrameDuplicateCount);
DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount);
DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount);
DEBUGFS_STATS_ADD(dot11TransmittedFrameCount);
DEBUGFS_STATS_ADD(tx_handlers_queued);
DEBUGFS_STATS_ADD(tx_handlers_drop_wep);
DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc);
DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port);
DEBUGFS_STATS_ADD(rx_handlers_drop);
DEBUGFS_STATS_ADD(rx_handlers_queued);
DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
DEBUGFS_STATS_ADD(tx_expand_skb_head);
DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
DEBUGFS_STATS_ADD(rx_handlers_fragments);
DEBUGFS_STATS_ADD(tx_status_drop);
#endif
DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
}
]
|
| 22/2 |
2026/05/16 07:35 |
llm |
expert |
0m
Model:
Tokens:
input: 18959
output: 18
thoughts: 709
Thoughts: **Analyzing the AQL Enable Debugfs Entry and Static Key**
Okay, so I'm looking at `net/mac80211/debugfs.c`, specifically how `aql_enable` is handled. It's created in `debugfs_hw_add` using `DEBUGFS_ADD_MODE`. This macro essentially creates a debugfs file named `aql_enable` with permissions set to 0600, which means root has read/write access and no one else does. So, the file will reside in `/sys/kernel/debug/mac80211/phyX/aql_enable`.
The concern here is a reported race condition when writing to `aql_enable`. However, it's flagged as a WARNING, not a BUG. That's immediately reassuring. Since it's in debugfs, my initial thought is that it is not exposed to unprivileged users. I need to solidify this.
I've confirmed that because of the 0600 permissions, only root can interact with it. Even if the debugfs mount itself were set to the less restrictive 0755, the file's individual permissions take precedence, and root ownership is the final barrier. So, unprivileged users and user namespaces can't touch it. It's not a network, remote, peripheral, filesystem, VM guest or host trigger either. This makes it non-exploitable from a typical attack vector.
Next, I'm digging into the static key underflow scenario, specifically within the `__static_key_slow_dec_cpuslocked` function. It looks like if `val == 0`, the function simply returns without decrementing the key. In this case, the value isn't allowed to underflow. It just logs a warning if that situation occurs. So, this wouldn't be exploitable. No DoS, nothing.
I'm thinking I need to briefly check if `aql_disable` is used anywhere else in the code that could be affected, but based on what I see here this is very unlikely to be an issue.
|
| 23/2 |
2026/05/16 07:35 |
tool |
grepper |
0m
Args: map[Expression:aql_disable]
Results: map[Output:net/mac80211/debugfs.c=308=static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
--
net/mac80211/debugfs.c-314- len = scnprintf(buf, sizeof(buf), "%d\n",
net/mac80211/debugfs.c:315: !static_key_false(&aql_disable.key));
net/mac80211/debugfs.c-316-
--
net/mac80211/debugfs.c=320=static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
--
net/mac80211/debugfs.c-322-{
net/mac80211/debugfs.c:323: bool aql_disabled = static_key_false(&aql_disable.key);
net/mac80211/debugfs.c-324- char buf[3];
--
net/mac80211/debugfs.c-338- if (buf[0] == '0' && buf[1] == '\0') {
net/mac80211/debugfs.c:339: if (!aql_disabled)
net/mac80211/debugfs.c:340: static_branch_inc(&aql_disable);
net/mac80211/debugfs.c-341- } else if (buf[0] == '1' && buf[1] == '\0') {
net/mac80211/debugfs.c:342: if (aql_disabled)
net/mac80211/debugfs.c:343: static_branch_dec(&aql_disable);
net/mac80211/debugfs.c-344- } else {
--
net/mac80211/ieee80211_i.h=1397=enum mac80211_scan_state {
--
net/mac80211/ieee80211_i.h-1405-
net/mac80211/ieee80211_i.h:1406:DECLARE_STATIC_KEY_FALSE(aql_disable);
net/mac80211/ieee80211_i.h-1407-
--
net/mac80211/tx.c=4149=EXPORT_SYMBOL(__ieee80211_schedule_txq);
net/mac80211/tx.c-4150-
net/mac80211/tx.c:4151:DEFINE_STATIC_KEY_FALSE(aql_disable);
net/mac80211/tx.c-4152-
net/mac80211/tx.c=4153=bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
--
net/mac80211/tx.c-4161-
net/mac80211/tx.c:4162: if (static_branch_unlikely(&aql_disable))
net/mac80211/tx.c-4163- return true;
]
|
| 24/2 |
2026/05/16 07:35 |
llm |
expert |
0m
Model:
Tokens:
input: 21008
output: 66
thoughts: 912
Thoughts: **Analysis of the AQL Enable/Disable Race Condition**
Okay, so I've been looking at this AQL (Airtime Queue Limits) disable mechanism and there's a potential race condition in the debugfs interface. The core of the problem seems to be with this `aql_disable` static key. I see that `aql_disable` is essentially a gate to enable or disable AQL, managed through a static key, and if the threads mess with the key it gets disabled or enabled incorrectly. Since `__static_key_slow_dec_cpuslocked` prevents underflow, if things go wrong the value just sticks at 0.
Let's break it down. If multiple threads concurrently try to decrement the static key to enable AQL, one succeeds, and the other hits a warning, which prevents further modification of the static key's value, which remains at 0, thus enabling AQL.
But if they are disabling AQL (incrementing the key), the threads can race and increment it multiple times. That means the key can end up greater than 1, so multiple "enables" are needed before AQL can be re-enabled. Fortunately, the check `aql_disabled` is there before incrementing/decrementing.
If two threads concurrently write "0" (disable AQL): both see AQL enabled. Then both threads increment `aql_disable`, leading to a value of 2. Then, when a thread writes "1" (enable AQL), it *thinks* AQL is disabled (due to the check `aql_disabled`), decrements the static key to 1, and the code keeps AQL disabled. This means an extra "1" write is needed to actually enable AQL.
So the bug, a functional flaw, is a race condition. Multiple threads can write to the debugfs file concurrently, leading to the `aql_disable` static key getting out of sync. It is a debug interface that is root-only.
Thankfully, from what I can see, this isn't a security issue. It's confined to a root-accessible debug interface. It's just a functional bug; there's no way this would lead to memory corruption, privilege escalation, or any kind of denial-of-service, or remote vectors, for that matter. No VM or filesystem vectors either. It's a race condition resulting in the need to write "1" multiple times to re-enable AQL.
|
| 25/2 |
2026/05/16 07:35 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 26/2 |
2026/05/16 07:35 |
llm |
expert |
0m
Model:
Tokens:
input: 22582
output: 578
thoughts: 428
Thoughts: **Final Thoughts on the Bug Report**
Okay, the analysis is complete, and it's time to craft the final response. My goal is to be crystal clear about the implications of this finding.
First, I need to concisely explain the bug itself. It's a race condition. Specifically, the vulnerability resides within `aql_enable_write` in `net/mac80211/debugfs.c`. It boils down to multiple threads concurrently writing to the `aql_enable` debugfs file. Due to this concurrency, they can end up reading the same stale value of `aql_disabled`, both then attempting to call `static_branch_dec(&aql_disable)`. The second of those calls is the culprit, because it tries to decrement the static key below zero, which kicks off a `WARN_ON_ONCE(val == 0)` call in `__static_key_slow_dec_cpuslocked`.
Now, onto exploitability. This is crucial: the bug is *not* exploitable in a meaningful way. The beauty (and frustration) of this specific function, `__static_key_slow_dec_cpuslocked`, is that it checks for a value of zero *before* decrementing. So, no underflow actually happens. The only tangible result is a warning in the kernel logs and, potentially, a mismatch in the internal state of the static key. Essentially, it's a minor functional hiccup within a debug interface. There's no memory corruption, no possibility of privilege escalation, absolutely nothing that puts the system at serious risk.
The same goes for a denial-of-service attack. The warning does *not* crash the kernel. The function gracefully handles the unexpected condition; the worst-case scenario is some slightly incorrect state.
Finally, accessibility. This is a critical point that further limits its impact. The `aql_enable` file is created with 0600 permissions within debugfs, meaning it's only readable and writable by the root user. Debugfs is typically mounted at `/sys/kernel/debug` with restricted permissions. On top of that, debugfs can't be mounted in unprivileged user namespaces. So, a regular user, or anything running inside a user namespace, can't trigger this bug.
Essentially, this is a minor race condition in debug code that's already protected behind root privileges, and has no direct exploitation path. The analysis is done; the report is closed.
|
| 27/1 |
2026/05/16 07:35 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug Report**
**1. Bug Mechanism:**
The bug is a race condition in the `aql_enable_write` function located in
`net/mac80211/debugfs.c`. This function handles writes to the `aql_enable`
debugfs file, which toggles the `aql_disable` static key.
The code reads the current state of the static key (`aql_disabled =
static_key_false(&aql_disable.key);`) and then conditionally increments or
decrements it based on the user input ('0' or '1'). If multiple threads
concurrently write '1' to this file, they can both read `aql_disabled` as `true`
and subsequently both call `static_branch_dec(&aql_disable)`. The second call
attempts to decrement the static key's reference count below 0, which triggers a
`WARN_ON_ONCE(val == 0)` in `__static_key_slow_dec_cpuslocked` (in
`kernel/jump_label.c`).
**2. Exploitability & Denial of Service:**
The bug is **not exploitable** and does **not cause a Denial of Service (DoS)**.
When the `WARN_ON_ONCE(val == 0)` is triggered in
`__static_key_slow_dec_cpuslocked`, the function immediately returns without
actually decrementing the atomic counter. This prevents any integer underflow or
memory corruption. The kernel continues to execute normally. The only
consequence is a kernel warning in the logs and a potential desynchronization of
the static key's state (requiring the user to write to the debugfs file again to
correct it), which is purely a minor functional glitch in a debug interface.
**3. Accessibility (Unprivileged & User Namespaces):**
The bug is **not accessible to unprivileged users** or from **user namespaces**.
The `aql_enable` file is exposed via debugfs (`/sys/kernel/debug/...`) and is
explicitly created with `0600` permissions (`DEBUGFS_ADD_MODE(aql_enable,
0600)`). Debugfs is a root-only interface that cannot be mounted or accessed by
unprivileged users, nor can it be mounted inside unprivileged user namespaces.
The syzkaller report confirms this by showing `UID: 0`, indicating it was
triggered by the root user.
**4. Other Triggers:**
The bug is strictly triggered by a local root user writing to a specific debugfs
file. It cannot be triggered via network traffic (Ethernet or other remote
vectors), malicious filesystems, peripheral devices, or from VM guest/host
contexts.]
|