| 0/0 |
2026/08/13 00:00 |
flow |
assessment-security |
34m
Results: map[DenialOfService:false Explanation:Based on the analysis of the provided bug report and kernel source code, here is
the assessment of the bug:
**Bug Mechanism:**
The bug is a KCSAN data-race report between `audit_log_start` and
`audit_receive`.
The race occurs on the `audit_cmd_mutex.owner` field:
1. **Write:** `audit_ctl_unlock()` sets `audit_cmd_mutex.owner = NULL;` when
releasing the audit control mutex.
2. **Read:** `audit_ctl_owner_current()` reads `audit_cmd_mutex.owner` to check
if it equals `current` (the currently executing task).
**Exploitability & Denial of Service:**
This is a classic benign data race in lock ownership checking.
- If `current` is the owner of the lock, it holds the lock, meaning no other
task can concurrently write to `audit_cmd_mutex.owner`.
- If `current` is *not* the owner, it does not hold the lock, so another task
might be concurrently writing to `owner` (e.g., setting it to `NULL` or to its
own task pointer). In this case, `current` will read either `NULL` or the other
task's pointer. In both cases, the read value will not equal `current`, so the
check correctly evaluates to `false`.
- Even in the astronomically unlikely event of reading a torn pointer that
happens to exactly match the `current` task pointer, the only consequence is
that `audit_log_start()` would skip waiting for the audit backlog limit and
proceed to allocate an audit buffer.
Therefore, this data race is completely benign. It cannot be exploited for
memory corruption, privilege escalation, or information leaks, nor can it cause
a denial of service.
**Accessibility:**
- **Unprivileged Processes:** Yes. The read side (`audit_log_start`) can be
triggered by unprivileged processes via seccomp filters (as seen in the stack
trace). The write side (`audit_receive`) can be triggered by unprivileged
processes by creating a `NETLINK_AUDIT` socket and sending a message to it.
While unprivileged users lack the capabilities to actually configure the audit
subsystem, the capability checks (`audit_netlink_ok`) happen *after* the
`audit_cmd_mutex` lock is acquired in `audit_receive()`.
- **User Namespaces:** Yes. Both seccomp and netlink sockets are accessible
within user namespaces.
- **Other Triggers:** The bug is a local kernel concurrency issue and is not
triggerable via VM guests/hosts, network traffic, remote connections,
peripherals, or malicious filesystems. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/13 00:00 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/13 00:00 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/0819932b3f903c5a520fe33cbda8d8afa49874c5]
|
| 3/1 |
2026/08/13 00:00 |
action |
kernel-builder |
20m
Results: map[KernelObj:/app/workdir/cache/build/2e67075ebcd80fa84f54c29f1121ef34365289e2]
|
| 4/1 |
2026/08/13 00:20 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/13 00:33 |
agent |
expert |
1m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
==================================================================
BUG: KCSAN: data-race in audit_log_start / audit_receive
write to 0xffffffff893dbb98 of 8 bytes by task 18825 on cpu 1:
audit_ctl_unlock kernel/audit.c:258 [inline]
audit_receive+0x1d2a/0x1ea0 kernel/audit.c:1627
netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
netlink_unicast+0x5a7/0x680 net/netlink/af_netlink.c:1345
netlink_sendmsg+0x5be/0x6f0 net/netlink/af_netlink.c:1900
sock_sendmsg_nosec net/socket.c:775 [inline]
__sock_sendmsg net/socket.c:790 [inline]
____sys_sendmsg+0x532/0x580 net/socket.c:2684
___sys_sendmsg+0x195/0x1e0 net/socket.c:2738
__sys_sendmsg net/socket.c:2770 [inline]
__do_sys_sendmsg net/socket.c:2775 [inline]
__se_sys_sendmsg net/socket.c:2773 [inline]
__x64_sys_sendmsg+0xd4/0x160 net/socket.c:2773
x64_sys_call+0x194c/0x3020 arch/x86/include/generated/asm/syscalls_64.h:47
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x136/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
read to 0xffffffff893dbb98 of 8 bytes by task 18823 on cpu 0:
audit_ctl_owner_current kernel/audit.c:271 [inline]
audit_log_start+0x103/0x730 kernel/audit.c:1933
audit_seccomp+0x49/0x110 kernel/auditsc.c:2948
seccomp_log kernel/seccomp.c:1048 [inline]
__seccomp_filter+0x121a/0x1370 kernel/seccomp.c:1376
__secure_computing+0x7d/0x150 kernel/seccomp.c:1404
syscall_trace_enter include/linux/entry-common.h:103 [inline]
syscall_enter_from_user_mode_work include/linux/entry-common.h:147 [inline]
syscall_enter_from_user_mode include/linux/entry-common.h:178 [inline]
do_syscall_64+0xd4/0x3c0 arch/x86/entry/syscall_64.c:89
entry_SYSCALL_64_after_hwframe+0x77/0x7f
value changed: 0xffff88810210e780 -> 0x0000000000000000
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 UID: 0 PID: 18823 Comm: syz.3.3476 Tainted: G W syzkaller #0 PREEMPT(lazy)
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
==================================================================
Reply: Based on the analysis of the provided bug report and kernel source code, here is the assessment of the bug:
**Bug Mechanism:**
The bug is a KCSAN data-race report between `audit_log_start` and `audit_receive`.
The race occurs on the `audit_cmd_mutex.owner` field:
1. **Write:** `audit_ctl_unlock()` sets `audit_cmd_mutex.owner = NULL;` when releasing the audit control mutex.
2. **Read:** `audit_ctl_owner_current()` reads `audit_cmd_mutex.owner` to check if it equals `current` (the currently executing task).
**Exploitability & Denial of Service:**
This is a classic benign data race in lock ownership checking.
- If `current` is the owner of the lock, it holds the lock, meaning no other task can concurrently write to `audit_cmd_mutex.owner`.
- If `current` is *not* the owner, it does not hold the lock, so another task might be concurrently writing to `owner` (e.g., setting it to `NULL` or to its own task pointer). In this case, `current` will read either `NULL` or the other task's pointer. In both cases, the read value will not equal `current`, so the check correctly evaluates to `false`.
- Even in the astronomically unlikely event of reading a torn pointer that happens to exactly match the `current` task pointer, the only consequence is that `audit_log_start()` would skip waiting for the audit backlog limit and proceed to allocate an audit buffer.
Therefore, this data race is completely benign. It cannot be exploited for memory corruption, privilege escalation, or information leaks, nor can it cause a denial of service.
**Accessibility:**
- **Unprivileged Processes:** Yes. The read side (`audit_log_start`) can be triggered by unprivileged processes via seccomp filters (as seen in the stack trace). The write side (`audit_receive`) can be triggered by unprivileged processes by creating a `NETLINK_AUDIT` socket and sending a message to it. While unprivileged users lack the capabilities to actually configure the audit subsystem, the capability checks (`audit_netlink_ok`) happen *after* the `audit_cmd_mutex` lock is acquired in `audit_receive()`.
- **User Namespaces:** Yes. Both seccomp and netlink sockets are accessible within user namespaces.
- **Other Triggers:** The bug is a local kernel concurrency issue and is not triggerable via VM guests/hosts, network traffic, remote connections, peripherals, or malicious filesystems.
|
| 6/2 |
2026/08/13 00:33 |
llm |
expert |
0m
Model:
Tokens:
input: 6700
output: 34
thoughts: 171
Thoughts: **Data Race Debugging in the Audit Subsystem**
Okay, so I've got a KCSAN data-race report that's caught my attention, specifically within the `audit_log_start` and `audit_receive` functions. This is potentially critical; data races are rarely benign, especially in security-sensitive areas like auditing. It looks like we have a write operation targeting memory address `0xffffffff893dbb98` of 8 bytes, performed by task 18825 on CPU 1. The stack trace points to `audit_ctl_unlock` at `kernel/audit.c:258` and ultimately to `audit_receive` at `kernel/audit.c:1627`. On the other hand, another task, 18823, on CPU 0, is reading from the *same* memory address, also 8 bytes. The call trace here goes through `audit_ctl_owner_current` at `kernel/audit.c:271` and then to `audit_log_start` at `kernel/audit.c:1933`. This tells me that `audit_log_start` is likely reading while `audit_receive` is in the process of writing to the same location. It's time to dig into the source code, specifically `kernel/audit.c`, and see what this shared memory region represents and how it is being accessed, or rather, not being properly synchronized. I suspect the shared memory involves control data, and now must inspect the lock strategy.
|
| 7/2 |
2026/08/13 00:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_ctl_unlock]
Results: map[SourceCode: 253: /**
254: * audit_ctl_unlock - Drop the audit control lock
255: */
256: void audit_ctl_unlock(void)
257: {
258: audit_cmd_mutex.owner = NULL;
259: mutex_unlock(&audit_cmd_mutex.lock);
260: }
SourceFile:kernel/audit.c]
|
| 8/2 |
2026/08/13 00:33 |
llm |
expert |
0m
Model:
Tokens:
input: 7371
output: 36
thoughts: 29
Thoughts: Here's a summary of my immediate thought process, as though I were analyzing this code:
**Analyzing Mutex Ownership**
Okay, so `audit_cmd_mutex.owner` is being written to, which likely means we're dealing with mutex ownership changes related to auditing commands. My first instinct is to immediately check the state of `audit_ctl_owner_current`. I need to understand the current owner's context relative to whatever is updating `audit_cmd_mutex.owner`. This variable likely holds crucial information about the current controller owner, and this could be an important point to understand the context. This allows me to cross-reference who should or shouldn't be making the change, and why.
|
| 9/2 |
2026/08/13 00:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_ctl_owner_current]
Results: map[SourceCode: 262: /**
263: * audit_ctl_owner_current - Test to see if the current task owns the lock
264: *
265: * Description:
266: * Return true if the current task owns the audit control lock, false if it
267: * doesn't own the lock.
268: */
269: static bool audit_ctl_owner_current(void)
270: {
271: return (current == audit_cmd_mutex.owner);
272: }
SourceFile:kernel/audit.c]
|
| 10/2 |
2026/08/13 00:33 |
llm |
expert |
0m
Model:
Tokens:
input: 7765
output: 34
thoughts: 62
Thoughts: **Analyzing a Potential Data Race**
Okay, so I'm looking at this code and immediately see a potential data race. The core issue seems to be a conflict between the line setting `audit_cmd_mutex.owner = NULL;` and a check that uses `current == audit_cmd_mutex.owner`. This is a pretty textbook example of a benign data race, specifically related to the lock ownership check.
My next step is to get a clearer picture of the context. I need to understand how `audit_log_start` fits into this. A deep dive into its usage should reveal the broader implications of this race condition, even if itβs benign, so I can accurately assess any possible problems within the system. Let's see what that function is doing.
|
| 11/2 |
2026/08/13 00:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_log_start]
Results: map[SourceCode:1898: /**
1899: * audit_log_start - obtain an audit buffer
1900: * @ctx: audit_context (may be NULL)
1901: * @gfp_mask: type of allocation
1902: * @type: audit message type
1903: *
1904: * Returns audit_buffer pointer on success or NULL on error.
1905: *
1906: * Obtain an audit buffer. This routine does locking to obtain the
1907: * audit buffer, but then no locking is required for calls to
1908: * audit_log_*format. If the task (ctx) is a task that is currently in a
1909: * syscall, then the syscall is marked as auditable and an audit record
1910: * will be written at syscall exit. If there is no associated task, then
1911: * task context (ctx) should be NULL.
1912: */
1913: struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
1914: int type)
1915: {
1916: struct audit_buffer *ab;
1917:
1918: if (audit_initialized != AUDIT_INITIALIZED)
1919: return NULL;
1920:
1921: if (unlikely(!audit_filter(type, AUDIT_FILTER_EXCLUDE)))
1922: return NULL;
1923:
1924: /* NOTE: don't ever fail/sleep on these two conditions:
1925: * 1. auditd generated record - since we need auditd to drain the
1926: * queue; also, when we are checking for auditd, compare PIDs using
1927: * task_tgid_vnr() since auditd_pid is set in audit_receive_msg()
1928: * using a PID anchored in the caller's namespace
1929: * 2. generator holding the audit_cmd_mutex - we don't want to block
1930: * while holding the mutex, although we do penalize the sender
1931: * later in audit_receive() when it is safe to block
1932: */
1933: if (!(auditd_test_task(current) || audit_ctl_owner_current())) {
1934: long stime = audit_backlog_wait_time;
1935:
1936: while (audit_backlog_limit &&
1937: (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
1938: /* wake kauditd to try and flush the queue */
1939: wake_up_interruptible(&kauditd_wait);
1940:
1941: /* sleep if we are allowed and we haven't exhausted our
1942: * backlog wait limit */
1943: if (gfpflags_allow_blocking(gfp_mask) && (stime > 0)) {
1944: long rtime = stime;
1945:
1946: DECLARE_WAITQUEUE(wait, current);
1947:
1948: add_wait_queue_exclusive(&audit_backlog_wait,
1949: &wait);
1950: set_current_state(TASK_UNINTERRUPTIBLE);
1951: stime = schedule_timeout(rtime);
1952: atomic_add(rtime - stime, &audit_backlog_wait_time_actual);
1953: remove_wait_queue(&audit_backlog_wait, &wait);
1954: } else {
1955: if (audit_rate_check() && printk_ratelimit())
1956: pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n",
1957: skb_queue_len_lockless(&audit_queue),
1958: audit_backlog_limit);
1959: audit_log_lost("backlog limit exceeded");
1960: return NULL;
1961: }
1962: }
1963: }
1964:
1965: ab = audit_buffer_alloc(ctx, gfp_mask, type);
1966: if (!ab) {
1967: audit_log_lost("out of memory in audit_log_start");
1968: return NULL;
1969: }
1970:
1971: audit_get_stamp(ab->ctx, &ab->stamp);
1972: /* cancel dummy context to enable supporting records */
1973: if (ctx)
1974: ctx->dummy = 0;
1975: audit_log_format(ab, "audit(%llu.%03lu:%u): ",
1976: (unsigned long long)ab->stamp.ctime.tv_sec,
1977: ab->stamp.ctime.tv_nsec/1000000,
1978: ab->stamp.serial);
1979:
1980: return ab;
1981: }
SourceFile:kernel/audit.c]
|
| 12/2 |
2026/08/13 00:33 |
llm |
expert |
0m
Model:
Tokens:
input: 9446
output: 32
thoughts: 455
Thoughts: **Analyzing the Benign Data Race in `audit_ctl_owner_current()`**
Okay, so I'm looking at `audit_ctl_owner_current()` and how it interacts with the `audit_cmd_mutex.owner`. The core of the problem is this read: `current == audit_cmd_mutex.owner`. If `current` *isn't* the owner, there's a possibility I'm reading a stale or even a garbage value β potentially even NULL β because another task could be modifying `audit_cmd_mutex.owner` while I'm reading it. If `current` *is* the owner, then by definition, I hold the lock, and no other task can be modifying that variable. The only write is `audit_cmd_mutex.owner = NULL` or `audit_cmd_mutex.owner = current`, right?
Wait, so if I *am* the owner and hold the lock, there's no race condition here, because I'm the only one who can touch that variable. The potential race is when *I'm not* the owner. If I'm not the owner, another task, *the one with the lock*, is free to write `audit_cmd_mutex.owner`. They can set it to NULL when they're unlocking, or they can set it to themselves when they're locking. So, in that case, `current` would read either NULL or the other task's pointer and the comparison would return `false`.
But, what about a torn read? Could I read a partially written pointer that happens to match `current`? I doubt it. Pointers are 8 bytes on 64-bit systems, and those aligned 8-byte reads and writes are guaranteed to be atomic. Even if they weren't, the odds of a partially written pointer happening to coincidentally equal my `current` pointer are basically astronomical. So, barring any bizarre hardware issues, this is a completely benign data race.
Now, let's step back and consider: Is this exploitable at all? Does it lead to anything remotely interesting? Nope, it's still a benign race. Can I cause a denial-of-service? Still a benign data race. Can unprivileged users trigger this race? Okay, here's where it gets slightly more interesting. `audit_log_start` can be triggered by seccomp, and seccomp is accessible to unprivileged processes. Also `audit_receive` is triggered by netlink messages to the audit subsystem. Unprivileged users can send those messages, though they may get rejected later. But regardless, that read is still a benign race. Let me investigate `audit_receive` anyway.
|
| 13/2 |
2026/08/13 00:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_receive]
Results: map[SourceCode:1594: /**
1595: * audit_receive - receive messages from a netlink control socket
1596: * @skb: the message buffer
1597: *
1598: * Parse the provided skb and deal with any messages that may be present,
1599: * malformed skbs are discarded.
1600: */
1601: static void audit_receive(struct sk_buff *skb)
1602: {
1603: struct nlmsghdr *nlh;
1604: bool ack;
1605: /*
1606: * len MUST be signed for nlmsg_next to be able to dec it below 0
1607: * if the nlmsg_len was not aligned
1608: */
1609: int len;
1610: int err;
1611:
1612: nlh = nlmsg_hdr(skb);
1613: len = skb->len;
1614:
1615: audit_ctl_lock();
1616: while (nlmsg_ok(nlh, len)) {
1617: ack = nlh->nlmsg_flags & NLM_F_ACK;
1618: err = audit_receive_msg(skb, nlh, &ack);
1619:
1620: /* send an ack if the user asked for one and audit_receive_msg
1621: * didn't already do it, or if there was an error. */
1622: if (ack || err)
1623: netlink_ack(skb, nlh, err, NULL);
1624:
1625: nlh = nlmsg_next(nlh, &len);
1626: }
1627: audit_ctl_unlock();
1628:
1629: /* can't block with the ctrl lock, so penalize the sender now */
1630: if (audit_backlog_limit &&
1631: (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
1632: DECLARE_WAITQUEUE(wait, current);
1633:
1634: /* wake kauditd to try and flush the queue */
1635: wake_up_interruptible(&kauditd_wait);
1636:
1637: add_wait_queue_exclusive(&audit_backlog_wait, &wait);
1638: set_current_state(TASK_UNINTERRUPTIBLE);
1639: schedule_timeout(audit_backlog_wait_time);
1640: remove_wait_queue(&audit_backlog_wait, &wait);
1641: }
1642: }
SourceFile:kernel/audit.c]
|
| 14/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 11287
output: 34
thoughts: 61
Thoughts: **My Audit System Musings**
Okay, so I'm looking at this `audit_receive` function, and it's pretty straightforward. It grabs that `audit_ctl_lock()`, does its message handling, and then releases the lock with `audit_ctl_unlock()`. Classic locking pattern. But then I started thinking about the implications of who can send those messages. Unprivileged users *can* send them to the audit netlink socket, which raises a flag. I need to dig deeper. My immediate thought is, can an unprivileged user even *open* the audit netlink socket in the first place? That's the crucial first step. If they can't even get that far, it simplifies things significantly. Time to investigate that access path.
|
| 15/2 |
2026/08/13 00:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_receive_msg]
Results: map[SourceCode:1255: static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
1256: bool *ack)
1257: {
1258: u32 seq;
1259: void *data;
1260: int data_len;
1261: int err;
1262: struct audit_buffer *ab;
1263: u16 msg_type = nlh->nlmsg_type;
1264: struct audit_sig_info *sig_data;
1265: struct lsm_context lsmctx = { NULL, 0, 0 };
1266:
1267: err = audit_netlink_ok(skb, msg_type);
1268: if (err)
1269: return err;
1270:
1271: seq = nlh->nlmsg_seq;
1272: data = nlmsg_data(nlh);
1273: data_len = nlmsg_len(nlh);
1274:
1275: switch (msg_type) {
1276: case AUDIT_GET: {
1277: struct audit_status s;
1278: memset(&s, 0, sizeof(s));
1279: s.enabled = audit_enabled;
1280: s.failure = audit_failure;
1281: /* NOTE: use pid_vnr() so the PID is relative to the current
1282: * namespace */
1283: s.pid = auditd_pid_vnr();
1284: s.rate_limit = audit_rate_limit;
1285: s.backlog_limit = audit_backlog_limit;
1286: s.lost = atomic_read(&audit_lost);
1287: s.backlog = skb_queue_len_lockless(&audit_queue);
1288: s.feature_bitmap = AUDIT_FEATURE_BITMAP_ALL;
1289: s.backlog_wait_time = audit_backlog_wait_time;
1290: s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual);
1291: audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &s, sizeof(s));
1292: break;
1293: }
1294: case AUDIT_SET: {
1295: struct audit_status s;
1296: memset(&s, 0, sizeof(s));
1297: /* guard against past and future API changes */
1298: memcpy(&s, data, min_t(size_t, sizeof(s), data_len));
1299: if (s.mask & ~AUDIT_STATUS_ALL)
1300: return -EINVAL;
1301: if (s.mask & AUDIT_STATUS_ENABLED) {
1302: err = audit_set_enabled(s.enabled);
1303: if (err < 0)
1304: return err;
1305: }
1306: if (s.mask & AUDIT_STATUS_FAILURE) {
1307: err = audit_set_failure(s.failure);
1308: if (err < 0)
1309: return err;
1310: }
1311: if (s.mask & AUDIT_STATUS_PID) {
1312: /* NOTE: we are using the vnr PID functions below
1313: * because the s.pid value is relative to the
1314: * namespace of the caller; at present this
1315: * doesn't matter much since you can really only
1316: * run auditd from the initial pid namespace, but
1317: * something to keep in mind if this changes */
1318: pid_t new_pid = s.pid;
1319: pid_t auditd_pid;
1320: struct pid *req_pid = task_tgid(current);
1321:
1322: /* Sanity check - PID values must match. Setting
1323: * pid to 0 is how auditd ends auditing. */
1324: if (new_pid && (new_pid != pid_vnr(req_pid)))
1325: return -EINVAL;
1326:
1327: /* test the auditd connection */
1328: audit_replace(req_pid);
1329:
1330: auditd_pid = auditd_pid_vnr();
1331: if (auditd_pid) {
1332: /* replacing a healthy auditd is not allowed */
1333: if (new_pid) {
1334: audit_log_config_change("audit_pid",
1335: new_pid, auditd_pid, 0);
1336: return -EEXIST;
1337: }
1338: /* only current auditd can unregister itself */
1339: if (pid_vnr(req_pid) != auditd_pid) {
1340: audit_log_config_change("audit_pid",
1341: new_pid, auditd_pid, 0);
1342: return -EACCES;
1343: }
1344: }
1345:
1346: if (new_pid) {
1347: /* register a new auditd connection */
1348: err = auditd_set(req_pid,
1349: NETLINK_CB(skb).portid,
1350: sock_net(NETLINK_CB(skb).sk),
1351: skb, ack);
1352: if (audit_enabled != AUDIT_OFF)
1353: audit_log_config_change("audit_pid",
1354: new_pid,
1355: auditd_pid,
1356: err ? 0 : 1);
1357: if (err)
1358: return err;
1359:
1360: /* try to process any backlog */
1361: wake_up_interruptible(&kauditd_wait);
1362: } else {
1363: if (audit_enabled != AUDIT_OFF)
1364: audit_log_config_change("audit_pid",
1365: new_pid,
1366: auditd_pid, 1);
1367:
1368: /* unregister the auditd connection */
1369: auditd_reset(NULL);
1370: }
1371: }
1372: if (s.mask & AUDIT_STATUS_RATE_LIMIT) {
1373: err = audit_set_rate_limit(s.rate_limit);
1374: if (err < 0)
1375: return err;
1376: }
1377: if (s.mask & AUDIT_STATUS_BACKLOG_LIMIT) {
1378: err = audit_set_backlog_limit(s.backlog_limit);
1379: if (err < 0)
1380: return err;
1381: }
1382: if (s.mask & AUDIT_STATUS_BACKLOG_WAIT_TIME) {
1383: if (sizeof(s) > (size_t)nlh->nlmsg_len)
1384: return -EINVAL;
1385: if (s.backlog_wait_time > 10*AUDIT_BACKLOG_WAIT_TIME)
1386: return -EINVAL;
1387: err = audit_set_backlog_wait_time(s.backlog_wait_time);
1388: if (err < 0)
1389: return err;
1390: }
1391: if (s.mask == AUDIT_STATUS_LOST) {
1392: u32 lost = atomic_xchg(&audit_lost, 0);
1393:
1394: audit_log_config_change("lost", 0, lost, 1);
1395: return lost;
1396: }
1397: if (s.mask == AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL) {
1398: u32 actual = atomic_xchg(&audit_backlog_wait_time_actual, 0);
1399:
1400: audit_log_config_change("backlog_wait_time_actual", 0, actual, 1);
1401: return actual;
1402: }
1403: break;
1404: }
1405: case AUDIT_GET_FEATURE:
1406: err = audit_get_feature(skb);
1407: if (err)
1408: return err;
1409: break;
1410: case AUDIT_SET_FEATURE:
1411: if (data_len < sizeof(struct audit_features))
1412: return -EINVAL;
1413: err = audit_set_feature(data);
1414: if (err)
1415: return err;
1416: break;
1417: case AUDIT_USER:
1418: case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
1419: case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
1420: if (!audit_enabled && msg_type != AUDIT_USER_AVC)
1421: return 0;
1422: /* exit early if there isn't at least one character to print */
1423: if (data_len < 2)
1424: return -EINVAL;
1425:
1426: err = audit_filter(msg_type, AUDIT_FILTER_USER);
1427: if (err == 1) { /* match or error */
1428: char *str = data;
1429:
1430: err = 0;
1431: if (msg_type == AUDIT_USER_TTY) {
1432: err = tty_audit_push();
1433: if (err)
1434: break;
1435: }
1436: audit_log_user_recv_msg(&ab, msg_type);
1437: if (msg_type != AUDIT_USER_TTY) {
1438: /* ensure NULL termination */
1439: str[data_len - 1] = '\0';
1440: audit_log_format(ab, " msg='%.*s'",
1441: AUDIT_MESSAGE_TEXT_MAX,
1442: str);
1443: } else {
1444: audit_log_format(ab, " data=");
1445: if (str[data_len - 1] == '\0')
1446: data_len--;
1447: audit_log_n_untrustedstring(ab, str, data_len);
1448: }
1449: audit_log_end(ab);
1450: }
1451: break;
1452: case AUDIT_ADD_RULE:
1453: case AUDIT_DEL_RULE:
1454: if (data_len < sizeof(struct audit_rule_data))
1455: return -EINVAL;
1456: if (audit_enabled == AUDIT_LOCKED) {
1457: audit_log_common_recv_msg(audit_context(), &ab,
1458: AUDIT_CONFIG_CHANGE);
1459: audit_log_format(ab, " op=%s audit_enabled=%d res=0",
1460: msg_type == AUDIT_ADD_RULE ?
1461: "add_rule" : "remove_rule",
1462: audit_enabled);
1463: audit_log_end(ab);
1464: return -EPERM;
1465: }
1466: err = audit_rule_change(msg_type, seq, data, data_len);
1467: break;
1468: case AUDIT_LIST_RULES:
1469: err = audit_list_rules_send(skb, seq);
1470: break;
1471: case AUDIT_TRIM:
1472: if (audit_enabled == AUDIT_LOCKED)
1473: return -EPERM;
1474: audit_trim_trees();
1475: audit_log_common_recv_msg(audit_context(), &ab,
1476: AUDIT_CONFIG_CHANGE);
1477: audit_log_format(ab, " op=trim res=1");
1478: audit_log_end(ab);
1479: break;
1480: case AUDIT_MAKE_EQUIV: {
1481: void *bufp = data;
1482: u32 sizes[2];
1483: size_t msglen = data_len;
1484: char *old, *new;
1485:
1486: if (audit_enabled == AUDIT_LOCKED)
1487: return -EPERM;
1488: err = -EINVAL;
1489: if (msglen < 2 * sizeof(u32))
1490: break;
1491: memcpy(sizes, bufp, 2 * sizeof(u32));
1492: bufp += 2 * sizeof(u32);
1493: msglen -= 2 * sizeof(u32);
1494: old = audit_unpack_string(&bufp, &msglen, sizes[0]);
1495: if (IS_ERR(old)) {
1496: err = PTR_ERR(old);
1497: break;
1498: }
1499: new = audit_unpack_string(&bufp, &msglen, sizes[1]);
1500: if (IS_ERR(new)) {
1501: err = PTR_ERR(new);
1502: kfree(old);
1503: break;
1504: }
1505: /* OK, here comes... */
1506: err = audit_tag_tree(old, new);
1507:
1508: audit_log_common_recv_msg(audit_context(), &ab,
1509: AUDIT_CONFIG_CHANGE);
1510: audit_log_format(ab, " op=make_equiv old=");
1511: audit_log_untrustedstring(ab, old);
1512: audit_log_format(ab, " new=");
1513: audit_log_untrustedstring(ab, new);
1514: audit_log_format(ab, " res=%d", !err);
1515: audit_log_end(ab);
1516: kfree(old);
1517: kfree(new);
1518: break;
1519: }
1520: case AUDIT_SIGNAL_INFO:
1521: if (lsmprop_is_set(&audit_sig_lsm)) {
1522: err = security_lsmprop_to_secctx(&audit_sig_lsm,
1523: &lsmctx, LSM_ID_UNDEF);
1524: if (err < 0)
1525: return err;
1526: }
1527: sig_data = kmalloc_flex(*sig_data, ctx, lsmctx.len);
1528: if (!sig_data) {
1529: if (lsmprop_is_set(&audit_sig_lsm))
1530: security_release_secctx(&lsmctx);
1531: return -ENOMEM;
1532: }
1533: sig_data->uid = from_kuid(&init_user_ns, audit_sig_uid);
1534: sig_data->pid = audit_sig_pid;
1535: if (lsmprop_is_set(&audit_sig_lsm)) {
1536: memcpy(sig_data->ctx, lsmctx.context, lsmctx.len);
1537: security_release_secctx(&lsmctx);
1538: }
1539: audit_send_reply(skb, seq, AUDIT_SIGNAL_INFO, 0, 0,
1540: sig_data, struct_size(sig_data, ctx,
1541: lsmctx.len));
1542: kfree(sig_data);
1543: break;
1544: case AUDIT_TTY_GET: {
1545: struct audit_tty_status s;
1546: unsigned int t;
1547:
1548: t = READ_ONCE(current->signal->audit_tty);
1549: s.enabled = t & AUDIT_TTY_ENABLE;
1550: s.log_passwd = !!(t & AUDIT_TTY_LOG_PASSWD);
1551:
1552: audit_send_reply(skb, seq, AUDIT_TTY_GET, 0, 0, &s, sizeof(s));
1553: break;
1554: }
1555: case AUDIT_TTY_SET: {
1556: struct audit_tty_status s, old;
1557: struct audit_buffer *ab;
1558: unsigned int t;
1559:
1560: memset(&s, 0, sizeof(s));
1561: /* guard against past and future API changes */
1562: memcpy(&s, data, min_t(size_t, sizeof(s), data_len));
1563: /* check if new data is valid */
1564: if ((s.enabled != 0 && s.enabled != 1) ||
1565: (s.log_passwd != 0 && s.log_passwd != 1))
1566: err = -EINVAL;
1567:
1568: if (err)
1569: t = READ_ONCE(current->signal->audit_tty);
1570: else {
1571: t = s.enabled | (-s.log_passwd & AUDIT_TTY_LOG_PASSWD);
1572: t = xchg(¤t->signal->audit_tty, t);
1573: }
1574: old.enabled = t & AUDIT_TTY_ENABLE;
1575: old.log_passwd = !!(t & AUDIT_TTY_LOG_PASSWD);
1576:
1577: audit_log_common_recv_msg(audit_context(), &ab,
1578: AUDIT_CONFIG_CHANGE);
1579: audit_log_format(ab, " op=tty_set old-enabled=%d new-enabled=%d"
1580: " old-log_passwd=%d new-log_passwd=%d res=%d",
1581: old.enabled, s.enabled, old.log_passwd,
1582: s.log_passwd, !err);
1583: audit_log_end(ab);
1584: break;
1585: }
1586: default:
1587: err = -EINVAL;
1588: break;
1589: }
1590:
1591: return err < 0 ? err : 0;
1592: }
SourceFile:kernel/audit.c]
|
| 16/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 17007
output: 35
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Checking `audit_netlink_ok`**
Alright, time to get to it. First, I need to check `audit_netlink_ok`. This is a critical function, and a fundamental building block in understanding how the audit subsystem in the kernel works. Before I proceed, I need to know the state of this function. Is it set up and good to go or am I dealing with a problem? The logic contained in `audit_netlink_ok` governs a crucial aspect of the system, so a deep understanding of its present state is essential. Given what I'm looking at, I need to determine if it's currently blocking something, or if it's clear for me to proceed.
|
| 17/2 |
2026/08/13 00:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_netlink_ok]
Results: map[SourceCode:1068: /*
1069: * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
1070: * control messages.
1071: */
1072: static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
1073: {
1074: int err = 0;
1075:
1076: /* Only support initial user namespace for now. */
1077: /*
1078: * We return ECONNREFUSED because it tricks userspace into thinking
1079: * that audit was not configured into the kernel. Lots of users
1080: * configure their PAM stack (because that's what the distro does)
1081: * to reject login if unable to send messages to audit. If we return
1082: * ECONNREFUSED the PAM stack thinks the kernel does not have audit
1083: * configured in and will let login proceed. If we return EPERM
1084: * userspace will reject all logins. This should be removed when we
1085: * support non init namespaces!!
1086: */
1087: if (current_user_ns() != &init_user_ns)
1088: return -ECONNREFUSED;
1089:
1090: switch (msg_type) {
1091: case AUDIT_LIST:
1092: case AUDIT_ADD:
1093: case AUDIT_DEL:
1094: return -EOPNOTSUPP;
1095: case AUDIT_GET:
1096: case AUDIT_SET:
1097: case AUDIT_GET_FEATURE:
1098: case AUDIT_SET_FEATURE:
1099: case AUDIT_LIST_RULES:
1100: case AUDIT_ADD_RULE:
1101: case AUDIT_DEL_RULE:
1102: case AUDIT_SIGNAL_INFO:
1103: case AUDIT_TTY_GET:
1104: case AUDIT_TTY_SET:
1105: case AUDIT_TRIM:
1106: case AUDIT_MAKE_EQUIV:
1107: /* Only support auditd and auditctl in initial pid namespace
1108: * for now. */
1109: if (task_active_pid_ns(current) != &init_pid_ns)
1110: return -EPERM;
1111:
1112: if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
1113: err = -EPERM;
1114: break;
1115: case AUDIT_USER:
1116: case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
1117: case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
1118: if (!netlink_capable(skb, CAP_AUDIT_WRITE))
1119: err = -EPERM;
1120: break;
1121: default: /* bad msg */
1122: err = -EINVAL;
1123: }
1124:
1125: return err;
1126: }
SourceFile:kernel/audit.c]
|
| 18/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 18151
output: 35
thoughts: 957
Thoughts: **Data Race in Audit Subsystem Analysis**
Okay, so I've been digging into this `audit_receive` function. It looks like it's called *before* `audit_receive_msg`, which immediately flags something. Let's recap what I see in `audit_receive`: `audit_ctl_lock()` is called unconditionally on netlink message receipt, regardless of any capabilities. The crucial thing is that this lock is taken *before* the capability checks happen in `audit_receive_msg` (specifically through `audit_netlink_ok`).
This means an unprivileged user can indeed trigger the lock! They can create a netlink socket, send a message to the audit socket, and bingo β `audit_ctl_lock()` gets called. And, of course, `audit_log_start` can be triggered via seccomp, which is accessible to unprivileged processes.
Now, a key question: user namespaces? Yep, seccomp and netlink are both accessible there. But wait, `audit_netlink_ok` has a check, preventing access if `current_user_ns()` is not `init_user_ns`. But I already established that `audit_receive` takes the lock *before* `audit_receive_msg`, which is where `audit_netlink_ok` is called. So, the data race *can* be triggered from within user namespaces.
Now, is this exploitable? Let's zoom in on the data race itself. We have `audit_ctl_owner_current()` returning if `current == audit_cmd_mutex.owner`, and then `audit_ctl_unlock()` setting `audit_cmd_mutex.owner = NULL`. If `current` isn't the owner, then `audit_ctl_owner_current` will return false. What if the read of `audit_cmd_mutex.owner` is torn and happens to equal `current`? But even in this highly unlikely scenario, it just means that `audit_log_start` will skip the backlog limit check and allocate a buffer, which is harmless, not exploitable.
So, in summary: This isn't exploitable. It's a benign data race. It's also not a denial-of-service, as KCSAN just flagged the race, and it does not result in a crash or leak resources. It *is* accessible from unprivileged processes, and from user namespaces. It's not a guest or host trigger or related to ethernet or other remote, peripheral or malicious filesystem triggers.
Finally, just to confirm, a quick stack trace shows that `audit_log_start` can indeed be triggered via seccomp, so there is confirmation of this trigger path.
I'll now verify the confirmation of netlink socket creation as part of unprivileged access to this bug.
|
| 19/2 |
2026/08/13 00:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/audit.c Name:audit_netlink_ok]
Results: map[SourceCode:1068: /*
1069: * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
1070: * control messages.
1071: */
1072: static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
1073: {
1074: int err = 0;
1075:
1076: /* Only support initial user namespace for now. */
1077: /*
1078: * We return ECONNREFUSED because it tricks userspace into thinking
1079: * that audit was not configured into the kernel. Lots of users
1080: * configure their PAM stack (because that's what the distro does)
1081: * to reject login if unable to send messages to audit. If we return
1082: * ECONNREFUSED the PAM stack thinks the kernel does not have audit
1083: * configured in and will let login proceed. If we return EPERM
1084: * userspace will reject all logins. This should be removed when we
1085: * support non init namespaces!!
1086: */
1087: if (current_user_ns() != &init_user_ns)
1088: return -ECONNREFUSED;
1089:
1090: switch (msg_type) {
1091: case AUDIT_LIST:
1092: case AUDIT_ADD:
1093: case AUDIT_DEL:
1094: return -EOPNOTSUPP;
1095: case AUDIT_GET:
1096: case AUDIT_SET:
1097: case AUDIT_GET_FEATURE:
1098: case AUDIT_SET_FEATURE:
1099: case AUDIT_LIST_RULES:
1100: case AUDIT_ADD_RULE:
1101: case AUDIT_DEL_RULE:
1102: case AUDIT_SIGNAL_INFO:
1103: case AUDIT_TTY_GET:
1104: case AUDIT_TTY_SET:
1105: case AUDIT_TRIM:
1106: case AUDIT_MAKE_EQUIV:
1107: /* Only support auditd and auditctl in initial pid namespace
1108: * for now. */
1109: if (task_active_pid_ns(current) != &init_pid_ns)
1110: return -EPERM;
1111:
1112: if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
1113: err = -EPERM;
1114: break;
1115: case AUDIT_USER:
1116: case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
1117: case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
1118: if (!netlink_capable(skb, CAP_AUDIT_WRITE))
1119: err = -EPERM;
1120: break;
1121: default: /* bad msg */
1122: err = -EINVAL;
1123: }
1124:
1125: return err;
1126: }
SourceFile:kernel/audit.c]
|
| 20/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 20665
output: 39
thoughts: 34
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating `NETLINK_AUDIT` Socket Creation**
Hmm, this is an interesting question. I'm wondering if a non-root user can actually create `NETLINK_AUDIT` sockets. That feels like it could potentially open a security hole, doesn't it? Let's get to the bottom of this. The place to start would be in the core `netlink` implementation, specifically `net/netlink/af_netlink.c`. I'm going to dive right into the code and see what access controls are defined around the socket creation process for this particular family. I'll be looking for any checks around permissions, capabilities, or other constraints. If it's a simple `socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT)` call, it'll be quick to find the socket creation code and the relevant security checks that get performed, if any. Let's see what's what.
|
| 21/2 |
2026/08/13 00:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/netlink/af_netlink.c Name:netlink_create]
Results: map[SourceCode: 645: static int netlink_create(struct net *net, struct socket *sock, int protocol,
646: int kern)
647: {
648: struct module *module = NULL;
649: struct netlink_sock *nlk;
650: int (*bind)(struct net *net, int group);
651: void (*unbind)(struct net *net, int group);
652: void (*release)(struct sock *sock, unsigned long *groups);
653: int err = 0;
654:
655: sock->state = SS_UNCONNECTED;
656:
657: if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
658: return -ESOCKTNOSUPPORT;
659:
660: if (protocol < 0 || protocol >= MAX_LINKS)
661: return -EPROTONOSUPPORT;
662: protocol = array_index_nospec(protocol, MAX_LINKS);
663:
664: netlink_lock_table();
665: #ifdef CONFIG_MODULES
666: if (!nl_table[protocol].registered) {
667: netlink_unlock_table();
668: request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol);
669: netlink_lock_table();
670: }
671: #endif
672: if (nl_table[protocol].registered &&
673: try_module_get(nl_table[protocol].module))
674: module = nl_table[protocol].module;
675: else
676: err = -EPROTONOSUPPORT;
677: bind = nl_table[protocol].bind;
678: unbind = nl_table[protocol].unbind;
679: release = nl_table[protocol].release;
680: netlink_unlock_table();
681:
682: if (err < 0)
683: goto out;
684:
685: err = __netlink_create(net, sock, protocol, kern);
686: if (err < 0)
687: goto out_module;
688:
689: sock_prot_inuse_add(net, &netlink_proto, 1);
690:
691: nlk = nlk_sk(sock->sk);
692: nlk->module = module;
693: nlk->netlink_bind = bind;
694: nlk->netlink_unbind = unbind;
695: nlk->netlink_release = release;
696: out:
697: return err;
698:
699: out_module:
700: module_put(module);
701: goto out;
702: }
SourceFile:net/netlink/af_netlink.c]
|
| 22/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 21829
output: 66
thoughts: 385
Thoughts: **My Assessment of the Audit Netlink Data Race**
Okay, so here's what I've got after digging into this: it looks like there's a data race in the audit netlink subsystem. The vulnerability is in the way the `audit_ctl_unlock` function can modify the `audit_cmd_mutex.owner` field concurrently with reads done in `audit_ctl_owner_current`. This race is triggered by unprivileged users, specifically through `seccomp` and netlink access.
Here's the breakdown, as I understand it:
* **No Protocol Check:** The initial problem starts because there's no capability check specifically within `netlink_create` for particular protocols. Thus anyone can make a `NETLINK_AUDIT` socket.
* **Message Handler Protection:** While the capability checks are done inside `audit_receive_msg`, the `audit_receive` function takes a lock before calling `audit_receive_msg`. This is key. The lock is only taken at the read side, so the write side can be accessed without privileges.
* **Read Side Trigger:** The "read" side of the race is initiated via `audit_log_start`, which is accessible through `seccomp`. This part, also, can be triggered by unprivileged users.
* **Race Specifics:** The actual race involves `audit_ctl_unlock` writing to the `owner` field while `audit_ctl_owner_current` reads it. The read is just checking if the `current` task is equal to the value of `audit_cmd_mutex.owner`
* **Benign Nature:** However, I've concluded that the race is, thankfully, benign. The checks that are done ensure that the only way to exploit the race is for the `current` task to be equal to a value in the `audit_cmd_mutex.owner`. This is very unlikely to occur. If the `current` task is the owner, then it already holds the lock. If it's not the owner, it might read `NULL` or something invalid and it won't equal `current`. Even in the exceedingly unlikely case of reading a torn pointer that happens to match `current`, the effect is simply skipping the backlog limit check, a relatively harmless consequence.
* **Accessibility:** This vulnerability is accessible from unprivileged processes, including those within user namespaces, which makes it an interesting point, but ultimately, not exploitable. It's not triggered by any obvious vectors like network input or malicious filesystems, or by virtual machines.
So, while it's a data race, it's not exploitable for privilege escalation or denial of service. It might cause a slightly incorrect backlog limit check, but that's the worst-case scenario. This should be reported and patched, but I don't see this as a high-severity issue.
|
| 23/2 |
2026/08/13 00:34 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 24/2 |
2026/08/13 00:34 |
llm |
expert |
0m
Model:
Tokens:
input: 22954
output: 566
thoughts: 415
Thoughts: **My Assessment of the KCSAN Data-Race Report**
Okay, the results are in, time to formulate the final response. This KCSAN report is flagging a data race between `audit_log_start` and `audit_receive`, specifically within the `audit_ctl_unlock` and `audit_ctl_owner_current` functions. I see it: the race is between writing to `audit_cmd_mutex.owner` (potentially setting it to `NULL` via `audit_ctl_unlock`) and reading from it to check for ownership in `audit_ctl_owner_current`. Classic benign data race territory, really.
The reasoning's sound. The lock's owner either *is* `current`, in which case no other task is touching `owner`, or it *isn't*, meaning another task is writing to it. So, a read will either return `NULL` or another task's pointer, and either way, the comparison to `current` will be false. Even if, and I mean *extremely* unlikely, we get a torn pointer that coincidentally equals `current`, the worst that happens is `audit_log_start` skips waiting on the audit backlog and proceeds to allocate a buffer. It's not ideal, but it's not a security issue by any stretch.
Given that this is a benign data race, it's not exploitable, and it certainly won't trigger a denial of service.
Furthermore, it looks like this is accessible to unprivileged processes, which is useful to know. Seccomp filters can trigger the read side in `audit_log_start`, and simply creating a `NETLINK_AUDIT` socket and sending a message opens up the write path in `audit_receive`, assuming the necessary capabilities are there. Capability checks happen *after* the mutex is taken, so the unprivileged access is a possibility. And because user namespaces can use seccomp and netlink sockets, this could also be accessed from user namespaces. This is not related to VM guests, network traffic, or other remote/peripheral triggers, so this is unlikely to affect the use cases I'm concerned about.
|
| 25/1 |
2026/08/13 00:34 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided bug report and kernel source code, here is
the assessment of the bug:
**Bug Mechanism:**
The bug is a KCSAN data-race report between `audit_log_start` and
`audit_receive`.
The race occurs on the `audit_cmd_mutex.owner` field:
1. **Write:** `audit_ctl_unlock()` sets `audit_cmd_mutex.owner = NULL;` when
releasing the audit control mutex.
2. **Read:** `audit_ctl_owner_current()` reads `audit_cmd_mutex.owner` to check
if it equals `current` (the currently executing task).
**Exploitability & Denial of Service:**
This is a classic benign data race in lock ownership checking.
- If `current` is the owner of the lock, it holds the lock, meaning no other
task can concurrently write to `audit_cmd_mutex.owner`.
- If `current` is *not* the owner, it does not hold the lock, so another task
might be concurrently writing to `owner` (e.g., setting it to `NULL` or to its
own task pointer). In this case, `current` will read either `NULL` or the other
task's pointer. In both cases, the read value will not equal `current`, so the
check correctly evaluates to `false`.
- Even in the astronomically unlikely event of reading a torn pointer that
happens to exactly match the `current` task pointer, the only consequence is
that `audit_log_start()` would skip waiting for the audit backlog limit and
proceed to allocate an audit buffer.
Therefore, this data race is completely benign. It cannot be exploited for
memory corruption, privilege escalation, or information leaks, nor can it cause
a denial of service.
**Accessibility:**
- **Unprivileged Processes:** Yes. The read side (`audit_log_start`) can be
triggered by unprivileged processes via seccomp filters (as seen in the stack
trace). The write side (`audit_receive`) can be triggered by unprivileged
processes by creating a `NETLINK_AUDIT` socket and sending a message to it.
While unprivileged users lack the capabilities to actually configure the audit
subsystem, the capability checks (`audit_netlink_ok`) happen *after* the
`audit_cmd_mutex` lock is acquired in `audit_receive()`.
- **User Namespaces:** Yes. Both seccomp and netlink sockets are accessible
within user namespaces.
- **Other Triggers:** The bug is a local kernel concurrency issue and is not
triggerable via VM guests/hosts, network traffic, remote connections,
peripherals, or malicious filesystems.]
|