| 0/0 |
2026/08/31 00:36 |
flow |
assessment-security |
24m
Results: map[DenialOfService:false Explanation:**Analysis of the Bug**
The bug is a false positive `WARN_ON_ONCE` in the arm64 implementation of
`ioremap_prot`.
When a user maps a PCI resource (or other specific device memory) with
`PROT_NONE`, the resulting Virtual Memory Area (VMA) has its `vm_page_prot` set
to `PAGE_NONE`. On the arm64 architecture, `PAGE_NONE` does not have the
`PTE_USER` bit set, as it grants no access permissions to user-space.
If a process then attempts to read from this `PROT_NONE` VMA via
`/proc/self/mem` (which is allowed and bypasses VMA access permissions), the
kernel uses `generic_access_phys()` to perform the read. `generic_access_phys()`
extracts the `pgprot_t` from the VMA (which is `PAGE_NONE`) and passes it to
`ioremap_prot()` to temporarily map the physical memory into the kernel address
space to perform the read.
`ioremap_prot()` contains a sanity check: `WARN_ON_ONCE(!(user_prot_val &
PTE_USER))`. This was introduced to ensure that `ioremap_prot()` is only called
with user-space protections and not accidentally with kernel protections.
However, this check fails to account for `PAGE_NONE`, which is a valid
user-space protection but intentionally lacks the `PTE_USER` bit. This triggers
the warning and causes `ioremap_prot()` to return `NULL`, resulting in an
`-ENOMEM` error for the read operation.
**Security Impact Assessment**
This bug has no security impact for the following reasons:
1. **Not Exploitable:** The bug is merely a false positive warning that results
in a failed read operation (`-ENOMEM`). It does not cause memory corruption,
use-after-free, out-of-bounds access, or information leaks.
2. **No Denial of Service:** The `WARN_ON_ONCE` macro does not panic or halt the
kernel in standard production builds. It simply logs a stack trace and allows
the kernel to continue executing safely.
3. **Requires Global Root Privileges:** The `generic_access_phys()` function is
only utilized by a handful of specific drivers (e.g., PCI sysfs/procfs
resources, `/dev/mem`, UIO devices). All of these device nodes and sysfs files
are strictly restricted to root (e.g., `0600` root:root). An unprivileged user
cannot open these files to create the necessary VMA in the first place.
4. **Not Accessible from User Namespaces:** Even if an unprivileged user creates
a new user namespace and mounts `sysfs`, the PCI resource files remain owned by
the global root (`GLOBAL_ROOT_UID`). Because the global root is not mapped
within an unprivileged user namespace, the kernel correctly denies
`CAP_DAC_OVERRIDE`. Thus, the user namespace root cannot bypass the file
permissions to open the PCI resources.
In conclusion, this is a functional edge-case bug affecting the ability to read
`PROT_NONE` device mappings via `/proc/pid/mem` on arm64, but it poses no
security risk and is completely unreachable by unprivileged attackers. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/31 00:36 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_mkdir
#define __NR_mkdir 39
#endif
#ifndef __NR_mmap
#define __NR_mmap 192
#endif
#ifndef __NR_mount
#define __NR_mount 21
#endif
#ifndef __NR_openat
#define __NR_openat 322
#endif
#ifndef __NR_read
#define __NR_read 3
#endif
#undef __NR_mmap
#define __NR_mmap __NR_mmap2
static long syz_open_procfs(volatile long a0, volatile long a1)
{
char buf[128];
memset(buf, 0, sizeof(buf));
if (a0 == 0) {
snprintf(buf, sizeof(buf), "/proc/self/%s", (char*)a1);
} else if (a0 == -1) {
snprintf(buf, sizeof(buf), "/proc/thread-self/%s", (char*)a1);
} else {
snprintf(buf, sizeof(buf), "/proc/self/task/%d/%s", (int)a0, (char*)a1);
}
int fd = open(buf, O_RDWR);
if (fd == -1)
fd = open(buf, O_RDONLY);
return fd;
}
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffff000, /*len=*/0x1000, /*prot=*/0, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
syscall(__NR_mmap, /*addr=*/0x20000000, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
syscall(__NR_mmap, /*addr=*/0x21000000, /*len=*/0x1000, /*prot=*/0, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// mkdir arguments: [
// path: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 00} (length 0x7)
// }
// mode: open_mode = 0x1ff (4 bytes)
// ]
memcpy((void*)0x20000000, "my_sys\000", 7);
syscall(__NR_mkdir, /*path=*/0x20000000, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1ff);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 00} (length 0x7)
// }
// type: ptr[in, buffer] {
// buffer: {73 79 73 66 73 00} (length 0x6)
// }
// flags: mount_flags = 0x0 (4 bytes)
// data: nil
// ]
memcpy((void*)0x20000200, "my_sys\000", 7);
memcpy((void*)0x20000300, "sysfs\000", 6);
syscall(__NR_mount, /*src=*/0, /*dst=*/0x20000200, /*type=*/0x20000300, /*flags=*/0, /*data=*/0);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 2f 62 75 73 2f 70 63 69 2f 64 65 76 69 63 65 73 2f 30 30 30 30 3a 30 30 3a 30 33 2e 30 2f 72 65 73 6f 75 72 63 65 31 00} (length 0x2e)
// }
// flags: open_flags = 0x2 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x20000500, "my_sys/bus/pci/devices/0000:00:03.0/resource1\000", 46);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x20000500, /*flags=O_RDWR*/2, /*mode=*/0);
if (res != -1)
r[0] = res;
// mmap$IORING_OFF_SQ_RING arguments: [
// addr: const = 0x0 (4 bytes)
// len: int32 = 0xff (4 bytes)
// prot: mmap_prot = 0x0 (4 bytes)
// flags: mmap_flags = 0x12 (4 bytes)
// fd: fd_io_uring (resource)
// offset: const = 0x0 (4 bytes)
// ]
// returns ring_ptr
syscall(__NR_mmap, /*addr=*/0, /*len=*/0xff, /*prot=*/0, /*flags=MAP_FIXED|MAP_PRIVATE*/0x12, /*fd=*/(intptr_t)r[0], /*offset=*/0);
// fsconfig$FSCONFIG_SET_BINARY arguments: [
// fd: fd_fscontext (resource)
// cmd: const = 0x2 (4 bytes)
// key: ptr[in, buffer] {
// buffer: {6d 65 6d 00 ff 45 65 76 f2 72 f1 18 1d b9 00 79 5f 68 75 67 24 08 6f 2d 03 51 60 5d 6f 8c 72 9d 5f 65 74 6c f9 dd 07 ad 5c 48 e9 34 08 2c 48 bf} (length 0x30)
// }
// value: nil
// aux: bytesize = 0x0 (4 bytes)
// ]
memcpy((void*)0x20000040, "mem\000\377Eev\362r\361\030\035\271\000y_hug$\bo-\003Q`]o\214r\235_etl\371\335\a\255\\H\3514\b,H\277", 48);
syscall(__NR_fsconfig, /*fd=*/(intptr_t)-1, /*cmd=*/2, /*key=*/0x20000040, /*value=*/0, /*aux=*/0);
// syz_open_procfs arguments: [
// pid: pid (resource)
// file: ptr[in, buffer] {
// buffer: {} (length 0x0)
// }
// ]
// returns fd
res = -1;
res = syz_open_procfs(/*pid=*/0, /*file=*/0x20000040);
if (res != -1)
r[1] = res;
// read$FUSE arguments: [
// fd: fd_fuse (resource)
// buf: ptr[out, fuse_in[read_buffer]] {
// fuse_in[read_buffer] {
// len: len = 0x2020 (4 bytes)
// opcode: int32 = 0x0 (4 bytes)
// unique: fuse_unique (resource)
// uid: uid (resource)
// gid: gid (resource)
// pid: pid (resource)
// padding: int32 = 0x0 (4 bytes)
// payload: buffer: (DirOut)
// }
// }
// len: bytesize = 0x2020 (4 bytes)
// ]
syscall(__NR_read, /*fd=*/(intptr_t)r[1], /*buf=*/0x200034c0, /*len=*/0x2020);
return 0;
}
]
|
| 2/1 |
2026/08/31 00:36 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/eea6fbc12bfc67a92c389af17a81758283de8801]
|
| 3/1 |
2026/08/31 00:36 |
action |
kernel-builder |
7m
Results: map[KernelObj:/app/workdir/cache/build/d39def7cad084eeffff0001b57d646eee9236196]
|
| 4/1 |
2026/08/31 00:44 |
action |
codesearch-prepare |
10m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/31 00:55 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
WARNING: ./arch/arm64/include/asm/io.h:283 at rcu_read_unlock include/linux/rcupdate.h:878 [inline], CPU#0: syz.2.18/3677
WARNING: ./arch/arm64/include/asm/io.h:283 at pte_unmap include/linux/pgtable.h:117 [inline], CPU#0: syz.2.18/3677
WARNING: ./arch/arm64/include/asm/io.h:283 at follow_pfnmap_end mm/memory.c:7077 [inline], CPU#0: syz.2.18/3677
WARNING: ./arch/arm64/include/asm/io.h:283 at generic_access_phys+0x26c/0x344 mm/memory.c:7126, CPU#0: syz.2.18/3677
Modules linked in:
CPU: 0 UID: 0 PID: 3677 Comm: syz.2.18 Tainted: G W syzkaller #0 PREEMPT
Tainted: [W]=WARN
Hardware name: linux,dummy-virt (DT)
pstate: 20000005 (nzCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : ioremap_prot arch/arm64/include/asm/io.h:283 [inline]
pc : generic_access_phys+0x26c/0x344 mm/memory.c:7116
lr : rcu_read_unlock include/linux/rcupdate.h:882 [inline]
lr : pte_unmap include/linux/pgtable.h:117 [inline]
lr : follow_pfnmap_end mm/memory.c:7077 [inline]
lr : generic_access_phys+0xcc/0x344 mm/memory.c:7111
sp : ffff8000a2197830
x29: ffff8000a21978f0 x28: ffff8000a2197870 x27: 0000000000000000
x26: 0000000000000001 x25: ffff700010ef8fd0 x24: 0000000000001000
x23: ffff8000877c7e80 x22: 0000000000000000 x21: 0160000000000f92
x20: ffff0000127a3c80 x19: 0000000010042000 x18: 0000000000000000
x17: ffff800080c267fc x16: ffff800080e49be0 x15: ffff800080e496d8
x14: ffff800080a8ead0 x13: ffff800089e66530 x12: ffff60000325ffd5
x11: 1fffe0000325ffd4 x10: ffff60000325ffd4 x9 : dfff800000000000
x8 : ffff0000192ffea0 x7 : 0000000000000001 x6 : ffff60000325ffd4
x5 : ffff0000192ffea0 x4 : 0000000000000000 x3 : 1fffe00002a63090
x2 : 0000000000000000 x1 : 0000000000001000 x0 : 000000003fffffff
Call trace:
rcu_read_unlock include/linux/rcupdate.h:878 [inline] (P)
pte_unmap include/linux/pgtable.h:117 [inline] (P)
follow_pfnmap_end mm/memory.c:7077 [inline] (P)
generic_access_phys+0x26c/0x344 mm/memory.c:7126 (P)
kernfs_vma_access+0x14c/0x1f0 fs/kernfs/file.c:437
__access_remote_vm+0x3dc/0x600 mm/memory.c:7194
access_remote_vm+0x10/0x20 mm/memory.c:7241
mem_rw+0x16c/0x580 fs/proc/base.c:912
mem_read+0x14/0x20 fs/proc/base.c:940
vfs_read+0x190/0xa18 fs/read_write.c:572
ksys_read+0xf0/0x1e0 fs/read_write.c:716
__do_sys_read fs/read_write.c:725 [inline]
__se_sys_read fs/read_write.c:723 [inline]
__arm64_sys_read+0x70/0xa0 fs/read_write.c:723
__invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
invoke_syscall+0x74/0x240 arch/arm64/kernel/syscall.c:49
el0_svc_common.constprop.0+0xac/0x230 arch/arm64/kernel/syscall.c:121
do_el0_svc_compat+0x40/0x58 arch/arm64/kernel/syscall.c:146
el0_svc_compat+0x54/0x264 arch/arm64/kernel/entry-common.c:909
el0t_32_sync_handler+0x88/0xac arch/arm64/kernel/entry-common.c:927
el0t_32_sync+0x19c/0x1a0 arch/arm64/kernel/entry.S:595
irq event stamp: 2632
hardirqs last enabled at (2631): [<ffff8000859d52ec>] __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:210 [inline]
hardirqs last enabled at (2631): [<ffff8000859d52ec>] _raw_spin_unlock_irqrestore+0x80/0xb4 kernel/locking/spinlock.c:221
hardirqs last disabled at (2632): [<ffff8000859a81d0>] el1_brk64+0x20/0x54 arch/arm64/kernel/entry-common.c:445
softirqs last enabled at (2592): [<ffff80008007ad70>] put_cpu_fpsimd_context arch/arm64/kernel/fpsimd.c:250 [inline]
softirqs last enabled at (2592): [<ffff80008007ad70>] fpsimd_restore_current_state+0x44/0x854 arch/arm64/kernel/fpsimd.c:1797
softirqs last disabled at (2590): [<ffff80008007b050>] fpsimd_restore_current_state+0x324/0x854 arch/arm64/kernel/fpsimd.c:1794
---[ end trace 0000000000000000 ]---
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.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_mkdir
#define __NR_mkdir 39
#endif
#ifndef __NR_mmap
#define __NR_mmap 192
#endif
#ifndef __NR_mount
#define __NR_mount 21
#endif
#ifndef __NR_openat
#define __NR_openat 322
#endif
#ifndef __NR_read
#define __NR_read 3
#endif
#undef __NR_mmap
#define __NR_mmap __NR_mmap2
static long syz_open_procfs(volatile long a0, volatile long a1)
{
char buf[128];
memset(buf, 0, sizeof(buf));
if (a0 == 0) {
snprintf(buf, sizeof(buf), "/proc/self/%s", (char*)a1);
} else if (a0 == -1) {
snprintf(buf, sizeof(buf), "/proc/thread-self/%s", (char*)a1);
} else {
snprintf(buf, sizeof(buf), "/proc/self/task/%d/%s", (int)a0, (char*)a1);
}
int fd = open(buf, O_RDWR);
if (fd == -1)
fd = open(buf, O_RDONLY);
return fd;
}
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffff000, /*len=*/0x1000, /*prot=*/0, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
syscall(__NR_mmap, /*addr=*/0x20000000, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
syscall(__NR_mmap, /*addr=*/0x21000000, /*len=*/0x1000, /*prot=*/0, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32, /*fd=*/(intptr_t)-1, /*offset=*/0);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// mkdir arguments: [
// path: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 00} (length 0x7)
// }
// mode: open_mode = 0x1ff (4 bytes)
// ]
memcpy((void*)0x20000000, "my_sys\000", 7);
syscall(__NR_mkdir, /*path=*/0x20000000, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1ff);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 00} (length 0x7)
// }
// type: ptr[in, buffer] {
// buffer: {73 79 73 66 73 00} (length 0x6)
// }
// flags: mount_flags = 0x0 (4 bytes)
// data: nil
// ]
memcpy((void*)0x20000200, "my_sys\000", 7);
memcpy((void*)0x20000300, "sysfs\000", 6);
syscall(__NR_mount, /*src=*/0, /*dst=*/0x20000200, /*type=*/0x20000300, /*flags=*/0, /*data=*/0);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {6d 79 5f 73 79 73 2f 62 75 73 2f 70 63 69 2f 64 65 76 69 63 65 73 2f 30 30 30 30 3a 30 30 3a 30 33 2e 30 2f 72 65 73 6f 75 72 63 65 31 00} (length 0x2e)
// }
// flags: open_flags = 0x2 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x20000500, "my_sys/bus/pci/devices/0000:00:03.0/resource1\000", 46);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x20000500, /*flags=O_RDWR*/2, /*mode=*/0);
if (res != -1)
r[0] = res;
// mmap$IORING_OFF_SQ_RING arguments: [
// addr: const = 0x0 (4 bytes)
// len: int32 = 0xff (4 bytes)
// prot: mmap_prot = 0x0 (4 bytes)
// flags: mmap_flags = 0x12 (4 bytes)
// fd: fd_io_uring (resource)
// offset: const = 0x0 (4 bytes)
// ]
// returns ring_ptr
syscall(__NR_mmap, /*addr=*/0, /*len=*/0xff, /*prot=*/0, /*flags=MAP_FIXED|MAP_PRIVATE*/0x12, /*fd=*/(intptr_t)r[0], /*offset=*/0);
// fsconfig$FSCONFIG_SET_BINARY arguments: [
// fd: fd_fscontext (resource)
// cmd: const = 0x2 (4 bytes)
// key: ptr[in, buffer] {
// buffer: {6d 65 6d 00 ff 45 65 76 f2 72 f1 18 1d b9 00 79 5f 68 75 67 24 08 6f 2d 03 51 60 5d 6f 8c 72 9d 5f 65 74 6c f9 dd 07 ad 5c 48 e9 34 08 2c 48 bf} (length 0x30)
// }
// value: nil
// aux: bytesize = 0x0 (4 bytes)
// ]
memcpy((void*)0x20000040, "mem\000\377Eev\362r\361\030\035\271\000y_hug$\bo-\003Q`]o\214r\235_etl\371\335\a\255\\H\3514\b,H\277", 48);
syscall(__NR_fsconfig, /*fd=*/(intptr_t)-1, /*cmd=*/2, /*key=*/0x20000040, /*value=*/0, /*aux=*/0);
// syz_open_procfs arguments: [
// pid: pid (resource)
// file: ptr[in, buffer] {
// buffer: {} (length 0x0)
// }
// ]
// returns fd
res = -1;
res = syz_open_procfs(/*pid=*/0, /*file=*/0x20000040);
if (res != -1)
r[1] = res;
// read$FUSE arguments: [
// fd: fd_fuse (resource)
// buf: ptr[out, fuse_in[read_buffer]] {
// fuse_in[read_buffer] {
// len: len = 0x2020 (4 bytes)
// opcode: int32 = 0x0 (4 bytes)
// unique: fuse_unique (resource)
// uid: uid (resource)
// gid: gid (resource)
// pid: pid (resource)
// padding: int32 = 0x0 (4 bytes)
// payload: buffer: (DirOut)
// }
// }
// len: bytesize = 0x2020 (4 bytes)
// ]
syscall(__NR_read, /*fd=*/(intptr_t)r[1], /*buf=*/0x200034c0, /*len=*/0x2020);
return 0;
}
Reply: **Analysis of the Bug**
The bug is a false positive `WARN_ON_ONCE` in the arm64 implementation of `ioremap_prot`.
When a user maps a PCI resource (or other specific device memory) with `PROT_NONE`, the resulting Virtual Memory Area (VMA) has its `vm_page_prot` set to `PAGE_NONE`. On the arm64 architecture, `PAGE_NONE` does not have the `PTE_USER` bit set, as it grants no access permissions to user-space.
If a process then attempts to read from this `PROT_NONE` VMA via `/proc/self/mem` (which is allowed and bypasses VMA access permissions), the kernel uses `generic_access_phys()` to perform the read. `generic_access_phys()` extracts the `pgprot_t` from the VMA (which is `PAGE_NONE`) and passes it to `ioremap_prot()` to temporarily map the physical memory into the kernel address space to perform the read.
`ioremap_prot()` contains a sanity check: `WARN_ON_ONCE(!(user_prot_val & PTE_USER))`. This was introduced to ensure that `ioremap_prot()` is only called with user-space protections and not accidentally with kernel protections. However, this check fails to account for `PAGE_NONE`, which is a valid user-space protection but intentionally lacks the `PTE_USER` bit. This triggers the warning and causes `ioremap_prot()` to return `NULL`, resulting in an `-ENOMEM` error for the read operation.
**Security Impact Assessment**
This bug has no security impact for the following reasons:
1. **Not Exploitable:** The bug is merely a false positive warning that results in a failed read operation (`-ENOMEM`). It does not cause memory corruption, use-after-free, out-of-bounds access, or information leaks.
2. **No Denial of Service:** The `WARN_ON_ONCE` macro does not panic or halt the kernel in standard production builds. It simply logs a stack trace and allows the kernel to continue executing safely.
3. **Requires Global Root Privileges:** The `generic_access_phys()` function is only utilized by a handful of specific drivers (e.g., PCI sysfs/procfs resources, `/dev/mem`, UIO devices). All of these device nodes and sysfs files are strictly restricted to root (e.g., `0600` root:root). An unprivileged user cannot open these files to create the necessary VMA in the first place.
4. **Not Accessible from User Namespaces:** Even if an unprivileged user creates a new user namespace and mounts `sysfs`, the PCI resource files remain owned by the global root (`GLOBAL_ROOT_UID`). Because the global root is not mapped within an unprivileged user namespace, the kernel correctly denies `CAP_DAC_OVERRIDE`. Thus, the user namespace root cannot bypass the file permissions to open the PCI resources.
In conclusion, this is a functional edge-case bug affecting the ability to read `PROT_NONE` device mappings via `/proc/pid/mem` on arm64, but it poses no security risk and is completely unreachable by unprivileged attackers.
|
| 6/2 |
2026/08/31 00:55 |
llm |
expert |
0m
Model:
Tokens:
input: 10517
output: 34
thoughts: 286
Thoughts: **Analyzing a Syzkaller Bug Report**
Okay, here's what I'm seeing: a syzkaller bug report. The warnings are all pointing to `arch/arm64/include/asm/io.h:283`. That's where I need to focus. It seems like the warnings are all triggered from within functions which indicate a race condition - `rcu_read_unlock`, `pte_unmap`, `follow_pfnmap_end` and `generic_access_phys` β suggesting memory access issues. My initial thought is that there's probably a locking issue or some sort of data corruption happening around physical memory access, and that the include file is merely the source of a function, defined elsewhere, where the issue is. This warrants some deeper investigation into the specific memory mapping and access patterns involved in those functions.
|
| 7/2 |
2026/08/31 00:55 |
tool |
grepper |
0m
Args: map[Expression:.* PathPrefix:arch/arm64/include/asm/io.h]
Results: map[Output:arch/arm64/include/asm/io.h:1:/* SPDX-License-Identifier: GPL-2.0-only */
arch/arm64/include/asm/io.h:2:/*
arch/arm64/include/asm/io.h:3: * Based on arch/arm/include/asm/io.h
arch/arm64/include/asm/io.h:4: *
arch/arm64/include/asm/io.h:5: * Copyright (C) 1996-2000 Russell King
arch/arm64/include/asm/io.h:6: * Copyright (C) 2012 ARM Ltd.
arch/arm64/include/asm/io.h:7: */
arch/arm64/include/asm/io.h:8:#ifndef __ASM_IO_H
arch/arm64/include/asm/io.h:9:#define __ASM_IO_H
arch/arm64/include/asm/io.h:10:
arch/arm64/include/asm/io.h:11:#include <linux/types.h>
arch/arm64/include/asm/io.h:12:#include <linux/pgtable.h>
arch/arm64/include/asm/io.h:13:
arch/arm64/include/asm/io.h:14:#include <asm/byteorder.h>
arch/arm64/include/asm/io.h:15:#include <asm/barrier.h>
arch/arm64/include/asm/io.h:16:#include <asm/memory.h>
arch/arm64/include/asm/io.h:17:#include <asm/early_ioremap.h>
arch/arm64/include/asm/io.h:18:#include <asm/alternative.h>
arch/arm64/include/asm/io.h:19:#include <asm/cpufeature.h>
arch/arm64/include/asm/io.h:20:#include <asm/rsi.h>
arch/arm64/include/asm/io.h:21:
arch/arm64/include/asm/io.h:22:/*
arch/arm64/include/asm/io.h:23: * Generic IO read/write. These perform native-endian accesses.
arch/arm64/include/asm/io.h:24: */
arch/arm64/include/asm/io.h:25:#define __raw_writeb __raw_writeb
arch/arm64/include/asm/io.h:26:static __always_inline void __raw_writeb(u8 val, volatile void __iomem *addr)
arch/arm64/include/asm/io.h:27:{
arch/arm64/include/asm/io.h:28: volatile u8 __iomem *ptr = addr;
arch/arm64/include/asm/io.h:29: asm volatile("strb %w0, %1" : : "rZ" (val), "Qo" (*ptr));
arch/arm64/include/asm/io.h:30:}
arch/arm64/include/asm/io.h:31:
arch/arm64/include/asm/io.h:32:#define __raw_writew __raw_writew
arch/arm64/include/asm/io.h:33:static __always_inline void __raw_writew(u16 val, volatile void __iomem *addr)
arch/arm64/include/asm/io.h:34:{
arch/arm64/include/asm/io.h:35: volatile u16 __iomem *ptr = addr;
arch/arm64/include/asm/io.h:36: asm volatile("strh %w0, %1" : : "rZ" (val), "Qo" (*ptr));
arch/arm64/include/asm/io.h:37:}
arch/arm64/include/asm/io.h:38:
arch/arm64/include/asm/io.h:39:#define __raw_writel __raw_writel
arch/arm64/include/asm/io.h:40:static __always_inline void __raw_writel(u32 val, volatile void __iomem *addr)
arch/arm64/include/asm/io.h:41:{
arch/arm64/include/asm/io.h:42: volatile u32 __iomem *ptr = addr;
arch/arm64/include/asm/io.h:43: asm volatile("str %w0, %1" : : "rZ" (val), "Qo" (*ptr));
arch/arm64/include/asm/io.h:44:}
arch/arm64/include/asm/io.h:45:
arch/arm64/include/asm/io.h:46:#define __raw_writeq __raw_writeq
arch/arm64/include/asm/io.h:47:static __always_inline void __raw_writeq(u64 val, volatile void __iomem *addr)
arch/arm64/include/asm/io.h:48:{
arch/arm64/include/asm/io.h:49: volatile u64 __iomem *ptr = addr;
arch/arm64/include/asm/io.h:50: asm volatile("str %x0, %1" : : "rZ" (val), "Qo" (*ptr));
arch/arm64/include/asm/io.h:51:}
arch/arm64/include/asm/io.h:52:
arch/arm64/include/asm/io.h:53:#define __raw_readb __raw_readb
arch/arm64/include/asm/io.h:54:static __always_inline u8 __raw_readb(const volatile void __iomem *addr)
arch/arm64/include/asm/io.h:55:{
arch/arm64/include/asm/io.h:56: u8 val;
arch/arm64/include/asm/io.h:57: asm volatile(ALTERNATIVE("nop", "dmb osh",
arch/arm64/include/asm/io.h:58: ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
arch/arm64/include/asm/io.h:59: ALTERNATIVE("ldrb %w0, [%1]",
arch/arm64/include/asm/io.h:60: "ldarb %w0, [%1]",
arch/arm64/include/asm/io.h:61: ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
arch/arm64/include/asm/io.h:62: : "=r" (val) : "r" (addr));
arch/arm64/include/asm/io.h:63: return val;
arch/arm64/include/asm/io.h:64:}
arch/arm64/include/asm/io.h:65:
arch/arm64/include/asm/io.h:66:#define __raw_readw __raw_readw
arch/arm64/include/asm/io.h:67:static __always_inline u16 __raw_readw(const volatile void __iomem *addr)
arch/arm64/include/asm/io.h:68:{
arch/arm64/include/asm/io.h:69: u16 val;
arch/arm64/include/asm/io.h:70:
arch/arm64/include/asm/io.h:71: asm volatile(ALTERNATIVE("nop", "dmb osh",
arch/arm64/include/asm/io.h:72: ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
arch/arm64/include/asm/io.h:73: ALTERNATIVE("ldrh %w0, [%1]",
arch/arm64/include/asm/io.h:74: "ldarh %w0, [%1]",
arch/arm64/include/asm/io.h:75: ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
arch/arm64/include/asm/io.h:76: : "=r" (val) : "r" (addr));
arch/arm64/include/asm/io.h:77: return val;
arch/arm64/include/asm/io.h:78:}
arch/arm64/include/asm/io.h:79:
arch/arm64/include/asm/io.h:80:#define __raw_readl __raw_readl
arch/arm64/include/asm/io.h:81:static __always_inline u32 __raw_readl(const volatile void __iomem *addr)
arch/arm64/include/asm/io.h:82:{
arch/arm64/include/asm/io.h:83: u32 val;
arch/arm64/include/asm/io.h:84: asm volatile(ALTERNATIVE("nop", "dmb osh",
arch/arm64/include/asm/io.h:85: ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
arch/arm64/include/asm/io.h:86: ALTERNATIVE("ldr %w0, [%1]",
arch/arm64/include/asm/io.h:87: "ldar %w0, [%1]",
arch/arm64/include/asm/io.h:88: ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
arch/arm64/include/asm/io.h:89: : "=r" (val) : "r" (addr));
arch/arm64/include/asm/io.h:90: return val;
arch/arm64/include/asm/io.h:91:}
arch/arm64/include/asm/io.h:92:
arch/arm64/include/asm/io.h:93:#define __raw_readq __raw_readq
arch/arm64/include/asm/io.h:94:static __always_inline u64 __raw_readq(const volatile void __iomem *addr)
arch/arm64/include/asm/io.h:95:{
arch/arm64/include/asm/io.h:96: u64 val;
arch/arm64/include/asm/io.h:97: asm volatile(ALTERNATIVE("nop", "dmb osh",
arch/arm64/include/asm/io.h:98: ARM64_WORKAROUND_NVIDIA_OLYMPUS_1027)
arch/arm64/include/asm/io.h:99: ALTERNATIVE("ldr %0, [%1]",
arch/arm64/include/asm/io.h:100: "ldar %0, [%1]",
arch/arm64/include/asm/io.h:101: ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
arch/arm64/include/asm/io.h:102: : "=r" (val) : "r" (addr));
arch/arm64/include/asm/io.h:103: return val;
arch/arm64/include/asm/io.h:104:}
arch/arm64/include/asm/io.h:105:
arch/arm64/include/asm/io.h:106:/* IO barriers */
arch/arm64/include/asm/io.h:107:#define __io_ar(v) \
arch/arm64/include/asm/io.h:108:({ \
arch/arm64/include/asm/io.h:109: unsigned long tmp; \
arch/arm64/include/asm/io.h:110: \
arch/arm64/include/asm/io.h:111: dma_rmb(); \
arch/arm64/include/asm/io.h:112: \
arch/arm64/include/asm/io.h:113: /* \
arch/arm64/include/asm/io.h:114: * Create a dummy control dependency from the IO read to any \
arch/arm64/include/asm/io.h:115: * later instructions. This ensures that a subsequent call to \
arch/arm64/include/asm/io.h:116: * udelay() will be ordered due to the ISB in get_cycles(). \
arch/arm64/include/asm/io.h:117: */ \
arch/arm64/include/asm/io.h:118: asm volatile("eor %0, %1, %1\n" \
arch/arm64/include/asm/io.h:119: "cbnz %0, ." \
arch/arm64/include/asm/io.h:120: : "=r" (tmp) : "r" ((unsigned long)(v)) \
arch/arm64/include/asm/io.h:121: : "memory"); \
arch/arm64/include/asm/io.h:122:})
arch/arm64/include/asm/io.h:123:
arch/arm64/include/asm/io.h:124:#define __io_bw() dma_wmb()
arch/arm64/include/asm/io.h:125:#define __io_br(v)
arch/arm64/include/asm/io.h:126:#define __io_aw(v)
arch/arm64/include/asm/io.h:127:
arch/arm64/include/asm/io.h:128:/* arm64-specific, don't use in portable drivers */
arch/arm64/include/asm/io.h:129:#define __iormb(v) __io_ar(v)
arch/arm64/include/asm/io.h:130:#define __iowmb() __io_bw()
arch/arm64/include/asm/io.h:131:#define __iomb() dma_mb()
arch/arm64/include/asm/io.h:132:
arch/arm64/include/asm/io.h:133:/*
arch/arm64/include/asm/io.h:134: * I/O port access primitives.
arch/arm64/include/asm/io.h:135: */
arch/arm64/include/asm/io.h:136:#define arch_has_dev_port() (1)
arch/arm64/include/asm/io.h:137:#define IO_SPACE_LIMIT (PCI_IO_SIZE - 1)
arch/arm64/include/asm/io.h:138:#define PCI_IOBASE ((void __iomem *)PCI_IO_START)
arch/arm64/include/asm/io.h:139:
arch/arm64/include/asm/io.h:140:/*
arch/arm64/include/asm/io.h:141: * The ARM64 iowrite implementation is intended to support drivers that want to
arch/arm64/include/asm/io.h:142: * use write combining. For instance PCI drivers using write combining with a 64
arch/arm64/include/asm/io.h:143: * byte __iowrite64_copy() expect to get a 64 byte MemWr TLP on the PCIe bus.
arch/arm64/include/asm/io.h:144: *
arch/arm64/include/asm/io.h:145: * Newer ARM core have sensitive write combining buffers, it is important that
arch/arm64/include/asm/io.h:146: * the stores be contiguous blocks of store instructions. Normal memcpy
arch/arm64/include/asm/io.h:147: * approaches have a very low chance to generate write combining.
arch/arm64/include/asm/io.h:148: *
arch/arm64/include/asm/io.h:149: * Since this is the only API on ARM64 that should be used with write combining
arch/arm64/include/asm/io.h:150: * it also integrates the DGH hint which is supposed to lower the latency to
arch/arm64/include/asm/io.h:151: * emit the large TLP from the CPU.
arch/arm64/include/asm/io.h:152: */
arch/arm64/include/asm/io.h:153:
arch/arm64/include/asm/io.h:154:static __always_inline void
arch/arm64/include/asm/io.h:155:__const_memcpy_toio_aligned32(volatile u32 __iomem *to, const u32 *from,
arch/arm64/include/asm/io.h:156: size_t count)
arch/arm64/include/asm/io.h:157:{
arch/arm64/include/asm/io.h:158: switch (count) {
arch/arm64/include/asm/io.h:159: case 8:
arch/arm64/include/asm/io.h:160: asm volatile("str %w0, [%8, #4 * 0]\n"
arch/arm64/include/asm/io.h:161: "str %w1, [%8, #4 * 1]\n"
arch/arm64/include/asm/io.h:162: "str %w2, [%8, #4 * 2]\n"
arch/arm64/include/asm/io.h:163: "str %w3, [%8, #4 * 3]\n"
arch/arm64/include/asm/io.h:164: "str %w4, [%8, #4 * 4]\n"
arch/arm64/include/asm/io.h:165: "str %w5, [%8, #4 * 5]\n"
arch/arm64/include/asm/io.h:166: "str %w6, [%8, #4 * 6]\n"
arch/arm64/include/asm/io.h:167: "str %w7, [%8, #4 * 7]\n"
arch/arm64/include/asm/io.h:168: :
arch/arm64/include/asm/io.h:169: : "rZ"(from[0]), "rZ"(from[1]), "rZ"(from[2]),
arch/arm64/include/asm/io.h:170: "rZ"(from[3]), "rZ"(from[4]), "rZ"(from[5]),
arch/arm64/include/asm/io.h:171: "rZ"(from[6]), "rZ"(from[7]), "r"(to));
arch/arm64/include/asm/io.h:172: break;
arch/arm64/include/asm/io.h:173: case 4:
arch/arm64/include/asm/io.h:174: asm volatile("str %w0, [%4, #4 * 0]\n"
arch/arm64/include/asm/io.h:175: "str %w1, [%4, #4 * 1]\n"
arch/arm64/include/asm/io.h:176: "str %w2, [%4, #4 * 2]\n"
arch/arm64/include/asm/io.h:177: "str %w3, [%4, #4 * 3]\n"
arch/arm64/include/asm/io.h:178: :
arch/arm64/include/asm/io.h:179: : "rZ"(from[0]), "rZ"(from[1]), "rZ"(from[2]),
arch/arm64/include/asm/io.h:180: "rZ"(from[3]), "r"(to));
arch/arm64/include/asm/io.h:181: break;
arch/arm64/include/asm/io.h:182: case 2:
arch/arm64/include/asm/io.h:183: asm volatile("str %w0, [%2, #4 * 0]\n"
arch/arm64/include/asm/io.h:184: "str %w1, [%2, #4 * 1]\n"
arch/arm64/include/asm/io.h:185: :
arch/arm64/include/asm/io.h:186: : "rZ"(from[0]), "rZ"(from[1]), "r"(to));
arch/arm64/include/asm/io.h:187: break;
arch/arm64/include/asm/io.h:188: case 1:
arch/arm64/include/asm/io.h:189: __raw_writel(*from, to);
arch/arm64/include/asm/io.h:190: break;
arch/arm64/include/asm/io.h:191: default:
arch/arm64/include/asm/io.h:192: BUILD_BUG();
arch/arm64/include/asm/io.h:193: }
arch/arm64/include/asm/io.h:194:}
arch/arm64/include/asm/io.h:195:
arch/arm64/include/asm/io.h:196:void __iowrite32_copy_full(void __iomem *to, const void *from, size_t count);
arch/arm64/include/asm/io.h:197:
arch/arm64/include/asm/io.h:198:static __always_inline void
arch/arm64/include/asm/io.h:199:__iowrite32_copy(void __iomem *to, const void *from, size_t count)
arch/arm64/include/asm/io.h:200:{
arch/arm64/include/asm/io.h:201: if (__builtin_constant_p(count) &&
arch/arm64/include/asm/io.h:202: (count == 8 || count == 4 || count == 2 || count == 1)) {
arch/arm64/include/asm/io.h:203: __const_memcpy_toio_aligned32(to, from, count);
arch/arm64/include/asm/io.h:204: dgh();
arch/arm64/include/asm/io.h:205: } else {
arch/arm64/include/asm/io.h:206: __iowrite32_copy_full(to, from, count);
arch/arm64/include/asm/io.h:207: }
arch/arm64/include/asm/io.h:208:}
arch/arm64/include/asm/io.h:209:#define __iowrite32_copy __iowrite32_copy
arch/arm64/include/asm/io.h:210:
arch/arm64/include/asm/io.h:211:static __always_inline void
arch/arm64/include/asm/io.h:212:__const_memcpy_toio_aligned64(volatile u64 __iomem *to, const u64 *from,
arch/arm64/include/asm/io.h:213: size_t count)
arch/arm64/include/asm/io.h:214:{
arch/arm64/include/asm/io.h:215: switch (count) {
arch/arm64/include/asm/io.h:216: case 8:
arch/arm64/include/asm/io.h:217: asm volatile("str %x0, [%8, #8 * 0]\n"
arch/arm64/include/asm/io.h:218: "str %x1, [%8, #8 * 1]\n"
arch/arm64/include/asm/io.h:219: "str %x2, [%8, #8 * 2]\n"
arch/arm64/include/asm/io.h:220: "str %x3, [%8, #8 * 3]\n"
arch/arm64/include/asm/io.h:221: "str %x4, [%8, #8 * 4]\n"
arch/arm64/include/asm/io.h:222: "str %x5, [%8, #8 * 5]\n"
arch/arm64/include/asm/io.h:223: "str %x6, [%8, #8 * 6]\n"
arch/arm64/include/asm/io.h:224: "str %x7, [%8, #8 * 7]\n"
arch/arm64/include/asm/io.h:225: :
arch/arm64/include/asm/io.h:226: : "rZ"(from[0]), "rZ"(from[1]), "rZ"(from[2]),
arch/arm64/include/asm/io.h:227: "rZ"(from[3]), "rZ"(from[4]), "rZ"(from[5]),
arch/arm64/include/asm/io.h:228: "rZ"(from[6]), "rZ"(from[7]), "r"(to));
arch/arm64/include/asm/io.h:229: break;
arch/arm64/include/asm/io.h:230: case 4:
arch/arm64/include/asm/io.h:231: asm volatile("str %x0, [%4, #8 * 0]\n"
arch/arm64/include/asm/io.h:232: "str %x1, [%4, #8 * 1]\n"
arch/arm64/include/asm/io.h:233: "str %x2, [%4, #8 * 2]\n"
arch/arm64/include/asm/io.h:234: "str %x3, [%4, #8 * 3]\n"
arch/arm64/include/asm/io.h:235: :
arch/arm64/include/asm/io.h:236: : "rZ"(from[0]), "rZ"(from[1]), "rZ"(from[2]),
arch/arm64/include/asm/io.h:237: "rZ"(from[3]), "r"(to));
arch/arm64/include/asm/io.h:238: break;
arch/arm64/include/asm/io.h:239: case 2:
arch/arm64/include/asm/io.h:240: asm volatile("str %x0, [%2, #8 * 0]\n"
arch/arm64/include/asm/io.h:241: "str %x1, [%2, #8 * 1]\n"
arch/arm64/include/asm/io.h:242: :
arch/arm64/include/asm/io.h:243: : "rZ"(from[0]), "rZ"(from[1]), "r"(to));
arch/arm64/include/asm/io.h:244: break;
arch/arm64/include/asm/io.h:245: case 1:
arch/arm64/include/asm/io.h:246: __raw_writeq(*from, to);
arch/arm64/include/asm/io.h:247: break;
arch/arm64/include/asm/io.h:248: default:
arch/arm64/include/asm/io.h:249: BUILD_BUG();
arch/arm64/include/asm/io.h:250: }
arch/arm64/include/asm/io.h:251:}
arch/arm64/include/asm/io.h:252:
arch/arm64/include/asm/io.h:253:void __iowrite64_copy_full(void __iomem *to, const void *from, size_t count);
arch/arm64/include/asm/io.h:254:
arch/arm64/include/asm/io.h:255:static __always_inline void
arch/arm64/include/asm/io.h:256:__iowrite64_copy(void __iomem *to, const void *from, size_t count)
arch/arm64/include/asm/io.h:257:{
arch/arm64/include/asm/io.h:258: if (__builtin_constant_p(count) &&
arch/arm64/include/asm/io.h:259: (count == 8 || count == 4 || count == 2 || count == 1)) {
arch/arm64/include/asm/io.h:260: __const_memcpy_toio_aligned64(to, from, count);
arch/arm64/include/asm/io.h:261: dgh();
arch/arm64/include/asm/io.h:262: } else {
arch/arm64/include/asm/io.h:263: __iowrite64_copy_full(to, from, count);
arch/arm64/include/asm/io.h:264: }
arch/arm64/include/asm/io.h:265:}
arch/arm64/include/asm/io.h:266:#define __iowrite64_copy __iowrite64_copy
arch/arm64/include/asm/io.h:267:
arch/arm64/include/asm/io.h:268:/*
arch/arm64/include/asm/io.h:269: * I/O memory mapping functions.
arch/arm64/include/asm/io.h:270: */
arch/arm64/include/asm/io.h:271:
arch/arm64/include/asm/io.h:272:typedef int (*ioremap_prot_hook_t)(phys_addr_t phys_addr, size_t size,
arch/arm64/include/asm/io.h:273: pgprot_t *prot);
arch/arm64/include/asm/io.h:274:int arm64_ioremap_prot_hook_register(const ioremap_prot_hook_t hook);
arch/arm64/include/asm/io.h:275:void __iomem *__ioremap_prot(phys_addr_t phys, size_t size, pgprot_t prot);
arch/arm64/include/asm/io.h:276:
arch/arm64/include/asm/io.h:277:static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
arch/arm64/include/asm/io.h:278: pgprot_t user_prot)
arch/arm64/include/asm/io.h:279:{
arch/arm64/include/asm/io.h:280: pgprot_t prot;
arch/arm64/include/asm/io.h:281: ptval_t user_prot_val = pgprot_val(user_prot);
arch/arm64/include/asm/io.h:282:
arch/arm64/include/asm/io.h:283: if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
arch/arm64/include/asm/io.h:284: return NULL;
arch/arm64/include/asm/io.h:285:
arch/arm64/include/asm/io.h:286: prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK,
arch/arm64/include/asm/io.h:287: user_prot_val & PTE_ATTRINDX_MASK);
arch/arm64/include/asm/io.h:288: return __ioremap_prot(phys, size, prot);
arch/arm64/include/asm/io.h:289:}
arch/arm64/include/asm/io.h:290:#define ioremap_prot ioremap_prot
arch/arm64/include/asm/io.h:291:
arch/arm64/include/asm/io.h:292:#define ioremap(addr, size) \
arch/arm64/include/asm/io.h:293: __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
arch/arm64/include/asm/io.h:294:#define ioremap_wc(addr, size) \
arch/arm64/include/asm/io.h:295: __ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC))
arch/arm64/include/asm/io.h:296:#define ioremap_np(addr, size) \
arch/arm64/include/asm/io.h:297: __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRnE))
arch/arm64/include/asm/io.h:298:
arch/arm64/include/asm/io.h:299:
arch/arm64/include/asm/io.h:300:#define ioremap_encrypted(addr, size) \
arch/arm64/include/asm/io.h:301: __ioremap_prot((addr), (size), PAGE_KERNEL)
arch/arm64/include/asm/io.h:302:
arch/arm64/include/asm/io.h:303:/*
arch/arm64/include/asm/io.h:304: * io{read,write}{16,32,64}be() macros
arch/arm64/include/asm/io.h:305: */
arch/arm64/include/asm/io.h:306:#define ioread16be(p) ({ __u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(__v); __v; })
arch/arm64/include/asm/io.h:307:#define ioread32be(p) ({ __u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(__v); __v; })
arch/arm64/include/asm/io.h:308:#define ioread64be(p) ({ __u64 __v = be64_to_cpu((__force __be64)__raw_readq(p)); __iormb(__v); __v; })
arch/arm64/include/asm/io.h:309:
arch/arm64/include/asm/io.h:310:#define iowrite16be(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_be16(v), p); })
arch/arm64/include/asm/io.h:311:#define iowrite32be(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_be32(v), p); })
arch/arm64/include/asm/io.h:312:#define iowrite64be(v,p) ({ __iowmb(); __raw_writeq((__force __u64)cpu_to_be64(v), p); })
arch/arm64/include/asm/io.h:313:
arch/arm64/include/asm/io.h:314:#include <asm-generic/io.h>
arch/arm64/include/asm/io.h:315:
arch/arm64/include/asm/io.h:316:#define ioremap_cache ioremap_cache
arch/arm64/include/asm/io.h:317:static inline void __iomem *ioremap_cache(phys_addr_t addr, size_t size)
arch/arm64/include/asm/io.h:318:{
arch/arm64/include/asm/io.h:319: if (pfn_is_map_memory(__phys_to_pfn(addr)))
arch/arm64/include/asm/io.h:320: return (void __iomem *)__phys_to_virt(addr);
arch/arm64/include/asm/io.h:321:
arch/arm64/include/asm/io.h:322: return __ioremap_prot(addr, size, __pgprot(PROT_NORMAL));
arch/arm64/include/asm/io.h:323:}
arch/arm64/include/asm/io.h:324:
arch/arm64/include/asm/io.h:325:/*
arch/arm64/include/asm/io.h:326: * More restrictive address range checking than the default implementation
arch/arm64/include/asm/io.h:327: * (PHYS_OFFSET and PHYS_MASK taken into account).
arch/arm64/include/asm/io.h:328: */
arch/arm64/include/asm/io.h:329:#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
arch/arm64/include/asm/io.h:330:extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
arch/arm64/include/asm/io.h:331:extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
arch/arm64/include/asm/io.h:332:
arch/arm64/include/asm/io.h:333:extern bool arch_memremap_can_ram_remap(resource_size_t offset, size_t size,
arch/arm64/include/asm/io.h:334: unsigned long flags);
arch/arm64/include/asm/io.h:335:#define arch_memremap_can_ram_remap arch_memremap_can_ram_remap
arch/arm64/include/asm/io.h:336:
arch/arm64/include/asm/io.h:337:static inline bool arm64_is_protected_mmio(phys_addr_t phys_addr, size_t size)
arch/arm64/include/asm/io.h:338:{
arch/arm64/include/asm/io.h:339: if (unlikely(is_realm_world()))
arch/arm64/include/asm/io.h:340: return arm64_rsi_is_protected(phys_addr, size);
arch/arm64/include/asm/io.h:341: return false;
arch/arm64/include/asm/io.h:342:}
arch/arm64/include/asm/io.h:343:
arch/arm64/include/asm/io.h:344:#endif /* __ASM_IO_H */
]
|
| 8/2 |
2026/08/31 00:55 |
llm |
expert |
0m
Model:
Tokens:
input: 21320
output: 30
thoughts: 476
Thoughts: **Investigating a Potential Memory Mapping Issue**
Okay, so I've got this warning stemming from `ioremap_prot`, specifically this snippet:
```c
static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
pgprot_t user_prot)
{
pgprot_t prot;
ptval_t user_prot_val = pgprot_val(user_prot);
if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
return NULL;
prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK,
user_prot_val & PTE_ATTRINDX_MASK);
return __ioremap_prot(phys, size, prot);
}
```
The warning is triggered if `PTE_USER` isn't set. The critical part is the stack trace. I see calls to `rcu_read_unlock`, `pte_unmap`, `follow_pfnmap_end`, and then `generic_access_phys`. I know that sequence, that's pretty standard for VM operations. But then it goes to `kernfs_vma_access`, `__access_remote_vm`, and eventually ends in `mem_rw` within `/proc/`. I need to figure out how `generic_access_phys` and `ioremap_prot` are connected. I need to trace this back.
My immediate thought is, "Wait, `generic_access_phys` calls `ioremap_prot`?" That's not immediately obvious, and that's the connection I need to verify. I need to understand the path of calls. That's the key. I should look in `mm/memory.c` at `generic_access_phys` and see if it is, in fact, the call site.
|
| 9/2 |
2026/08/31 00:55 |
tool |
grepper |
0m
Args: map[Expression:generic_access_phys PathPrefix:mm/memory.c]
Results: map[Output:mm/memory.c=7079=EXPORT_SYMBOL_GPL(follow_pfnmap_end);
--
mm/memory.c-7082-/**
mm/memory.c:7083: * generic_access_phys - generic implementation for iomem mmap access
mm/memory.c-7084- * @vma: the vma to access
--
mm/memory.c-7093- */
mm/memory.c:7094:int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-7095- void *buf, int len, int write)
--
mm/memory.c-7141-}
mm/memory.c:7142:EXPORT_SYMBOL_GPL(generic_access_phys);
mm/memory.c-7143-#endif
]
|
| 10/2 |
2026/08/31 00:55 |
llm |
expert |
0m
Model:
Tokens:
input: 22470
output: 35
thoughts: 19
Thoughts: Here's my summary, as though I'm reading and thinking about the code:
**Diving into `generic_access_phys` in `mm/memory.c`**
Alright, let's get into this. I'm going to pull up `generic_access_phys` in `mm/memory.c`. I need to refresh my understanding of how the kernel handles physical memory access directly. This function is crucial, especially when we're dealing with things like device drivers or low-level memory operations that bypass the virtual memory mapping. I expect it handles the low-level aspects, potentially dealing with the page tables, address translation (or bypassing it!), and the appropriate memory barriers to ensure data consistency. I'm hoping this gives me a clearer picture of how raw physical addresses are accessed within the kernel's memory management framework, particularly if the access is *generic*, i.e. not device-specific. Let's see what the function signature reveals about potential flags for access type or any locking mechanisms involved. I'm also interested in how it handles potential errors. This is usually the stage where things can go sideways if you're not careful. I'm ready to dig into the details to understand the core logic and any potential subtleties in its implementation. It could be interesting to see how it interacts with the architecture-specific routines or if it offers a more portable abstraction layer.
|
| 11/2 |
2026/08/31 00:55 |
tool |
read-file |
0m
Args: map[File:mm/memory.c FirstLine:7080 LineCount:100]
Results: map[Contents:7080:
7081: #ifdef CONFIG_HAVE_IOREMAP_PROT
7082: /**
7083: * generic_access_phys - generic implementation for iomem mmap access
7084: * @vma: the vma to access
7085: * @addr: userspace address, not relative offset within @vma
7086: * @buf: buffer to read/write
7087: * @len: length of transfer
7088: * @write: set to FOLL_WRITE when writing, otherwise reading
7089: *
7090: * This is a generic implementation for &vm_operations_struct.access for an
7091: * iomem mapping. This callback is used by access_process_vm() when the @vma is
7092: * not page based.
7093: */
7094: int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
7095: void *buf, int len, int write)
7096: {
7097: resource_size_t phys_addr;
7098: pgprot_t prot = __pgprot(0);
7099: void __iomem *maddr;
7100: int offset = offset_in_page(addr);
7101: int ret = -EINVAL;
7102: bool writable;
7103: struct follow_pfnmap_args args = { .vma = vma, .address = addr };
7104:
7105: retry:
7106: if (follow_pfnmap_start(&args))
7107: return -EINVAL;
7108: prot = args.pgprot;
7109: phys_addr = (resource_size_t)args.pfn << PAGE_SHIFT;
7110: writable = args.writable;
7111: follow_pfnmap_end(&args);
7112:
7113: if ((write & FOLL_WRITE) && !writable)
7114: return -EINVAL;
7115:
7116: maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
7117: if (!maddr)
7118: return -ENOMEM;
7119:
7120: if (follow_pfnmap_start(&args))
7121: goto out_unmap;
7122:
7123: if ((pgprot_val(prot) != pgprot_val(args.pgprot)) ||
7124: (phys_addr != (args.pfn << PAGE_SHIFT)) ||
7125: (writable != args.writable)) {
7126: follow_pfnmap_end(&args);
7127: iounmap(maddr);
7128: goto retry;
7129: }
7130:
7131: if (write)
7132: memcpy_toio(maddr + offset, buf, len);
7133: else
7134: memcpy_fromio(buf, maddr + offset, len);
7135: ret = len;
7136: follow_pfnmap_end(&args);
7137: out_unmap:
7138: iounmap(maddr);
7139:
7140: return ret;
7141: }
7142: EXPORT_SYMBOL_GPL(generic_access_phys);
7143: #endif
7144:
7145: /*
7146: * Access another process' address space as given in mm.
7147: */
7148: static int __access_remote_vm(struct mm_struct *mm, unsigned long addr,
7149: void *buf, int len, unsigned int gup_flags)
7150: {
7151: void *old_buf = buf;
7152: int write = gup_flags & FOLL_WRITE;
7153:
7154: if (mmap_read_lock_killable(mm))
7155: return 0;
7156:
7157: /* Untag the address before looking up the VMA */
7158: addr = untagged_addr_remote(mm, addr);
7159:
7160: /* Avoid triggering the temporary warning in __get_user_pages */
7161: if (!vma_lookup(mm, addr) && !expand_stack(mm, addr))
7162: return 0;
7163:
7164: /* ignore errors, just check how much was successfully transferred */
7165: while (len) {
7166: int bytes, offset;
7167: void *maddr;
7168: struct folio *folio;
7169: struct vm_area_struct *vma = NULL;
7170: struct page *page = get_user_page_vma_remote(mm, addr,
7171: gup_flags, &vma);
7172:
7173: if (IS_ERR(page)) {
7174: /* We might need to expand the stack to access it */
7175: vma = vma_lookup(mm, addr);
7176: if (!vma) {
7177: vma = expand_stack(mm, addr);
7178:
7179: /* mmap_lock was dropped on failure */
]
|
| 12/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 24333
output: 32
thoughts: 125
Thoughts: **Analyzing a Potential Security Vulnerability**
Okay, so I'm looking at this warning: `if (WARN_ON_ONCE(!(user_prot_val & PTE_USER))) return NULL;`. Right, that's telling me that `ioremap_prot` was called, and the `user_prot` argument *didn't* have the `PTE_USER` bit set. That's immediately interesting, because it implies something's going awry with how memory is being mapped and accessed, likely related to user vs. kernel space access.
Now, where did this invalid `user_prot` originate? Diving deeper, I see this `generic_access_phys` function is involved. Specifically, it seems `prot` is derived from `follow_pfnmap_start(&args)`. Okay, so `follow_pfnmap_start` seems to be responsible for setting `args.pgprot`.
My next logical step is to understand what's happening within `follow_pfnmap_start`. I need to trace back from there to find out why the `user_prot` isn't setting the `PTE_USER` bit when it should be. The goal is to determine the root cause, verify the risk, and develop a fix if it's a valid vulnerability. Let's see how `follow_pfnmap_start` works.
|
| 13/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:follow_pfnmap_start PathPrefix:mm/memory.c]
Results: map[Output:mm/memory.c=6932=static inline void pfnmap_lockdep_assert(struct vm_area_struct *vma)
--
mm/memory.c-6946-/**
mm/memory.c:6947: * follow_pfnmap_start() - Look up a pfn mapping at a user virtual address
mm/memory.c-6948- * @args: Pointer to struct @follow_pfnmap_args
--
mm/memory.c-6976- */
mm/memory.c:6977:int follow_pfnmap_start(struct follow_pfnmap_args *args)
mm/memory.c-6978-{
--
mm/memory.c-7062-}
mm/memory.c:7063:EXPORT_SYMBOL_GPL(follow_pfnmap_start);
mm/memory.c-7064-
mm/memory.c-7065-/**
mm/memory.c:7066: * follow_pfnmap_end(): End a follow_pfnmap_start() process
mm/memory.c-7067- * @args: Pointer to struct @follow_pfnmap_args
mm/memory.c-7068- *
mm/memory.c:7069: * Must be used in pair of follow_pfnmap_start(). See the start() function
mm/memory.c-7070- * above for more information.
--
mm/memory.c=7094=int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
--
mm/memory.c-7105-retry:
mm/memory.c:7106: if (follow_pfnmap_start(&args))
mm/memory.c-7107- return -EINVAL;
--
mm/memory.c-7119-
mm/memory.c:7120: if (follow_pfnmap_start(&args))
mm/memory.c-7121- goto out_unmap;
]
|
| 14/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 25305
output: 35
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were having them, targeted towards an expert:
**Delving into `follow_pfnmap_start`**
Right, let's dive into this `follow_pfnmap_start` function. I'm guessing this is the starting point for some sort of physical page frame number (PFN) mapping or tracking operation. I need to understand its role within the larger context, so I'll be looking for clues about the architecture it operates within, the data structures it manipulates, and the overall objective it's trying to achieve. Is this part of a direct memory access (DMA) implementation, memory management unit (MMU) handling, or something else entirely? The name itself gives us a hint, but the specific implementation details are what I need to unpack to form a solid understanding. Let's see what's inside. I'll be looking for any initializations, loop conditions, calls to other functions (that will give me hints), and of course, where it eventually leads. The devil's always in the details, especially in kernel-level code.
|
| 15/2 |
2026/08/31 00:56 |
tool |
read-file |
0m
Args: map[File:mm/memory.c FirstLine:6977 LineCount:100]
Results: map[Contents:6977: int follow_pfnmap_start(struct follow_pfnmap_args *args)
6978: {
6979: struct vm_area_struct *vma = args->vma;
6980: unsigned long address = args->address;
6981: struct mm_struct *mm = vma->vm_mm;
6982: spinlock_t *lock;
6983: pgd_t *pgdp;
6984: p4d_t *p4dp, p4d;
6985: pud_t *pudp, pud;
6986: pmd_t *pmdp, pmd;
6987: pte_t *ptep, pte;
6988:
6989: pfnmap_lockdep_assert(vma);
6990:
6991: if (unlikely(address < vma->vm_start || address >= vma->vm_end))
6992: goto out;
6993:
6994: if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
6995: goto out;
6996: retry:
6997: pgdp = pgd_offset(mm, address);
6998: if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp)))
6999: goto out;
7000:
7001: p4dp = p4d_offset(pgdp, address);
7002: p4d = p4dp_get(p4dp);
7003: if (p4d_none(p4d) || unlikely(p4d_bad(p4d)))
7004: goto out;
7005:
7006: pudp = pud_offset(p4dp, address);
7007: pud = pudp_get(pudp);
7008: if (!pud_present(pud))
7009: goto out;
7010: if (pud_leaf(pud)) {
7011: lock = pud_lock(mm, pudp);
7012: pud = pudp_get(pudp);
7013:
7014: if (unlikely(!pud_present(pud))) {
7015: spin_unlock(lock);
7016: goto out;
7017: } else if (unlikely(!pud_leaf(pud))) {
7018: spin_unlock(lock);
7019: goto retry;
7020: }
7021: pfnmap_args_setup(args, lock, NULL, pud_pgprot(pud),
7022: pud_pfn(pud), PUD_MASK, pud_write(pud),
7023: pud_special(pud));
7024: return 0;
7025: }
7026:
7027: pmdp = pmd_offset(pudp, address);
7028: pmd = pmdp_get_lockless(pmdp);
7029: if (!pmd_present(pmd))
7030: goto out;
7031: if (pmd_leaf(pmd)) {
7032: lock = pmd_lock(mm, pmdp);
7033: pmd = pmdp_get(pmdp);
7034:
7035: if (unlikely(!pmd_present(pmd))) {
7036: spin_unlock(lock);
7037: goto out;
7038: } else if (unlikely(!pmd_leaf(pmd))) {
7039: spin_unlock(lock);
7040: goto retry;
7041: }
7042: pfnmap_args_setup(args, lock, NULL, pmd_pgprot(pmd),
7043: pmd_pfn(pmd), PMD_MASK, pmd_write(pmd),
7044: pmd_special(pmd));
7045: return 0;
7046: }
7047:
7048: ptep = pte_offset_map_lock(mm, pmdp, address, &lock);
7049: if (!ptep)
7050: goto out;
7051: pte = ptep_get(ptep);
7052: if (!pte_present(pte))
7053: goto unlock;
7054: pfnmap_args_setup(args, lock, ptep, pte_pgprot(pte),
7055: pte_pfn(pte), PAGE_MASK, pte_write(pte),
7056: pte_special(pte));
7057: return 0;
7058: unlock:
7059: pte_unmap_unlock(ptep, lock);
7060: out:
7061: return -EINVAL;
7062: }
7063: EXPORT_SYMBOL_GPL(follow_pfnmap_start);
7064:
7065: /**
7066: * follow_pfnmap_end(): End a follow_pfnmap_start() process
7067: * @args: Pointer to struct @follow_pfnmap_args
7068: *
7069: * Must be used in pair of follow_pfnmap_start(). See the start() function
7070: * above for more information.
7071: */
7072: void follow_pfnmap_end(struct follow_pfnmap_args *args)
7073: {
7074: if (args->lock)
7075: spin_unlock(args->lock);
7076: if (args->ptep)
]
|
| 16/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 27190
output: 39
thoughts: 474
Thoughts: **Analyzing a Memory Protection Issue**
Okay, so I'm following the code flow, starting with `follow_pfnmap_start`, which grabs the `pgprot` from the page table entry, or `pte_pgprot(pte)`. Then, `generic_access_phys` calls `ioremap_prot` using that `pgprot`, mapping the physical address. And `ioremap_prot` is the one throwing the `WARN_ON_ONCE` message, specifically when `!(user_prot_val & PTE_USER)`. My gut tells me this is the root cause.
Now, `pte_pgprot` should give us the protection bits from the PTE, right? So, if the PTE *doesn't* have `PTE_USER` set, `ioremap_prot` will raise a warning. But why would a user-space VMA ever have a PTE *without* the user bit? That doesn't make sense on the face of it.
Okay, let's step back and look at the reproducer: it's `mmap`ing something on `/sys/bus/pci/devices/0000:00:03.0/resource1`. That's a PCI resource, so I expect the `mmap` call to set up the PTEs with `PTE_USER` because it's being mapped into user space, which is expected behavior. The PCI resource mapping likely goes through `pci_mmap_resource_range` or something similar that actually sets up the PTEs with the appropriate permissions.
But wait, looking at the code, it's opening the resource using `openat` and then `mmap`ing it. The key is in the `mmap` call. It's using `prot=0`! That's `PROT_NONE`. Bingo!
`PROT_NONE` means, essentially, no access. So, if the VMA's protection is `PROT_NONE`, the VMA has `vm_page_prot` without `PTE_USER`? That's the key. This is why the warning is triggered. It seems `mmap` is setting up a page table entry, but with no access and no user permissions. Time to check what `PROT_NONE` actually maps to on arm64 and confirm this hypothesis.
|
| 17/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:PAGE_NONE PathPrefix:arch/arm64/include/asm/pgtable-prot.h]
Results: map[Output:arch/arm64/include/asm/pgtable-prot.h=87=static inline bool __pure lpa2_is_enabled(void)
--
arch/arm64/include/asm/pgtable-prot.h-123-
arch/arm64/include/asm/pgtable-prot.h:124:#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PRESENT_INVALID | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
arch/arm64/include/asm/pgtable-prot.h-125-/* shared+writable pages are clean by default, hence PTE_RDONLY|PTE_WRITE */
]
|
| 18/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 28395
output: 40
thoughts: 44
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as a seasoned expert:
**Investigating Page Table Entry Flags**
Alright, that's interesting. I just realized that `PAGE_NONE`, as I'd expect, doesn't include the `PTE_USER` flag. That makes sense, given its usual purpose. Now, let me quickly verify something... I seem to recall that the default page settings, the ones defined by `_PAGE_DEFAULT`, *do* include `PTE_USER`. I should double-check that to confirm. Let me just take a look at the definition of `_PAGE_DEFAULT` to be absolutely sure. Gotta maintain that precision!
|
| 19/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:_PAGE_DEFAULT PathPrefix:arch/arm64/include/asm/pgtable-prot.h]
Results: map[Output:arch/arm64/include/asm/pgtable-prot.h-52-
arch/arm64/include/asm/pgtable-prot.h:53:#define _PAGE_DEFAULT (_PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL))
arch/arm64/include/asm/pgtable-prot.h-54-
--
arch/arm64/include/asm/pgtable-prot.h-60-
arch/arm64/include/asm/pgtable-prot.h:61:#define _PAGE_SHARED (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
arch/arm64/include/asm/pgtable-prot.h:62:#define _PAGE_SHARED_EXEC (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_WRITE)
arch/arm64/include/asm/pgtable-prot.h:63:#define _PAGE_READONLY (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
arch/arm64/include/asm/pgtable-prot.h:64:#define _PAGE_READONLY_EXEC (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
arch/arm64/include/asm/pgtable-prot.h:65:#define _PAGE_EXECONLY (_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
arch/arm64/include/asm/pgtable-prot.h-66-
--
arch/arm64/include/asm/pgtable-prot.h=87=static inline bool __pure lpa2_is_enabled(void)
--
arch/arm64/include/asm/pgtable-prot.h-123-
arch/arm64/include/asm/pgtable-prot.h:124:#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PRESENT_INVALID | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
arch/arm64/include/asm/pgtable-prot.h-125-/* shared+writable pages are clean by default, hence PTE_RDONLY|PTE_WRITE */
--
arch/arm64/include/asm/pgtable-prot.h-165-
arch/arm64/include/asm/pgtable-prot.h:166:#define _PAGE_GCS (_PAGE_DEFAULT | PTE_NG | PTE_UXN | PTE_WRITE | PTE_USER)
arch/arm64/include/asm/pgtable-prot.h:167:#define _PAGE_GCS_RO (_PAGE_DEFAULT | PTE_NG | PTE_UXN | PTE_USER)
arch/arm64/include/asm/pgtable-prot.h-168-
]
|
| 20/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 29359
output: 41
thoughts: 279
Thoughts: **My Analysis of PCI Resource Mapping**
Okay, so I'm thinking about how the page table entries (PTEs) are set up, specifically when dealing with PCI resources. Right, `_PAGE_DEFAULT` is missing `PTE_USER`, and `_PAGE_SHARED` *does* add it. This means `PAGE_NONE` definitely won't have the user access bit set. Now, when a user maps a PCI resource with `PROT_NONE`, the PTEs are *usually* created with `PAGE_NONE`, or something very similar. Actually, wait a second. PCI resources are often mapped using `pgprot_noncached` or something like that, which mostly tweaks the memory type rather than setting or removing access bits.
Hold on. If the protection is `PROT_NONE`, are PTEs even *created* at all? Because if there's no access allowed, why bother? But what about `mmap` with `MAP_PRIVATE`? That changes things. Hmmm, `mmap` of a PCI resource specifically with `MAP_PRIVATE`... Now we are getting somewhere. Usually PCI resources are mapped as `MAP_SHARED`, which is the typical use case.
Looking at the reproducer: I see the `syscall(__NR_mmap, ...)` call with `MAP_FIXED | MAP_PRIVATE`. Specifically, it uses `MAP_PRIVATE` on a PCI resource. Wait, *`MAP_PRIVATE`* on a PCI resource? That's unusual. It seems like the critical area to check is `pci_mmap_resource_range`. Let's dig into that code to see what's really happening.
|
| 21/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:pci_mmap_resource_range PathPrefix:drivers/pci/pci-sysfs.c]
Results: map[Output:drivers/pci/pci-sysfs.c=1153=static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *attr,
--
drivers/pci/pci-sysfs.c-1177-
drivers/pci/pci-sysfs.c:1178: return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
drivers/pci/pci-sysfs.c-1179-}
]
|
| 22/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 30179
output: 38
thoughts: 15
Thoughts: Okay, let's dive into this. Here's my take on `pci_mmap_resource_range`:
**Examining `pci_mmap_resource_range`**
Alright, so we're looking at `pci_mmap_resource_range`. My initial thought is, this is likely a critical function for PCI device interaction within the kernel. Mapping resources... that screams "memory mapping" to me. I need to understand exactly what resources are being mapped and, more importantly, *how* this mapping is handled. Is this a physical to virtual address translation? What protections are applied? Are we talking about BARs (Base Address Registers)?
I expect this function to be used primarily to expose PCI device resources (like memory or I/O space) to userspace applications, or potentially to other kernel components. So, I need to consider the security implications right off the bat. Improperly handled mappings are a recipe for instability and security vulnerabilities. Are there access control mechanisms involved? How does it handle potential conflicts if multiple processes try to map the same region?
I need to quickly understand the arguments it takes β which will define the scope, protections, and the resource being mapped. I'm expecting some kind of `pci_dev` pointer, defining the device, and some range definition, likely base and size, to pinpoint the specific area of the device's resources. And how is it related to the platform/architecture? Is there arch-specific code involved? This may expose the underlying hardware's nuances. I need to be ready to dig through the platform specific code.
Ultimately, I need to get a clear picture of how this function contributes to the overall PCI subsystem's functionality, and identify potential areas of concern for performance, security, and stability.
|
| 23/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:pci_mmap_resource_range PathPrefix:drivers/pci/mmap.c]
Results: map[Output:drivers/pci/mmap.c=18=static const struct vm_operations_struct pci_phys_vm_ops = {
--
drivers/pci/mmap.c-23-
drivers/pci/mmap.c:24:int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
drivers/pci/mmap.c-25- struct vm_area_struct *vma,
]
|
| 24/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 30730
output: 37
thoughts: 15
Thoughts: Okay, let's dive into this `pci_mmap_resource_range` function. I need to understand this implementation because it's crucial for how we expose PCI resources to userspace, and even a minor oversight can lead to serious security implications or resource conflicts, and that's the last thing I want!
I'll start by tracing the calls, noting the arguments, and thinking about the intent of each component and its role. I need to focus on how the physical address is mapped to a virtual one, and what permissions are applied. I need to keep in mind the potential risks of overlapping regions or memory leaks, considering how the function handles different memory types like MMIO and BARs, and how this relates to my own work on the driver, what resources it uses, and so on.
I should also make sure to check for any edge cases it might be missing and consider how the code might interact with other memory management subsystems, like the device memory mapping and so on. I need to identify any potential race conditions and locking mechanisms utilized within this function to avoid any concurrent access issues. From there, I'll assess the potential impact if something is changed or incorrectly configured. I have to think through any security implications or compatibility problems as part of my analysis.
|
| 25/2 |
2026/08/31 00:56 |
tool |
read-file |
0m
Args: map[File:drivers/pci/mmap.c FirstLine:24 LineCount:100]
Results: map[Contents: 24: int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
25: struct vm_area_struct *vma,
26: enum pci_mmap_state mmap_state, int write_combine)
27: {
28: unsigned long size;
29: int ret;
30:
31: size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1;
32: if (vma->vm_pgoff + vma_pages(vma) > size)
33: return -EINVAL;
34:
35: if (write_combine)
36: vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
37: else
38: vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
39:
40: if (mmap_state == pci_mmap_io) {
41: ret = pci_iobar_pfn(pdev, bar, vma);
42: if (ret)
43: return ret;
44: } else
45: vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT);
46:
47: vma->vm_ops = &pci_phys_vm_ops;
48:
49: return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
50: vma->vm_end - vma->vm_start,
51: vma->vm_page_prot);
52: }
53:
54: #endif
55:
56: #if (defined(CONFIG_SYSFS) || defined(CONFIG_PROC_FS)) && \
57: (defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE))
58:
59: int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
60: enum pci_mmap_api mmap_api)
61: {
62: resource_size_t pci_start = 0, pci_end;
63: unsigned long nr, start, size;
64:
65: if (pci_resource_len(pdev, resno) == 0)
66: return 0;
67: nr = vma_pages(vma);
68: start = vma->vm_pgoff;
69: size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
70: if (mmap_api == PCI_MMAP_PROCFS) {
71: pci_resource_to_user(pdev, resno, &pdev->resource[resno],
72: &pci_start, &pci_end);
73: pci_start >>= PAGE_SHIFT;
74: }
75: if (start >= pci_start && start < pci_start + size &&
76: start + nr <= pci_start + size)
77: return 1;
78: return 0;
79: }
80:
81: #endif
]
|
| 26/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 32021
output: 40
thoughts: 1241
Thoughts: **Analyzing a Potential Kernel Memory Exposure**
Okay, let's break this down. The code snippet shows a potential vulnerability, or at least a warning, related to how PCI resources are mapped and accessed. Here's my thought process:
First, `pci_mmap_resource_range` utilizes `io_remap_pfn_range`, and the crux lies in the `vma->vm_page_prot` protection flags. If `prot` is 0 (PROT_NONE), then `PTE_USER` is not set in `vma->vm_page_prot`. This means the resulting Page Table Entries (PTEs) also lack `PTE_USER`.
The reproducer then does some odd things. It writes a string to address `0x20000040`, but then more interestingly it uses a combination of `fsconfig`, `syz_open_procfs`, and `/proc/self/mem`. Specifically, `syz_open_procfs` opens `/proc/self/mem`. This then reads from the address which was mapped to the PCI resource via `mmap`. The `mmap` was at address 0. I suspect this will be interesting later.
The key observation is how the subsequent `read` from `/proc/self/mem` gets translated. This triggers a chain of functions: `mem_read` -> `access_remote_vm` -> `__access_remote_vm` -> `generic_access_phys`. `generic_access_phys` is where things get interesting. It calls `follow_pfnmap_start`, which retrieves the PTE protection. Given the initial `PROT_NONE` from the `mmap`, the PTE doesn't have `PTE_USER`. That's where we get the warning `ioremap_prot` which, by the way, calls `user_prot_val & PTE_USER`.
The warning looks like:
```c
if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
return NULL;
```
This checks if `PTE_USER` is set. It is not. If `ioremap_prot` returns `NULL` it results in `generic_access_phys` returning `-ENOMEM`. The function is warning, but appears not to have a fatal outcome.
The question then becomes: can an unprivileged user trigger this? The reproducer mounts sysfs. However, `mkdir` and `mount` require `CAP_SYS_ADMIN` in the user namespace, which is more privileged than a typical user. However, `/sys/bus/pci/devices/0000:00:03.0/resource1` is being opened. Can an unprivileged user open a PCI resource file in sysfs? This is the crux. What are the permissions on these PCI resource files? If an unprivileged user can open them, then there is a potential information leak.
|
| 27/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Error:
Args: map[Expression:pci_create_resource_files PathPrefix:drivers/pci/pci-sysfs.c]
Results:
|
| 28/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 33953
output: 35
thoughts: 23
Thoughts: Here's a summary of my thought process, as requested:
**Digging into PCI Sysfs for Resource Management**
Okay, so I need to understand how resources are being handled within the PCI subsystem's sysfs interface. My first instinct is to drill down into the code itself. "resource" is a pretty generic term in this context, so I'm betting the core logic will be in the `drivers/pci/pci-sysfs.c` file. It's the logical place to start looking at how the kernel exposes PCI device information through sysfs. I expect to find functions related to reading, writing, and possibly manipulating resource-related attributes that are exposed to userspace. I'll be looking for things like memory region mappings, I/O ports, and interrupt vectors, and how their attributes are presented through sysfs. Let's see what we can find.
|
| 29/2 |
2026/08/31 00:56 |
tool |
grepper |
0m
Args: map[Expression:"resource" PathPrefix:drivers/pci/pci-sysfs.c]
Results: map[Output:drivers/pci/pci-sysfs.c=1297=static const struct bin_attribute pci_dev_resource##_bar##_io_attr = { \
drivers/pci/pci-sysfs.c:1298: .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
drivers/pci/pci-sysfs.c-1299- .private = (void *)(unsigned long)(_bar), \
--
drivers/pci/pci-sysfs.c=1306=static const struct bin_attribute pci_dev_resource##_bar##_uc_attr = { \
drivers/pci/pci-sysfs.c:1307: .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
drivers/pci/pci-sysfs.c-1308- .private = (void *)(unsigned long)(_bar), \
--
drivers/pci/pci-sysfs.c=1315=static const struct bin_attribute pci_dev_resource##_bar##_wc_attr = { \
drivers/pci/pci-sysfs.c:1316: .attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \
drivers/pci/pci-sysfs.c-1317- .private = (void *)(unsigned long)(_bar), \
]
|
| 30/2 |
2026/08/31 00:56 |
llm |
expert |
0m
Model:
Tokens:
input: 34587
output: 32
thoughts: 481
Thoughts: **Analyzing Privilege Escalation via `mmap` and `PROT_NONE`**
Okay, so I'm thinking about this file mode, `0600`. That means it's pretty much locked down to root. But wait, if a user creates their own user namespace, they essentially *are* root in that little isolated world, right? Hmm, but sysfs isn't fully mountable in a user namespace. Even if it *is* mounted, the files are owned by the "global" root (uid 0 in the `init_user_ns`), which ends up mapping to something like `nobody` or even unmapped within that user namespace.
So, can a user namespace root *actually* open a file owned by the global root? No, because even though they *appear* as uid 0 within their namespace, their "real" uid outside is something completely different, like 1000. So the kernel's permission checks are still using that "global" uid 0. That's a problem, because that global uid 0 doesn't have `CAP_DAC_OVERRIDE` in the `init_user_ns`, so an unprivileged user just can't open something like `/sys/bus/pci/devices/.../resource1`, even in a user namespace. So, I can confirm that that avenue is pretty much closed.
Alright, what else could work? Is there any other way to map a PFN map with `PROT_NONE`? It's a tricky question, that. Are there any device drivers that let an unprivileged user `mmap` something and then use `io_remap_pfn_range` or `remap_pfn_range`? Wait, yes! Many drivers allow unprivileged `mmap`! DRM (GPU) drivers, sound drivers, they all have a chance here.
Okay, let's say an unprivileged user opens `/dev/dri/renderD128`, mmaps it with `PROT_NONE`, and then tries to read it through `/proc/self/mem`. What happens then? I need to figure out if `PROT_NONE` is even allowed when mmaping DRM stuff. Generally, it's allowed.
Okay, the general process should be: when `mmap` is called with `PROT_NONE`, `vma->vm_page_prot` gets set to `PAGE_NONE`. Then the driver's `mmap` callback is invoked. The driver might call `io_remap_pfn_range` or `remap_pfn_range`. Now the real question: does `remap_pfn_range` actually allow `PAGE_NONE`? This could be a good vector of exploitation.
|
| 31/2 |
2026/08/31 00:57 |
tool |
grepper |
0m
Args: map[Expression:remap_pfn_range PathPrefix:mm/memory.c]
Results: map[Output:mm/memory.c=616=static inline bool pgtable_level_has_pxx_special(enum pgtable_level level)
--
mm/memory.c-668- * The way we recognize COWed pages within VM_PFNMAP mappings is through the
mm/memory.c:669: * rules set up by "remap_pfn_range()": the vma will have the VM_PFNMAP bit
mm/memory.c-670- * set, and the vm_pgoff will point to the first PFN mapped: thus every special
--
mm/memory.c=2618=EXPORT_SYMBOL(map_kernel_pages_complete);
--
mm/memory.c-2634- *
mm/memory.c:2635: * NOTE! Traditionally this was done with "remap_pfn_range()" which
mm/memory.c-2636- * took an arbitrary page protection parameter. This doesn't allow
--
mm/memory.c=2823=vm_fault_t vmf_insert_pfn_prot(struct vm_area_struct *vma, unsigned long addr,
--
mm/memory.c-2827- * Technically, architectures with pte_special can avoid all these
mm/memory.c:2828: * restrictions (same for remap_pfn_range). However we would like
mm/memory.c-2829- * consistency in testing and feature parity among all, so we should
--
mm/memory.c=3073=static int get_remap_pgoff(bool is_cow, unsigned long addr,
--
mm/memory.c-3091-
mm/memory.c:3092:static int remap_pfn_range_internal(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3093- unsigned long pfn, unsigned long size, pgprot_t prot)
--
mm/memory.c-3121-/*
mm/memory.c:3122: * Variant of remap_pfn_range that does not call track_pfn_remap. The caller
mm/memory.c-3123- * must have pre-validated the caching bits of the pgprot_t.
mm/memory.c-3124- */
mm/memory.c:3125:static int remap_pfn_range_notrack(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3126- unsigned long pfn, unsigned long size, pgprot_t prot)
mm/memory.c-3127-{
mm/memory.c:3128: int error = remap_pfn_range_internal(vma, addr, pfn, size, prot);
mm/memory.c-3129-
--
mm/memory.c=3163=void pfnmap_track_ctx_release(struct kref *ref)
--
mm/memory.c-3170-
mm/memory.c:3171:static int remap_pfn_range_track(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3172- unsigned long pfn, unsigned long size, pgprot_t prot)
--
mm/memory.c-3197-
mm/memory.c:3198: err = remap_pfn_range_notrack(vma, addr, pfn, size, prot);
mm/memory.c-3199- if (ctx) {
--
mm/memory.c-3207-
mm/memory.c:3208:static int do_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3209- unsigned long pfn, unsigned long size, pgprot_t prot)
mm/memory.c-3210-{
mm/memory.c:3211: return remap_pfn_range_track(vma, addr, pfn, size, prot);
mm/memory.c-3212-}
mm/memory.c-3213-#else
mm/memory.c:3214:static int do_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3215- unsigned long pfn, unsigned long size, pgprot_t prot)
mm/memory.c-3216-{
mm/memory.c:3217: return remap_pfn_range_notrack(vma, addr, pfn, size, prot);
mm/memory.c-3218-}
--
mm/memory.c-3220-
mm/memory.c:3221:int remap_pfn_range_prepare(struct vm_area_desc *desc)
mm/memory.c-3222-{
--
mm/memory.c-3241-
mm/memory.c:3242:static int remap_pfn_range_prepare_vma(struct vm_area_struct *vma,
mm/memory.c-3243- unsigned long addr, unsigned long pfn,
--
mm/memory.c-3259-/**
mm/memory.c:3260: * remap_pfn_range - remap kernel memory to userspace
mm/memory.c-3261- * @vma: user vma to map to
--
mm/memory.c-3270- */
mm/memory.c:3271:int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-3272- unsigned long pfn, unsigned long size, pgprot_t prot)
--
mm/memory.c-3275-
mm/memory.c:3276: err = remap_pfn_range_prepare_vma(vma, addr, pfn, size);
mm/memory.c-3277- if (err)
--
mm/memory.c-3279-
mm/memory.c:3280: return do_remap_pfn_range(vma, addr, pfn, size, prot);
mm/memory.c-3281-}
mm/memory.c:3282:EXPORT_SYMBOL(remap_pfn_range);
mm/memory.c-3283-
mm/memory.c:3284:int remap_pfn_range_complete(struct vm_area_struct *vma,
mm/memory.c-3285- struct mmap_action *action)
--
mm/memory.c-3291-
mm/memory.c:3292: return do_remap_pfn_range(vma, start, pfn, size, prot);
mm/memory.c-3293-}
--
mm/memory.c=3329=int simple_ioremap_prepare(struct vm_area_desc *desc)
--
mm/memory.c-3343- mmap_action_ioremap_full(desc, pfn);
mm/memory.c:3344: return io_remap_pfn_range_prepare(desc);
mm/memory.c-3345-}
--
mm/memory.c-3352- *
mm/memory.c:3353: * This is a simplified io_remap_pfn_range() for common driver use. The
mm/memory.c-3354- * driver just needs to give us the physical memory range to be mapped,
--
mm/memory.c=3362=int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len)
--
mm/memory.c-3375- /* Ok, let it rip */
mm/memory.c:3376: return io_remap_pfn_range(vma, vma->vm_start, pfn, vm_len, vma->vm_page_prot);
mm/memory.c-3377-}
]
|
| 32/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 37725
output: 25
thoughts: 244
Thoughts: **My Line of Reasoning with `remap_pfn_range` and Memory Protection**
Okay, so I'm thinking about `remap_pfn_range` and how it handles protection. It takes a `pgprot_t prot`, right? And I'm seeing a scenario where a driver calls `remap_pfn_range` directly, passing in `vma->vm_page_prot`. Now, if the user space code that called `mmap` used `PROT_NONE`, then `vma->vm_page_prot` is going to be `PAGE_NONE`. Consequently, `remap_pfn_range` will map the region with `PAGE_NONE`.
Here's the problem: if the user then attempts to access this memory via something like `/proc/self/mem`, it will trigger `generic_access_phys`. That leads to a chain: `generic_access_phys` calls `follow_pfnmap_start`, which calls `ioremap_prot`. And then bam, I see a `WARN_ON_ONCE` about `!(user_prot_val & PTE_USER)`. It's complaining because we're violating user space protection.
But wait, I need to make sure I'm following the right path here. Is `generic_access_phys` always used for all `VM_PFNMAP` VMAs? I don't think so...no, it's only used if the VMA's `vm_ops->access` is set to `generic_access_phys`. That's important.
So, the next step is to figure out who's actually setting `generic_access_phys`. I need to track down where that's happening to fully understand this issue.
|
| 33/2 |
2026/08/31 00:57 |
tool |
grepper |
0m
Args: map[Expression:generic_access_phys PathPrefix:]
Results: map[Output:drivers/cdx/cdx.c=662=static const struct vm_operations_struct cdx_phys_vm_ops = {
drivers/cdx/cdx.c-663-#ifdef CONFIG_HAVE_IOREMAP_PROT
drivers/cdx/cdx.c:664: .access = generic_access_phys,
drivers/cdx/cdx.c-665-#endif
--
drivers/char/mem.c=319=static const struct vm_operations_struct mmap_mem_ops = {
drivers/char/mem.c-320-#ifdef CONFIG_HAVE_IOREMAP_PROT
drivers/char/mem.c:321: .access = generic_access_phys
drivers/char/mem.c-322-#endif
--
drivers/fpga/dfl-afu-main.c=805=static const struct vm_operations_struct afu_vma_ops = {
drivers/fpga/dfl-afu-main.c-806-#ifdef CONFIG_HAVE_IOREMAP_PROT
drivers/fpga/dfl-afu-main.c:807: .access = generic_access_phys,
drivers/fpga/dfl-afu-main.c-808-#endif
--
drivers/pci/mmap.c=18=static const struct vm_operations_struct pci_phys_vm_ops = {
drivers/pci/mmap.c-19-#ifdef CONFIG_HAVE_IOREMAP_PROT
drivers/pci/mmap.c:20: .access = generic_access_phys,
drivers/pci/mmap.c-21-#endif
--
drivers/uio/uio.c=736=static const struct vm_operations_struct uio_physical_vm_ops = {
drivers/uio/uio.c-737-#ifdef CONFIG_HAVE_IOREMAP_PROT
drivers/uio/uio.c:738: .access = generic_access_phys,
drivers/uio/uio.c-739-#endif
--
include/linux/mm.h=806=struct vm_operations_struct {
--
include/linux/mm.h-858- /* called by access_process_vm when get_user_pages() fails, typically
include/linux/mm.h:859: * for use by special VMAs. See also generic_access_phys() for a generic
include/linux/mm.h-860- * implementation useful for any iomem mapping.
--
include/linux/mm.h=3175=copy_page_range(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma);
include/linux/mm.h:3176:int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
include/linux/mm.h-3177- void *buf, int len, int write);
--
mm/memory.c=7079=EXPORT_SYMBOL_GPL(follow_pfnmap_end);
--
mm/memory.c-7082-/**
mm/memory.c:7083: * generic_access_phys - generic implementation for iomem mmap access
mm/memory.c-7084- * @vma: the vma to access
--
mm/memory.c-7093- */
mm/memory.c:7094:int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
mm/memory.c-7095- void *buf, int len, int write)
--
mm/memory.c-7141-}
mm/memory.c:7142:EXPORT_SYMBOL_GPL(generic_access_phys);
mm/memory.c-7143-#endif
--
tools/testing/vma/include/dup.h=636=struct vm_operations_struct {
--
tools/testing/vma/include/dup.h-688- /* called by access_process_vm when get_user_pages() fails, typically
tools/testing/vma/include/dup.h:689: * for use by special VMAs. See also generic_access_phys() for a generic
tools/testing/vma/include/dup.h-690- * implementation useful for any iomem mapping.
]
|
| 34/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 39468
output: 29
thoughts: 570
Thoughts: **Analyzing the Security Implications of `generic_access_phys`**
Okay, so I'm looking at this `generic_access_phys` thing, trying to figure out if there's any real security risk here. Seems like only a handful of drivers actually use it: `cdx.c`, `/dev/mem`, FPGA stuff, PCI mmap, and UIO devices. Right away, my expert instinct kicks in: almost all of these are locked down tight, usually requiring root or specific privileges.
`/dev/mem` needs `CAP_SYS_RAWIO`, and even that's limited by `CONFIG_STRICT_DEVMEM`. PCI mmap, via sysfs/procfs, is root-only because the files are `0600`. UIO devices are usually the same, although udev rules could *maybe* mess with this. CDX and FPGA devices seem similarly restricted.
So, realistically, an unprivileged user can't even get near these things, let alone trigger some kind of vulnerability that would throw a warning. But wait, what about user namespaces? That's where things get interesting. Can a user within a user namespace map a PCI device? I'm thinking back to my understanding of how sysfs works. Those sysfs files are owned by global root, right? And user namespace root has a *different* global UID. Let's say it's 1000, not 0. So, they *shouldn't* be able to open those files.
Okay, let's confirm this. Let's say a user creates a user namespace and mounts sysfs β do they have access to the PCI devices then? And the answer is, no, because sysfs isn't *fully* mountable in a user namespace. Hang on... isn't there something about how `sysfs` can be mounted in a user namespace if the user also has a network namespace? Right, they can create that too.
But when you mount `sysfs` inside a user namespace, the kernel either only shows a subset of files (usually network-related) or it shows *all* the files, but they're owned by global root, which maps to `nobody` (UID 65534) within the user namespace. And since those files are `0600` owned by `nobody`, even the user namespace root (UID 0) *can't* access them!
Wait, but root in a user namespace has `CAP_DAC_OVERRIDE` *within* that namespace. But that only applies to files where the owner *is* mapped in the user namespace, right? If the file's owned by global root, and global root isn't mapped (which is the case for unprivileged user namespaces), the UID is actually `INVALID_UID`. The kernel explicitly denies `CAP_DAC_OVERRIDE` for files owned by `INVALID_UID`. I need to double-check that though...just to be sure.
|
| 35/2 |
2026/08/31 00:57 |
tool |
grepper |
0m
Args: map[Expression:generic_permission PathPrefix:fs/namei.c]
Results: map[Output:fs/namei.c=438=static int acl_permission_check(struct mnt_idmap *idmap,
--
fs/namei.c-499-/**
fs/namei.c:500: * generic_permission - check for access rights on a Posix-like filesystem
fs/namei.c-501- * @idmap: idmap of the mount the inode was found from
--
fs/namei.c-510- *
fs/namei.c:511: * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
fs/namei.c-512- * request cannot be satisfied (eg. requires blocking or too much complexity).
--
fs/namei.c-520- */
fs/namei.c:521:int generic_permission(struct mnt_idmap *idmap, struct inode *inode,
fs/namei.c-522- int mask)
--
fs/namei.c-564-}
fs/namei.c:565:EXPORT_SYMBOL(generic_permission);
fs/namei.c-566-
--
fs/namei.c-572- *
fs/namei.c:573: * We _really_ want to just do "generic_permission()" without
fs/namei.c-574- * even looking at the inode->i_op values. So we keep a cache
--
fs/namei.c=578=static inline int do_inode_permission(struct mnt_idmap *idmap,
--
fs/namei.c-589- }
fs/namei.c:590: return generic_permission(idmap, inode, mask);
fs/namei.c-591-}
]
|
| 36/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 41162
output: 34
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in the weeds of this subject:
**Analyzing the `generic_permission` Code**
Alright, time to dig into `generic_permission`. This likely means I'm reviewing or debugging some code related to access control, likely involving some sort of abstract or generalized permission system. I'm expecting to find a foundational piece of logic here, something that defines the basic building blocks for determining user authorization. It could be an enum, a class, an interface, or even a set of functions dealing with the core permissions schema. I'm already anticipating that this `generic_permission` structure will be a key component in a larger system, so I'll be looking for potential performance bottlenecks, areas that might be difficult to extend, or any obvious security flaws in its design. Let's get to it.
|
| 37/2 |
2026/08/31 00:57 |
tool |
read-file |
0m
Args: map[File:fs/namei.c FirstLine:521 LineCount:50]
Results: map[Contents: 521: int generic_permission(struct mnt_idmap *idmap, struct inode *inode,
522: int mask)
523: {
524: int ret;
525:
526: /*
527: * Do the basic permission checks.
528: */
529: ret = acl_permission_check(idmap, inode, mask);
530: if (ret != -EACCES)
531: return ret;
532:
533: if (S_ISDIR(inode->i_mode)) {
534: /* DACs are overridable for directories */
535: if (!(mask & MAY_WRITE))
536: if (capable_wrt_inode_uidgid(idmap, inode,
537: CAP_DAC_READ_SEARCH))
538: return 0;
539: if (capable_wrt_inode_uidgid(idmap, inode,
540: CAP_DAC_OVERRIDE))
541: return 0;
542: return -EACCES;
543: }
544:
545: /*
546: * Searching includes executable on directories, else just read.
547: */
548: mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
549: if (mask == MAY_READ)
550: if (capable_wrt_inode_uidgid(idmap, inode,
551: CAP_DAC_READ_SEARCH))
552: return 0;
553: /*
554: * Read/write DACs are always overridable.
555: * Executable DACs are overridable when there is
556: * at least one exec bit set.
557: */
558: if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
559: if (capable_wrt_inode_uidgid(idmap, inode,
560: CAP_DAC_OVERRIDE))
561: return 0;
562:
563: return -EACCES;
564: }
565: EXPORT_SYMBOL(generic_permission);
566:
567: /**
568: * do_inode_permission - UNIX permission checking
569: * @idmap: idmap of the mount the inode was found from
570: * @inode: inode to check permissions on
]
|
| 38/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 42118
output: 34
thoughts: 24
Thoughts: Here's my summary, as though I were thinking through the process:
**Validating Capabilities Against Inode Ownership**
Okay, so I'm looking at `capable_wrt_inode_uidgid`. What I'm doing, essentially, is verifying a particular capability, right? But the context is crucial: I'm making sure this capability holds true *in relation to the inode's owner*. It's a fundamental security check. I'm not just looking at a generic capability; I'm grounding it in the file system and the ownership of the file itself. This is about granular access control, ensuring that only those with the proper permissions, as defined by the user and group IDs associated with the inode, can actually leverage this specific capability. It's a critical layer of protection in maintaining file system integrity. It's about combining capabilities with traditional UNIX file permissions.
|
| 39/2 |
2026/08/31 00:57 |
tool |
grepper |
0m
Args: map[Expression:capable_wrt_inode_uidgid PathPrefix:kernel/capability.c]
Results: map[Output:kernel/capability.c=455=bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
--
kernel/capability.c-463-/**
kernel/capability.c:464: * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
kernel/capability.c-465- * @idmap: idmap of the mount @inode was found from
--
kernel/capability.c-472- */
kernel/capability.c:473:bool capable_wrt_inode_uidgid(struct mnt_idmap *idmap,
kernel/capability.c-474- const struct inode *inode, int cap)
--
kernel/capability.c-480-}
kernel/capability.c:481:EXPORT_SYMBOL(capable_wrt_inode_uidgid);
kernel/capability.c-482-
]
|
| 40/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 42591
output: 33
thoughts: 15
Thoughts: Okay, here's my interpretation of what's going on, framed as a first-person expert perspective:
**Analyzing `capable_wrt_inode_uidgid`**
Alright, let's dive into this `capable_wrt_inode_uidgid` function. From the name alone, I can infer we're talking about a capability check related to user and group IDs, likely within the context of an inode. Given my experience, I'd immediately start thinking about how this ties into file system permissions, access control lists (ACLs), and the broader security model of the operating system. Is this a core kernel function or a helper? What level of abstraction does it operate on? Is it checking direct UIDs/GIDs or perhaps effective UIDs/GIDs? The "capable" part suggests a check for a privileged operation or an override of standard permissions. I'll need to understand how it interacts with the broader context of the file system and which other related checks are performed before or after this one. I'm especially keen to see if this function handles any nuances related to extended attributes, setuid/setgid bits, or capabilities. And, most importantly, I'll need to see the exact conditions it evaluates.
|
| 41/2 |
2026/08/31 00:57 |
tool |
read-file |
0m
Args: map[File:kernel/capability.c FirstLine:473 LineCount:10]
Results: map[Contents: 473: bool capable_wrt_inode_uidgid(struct mnt_idmap *idmap,
474: const struct inode *inode, int cap)
475: {
476: struct user_namespace *ns = current_user_ns();
477:
478: return ns_capable(ns, cap) &&
479: privileged_wrt_inode_uidgid(ns, idmap, inode);
480: }
481: EXPORT_SYMBOL(capable_wrt_inode_uidgid);
482:
]
|
| 42/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 43069
output: 33
thoughts: 12
Thoughts: Okay, here's my summary, taking on the persona you requested:
**My Thoughts on `privileged_wrt_inode_uidgid`**
Alright, let's break this down. When I see `privileged_wrt_inode_uidgid`, my mind immediately goes to the heart of file system security. This is all about how we handle privilege and how it relates to the user and group IDs associated with an inode. It's a fundamental aspect of access control, and frankly, a place where things can get very tricky, very quickly.
I'm thinking about the scenarios where we need to override the normal access checks, the cases where a "privileged" operation is required, and what the implications are. Am I dealing with a setuid/setgid file? Am I trying to implement capabilities? Am I concerned about root escalation vectors? Are we talking about user namespaces and how those affect the apparent UID/GID? I have to consider the potential security risks and tradeoffs.
My analysis also extends to considering the kernel implementation details. What specific file system operations are affected by this? What are the implications for things like file creation, modification, deletion, and permission changes? How robust are the protections against things like symlink attacks or other kinds of race conditions? What are the implications for things like NFS or other network filesystems? Do I need to consider things like ACLs (access control lists)?
The phrase itself suggests a degree of specificity. It implies that thereβs a defined concept of privilege related *specifically* to these user/group IDs that are stored on an inode. I'm probably considering whether this is a hard-coded privilege, a configurable one, or a dynamic privilege determined by the context of the operation. I have to think about how this interacts with other security mechanisms and how it all hangs together.
Ultimately, I need to know the why and the how. Why are we privileged with respect to the inode's UID/GID? What is the *exact* privilege being granted, and how can I confirm that the implementation is sound and that itβs not opening any new vulnerabilities? I'm already mentally running through potential fuzzing strategies and code review checklists. Itβs all about the details and the edge cases.
|
| 43/2 |
2026/08/31 00:57 |
tool |
read-file |
0m
Args: map[File:kernel/capability.c FirstLine:455 LineCount:10]
Results: map[Contents: 455: bool privileged_wrt_inode_uidgid(struct user_namespace *ns,
456: struct mnt_idmap *idmap,
457: const struct inode *inode)
458: {
459: return vfsuid_has_mapping(ns, i_uid_into_vfsuid(idmap, inode)) &&
460: vfsgid_has_mapping(ns, i_gid_into_vfsgid(idmap, inode));
461: }
462:
463: /**
464: * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
]
|
| 44/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 43774
output: 34
thoughts: 624
Thoughts: **My Analysis of Unprivileged Access and Memory Mapping**
Okay, so I've been digging into how unprivileged users can potentially bypass security measures to access physical memory, and I think I've got a handle on it. Let's break it down.
First, I confirmed that `vfsuid_has_mapping` is the key. If an inode's owner isn't mapped in the user namespace, like with global root (UID 0) and a sysfs file, then it returns false. Consequently, `capable_wrt_inode_uidgid` fails, and `CAP_DAC_OVERRIDE` is denied. This blocks an unprivileged user from directly opening and reading, for example, a PCI resource file, even inside a user namespace. So far, so good.
But that made me think about ways around this. Could I, for instance, mount a FUSE filesystem, create a file, mmap it with `PROT_NONE`, and then try to read it via `/proc/self/mem`? If it's a FUSE filesystem, the VMA's `vm_ops` will point to FUSE's `vm_ops`. FUSE's `vm_ops` *doesn't* include `generic_access_phys`. And because the `vma->vm_ops->access` is NULL, `access_process_vm` ends up using `get_user_pages_remote`. That calls `faultin_page`, and then reads the page. Crucially, it won't trigger `generic_access_phys`. So, that seems safe.
However, that got me thinking about other drivers. What if I use a driver that *does* allow unprivileged access and *uses* `generic_access_phys`? Are there any of those lurking around? I went back through the list:
* `drivers/cdx/cdx.c`: Usually restricted.
* `drivers/char/mem.c`: `/dev/mem`. Also restricted.
* `drivers/fpga/dfl-afu-main.c`: FPGA, usually restricted.
* `drivers/pci/mmap.c`: PCI sysfs/procfs -- restricted.
* `drivers/uio/uio.c`: UIO, restricted.
So, that looks promising, but what about `remap_pfn_range`? If a driver uses that, does it automatically use `generic_access_phys`? No. `remap_pfn_range` sets `VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP`, but it *doesn't* set `vma->vm_ops->access` to `generic_access_phys`. Unless the driver explicitly defines `vma->vm_ops` to something that *does* use `generic_access_phys`. If the driver doesn't, the `access_process_vm` will see `vma->vm_ops->access == NULL`. So, then what happens for `VM_PFNMAP`? I think I need to look into `__access_remote_vm` next to be sure.
|
| 45/2 |
2026/08/31 00:57 |
tool |
read-file |
0m
Args: map[File:mm/memory.c FirstLine:7165 LineCount:50]
Results: map[Contents:7165: while (len) {
7166: int bytes, offset;
7167: void *maddr;
7168: struct folio *folio;
7169: struct vm_area_struct *vma = NULL;
7170: struct page *page = get_user_page_vma_remote(mm, addr,
7171: gup_flags, &vma);
7172:
7173: if (IS_ERR(page)) {
7174: /* We might need to expand the stack to access it */
7175: vma = vma_lookup(mm, addr);
7176: if (!vma) {
7177: vma = expand_stack(mm, addr);
7178:
7179: /* mmap_lock was dropped on failure */
7180: if (!vma)
7181: return buf - old_buf;
7182:
7183: /* Try again if stack expansion worked */
7184: continue;
7185: }
7186:
7187: /*
7188: * Check if this is a VM_IO | VM_PFNMAP VMA, which
7189: * we can access using slightly different code.
7190: */
7191: bytes = 0;
7192: #ifdef CONFIG_HAVE_IOREMAP_PROT
7193: if (vma->vm_ops && vma->vm_ops->access)
7194: bytes = vma->vm_ops->access(vma, addr, buf,
7195: len, write);
7196: #endif
7197: if (bytes <= 0)
7198: break;
7199: } else {
7200: folio = page_folio(page);
7201: bytes = len;
7202: offset = addr & (PAGE_SIZE-1);
7203: if (bytes > PAGE_SIZE-offset)
7204: bytes = PAGE_SIZE-offset;
7205:
7206: maddr = kmap_local_folio(folio, folio_page_idx(folio, page) * PAGE_SIZE);
7207: if (write) {
7208: copy_to_user_page(vma, page, addr,
7209: maddr + offset, buf, bytes);
7210: folio_mark_dirty_lock(folio);
7211: } else {
7212: copy_from_user_page(vma, page, addr,
7213: buf, maddr + offset, bytes);
7214: }
]
|
| 46/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 45867
output: 35
thoughts: 253
Thoughts: **My Analysis of the `__access_remote_vm` Function**
Right, okay, so `__access_remote_vm` is the key function here. It immediately checks if a `vma` has valid `vm_ops` and, crucially, an `.access` function defined. If that `.access` is missing, the whole thing just bails out, returns right away. Interesting.
So, the takeaway is, if a device driver *doesn't* provide an `.access` method, you simply *cannot* interact with it via `/proc/self/mem`. That seems pretty airtight security. I can see the logic there.
Now, only those five drivers that use `generic_access_phys` would let a user read or write through the `/proc/self/mem` interface? And all those drivers are root-restricted? Excellent. So, an unprivileged user can't trigger the warning. That makes sense.
Wait a second⦠what about `kernfs_vma_access`? That's what the stack trace is showing! Let's see... `generic_access_phys` is being called from `kernfs_vma_access` (according to the stack trace), and *then* the `__access_remote_vm` that we've been discussing? Right, right. Let's dig into `kernfs_vma_access` and see how it fits into all this. The puzzle deepens, time to look at the code.
|
| 47/2 |
2026/08/31 00:57 |
tool |
grepper |
0m
Args: map[Expression:kernfs_vma_access PathPrefix:fs/kernfs/file.c]
Results: map[Output:fs/kernfs/file.c=400=static vm_fault_t kernfs_vma_page_mkwrite(struct vm_fault *vmf)
--
fs/kernfs/file.c-421-
fs/kernfs/file.c:422:static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr,
fs/kernfs/file.c-423- void *buf, int len, int write)
--
fs/kernfs/file.c=443=static const struct vm_operations_struct kernfs_vm_ops = {
--
fs/kernfs/file.c-446- .page_mkwrite = kernfs_vma_page_mkwrite,
fs/kernfs/file.c:447: .access = kernfs_vma_access,
fs/kernfs/file.c-448-};
]
|
| 48/2 |
2026/08/31 00:57 |
llm |
expert |
0m
Model:
Tokens:
input: 46712
output: 36
thoughts: 13
Thoughts: Okay, let's dive into this `kernfs_vma_access` function. I need to understand its role within the kernel filesystem framework. My initial thought is to figure out what it's supposed to *do*. Is it involved in memory access control, permission checks, or some kind of virtual memory area (VMA) management specific to kernfs? Knowing the "what" is the essential first step before I try to decipher the "how".
I need to see how it fits within the broader picture of kernfs operations. Is this called during a read or write operation, or something else entirely? I should look for the call sites. The surrounding code will provide important context - are there locks involved? Error handling? How does it interact with other kernfs data structures, like kernfs nodes, files, or dentries? Those things are key.
Then I want to scrutinize the arguments passed to the function. What does `vma` represent? Is this the user space virtual memory area being accessed? What are the other arguments? What information are they providing about the access? I'll pay close attention to the return value, is it a success/failure indicator or an error code?
Ultimately, my goal is to grasp the function's overall purpose and its contribution to the overall integrity and security of the kernfs subsystem. Knowing that will give me a better understanding of the code.
|
| 49/2 |
2026/08/31 00:58 |
tool |
read-file |
0m
Args: map[File:fs/kernfs/file.c FirstLine:422 LineCount:30]
Results: map[Contents: 422: static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr,
423: void *buf, int len, int write)
424: {
425: struct file *file = vma->vm_file;
426: struct kernfs_open_file *of = kernfs_of(file);
427: int ret;
428:
429: if (!of->vm_ops)
430: return -EINVAL;
431:
432: if (!kernfs_get_active_of(of))
433: return -EINVAL;
434:
435: ret = -EINVAL;
436: if (of->vm_ops->access)
437: ret = of->vm_ops->access(vma, addr, buf, len, write);
438:
439: kernfs_put_active_of(of);
440: return ret;
441: }
442:
443: static const struct vm_operations_struct kernfs_vm_ops = {
444: .open = kernfs_vma_open,
445: .fault = kernfs_vma_fault,
446: .page_mkwrite = kernfs_vma_page_mkwrite,
447: .access = kernfs_vma_access,
448: };
449:
450: static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
451: {
]
|
| 50/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 47523
output: 36
thoughts: 1112
Thoughts: **Analyzing Potential Vulnerabilities in Sysfs mmap with `generic_access_phys`**
Okay, so I'm digging into this `kernfs_vma_access` thing, which ultimately calls `of->vm_ops->access`. The key is that the `vm_ops` comes from the sysfs file itself. I've got my eye on PCI resources here, since `pci_mmap_resource_range` sets `vma->vm_ops = &pci_phys_vm_ops`. *But* `kernfs` intercepts `vma->vm_ops` during the mmap operation, and makes the original `of->vm_ops` the *actual* `vm_ops` later. So it ends up calling `pci_phys_vm_ops.access`, which is `generic_access_phys`.
The pattern I see here is that any sysfs file enabling `mmap` *and* assigning a `.access` function of `generic_access_phys` to the `vm_ops` will trigger this. We know PCI resources are one example, specifically because `pci_phys_vm_ops` *does* have that function. But PCI resources are `0600` owned by root, so the first question is, are there any *other* sysfs files that are accessible to unprivileged users, that set `.access = generic_access_phys`?
I already grepped the kernel source for all uses of `generic_access_phys`. The hits I got were: `cdx.c`, `mem.c`, `dfl-afu-main.c`, `mmap.c` (PCI again), and `uio.c`. Let's evaluate the accessibility of those files:
* `cdx.c`: CDX devices. Usually root only.
* `mem.c`: `/dev/mem`. Root only.
* `dfl-afu-main.c`: FPGA AFU. Usually root only.
* `mmap.c`: PCI resources. Root only.
* `uio.c`: UIO devices. Usually root only (though udev might change this, but by default it's root).
So, on the face of it, it looks like everything's protected and not accessible from unprivileged users. *However*, the user namespace situation introduces a twist! What happens when a user mounts sysfs within a user namespace? If they do this, what would the file ownership be?
I'm thinking about this in terms of the reproducer, which mounts sysfs, then opens the PCI resource. But wait a sec! That reproducer is run by syzkaller. Syzkaller usually runs as root in a user namespace. But, sometimes it runs as global root if the sandbox is `none`. If the sandbox is `none`, then it *does* run as global root. That's a key distinction! If the sandbox is none, then it *could* access these resources.
So, to summarize: is this accessible from unprivileged processes, and accessible from user namespaces? The answer hinges on that sandbox. If the user can't mount the sysfs, then it's not. But if they are global root, or if they are in a namespace, it could be. And that's what I have to check next: Can a user namespace root *actually* open PCI resources if they mount sysfs? We need to verify file ownership in that scenario.
|
| 51/2 |
2026/08/31 00:58 |
tool |
grepper |
0m
Args: map[Expression:sysfs_init_fs_context PathPrefix:fs/sysfs/mount.c]
Results: map[Output:fs/sysfs/mount.c=36=static const struct fs_context_operations sysfs_fs_context_ops = {
--
fs/sysfs/mount.c-40-
fs/sysfs/mount.c:41:static int sysfs_init_fs_context(struct fs_context *fc)
fs/sysfs/mount.c-42-{
--
fs/sysfs/mount.c=78=static struct file_system_type sysfs_fs_type = {
fs/sysfs/mount.c-79- .name = "sysfs",
fs/sysfs/mount.c:80: .init_fs_context = sysfs_init_fs_context,
fs/sysfs/mount.c-81- .kill_sb = sysfs_kill_sb,
]
|
| 52/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 49628
output: 35
thoughts: 14
Thoughts: Okay, let's dive into this `sysfs_init_fs_context` function. I'm already visualizing the kernel data structures related to sysfs β the `fs_context`, the `file_system_type`, and the likely interaction with the virtual filesystem (VFS) layer. I suspect this function is responsible for the crucial initial setup of a filesystem context specifically for sysfs, which will be the groundwork for all operations that will be done on it. Iβm thinking this is where the core parameters get defined. This is where the magic really begins for any sort of new filesystems.
I anticipate the function will do the following:
* **Allocate and initialize an `fs_context` structure:** This seems obvious; we need a container to hold all the sysfs-specific details. What specific flags or callbacks does it initialize?
* **Populate the `fs_context` with relevant information:** This is likely where the `file_system_type` pointer comes into play. I need to look for how this context is used later on, the interaction with other fs structures. What about default mount options or security settings?
* **Handle any potential errors:** Of course, there could be failures in memory allocation or initialization. What error codes does it return? I expect that it's going to be pretty robust, I mean, sysfs is really important.
* **Potentially register the `fs_context` with the kernel:** Depending on the design, the initialization might involve making the context available for the VFS to use. Is there any registration or association here?
I need to quickly see what's being done inside this function, as it's the foundation for any kind of operations on sysfs.
|
| 53/2 |
2026/08/31 00:58 |
tool |
read-file |
0m
Args: map[File:fs/sysfs/mount.c FirstLine:41 LineCount:20]
Results: map[Contents: 41: static int sysfs_init_fs_context(struct fs_context *fc)
42: {
43: struct kernfs_fs_context *kfc;
44: struct ns_common *ns;
45:
46: if (!(fc->sb_flags & SB_KERNMOUNT)) {
47: if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET))
48: return -EPERM;
49: }
50:
51: kfc = kzalloc_obj(struct kernfs_fs_context);
52: if (!kfc)
53: return -ENOMEM;
54:
55: kfc->ns_tag = ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
56: kfc->root = sysfs_root;
57: kfc->magic = SYSFS_MAGIC;
58: fc->fs_private = kfc;
59: fc->ops = &sysfs_fs_context_ops;
60: if (ns) {
]
|
| 54/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 50358
output: 34
thoughts: 133
Thoughts: **My Analysis of Sysfs Mounting and File Ownership**
Okay, so the system allows sysfs to be mounted when `kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)` evaluates to true. That's a key point. Essentially, if a user has a network namespace set up, they're granted the ability to mount sysfs. That's good security design, generally.
Now, the interesting bit: when sysfs is mounted within a user namespace, it's `kernfs` that handles the file creation. This gets me thinking β how is file ownership handled in this scenario? I know that `kernfs` nodes have the standard `i_uid` and `i_gid` fields, which is the mechanism.
For sysfs, specifically, the code tends to initialize most nodes with `GLOBAL_ROOT_UID` and `GLOBAL_ROOT_GID`, at least from my general knowledge. To be absolutely certain, I should probably double-check the `sysfs_create_file` function. I should confirm my assumptions on ownership and creation.
|
| 55/2 |
2026/08/31 00:58 |
tool |
grepper |
0m
Args: map[Expression:sysfs_create_file PathPrefix:fs/sysfs/file.c]
Results: map[Output:fs/sysfs/file.c=326=int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent,
--
fs/sysfs/file.c-361-/**
fs/sysfs/file.c:362: * sysfs_create_file_ns - create an attribute file for an object with custom ns
fs/sysfs/file.c-363- * @kobj: object we're creating for
--
fs/sysfs/file.c-366- */
fs/sysfs/file.c:367:int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
fs/sysfs/file.c-368- const struct ns_common *ns)
--
fs/sysfs/file.c-378-}
fs/sysfs/file.c:379:EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
fs/sysfs/file.c-380-
fs/sysfs/file.c:381:int sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr)
fs/sysfs/file.c-382-{
--
fs/sysfs/file.c-386- for (i = 0; ptr[i] && !err; i++)
fs/sysfs/file.c:387: err = sysfs_create_file(kobj, ptr[i]);
fs/sysfs/file.c-388- if (err)
--
fs/sysfs/file.c-392-}
fs/sysfs/file.c:393:EXPORT_SYMBOL_GPL(sysfs_create_files);
fs/sysfs/file.c-394-
]
|
| 56/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 51210
output: 38
thoughts: 16
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `sysfs_add_file_mode_ns`**
Okay, time to dig into `sysfs_add_file_mode_ns`. I need to refresh my memory on what this function actually *does* in the grand scheme of the sysfs architecture. My initial instinct is that it's probably related to setting file permissions and namespace management within sysfs, but I should verify that. I'll need to remember the typical sysfs structure and how it relates to the kernel's virtual filesystem implementation.
I need to understand what namespaces are being handled. Is it network namespaces? Mount namespaces? User namespaces? And how are the file permissions being handled in relation to those namespaces? It's crucial to understand the interactions. I'll want to trace its usage in various contexts, possibly looking at how device drivers or other kernel subsystems might be using it. I'll focus on how the mode argument interacts with the namespace-specific attributes. Then, I need to check the potential implications for security, especially if this function is used for sensitive files or if it interacts with user-space in a way that might introduce vulnerabilities. Ultimately, I'm trying to see if this function is behaving correctly and if there's any room for optimizations or security enhancements.
|
| 57/2 |
2026/08/31 00:58 |
tool |
grepper |
0m
Args: map[Expression:sysfs_add_file_mode_ns PathPrefix:fs/sysfs/file.c]
Results: map[Output:fs/sysfs/file.c=268=static const struct kernfs_ops sysfs_bin_kfops_mmap = {
--
fs/sysfs/file.c-275-
fs/sysfs/file.c:276:int sysfs_add_file_mode_ns(struct kernfs_node *parent,
fs/sysfs/file.c-277- const struct attribute *attr, umode_t mode, kuid_t uid,
--
fs/sysfs/file.c=367=int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
--
fs/sysfs/file.c-376- kobject_get_ownership(kobj, &uid, &gid);
fs/sysfs/file.c:377: return sysfs_add_file_mode_ns(kobj->sd, attr, attr->mode, uid, gid, ns);
fs/sysfs/file.c-378-}
--
fs/sysfs/file.c=401=int sysfs_add_file_to_group(struct kobject *kobj,
--
fs/sysfs/file.c-419- kobject_get_ownership(kobj, &uid, &gid);
fs/sysfs/file.c:420: error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid,
fs/sysfs/file.c-421- NULL);
]
|
| 58/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 51926
output: 32
thoughts: 16
Thoughts: Okay, here's my summary, in the first person, as if these were my thoughts, and tailored for a field expert:
**Understanding Kobject Ownership**
Alright, so I'm thinking about `kobject_get_ownership`. It's pretty straightforward, really. It boils down to this: when I call this function, I'm essentially *claiming* the ownership of a particular `kobject`. It's a fundamental operation within the kobject hierarchy. I need to make sure I grasp the ramifications of that claim, the implications of reference counting, and the eventual responsibility I now bear to release this object when it is no longer required, to prevent resource leaks and general system instability. It also gives me the right to ensure that the kobject stays valid and present in the system, even if other code might be trying to release it. This helps to prevent race conditions and ensures the safety of the object. So, in effect, `kobject_get_ownership` is my signal to the kernel that I intend to interact with this `kobject` and am accountable for its lifecycle.
|
| 59/2 |
2026/08/31 00:58 |
tool |
grepper |
0m
Args: map[Expression:kobject_get_ownership PathPrefix:lib/kobject.c]
Results: map[Output:lib/kobject.c=30=const struct ns_common *kobject_namespace(const struct kobject *kobj)
--
lib/kobject.c-40-/**
lib/kobject.c:41: * kobject_get_ownership() - Get sysfs ownership data for @kobj.
lib/kobject.c-42- * @kobj: kobject in question
--
lib/kobject.c-49- */
lib/kobject.c:50:void kobject_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid)
lib/kobject.c-51-{
--
lib/kobject.c=935=static void kset_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid)
--
lib/kobject.c-937- if (kobj->parent)
lib/kobject.c:938: kobject_get_ownership(kobj->parent, uid, gid);
lib/kobject.c-939-}
]
|
| 60/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 52480
output: 33
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `kobject_get_ownership`**
Alright, let's pull up the source code for `kobject_get_ownership`. I know this routine is fundamental to how the kernel manages object lifecycle and reference counting, particularly within the kobject hierarchy. My primary concern is understanding how it interacts with the underlying reference count and the ownership model. Specifically, I want to see:
* **Reference Acquisition:** How does this function actually *get* a reference to a kobject? Is it a simple atomic increment, or are there more intricate checks involved? I'm anticipating some sort of `atomic_inc_not_zero` or similar construct. I need to understand the potential race conditions and how they're handled. What is the expected memory model in play here?
* **Ownership Semantics:** What constitutes "ownership" in this context? Is it simply a matter of acquiring a reference, or does this function imply a deeper relationship, like preventing the underlying kobject from being garbage collected. I'm thinking about the implications for driver modules and their interaction with the kernel object tree.
* **Error Conditions:** What are the failure modes of this function? When will it return `NULL` or some other error indicator? I need to analyze any related comments or documentation explaining these cases and how they should be handled by calling code. Are there any potential deadlocks I need to keep in mind?
* **Relationship to `kobject_put`:** How does `kobject_get_ownership` relate to the corresponding `kobject_put` call? What guarantees does it provide about the validity of the object?
* **Context:** What architectural context is important here? Are we dealing with preemption, interrupts, or something else that impacts the locking strategy? Any use of memory barriers, perhaps?
Ultimately, I expect to understand the guarantees and assumptions this function makes regarding kobject lifecycle, reference counting, and ownership to ensure that my own code interacts with it correctly and safely. I need to be sure that I am understanding and using it correctly.
|
| 61/2 |
2026/08/31 00:58 |
tool |
read-file |
0m
Args: map[File:lib/kobject.c FirstLine:50 LineCount:20]
Results: map[Contents: 50: void kobject_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid)
51: {
52: *uid = GLOBAL_ROOT_UID;
53: *gid = GLOBAL_ROOT_GID;
54:
55: if (kobj->ktype->get_ownership)
56: kobj->ktype->get_ownership(kobj, uid, gid);
57: }
58:
59: static bool kobj_ns_type_is_valid(enum kobj_ns_type type)
60: {
61: if ((type <= KOBJ_NS_TYPE_NONE) || (type >= KOBJ_NS_TYPES))
62: return false;
63:
64: return true;
65: }
66:
67: static int create_dir(struct kobject *kobj)
68: {
69: const struct kobj_type *ktype = get_ktype(kobj);
]
|
| 62/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 53272
output: 28
thoughts: 48
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Ownership Defaults and PCI Device Queries**
Right, so the system defaults to using `GLOBAL_ROOT_UID` and `GLOBAL_ROOT_GID` for ownership. That's the baseline, the expected behaviour. My immediate question then becomes: are there overrides or more granular control mechanisms within the PCI bus or device structures? Specifically, does the `pci_bus_type` structure, or, even more granularly, the `pci_dev_type` structure, expose a `get_ownership` method or function, or something similar? I need to understand if the default behaviour can be overridden on a per-bus or per-device basis. I'm looking for a way to interrogate a specific PCI device to determine if it has custom ownership rules defined or if it's falling back to the global defaults. Ultimately, I need to figure out where ownership is *actually* derived from in the PCI device model.
|
| 63/2 |
2026/08/31 00:58 |
tool |
grepper |
0m
Error:
Args: map[Expression:get_ownership PathPrefix:drivers/pci/]
Results:
|
| 64/2 |
2026/08/31 00:58 |
llm |
expert |
0m
Model:
Tokens:
input: 53578
output: 28
thoughts: 1780
Thoughts: **Analysis of the `generic_access_phys` Warning**
Okay, let's break this down. My initial thought was about PCI devices. Specifically, if an unprivileged user in a user namespace could somehow access a PCI resource. The conclusion is that they can't. PCI devices lack `get_ownership`, so they're owned by `GLOBAL_ROOT_UID`. And, as established, that UID isn't mapped in an unprivileged user namespace, so the checks fail, and `CAP_DAC_OVERRIDE` is denied. Therefore, no unprivileged user can open those PCI resource files, even with sysfs mounted in a user namespace. So that's a dead end.
I then started looking for other drivers that use `generic_access_phys`. I found a few: `cdx`, `mem`, `dfl-afu-main`, `mmap` and `uio`. So I thought maybe UIO devices could be the key, since they *can* be accessed by unprivileged users in specific scenarios. But by default, UIO devices are `0600` and require a permission change by the system administrator. `/dev/mem` and `/dev/dfl-port.*` are root only, and `cdx` is the same as PCI: root only.
So, this vulnerability isn't accessible from unprivileged processes or user namespaces. It needs root, or specific device access. However, the report is a WARNING, and that got my attention. It points to a specific line in `arch/arm64/include/asm/io.h:283`.
That code checks if the PTE has the `PTE_USER` bit set. If not, it returns `NULL`. This triggers an `-ENOMEM` from `generic_access_phys`. The question is if there are consequences. Does it lead to corruption, DoS, is it exploitable? Itβs just a warning. In a production build, it won't crash the kernel, and it just returns an error. Itβs not exploitable.
Then, I shifted my focus to the case where a user *does* map a PCI resource with `PROT_READ`. In that case, the `PTE_USER` *would* be set, `ioremap_prot` would succeed, and `generic_access_phys` would read the PCI resource. And, this is the expected behavior, which then made me wonder why a `PROT_NONE` mapping wouldn't have the `PTE_USER` set. This led to `PAGE_NONE`, and that's the protection type used.
The minor bug is then in `generic_access_phys` not handling `PROT_NONE` gracefully, since it should be checking if it's readable/writable. It does check for write access. But what about read access? It doesn't check for that, so if `write == 0`, it just continues. But it doesn't matter, since `access_remote_vm` allows reading even with `PROT_NONE` VMAs! `/proc/self/mem` shows this.
Then, I looked at how `FOLL_FORCE` is used and how it interacts with the access to `PROT_NONE` VMAs, and how `generic_access_phys` uses its flags. The upshot is that while the `FOLL_FORCE` flag can allow access to read `PROT_NONE` memory, `generic_access_phys` doesn't use these flags. It just tries to map it and read it, and that's when the warning appears, because the PTE is `PAGE_NONE`.
So, the conclusion is no real security impact: Not exploitable, no DoS, and not accessible to unprivileged users in user namespaces. And as a final check, is there another way to get to `generic_access_phys` with `PROT_NONE`? What if some driver uses `remap_pfn_range` and sets `.access = generic_access_phys`, and as far as I could tell, it is used by only 5 drivers, and they require root access. And what about `kernfs_vma_access` with a sysfs file. `kernfs_vma_access` can be used to handle mmap for various sysfs files, but I have no idea if there are any sysfs files that support `mmap` that are accessible to unprivileged users.
|
| 65/2 |
2026/08/31 00:59 |
tool |
grepper |
0m
Args: map[Expression:\.mmap\s*= PathPrefix:drivers/]
Results: map[Output:
Full output is too long, showing 500 out of 2021 lines.
Use more precise expression if possible.
drivers/accel/amdxdna/aie4_pci.c=659=const struct amdxdna_dev_ops aie4_vf_ops = {
--
drivers/accel/amdxdna/aie4_pci.c-663- .hwctx_fini = aie4_hwctx_fini,
drivers/accel/amdxdna/aie4_pci.c:664: .mmap = aie4_doorbell_mmap,
drivers/accel/amdxdna/aie4_pci.c-665- .cmd_wait = aie4_cmd_wait,
--
drivers/accel/amdxdna/amdxdna_cbuf.c=212=static const struct dma_buf_ops amdxdna_cbuf_dmabuf_ops = {
--
drivers/accel/amdxdna/amdxdna_cbuf.c-215- .release = amdxdna_cbuf_release,
drivers/accel/amdxdna/amdxdna_cbuf.c:216: .mmap = amdxdna_cbuf_mmap,
drivers/accel/amdxdna/amdxdna_cbuf.c-217- .vmap = amdxdna_cbuf_vmap,
--
drivers/accel/amdxdna/amdxdna_gem.c=559=static const struct dma_buf_ops amdxdna_dmabuf_ops = {
--
drivers/accel/amdxdna/amdxdna_gem.c-564- .release = drm_gem_dmabuf_release,
drivers/accel/amdxdna/amdxdna_gem.c:565: .mmap = amdxdna_gem_dmabuf_mmap,
drivers/accel/amdxdna/amdxdna_gem.c-566- .vmap = drm_gem_dmabuf_vmap,
--
drivers/accel/amdxdna/amdxdna_gem.c=772=static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
--
drivers/accel/amdxdna/amdxdna_gem.c-781- .vunmap = amdxdna_gem_obj_vunmap,
drivers/accel/amdxdna/amdxdna_gem.c:782: .mmap = amdxdna_gem_obj_mmap,
drivers/accel/amdxdna/amdxdna_gem.c-783- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/accel/amdxdna/amdxdna_pci_drv.c=315=static const struct file_operations amdxdna_fops = {
--
drivers/accel/amdxdna/amdxdna_pci_drv.c-323- .llseek = noop_llseek,
drivers/accel/amdxdna/amdxdna_pci_drv.c:324: .mmap = amdxdna_drm_gem_mmap,
drivers/accel/amdxdna/amdxdna_pci_drv.c-325- .show_fdinfo = drm_show_fdinfo,
--
drivers/accel/ethosu/ethosu_gem.c=33=static const struct drm_gem_object_funcs ethosu_gem_funcs = {
--
drivers/accel/ethosu/ethosu_gem.c-37- .vmap = drm_gem_dma_object_vmap,
drivers/accel/ethosu/ethosu_gem.c:38: .mmap = ethosu_gem_mmap,
drivers/accel/ethosu/ethosu_gem.c-39- .vm_ops = &drm_gem_dma_vm_ops,
--
drivers/accel/habanalabs/common/command_buffer.c=258=static struct hl_mmap_mem_buf_behavior cb_behavior = {
--
drivers/accel/habanalabs/common/command_buffer.c-262- .release = hl_cb_mmap_mem_release,
drivers/accel/habanalabs/common/command_buffer.c:263: .mmap = hl_cb_mmap,
drivers/accel/habanalabs/common/command_buffer.c-264-};
--
drivers/accel/habanalabs/common/habanalabs_drv.c=86=static const struct file_operations hl_fops = {
--
drivers/accel/habanalabs/common/habanalabs_drv.c-92- .llseek = noop_llseek,
drivers/accel/habanalabs/common/habanalabs_drv.c:93: .mmap = hl_mmap
drivers/accel/habanalabs/common/habanalabs_drv.c-94-};
--
drivers/accel/habanalabs/common/memory.c=2152=static struct hl_mmap_mem_buf_behavior hl_ts_behavior = {
--
drivers/accel/habanalabs/common/memory.c-2154- .mem_id = HL_MMAP_TYPE_TS_BUFF,
drivers/accel/habanalabs/common/memory.c:2155: .mmap = hl_ts_mmap,
drivers/accel/habanalabs/common/memory.c-2156- .alloc = hl_ts_alloc_buf,
--
drivers/accel/habanalabs/gaudi/gaudi.c=9130=static const struct hl_asic_funcs gaudi_funcs = {
--
drivers/accel/habanalabs/gaudi/gaudi.c-9141- .resume = gaudi_resume,
drivers/accel/habanalabs/gaudi/gaudi.c:9142: .mmap = gaudi_mmap,
drivers/accel/habanalabs/gaudi/gaudi.c-9143- .ring_doorbell = gaudi_ring_doorbell,
--
drivers/accel/habanalabs/gaudi2/gaudi2.c=11905=static const struct hl_asic_funcs gaudi2_funcs = {
--
drivers/accel/habanalabs/gaudi2/gaudi2.c-11916- .resume = gaudi2_resume,
drivers/accel/habanalabs/gaudi2/gaudi2.c:11917: .mmap = gaudi2_mmap,
drivers/accel/habanalabs/gaudi2/gaudi2.c-11918- .ring_doorbell = gaudi2_ring_doorbell,
--
drivers/accel/habanalabs/goya/goya.c=5431=static const struct hl_asic_funcs goya_funcs = {
--
drivers/accel/habanalabs/goya/goya.c-5442- .resume = goya_resume,
drivers/accel/habanalabs/goya/goya.c:5443: .mmap = goya_mmap,
drivers/accel/habanalabs/goya/goya.c-5444- .ring_doorbell = goya_ring_doorbell,
--
drivers/accel/ivpu/ivpu_gem.c=347=static const struct drm_gem_object_funcs ivpu_gem_funcs = {
--
drivers/accel/ivpu/ivpu_gem.c-355- .vunmap = drm_gem_shmem_object_vunmap,
drivers/accel/ivpu/ivpu_gem.c:356: .mmap = drm_gem_shmem_object_mmap,
drivers/accel/ivpu/ivpu_gem.c-357- .status = ivpu_gem_status,
--
drivers/accel/qaic/qaic_data.c=693=static const struct drm_gem_object_funcs qaic_gem_funcs = {
--
drivers/accel/qaic/qaic_data.c-696- .print_info = qaic_gem_print_info,
drivers/accel/qaic/qaic_data.c:697: .mmap = qaic_gem_object_mmap,
drivers/accel/qaic/qaic_data.c-698- .vm_ops = &drm_vm_ops,
--
drivers/accel/rocket/rocket_gem.c=35=static const struct drm_gem_object_funcs rocket_gem_funcs = {
--
drivers/accel/rocket/rocket_gem.c-42- .vunmap = drm_gem_shmem_object_vunmap,
drivers/accel/rocket/rocket_gem.c:43: .mmap = drm_gem_shmem_object_mmap,
drivers/accel/rocket/rocket_gem.c-44- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/acpi/pfr_telemetry.c=340=static const struct file_operations acpi_pfrt_log_fops = {
drivers/acpi/pfr_telemetry.c-341- .owner = THIS_MODULE,
drivers/acpi/pfr_telemetry.c:342: .mmap = pfrt_log_mmap,
drivers/acpi/pfr_telemetry.c-343- .unlocked_ioctl = pfrt_log_ioctl,
--
drivers/android/binder.c=7001=const struct file_operations binder_fops = {
--
drivers/android/binder.c-7005- .compat_ioctl = compat_ptr_ioctl,
drivers/android/binder.c:7006: .mmap = binder_mmap,
drivers/android/binder.c-7007- .open = binder_open,
--
drivers/android/tests/binder_alloc_kunit.c=491=static const struct file_operations binder_alloc_test_fops = {
drivers/android/tests/binder_alloc_kunit.c:492: .mmap = binder_alloc_test_mmap_handler,
drivers/android/tests/binder_alloc_kunit.c-493-};
--
drivers/block/ublk_drv.c=4144=static const struct file_operations ublk_ch_fops = {
--
drivers/block/ublk_drv.c-4150- .uring_cmd = ublk_ch_uring_cmd,
drivers/block/ublk_drv.c:4151: .mmap = ublk_ch_mmap,
drivers/block/ublk_drv.c-4152-};
--
drivers/block/ublk_drv.c=4154=static const struct file_operations ublk_ch_batch_io_fops = {
--
drivers/block/ublk_drv.c-4160- .uring_cmd = ublk_ch_batch_io_uring_cmd,
drivers/block/ublk_drv.c:4161: .mmap = ublk_ch_mmap,
drivers/block/ublk_drv.c-4162-};
--
drivers/char/bsr.c=148=static const struct file_operations bsr_fops = {
drivers/char/bsr.c-149- .owner = THIS_MODULE,
drivers/char/bsr.c:150: .mmap = bsr_mmap,
drivers/char/bsr.c-151- .open = bsr_open,
--
drivers/char/uv_mmtimer.c=50=static const struct file_operations uv_mmtimer_fops = {
drivers/char/uv_mmtimer.c-51- .owner = THIS_MODULE,
drivers/char/uv_mmtimer.c:52: .mmap = uv_mmtimer_mmap,
drivers/char/uv_mmtimer.c-53- .unlocked_ioctl = uv_mmtimer_ioctl,
--
drivers/comedi/comedi_fops.c=3432=static const struct file_operations comedi_fops = {
--
drivers/comedi/comedi_fops.c-3439- .write = comedi_write,
drivers/comedi/comedi_fops.c:3440: .mmap = comedi_mmap,
drivers/comedi/comedi_fops.c-3441- .poll = comedi_poll,
--
drivers/crypto/hisilicon/qm.c=2887=static const struct uacce_ops uacce_qm_ops = {
--
drivers/crypto/hisilicon/qm.c-2892- .stop_queue = hisi_qm_uacce_stop_queue,
drivers/crypto/hisilicon/qm.c:2893: .mmap = hisi_qm_uacce_mmap,
drivers/crypto/hisilicon/qm.c-2894- .ioctl = hisi_qm_uacce_ioctl,
--
drivers/crypto/tegra/tegra-se-main.c=110=static const struct host1x_bo_ops tegra_se_cmdbuf_ops = {
--
drivers/crypto/tegra/tegra-se-main.c-114- .unpin = tegra_se_cmdbuf_unpin,
drivers/crypto/tegra/tegra-se-main.c:115: .mmap = tegra_se_cmdbuf_mmap,
drivers/crypto/tegra/tegra-se-main.c-116- .munmap = tegra_se_cmdbuf_munmap,
--
drivers/dma-buf/dma-buf.c=609=static const struct file_operations dma_buf_fops = {
drivers/dma-buf/dma-buf.c-610- .release = dma_buf_file_release,
drivers/dma-buf/dma-buf.c:611: .mmap = dma_buf_mmap_internal,
drivers/dma-buf/dma-buf.c-612- .llseek = dma_buf_llseek,
--
drivers/dma-buf/heaps/cma_heap.c=268=static const struct dma_buf_ops cma_heap_buf_ops = {
--
drivers/dma-buf/heaps/cma_heap.c-274- .end_cpu_access = cma_heap_dma_buf_end_cpu_access,
drivers/dma-buf/heaps/cma_heap.c:275: .mmap = cma_heap_mmap,
drivers/dma-buf/heaps/cma_heap.c-276- .vmap = cma_heap_vmap,
--
drivers/dma-buf/heaps/system_heap.c=365=static const struct dma_buf_ops system_heap_buf_ops = {
--
drivers/dma-buf/heaps/system_heap.c-371- .end_cpu_access = system_heap_dma_buf_end_cpu_access,
drivers/dma-buf/heaps/system_heap.c:372: .mmap = system_heap_mmap,
drivers/dma-buf/heaps/system_heap.c-373- .vmap = system_heap_vmap,
--
drivers/dma-buf/udmabuf.c=258=static const struct dma_buf_ops udmabuf_ops = {
--
drivers/dma-buf/udmabuf.c-261- .release = release_udmabuf,
drivers/dma-buf/udmabuf.c:262: .mmap = mmap_udmabuf,
drivers/dma-buf/udmabuf.c-263- .vmap = vmap_udmabuf,
--
drivers/dma/idxd/cdev.c=516=static const struct file_operations idxd_cdev_fops = {
--
drivers/dma/idxd/cdev.c-519- .release = idxd_cdev_release,
drivers/dma/idxd/cdev.c:520: .mmap = idxd_cdev_mmap,
drivers/dma/idxd/cdev.c-521- .write = idxd_cdev_write,
--
drivers/firewire/core-cdev.c=1979=const struct file_operations fw_device_ops = {
--
drivers/firewire/core-cdev.c-1983- .unlocked_ioctl = fw_device_op_ioctl,
drivers/firewire/core-cdev.c:1984: .mmap = fw_device_op_mmap,
drivers/firewire/core-cdev.c-1985- .release = fw_device_op_release,
--
drivers/fpga/dfl-afu-main.c=850=static const struct file_operations afu_fops = {
--
drivers/fpga/dfl-afu-main.c-854- .unlocked_ioctl = afu_ioctl,
drivers/fpga/dfl-afu-main.c:855: .mmap = afu_mmap,
drivers/fpga/dfl-afu-main.c-856-};
--
drivers/gpu/drm/adp/adp_drv.c=78=static const struct file_operations adp_fops = {
--
drivers/gpu/drm/adp/adp_drv.c-86- .llseek = noop_llseek,
drivers/gpu/drm/adp/adp_drv.c:87: .mmap = drm_gem_mmap,
drivers/gpu/drm/adp/adp_drv.c-88- .fop_flags = FOP_UNSIGNED_OFFSET,
--
drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c=351=const struct dma_buf_ops amdgpu_dmabuf_ops = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c-358- .begin_cpu_access = amdgpu_dma_buf_begin_cpu_access,
drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c:359: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c-360- .vmap = amdgpu_dma_buf_vmap,
--
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c=3051=static const struct file_operations amdgpu_driver_kms_fops = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c-3056- .unlocked_ioctl = amdgpu_drm_ioctl,
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c:3057: .mmap = drm_gem_mmap,
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c-3058- .poll = drm_poll,
--
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c=389=const struct drm_gem_object_funcs amdgpu_gem_object_funcs = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c-395- .vunmap = drm_gem_ttm_vunmap,
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c:396: .mmap = amdgpu_gem_object_mmap,
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c-397- .vm_ops = &amdgpu_gem_vm_ops,
--
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c=57=static const struct file_operations kfd_fops = {
--
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c-62- .release = kfd_release,
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c:63: .mmap = kfd_mmap,
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c-64-};
--
drivers/gpu/drm/armada/armada_gem.c=480=static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
--
drivers/gpu/drm/armada/armada_gem.c-483- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/armada/armada_gem.c:484: .mmap = armada_gem_dmabuf_mmap,
drivers/gpu/drm/armada/armada_gem.c-485-};
--
drivers/gpu/drm/drm_file.c=57=bool drm_dev_needs_global_mutex(struct drm_device *dev)
--
drivers/gpu/drm/drm_file.c-107- * .read = drm_read,
drivers/gpu/drm/drm_file.c:108: * .mmap = drm_gem_mmap,
drivers/gpu/drm/drm_file.c-109- * };
--
drivers/gpu/drm/drm_gem_dma_helper.c=50=static const struct drm_gem_object_funcs drm_gem_dma_default_funcs = {
--
drivers/gpu/drm/drm_gem_dma_helper.c-54- .vmap = drm_gem_dma_object_vmap,
drivers/gpu/drm/drm_gem_dma_helper.c:55: .mmap = drm_gem_dma_object_mmap,
drivers/gpu/drm/drm_gem_dma_helper.c-56- .vm_ops = &drm_gem_dma_vm_ops,
--
drivers/gpu/drm/drm_gem_shmem_helper.c=42=static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
--
drivers/gpu/drm/drm_gem_shmem_helper.c-49- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/drm_gem_shmem_helper.c:50: .mmap = drm_gem_shmem_object_mmap,
drivers/gpu/drm/drm_gem_shmem_helper.c-51- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/gpu/drm/drm_gem_vram_helper.c=689=static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = {
--
drivers/gpu/drm/drm_gem_vram_helper.c-692- .vunmap = drm_gem_vram_object_vunmap,
drivers/gpu/drm/drm_gem_vram_helper.c:693: .mmap = drm_gem_ttm_mmap,
drivers/gpu/drm/drm_gem_vram_helper.c-694- .print_info = drm_gem_ttm_print_info,
--
drivers/gpu/drm/drm_prime.c=827=static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
--
drivers/gpu/drm/drm_prime.c-832- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/drm_prime.c:833: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/drm_prime.c-834- .vmap = drm_gem_dmabuf_vmap,
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c=495=static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = {
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c-498- .vmap = etnaviv_gem_vmap_impl,
drivers/gpu/drm/etnaviv/etnaviv_gem.c:499: .mmap = etnaviv_gem_mmap_obj,
drivers/gpu/drm/etnaviv/etnaviv_gem.c-500-};
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c=562=static const struct drm_gem_object_funcs etnaviv_gem_object_funcs = {
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c-567- .vmap = etnaviv_gem_prime_vmap,
drivers/gpu/drm/etnaviv/etnaviv_gem.c:568: .mmap = etnaviv_gem_mmap,
drivers/gpu/drm/etnaviv/etnaviv_gem.c-569- .status = etnaviv_gem_status,
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c=728=static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = {
--
drivers/gpu/drm/etnaviv/etnaviv_gem.c-731- .vmap = etnaviv_gem_vmap_impl,
drivers/gpu/drm/etnaviv/etnaviv_gem.c:732: .mmap = etnaviv_gem_userptr_mmap_obj,
drivers/gpu/drm/etnaviv/etnaviv_gem.c-733-};
--
drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c=105=static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
--
drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c-108- .vmap = etnaviv_gem_prime_vmap_impl,
drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c:109: .mmap = etnaviv_gem_prime_mmap_obj,
drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c-110-};
--
drivers/gpu/drm/exynos/exynos_drm_gem.c=147=static const struct drm_gem_object_funcs exynos_drm_gem_object_funcs = {
--
drivers/gpu/drm/exynos/exynos_drm_gem.c-149- .get_sg_table = exynos_drm_gem_prime_get_sg_table,
drivers/gpu/drm/exynos/exynos_drm_gem.c:150: .mmap = exynos_drm_gem_mmap,
drivers/gpu/drm/exynos/exynos_drm_gem.c-151- .vm_ops = &exynos_drm_gem_vm_ops,
--
drivers/gpu/drm/gma500/psb_drv.c=493=static const struct file_operations psb_gem_fops = {
--
drivers/gpu/drm/gma500/psb_drv.c-498- .compat_ioctl = drm_compat_ioctl,
drivers/gpu/drm/gma500/psb_drv.c:499: .mmap = drm_gem_mmap,
drivers/gpu/drm/gma500/psb_drv.c-500- .poll = drm_poll,
--
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c=203=static const struct dma_buf_ops i915_dmabuf_ops = {
--
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c-208- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c:209: .mmap = i915_gem_dmabuf_mmap,
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c-210- .vmap = i915_gem_dmabuf_vmap,
--
drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c=90=static const struct dma_buf_ops mock_dmabuf_ops = {
--
drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c-93- .release = mock_dmabuf_release,
drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c:94: .mmap = mock_dmabuf_mmap,
drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c-95- .vmap = mock_dmabuf_vmap,
--
drivers/gpu/drm/i915/gvt/kvmgt.c=1440=static const struct vfio_device_ops intel_vgpu_dev_ops = {
--
drivers/gpu/drm/i915/gvt/kvmgt.c-1446- .write = intel_vgpu_write,
drivers/gpu/drm/i915/gvt/kvmgt.c:1447: .mmap = intel_vgpu_mmap,
drivers/gpu/drm/i915/gvt/kvmgt.c-1448- .ioctl = intel_vgpu_ioctl,
--
drivers/gpu/drm/i915/i915_driver.c=1667=static const struct file_operations i915_driver_fops = {
--
drivers/gpu/drm/i915/i915_driver.c-1671- .unlocked_ioctl = drm_ioctl,
drivers/gpu/drm/i915/i915_driver.c:1672: .mmap = i915_gem_mmap,
drivers/gpu/drm/i915/i915_driver.c-1673- .poll = drm_poll,
--
drivers/gpu/drm/i915/i915_sysfs.c=142=static const struct bin_attribute dpf_attrs = {
--
drivers/gpu/drm/i915/i915_sysfs.c-146- .write = i915_l3_write,
drivers/gpu/drm/i915/i915_sysfs.c:147: .mmap = NULL,
drivers/gpu/drm/i915/i915_sysfs.c-148- .private = (void *)0
--
drivers/gpu/drm/i915/i915_sysfs.c=151=static const struct bin_attribute dpf_attrs_1 = {
--
drivers/gpu/drm/i915/i915_sysfs.c-155- .write = i915_l3_write,
drivers/gpu/drm/i915/i915_sysfs.c:156: .mmap = NULL,
drivers/gpu/drm/i915/i915_sysfs.c-157- .private = (void *)1
--
drivers/gpu/drm/imagination/pvr_gem.c=55=static const struct drm_gem_object_funcs pvr_gem_object_funcs = {
--
drivers/gpu/drm/imagination/pvr_gem.c-63- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/imagination/pvr_gem.c:64: .mmap = pvr_gem_mmap,
drivers/gpu/drm/imagination/pvr_gem.c-65- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/gpu/drm/lima/lima_gem.c=211=static const struct drm_gem_object_funcs lima_gem_funcs = {
--
drivers/gpu/drm/lima/lima_gem.c-220- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/lima/lima_gem.c:221: .mmap = lima_gem_mmap,
drivers/gpu/drm/lima/lima_gem.c-222- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/gpu/drm/loongson/lsdc_gem.c=131=static const struct drm_gem_object_funcs lsdc_gem_object_funcs = {
--
drivers/gpu/drm/loongson/lsdc_gem.c-138- .vunmap = lsdc_gem_object_vunmap,
drivers/gpu/drm/loongson/lsdc_gem.c:139: .mmap = lsdc_gem_object_mmap,
drivers/gpu/drm/loongson/lsdc_gem.c-140-};
--
drivers/gpu/drm/msm/msm_gem.c=1186=static const struct drm_gem_object_funcs msm_gem_object_funcs = {
--
drivers/gpu/drm/msm/msm_gem.c-1195- .vunmap = msm_gem_prime_vunmap,
drivers/gpu/drm/msm/msm_gem.c:1196: .mmap = msm_gem_object_mmap,
drivers/gpu/drm/msm/msm_gem.c-1197- .status = msm_gem_status,
--
drivers/gpu/drm/msm/msm_gem_prime.c=54=static const struct dma_buf_ops msm_gem_prime_dmabuf_ops = {
--
drivers/gpu/drm/msm/msm_gem_prime.c-59- .release = msm_gem_dmabuf_release,
drivers/gpu/drm/msm/msm_gem_prime.c:60: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/msm/msm_gem_prime.c-61- .vmap = drm_gem_dmabuf_vmap,
--
drivers/gpu/drm/nouveau/nouveau_drm.c=1347=nouveau_driver_fops = {
--
drivers/gpu/drm/nouveau/nouveau_drm.c-1351- .unlocked_ioctl = nouveau_drm_ioctl,
drivers/gpu/drm/nouveau/nouveau_drm.c:1352: .mmap = drm_gem_mmap,
drivers/gpu/drm/nouveau/nouveau_drm.c-1353- .poll = drm_poll,
--
drivers/gpu/drm/nouveau/nouveau_gem.c=217=const struct drm_gem_object_funcs nouveau_gem_object_funcs = {
--
drivers/gpu/drm/nouveau/nouveau_gem.c-226- .vunmap = drm_gem_ttm_vunmap,
drivers/gpu/drm/nouveau/nouveau_gem.c:227: .mmap = drm_gem_ttm_mmap,
drivers/gpu/drm/nouveau/nouveau_gem.c-228- .vm_ops = &nouveau_ttm_vm_ops,
--
drivers/gpu/drm/omapdrm/omap_gem.c=1274=static const struct drm_gem_object_funcs omap_gem_object_funcs = {
--
drivers/gpu/drm/omapdrm/omap_gem.c-1276- .export = omap_gem_prime_export,
drivers/gpu/drm/omapdrm/omap_gem.c:1277: .mmap = omap_gem_object_mmap,
drivers/gpu/drm/omapdrm/omap_gem.c-1278- .vm_ops = &omap_gem_vm_ops,
--
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c=71=static const struct dma_buf_ops omap_dmabuf_ops = {
--
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c-76- .end_cpu_access = omap_gem_dmabuf_end_cpu_access,
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c:77: .mmap = omap_gem_dmabuf_mmap,
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c-78-};
--
drivers/gpu/drm/panfrost/panfrost_gem.c=355=static const struct dma_buf_ops panfrost_dma_buf_ops = {
--
drivers/gpu/drm/panfrost/panfrost_gem.c-360- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/panfrost/panfrost_gem.c:361: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/panfrost/panfrost_gem.c-362- .vmap = drm_gem_dmabuf_vmap,
--
drivers/gpu/drm/panfrost/panfrost_gem.c=402=static const struct drm_gem_object_funcs panfrost_gem_funcs = {
--
drivers/gpu/drm/panfrost/panfrost_gem.c-412- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/panfrost/panfrost_gem.c:413: .mmap = drm_gem_shmem_object_mmap,
drivers/gpu/drm/panfrost/panfrost_gem.c-414- .status = panfrost_gem_status,
--
drivers/gpu/drm/panthor/panthor_drv.c=1752=static const struct file_operations panthor_drm_driver_fops = {
--
drivers/gpu/drm/panthor/panthor_drv.c-1760- .llseek = noop_llseek,
drivers/gpu/drm/panthor/panthor_drv.c:1761: .mmap = panthor_mmap,
drivers/gpu/drm/panthor/panthor_drv.c-1762- .get_unmapped_area = drm_gem_get_unmapped_area,
--
drivers/gpu/drm/panthor/panthor_gem.c=549=static const struct dma_buf_ops panthor_dma_buf_ops = {
--
drivers/gpu/drm/panthor/panthor_gem.c-554- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/panthor/panthor_gem.c:555: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/panthor/panthor_gem.c-556- .vmap = drm_gem_dmabuf_vmap,
--
drivers/gpu/drm/panthor/panthor_gem.c=968=static const struct drm_gem_object_funcs panthor_gem_funcs = {
--
drivers/gpu/drm/panthor/panthor_gem.c-975- .vunmap = panthor_gem_vunmap_locked,
drivers/gpu/drm/panthor/panthor_gem.c:976: .mmap = panthor_gem_mmap,
drivers/gpu/drm/panthor/panthor_gem.c-977- .status = panthor_gem_status,
--
drivers/gpu/drm/qxl/qxl_object.c=91=static const struct drm_gem_object_funcs qxl_object_funcs = {
--
drivers/gpu/drm/qxl/qxl_object.c-99- .vunmap = qxl_gem_prime_vunmap,
drivers/gpu/drm/qxl/qxl_object.c:100: .mmap = drm_gem_ttm_mmap,
drivers/gpu/drm/qxl/qxl_object.c-101- .print_info = drm_gem_ttm_print_info,
--
drivers/gpu/drm/radeon/radeon_drv.c=538=static const struct file_operations radeon_driver_kms_fops = {
--
drivers/gpu/drm/radeon/radeon_drv.c-542- .unlocked_ioctl = radeon_drm_ioctl,
drivers/gpu/drm/radeon/radeon_drv.c:543: .mmap = drm_gem_mmap,
drivers/gpu/drm/radeon/radeon_drv.c-544- .poll = drm_poll,
--
drivers/gpu/drm/radeon/radeon_gem.c=278=const struct drm_gem_object_funcs radeon_gem_object_funcs = {
--
drivers/gpu/drm/radeon/radeon_gem.c-287- .vunmap = drm_gem_ttm_vunmap,
drivers/gpu/drm/radeon/radeon_gem.c:288: .mmap = radeon_gem_object_mmap,
drivers/gpu/drm/radeon/radeon_gem.c-289- .vm_ops = &radeon_gem_vm_ops,
--
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c=363=static const struct drm_gem_object_funcs rcar_du_gem_funcs = {
--
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c-367- .vmap = drm_gem_dma_object_vmap,
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c:368: .mmap = drm_gem_dma_object_mmap,
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c-369- .vm_ops = &drm_gem_dma_vm_ops,
--
drivers/gpu/drm/rockchip/rockchip_drm_gem.c=275=static const struct drm_gem_object_funcs rockchip_gem_object_funcs = {
--
drivers/gpu/drm/rockchip/rockchip_drm_gem.c-279- .vunmap = rockchip_gem_prime_vunmap,
drivers/gpu/drm/rockchip/rockchip_drm_gem.c:280: .mmap = rockchip_drm_gem_object_mmap,
drivers/gpu/drm/rockchip/rockchip_drm_gem.c-281- .vm_ops = &drm_gem_dma_vm_ops,
--
drivers/gpu/drm/tegra/drm.c=795=static const struct file_operations tegra_drm_fops = {
--
drivers/gpu/drm/tegra/drm.c-799- .unlocked_ioctl = drm_ioctl,
drivers/gpu/drm/tegra/drm.c:800: .mmap = tegra_drm_mmap,
drivers/gpu/drm/tegra/drm.c-801- .poll = drm_poll,
--
drivers/gpu/drm/tegra/gem.c=226=static const struct host1x_bo_ops tegra_bo_ops = {
--
drivers/gpu/drm/tegra/gem.c-230- .unpin = tegra_bo_unpin,
drivers/gpu/drm/tegra/gem.c:231: .mmap = tegra_bo_mmap,
drivers/gpu/drm/tegra/gem.c-232- .munmap = tegra_bo_munmap,
--
drivers/gpu/drm/tegra/gem.c=742=static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = {
--
drivers/gpu/drm/tegra/gem.c-747- .end_cpu_access = tegra_gem_prime_end_cpu_access,
drivers/gpu/drm/tegra/gem.c:748: .mmap = tegra_gem_prime_mmap,
drivers/gpu/drm/tegra/gem.c-749- .vmap = tegra_gem_prime_vmap,
--
drivers/gpu/drm/tegra/submit.c=135=static const struct host1x_bo_ops gather_bo_ops = {
--
drivers/gpu/drm/tegra/submit.c-139- .unpin = gather_bo_unpin,
drivers/gpu/drm/tegra/submit.c:140: .mmap = gather_bo_mmap,
drivers/gpu/drm/tegra/submit.c-141- .munmap = gather_bo_munmap,
--
drivers/gpu/drm/v3d/v3d_bo.c=65=static const struct drm_gem_object_funcs v3d_gem_funcs = {
--
drivers/gpu/drm/v3d/v3d_bo.c-72- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/v3d/v3d_bo.c:73: .mmap = drm_gem_shmem_object_mmap,
drivers/gpu/drm/v3d/v3d_bo.c-74- .status = v3d_gem_status,
--
drivers/gpu/drm/vc4/vc4_bo.c=763=static const struct drm_gem_object_funcs vc4_gem_object_funcs = {
--
drivers/gpu/drm/vc4/vc4_bo.c-767- .vmap = drm_gem_dma_object_vmap,
drivers/gpu/drm/vc4/vc4_bo.c:768: .mmap = vc4_gem_object_mmap,
drivers/gpu/drm/vc4/vc4_bo.c-769- .vm_ops = &vc4_vm_ops,
--
drivers/gpu/drm/virtio/virtgpu_object.c=136=static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = {
--
drivers/gpu/drm/virtio/virtgpu_object.c-146- .vunmap = drm_gem_shmem_object_vunmap,
drivers/gpu/drm/virtio/virtgpu_object.c:147: .mmap = drm_gem_shmem_object_mmap,
drivers/gpu/drm/virtio/virtgpu_object.c-148- .vm_ops = &drm_gem_shmem_vm_ops,
--
drivers/gpu/drm/virtio/virtgpu_prime.c=77=static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = {
--
drivers/gpu/drm/virtio/virtgpu_prime.c-83- .release = drm_gem_dmabuf_release,
drivers/gpu/drm/virtio/virtgpu_prime.c:84: .mmap = drm_gem_dmabuf_mmap,
drivers/gpu/drm/virtio/virtgpu_prime.c-85- .vmap = drm_gem_dmabuf_vmap,
--
drivers/gpu/drm/virtio/virtgpu_vram.c=139=static const struct drm_gem_object_funcs virtio_gpu_vram_funcs = {
--
drivers/gpu/drm/virtio/virtgpu_vram.c-142- .free = virtio_gpu_vram_free,
drivers/gpu/drm/virtio/virtgpu_vram.c:143: .mmap = virtio_gpu_vram_mmap,
drivers/gpu/drm/virtio/virtgpu_vram.c-144- .export = virtgpu_gem_prime_export,
--
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c=1568=static const struct file_operations vmwgfx_driver_fops = {
--
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c-1572- .unlocked_ioctl = vmw_unlocked_ioctl,
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c:1573: .mmap = drm_gem_mmap,
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c-1574- .poll = drm_poll,
--
drivers/gpu/drm/vmwgfx/vmwgfx_gem.c=143=const struct drm_gem_object_funcs vmw_gem_object_funcs = {
--
]
|
| 66/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 68883
output: 34
thoughts: 722
Thoughts: **Analyzing the Vulnerability: A Deep Dive**
Okay, let's break this down systematically. My initial thought was to quickly check `kernfs_fop_mmap` for any world-readable/writable sysfs files supporting mmap, but it was a long shot. Even if there were, they'd have to use `generic_access_phys` to trigger the warning, and, as far as I know, the only sysfs files that use `generic_access_phys` are PCI resources and CDX devices, which are both `0600` root:root. So, that's a dead end β no way an unprivileged user can hit this.
I then had to be certain. Could some other VMA call `generic_access_phys`? Well, it's exported as a symbol, so, theoretically, a module could use it. But in-tree modules are my primary concern. I ran a quick `grep` for `generic_access_phys`, and as suspected, it was limited to those five drivers. Definitely root-restricted.
Next, I considered some "obvious" potential bypasses. What about `/dev/mem`? Nope, root-restricted. UIO devices? Also root-restricted. Okay, good. So the bug isn't accessible from unprivileged processes.
User namespaces? I considered that angle but quickly dismissed it. The files involved are owned by global root, which isn't mapped in user namespaces, so `CAP_DAC_OVERRIDE` would be denied.
Denial of service? No, it just prints a warning and returns an error. Exploitability? Seems unlikely. It's just returning an error.
Let's revisit the bug report's stack trace. The `rcu_read_unlock` and `pte_unmap` caught my eye. These are probably occurring because the `WARN_ON_ONCE` is within `ioremap_prot`. And *aha*! `ioremap_prot` is called directly from `generic_access_phys`.
```c
maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
```
But wait, `generic_access_phys` calls `ioremap_prot` *after* `follow_pfnmap_end(&args)`! We need to go back and re-examine `generic_access_phys`.
|
| 67/2 |
2026/08/31 00:59 |
tool |
read-file |
0m
Args: map[File:mm/memory.c FirstLine:7105 LineCount:20]
Results: map[Contents:7105: retry:
7106: if (follow_pfnmap_start(&args))
7107: return -EINVAL;
7108: prot = args.pgprot;
7109: phys_addr = (resource_size_t)args.pfn << PAGE_SHIFT;
7110: writable = args.writable;
7111: follow_pfnmap_end(&args);
7112:
7113: if ((write & FOLL_WRITE) && !writable)
7114: return -EINVAL;
7115:
7116: maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
7117: if (!maddr)
7118: return -ENOMEM;
7119:
7120: if (follow_pfnmap_start(&args))
7121: goto out_unmap;
7122:
7123: if ((pgprot_val(prot) != pgprot_val(args.pgprot)) ||
7124: (phys_addr != (args.pfn << PAGE_SHIFT)) ||
]
|
| 68/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 70478
output: 34
thoughts: 186
Thoughts: **Debugging Deep Dive**
Okay, the stack trace is staring me in the face, and I need to untangle this. First, `rcu_read_unlock` is at the top, which seems standard for this sort of issue. Then, the `pte_unmap` and `follow_pfnmap_end` calls are there, and I know that means we're dealing with page table manipulation. Ah, and here's the kicker: `generic_access_phys` at line 7126 is the key function. Let me see... 7126 calls `follow_pfnmap_end` directly. The warning! Wait, it's occurring *within* `follow_pfnmap_end` itself, not in the calling functions. Alright, that simplifies things a bit. I need to zero in on what's happening specifically in `follow_pfnmap_end`. Time to examine that function in more detail.
|
| 69/2 |
2026/08/31 00:59 |
tool |
read-file |
0m
Args: map[File:mm/memory.c FirstLine:7065 LineCount:20]
Results: map[Contents:7065: /**
7066: * follow_pfnmap_end(): End a follow_pfnmap_start() process
7067: * @args: Pointer to struct @follow_pfnmap_args
7068: *
7069: * Must be used in pair of follow_pfnmap_start(). See the start() function
7070: * above for more information.
7071: */
7072: void follow_pfnmap_end(struct follow_pfnmap_args *args)
7073: {
7074: if (args->lock)
7075: spin_unlock(args->lock);
7076: if (args->ptep)
7077: pte_unmap(args->ptep);
7078: }
7079: EXPORT_SYMBOL_GPL(follow_pfnmap_end);
7080:
7081: #ifdef CONFIG_HAVE_IOREMAP_PROT
7082: /**
7083: * generic_access_phys - generic implementation for iomem mmap access
7084: * @vma: the vma to access
]
|
| 70/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 71228
output: 39
thoughts: 2028
Thoughts: **Analyzing a Warning in `generic_access_phys` on ARM64**
Okay, so I'm looking at this warning that seems to originate from `pte_unmap`, but the stack trace is a little weird, specifically around the `ioremap_prot` call and how syzkaller is reporting the inline frames. The core warning is at `arch/arm64/include/asm/io.h:283`, which is the `WARN_ON_ONCE` about the user page table entry (PTE) not having the user bit set. I was initially thrown off because the stack trace shows `rcu_read_unlock` and `pte_unmap`, which are functions called by ioremap, and it seemed out of sync with the warning's location.
It seems I initially misread syzkaller's stack trace parsing. The "at rcu_read_unlock" part is syzkaller trying to map the PC to the correct inline frame. The warning is, in fact, at `arch/arm64/include/asm/io.h:283` which is within `ioremap_prot`. The stack trace seems to indicate that the inlining of `ioremap_prot` and the potential inlining of `follow_pfnmap_end` cause the PC to be misattributed to different line numbers. The line number for the warning makes perfect sense. The compiler might be optimizing around the `WARN_ON_ONCE` call, or maybe there is a reordering of the code.
Given `generic_access_phys` calls `ioremap_prot`, and that's where the warning is, it's about the protection bits passed to `ioremap_prot`. The relevant code is within the `generic_access_phys` function which is called when a user mmap's a device resource. It means an issue with how the protection bits are being set within `ioremap_prot`, likely the `PTE_USER` bit. Now, who calls `generic_access_phys`?
I need to confirm that this is triggered by a call to `ioremap_prot`, called from `generic_access_phys` and that `generic_access_phys` is used by a device that may potentially be accessible to an unprivileged user. I can't think of any scenarios with the standard drivers, given it is only called from pci, pcm, mtd, tty and sbus. It is highly restricted to root, the only possible exception would be the mmap of a PCI resource. The `pci_mmap_resource_range` call is, therefore, the more likely place.
I need to quickly eliminate any other potential entry points for an unprivileged user, just to be sure. I'm thinking about ALSA (`snd_pcm_mmap`), DRM, V4L2, and dma-buf but I know those drivers use their own mmap handlers. I've eliminated a few potential ways for an unprivileged user to reach that code path with those quick checks. So, the only realistic path is through PCI resource mapping.
So, I think I've traced the issue back to `generic_access_phys`, called through the PCI resource mapping. It seems like the user protection is not being set correctly within the device mmap handler.
|
| 71/2 |
2026/08/31 00:59 |
tool |
grepper |
0m
Error:
Args: map[Expression:proc_bus_pci_dev_fops PathPrefix:drivers/pci/proc.c]
Results:
|
| 72/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 74018
output: 31
thoughts: 14
Thoughts: Here's my take on the thought process, as requested:
**Diving into PCI Probe Information**
Alright, time to dig into the `drivers/pci/proc.c` file. This is where the magic happens, the guts of the PCI probing and information reporting. I need to refresh my memory on how this particular driver structures its data and exports it. Let's see... I'm expecting to find the standard PCI device identification routines, the handling of the `/proc/bus/pci` filesystem entries (crucial for system introspection), and hopefully some of the core functions that handle the device enumeration and resource allocation at boot. I'm especially curious about the implementation of error handling and how it interacts with the kernel's overall PCI subsystem. Any novel approaches or optimizations here always catch my eye. I'm also hoping to see how the code interfaces with the ACPI or other power management mechanisms, just out of professional interest. I'll be looking for things like mutex use and spinlock usage, as well as if there are any specific locking strategies employed to keep things consistent across the kernel. This is going to be good!
|
| 73/2 |
2026/08/31 00:59 |
tool |
grepper |
0m
Error:
Args: map[Expression:\.mmap PathPrefix:drivers/pci/proc.c]
Results:
|
| 74/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 74315
output: 30
thoughts: 19
Thoughts: Here's a summary of my immediate thought process, expressed in the first person, assuming I'm a seasoned kernel developer:
**Initial Assessment: mmap Absence in drivers/pci/proc.c**
Okay, hold on a second... `drivers/pci/proc.c` shouldn't be missing the `mmap` functionality, should it? That's a bit unexpected. I was expecting to find the standard memory-mapping setup there, particularly if it's interacting with PCI configuration space or BARs. I need to figure out why `mmap` isn't present, or more accurately, why it appears to be absent, and what alternatives are being used, if any. Is it using a custom approach, a different mechanism, or am I overlooking something fundamental in the code? This merits a closer look. I need to understand how memory regions are being exposed to userspace, or if they are at all. Perhaps there's an existing design choice at play. Let's dig in and figure out the reasoning behind this design!
|
| 75/2 |
2026/08/31 00:59 |
tool |
grepper |
0m
Args: map[Expression:mmap PathPrefix:drivers/pci/proc.c]
Results: map[Output:drivers/pci/proc.c=203=struct pci_filp_private {
drivers/pci/proc.c:204: enum pci_mmap_state mmap_state;
drivers/pci/proc.c-205- int write_combine;
--
drivers/pci/proc.c=209=static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
--
drivers/pci/proc.c-228- case PCIIOC_MMAP_IS_IO:
drivers/pci/proc.c:229: if (!arch_can_pci_mmap_io())
drivers/pci/proc.c-230- return -EINVAL;
drivers/pci/proc.c:231: fpriv->mmap_state = pci_mmap_io;
drivers/pci/proc.c-232- break;
--
drivers/pci/proc.c-234- case PCIIOC_MMAP_IS_MEM:
drivers/pci/proc.c:235: fpriv->mmap_state = pci_mmap_mem;
drivers/pci/proc.c-236- break;
--
drivers/pci/proc.c-238- case PCIIOC_WRITE_COMBINE:
drivers/pci/proc.c:239: if (arch_can_pci_mmap_wc()) {
drivers/pci/proc.c-240- if (arg)
--
drivers/pci/proc.c-257-#ifdef HAVE_PCI_MMAP
drivers/pci/proc.c:258:static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
drivers/pci/proc.c-259-{
--
drivers/pci/proc.c-272-
drivers/pci/proc.c:273: if (fpriv->mmap_state == pci_mmap_io) {
drivers/pci/proc.c:274: if (!arch_can_pci_mmap_io())
drivers/pci/proc.c-275- return -EINVAL;
--
drivers/pci/proc.c-281- if (dev->resource[i].flags & res_bit &&
drivers/pci/proc.c:282: pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
drivers/pci/proc.c-283- break;
--
drivers/pci/proc.c-288-
drivers/pci/proc.c:289: if (fpriv->mmap_state == pci_mmap_mem &&
drivers/pci/proc.c-290- fpriv->write_combine) {
--
drivers/pci/proc.c-304- vma->vm_pgoff -= start >> PAGE_SHIFT;
drivers/pci/proc.c:305: ret = pci_mmap_resource_range(dev, i, vma,
drivers/pci/proc.c:306: fpriv->mmap_state, write_combine);
drivers/pci/proc.c-307- if (ret < 0)
--
drivers/pci/proc.c=313=static int proc_bus_pci_open(struct inode *inode, struct file *file)
--
drivers/pci/proc.c-319-
drivers/pci/proc.c:320: fpriv->mmap_state = pci_mmap_io;
drivers/pci/proc.c-321- fpriv->write_combine = 0;
--
drivers/pci/proc.c=338=static const struct proc_ops proc_bus_pci_ops = {
--
drivers/pci/proc.c-348- .proc_release = proc_bus_pci_release,
drivers/pci/proc.c:349: .proc_mmap = proc_bus_pci_mmap,
drivers/pci/proc.c-350-#ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA
]
|
| 76/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 75671
output: 36
thoughts: 33
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating PCI Device File Permissions**
Alright, a moment to pause and dig into something. My memory is pinging that `/proc/bus/pci/` files are dynamically generated, specifically by `pci_proc_attach_device`. So, that's the likely source of their permissions. The next step, naturally, is to check those permissions. I need to understand if there's any inherent vulnerability or misconfiguration there that I need to be aware of, especially given the kernel's role in the system. Let's see how these files are set up by default, and if there are any obvious red flags I should investigate. Time to dive into the code!
|
| 77/2 |
2026/08/31 00:59 |
tool |
grepper |
0m
Args: map[Expression:pci_proc_attach_device PathPrefix:drivers/pci/proc.c]
Results: map[Output:drivers/pci/proc.c=434=static int __pci_proc_attach_bus(struct pci_bus *bus)
--
drivers/pci/proc.c-460-
drivers/pci/proc.c:461:int pci_proc_attach_device(struct pci_dev *dev)
drivers/pci/proc.c-462-{
--
drivers/pci/proc.c=505=static int __init pci_proc_init(void)
--
drivers/pci/proc.c-517- for_each_pci_dev(dev)
drivers/pci/proc.c:518: pci_proc_attach_device(dev);
drivers/pci/proc.c-519- pci_unlock_rescan_remove();
]
|
| 78/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 76117
output: 36
thoughts: 14
Thoughts: Okay, let's dive into `pci_proc_attach_device`. I want to understand exactly how a PCI device gets integrated into the system's process space. I'm expecting this to be the crucial function that links the physical hardware with the software representation, probably involving memory mapping or I/O port assignments. I anticipate it will handle resource allocation β figuring out where this new device "lives" in the memory map and interrupt vectors. This is a critical point for device drivers; incorrect mapping, bad interrupts, and it's a non-functioning device. I bet the driver calls this, or calls something that calls this. I need to see if it includes any error handling. I'm especially interested in how it handles potential conflicts if another device already claims the same resources. It'll probably involve some kind of locking mechanism to ensure coherency. I need to trace this call, see the data it's passing, and what assumptions it's making about the PCI device. Okay, let's see what's what.
|
| 79/2 |
2026/08/31 00:59 |
tool |
read-file |
0m
Args: map[File:drivers/pci/proc.c FirstLine:461 LineCount:20]
Results: map[Contents: 461: int pci_proc_attach_device(struct pci_dev *dev)
462: {
463: struct pci_bus *bus = dev->bus;
464: struct proc_dir_entry *entry;
465: char name[16];
466: int ret;
467:
468: guard(mutex)(&pci_proc_lock);
469:
470: if (dev->procent)
471: return 0;
472:
473: ret = __pci_proc_attach_bus(bus);
474: if (ret)
475: return ret;
476:
477: sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
478: entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR,
479: bus->procdir, &proc_bus_pci_ops, dev);
480: if (!entry)
]
|
| 80/2 |
2026/08/31 00:59 |
llm |
expert |
0m
Model:
Tokens:
input: 76706
output: 36
thoughts: 73
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience:
**Analyzing File Permissions and Memory Mapping Possibilities**
Right, so that bit pattern, `S_IFREG | S_IRUGO | S_IWUSR`, instantly translates to `0644`. Standard file, read for everyone, writeable only for the owner. Basic stuff. But the "world-readable" bit immediately triggers a more nuanced question in my mind. The immediate question is: Can an unprivileged user actually *use* that world-readability to memory map the file? Itβs not a given. File permissions are one thing, system-level security constraints and capabilities are another. We know the permissions say yes. But thatβs not enough to be sure.
The obvious next step is to examine the `proc_bus_pci_mmap` code, specifically the security checks and access control logic it performs when a memory map request comes in. It's the point of interaction with the system, not the file permissions themselves. I need to refresh my memory about how the driver handles that. Itβs likely there's a check somewhere to see if the requesting user has the appropriate privileges or if they're attempting to mmap a device mapped file, which is often a more constrained operation than mmaping a regular file. I'll need to dig into the driver implementation details to confirm.
|
| 81/2 |
2026/08/31 01:00 |
tool |
read-file |
0m
Args: map[File:drivers/pci/proc.c FirstLine:258 LineCount:50]
Results: map[Contents: 258: static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
259: {
260: struct pci_dev *dev = pde_data(file_inode(file));
261: struct pci_filp_private *fpriv = file->private_data;
262: resource_size_t start, end;
263: int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM;
264:
265: if (!capable(CAP_SYS_RAWIO) ||
266: security_locked_down(LOCKDOWN_PCI_ACCESS))
267: return -EPERM;
268:
269: /* Skip devices with non-mappable BARs */
270: if (dev->non_mappable_bars)
271: return -EINVAL;
272:
273: if (fpriv->mmap_state == pci_mmap_io) {
274: if (!arch_can_pci_mmap_io())
275: return -EINVAL;
276: res_bit = IORESOURCE_IO;
277: }
278:
279: /* Make sure the caller is mapping a real resource for this device */
280: for (i = 0; i < PCI_STD_NUM_BARS; i++) {
281: if (dev->resource[i].flags & res_bit &&
282: pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
283: break;
284: }
285:
286: if (i >= PCI_STD_NUM_BARS)
287: return -ENODEV;
288:
289: if (fpriv->mmap_state == pci_mmap_mem &&
290: fpriv->write_combine) {
291: if (dev->resource[i].flags & IORESOURCE_PREFETCH)
292: write_combine = 1;
293: else
294: return -EINVAL;
295: }
296:
297: if (dev->resource[i].flags & IORESOURCE_MEM &&
298: iomem_is_exclusive(dev->resource[i].start))
299: return -EINVAL;
300:
301: pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
302:
303: /* Adjust vm_pgoff to be the offset within the resource */
304: vma->vm_pgoff -= start >> PAGE_SHIFT;
305: ret = pci_mmap_resource_range(dev, i, vma,
306: fpriv->mmap_state, write_combine);
307: if (ret < 0)
]
|
| 82/2 |
2026/08/31 01:00 |
llm |
expert |
0m
Model:
Tokens:
input: 77966
output: 41
thoughts: 181
Thoughts: **Analyzing PCI Access Restrictions**
Okay, here's what's running through my mind as I dissect this code snippet. First, that `if` statement⦠`if (!capable(CAP_SYS_RAWIO) || security_locked_down(LOCKDOWN_PCI_ACCESS)) return -EPERM;`... Ah, right. It's immediately evident that `CAP_SYS_RAWIO` is the key. The function, I presume, is checking for this capability within the *initial* user namespace. The `capable()` call is implicitly comparing `current_user_ns()` to `&init_user_ns` because it's checking against the global root. Therefore, possessing `CAP_SYS_RAWIO` in a nested user namespace *won't* circumvent this check. This is a crucial security measure. A user namespace root is strictly restricted. This access is guarded by the global root. I need to keep that in mind. Now, I should shift my attention to `sysfs`. Perhaps there are other avenues for control there. Specifically, I should look into the `pci_mmap_resource` function within `drivers/pci/pci-sysfs.c`.
|
| 83/2 |
2026/08/31 01:00 |
tool |
read-file |
0m
Args: map[File:drivers/pci/pci-sysfs.c FirstLine:1153 LineCount:30]
Results: map[Contents:1153: static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *attr,
1154: struct vm_area_struct *vma, int write_combine)
1155: {
1156: struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1157: int bar = (unsigned long)attr->private;
1158: enum pci_mmap_state mmap_type;
1159: int ret;
1160:
1161: ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
1162: if (ret)
1163: return ret;
1164:
1165: if (!pci_resource_is_mem(pdev, bar) &&
1166: !(pci_resource_is_io(pdev, bar) && arch_can_pci_mmap_io()))
1167: return -EIO;
1168:
1169: if (pci_resource_is_mem(pdev, bar) &&
1170: iomem_is_exclusive(pci_resource_start(pdev, bar)))
1171: return -EINVAL;
1172:
1173: if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS))
1174: return -EINVAL;
1175:
1176: mmap_type = pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
1177:
1178: return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
1179: }
1180:
1181: static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
1182: const struct bin_attribute *attr,
]
|
| 84/2 |
2026/08/31 01:00 |
llm |
expert |
0m
Model:
Tokens:
input: 79005
output: 35
thoughts: 112
Thoughts: **Analyzing the PCI Resource Mapping Security Vulnerability**
Okay, so I've been taking a look at this potential security issue, and it seems like the code doesn't explicitly check for the `capable(CAP_SYS_RAWIO)` capability. But, considering the context, that's not the critical factor here. We know the sysfs files in question are set to `0600` permissions, owned by root. This means only the global root user can even *open* those files in the first place.
Consequently, any attempt to map a PCI resource, in *any* scenario, demands global root privileges. That solidifies the conclusion: this supposed bug is completely inaccessible to processes running without those elevated permissions, including those within user namespaces. It looks like we're safe on this count.
Now, I should still explore this area thoroughly. Is there some other driver, some other way to achieve the goal? What about `/dev/mem`? I'll need to check the `mmap_mem` function in `drivers/char/mem.c` to see if there's anything there.
|
| 85/2 |
2026/08/31 01:00 |
tool |
read-file |
0m
Args: map[File:drivers/char/mem.c FirstLine:324 LineCount:30]
Results: map[Contents: 324:
325: static int mmap_mem_prepare(struct vm_area_desc *desc)
326: {
327: struct file *file = desc->file;
328: const size_t size = vma_desc_size(desc);
329: const phys_addr_t offset = (phys_addr_t)desc->pgoff << PAGE_SHIFT;
330:
331: /* Does it even fit in phys_addr_t? */
332: if (offset >> PAGE_SHIFT != desc->pgoff)
333: return -EINVAL;
334:
335: /* It's illegal to wrap around the end of the physical address space. */
336: if (offset + (phys_addr_t)size - 1 < offset)
337: return -EINVAL;
338:
339: if (!valid_mmap_phys_addr_range(desc->pgoff, size))
340: return -EINVAL;
341:
342: if (!private_mapping_ok(desc))
343: return -ENOSYS;
344:
345: if (!range_is_allowed(desc->pgoff, size))
346: return -EPERM;
347:
348: if (!phys_mem_access_prot_allowed(file, desc->pgoff, size,
349: &desc->page_prot))
350: return -EINVAL;
351:
352: desc->page_prot = phys_mem_access_prot(file, desc->pgoff,
353: size,
]
|
| 86/2 |
2026/08/31 01:00 |
llm |
expert |
0m
Model:
Tokens:
input: 79874
output: 37
thoughts: 40
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as someone familiar with system internals:
**Investigating Memory Access Restrictions**
Alright, let's see... `/dev/mem` has permissions `0640` and is owned by `root:kmem`. That immediately tells me it's restricted, not just wide open for anyone to mess with. Good. That's as expected, it's a privileged interface.
Now, I need to check something related. What about the user-space I/O driver (`drivers/uio/uio.c`)? I have some ideas in mind that I want to test.
|
| 87/2 |
2026/08/31 01:00 |
tool |
read-file |
0m
Args: map[File:drivers/uio/uio.c FirstLine:741 LineCount:30]
Results: map[Contents: 741:
742: static int uio_mmap_physical(struct vm_area_struct *vma)
743: {
744: struct uio_device *idev = vma->vm_private_data;
745: int mi = uio_find_mem_index(vma);
746: struct uio_mem *mem;
747:
748: if (mi < 0)
749: return -EINVAL;
750: mem = idev->info->mem + mi;
751:
752: if (mem->addr & ~PAGE_MASK)
753: return -ENODEV;
754: if (vma->vm_end - vma->vm_start > mem->size)
755: return -EINVAL;
756:
757: vma->vm_ops = &uio_physical_vm_ops;
758: if (idev->info->mem[mi].memtype == UIO_MEM_PHYS)
759: vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
760:
761: /*
762: * We cannot use the vm_iomap_memory() helper here,
763: * because vma->vm_pgoff is the map index we looked
764: * up above in uio_find_mem_index(), rather than an
765: * actual page offset into the mmap.
766: *
767: * So we just do the physical mmap without a page
768: * offset.
769: */
770: return remap_pfn_range(vma,
]
|
| 88/2 |
2026/08/31 01:00 |
llm |
expert |
0m
Model:
Tokens:
input: 80589
output: 66
thoughts: 2054
Thoughts: **Analysis of the PCI/UIO Access Restriction Bug**
Okay, let's break this down from a security perspective. My initial thought process focused on how UIO devices are created (`uio_get_minor`), and how they, along with devices in drivers like `fpga/dfl-afu-main.c` and `cdx/cdx.c`, are created and their permissions. They're all restricted to root, right? Default `0600` permissions. So, the direct attack vector requiring root seemed obvious.
The core of the issue is that `WARN_ON_ONCE` returning `-ENOMEM`. The crucial aspect here is *no* memory corruption or panic (unless specifically configured), and *no* information leak. This immediately screams "not exploitable" from a security perspective. It's essentially a non-fatal error.
I systematically looked at different attack surfaces: no denial-of-service, since it's just a warning. User processes? Nope, requires root to open the PCI resource or sysfs entry. User namespaces? Global root owns the sysfs files, so `CAP_DAC_OVERRIDE` is denied. No VM guests, no host, no network, no remote, no peripherals, no filesystem triggers.
The filesystem idea was a good one, but no filesystem I'm aware of uses `generic_access_phys`. They all have their own `vm_ops` or use generic ones. The peripheral device scenario seemed interesting, but even with a new device, you still need root to open it. Udev does read sysfs, but it doesn't mmap with `PROT_NONE` and use `/proc/self/mem`.
The guest kernel trigger was an irrelevant concern as the underlying issue only concerns the host kernel and is not triggered by guests, and, therefore, is not host kernel exploitable by a guest.
Double-checking, could `generic_access_phys` be reached indirectly, like via `access_process_vm` used by `ptrace`? It might be triggered by `ptrace`, especially with `PTRACE_PEEKTEXT` or `PTRACE_POKETEXT`. However, the process doing the `ptrace` needs the same uid/gid or root.
The crucial thought was: could a root process open, mmap, and drop privileges, then be triggered by an unprivileged process? This is possible, but it is explicitly set up by root, not a vulnerability, making this a non-issue. Even if root passes the fd to an unprivileged process, the unprivileged process cannot use it to trigger the warning because no well-known daemon or binary passes such fds to other unprivileged processes. So: still not exploitable.
Then I reviewed the `WARN_ON_ONCE` in `ioremap_prot` and the lightbulb went on. The check looks for `PTE_USER`, but `ioremap_prot` maps into the kernel space! This warning, introduced in commit `437b38c5b26c`, tries to ensure `ioremap_prot` gets a user protection (like `pgprot_noncached`) and not a kernel protection. This is a false positive warning! With `PROT_NONE` the protection will be `PAGE_NONE` which doesn't have `PTE_USER`, because the `PTE_USER` means accessible by user, and `PROT_NONE` means not accessible! So, `ioremap_prot` warns, thinking the protection is a kernel protection and that it should have `PTE_USER`, but it does not, so it is just a false positive! It returns `NULL`, causing `generic_access_phys` to return `-ENOMEM`, failing a read of `/proc/self/mem`, which is designed to work as a means to circumvent VMA protection.
The arm64 kernel thinks that `PAGE_NONE` is an invalid user protection, but it's not and causes the warning.
So, in the end:
* Exploitable: No.
* Denial of Service: No.
* Unprivileged Access: No.
* User Namespace: No.
* VM Guest Trigger: No.
* VM Host Trigger: No.
* Network Trigger: No.
* Remote Trigger: No.
* Peripheral Trigger: No.
* Filesystem Trigger: No.
Functional, yes, but not a security bug.
|
| 89/2 |
2026/08/31 01:00 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 90/2 |
2026/08/31 01:00 |
llm |
expert |
0m
Model:
Tokens:
input: 83730
output: 703
thoughts: 574
Thoughts: **My Assessment of the `WARN_ON_ONCE` Bug in `ioremap_prot` on arm64**
Okay, the results are in. Time to solidify my findings in a concise summary. This `WARN_ON_ONCE` bug within `ioremap_prot` on arm64 is, as I suspected, a false positive. It arises when a user-space process maps a PCI resource or other device memory with `PROT_NONE`, resulting in a VMA with `vm_page_prot` set to `PAGE_NONE`. The problem is that on arm64, `PAGE_NONE` doesn't have the `PTE_USER` bit set, because it's meant to grant absolutely no access to user space.
When a user, for whatever reason, then tries to read from this memory region via `/proc/self/mem`, the kernel ends up calling `generic_access_phys` to handle the request. This function pulls the protection flags, which, of course, are `PAGE_NONE`, and passes them to `ioremap_prot` to temporarily map that physical memory into the kernel's address space.
The issue is that `ioremap_prot` has this sanity check β a `WARN_ON_ONCE` β that's meant to ensure it's only called with user-space protections, specifically looking for the `PTE_USER` bit. But it completely misses the fact that `PAGE_NONE` *is* a legitimate user-space protection, even if it lacks that particular bit. So, the warning triggers, `ioremap_prot` ends up returning `NULL`, and that's it; a simple `-ENOMEM` error is the result.
Now, the important part: this isn't a security vulnerability. Not even close.
First, it's not exploitable. It's a harmless warning; a false positive that just returns an error. No memory corruption, no info leaks, nothing that could be leveraged.
Second, it's not a denial-of-service risk. The `WARN_ON_ONCE` won't crash the kernel in a production build, just print a message and carry on.
Third, the entire thing hinges on root privileges. `generic_access_phys` is only used by a few specific drivers β PCI sysfs/procfs, `/dev/mem`, UIO devices, that sort of thing. All of those device nodes and sysfs files are protected. A standard user can't create that kind of VMA to begin with.
Finally, user namespaces don't help. Even if an unprivileged user created a user namespace and mounted sysfs, those PCI resources are owned by the global root. The user namespace trick wouldn't work because the global root isn't mapped, so the kernel won't grant `CAP_DAC_OVERRIDE`.
So, in conclusion, this bug has no security implications and is not something that an unprivileged user could trigger. The warning is simply a misinterpretation of a valid, albeit unusual, memory protection.
|
| 91/1 |
2026/08/31 01:00 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug**
The bug is a false positive `WARN_ON_ONCE` in the arm64 implementation of
`ioremap_prot`.
When a user maps a PCI resource (or other specific device memory) with
`PROT_NONE`, the resulting Virtual Memory Area (VMA) has its `vm_page_prot` set
to `PAGE_NONE`. On the arm64 architecture, `PAGE_NONE` does not have the
`PTE_USER` bit set, as it grants no access permissions to user-space.
If a process then attempts to read from this `PROT_NONE` VMA via
`/proc/self/mem` (which is allowed and bypasses VMA access permissions), the
kernel uses `generic_access_phys()` to perform the read. `generic_access_phys()`
extracts the `pgprot_t` from the VMA (which is `PAGE_NONE`) and passes it to
`ioremap_prot()` to temporarily map the physical memory into the kernel address
space to perform the read.
`ioremap_prot()` contains a sanity check: `WARN_ON_ONCE(!(user_prot_val &
PTE_USER))`. This was introduced to ensure that `ioremap_prot()` is only called
with user-space protections and not accidentally with kernel protections.
However, this check fails to account for `PAGE_NONE`, which is a valid
user-space protection but intentionally lacks the `PTE_USER` bit. This triggers
the warning and causes `ioremap_prot()` to return `NULL`, resulting in an
`-ENOMEM` error for the read operation.
**Security Impact Assessment**
This bug has no security impact for the following reasons:
1. **Not Exploitable:** The bug is merely a false positive warning that results
in a failed read operation (`-ENOMEM`). It does not cause memory corruption,
use-after-free, out-of-bounds access, or information leaks.
2. **No Denial of Service:** The `WARN_ON_ONCE` macro does not panic or halt the
kernel in standard production builds. It simply logs a stack trace and allows
the kernel to continue executing safely.
3. **Requires Global Root Privileges:** The `generic_access_phys()` function is
only utilized by a handful of specific drivers (e.g., PCI sysfs/procfs
resources, `/dev/mem`, UIO devices). All of these device nodes and sysfs files
are strictly restricted to root (e.g., `0600` root:root). An unprivileged user
cannot open these files to create the necessary VMA in the first place.
4. **Not Accessible from User Namespaces:** Even if an unprivileged user creates
a new user namespace and mounts `sysfs`, the PCI resource files remain owned by
the global root (`GLOBAL_ROOT_UID`). Because the global root is not mapped
within an unprivileged user namespace, the kernel correctly denies
`CAP_DAC_OVERRIDE`. Thus, the user namespace root cannot bypass the file
permissions to open the PCI resources.
In conclusion, this is a functional edge-case bug affecting the ability to read
`PROT_NONE` device mappings via `/proc/pid/mem` on arm64, but it poses no
security risk and is completely unreachable by unprivileged attackers.]
|