| 0/0 |
2026/08/12 14:20 |
flow |
assessment-security |
5m
Results: map[DenialOfService:true Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Bug Mechanism:**
The bug is a race condition between changing the ring buffer sub-buffer size
(`ring_buffer_subbuf_order_set`) and reading from the raw trace pipe
(`ring_buffer_read_page` via `tracing_buffers_read` or
`tracing_buffers_splice_read`).
When the sub-buffer order is changed, `ring_buffer_subbuf_order_set` updates
`buffer->subbuf_order` to the new order and then proceeds to allocate new pages
for each CPU buffer. Concurrently, a reader can call `tracing_buffers_read`,
which allocates a new `spare` page using the *new* `buffer->subbuf_order`.
Then, `ring_buffer_read_page` is called, which swaps the newly allocated `spare`
page (with the new order) with the old `reader_page` in the ring buffer (which
still has the old order). Because `ring_buffer_read_page` only swaps the page
pointers but does not update the `order` field of the `buffer_page` structure,
the old `reader_page` structure now points to a page with the new order, while
its `order` field still holds the old order. Similarly, the `spare` page
structure now points to a page with the old order, while its `order` field holds
the new order.
When `ring_buffer_subbuf_order_set` finishes allocating new pages, it frees the
old `reader_page` by calling `free_pages(bpage->page, bpage->order)`. This calls
`free_pages` with a mismatched order.
If the old order is larger than the new order, `free_pages` will access
out-of-bounds pages (assuming they are tail pages of a compound page) and call
`free_tail_page_prepare` on them. This function clears
`PAGE_FLAGS_CHECK_AT_PREP` (which includes almost all page flags, such as
`PG_slab`, `PG_locked`, `PG_lru`, etc.) from these out-of-bounds pages.
If the old order is smaller than the new order, the swapped `spare` page will
eventually be freed with the new order, which again causes `free_pages` to be
called with a larger order than the actual page order, leading to the exact same
out-of-bounds page flag corruption.
**Exploitability:**
The bug is **exploitable**. Corrupting the page flags of adjacent pages in
memory is a powerful primitive. For example, clearing `PG_slab` on a slab page
will cause massive memory corruption when the SLUB allocator tries to use or
free it. Clearing `PG_locked` on a locked page will cause race conditions and
use-after-free. This can be reliably exploited for privilege escalation or to
cause a Denial of Service (kernel panic).
**Accessibility:**
The bug is **NOT accessible from unprivileged processes** or **user
namespaces**.
Triggering the bug requires interacting with files in `tracefs` (specifically
`buffer_subbuf_size_kb` and `trace_pipe_raw` or `snapshot`). These files are
created with `0640` or `0440` permissions and are owned by root. Furthermore,
`tracefs` is not marked with `FS_USERNS_MOUNT`, meaning it cannot be mounted
inside an unprivileged user namespace. Therefore, only a privileged user (root
in the initial namespace) can trigger this vulnerability. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/12 14:20 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int fd_size;
int fd_raw;
int fd_marker;
void *size_thread(void *arg)
{
while (1) {
if (pwrite(fd_size, "4\n", 2, 0) < 0) {
// Ignore errors as it might fail if busy
}
usleep(1000);
if (pwrite(fd_size, "8\n", 2, 0) < 0) {
// Ignore errors
}
usleep(1000);
}
return NULL;
}
void *read_thread(void *arg)
{
char buf[8192];
while (1) {
if (read(fd_raw, buf, sizeof(buf)) < 0) {
// Ignore errors, especially EAGAIN due to O_NONBLOCK
}
}
return NULL;
}
void *write_thread(void *arg)
{
char buf[1024];
memset(buf, 'A', sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\n';
while (1) {
if (write(fd_marker, buf, sizeof(buf)) < 0) {
// Ignore errors
}
}
return NULL;
}
int main()
{
fd_size = open("/sys/kernel/tracing/buffer_subbuf_size_kb", O_WRONLY);
if (fd_size < 0) {
fd_size = open("/sys/kernel/debug/tracing/buffer_subbuf_size_kb", O_WRONLY);
}
if (fd_size < 0) {
printf("[-] Failed to open buffer_subbuf_size_kb: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened buffer_subbuf_size_kb successfully.\n");
fd_raw = open("/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw", O_RDONLY | O_NONBLOCK);
if (fd_raw < 0) {
fd_raw = open("/sys/kernel/debug/tracing/per_cpu/cpu0/trace_pipe_raw", O_RDONLY | O_NONBLOCK);
}
if (fd_raw < 0) {
printf("[-] Failed to open trace_pipe_raw: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened trace_pipe_raw successfully.\n");
fd_marker = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
if (fd_marker < 0) {
fd_marker = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
}
if (fd_marker < 0) {
printf("[-] Failed to open trace_marker: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened trace_marker successfully.\n");
pthread_t t1, t2, t3;
if (pthread_create(&t1, NULL, size_thread, NULL) != 0) {
printf("[-] Failed to create size_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created size_thread successfully.\n");
if (pthread_create(&t2, NULL, read_thread, NULL) != 0) {
printf("[-] Failed to create read_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created read_thread successfully.\n");
if (pthread_create(&t3, NULL, write_thread, NULL) != 0) {
printf("[-] Failed to create write_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created write_thread successfully.\n");
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
if (pthread_setaffinity_np(t2, sizeof(set), &set) != 0) {
printf("[-] Failed to set affinity for read_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Set affinity for read_thread successfully.\n");
if (pthread_setaffinity_np(t3, sizeof(set), &set) != 0) {
printf("[-] Failed to set affinity for write_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Set affinity for write_thread successfully.\n");
sleep(10);
printf("[+] Finished sleeping. Exiting.\n");
return 0;
}]
|
| 2/1 |
2026/08/12 14:20 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/1a0aec36943dd0206494f9376d07cfbf81ffc6ec]
|
| 3/1 |
2026/08/12 14:20 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/fcbeaa90f5c00d2d8b2141695e1d17799fd08088]
|
| 4/1 |
2026/08/12 14:20 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/12 14:21 |
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:
vfs_read+0x1e4/0xb40 fs/read_write.c:572
ksys_read+0x12a/0x250 fs/read_write.c:716
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
------------[ cut here ]------------
kernel BUG at mm/page_alloc.c:1361!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 1 UID: 0 PID: 6026 Comm: syz-executor398 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:__free_pages_prepare mm/page_alloc.c:1361 [inline]
RIP: 0010:__free_frozen_pages+0xcd3/0x1090 mm/page_alloc.c:2950
Code: 48 c7 c6 c0 8c e1 8b 4c 89 f7 e8 68 eb ef ff 90 0f 0b 85 db 0f 84 6e f5 ff ff 48 c7 c6 20 8d e1 8b 4c 89 ff e8 4e eb ef ff 90 <0f> 0b 48 c7 44 24 20 01 00 00 00 48 c7 44 24 28 01 00 00 00 e9 d5
RSP: 0018:ffffc900030f7af8 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffc900030f79dc
RDX: ffff88802b830000 RSI: ffffffff825c2128 RDI: ffff88802b8304c4
RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
R13: ffffea00009afb88 R14: ffffea00009afb80 R15: ffffea00009afb80
FS: 00007f34a2fe96c0(0000) GS:ffff8880d5eec000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f34a27e6208 CR3: 0000000027e6e000 CR4: 0000000000352ef0
Call Trace:
<TASK>
free_buffer_page kernel/trace/ring_buffer.c:398 [inline]
ring_buffer_subbuf_order_set+0x1325/0x18e0 kernel/trace/ring_buffer.c:7444
buffer_subbuf_size_write+0x182/0x280 kernel/trace/trace.c:8221
vfs_write+0x2aa/0x1050 fs/read_write.c:685
ksys_pwrite64 fs/read_write.c:794 [inline]
__do_sys_pwrite64 fs/read_write.c:802 [inline]
__se_sys_pwrite64 fs/read_write.c:799 [inline]
__x64_sys_pwrite64+0x1eb/0x250 fs/read_write.c:799
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:0x7f34a303ec1e
Code: 08 0f 85 f5 e2 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 41 57 41 56 4d 89 c6 41 55 4d 89 cd 41 54 55 53 48 83 ec 08
RSP: 002b:00007f34a2fe9198 EFLAGS: 00000246 ORIG_RAX: 0000000000000012
RAX: ffffffffffffffda RBX: 00007f34a2fe96c0 RCX: 00007f34a303ec1e
RDX: 0000000000000002 RSI: 00007f34a308a02b RDI: 0000000000000003
RBP: 00007f34a308a028 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffffd0
R13: 0000000000000000 R14: 00007ffcb8a85c30 R15: 00007ffcb8a85d18
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:__free_pages_prepare mm/page_alloc.c:1361 [inline]
RIP: 0010:__free_frozen_pages+0xcd3/0x1090 mm/page_alloc.c:2950
Code: 48 c7 c6 c0 8c e1 8b 4c 89 f7 e8 68 eb ef ff 90 0f 0b 85 db 0f 84 6e f5 ff ff 48 c7 c6 20 8d e1 8b 4c 89 ff e8 4e eb ef ff 90 <0f> 0b 48 c7 44 24 20 01 00 00 00 48 c7 44 24 28 01 00 00 00 e9 d5
RSP: 0018:ffffc900030f7af8 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffc900030f79dc
RDX: ffff88802b830000 RSI: ffffffff825c2128 RDI: ffff88802b8304c4
RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
R13: ffffea00009afb88 R14: ffffea00009afb80 R15: ffffea00009afb80
FS: 00007f34a2fe96c0(0000) GS:ffff8880d5eec000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f34a27e6208 CR3: 0000000027e6e000 CR4: 0000000000352ef0
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.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int fd_size;
int fd_raw;
int fd_marker;
void *size_thread(void *arg)
{
while (1) {
if (pwrite(fd_size, "4\n", 2, 0) < 0) {
// Ignore errors as it might fail if busy
}
usleep(1000);
if (pwrite(fd_size, "8\n", 2, 0) < 0) {
// Ignore errors
}
usleep(1000);
}
return NULL;
}
void *read_thread(void *arg)
{
char buf[8192];
while (1) {
if (read(fd_raw, buf, sizeof(buf)) < 0) {
// Ignore errors, especially EAGAIN due to O_NONBLOCK
}
}
return NULL;
}
void *write_thread(void *arg)
{
char buf[1024];
memset(buf, 'A', sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\n';
while (1) {
if (write(fd_marker, buf, sizeof(buf)) < 0) {
// Ignore errors
}
}
return NULL;
}
int main()
{
fd_size = open("/sys/kernel/tracing/buffer_subbuf_size_kb", O_WRONLY);
if (fd_size < 0) {
fd_size = open("/sys/kernel/debug/tracing/buffer_subbuf_size_kb", O_WRONLY);
}
if (fd_size < 0) {
printf("[-] Failed to open buffer_subbuf_size_kb: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened buffer_subbuf_size_kb successfully.\n");
fd_raw = open("/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw", O_RDONLY | O_NONBLOCK);
if (fd_raw < 0) {
fd_raw = open("/sys/kernel/debug/tracing/per_cpu/cpu0/trace_pipe_raw", O_RDONLY | O_NONBLOCK);
}
if (fd_raw < 0) {
printf("[-] Failed to open trace_pipe_raw: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened trace_pipe_raw successfully.\n");
fd_marker = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
if (fd_marker < 0) {
fd_marker = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
}
if (fd_marker < 0) {
printf("[-] Failed to open trace_marker: %s\n", strerror(errno));
exit(1);
}
printf("[+] Opened trace_marker successfully.\n");
pthread_t t1, t2, t3;
if (pthread_create(&t1, NULL, size_thread, NULL) != 0) {
printf("[-] Failed to create size_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created size_thread successfully.\n");
if (pthread_create(&t2, NULL, read_thread, NULL) != 0) {
printf("[-] Failed to create read_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created read_thread successfully.\n");
if (pthread_create(&t3, NULL, write_thread, NULL) != 0) {
printf("[-] Failed to create write_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Created write_thread successfully.\n");
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
if (pthread_setaffinity_np(t2, sizeof(set), &set) != 0) {
printf("[-] Failed to set affinity for read_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Set affinity for read_thread successfully.\n");
if (pthread_setaffinity_np(t3, sizeof(set), &set) != 0) {
printf("[-] Failed to set affinity for write_thread: %s\n", strerror(errno));
exit(1);
}
printf("[+] Set affinity for write_thread successfully.\n");
sleep(10);
printf("[+] Finished sleeping. Exiting.\n");
return 0;
}
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Bug Mechanism:**
The bug is a race condition between changing the ring buffer sub-buffer size (`ring_buffer_subbuf_order_set`) and reading from the raw trace pipe (`ring_buffer_read_page` via `tracing_buffers_read` or `tracing_buffers_splice_read`).
When the sub-buffer order is changed, `ring_buffer_subbuf_order_set` updates `buffer->subbuf_order` to the new order and then proceeds to allocate new pages for each CPU buffer. Concurrently, a reader can call `tracing_buffers_read`, which allocates a new `spare` page using the *new* `buffer->subbuf_order`.
Then, `ring_buffer_read_page` is called, which swaps the newly allocated `spare` page (with the new order) with the old `reader_page` in the ring buffer (which still has the old order). Because `ring_buffer_read_page` only swaps the page pointers but does not update the `order` field of the `buffer_page` structure, the old `reader_page` structure now points to a page with the new order, while its `order` field still holds the old order. Similarly, the `spare` page structure now points to a page with the old order, while its `order` field holds the new order.
When `ring_buffer_subbuf_order_set` finishes allocating new pages, it frees the old `reader_page` by calling `free_pages(bpage->page, bpage->order)`. This calls `free_pages` with a mismatched order.
If the old order is larger than the new order, `free_pages` will access out-of-bounds pages (assuming they are tail pages of a compound page) and call `free_tail_page_prepare` on them. This function clears `PAGE_FLAGS_CHECK_AT_PREP` (which includes almost all page flags, such as `PG_slab`, `PG_locked`, `PG_lru`, etc.) from these out-of-bounds pages.
If the old order is smaller than the new order, the swapped `spare` page will eventually be freed with the new order, which again causes `free_pages` to be called with a larger order than the actual page order, leading to the exact same out-of-bounds page flag corruption.
**Exploitability:**
The bug is **exploitable**. Corrupting the page flags of adjacent pages in memory is a powerful primitive. For example, clearing `PG_slab` on a slab page will cause massive memory corruption when the SLUB allocator tries to use or free it. Clearing `PG_locked` on a locked page will cause race conditions and use-after-free. This can be reliably exploited for privilege escalation or to cause a Denial of Service (kernel panic).
**Accessibility:**
The bug is **NOT accessible from unprivileged processes** or **user namespaces**.
Triggering the bug requires interacting with files in `tracefs` (specifically `buffer_subbuf_size_kb` and `trace_pipe_raw` or `snapshot`). These files are created with `0640` or `0440` permissions and are owned by root. Furthermore, `tracefs` is not marked with `FS_USERNS_MOUNT`, meaning it cannot be mounted inside an unprivileged user namespace. Therefore, only a privileged user (root in the initial namespace) can trigger this vulnerability.
|
| 6/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 9618
output: 20
thoughts: 334
Thoughts: **Debugging a Kernel Bug**
Okay, here's what I'm seeing: we've got a pretty nasty `kernel BUG at mm/page_alloc.c:1361!`. That's an immediate red flag, especially with `__free_pages_prepare` and `__free_frozen_pages` involved. It looks like we're hitting an issue deep within the page allocator, specifically during the freeing of pages. The call trace points us to `ring_buffer.c`, specifically `free_buffer_page` and then `ring_buffer_subbuf_order_set` and all the way back to `vfs_write` so it's likely a write into the trace buffer which is then triggering the page free.
We know the reproducer is interacting with the tracing system: it's writing to `/sys/kernel/tracing/buffer_subbuf_size_kb`, reading from `/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw` and also writing to `/sys/kernel/tracing/trace_marker`.
Now, I need to get my hands dirty. I don't have the exact kernel version, but I can definitely use my usual grep tools. I'll search for the functions `__free_pages_prepare`, `__free_frozen_pages`, and `free_buffer_page` in the kernel source. That should pinpoint the exact line in `mm/page_alloc.c:1361` and help me understand what's happening during the page free operation and pinpoint the issue. Knowing the context of this free operation is key to finding the root cause.
|
| 7/2 |
2026/08/12 14:21 |
tool |
grepper |
0m
Args: map[Expression:__free_pages_prepare]
Results: map[Output:mm/page_alloc.c=1308=static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) {}
--
mm/page_alloc.c-1311-
mm/page_alloc.c:1312:static __always_inline bool __free_pages_prepare(struct page *page,
mm/page_alloc.c-1313- unsigned int order, fpi_t fpi_flags)
--
mm/page_alloc.c=1449=bool free_pages_prepare(struct page *page, unsigned int order)
mm/page_alloc.c-1450-{
mm/page_alloc.c:1451: return __free_pages_prepare(page, order, FPI_NONE);
mm/page_alloc.c-1452-}
--
mm/page_alloc.c=1578=static void __free_pages_ok(struct page *page, unsigned int order,
--
mm/page_alloc.c-1583-
mm/page_alloc.c:1584: if (__free_pages_prepare(page, order, fpi_flags))
mm/page_alloc.c-1585- free_one_page(zone, page, pfn, order, fpi_flags);
--
mm/page_alloc.c=2937=static void __free_frozen_pages(struct page *page, unsigned int order,
--
mm/page_alloc.c-2949-
mm/page_alloc.c:2950: if (!__free_pages_prepare(page, order, fpi_flags))
mm/page_alloc.c-2951- return;
--
mm/page_alloc.c=2999=void free_unref_folios(struct folio_batch *folios)
--
mm/page_alloc.c-3010-
mm/page_alloc.c:3011: if (!__free_pages_prepare(&folio->page, order, FPI_NONE))
mm/page_alloc.c-3012- continue;
]
|
| 8/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 10859
output: 36
thoughts: 39
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Diagnosing a Memory Allocation Issue**
Right, so the error report points directly to `mm/page_alloc.c:1361`. That's a very specific pinpointing of the source, which is always helpful, but as we know, a line number is rarely the whole story. I'm going to pull up `mm/page_alloc.c` and zero in on that section around line 1361. I need to understand what's happening there to trace the problem effectively. That line is likely the immediate *location* of the issue, but not necessarily the *cause*. My focus will be on the surrounding code: What are the conditions that lead to that line being executed? What resources are being manipulated? And from there, I'll start to reconstruct the call stack in my head, or use debugging tools if necessary, to figure out the root cause.
|
| 9/2 |
2026/08/12 14:21 |
tool |
read-file |
0m
Args: map[File:mm/page_alloc.c FirstLine:1340 LineCount:50]
Results: map[Contents:1340:
1341: __folio_clear_mlocked(folio);
1342: zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages);
1343: count_vm_events(UNEVICTABLE_PGCLEARED, nr_pages);
1344: }
1345:
1346: if (unlikely(PageHWPoison(page)) && !order) {
1347: /* Do not let hwpoison pages hit pcplists/buddy */
1348: reset_page_owner(page, order);
1349: page_table_check_free(page, order);
1350: pgalloc_tag_sub(page, 1 << order);
1351:
1352: /*
1353: * The page is isolated and accounted for.
1354: * Mark the codetag as empty to avoid accounting error
1355: * when the page is freed by unpoison_memory().
1356: */
1357: clear_page_tag_ref(page);
1358: return false;
1359: }
1360:
1361: VM_BUG_ON_PAGE(compound && compound_order(page) != order, page);
1362:
1363: /*
1364: * Check tail pages before head page information is cleared to
1365: * avoid checking PageCompound for order-0 pages.
1366: */
1367: if (unlikely(order)) {
1368: int i;
1369:
1370: if (compound) {
1371: page[1].flags.f &= ~PAGE_FLAGS_SECOND;
1372: #ifdef NR_PAGES_IN_LARGE_FOLIO
1373: folio->_nr_pages = 0;
1374: #endif
1375: }
1376: for (i = 1; i < (1 << order); i++) {
1377: if (compound)
1378: bad += free_tail_page_prepare(page, page + i);
1379: if (is_check_pages_enabled()) {
1380: if (free_page_is_bad(page + i)) {
1381: bad++;
1382: continue;
1383: }
1384: }
1385: (page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
1386: }
1387: }
1388: if (folio_test_anon(folio)) {
1389: mod_mthp_stat(order, MTHP_STAT_NR_ANON, -1);
]
|
| 10/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 11894
output: 37
thoughts: 134
Thoughts: **Analyzing a Memory Management Bug**
Okay, so I'm looking at this bug, and it seems to be centered around a `VM_BUG_ON_PAGE` assertion. The key part is `compound && compound_order(page) != order`. Basically, we're dealing with a compound page, and there's a mismatch between the order used when it was *allocated* and the order that's being *passed* to free it. The bug report points specifically to `mm/page_alloc.c:1361`, which confirms that the assertion is failing during the page free operation.
This means something is going wrong when we're trying to deallocate a multi-page chunk of memory, and the system is getting confused about its size. I need to trace this back to where the page is actually being freed. Based on the context, I think the `free_buffer_page` function might be involved in `kernel/trace/ring_buffer.c:398`. Time to dive into that function and see what's happening.
|
| 11/2 |
2026/08/12 14:21 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:380 LineCount:50]
Results: map[Contents: 380: {
381: return rb_data_page_commit(dpage) & ~RB_MISSED_MASK;
382: }
383:
384: static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
385: {
386: return rb_data_page_commit(bpage->page);
387: }
388:
389: static __always_inline unsigned int rb_page_size(struct buffer_page *bpage)
390: {
391: return rb_data_page_size(bpage->page);
392: }
393:
394: static void free_buffer_page(struct buffer_page *bpage)
395: {
396: /* Range pages are not to be freed */
397: if (!bpage->range)
398: free_pages((unsigned long)bpage->page, bpage->order);
399: kfree(bpage);
400: }
401:
402: /*
403: * For best performance, allocate cpu buffer data cache line sized
404: * and per CPU.
405: */
406: #define alloc_cpu_buffer(cpu) (struct ring_buffer_per_cpu *) \
407: kzalloc_node(ALIGN(sizeof(struct ring_buffer_per_cpu), \
408: cache_line_size()), GFP_KERNEL, cpu_to_node(cpu));
409:
410: #define alloc_cpu_page(cpu) (struct buffer_page *) \
411: kzalloc_node(ALIGN(sizeof(struct buffer_page), \
412: cache_line_size()), GFP_KERNEL, cpu_to_node(cpu));
413:
414: static struct buffer_data_page *alloc_cpu_data(int cpu, int order)
415: {
416: struct buffer_data_page *dpage;
417: struct page *page;
418: gfp_t mflags;
419:
420: /*
421: * __GFP_RETRY_MAYFAIL flag makes sure that the allocation fails
422: * gracefully without invoking oom-killer and the system is not
423: * destabilized.
424: */
425: mflags = GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_COMP | __GFP_ZERO;
426:
427: page = alloc_pages_node(cpu_to_node(cpu), mflags, order);
428: if (!page)
429: return NULL;
]
|
| 12/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 13072
output: 38
thoughts: 73
Thoughts: **Analyzing the Page Deallocation in the Ring Buffer**
Okay, so I'm looking at `free_buffer_page` and, right away, my brain jumps to that `free_pages` call. What's interesting is how it's being called: specifically, the use of `bpage->order`. That's the key variable here, the order of the page being freed. It's a key part of the deallocation. I need to understand where this `bpage->order` is coming from, because that determines the size of the free pages. Therefore I'm going to jump to `ring_buffer_subbuf_order_set` in `kernel/trace/ring_buffer.c:7444`. I'll need to see how the order is actually being *set* in the first place, and then that will make the logic of the free call click. Understanding this link is critical for understanding the memory management dynamics in this area.
|
| 13/2 |
2026/08/12 14:21 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7420 LineCount:50]
Results: map[Contents:7420: list_del_init(&cpu_buffer->reader_page->list);
7421:
7422: /* Install the new pages, remove the head from the list */
7423: cpu_buffer->pages = cpu_buffer->new_pages.next;
7424: list_del_init(&cpu_buffer->new_pages);
7425: cpu_buffer->cnt++;
7426:
7427: cpu_buffer->head_page
7428: = list_entry(cpu_buffer->pages, struct buffer_page, list);
7429: cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page;
7430:
7431: cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update;
7432: cpu_buffer->nr_pages_to_update = 0;
7433:
7434: old_free_data_page = cpu_buffer->free_page;
7435: cpu_buffer->free_page = NULL;
7436:
7437: rb_head_page_activate(cpu_buffer);
7438:
7439: raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
7440:
7441: /* Free old sub buffers */
7442: list_for_each_entry_safe(bpage, tmp, &old_pages, list) {
7443: list_del_init(&bpage->list);
7444: free_buffer_page(bpage);
7445: }
7446: free_pages((unsigned long)old_free_data_page, old_order);
7447:
7448: rb_check_pages(cpu_buffer);
7449: }
7450:
7451: atomic_dec(&buffer->record_disabled);
7452:
7453: return 0;
7454:
7455: error:
7456: buffer->subbuf_order = old_order;
7457: buffer->subbuf_size = old_size;
7458:
7459: atomic_dec(&buffer->record_disabled);
7460:
7461: for_each_buffer_cpu(buffer, cpu) {
7462: cpu_buffer = buffer->buffers[cpu];
7463:
7464: if (!cpu_buffer->nr_pages_to_update)
7465: continue;
7466:
7467: list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages, list) {
7468: list_del_init(&bpage->list);
7469: free_buffer_page(bpage);
]
|
| 14/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 14174
output: 19
thoughts: 40
Thoughts: Okay, let me think this through.
**Analyzing the Memory Deallocation Path**
Right, so I'm tracing through this memory deallocation function. The critical call here is `free_buffer_page(bpage)`. That makes sense; it's the gateway to returning this buffer page to the system. Now, looking at that function, I see the core logic hinges on `bpage->order`. Ah-ha, that's how it determines the size and structure of the memory it's freeing. But that immediately raises a key question in my mind: where is `bpage->order` being initialized? Where is *that* variable set? I need to understand the lifecycle of this structure to ensure the correct memory block is deallocated. The order is clearly defining the block's size and, therefore, the mechanics of how it's returned to the page allocator, so I must go back up the call chain to track down its origin. This is all about confirming the correct free operation is happening.
|
| 15/2 |
2026/08/12 14:21 |
tool |
grepper |
0m
Args: map[Expression:bpage->order =]
Results: map[Output:kernel/trace/ring_buffer.c=2358=static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-2439- }
kernel/trace/ring_buffer.c:2440: bpage->order = cpu_buffer->buffer->subbuf_order;
kernel/trace/ring_buffer.c-2441-
--
kernel/trace/ring_buffer.c=2487=rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
--
kernel/trace/ring_buffer.c-2512- return NULL;
kernel/trace/ring_buffer.c:2513: bpage->order = cpu_buffer->buffer->subbuf_order;
kernel/trace/ring_buffer.c-2514-
--
kernel/trace/ring_buffer.c=6947=ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
--
kernel/trace/ring_buffer.c-6959-
kernel/trace/ring_buffer.c:6960: bpage->order = buffer->subbuf_order;
kernel/trace/ring_buffer.c-6961- cpu_buffer = buffer->buffers[cpu];
]
|
| 16/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 14788
output: 38
thoughts: 73
Thoughts: Here's my summary, as though I'm thinking through this myself:
**Investigating a Potential Race Condition in Page Allocation**
Okay, so I'm digging into this page allocation code. I see that `bpage->order` is being initialized with the value of `cpu_buffer->buffer->subbuf_order`. Makes sense, so far. But, a red flag just went up. I need to be absolutely certain that this `buffer->subbuf_order` isn't subject to concurrent modification. That's the critical piece here, and if it is, we've got a classic race condition brewing. Let's trace through this and see where that variable is set. Right, let's look for `ring_buffer_subbuf_order_set` in `kernel/trace/ring_buffer.c`. That should give me the full picture. I need to understand *who* and *when* this thing can be changed. Gotta get to the bottom of this!
|
| 17/2 |
2026/08/12 14:21 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7340 LineCount:50]
Results: map[Contents:7340: if (psize > RB_WRITE_MASK + 1)
7341: return -EINVAL;
7342:
7343: old_order = buffer->subbuf_order;
7344: old_size = buffer->subbuf_size;
7345:
7346: /* prevent another thread from changing buffer sizes */
7347: guard(mutex)(&buffer->mutex);
7348: atomic_inc(&buffer->record_disabled);
7349:
7350: /* Make sure all commits have finished */
7351: synchronize_rcu();
7352:
7353: buffer->subbuf_order = order;
7354: buffer->subbuf_size = psize - BUF_PAGE_HDR_SIZE;
7355:
7356: /* Make sure all new buffers are allocated, before deleting the old ones */
7357: for_each_buffer_cpu(buffer, cpu) {
7358:
7359: if (!cpumask_test_cpu(cpu, buffer->cpumask))
7360: continue;
7361:
7362: cpu_buffer = buffer->buffers[cpu];
7363:
7364: if (atomic_read(&cpu_buffer->resize_disabled)) {
7365: err = -EBUSY;
7366: goto error;
7367: }
7368:
7369: /* Update the number of pages to match the new size */
7370: nr_pages = old_size * buffer->buffers[cpu]->nr_pages;
7371: nr_pages = DIV_ROUND_UP(nr_pages, buffer->subbuf_size);
7372:
7373: /* we need a minimum of two pages */
7374: if (nr_pages < 2)
7375: nr_pages = 2;
7376:
7377: cpu_buffer->nr_pages_to_update = nr_pages;
7378:
7379: /* Include the reader page */
7380: nr_pages++;
7381:
7382: /* Allocate the new size buffer */
7383: INIT_LIST_HEAD(&cpu_buffer->new_pages);
7384: if (__rb_allocate_pages(cpu_buffer, nr_pages,
7385: &cpu_buffer->new_pages)) {
7386: /* not enough memory for new pages */
7387: err = -ENOMEM;
7388: goto error;
7389: }
]
|
| 18/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 15836
output: 38
thoughts: 74
Thoughts: Here's my analysis of that code snippet:
**Debugging Memory Allocation in the Subbuffer System**
Okay, so I'm tracking down this potential memory allocation issue. Line 7353 is where `buffer->subbuf_order` gets modified, which is crucial. It looks like that updated value of `subbuf_order` is then immediately fed into `__rb_allocate_pages` during the `bpage->order` assignment. That's good, so the order is getting correctly set. The logic seems straight forward so far. If `__rb_allocate_pages` encounters a problem, we end up in the `error` block. Alright, so the key is in what happens at `error`. Let me check the context there. I need to figure out the root cause of the allocation failure and verify that all necessary actions, such as resource cleanup, are occurring after an allocation failure.
|
| 19/2 |
2026/08/12 14:21 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7455 LineCount:50]
Results: map[Contents:7455: error:
7456: buffer->subbuf_order = old_order;
7457: buffer->subbuf_size = old_size;
7458:
7459: atomic_dec(&buffer->record_disabled);
7460:
7461: for_each_buffer_cpu(buffer, cpu) {
7462: cpu_buffer = buffer->buffers[cpu];
7463:
7464: if (!cpu_buffer->nr_pages_to_update)
7465: continue;
7466:
7467: list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages, list) {
7468: list_del_init(&bpage->list);
7469: free_buffer_page(bpage);
7470: }
7471: }
7472:
7473: return err;
7474: }
7475: EXPORT_SYMBOL_GPL(ring_buffer_subbuf_order_set);
7476:
7477: static int rb_alloc_meta_page(struct ring_buffer_per_cpu *cpu_buffer)
7478: {
7479: struct page *page;
7480:
7481: if (cpu_buffer->meta_page)
7482: return 0;
7483:
7484: page = alloc_page(GFP_USER | __GFP_ZERO);
7485: if (!page)
7486: return -ENOMEM;
7487:
7488: cpu_buffer->meta_page = page_to_virt(page);
7489:
7490: return 0;
7491: }
7492:
7493: static void rb_free_meta_page(struct ring_buffer_per_cpu *cpu_buffer)
7494: {
7495: unsigned long addr = (unsigned long)cpu_buffer->meta_page;
7496:
7497: free_page(addr);
7498: cpu_buffer->meta_page = NULL;
7499: }
7500:
7501: static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer,
7502: struct buffer_page **subbuf_ids)
7503: {
7504: struct trace_buffer_meta *meta = cpu_buffer->meta_page;
]
|
| 20/2 |
2026/08/12 14:21 |
llm |
expert |
0m
Model:
Tokens:
input: 16846
output: 38
thoughts: 128
Thoughts: **Analyzing the Buffer Page Deallocation**
Okay, so I'm tracing through this buffer management code, and something's bugging me. I see `free_buffer_page(bpage)` called, and I know that function, it ultimately calls `free_pages(bpage->page, bpage->order)`. That makes sense so far; we're freeing the pages associated with a buffer page.
Here's where it gets a little hazy. I see that `bpage->order` was set during allocation, specifically to `buffer->subbuf_order` within `__rb_allocate_pages`. Fine, no problems, the order gets defined. But hang on... `bpage->page` *itself* was allocated with `buffer->subbuf_order` as well. So both `bpage->order` and the allocation of `bpage->page` use the same value!
If they both use the same value, then `bpage->order` *should* always match the order of `bpage->page`, right? Why is this even a question? Why would `compound_order(page) != order` ever be true? I must be missing something here.
I'm going to have to go back and examine `__rb_allocate_pages` with a fine-toothed comb to see where the logic's going off track. Maybe something's getting overwritten, or there's a subtly that I'm not seeing. Time to dig in.
|
| 21/2 |
2026/08/12 14:22 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:2358 LineCount:50]
Results: map[Contents:2358: static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
2359: long nr_pages, struct list_head *pages)
2360: {
2361: struct trace_buffer *buffer = cpu_buffer->buffer;
2362: struct ring_buffer_cpu_meta *meta = NULL;
2363: struct buffer_page *bpage, *tmp;
2364: bool user_thread = current->mm != NULL;
2365: struct ring_buffer_desc *desc = NULL;
2366: long i;
2367:
2368: /*
2369: * Check if the available memory is there first.
2370: * Note, si_mem_available() only gives us a rough estimate of available
2371: * memory. It may not be accurate. But we don't care, we just want
2372: * to prevent doing any allocation when it is obvious that it is
2373: * not going to succeed.
2374: */
2375: i = si_mem_available();
2376: if (i < nr_pages)
2377: return -ENOMEM;
2378:
2379: /*
2380: * If a user thread allocates too much, and si_mem_available()
2381: * reports there's enough memory, even though there is not.
2382: * Make sure the OOM killer kills this thread. This can happen
2383: * even with RETRY_MAYFAIL because another task may be doing
2384: * an allocation after this task has taken all memory.
2385: * This is the task the OOM killer needs to take out during this
2386: * loop, even if it was triggered by an allocation somewhere else.
2387: */
2388: if (user_thread)
2389: set_current_oom_origin();
2390:
2391: if (buffer->range_addr_start)
2392: meta = rb_range_meta(buffer, nr_pages, cpu_buffer->cpu);
2393:
2394: if (buffer->remote) {
2395: desc = ring_buffer_desc(buffer->remote->desc, cpu_buffer->cpu);
2396: if (!desc || WARN_ON(desc->nr_page_va != (nr_pages + 1)))
2397: return -EINVAL;
2398: }
2399:
2400: for (i = 0; i < nr_pages; i++) {
2401:
2402: bpage = alloc_cpu_page(cpu_buffer->cpu);
2403: if (!bpage)
2404: goto free_pages;
2405:
2406: rb_check_bpage(cpu_buffer, bpage);
2407:
]
|
| 22/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 18137
output: 38
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, geared toward an expert audience, and written in the first person:
**Diving Back into `__rb_allocate_pages`**
Alright, let's pick this up where we left off. I'm going to delve back into the function `__rb_allocate_pages`. My expertise in memory management and the Ruby runtime makes me instinctively want to dig deeper into the details of this allocation function. I anticipate it'll be a critical piece in understanding the Ruby garbage collector's behavior and the overall performance characteristics of the language. Let's see what we can find. I expect we'll be looking at how pages are mapped, managed, potentially paged out, and the interactions with the underlying OS. I'm keen to examine the allocation strategies, any potential optimizations, and where this fits within the overall Ruby memory model. This is where the rubber meets the road, so to speak.
|
| 23/2 |
2026/08/12 14:22 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:2408 LineCount:50]
Results: map[Contents:2408: /*
2409: * Append the pages as for mapped buffers we want to keep
2410: * the order
2411: */
2412: list_add_tail(&bpage->list, pages);
2413:
2414: if (meta) {
2415: /* A range was given. Use that for the buffer page */
2416: bpage->page = rb_range_buffer(cpu_buffer, i + 1);
2417: if (!bpage->page)
2418: goto free_pages;
2419: /* If this is valid from a previous boot */
2420: if (meta->head_buffer)
2421: rb_meta_buffer_update(cpu_buffer, bpage);
2422: bpage->range = 1;
2423: bpage->id = i + 1;
2424: } else if (desc) {
2425: void *p = ring_buffer_desc_page(desc, i + 1);
2426:
2427: if (WARN_ON(!p))
2428: goto free_pages;
2429:
2430: bpage->page = p;
2431: bpage->range = 1; /* bpage->page can't be freed */
2432: bpage->id = i + 1;
2433: cpu_buffer->subbuf_ids[i + 1] = bpage;
2434: } else {
2435: int order = cpu_buffer->buffer->subbuf_order;
2436: bpage->page = alloc_cpu_data(cpu_buffer->cpu, order);
2437: if (!bpage->page)
2438: goto free_pages;
2439: }
2440: bpage->order = cpu_buffer->buffer->subbuf_order;
2441:
2442: if (user_thread && fatal_signal_pending(current))
2443: goto free_pages;
2444: }
2445: if (user_thread)
2446: clear_current_oom_origin();
2447:
2448: return 0;
2449:
2450: free_pages:
2451: list_for_each_entry_safe(bpage, tmp, pages, list) {
2452: list_del_init(&bpage->list);
2453: free_buffer_page(bpage);
2454: }
2455: if (user_thread)
2456: clear_current_oom_origin();
2457:
]
|
| 24/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 19140
output: 19
thoughts: 201
Thoughts: **Investigating a Potential Race Condition in `__rb_allocate_pages`**
Okay, so I'm looking at this snippet from `__rb_allocate_pages` and a potential concurrency issue is immediately raising a flag. I see that inside the conditional block, we grab an order value from `cpu_buffer->buffer->subbuf_order`. This order determines the size of the memory allocated via `alloc_cpu_data`. Crucially, it looks like there's a possibility that `cpu_buffer->buffer->subbuf_order` could change *concurrently* between when we call `alloc_cpu_data` and when we assign the order to `bpage->order`.
If that happens, `bpage->order` would then hold a *different* value than the order used in the allocation. That's bad news! It would lead to an internal inconsistency about the memory structure.
But hold on... I remember that `buffer->subbuf_order` is changed by the function `ring_buffer_subbuf_order_set`. The critical detail here is that `ring_buffer_subbuf_order_set` *holds* the `buffer->mutex` while it modifies the order value. So, there should be no concurrent modification, right?
The question remains: is there *any* other place that modifies `buffer->subbuf_order`? If so, this could definitely be a race condition. I need to track down all modifications.
|
| 25/2 |
2026/08/12 14:22 |
tool |
grepper |
0m
Args: map[Expression:subbuf_order =]
Results: map[Output:kernel/trace/ring_buffer.c=2709=static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
--
kernel/trace/ring_buffer.c-2731-
kernel/trace/ring_buffer.c:2732: buffer->subbuf_order = order;
kernel/trace/ring_buffer.c-2733- subbuf_size = (PAGE_SIZE << order);
--
kernel/trace/ring_buffer.c=7319=int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
--
kernel/trace/ring_buffer.c-7331-
kernel/trace/ring_buffer.c:7332: if (buffer->subbuf_order == order)
kernel/trace/ring_buffer.c-7333- return 0;
--
kernel/trace/ring_buffer.c-7352-
kernel/trace/ring_buffer.c:7353: buffer->subbuf_order = order;
kernel/trace/ring_buffer.c-7354- buffer->subbuf_size = psize - BUF_PAGE_HDR_SIZE;
--
kernel/trace/ring_buffer.c-7455-error:
kernel/trace/ring_buffer.c:7456: buffer->subbuf_order = old_order;
kernel/trace/ring_buffer.c-7457- buffer->subbuf_size = old_size;
--
kernel/trace/ring_buffer.c=7616=static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-7629-
kernel/trace/ring_buffer.c:7630: subbuf_order = cpu_buffer->buffer->subbuf_order;
kernel/trace/ring_buffer.c-7631- subbuf_pages = 1 << subbuf_order;
]
|
| 26/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 20174
output: 27
thoughts: 978
Thoughts: **Analyzing `ring_buffer` Sub-buffer Allocation and Deallocation**
Okay, so I'm diving into this `ring_buffer` code and tracking how sub-buffer orders are handled. My initial observation is that `buffer->subbuf_order` is changed exclusively within `ring_buffer_subbuf_order_set` and `alloc_buffer`. And `ring_buffer_subbuf_order_set` does it under a mutex, which is good for concurrency.
`ring_buffer_subbuf_order_set` sets `buffer->subbuf_order` and then calls `__rb_allocate_pages`. Now, if `__rb_allocate_pages` fails, it jumps to an `error` label. This is where it gets interesting. Inside `error`, it restores `buffer->subbuf_order` to the `old_order` and then frees allocated pages from `cpu_buffer->new_pages` using `free_buffer_page(bpage)`.
The concern I had was whether `free_buffer_page` would correctly deallocate pages that were allocated with a potentially *new* order if `__rb_allocate_pages` fails *after* succeeding for some CPUs. The key is in how `free_buffer_page` is used. Looking at the `error` block, even if allocation was partially successful, `buffer->subbuf_order` gets restored, and all the "new" pages are freed. Crucially, `free_buffer_page` uses `bpage->order` which was set to the *new* order *inside* `__rb_allocate_pages`. The actual `bpage->page` was allocated with the new order as well! So, yes, if the `bpage->order` is the *new* order, `free_buffer_page` will free it with the new order. The restored `buffer->subbuf_order` doesn't affect the freeing process because `free_buffer_page` uses `bpage->order`. That's good. The allocation and deallocation orders are consistent.
Then the question of `compound_order(page) != order` arose. Why would those values differ?
Let me review `__rb_allocate_pages` again, specifically the loop where pages are allocated:
```c
for (i = 0; i < nr_pages; i++) {
bpage = alloc_cpu_page(cpu_buffer->cpu);
if (!bpage)
goto free_pages;
rb_check_bpage(cpu_buffer, bpage);
/*
* Append the pages as for mapped buffers we want to keep
* the order
*/
list_add_tail(&bpage->list, pages);
if (meta) {
...
} else if (desc) {
...
} else {
int order = cpu_buffer->buffer->subbuf_order;
bpage->page = alloc_cpu_data(cpu_buffer->cpu, order);
if (!bpage->page)
goto free_pages;
}
bpage->order = cpu_buffer->buffer->subbuf_order;
if (user_thread && fatal_signal_pending(current))
goto free_pages;
}
```
Now, if `alloc_cpu_data` fails, it goes to `free_pages`. And what happens in `free_pages`?
```c
free_pages:
list_for_each_entry_safe(bpage, tmp, pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
```
Okay, so when `alloc_cpu_data` fails, `bpage->page` is `NULL`.
And what does `free_buffer_page` do then?
```c
static void free_buffer_page(struct buffer_page *bpage)
{
/* Range pages are not to be freed */
if (!bpage->range)
free_pages((unsigned long)bpage->page, bpage->order);
kfree(bpage);
}
```
If `bpage->page` is `NULL`, `free_pages(0, bpage->order)` is called. And *that* becomes the crucial question: Is `free_pages(0, order)` safe? What does it do? Time to check `free_pages`. From the looks of things, that should be fine. A zero address, given a valid order, should be a no-op from a memory management perspective.
|
| 27/2 |
2026/08/12 14:22 |
tool |
grepper |
0m
Args: map[Expression:void free_pages\(unsigned long addr, unsigned int order\)]
Results: map[Output:include/linux/gfp.h=392=extern void free_pages_nolock(struct page *page, unsigned int order);
include/linux/gfp.h:393:extern void free_pages(unsigned long addr, unsigned int order);
include/linux/gfp.h-394-
--
mm/page_alloc.c=5431=void free_pages_nolock(struct page *page, unsigned int order)
--
mm/page_alloc.c-5444- */
mm/page_alloc.c:5445:void free_pages(unsigned long addr, unsigned int order)
mm/page_alloc.c-5446-{
]
|
| 28/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 22380
output: 36
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing the Landscape of Available Memory: Let's Examine `free_pages`**
Right, let's dive in. The instruction "Let's read `free_pages`" immediately places me into a specific context β likely kernel-level memory management. *`free_pages`*, of course, is a critical data structure, and the implied intent is to *understand its current state*. This could be for debugging, performance analysis, or simply to gain situational awareness during a specific task.
My immediate assumptions are: I'm probably operating within a system where I have direct access to, or can indirectly inspect, this memory management metadata. The "read" implies that I'm not necessarily going to *modify* it yet, at least not in this initial step. Instead, I'm aiming for introspection.
I'll be looking for several key pieces of information, depending on the system's architecture and the context:
* **Structure:** What is the underlying data structure? Is it a bitmap, a linked list, a buddy system, a slab allocator, or something else entirely? Understanding the format is crucial to deciphering the data.
* **Context:** What does this "free" represent? Are we dealing with physical pages, virtual pages, or both? Is this per-NUMA node? Are we looking at kernel memory, user space memory, or something else? I need to understand the relevant page size(s).
* **Granularity:** What level of detail does the information provide? Can I discern individual page allocation state, or is it coarser (e.g., page frame numbers)?
* **Metadata:** What other information is associated with these free pages? Are there hints about their previous usage (e.g., to implement a caching strategy, or to reduce fragmentation), or are there any flags that signal special conditions?
* **Potential Issues:** The very act of reading `free_pages` raises questions. Is there a lock or other synchronization mechanism I need to consider? Is the data potentially inconsistent (perhaps from concurrent modifications)? Are there any known caveats with the tooling I'm using to examine this data?
My approach will depend on the tools available. A debugger would offer the most flexibility, allowing me to step through the code that interacts with `free_pages` and observe its values. Alternatively, I might be relying on system calls, procfs entries, or tracing tools to gather the necessary data. In any case, I'll be forming hypotheses based on these raw data.
|
| 29/2 |
2026/08/12 14:22 |
tool |
read-file |
0m
Args: map[File:mm/page_alloc.c FirstLine:5445 LineCount:20]
Results: map[Contents:5445: void free_pages(unsigned long addr, unsigned int order)
5446: {
5447: if (addr != 0) {
5448: VM_BUG_ON(!virt_addr_valid((void *)addr));
5449: __free_pages(virt_to_page((void *)addr), order);
5450: }
5451: }
5452:
5453: EXPORT_SYMBOL(free_pages);
5454:
5455: static void *make_alloc_exact(unsigned long addr, unsigned int order,
5456: size_t size)
5457: {
5458: if (addr) {
5459: unsigned long nr = DIV_ROUND_UP(size, PAGE_SIZE);
5460: struct page *page = virt_to_page((void *)addr);
5461: struct page *last = page + nr;
5462:
5463: __split_page(page, order);
5464: while (page < --last)
]
|
| 30/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 23279
output: 38
thoughts: 437
Thoughts: **Analyzing the Buffer Page Allocation and Deallocation in the Ring Buffer Subbuf Order Set**
Okay, so I'm diving into this code related to the ring buffer and sub-buffer management. The first thing I'm checking is that `free_pages(0, order)` is a null operation. That's good to know, a simple no-op.
Now, I'm thinking about the allocation scenarios. What if `alloc_cpu_page` works, but `alloc_cpu_data` fails? In that case, `bpage->page` would be NULL, and `bpage->order` would either be uninitialized or zero (since `alloc_cpu_page` uses `kzalloc_node`). Then the code jumps to `free_pages`, which calls `free_buffer_page(bpage)`. `free_buffer_page` then calls `free_pages(0, 0)`, which is, again, a no-op. Then `kfree(bpage)` is called. That seems perfectly fine, as expected.
Next, what if `alloc_cpu_data` *succeeds*, but a fatal signal is pending? This means `goto free_pages` would execute, and `bpage->page` would be valid, and `bpage->order` would be set to `cpu_buffer->buffer->subbuf_order`. `free_buffer_page` is called, which then calls `free_pages(bpage->page, bpage->order)`. This also seems correct. So far, the error paths I've considered are handled without issue.
However, the bug report... this is where things get interesting. The bug report is pointing to the `free_buffer_page` function at a specific call site within the `ring_buffer_subbuf_order_set` function at line 7444. This is specifically the SUCCESS path. Here's that snippet:
```c
/* Free old sub buffers */
list_for_each_entry_safe(bpage, tmp, &old_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
```
This is *unexpected*, because I had assumed `free_buffer_page` would only be called on error paths. I need to go back and examine `ring_buffer_subbuf_order_set` more closely, specifically around this line to see what's happening.
|
| 31/2 |
2026/08/12 14:22 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7430 LineCount:30]
Results: map[Contents:7430:
7431: cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update;
7432: cpu_buffer->nr_pages_to_update = 0;
7433:
7434: old_free_data_page = cpu_buffer->free_page;
7435: cpu_buffer->free_page = NULL;
7436:
7437: rb_head_page_activate(cpu_buffer);
7438:
7439: raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
7440:
7441: /* Free old sub buffers */
7442: list_for_each_entry_safe(bpage, tmp, &old_pages, list) {
7443: list_del_init(&bpage->list);
7444: free_buffer_page(bpage);
7445: }
7446: free_pages((unsigned long)old_free_data_page, old_order);
7447:
7448: rb_check_pages(cpu_buffer);
7449: }
7450:
7451: atomic_dec(&buffer->record_disabled);
7452:
7453: return 0;
7454:
7455: error:
7456: buffer->subbuf_order = old_order;
7457: buffer->subbuf_size = old_size;
7458:
7459: atomic_dec(&buffer->record_disabled);
]
|
| 32/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 24736
output: 19
thoughts: 95
Thoughts: Okay, here's my breakdown of this thought process:
**Thinking Through Page Order and Release**
Right, so we're talking about freeing *old* pages. That's the initial thought. Now, if we're freeing old pages, the `bpage->order` field *should* reflect the order of those old pages. Makes sense so far. It's a key piece of information for the deallocation process, likely determining the size of the block being freed.
The next question β and it's a critical one β is *where* is `bpage->order` set for these old pages? Where did we store the information about their original order? We need to trace its origin to understand its current state and ensure the freeing operation works as expected.
Ah, memory jogged! When we first *allocated* those old pages, we set `bpage->order` to `buffer->subbuf_order` at that point in time. That seems logical β `buffer->subbuf_order` likely represents the original allocation size. Good. It's a link to the original allocation context.
But hold on... is `bpage->order` *modified* anywhere else in the code? This is a crucial check. If it's been updated at any point since the initial allocation, the value we're relying on for freeing might be incorrect. We need to make sure the value hasn't been corrupted or re-interpreted somewhere down the line. That could lead to memory corruption during deallocation.
|
| 33/2 |
2026/08/12 14:22 |
tool |
grepper |
0m
Args: map[Expression:bpage->order =]
Results: map[Output:kernel/trace/ring_buffer.c=2358=static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-2439- }
kernel/trace/ring_buffer.c:2440: bpage->order = cpu_buffer->buffer->subbuf_order;
kernel/trace/ring_buffer.c-2441-
--
kernel/trace/ring_buffer.c=2487=rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
--
kernel/trace/ring_buffer.c-2512- return NULL;
kernel/trace/ring_buffer.c:2513: bpage->order = cpu_buffer->buffer->subbuf_order;
kernel/trace/ring_buffer.c-2514-
--
kernel/trace/ring_buffer.c=6947=ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
--
kernel/trace/ring_buffer.c-6959-
kernel/trace/ring_buffer.c:6960: bpage->order = buffer->subbuf_order;
kernel/trace/ring_buffer.c-6961- cpu_buffer = buffer->buffers[cpu];
]
|
| 34/2 |
2026/08/12 14:22 |
llm |
expert |
0m
Model:
Tokens:
input: 25513
output: 38
thoughts: 73
Thoughts: **Analyzing Potential Order Discrepancies**
Okay, here's what's going through my head as I dissect this code. I'm focusing on how page allocation and ordering are handled within this ring buffer implementation. Specifically, I'm noting that `bpage->order` is being assigned the value of `buffer->subbuf_order` inside `ring_buffer_alloc_read_page`. That's the baseline. My next logical step is to pinpoint any potential vulnerabilities.
The critical question now is: Are there any other locations where we are changing the page being used `bpage->page`, but *don't* also ensure that `bpage->order` is properly updated? This is where a subtle bug could lie, a discrepancy between the page being used and its intended order. I need to be absolutely certain there isn't a situation where the page pointer gets modified without the order following suit. It would corrupt the underlying data structure if these are not in sync. I need to closely examine `ring_buffer_alloc_read_page` to verify that's all there is to it. I'll thoroughly comb through this function to ensure there are no subtle gaps in the logic.
|
| 35/2 |
2026/08/12 14:23 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:6947 LineCount:50]
Results: map[Contents:6947: ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
6948: {
6949: struct ring_buffer_per_cpu *cpu_buffer;
6950: struct buffer_data_read_page *bpage = NULL;
6951: unsigned long flags;
6952:
6953: if (!cpumask_test_cpu(cpu, buffer->cpumask))
6954: return ERR_PTR(-ENODEV);
6955:
6956: bpage = kzalloc_obj(*bpage);
6957: if (!bpage)
6958: return ERR_PTR(-ENOMEM);
6959:
6960: bpage->order = buffer->subbuf_order;
6961: cpu_buffer = buffer->buffers[cpu];
6962: local_irq_save(flags);
6963: arch_spin_lock(&cpu_buffer->lock);
6964:
6965: if (cpu_buffer->free_page) {
6966: bpage->data = cpu_buffer->free_page;
6967: cpu_buffer->free_page = NULL;
6968: }
6969:
6970: arch_spin_unlock(&cpu_buffer->lock);
6971: local_irq_restore(flags);
6972:
6973: if (bpage->data) {
6974: rb_init_data_page(bpage->data);
6975: } else {
6976: bpage->data = alloc_cpu_data(cpu, cpu_buffer->buffer->subbuf_order);
6977: if (!bpage->data) {
6978: kfree(bpage);
6979: return ERR_PTR(-ENOMEM);
6980: }
6981: }
6982:
6983: return bpage;
6984: }
6985: EXPORT_SYMBOL_GPL(ring_buffer_alloc_read_page);
6986:
6987: /**
6988: * ring_buffer_free_read_page - free an allocated read page
6989: * @buffer: the buffer the page was allocate for
6990: * @cpu: the cpu buffer the page came from
6991: * @data_page: the page to free
6992: *
6993: * Free a page allocated from ring_buffer_alloc_read_page.
6994: */
6995: void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
6996: struct buffer_data_read_page *data_page)
]
|
| 36/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 26639
output: 17
thoughts: 51
Thoughts: Okay, here's my interpretation of those thoughts, considering my expertise:
**Examining Ring Buffer Read Operations**
Alright, let's step back for a moment. I was just reviewing the allocation process for this ring buffer read operation, and it's hitting me... `ring_buffer_alloc_read_page` is allocating a `struct buffer_data_read_page`, not directly a `struct buffer_page`. That's a key distinction I need to keep in mind. We are dealing with an intermediary here, a wrapper perhaps, not the raw page itself.
Now, my mind is racing to the complementary function, `ring_buffer_read_page`. I need to understand its behavior in the context of the allocation. Does `ring_buffer_read_page` potentially swap pages internally? Is it responsible for actually moving data between the read buffer and the ring buffer's storage, or does the allocation setup leave that to a later stage? The relationship between those two calls β allocation of the `buffer_data_read_page` and the subsequent read β needs some close scrutiny. I need to trace the data flow to be sure.
|
| 37/2 |
2026/08/12 14:23 |
tool |
grepper |
0m
Args: map[Expression:reader_page]
Results: map[Output:Documentation/trace/ring-buffer-design.rst=36=reader
--
Documentation/trace/ring-buffer-design.rst-38-
Documentation/trace/ring-buffer-design.rst:39:reader_page
Documentation/trace/ring-buffer-design.rst-40- - A page outside the ring buffer used solely (for the most part)
--
Documentation/trace/ring-buffer-design.rst=124=become part of the ring buffer and the head_page will be removed.
Documentation/trace/ring-buffer-design.rst:125:The page after the inserted page (old reader_page) will become the
Documentation/trace/ring-buffer-design.rst-126-new head page.
--
arch/arm64/kvm/hyp/nvhe/trace.c=262=int __tracing_swap_reader(unsigned int cpu)
--
arch/arm64/kvm/hyp/nvhe/trace.c-271- if (hyp_trace_buffer_loaded(&trace_buffer))
arch/arm64/kvm/hyp/nvhe/trace.c:272: ret = simple_ring_buffer_swap_reader_page(
arch/arm64/kvm/hyp/nvhe/trace.c-273- per_cpu_ptr(trace_buffer.simple_rbs, cpu));
--
arch/arm64/kvm/hyp_trace.c=310=static int hyp_trace_enable_tracing(bool enable, void *priv)
--
arch/arm64/kvm/hyp_trace.c-322-
arch/arm64/kvm/hyp_trace.c:323:static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv)
arch/arm64/kvm/hyp_trace.c-324-{
--
arch/arm64/kvm/hyp_trace.c=393=static struct trace_remote_callbacks trace_remote_callbacks = {
--
arch/arm64/kvm/hyp_trace.c-397- .enable_tracing = hyp_trace_enable_tracing,
arch/arm64/kvm/hyp_trace.c:398: .swap_reader_page = hyp_trace_swap_reader_page,
arch/arm64/kvm/hyp_trace.c-399- .reset = hyp_trace_reset,
--
include/linux/ring_buffer.h=295=struct ring_buffer_remote {
include/linux/ring_buffer.h-296- struct trace_buffer_desc *desc;
include/linux/ring_buffer.h:297: int (*swap_reader_page)(unsigned int cpu, void *priv);
include/linux/ring_buffer.h-298- int (*reset)(unsigned int cpu, void *priv);
--
include/linux/simple_ring_buffer.h=22=struct simple_rb_per_cpu {
include/linux/simple_ring_buffer.h-23- struct simple_buffer_page *tail_page;
include/linux/simple_ring_buffer.h:24: struct simple_buffer_page *reader_page;
include/linux/simple_ring_buffer.h-25- struct simple_buffer_page *head_page;
--
include/linux/simple_ring_buffer.h=53=int simple_ring_buffer_reset(struct simple_rb_per_cpu *cpu_buffer);
include/linux/simple_ring_buffer.h-54-
include/linux/simple_ring_buffer.h:55:int simple_ring_buffer_swap_reader_page(struct simple_rb_per_cpu *cpu_buffer);
include/linux/simple_ring_buffer.h-56-
--
include/linux/trace_remote.h-21- * remote to allow writing.
include/linux/trace_remote.h:22: * @swap_reader_page: Called when Tracefs consumes a new page from a
include/linux/trace_remote.h-23- * ring-buffer. It is expected from the remote to isolate a
--
include/linux/trace_remote.h=30=struct trace_remote_callbacks {
--
include/linux/trace_remote.h-34- int (*enable_tracing)(bool enable, void *priv);
include/linux/trace_remote.h:35: int (*swap_reader_page)(unsigned int cpu, void *priv);
include/linux/trace_remote.h-36- int (*reset)(unsigned int cpu, void *priv);
--
kernel/trace/remote_test.c=140=static int remote_test_enable_tracing(bool enable, void *unused)
--
kernel/trace/remote_test.c-153-
kernel/trace/remote_test.c:154:static int remote_test_swap_reader_page(unsigned int cpu, void *unused)
kernel/trace/remote_test.c-155-{
--
kernel/trace/remote_test.c-164-
kernel/trace/remote_test.c:165: return simple_ring_buffer_swap_reader_page(cpu_buffer);
kernel/trace/remote_test.c-166-}
--
kernel/trace/remote_test.c=241=static struct trace_remote_callbacks trace_remote_callbacks = {
--
kernel/trace/remote_test.c-245- .enable_tracing = remote_test_enable_tracing,
kernel/trace/remote_test.c:246: .swap_reader_page = remote_test_swap_reader_page,
kernel/trace/remote_test.c-247- .reset = remote_test_reset,
--
kernel/trace/ring_buffer.c=502=struct ring_buffer_per_cpu {
--
kernel/trace/ring_buffer.c-518- struct buffer_page *commit_page; /* committed pages */
kernel/trace/ring_buffer.c:519: struct buffer_page *reader_page;
kernel/trace/ring_buffer.c-520- unsigned long lost_events;
--
kernel/trace/ring_buffer.c=594=struct ring_buffer_iter {
--
kernel/trace/ring_buffer.c-598- struct buffer_page *head_page;
kernel/trace/ring_buffer.c:599: struct buffer_page *cache_reader_page;
kernel/trace/ring_buffer.c-600- unsigned long cache_read;
--
kernel/trace/ring_buffer.c=882=static bool rb_watermark_hit(struct trace_buffer *buffer, int cpu, int full)
--
kernel/trace/ring_buffer.c-900- raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
kernel/trace/ring_buffer.c:901: pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page;
kernel/trace/ring_buffer.c-902- ret = !pagebusy && full_hit(buffer, cpu, full);
--
kernel/trace/ring_buffer.c=1253=rb_is_head_page(struct buffer_page *page, struct list_head *list)
--
kernel/trace/ring_buffer.c-1265-/*
kernel/trace/ring_buffer.c:1266: * rb_is_reader_page
kernel/trace/ring_buffer.c-1267- *
--
kernel/trace/ring_buffer.c-1271- */
kernel/trace/ring_buffer.c:1272:static bool rb_is_reader_page(struct buffer_page *page)
kernel/trace/ring_buffer.c-1273-{
--
kernel/trace/ring_buffer.c=1961=static void rb_validate_buffer(struct buffer_page *bpage,
--
kernel/trace/ring_buffer.c-1985-
kernel/trace/ring_buffer.c:1986:static void rb_meta_inject_reader_page(struct ring_buffer_per_cpu *cpu_buffer,
kernel/trace/ring_buffer.c-1987- struct ring_buffer_cpu_meta *meta,
--
kernel/trace/ring_buffer.c-1995- /*
kernel/trace/ring_buffer.c:1996: * Insert the reader_page before the original head page.
kernel/trace/ring_buffer.c-1997- * Since the list encode RB_PAGE flags, general list
--
kernel/trace/ring_buffer.c-1999- */
kernel/trace/ring_buffer.c:2000: cpu_buffer->reader_page->list.next = &orig_head->list;
kernel/trace/ring_buffer.c:2001: cpu_buffer->reader_page->list.prev = orig_head->list.prev;
kernel/trace/ring_buffer.c:2002: orig_head->list.prev = &cpu_buffer->reader_page->list;
kernel/trace/ring_buffer.c:2003: bpage->list.next = &cpu_buffer->reader_page->list;
kernel/trace/ring_buffer.c-2004-
kernel/trace/ring_buffer.c-2005- /* Make the head_page the reader page */
kernel/trace/ring_buffer.c:2006: cpu_buffer->reader_page = head_page;
kernel/trace/ring_buffer.c-2007- bpage = head_page;
--
kernel/trace/ring_buffer.c-2018- /* Reset all the indexes */
kernel/trace/ring_buffer.c:2019: bpage = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-2020- meta->buffers[0] = rb_meta_subbuf_idx(meta, bpage->page);
--
kernel/trace/ring_buffer.c=2031=static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-2043- orig_head = head_page = cpu_buffer->head_page;
kernel/trace/ring_buffer.c:2044: orig_reader = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-2045-
--
kernel/trace/ring_buffer.c-2099- if (head_page != orig_head) {
kernel/trace/ring_buffer.c:2100: rb_meta_inject_reader_page(cpu_buffer, meta, orig_head, head_page);
kernel/trace/ring_buffer.c-2101- /* We'll restart verifying from orig_head */
--
kernel/trace/ring_buffer.c-2106- /* If the commit_buffer is the reader page, update the commit page */
kernel/trace/ring_buffer.c:2107: if (meta->commit_buffer == (unsigned long)cpu_buffer->reader_page->page) {
kernel/trace/ring_buffer.c:2108: cpu_buffer->commit_page = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-2109- /* Nothing more to do, the only page is the reader page */
--
kernel/trace/ring_buffer.c-2162- /* Reset the reader page */
kernel/trace/ring_buffer.c:2163: local_set(&cpu_buffer->reader_page->entries, 0);
kernel/trace/ring_buffer.c:2164: rb_init_data_page(cpu_buffer->reader_page->page);
kernel/trace/ring_buffer.c-2165-
--
kernel/trace/ring_buffer.c=2487=rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
--
kernel/trace/ring_buffer.c-2516-
kernel/trace/ring_buffer.c:2517: cpu_buffer->reader_page = bpage;
kernel/trace/ring_buffer.c-2518-
--
kernel/trace/ring_buffer.c-2564-
kernel/trace/ring_buffer.c:2565: INIT_LIST_HEAD(&cpu_buffer->reader_page->list);
kernel/trace/ring_buffer.c-2566- INIT_LIST_HEAD(&cpu_buffer->new_pages);
--
kernel/trace/ring_buffer.c-2605- kfree(cpu_buffer->subbuf_ids);
kernel/trace/ring_buffer.c:2606: free_buffer_page(cpu_buffer->reader_page);
kernel/trace/ring_buffer.c-2607-
--
kernel/trace/ring_buffer.c=2611=static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-2620-
kernel/trace/ring_buffer.c:2621: free_buffer_page(cpu_buffer->reader_page);
kernel/trace/ring_buffer.c-2622-
--
kernel/trace/ring_buffer.c=2987=rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned long nr_pages)
--
kernel/trace/ring_buffer.c-3014- */
kernel/trace/ring_buffer.c:3015: if (cpu_buffer->tail_page == cpu_buffer->reader_page)
kernel/trace/ring_buffer.c-3016- tail_page = rb_list_head(tail_page->next);
--
kernel/trace/ring_buffer.c=3440=rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer)
kernel/trace/ring_buffer.c-3441-{
kernel/trace/ring_buffer.c:3442: return __rb_page_index(cpu_buffer->reader_page,
kernel/trace/ring_buffer.c:3443: cpu_buffer->reader_page->read);
kernel/trace/ring_buffer.c-3444-}
--
kernel/trace/ring_buffer.c=3521=static void rb_inc_iter(struct ring_buffer_iter *iter)
--
kernel/trace/ring_buffer.c-3530- */
kernel/trace/ring_buffer.c:3531: if (iter->head_page == cpu_buffer->reader_page)
kernel/trace/ring_buffer.c-3532- iter->head_page = rb_set_head_page(cpu_buffer);
--
kernel/trace/ring_buffer.c=3571=static void rb_update_meta_reader(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-3574- struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta;
kernel/trace/ring_buffer.c:3575: void *old_reader = cpu_buffer->reader_page->page;
kernel/trace/ring_buffer.c-3576- void *new_reader = reader->page;
--
kernel/trace/ring_buffer.c-3579- id = reader->id;
kernel/trace/ring_buffer.c:3580: cpu_buffer->reader_page->id = id;
kernel/trace/ring_buffer.c-3581- reader->id = 0;
--
kernel/trace/ring_buffer.c=3838=rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-3880- */
kernel/trace/ring_buffer.c:3881: if (!rb_is_reader_page(cpu_buffer->commit_page)) {
kernel/trace/ring_buffer.c-3882- /*
--
kernel/trace/ring_buffer.c-3906- * Note, if the tail page is also on the
kernel/trace/ring_buffer.c:3907: * reader_page, we let it move out.
kernel/trace/ring_buffer.c-3908- */
--
kernel/trace/ring_buffer.c-3911- (cpu_buffer->commit_page ==
kernel/trace/ring_buffer.c:3912: cpu_buffer->reader_page))) {
kernel/trace/ring_buffer.c-3913- local_inc(&cpu_buffer->commit_overrun);
--
kernel/trace/ring_buffer.c=4167=rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-4185- if (RB_WARN_ON(cpu_buffer,
kernel/trace/ring_buffer.c:4186: rb_is_reader_page(cpu_buffer->tail_page)))
kernel/trace/ring_buffer.c-4187- return;
--
kernel/trace/ring_buffer.c=4295=rb_wakeups(struct trace_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-4311-
kernel/trace/ring_buffer.c:4312: if (cpu_buffer->reader_page == cpu_buffer->commit_page)
kernel/trace/ring_buffer.c-4313- return;
--
kernel/trace/ring_buffer.c=5338=u64 ring_buffer_oldest_event_ts(struct trace_buffer *buffer, int cpu)
--
kernel/trace/ring_buffer.c-5350- /*
kernel/trace/ring_buffer.c:5351: * if the tail is on reader_page, oldest time stamp is on the reader
kernel/trace/ring_buffer.c-5352- * page
kernel/trace/ring_buffer.c-5353- */
kernel/trace/ring_buffer.c:5354: if (cpu_buffer->tail_page == cpu_buffer->reader_page)
kernel/trace/ring_buffer.c:5355: bpage = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-5356- else
--
kernel/trace/ring_buffer.c=5580=static void rb_iter_reset(struct ring_buffer_iter *iter)
--
kernel/trace/ring_buffer.c-5589- /* Iterator usage is expected to have record disabled */
kernel/trace/ring_buffer.c:5590: iter->head_page = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c:5591: iter->head = cpu_buffer->reader_page->read;
kernel/trace/ring_buffer.c-5592- iter->next_event = iter->head;
--
kernel/trace/ring_buffer.c-5594-
kernel/trace/ring_buffer.c:5595: iter->cache_reader_page = iter->head_page;
kernel/trace/ring_buffer.c-5596- iter->cache_read = cpu_buffer->read;
--
kernel/trace/ring_buffer.c-5600- iter->read_stamp = cpu_buffer->read_stamp;
kernel/trace/ring_buffer.c:5601: iter->page_stamp = cpu_buffer->reader_page->page->time_stamp;
kernel/trace/ring_buffer.c-5602- } else {
--
kernel/trace/ring_buffer.c=5635=int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
--
kernel/trace/ring_buffer.c-5646- cpu_buffer = iter->cpu_buffer;
kernel/trace/ring_buffer.c:5647: reader = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-5648- head_page = cpu_buffer->head_page;
--
kernel/trace/ring_buffer.c-5674- head_page->read == commit &&
kernel/trace/ring_buffer.c:5675: iter->head == rb_page_size(cpu_buffer->reader_page)));
kernel/trace/ring_buffer.c-5676-}
--
kernel/trace/ring_buffer.c=5739=static struct buffer_page *
kernel/trace/ring_buffer.c:5740:__rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
kernel/trace/ring_buffer.c-5741-{
--
kernel/trace/ring_buffer.c-5747- /* More to read on the reader page */
kernel/trace/ring_buffer.c:5748: if (cpu_buffer->reader_page->read < rb_page_size(cpu_buffer->reader_page)) {
kernel/trace/ring_buffer.c:5749: if (!cpu_buffer->reader_page->read)
kernel/trace/ring_buffer.c:5750: cpu_buffer->read_stamp = cpu_buffer->reader_page->page->time_stamp;
kernel/trace/ring_buffer.c:5751: return cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-5752- }
--
kernel/trace/ring_buffer.c-5755-
kernel/trace/ring_buffer.c:5756: WARN_ON_ONCE(cpu_buffer->remote->swap_reader_page(cpu_buffer->cpu,
kernel/trace/ring_buffer.c-5757- cpu_buffer->remote->priv));
--
kernel/trace/ring_buffer.c-5788- cpu_buffer->head_page = new_head;
kernel/trace/ring_buffer.c:5789: cpu_buffer->reader_page = new_reader;
kernel/trace/ring_buffer.c:5790: cpu_buffer->reader_page->read = 0;
kernel/trace/ring_buffer.c-5791- cpu_buffer->pages = &new_head->list;
--
kernel/trace/ring_buffer.c-5794-
kernel/trace/ring_buffer.c:5795: return rb_page_size(cpu_buffer->reader_page) ? cpu_buffer->reader_page : NULL;
kernel/trace/ring_buffer.c-5796-}
--
kernel/trace/ring_buffer.c=5798=static struct buffer_page *
kernel/trace/ring_buffer.c:5799:__rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
kernel/trace/ring_buffer.c-5800-{
--
kernel/trace/ring_buffer.c-5827-
kernel/trace/ring_buffer.c:5828: reader = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-5829-
kernel/trace/ring_buffer.c-5830- /* If there's more to read, return this page */
kernel/trace/ring_buffer.c:5831: if (cpu_buffer->reader_page->read < rb_page_size(reader))
kernel/trace/ring_buffer.c-5832- goto out;
--
kernel/trace/ring_buffer.c-5835- if (RB_WARN_ON(cpu_buffer,
kernel/trace/ring_buffer.c:5836: cpu_buffer->reader_page->read > rb_page_size(reader)))
kernel/trace/ring_buffer.c-5837- goto out;
--
kernel/trace/ring_buffer.c-5840- reader = NULL;
kernel/trace/ring_buffer.c:5841: if (cpu_buffer->commit_page == cpu_buffer->reader_page)
kernel/trace/ring_buffer.c-5842- goto out;
--
kernel/trace/ring_buffer.c-5850- */
kernel/trace/ring_buffer.c:5851: local_set(&cpu_buffer->reader_page->write, 0);
kernel/trace/ring_buffer.c:5852: local_set(&cpu_buffer->reader_page->entries, 0);
kernel/trace/ring_buffer.c:5853: rb_init_data_page(cpu_buffer->reader_page->page);
kernel/trace/ring_buffer.c:5854: cpu_buffer->reader_page->real_end = 0;
kernel/trace/ring_buffer.c-5855-
--
kernel/trace/ring_buffer.c-5862- goto out;
kernel/trace/ring_buffer.c:5863: cpu_buffer->reader_page->list.next = rb_list_head(reader->list.next);
kernel/trace/ring_buffer.c:5864: cpu_buffer->reader_page->list.prev = reader->list.prev;
kernel/trace/ring_buffer.c-5865-
--
kernel/trace/ring_buffer.c-5873- /* The reader page will be pointing to the new head */
kernel/trace/ring_buffer.c:5874: rb_set_list_to_head(&cpu_buffer->reader_page->list);
kernel/trace/ring_buffer.c-5875-
--
kernel/trace/ring_buffer.c-5898-
kernel/trace/ring_buffer.c:5899: ret = rb_head_page_replace(reader, cpu_buffer->reader_page);
kernel/trace/ring_buffer.c-5900-
--
kernel/trace/ring_buffer.c-5917- */
kernel/trace/ring_buffer.c:5918: rb_list_head(reader->list.next)->prev = &cpu_buffer->reader_page->list;
kernel/trace/ring_buffer.c-5919- rb_inc_page(&cpu_buffer->head_page);
--
kernel/trace/ring_buffer.c-5924- /* Finally update the reader page to the new head */
kernel/trace/ring_buffer.c:5925: cpu_buffer->reader_page = reader;
kernel/trace/ring_buffer.c:5926: cpu_buffer->reader_page->read = 0;
kernel/trace/ring_buffer.c-5927-
--
kernel/trace/ring_buffer.c=5980=static struct buffer_page *
kernel/trace/ring_buffer.c:5981:rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
kernel/trace/ring_buffer.c-5982-{
kernel/trace/ring_buffer.c:5983: return cpu_buffer->remote ? __rb_get_reader_page_from_remote(cpu_buffer) :
kernel/trace/ring_buffer.c:5984: __rb_get_reader_page(cpu_buffer);
kernel/trace/ring_buffer.c-5985-}
--
kernel/trace/ring_buffer.c=5987=static void rb_advance_reader(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-5992-
kernel/trace/ring_buffer.c:5993: reader = rb_get_reader_page(cpu_buffer);
kernel/trace/ring_buffer.c-5994-
--
kernel/trace/ring_buffer.c-6006- length = rb_event_length(event);
kernel/trace/ring_buffer.c:6007: cpu_buffer->reader_page->read += length;
kernel/trace/ring_buffer.c-6008- cpu_buffer->read_bytes += length;
--
kernel/trace/ring_buffer.c=6046=rb_buffer_peek(struct ring_buffer_per_cpu *cpu_buffer, u64 *ts,
--
kernel/trace/ring_buffer.c-6064-
kernel/trace/ring_buffer.c:6065: reader = rb_get_reader_page(cpu_buffer);
kernel/trace/ring_buffer.c-6066- if (!reader)
--
kernel/trace/ring_buffer.c=6120=rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
--
kernel/trace/ring_buffer.c-6140- if (unlikely(iter->cache_read != cpu_buffer->read ||
kernel/trace/ring_buffer.c:6141: iter->cache_reader_page != cpu_buffer->reader_page ||
kernel/trace/ring_buffer.c-6142- iter->cache_pages_removed != cpu_buffer->pages_removed))
--
kernel/trace/ring_buffer.c=6526=static void rb_update_meta_page(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-6532-
kernel/trace/ring_buffer.c:6533: meta->reader.read = cpu_buffer->reader_page->read;
kernel/trace/ring_buffer.c:6534: meta->reader.id = rb_page_id(cpu_buffer, cpu_buffer->reader_page,
kernel/trace/ring_buffer.c:6535: cpu_buffer->reader_page->id);
kernel/trace/ring_buffer.c-6536-
--
kernel/trace/ring_buffer.c=6550=rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)
--
kernel/trace/ring_buffer.c-6565- cpu_buffer->last_overrun = 0;
kernel/trace/ring_buffer.c:6566: cpu_buffer->reader_page->read = 0;
kernel/trace/ring_buffer.c-6567-
--
kernel/trace/ring_buffer.c-6582-
kernel/trace/ring_buffer.c:6583: INIT_LIST_HEAD(&cpu_buffer->reader_page->list);
kernel/trace/ring_buffer.c-6584- INIT_LIST_HEAD(&cpu_buffer->new_pages);
kernel/trace/ring_buffer.c:6585: rb_clear_buffer_page(cpu_buffer->reader_page);
kernel/trace/ring_buffer.c-6586-
--
kernel/trace/ring_buffer.c=7067=int ring_buffer_read_page(struct trace_buffer *buffer,
--
kernel/trace/ring_buffer.c-7105-
kernel/trace/ring_buffer.c:7106: reader = rb_get_reader_page(cpu_buffer);
kernel/trace/ring_buffer.c-7107- if (!reader)
--
kernel/trace/ring_buffer.c-7128- if (read || (len < (size - read)) ||
kernel/trace/ring_buffer.c:7129: cpu_buffer->reader_page == cpu_buffer->commit_page ||
kernel/trace/ring_buffer.c-7130- force_memcpy) {
kernel/trace/ring_buffer.c:7131: struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
kernel/trace/ring_buffer.c-7132- unsigned int rpos = read;
--
kernel/trace/ring_buffer.c-7144- (!read || (len < (size - read)) ||
kernel/trace/ring_buffer.c:7145: cpu_buffer->reader_page == cpu_buffer->commit_page))
kernel/trace/ring_buffer.c-7146- return -1;
--
kernel/trace/ring_buffer.c=7319=int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
--
kernel/trace/ring_buffer.c-7408- * Collect buffers from the cpu_buffer pages list and the
kernel/trace/ring_buffer.c:7409: * reader_page on old_pages, so they can be freed later when not
kernel/trace/ring_buffer.c-7410- * under a spinlock. The pages list is a linked list with no
--
kernel/trace/ring_buffer.c-7414- list_add(&old_pages, cpu_buffer->pages);
kernel/trace/ring_buffer.c:7415: list_add(&cpu_buffer->reader_page->list, &old_pages);
kernel/trace/ring_buffer.c-7416-
kernel/trace/ring_buffer.c-7417- /* One page was allocated for the reader page */
kernel/trace/ring_buffer.c:7418: cpu_buffer->reader_page = list_entry(cpu_buffer->new_pages.next,
kernel/trace/ring_buffer.c-7419- struct buffer_page, list);
kernel/trace/ring_buffer.c:7420: list_del_init(&cpu_buffer->reader_page->list);
kernel/trace/ring_buffer.c-7421-
--
kernel/trace/ring_buffer.c=7501=static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer,
--
kernel/trace/ring_buffer.c-7509-
kernel/trace/ring_buffer.c:7510: id = rb_page_id(cpu_buffer, cpu_buffer->reader_page, id);
kernel/trace/ring_buffer.c:7511: subbuf_ids[id++] = cpu_buffer->reader_page;
kernel/trace/ring_buffer.c-7512- cnt++;
--
kernel/trace/ring_buffer.c=7839=int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
--
kernel/trace/ring_buffer.c-7856-
kernel/trace/ring_buffer.c:7857: reader_size = rb_page_size(cpu_buffer->reader_page);
kernel/trace/ring_buffer.c-7858-
--
kernel/trace/ring_buffer.c-7863- */
kernel/trace/ring_buffer.c:7864: if (cpu_buffer->reader_page->read < reader_size) {
kernel/trace/ring_buffer.c:7865: while (cpu_buffer->reader_page->read < reader_size)
kernel/trace/ring_buffer.c-7866- rb_advance_reader(cpu_buffer);
--
kernel/trace/ring_buffer.c-7870- /* Did the reader catch up with the writer? */
kernel/trace/ring_buffer.c:7871: if (cpu_buffer->reader_page == cpu_buffer->commit_page)
kernel/trace/ring_buffer.c-7872- goto out;
kernel/trace/ring_buffer.c-7873-
kernel/trace/ring_buffer.c:7874: reader = rb_get_reader_page(cpu_buffer);
kernel/trace/ring_buffer.c-7875- if (WARN_ON(!reader))
--
kernel/trace/ring_buffer.c-7881- if (missed_events) {
kernel/trace/ring_buffer.c:7882: if (cpu_buffer->reader_page != cpu_buffer->commit_page) {
kernel/trace/ring_buffer.c-7883- struct buffer_data_page *dpage = reader->page;
--
kernel/trace/ring_buffer.c-7902- local_add(RB_MISSED_EVENTS, &dpage->commit);
kernel/trace/ring_buffer.c:7903: } else if (!WARN_ONCE(cpu_buffer->reader_page == cpu_buffer->tail_page,
kernel/trace/ring_buffer.c-7904- "Reader on commit with %ld missed events",
--
kernel/trace/ring_buffer.c-7917- pr_info("Ring buffer [%d] commit overrun lost %ld events at timestamp:%lld\n",
kernel/trace/ring_buffer.c:7918: cpu, missed_events, cpu_buffer->reader_page->page->time_stamp);
kernel/trace/ring_buffer.c-7919- }
--
kernel/trace/ring_buffer.c-7927- /* Some archs do not have data cache coherency between kernel and user-space */
kernel/trace/ring_buffer.c:7928: flush_kernel_vmap_range(cpu_buffer->reader_page->page,
kernel/trace/ring_buffer.c-7929- buffer->subbuf_size + BUF_PAGE_HDR_SIZE);
--
kernel/trace/simple_ring_buffer.c=90=static int simple_rb_find_head(struct simple_rb_per_cpu *cpu_buffer)
--
kernel/trace/simple_ring_buffer.c-120-/**
kernel/trace/simple_ring_buffer.c:121: * simple_ring_buffer_swap_reader_page - Swap ring-buffer head with the reader
kernel/trace/simple_ring_buffer.c-122- * @cpu_buffer: A simple_rb_per_cpu
--
kernel/trace/simple_ring_buffer.c-129- */
kernel/trace/simple_ring_buffer.c:130:int simple_ring_buffer_swap_reader_page(struct simple_rb_per_cpu *cpu_buffer)
kernel/trace/simple_ring_buffer.c-131-{
--
kernel/trace/simple_ring_buffer.c-139-
kernel/trace/simple_ring_buffer.c:140: reader = cpu_buffer->reader_page;
kernel/trace/simple_ring_buffer.c-141-
--
kernel/trace/simple_ring_buffer.c-167- cpu_buffer->head_page->link.prev = &reader->link;
kernel/trace/simple_ring_buffer.c:168: cpu_buffer->reader_page = head;
kernel/trace/simple_ring_buffer.c-169- cpu_buffer->meta->reader.lost_events = overrun - cpu_buffer->last_overrun;
kernel/trace/simple_ring_buffer.c:170: cpu_buffer->meta->reader.id = cpu_buffer->reader_page->id;
kernel/trace/simple_ring_buffer.c-171- cpu_buffer->last_overrun = overrun;
--
kernel/trace/simple_ring_buffer.c-174-}
kernel/trace/simple_ring_buffer.c:175:EXPORT_SYMBOL_GPL(simple_ring_buffer_swap_reader_page);
kernel/trace/simple_ring_buffer.c-176-
--
kernel/trace/simple_ring_buffer.c=334=int simple_ring_buffer_reset(struct simple_rb_per_cpu *cpu_buffer)
--
kernel/trace/simple_ring_buffer.c-354-
kernel/trace/simple_ring_buffer.c:355: simple_bpage_reset(cpu_buffer->reader_page);
kernel/trace/simple_ring_buffer.c-356-
--
kernel/trace/simple_ring_buffer.c=375=int simple_ring_buffer_init_mm(struct simple_rb_per_cpu *cpu_buffer,
--
kernel/trace/simple_ring_buffer.c-410-
kernel/trace/simple_ring_buffer.c:411: cpu_buffer->reader_page = bpage;
kernel/trace/simple_ring_buffer.c-412- cpu_buffer->tail_page = bpage + 1;
--
kernel/trace/trace_remote.c=65=static int trace_remote_load(struct trace_remote *remote)
--
kernel/trace/trace_remote.c-79- rb_remote->desc = desc;
kernel/trace/trace_remote.c:80: rb_remote->swap_reader_page = remote->cbs->swap_reader_page;
kernel/trace/trace_remote.c-81- rb_remote->priv = remote->priv;
]
|
| 38/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 37258
output: 38
thoughts: 433
Thoughts: **Analyzing the Memory Management in `ring_buffer_subbuf_order_set`**
Okay, let's break this down. My initial observation is focused on how `ring_buffer_subbuf_order_set` handles page allocation and deallocation. Specifically, I'm tracing the flow with regard to the `reader_page`. I see that a new `reader_page` is acquired from `cpu_buffer->new_pages`. That's straightforward enough. The older `reader_page` is then added to a list called `old_pages`. So far, so good, a standard swap.
The interesting part, of course, is the deallocation of these "old" pages: I see that `list_for_each_entry_safe` is used to iterate through the `old_pages` list, and `free_buffer_page` is invoked on each element. This part looks correct at face value, in the sense that resources are being returned when they are no longer in use.
Now the question is around how `free_buffer_page` should behave. If I think about what the state of `bpage` should be after `ring_buffer_subbuf_order_set`, it's not clear. `bpage->order` is supposed to be the *old* sub-buffer order before it was changed. That's a key detail. But then I am struck by a potentially critical inconsistency: Why, in the next function, would `compound_order(page) != order` be possible? Why isn't the order updated? Something must be going wrong if the order is not correctly tracked, or if the order is being confused with the physical page of the buffer.
This hints that there might be a subtle bug. It's possible that the `bpage->order` value wasn't properly updated at some point, or that the association between `bpage->page` and the correct "order" is being lost. The next step is to examine the allocation, which will hopefully illuminate the situation.
Ah, yes. Here we go. `ring_buffer_alloc_read_page` is relevant. It allocates a `struct buffer_data_read_page`, and critically, it sets the `order` field to `buffer->subbuf_order`. So, it looks like `buffer->subbuf_order` is being set correctly during initial allocation.
I'll keep digging. Then, I see `ring_buffer_read_page` swaps `bpage->data` (which is a `struct buffer_data_page *`) with `cpu_buffer->free_page`. I will look into that further. Oh, and I see that `cpu_buffer->free_page` is also a `struct buffer_data_page *`. So `ring_buffer_read_page` is swapping the data pointer itself. I should check how that impacts the "order" value.
|
| 39/2 |
2026/08/12 14:23 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7067 LineCount:50]
Results: map[Contents:7067: int ring_buffer_read_page(struct trace_buffer *buffer,
7068: struct buffer_data_read_page *data_page,
7069: size_t len, int cpu, int full)
7070: {
7071: struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu];
7072: struct ring_buffer_event *event;
7073: struct buffer_data_page *dpage;
7074: struct buffer_page *reader;
7075: long missed_events;
7076: unsigned int commit;
7077: unsigned int size;
7078: unsigned int read;
7079: u64 save_timestamp;
7080: bool force_memcpy;
7081:
7082: if (!cpumask_test_cpu(cpu, buffer->cpumask))
7083: return -1;
7084:
7085: /*
7086: * If len is not big enough to hold the page header, then
7087: * we can not copy anything.
7088: */
7089: if (len <= BUF_PAGE_HDR_SIZE)
7090: return -1;
7091:
7092: len -= BUF_PAGE_HDR_SIZE;
7093:
7094: if (!data_page || !data_page->data)
7095: return -1;
7096:
7097: if (data_page->order != buffer->subbuf_order)
7098: return -1;
7099:
7100: dpage = data_page->data;
7101: if (!dpage)
7102: return -1;
7103:
7104: guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
7105:
7106: reader = rb_get_reader_page(cpu_buffer);
7107: if (!reader)
7108: return -1;
7109:
7110: event = rb_reader_event(cpu_buffer);
7111:
7112: read = reader->read;
7113: commit = rb_page_commit(reader);
7114: size = rb_page_size(reader);
7115:
7116: /* Check if any events were dropped */
]
|
| 40/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 39043
output: 38
thoughts: 14
Thoughts: Okay, let's dive into this `ring_buffer_read_page` function. I need to understand its role within the larger system. I'm already pretty familiar with ring buffers, of course, but the specifics of *this* implementation are what matters. I want to see how data is read from the buffer, handled, and potentially passed on. I'm going to pay close attention to the memory access patterns β are they efficient? Are there any potential bottlenecks I should be aware of? I'll be looking for concurrency concerns, too. Are there any locks or atomic operations that could impact performance? And, importantly, I need to see how errors are handled. What happens if the buffer is empty? Or if there's a problem with the underlying storage? I'm going to look for any clever optimizations or potential gotchas. I need to be able to evaluate this function's contribution to overall performance and reliability. Let's get into it.
|
| 41/2 |
2026/08/12 14:23 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7117 LineCount:50]
Results: map[Contents:7117: missed_events = cpu_buffer->lost_events;
7118:
7119: force_memcpy = cpu_buffer->mapped || cpu_buffer->remote;
7120:
7121: /*
7122: * If this page has been partially read or
7123: * if len is not big enough to read the rest of the page or
7124: * a writer is still on the page, then
7125: * we must copy the data from the page to the buffer.
7126: * Otherwise, we can simply swap the page with the one passed in.
7127: */
7128: if (read || (len < (size - read)) ||
7129: cpu_buffer->reader_page == cpu_buffer->commit_page ||
7130: force_memcpy) {
7131: struct buffer_data_page *rpage = cpu_buffer->reader_page->page;
7132: unsigned int rpos = read;
7133: unsigned int pos = 0;
7134: unsigned int event_size;
7135: unsigned int flags = 0;
7136:
7137: /*
7138: * If a full page is expected, this can still be returned
7139: * if there's been a previous partial read and the
7140: * rest of the page can be read and the commit page is off
7141: * the reader page.
7142: */
7143: if (full &&
7144: (!read || (len < (size - read)) ||
7145: cpu_buffer->reader_page == cpu_buffer->commit_page))
7146: return -1;
7147:
7148: if (len > (size - read))
7149: len = (size - read);
7150:
7151: /* Always keep the time extend and data together */
7152: event_size = rb_event_ts_length(event);
7153:
7154: if (len < event_size)
7155: return -1;
7156:
7157: if (commit & RB_MISSED_EVENTS)
7158: flags = RB_MISSED_EVENTS;
7159:
7160: /* save the current timestamp, since the user will need it */
7161: save_timestamp = cpu_buffer->read_stamp;
7162:
7163: /* Need to copy one event at a time */
7164: do {
7165: /* We need the size of one event, because
7166: * rb_advance_reader only advances by one event,
]
|
| 42/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 40049
output: 38
thoughts: 20
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving Deeper into the Swap Logic**
Okay, so we were looking at `ring_buffer_read_page`, specifically to understand the intricacies of the swap logic. Now, let's keep going and see how the memory management interacts with this ring buffer mechanism when swapping is involved. I'm keen to see if there are any subtle optimization strategies or potential pitfalls in how they handle page replacement and data transfer within this context. I'm particularly interested in seeing how they deal with the atomicity and consistency of the data across the swap and how they handle potential race conditions. Let's see if we can find any areas where the design may be subject to thrashing or increased latency.
|
| 43/2 |
2026/08/12 14:23 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7167 LineCount:50]
Results: map[Contents:7167: * whereas rb_event_ts_length may include the size of
7168: * one or two events.
7169: * We have already ensured there's enough space if this
7170: * is a time extend. */
7171: event_size = rb_event_length(event);
7172: memcpy(dpage->data + pos, rpage->data + rpos, event_size);
7173:
7174: len -= event_size;
7175:
7176: rb_advance_reader(cpu_buffer);
7177: rpos = reader->read;
7178: pos += event_size;
7179:
7180: if (rpos >= size)
7181: break;
7182:
7183: event = rb_reader_event(cpu_buffer);
7184: /* Always keep the time extend and data together */
7185: event_size = rb_event_ts_length(event);
7186: } while (len >= event_size);
7187:
7188: /* update dpage */
7189: local_set(&dpage->commit, pos | flags);
7190: dpage->time_stamp = save_timestamp;
7191:
7192: /* we copied everything to the beginning */
7193: read = 0;
7194: } else {
7195: /* update the entry counter */
7196: cpu_buffer->read += rb_page_entries(reader);
7197: cpu_buffer->read_bytes += rb_page_size(reader);
7198:
7199: /* swap the pages */
7200: rb_init_data_page(dpage);
7201: dpage = reader->page;
7202: reader->page = data_page->data;
7203: local_set(&reader->write, 0);
7204: local_set(&reader->entries, 0);
7205: reader->read = 0;
7206: data_page->data = dpage;
7207: if (!missed_events && rb_data_page_commit(dpage) & RB_MISSED_EVENTS)
7208: missed_events = -1;
7209:
7210: /*
7211: * Use the real_end for the data size,
7212: * This gives us a chance to store the lost events
7213: * on the page.
7214: */
7215: if (reader->real_end)
7216: local_set(&dpage->commit, reader->real_end);
]
|
| 44/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 41025
output: 38
thoughts: 292
Thoughts: **Analyzing the Ring Buffer Read Page Operation**
Okay, here's what's going through my head as I dissect this `ring_buffer_read_page` function. I see the code block where the core page swapping happens:
```c
/* swap the pages */
rb_init_data_page(dpage);
dpage = reader->page;
reader->page = data_page->data;
local_set(&reader->write, 0);
local_set(&reader->entries, 0);
reader->read = 0;
data_page->data = dpage;
```
Interesting, so `reader->page` is being swapped with `data_page->data`. I have to step back and make sure I understand the memory management. Now, `data_page->data` was allocated based on the `data_page->order`. And that `data_page->order` is crucial because there's a check, right here:
```c
if (data_page->order != buffer->subbuf_order)
return -1;
```
Ah, right, so `data_page->order` *must* be equal to `buffer->subbuf_order` for the read to succeed. That's a sanity check to make sure the data size is what's expected. Okay, so far, so good.
But, a red flag! What if `buffer->subbuf_order` is being modified concurrently? `ring_buffer_read_page` doesn't seem to hold `buffer->mutex`, right? It only holds `cpu_buffer->reader_lock`. That's a potential race condition! I know `ring_buffer_subbuf_order_set` *does* hold `buffer->mutex`, but it also *takes* `cpu_buffer->reader_lock`. So, the lock ordering seems to protect the modification in `ring_buffer_subbuf_order_set` from races from `ring_buffer_read_page`. Okay, good. That should protect against the race, but I'll still have to review how the reader and the subbuf order operations are interlocked.
|
| 45/2 |
2026/08/12 14:23 |
tool |
read-file |
0m
Args: map[File:kernel/trace/ring_buffer.c FirstLine:7390 LineCount:50]
Results: map[Contents:7390: }
7391:
7392: for_each_buffer_cpu(buffer, cpu) {
7393: struct buffer_data_page *old_free_data_page;
7394: struct list_head old_pages;
7395: unsigned long flags;
7396:
7397: if (!cpumask_test_cpu(cpu, buffer->cpumask))
7398: continue;
7399:
7400: cpu_buffer = buffer->buffers[cpu];
7401:
7402: raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
7403:
7404: /* Clear the head bit to make the link list normal to read */
7405: rb_head_page_deactivate(cpu_buffer);
7406:
7407: /*
7408: * Collect buffers from the cpu_buffer pages list and the
7409: * reader_page on old_pages, so they can be freed later when not
7410: * under a spinlock. The pages list is a linked list with no
7411: * head, adding old_pages turns it into a regular list with
7412: * old_pages being the head.
7413: */
7414: list_add(&old_pages, cpu_buffer->pages);
7415: list_add(&cpu_buffer->reader_page->list, &old_pages);
7416:
7417: /* One page was allocated for the reader page */
7418: cpu_buffer->reader_page = list_entry(cpu_buffer->new_pages.next,
7419: struct buffer_page, list);
7420: list_del_init(&cpu_buffer->reader_page->list);
7421:
7422: /* Install the new pages, remove the head from the list */
7423: cpu_buffer->pages = cpu_buffer->new_pages.next;
7424: list_del_init(&cpu_buffer->new_pages);
7425: cpu_buffer->cnt++;
7426:
7427: cpu_buffer->head_page
7428: = list_entry(cpu_buffer->pages, struct buffer_page, list);
7429: cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page;
7430:
7431: cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update;
7432: cpu_buffer->nr_pages_to_update = 0;
7433:
7434: old_free_data_page = cpu_buffer->free_page;
7435: cpu_buffer->free_page = NULL;
7436:
7437: rb_head_page_activate(cpu_buffer);
7438:
7439: raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
]
|
| 46/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 42658
output: 19
thoughts: 1499
Thoughts: **Analyzing a Concurrency Bug in Ring Buffer Subbuffer Order Setting**
Okay, so I'm looking at this code and I've got a problem. `ring_buffer_subbuf_order_set` takes `cpu_buffer->reader_lock`, right? But wait a minute, it updates `buffer->subbuf_order` *before* taking the lock. That's a potential race condition alert.
Specifically, it updates `buffer->subbuf_order` to the *new* order and calculates the buffer and page sizes. Then it allocates pages.
Now, imagine `ring_buffer_read_page` is called concurrently on another CPU. That function does a check: `data_page->order != buffer->subbuf_order`. `data_page->order` is the *user's* order, and `buffer->subbuf_order` has already been updated to the *new* order. So, if the user passed in a page with the new order, that check *succeeds*!
Then `ring_buffer_read_page` grabs the `cpu_buffer->reader_lock`. Then it swaps `reader->page` with `data_page->data`. But the `reader->page` is the old page with the *old* order. And `data_page->data` has the *new* order! The code just swaps the *pointers*. The important thing is, *the `reader->order` field is never updated*! It *remains* the old order.
The `reader` struct is a `struct buffer_page`. So, after the swap, `reader->page` points to a page with the *new* order, but `reader->order` still reflects the *old* order. This seems wrong.
Then, when `ring_buffer_subbuf_order_set` *does* take the `reader_lock` to free the old pages, it calls `free_buffer_page(reader)`. `free_buffer_page` then calls `free_pages((unsigned long)reader->page, reader->order)`. Here's the kicker: `reader->page` now points to the *new* page, but `reader->order` is still the *old* order! Boom. That's a classic mismatch.
If `reader->order` is the OLD order, and `reader->page` has the NEW order, `free_pages` is called with the wrong order, that's exactly where the bug lies. This explains the bug report, with `VM_BUG_ON_PAGE(compound && compound_order(page) != order, page);`.
Let's break it down in a trace.
1. Thread A calls `ring_buffer_subbuf_order_set(buffer, NEW_ORDER)`.
2. Thread A sets `buffer->subbuf_order = NEW_ORDER`.
3. Thread A starts allocating new pages, which takes time.
4. Thread B calls `ring_buffer_alloc_read_page(buffer, cpu)`. It allocates `data_page` and allocates the data for it with `NEW_ORDER`.
5. Thread B calls `ring_buffer_read_page(buffer, data_page, ...)`. It checks the order, sees it matches and locks the reader. It swaps. So now, the `reader` page is NEW and its `order` is OLD. `data_page->data` has OLD.
6. Thread A finishes allocating new pages.
7. Thread A takes `cpu_buffer->reader_lock`.
8. Thread A replaces the old `reader_page`.
9. Thread A puts the old `reader_page` into `old_pages`.
10. Thread A releases `cpu_buffer->reader_lock`.
11. Thread A iterates over `old_pages` and calls `free_buffer_page(bpage)`.
12. For the old `reader_page`, `free_buffer_page` calls `free_pages(bpage->page, bpage->order)`. `bpage->page` is now NEW but `bpage->order` is OLD, triggering the bug.
Now, wait a minute, is `ring_buffer_alloc_read_page` exposed to userspace? Looks like the reproducer uses:
* Writing to `/sys/kernel/tracing/buffer_subbuf_size_kb` (calls `ring_buffer_subbuf_order_set`).
* Reading from `/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw` (calls `tracing_buffers_read`).
* Writing to
|
| 47/2 |
2026/08/12 14:23 |
tool |
grepper |
0m
Args: map[Expression:tracing_buffers_read]
Results: map[Output:kernel/trace/trace.c=7070=tracing_buffers_poll(struct file *filp, poll_table *poll_table)
--
kernel/trace/trace.c-7077-
kernel/trace/trace.c:7078:ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
kernel/trace/trace.c-7079- size_t count, loff_t *ppos)
--
kernel/trace/trace.c=7469=static const struct file_operations tracing_buffers_fops = {
kernel/trace/trace.c-7470- .open = tracing_buffers_open,
kernel/trace/trace.c:7471: .read = tracing_buffers_read,
kernel/trace/trace.c-7472- .poll = tracing_buffers_poll,
--
kernel/trace/trace.h=721=int tracing_buffers_open(struct inode *inode, struct file *filp);
kernel/trace/trace.h:722:ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
kernel/trace/trace.h-723- size_t count, loff_t *ppos);
--
kernel/trace/trace_snapshot.c=764=const struct file_operations snapshot_raw_fops = {
kernel/trace/trace_snapshot.c-765- .open = snapshot_raw_open,
kernel/trace/trace_snapshot.c:766: .read = tracing_buffers_read,
kernel/trace/trace_snapshot.c-767- .release = tracing_buffers_release,
]
|
| 48/2 |
2026/08/12 14:23 |
llm |
expert |
0m
Model:
Tokens:
input: 45639
output: 36
thoughts: 11
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `tracing_buffers_read`**
Right, let's get into `tracing_buffers_read`. I want to understand exactly what's going on here. I'm going to start by carefully parsing the name; it suggests a process involved with *reading* data from *tracing buffers*. Given the name, I'm expecting to find the mechanics of extracting traced events or logs, likely from an in-memory or persistent buffer. I'll need to see how the buffer is structured, what the read process does with the data, and if there are any considerations for concurrency or atomicity. I need to keep in mind the potential for partial reads, error handling, and perhaps even buffering strategies within the reading process itself. It's time to dig in and break it down. Let's get to the code.
|
| 49/2 |
2026/08/12 14:24 |
tool |
read-file |
0m
Args: map[File:kernel/trace/trace.c FirstLine:7078 LineCount:50]
Results: map[Contents:7078: ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
7079: size_t count, loff_t *ppos)
7080: {
7081: struct ftrace_buffer_info *info = filp->private_data;
7082: struct trace_iterator *iter = &info->iter;
7083: void *trace_data;
7084: int page_size;
7085: ssize_t ret = 0;
7086: ssize_t size;
7087:
7088: if (!count)
7089: return 0;
7090:
7091: if (iter->snapshot && tracer_uses_snapshot(iter->tr->current_trace))
7092: return -EBUSY;
7093:
7094: page_size = ring_buffer_subbuf_size_get(iter->array_buffer->buffer);
7095:
7096: /* Make sure the spare matches the current sub buffer size */
7097: if (info->spare) {
7098: if (page_size != info->spare_size) {
7099: ring_buffer_free_read_page(iter->array_buffer->buffer,
7100: info->spare_cpu, info->spare);
7101: info->spare = NULL;
7102: }
7103: }
7104:
7105: if (!info->spare) {
7106: info->spare = ring_buffer_alloc_read_page(iter->array_buffer->buffer,
7107: iter->cpu_file);
7108: if (IS_ERR(info->spare)) {
7109: ret = PTR_ERR(info->spare);
7110: info->spare = NULL;
7111: } else {
7112: info->spare_cpu = iter->cpu_file;
7113: info->spare_size = page_size;
7114: }
7115: }
7116: if (!info->spare)
7117: return ret;
7118:
7119: /* Do we have previous read data to read? */
7120: if (info->read < page_size)
7121: goto read;
7122:
7123: again:
7124: trace_access_lock(iter->cpu_file);
7125: ret = ring_buffer_read_page(iter->array_buffer->buffer,
7126: info->spare,
7127: count,
]
|
| 50/2 |
2026/08/12 14:24 |
llm |
expert |
0m
Model:
Tokens:
input: 46631
output: 22
thoughts: 697
Thoughts: **Analyzing the Ring Buffer and the Potential for a Critical Bug**
Alright, this is exactly what I thought was happening! The `tracing_buffers_read` function is the key here. I'm seeing that it uses the `ring_buffer_subbuf_size_get` function. This fetches the page size from the ring buffer. Now, if `ring_buffer_subbuf_order_set` is called concurrently, then the buffer's `subbuf_order` is updated. That `ring_buffer_subbuf_size_get` will return the *new* size, which is based on the new order. The code then allocates `info->spare` with this *new* size using `ring_buffer_alloc_read_page`. Then, it calls `ring_buffer_read_page`, which crucially checks `data_page->order == buffer->subbuf_order`, which will be the NEW order. It's supposed to swap pages.
Here's the problem: The *old* `reader_page` in the ring buffer still has its `bpage->order` set to the *OLD* order. So, `reader_page->page` gets a page with `NEW_ORDER`, but `reader_page->order` *remains* `OLD_ORDER`! Then `ring_buffer_subbuf_order_set` will get the lock, replace `reader_page`, and free the old `reader_page`. And when freeing the *old* `reader_page`, it calls `free_pages(bpage->page, bpage->order)`.
Since the `bpage->page` is the *new* page, but the `bpage->order` is the *old* order, this is where the bug kicks in. That means we're freeing the wrong amount of memory!
Hang on, what if `OLD_ORDER < NEW_ORDER`? Then `free_pages` gets called with a smaller order than the actual page order. That's even worse β it's going to free *only part* of the compound page!
Let's see, `free_pages` calls `__free_pages`. `__free_pages` then calls `__free_pages_prepare`. Now, `__free_pages_prepare` checks with `VM_BUG_ON_PAGE(compound && compound_order(page) != order, page);`. If `CONFIG_DEBUG_VM` is enabled, this will trigger a BUG. But if `CONFIG_DEBUG_VM` is NOT enabled, `VM_BUG_ON_PAGE` is effectively a no-op! So, if the configuration is set in that way, it may not surface the problem, unless we explicitly enable that debug configuration. I need to verify that `VM_BUG_ON_PAGE` is indeed a no-op when `CONFIG_DEBUG_VM` is not set. This could be a very serious problem that is hidden unless the right debugging flags are set.
|
| 51/2 |
2026/08/12 14:24 |
tool |
grepper |
0m
Args: map[Expression:define VM_BUG_ON_PAGE]
Results: map[Output:include/linux/mmdebug.h=18=void vma_iter_dump_tree(const struct vma_iterator *vmi);
--
include/linux/mmdebug.h-21-#define VM_BUG_ON(cond) BUG_ON(cond)
include/linux/mmdebug.h:22:#define VM_BUG_ON_PAGE(cond, page) \
include/linux/mmdebug.h-23- do { \
--
include/linux/mmdebug.h-127-#define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond)
include/linux/mmdebug.h:128:#define VM_BUG_ON_PAGE(cond, page) VM_BUG_ON(cond)
include/linux/mmdebug.h-129-#define VM_BUG_ON_FOLIO(cond, folio) VM_BUG_ON(cond)
]
|
| 52/2 |
2026/08/12 14:24 |
llm |
expert |
0m
Model:
Tokens:
input: 48216
output: 25
thoughts: 2295
Thoughts: **Analysis of Potential Out-of-Bounds Memory Corruption in Page Freeing**
Okay, so `VM_BUG_ON_PAGE` is a no-op when `CONFIG_DEBUG_VM` is not set; no immediate concern there, I can move on. Let's dig into this `order` discrepancy.
Right, so in this code snippet, there's a problem when the original `OLD_ORDER` of a page doesn't match the `NEW_ORDER` it was allocated with. When that's the case, the loop `for` is going to iterate using the original `OLD_ORDER`. Let's assume `OLD_ORDER` is the smaller value. The page is allocated with the *larger* `NEW_ORDER`. If `OLD_ORDER < NEW_ORDER`, the loop will process fewer pages than actually belong to the compound page. The rest of those pages that fall outside that `1 << OLD_ORDER` range are going to be leaked. But the more pressing issue here is what happens when `OLD_ORDER > NEW_ORDER`. Then, the loop will access memory OUTSIDE the original allocation! It is going to call `free_tail_page_prepare` on what is NOT part of the page allocated. This means that we're corrupting the flags on unrelated pages. We have a potentially serious bug that will likely be triggered with any mismatch in `OLD_ORDER` and `NEW_ORDER`.
Let's break down that `free_tail_page_prepare` function. Right, the function's checks: `PageTail`, `compound_head`, and `page_mapcount`. In the case where `OLD_ORDER > NEW_ORDER`, these checks will likely FAIL for the out-of-bounds pages. The call to `bad_page` won't panic the system immediately, but this taints the kernel and increments the `bad` counter. The next condition, that checks `if (unlikely(bad))`, may prevent the page from being freed, causing a memory leak. Now, even if the page isn't freed at this stage, it will corrupt the flags on those "extra" pages with that `(page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;` call, and the initial `page[1].flags.f &= ~PAGE_FLAGS_SECOND;`. If `NEW_ORDER` is 0 and `OLD_ORDER` is greater than 0, then the code corrupts an out-of-bounds page!
If `OLD_ORDER < NEW_ORDER`, the opposite happens. The pages within the `OLD_ORDER` allocation *will* be freed, but the larger compound page persists. It's a leak, yes, but also the freed pages are allocated with `OLD_ORDER` within the buddy system. This, eventually, leads to a discrepancy with `data_page` and its swap buffers. The crucial part here is where this `data_page` is eventually freed, because it has to be `free`d with its original allocated size, `NEW_ORDER`, but now the `data` pointer actually points to a page of size `OLD_ORDER`. `ring_buffer_free_read_page` is called, and `free_pages` gets called with the wrong order! So that's out-of-bounds corruption! The system then gets back to the situation that we started with, where `order` used in `free_pages` is larger than the actual page.
So, regardless of whether `OLD_ORDER` is less or greater than `NEW_ORDER`, we're going to get this out-of-bounds corruption during a call to `free_pages` because the order is WRONG, and we're writing flags outside of the valid memory allocation.
And just look at those flag bits, we're clearing flag bits indiscriminately on other pages, things like `PG_locked`, `PG_lru`, `PG_slab`. We're going to wreak havoc on the SLUB allocator. This is a potential vulnerability right here, very exploitable, and a clear and dangerous out-of-bounds memory corruption. Time to dig deeper into the page flags.
|
| 53/2 |
2026/08/12 14:24 |
tool |
grepper |
0m
Args: map[Expression:define PAGE_FLAGS_CHECK_AT_PREP]
Results: map[Output:include/linux/page-flags.h=1168=static __always_inline void __ClearPageAnonExclusive(struct page *page)
--
include/linux/page-flags.h-1199- */
include/linux/page-flags.h:1200:#define PAGE_FLAGS_CHECK_AT_PREP \
include/linux/page-flags.h-1201- ((PAGEFLAGS_MASK & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK)
]
|
| 54/2 |
2026/08/12 14:24 |
llm |
expert |
0m
Model:
Tokens:
input: 51568
output: 28
thoughts: 455
Thoughts: **Exploiting a Kernel Page Flag Vulnerability**
Okay, so looking at this code, `PAGE_FLAGS_CHECK_AT_PREP` seems to encompass practically *all* page flags! I see that we're clearing flags with `(page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;`. That's bad. Really bad. We're talking about wiping out a whole raft of crucial flags here: `PG_locked`, `PG_error`, `PG_referenced`, `PG_uptodate`, `PG_dirty`, `PG_lru`, `PG_active`, the `PG_slab` flag, `PG_head`, and so on, down the line. I mean, clearing `PG_slab` on a page managed by the slab allocator? That's just asking for a memory corruption disaster when the allocator tries to touch or release it. Likewise, clearing `PG_head` on a compound page is a guaranteed breakage of compound page handling. And of course, zapping `PG_locked` on a locked page? Hello, race conditions and use-after-free! This is a textbook example of a privilege escalation vulnerability. It's a gold mine, really!
Now, the question is, how accessible is this particular bug? The reproducer code has these file operations:
```c
fd_size = open("/sys/kernel/tracing/buffer_subbuf_size_kb", O_WRONLY);
fd_raw = open("/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw", O_RDONLY | O_NONBLOCK);
fd_marker = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
```
The next step is to check if these tracefs files are accessible to unprivileged users. I need to dive in and check the permissions. I suspect they might be, and if so, this vulnerability is wide open.
|
| 55/2 |
2026/08/12 14:24 |
tool |
grepper |
0m
Args: map[Expression:trace_create_file.*buffer_subbuf_size_kb]
Results: map[Output:kernel/trace/trace.c=8876=init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
--
kernel/trace/trace.c-8916-
kernel/trace/trace.c:8917: trace_create_file("buffer_subbuf_size_kb", writable_mode, d_tracer,
kernel/trace/trace.c-8918- tr, &buffer_subbuf_size_fops);
]
|
| 56/2 |
2026/08/12 14:24 |
llm |
expert |
0m
Model:
Tokens:
input: 52606
output: 18
thoughts: 8
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Unpacking `writable_mode`**
Okay, so I've encountered `writable_mode` again. My immediate thought is, what's the specific context here? Is this in a filesystem API, a database connection setting, a serialization library, or something else entirely? The name itself is fairly generic, which makes a precise understanding of its implications critical.
I'm starting to think about the different meanings it *could* have. Is it a simple boolean flag, indicating read-write access versus read-only? Or is it a more complex setting, perhaps controlling the level of atomicity or the buffering behavior when writing? Does it affect the underlying data structures or locking mechanisms in any way? I need to delve into the documentation, or better yet, trace the code path where this `writable_mode` variable is used.
I also wonder about the potential for concurrency issues. If multiple processes or threads are involved, what guarantees does `writable_mode` provide? Does it offer any inherent protection against data corruption or race conditions? Thinking ahead, what happens if `writable_mode` is set incorrectly? Are there any safeguards, or would I potentially introduce subtle, hard-to-debug problems downstream?
The specific implications are, as always, context-dependent. Time to dig deeper.
|
| 57/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Args: map[Expression:writable_mode =]
Results: map[Output:kernel/trace/trace.c=8876=init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
kernel/trace/trace.c-8877-{
kernel/trace/trace.c:8878: umode_t writable_mode = TRACE_MODE_WRITE;
kernel/trace/trace.c-8879- int cpu;
--
kernel/trace/trace.c-8881- if (trace_array_is_readonly(tr))
kernel/trace/trace.c:8882: writable_mode = TRACE_MODE_READ;
kernel/trace/trace.c-8883-
]
|
| 58/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 53097
output: 21
thoughts: 10
Thoughts: Okay, here's my interpretation of what's going through my mind, as if I'm pondering this question:
**Unpacking `TRACE_MODE_WRITE`**
Alright, let's break this down. `TRACE_MODE_WRITE`... my initial thought is, what's the context? Is this related to debugging, logging, profiling, or something else entirely? The "TRACE" part suggests some form of tracing or observation, but "WRITE" hints at more than just passive monitoring. It seems like we're talking about actively *writing* some sort of trace information, rather than just passively observing existing data.
Is this for instrumentation? Are we talking about a logging level? Is it writing to standard output, a file, a database, or even a hardware tracing buffer? The specific destination and format of this "WRITE" action is critical to understanding its purpose. I'd need to consider the system architecture and determine where the trace information is written to. I should also think about what data is being traced and how it's being formatted. Are we capturing function calls, variable values, timestamps, or something else? Understanding the scope and granularity of the trace is key.
Essentially, this likely determines a specific mode for a tracing or logging mechanism, setting up how information will be written and where it will be written to. I would want to explore the possible implications of this setting.
|
| 59/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Args: map[Expression:define TRACE_MODE_WRITE]
Results: map[Output:kernel/trace/trace.h-33-
kernel/trace/trace.h:34:#define TRACE_MODE_WRITE 0640
kernel/trace/trace.h-35-#define TRACE_MODE_READ 0440
]
|
| 60/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 53491
output: 25
thoughts: 49
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience:
**Analyzing Tracefs Permissions and Namespace Compatibility**
Right, so `0640`... that's standard Unix permissions: read and write for the owner, read-only for the group, and no access for others. Pretty typical. In the context of the `tracefs` file system, the owner is almost always going to be `root`. Makes sense, considering the sensitive nature of the data it exposes.
Now, my real interest lies in how `tracefs` behaves in user namespaces. Can we mount it within a user namespace? That's the key question. If so, what are the implications for trace data isolation and security within these confined environments? This is where the fun begins. Let's dig in and figure out how to configure it.
|
| 61/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Args: map[Expression:fs_flags.*FS_USERNS_MOUNT]
Results: map[Output:drivers/android/binder/rust_binderfs.c=753=static struct file_system_type binder_fs_type = {
--
drivers/android/binder/rust_binderfs.c-757- .kill_sb = binderfs_kill_super,
drivers/android/binder/rust_binderfs.c:758: .fs_flags = FS_USERNS_MOUNT,
drivers/android/binder/rust_binderfs.c-759-};
--
drivers/android/binderfs.c=749=static struct file_system_type binder_fs_type = {
--
drivers/android/binderfs.c-753- .kill_sb = binderfs_kill_super,
drivers/android/binderfs.c:754: .fs_flags = FS_USERNS_MOUNT,
drivers/android/binderfs.c-755-};
--
fs/binfmt_misc.c=1034=static struct file_system_type bm_fs_type = {
--
fs/binfmt_misc.c-1037- .init_fs_context = bm_init_fs_context,
fs/binfmt_misc.c:1038: .fs_flags = FS_USERNS_MOUNT,
fs/binfmt_misc.c-1039- .kill_sb = bm_kill_sb,
--
fs/devpts/inode.c=453=static struct file_system_type devpts_fs_type = {
--
fs/devpts/inode.c-457- .kill_sb = devpts_kill_sb,
fs/devpts/inode.c:458: .fs_flags = FS_USERNS_MOUNT,
fs/devpts/inode.c-459-};
--
fs/fuse/inode.c=2001=static struct file_system_type fuse_fs_type = {
--
fs/fuse/inode.c-2003- .name = "fuse",
fs/fuse/inode.c:2004: .fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
fs/fuse/inode.c-2005- .init_fs_context = fuse_init_fs_context,
--
fs/namespace.c=1076=static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
--
fs/namespace.c-1101-
fs/namespace.c:1102: if ((mnt->mnt.mnt_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED) &&
fs/namespace.c-1103- mnt->mnt.mnt_root == mnt->mnt.mnt_sb->s_root)
--
fs/namespace.c=6415=static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags)
--
fs/namespace.c-6424- /* Can this filesystem be too revealing? */
fs/namespace.c:6425: if (!(sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED))
fs/namespace.c-6426- return false;
--
fs/overlayfs/super.c=1571=struct file_system_type ovl_fs_type = {
--
fs/overlayfs/super.c-1575- .parameters = ovl_parameter_spec,
fs/overlayfs/super.c:1576: .fs_flags = FS_USERNS_MOUNT,
fs/overlayfs/super.c-1577- .kill_sb = kill_anon_super,
--
fs/proc/root.c=369=static struct file_system_type proc_fs_type = {
--
fs/proc/root.c-373- .kill_sb = proc_kill_sb,
fs/proc/root.c:374: .fs_flags = FS_USERNS_MOUNT | FS_USERNS_MOUNT_RESTRICTED | FS_DISALLOW_NOTIFY_PERM,
fs/proc/root.c-375-};
--
fs/ramfs/inode.c=317=static struct file_system_type ramfs_fs_type = {
--
fs/ramfs/inode.c-321- .kill_sb = ramfs_kill_sb,
fs/ramfs/inode.c:322: .fs_flags = FS_USERNS_MOUNT,
fs/ramfs/inode.c-323-};
--
fs/super.c=695=bool mount_capable(struct fs_context *fc)
fs/super.c-696-{
fs/super.c:697: if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT))
fs/super.c-698- return capable(CAP_SYS_ADMIN);
--
fs/super.c=735=struct super_block *sget_fc(struct fs_context *fc,
--
fs/super.c-750- if (user_ns != &init_user_ns &&
fs/super.c:751: !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) {
fs/super.c-752- errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed");
--
fs/sysfs/mount.c=78=static struct file_system_type sysfs_fs_type = {
--
fs/sysfs/mount.c-81- .kill_sb = sysfs_kill_sb,
fs/sysfs/mount.c:82: .fs_flags = FS_USERNS_MOUNT | FS_USERNS_MOUNT_RESTRICTED,
fs/sysfs/mount.c-83-};
--
ipc/mqueue.c=1613=static struct file_system_type mqueue_fs_type = {
--
ipc/mqueue.c-1616- .kill_sb = kill_anon_super,
ipc/mqueue.c:1617: .fs_flags = FS_USERNS_MOUNT,
ipc/mqueue.c-1618-};
--
kernel/acct.c=216=static int acct_on(const char __user *name)
--
kernel/acct.c-251- /* Exclude procfs and sysfs. */
kernel/acct.c:252: if (file_inode(file)->i_sb->s_type->fs_flags & FS_USERNS_MOUNT_RESTRICTED)
kernel/acct.c-253- return -EINVAL;
--
kernel/bpf/inode.c=1277=static struct file_system_type bpf_fs_type = {
--
kernel/bpf/inode.c-1282- .kill_sb = bpf_kill_super,
kernel/bpf/inode.c:1283: .fs_flags = FS_USERNS_MOUNT,
kernel/bpf/inode.c-1284-};
--
kernel/cgroup/cgroup.c=2388=struct file_system_type cgroup_fs_type = {
--
kernel/cgroup/cgroup.c-2392- .kill_sb = cgroup_kill_sb,
kernel/cgroup/cgroup.c:2393: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2394-};
--
kernel/cgroup/cgroup.c=2396=static struct file_system_type cgroup2_fs_type = {
--
kernel/cgroup/cgroup.c-2400- .kill_sb = cgroup_kill_sb,
kernel/cgroup/cgroup.c:2401: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2402-};
--
kernel/cgroup/cgroup.c=2469=static struct file_system_type cpuset_fs_type = {
--
kernel/cgroup/cgroup.c-2472- .parameters = cpuset_fs_parameters,
kernel/cgroup/cgroup.c:2473: .fs_flags = FS_USERNS_MOUNT,
kernel/cgroup/cgroup.c-2474-};
--
mm/shmem.c=5280=static struct file_system_type shmem_fs_type = {
--
mm/shmem.c-5287- .kill_sb = kill_anon_super,
mm/shmem.c:5288: .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME,
mm/shmem.c-5289-};
--
mm/shmem.c=5712=static struct file_system_type shmem_fs_type = {
--
mm/shmem.c-5716- .kill_sb = ramfs_kill_sb,
mm/shmem.c:5717: .fs_flags = FS_USERNS_MOUNT,
mm/shmem.c-5718-};
]
|
| 62/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 56132
output: 25
thoughts: 125
Thoughts: **Analyzing Tracefs and User Namespaces**
Okay, so I'm looking into this `tracefs` situation and it seems pretty straightforward at first glance. It's *not* listed among the filesystems that a user namespace can directly mount via `FS_USERNS_MOUNT`. Therefore, logically, an unprivileged user can't simply mount `tracefs` themselves.
But then my brain kicks in, asking "What if it's *already* mounted, say, at `/sys/kernel/tracing`? That's a different scenario. We need to look deeper."
The next logical step, then, is to examine some files within that potential mount point. Okay, I check the permissions on `/sys/kernel/tracing/buffer_subbuf_size_kb`. Ah, it's `0640`, meaning root owns it, and the group is also root. Clearly, an unprivileged user is out of luck reading or writing to that particular file.
Now, a new thought occurs, I need to check something else. Is `trace_pipe_raw` accessible? This warrants further investigation.
|
| 63/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Error:
Args: map[Expression:trace_create_file.*trace_pipe_raw]
Results:
|
| 64/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 56534
output: 21
thoughts: 12
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `trace_pipe_raw`**
Alright, so the initial thought is clear: I need to locate where `trace_pipe_raw` is defined and used. No need for fluff here. It's a specific function, and I need to dive into the codebase and get to the bottom of it. I'm going to start with a targeted search, likely using my preferred tools (grep, ctags, or whatever's fastest for the project). The goal is rapid identification β I want to see the contexts immediately. I'm thinking about the likely candidates for the file locations based on the project's structure... Let's see what the search reveals.
|
| 65/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Args: map[Expression:"trace_pipe_raw"]
Results: map[Output:Documentation/trace/debugging.rst=179=if you clear the trace data or read out all trace data from the "trace_pipe"
Documentation/trace/debugging.rst:180:or the "trace_pipe_raw" files.
--
kernel/trace/trace.c=7634=tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
--
kernel/trace/trace.c-7657-
kernel/trace/trace.c:7658: trace_create_cpu_file("trace_pipe_raw", TRACE_MODE_READ, d_cpu,
kernel/trace/trace.c-7659- tr, cpu, &tracing_buffers_fops);
]
|
| 66/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 56922
output: 24
thoughts: 384
Thoughts: **Analyzing the `trace_pipe_raw` Vulnerability**
Okay, let's break this down. `TRACE_MODE_READ` is defined as `0440`, which means that `trace_pipe_raw` is only readable by root or the root group. That initially seems to protect against unprivileged users triggering this. But hold on, what about users within a user namespace who *appear* to be root? They can't just mount `tracefs` anyway, because it's not mountable within a user namespace. But, if `tracefs` is *already* mounted on the host, can a user namespace access the files? It looks like `tracefs` isn't mounted with the `SB_I_USERNS_VISIBLE` flag, so that's not the issue. Even if it was visible, the files would be owned by the host's root (UID 0 in `init_user_ns`). A process in a user namespace has a different UID, likely mapped to something like "nobody" on the host, and therefore won't be able to access those host-owned files given the permissions of `0640` or `0440`. So, no, it's not accessible from unprivileged processes or user namespaces.
Now, is this actually exploitable? Yes, this is where it gets interesting. It causes out-of-bounds page flag corruption, which is practically screaming "potential memory corruption and privilege escalation!" Okay, that makes it serious.
And is it a denial of service (DoS) vulnerability? Yes, it *can* cause a kernel panic or memory corruption, which could lead to a crash. Definitely a risk.
So, just to recap the access paths: unprivileged processes: no. User namespaces: no. VM guests: no. VM host: no. Network: no. Remote: no. Peripheral: no. Malicious filesystem: no.
Let's do a quick sweep. Is there any other way to trigger `ring_buffer_subbuf_order_set`? Gotta consider all the angles.
|
| 67/2 |
2026/08/12 14:25 |
tool |
grepper |
0m
Args: map[Expression:ring_buffer_subbuf_order_set]
Results: map[Output:include/linux/ring_buffer.h=235=int ring_buffer_subbuf_order_get(struct trace_buffer *buffer);
include/linux/ring_buffer.h:236:int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order);
include/linux/ring_buffer.h-237-int ring_buffer_subbuf_size_get(struct trace_buffer *buffer);
--
kernel/trace/ring_buffer.c=7301=EXPORT_SYMBOL_GPL(ring_buffer_subbuf_order_get);
--
kernel/trace/ring_buffer.c-7303-/**
kernel/trace/ring_buffer.c:7304: * ring_buffer_subbuf_order_set - set the size of ring buffer sub page.
kernel/trace/ring_buffer.c-7305- * @buffer: The ring_buffer to set the new page size.
--
kernel/trace/ring_buffer.c-7318- */
kernel/trace/ring_buffer.c:7319:int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
kernel/trace/ring_buffer.c-7320-{
--
kernel/trace/ring_buffer.c-7474-}
kernel/trace/ring_buffer.c:7475:EXPORT_SYMBOL_GPL(ring_buffer_subbuf_order_set);
kernel/trace/ring_buffer.c-7476-
--
kernel/trace/trace.c=8191=buffer_subbuf_size_write(struct file *filp, const char __user *ubuf,
--
kernel/trace/trace.c-8220-
kernel/trace/trace.c:8221: ret = ring_buffer_subbuf_order_set(tr->array_buffer.buffer, order);
kernel/trace/trace.c-8222- if (ret)
--
kernel/trace/trace.c-8229-
kernel/trace/trace.c:8230: ret = ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, order);
kernel/trace/trace.c-8231- if (ret) {
kernel/trace/trace.c-8232- /* Put back the old order */
kernel/trace/trace.c:8233: cnt = ring_buffer_subbuf_order_set(tr->array_buffer.buffer, old_order);
kernel/trace/trace.c-8234- if (WARN_ON_ONCE(cnt)) {
--
kernel/trace/trace_snapshot.c=162=int tracing_alloc_snapshot_instance(struct trace_array *tr)
--
kernel/trace/trace_snapshot.c-170- order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer);
kernel/trace/trace_snapshot.c:171: ret = ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, order);
kernel/trace/trace_snapshot.c-172- if (ret < 0)
--
kernel/trace/trace_snapshot.c=187=void free_snapshot(struct trace_array *tr)
--
kernel/trace/trace_snapshot.c-193- */
kernel/trace/trace_snapshot.c:194: ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, 0);
kernel/trace/trace_snapshot.c-195- ring_buffer_resize(tr->snapshot_buffer.buffer, 1, RING_BUFFER_ALL_CPUS);
]
|
| 68/2 |
2026/08/12 14:25 |
llm |
expert |
0m
Model:
Tokens:
input: 58690
output: 21
thoughts: 694
Thoughts: **Analyzing the Kernel Race Condition Bug**
Okay, so here's what I'm thinking. The core issue lies within the interaction between `ring_buffer_subbuf_order_set` and `tracing_buffers_read`, specifically `ring_buffer_read_page`. I see that `ring_buffer_subbuf_order_set` gets called from two places: `buffer_subbuf_size_write` (through the `buffer_subbuf_size_kb` file) and during snapshot operations. Snapshots themselves are triggered by writing to the `snapshot` file. Both of these files, along with `trace_pipe_raw` and `snapshot_raw`, reside in tracefs, and that means root permissions are required to access them.
So, the race condition is this: `ring_buffer_subbuf_order_set` updates the buffer's `subbuf_order`. Meanwhile, `tracing_buffers_read` might be allocating a new "spare" page with this *new* order. Then, `ring_buffer_read_page` swaps the new `spare` page with the old `reader_page` in the ring buffer. This means the old `reader_page` has the old order but is moved to `spare`, which now has the new order. The new `spare` page has the new order but ends up as the new `reader_page` which has the old order. When `ring_buffer_subbuf_order_set` later frees the old `reader_page`, it uses the *old* order to free this *new* page, leading to a size mismatch. `tracing_buffers_release` frees the `spare` page using the *new* order, again mismatching.
The consequence? `free_pages` gets called with an incorrect order. If the order is inflated, it's going to access out-of-bounds pages, assuming they're part of a larger compound page. Then it calls `free_tail_page_prepare` on these out-of-bounds pages, which clears `PAGE_FLAGS_CHECK_AT_PREP` and effectively corrupts the page flags of adjacent memory regions. This is *gold* for an attacker, ripe for privilege escalation by manipulating flags like `PG_slab`, `PG_locked`, and the like. If the order is smaller, you leak pages, but the important issue is that both scenarios happen, and there's guaranteed page flag corruption.
The exploitability is definite. It leads to Denial of Service (kernel panic or memory corruption). However, the good news (from a security perspective) is it's not accessible from unprivileged processes or user namespaces. The tracefs files' permissions are very restrictive (0640 or 0440), and it's not mountable in user namespaces. And I'm checking that this can't be triggered remotely, through VMs, through networks, through peripherals, or through malicious filesystems. I'm taking a look at `tracing_buffers_read` to see where else it's called. It's the `.read` handler for `trace_pipe_raw` and `snapshot_raw`, and those are tracefs files, so it looks like the entry points are still limited to root. I'm checking to see if `ring_buffer_read_page` has other callers, just to be sure.
|
| 69/2 |
2026/08/12 14:26 |
tool |
grepper |
0m
Args: map[Expression:ring_buffer_read_page]
Results: map[Output:include/linux/ring_buffer.h=223=void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu,
include/linux/ring_buffer.h-224- struct buffer_data_read_page *page);
include/linux/ring_buffer.h:225:int ring_buffer_read_page(struct trace_buffer *buffer,
include/linux/ring_buffer.h-226- struct buffer_data_read_page *data_page,
include/linux/ring_buffer.h-227- size_t len, int cpu, int full);
include/linux/ring_buffer.h:228:void *ring_buffer_read_page_data(struct buffer_data_read_page *page);
include/linux/ring_buffer.h-229-
--
kernel/trace/ring_buffer.c=6927=EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu);
--
kernel/trace/ring_buffer.c-6934- *
kernel/trace/ring_buffer.c:6935: * This function is used in conjunction with ring_buffer_read_page.
kernel/trace/ring_buffer.c-6936- * When reading a full page from the ring buffer, these functions
--
kernel/trace/ring_buffer.c-6939- * needs to get pages from the ring buffer, it passes the result
kernel/trace/ring_buffer.c:6940: * of this function into ring_buffer_read_page, which will swap
kernel/trace/ring_buffer.c-6941- * the page that was allocated, with the read page of the buffer.
--
kernel/trace/ring_buffer.c=7031=EXPORT_SYMBOL_GPL(ring_buffer_free_read_page);
--
kernel/trace/ring_buffer.c-7033-/**
kernel/trace/ring_buffer.c:7034: * ring_buffer_read_page - extract a page from the ring buffer
kernel/trace/ring_buffer.c-7035- * @buffer: buffer to extract from
--
kernel/trace/ring_buffer.c-7049- * return PTR_ERR(rpage);
kernel/trace/ring_buffer.c:7050: * ret = ring_buffer_read_page(buffer, rpage, len, cpu, 0);
kernel/trace/ring_buffer.c-7051- * if (ret >= 0)
kernel/trace/ring_buffer.c:7052: * process_page(ring_buffer_read_page_data(rpage), ret);
kernel/trace/ring_buffer.c-7053- * ring_buffer_free_read_page(buffer, cpu, rpage);
--
kernel/trace/ring_buffer.c-7066- */
kernel/trace/ring_buffer.c:7067:int ring_buffer_read_page(struct trace_buffer *buffer,
kernel/trace/ring_buffer.c-7068- struct buffer_data_read_page *data_page,
--
kernel/trace/ring_buffer.c-7255-}
kernel/trace/ring_buffer.c:7256:EXPORT_SYMBOL_GPL(ring_buffer_read_page);
kernel/trace/ring_buffer.c-7257-
kernel/trace/ring_buffer.c-7258-/**
kernel/trace/ring_buffer.c:7259: * ring_buffer_read_page_data - get pointer to the data in the page.
kernel/trace/ring_buffer.c-7260- * @page: the page to get the data from
--
kernel/trace/ring_buffer.c-7263- */
kernel/trace/ring_buffer.c:7264:void *ring_buffer_read_page_data(struct buffer_data_read_page *page)
kernel/trace/ring_buffer.c-7265-{
--
kernel/trace/ring_buffer.c-7267-}
kernel/trace/ring_buffer.c:7268:EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
kernel/trace/ring_buffer.c-7269-
--
kernel/trace/ring_buffer_benchmark.c=105=static enum event_status read_page(int cpu)
--
kernel/trace/ring_buffer_benchmark.c-121- page_size = ring_buffer_subbuf_size_get(buffer);
kernel/trace/ring_buffer_benchmark.c:122: ret = ring_buffer_read_page(buffer, bpage, page_size, cpu, 1);
kernel/trace/ring_buffer_benchmark.c-123- if (ret >= 0) {
kernel/trace/ring_buffer_benchmark.c:124: rpage = ring_buffer_read_page_data(bpage);
kernel/trace/ring_buffer_benchmark.c-125- /* The commit may have missed event flags set, clear them */
--
kernel/trace/trace.c=7078=ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf,
--
kernel/trace/trace.c-7124- trace_access_lock(iter->cpu_file);
kernel/trace/trace.c:7125: ret = ring_buffer_read_page(iter->array_buffer->buffer,
kernel/trace/trace.c-7126- info->spare,
--
kernel/trace/trace.c-7152- size = count;
kernel/trace/trace.c:7153: trace_data = ring_buffer_read_page_data(info->spare);
kernel/trace/trace.c-7154- ret = copy_to_user(ubuf, trace_data + info->read, size);
--
kernel/trace/trace.c=7254=ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos,
--
kernel/trace/trace.c-7315-
kernel/trace/trace.c:7316: r = ring_buffer_read_page(ref->buffer, ref->page,
kernel/trace/trace.c-7317- len, iter->cpu_file, 1);
--
kernel/trace/trace.c-7324-
kernel/trace/trace.c:7325: page = virt_to_page(ring_buffer_read_page_data(ref->page));
kernel/trace/trace.c-7326-
]
|
| 70/2 |
2026/08/12 14:26 |
llm |
expert |
0m
Model:
Tokens:
input: 61789
output: 66
thoughts: 357
Thoughts: **My Assessment of the Tracefs Vulnerability**
Okay, so `tracing_buffers_splice_read` is the key function here. It uses `ring_buffer_read_page` internally, and it's specifically the `.splice_read` handler for both `trace_pipe_raw` and `snapshot_raw` β both tracefs files. That means this is already a root-only issue from the get-go.
Now, I need to confirm if there are other ways to call `ring_buffer_subbuf_order_set`. Let's see... We've reviewed the callers: `buffer_subbuf_size_write` (accessed via the `buffer_subbuf_size_kb` tracefs file), `tracing_alloc_snapshot_instance` (through the `snapshot` tracefs file), and `free_snapshot` (also through `snapshot`). So, *all* of the access points are through tracefs.
Given that, this vulnerability is definitively limited to root users. It *is* exploitable and will lead to a Denial of Service. However, it's *not* something unprivileged processes can trigger. It's also not accessible from user namespaces, which is good. There's no remote, network, VM-related, or peripheral attack vector here, nor can it be triggered by a malicious filesystem.
I have to double-check: can `buffer_subbuf_size_kb` or `trace_pipe_raw` be somehow accessed by unprivileged users in *any* configuration? Okay, no. Tracefs files are created with `TRACE_MODE_WRITE` (0640) or `TRACE_MODE_READ` (0440), meaning root:root ownership and standard permissions. Unless the administrator explicitly modifies the permissions or ownership, unprivileged users can't touch them. Plus, tracefs can't be mounted within a user namespace.
So, in conclusion, this is a root-only DoS, and I'll record this accordingly.
|
| 71/2 |
2026/08/12 14:26 |
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]
|
| 72/2 |
2026/08/12 14:26 |
llm |
expert |
0m
Model:
Tokens:
input: 62706
output: 773
thoughts: 631
Thoughts: **My Analysis of the Race Condition Bug**
Okay, the results are in. Time to solidify my findings in the final write-up. This is a classic race condition, and a nasty one at that, between `ring_buffer_subbuf_order_set` and `ring_buffer_read_page`, which is itself invoked through `tracing_buffers_read` or `tracing_buffers_splice_read`.
Essentially, hereβs whatβs happening: when we change the sub-buffer order using `ring_buffer_subbuf_order_set`, it updates `buffer->subbuf_order` and allocates new pages for each CPU buffer, that makes sense. The problem arises because concurrently, a reader is able to call `tracing_buffers_read`, and this allocates a new `spare` page using this *new* `buffer->subbuf_order`.
Now comes the tricky part: `ring_buffer_read_page` is called, and this swaps that fresh `spare` page (with the *new* order) with the existing, older `reader_page` in the ring buffer. The kicker? `ring_buffer_read_page` only swaps the pointers; it doesnβt update the `order` field within the `buffer_page` structure. This leaves us with a mismatch. The old `reader_page` structure now points to a page with the *new* order, while its `order` field still reflects the *old* order. The `spare` page gets the same treatment, with its `order` field now representing the new order, but actually pointing to a page with the old order.
When `ring_buffer_subbuf_order_set` eventually finishes allocating, it frees the old `reader_page` using `free_pages(bpage->page, bpage->order)`. This is where the memory corruption happens: because of the swap we performed, we end up calling `free_pages` with an `order` that *doesn't* match the actual page order.
If the old order was *larger* than the new order, `free_pages` ends up trying to free out-of-bounds pages β assuming they are tail pages of a compound page β and call `free_tail_page_prepare`. This clears `PAGE_FLAGS_CHECK_AT_PREP` from those out-of-bounds pages which effectively corrupts the page flags of *adjacent* pages. Thatβs a serious privilege escalation vulnerability right there. On the other hand, if the old order is *smaller*, it's going to cause a leak of the remaining pages of the compound page. The swapped `spare` page will eventually get freed with the new order, but this still causes the same memory corruption issue.
So, in essence, this is a race condition leading to a potential denial of service through memory corruption.
However, after looking deeper, I can tell that the exploitability is limited. This is only accessible to root since the files involved (`buffer_subbuf_size_kb`, `trace_pipe_raw`, `snapshot`) are within `tracefs`, and they have restrictive permissions of `0640` or `0440`, and are owned by root. Furthermore, `tracefs` cannot be mounted within user namespaces. This means an unprivileged process or a process running in a user namespace can't trigger this bug.
|
| 73/1 |
2026/08/12 14:26 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Bug Mechanism:**
The bug is a race condition between changing the ring buffer sub-buffer size
(`ring_buffer_subbuf_order_set`) and reading from the raw trace pipe
(`ring_buffer_read_page` via `tracing_buffers_read` or
`tracing_buffers_splice_read`).
When the sub-buffer order is changed, `ring_buffer_subbuf_order_set` updates
`buffer->subbuf_order` to the new order and then proceeds to allocate new pages
for each CPU buffer. Concurrently, a reader can call `tracing_buffers_read`,
which allocates a new `spare` page using the *new* `buffer->subbuf_order`.
Then, `ring_buffer_read_page` is called, which swaps the newly allocated `spare`
page (with the new order) with the old `reader_page` in the ring buffer (which
still has the old order). Because `ring_buffer_read_page` only swaps the page
pointers but does not update the `order` field of the `buffer_page` structure,
the old `reader_page` structure now points to a page with the new order, while
its `order` field still holds the old order. Similarly, the `spare` page
structure now points to a page with the old order, while its `order` field holds
the new order.
When `ring_buffer_subbuf_order_set` finishes allocating new pages, it frees the
old `reader_page` by calling `free_pages(bpage->page, bpage->order)`. This calls
`free_pages` with a mismatched order.
If the old order is larger than the new order, `free_pages` will access
out-of-bounds pages (assuming they are tail pages of a compound page) and call
`free_tail_page_prepare` on them. This function clears
`PAGE_FLAGS_CHECK_AT_PREP` (which includes almost all page flags, such as
`PG_slab`, `PG_locked`, `PG_lru`, etc.) from these out-of-bounds pages.
If the old order is smaller than the new order, the swapped `spare` page will
eventually be freed with the new order, which again causes `free_pages` to be
called with a larger order than the actual page order, leading to the exact same
out-of-bounds page flag corruption.
**Exploitability:**
The bug is **exploitable**. Corrupting the page flags of adjacent pages in
memory is a powerful primitive. For example, clearing `PG_slab` on a slab page
will cause massive memory corruption when the SLUB allocator tries to use or
free it. Clearing `PG_locked` on a locked page will cause race conditions and
use-after-free. This can be reliably exploited for privilege escalation or to
cause a Denial of Service (kernel panic).
**Accessibility:**
The bug is **NOT accessible from unprivileged processes** or **user
namespaces**.
Triggering the bug requires interacting with files in `tracefs` (specifically
`buffer_subbuf_size_kb` and `trace_pipe_raw` or `snapshot`). These files are
created with `0640` or `0440` permissions and are owned by root. Furthermore,
`tracefs` is not marked with `FS_USERNS_MOUNT`, meaning it cannot be mounted
inside an unprivileged user namespace. Therefore, only a privileged user (root
in the initial namespace) can trigger this vulnerability.]
|