linux-user: openat2() bypasses absolute-path prefix rewriting under -L
## Host environment
- Operating system:
EndeavourOS x86_64
- OS/kernel version:
Linux ArchLinux-desktop 6.19.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 04 Mar 2026\
18:25:08 +0000 x86_64 GNU/Linux\\
- Architecture:
ALL
- QEMU flavor:
qemu-user(I tested qemu-x86_64 and riscv64)
- QEMU version:
10.2.1
- QEMU command line:
qemu-x86_64 -L /tmp/qemu-openat2-root /tmp/test-02 /tmp/qemu-openat2-check.txt
## Emulated/Virtualized environment
QEMU-USER
- Architecture:ALL
## Description of problem
openat2() in linux-user appears to bypass the absolute-path prefix rewriting that openat() uses under -L or QEMU_LD_PREFIX.
I can reproduce this locally with qemu-x86_64 10.2.1, and the source locations below match the 10.2.50 code layout.
Relevant source locations: linux-user/syscall.c:8806 linux-user/syscall.c:8851 util/path.c:36
At linux-user/syscall.c:8806, openat() goes through path(pathname). At linux-user/syscall.c:8851, openat2() passes pathname directly to safe_openat2(). At util/path.c:36, path() only rewrites absolute paths.
This creates a guest-visible split: openat("/tmp/file") resolves inside the configured guest prefix, while openat2("/tmp/file") still resolves against the host root.
Observed output under qemu-x86_64 -L /tmp/qemu-openat2-root: openat ok "guest-file" openat2 ok "host-file"
Expected behavior: openat() and openat2() should see the same guest-root-prefixed absolute path.
Actual behavior: openat() opens the guest-root file, while openat2() opens the host-root file.
This is a direct guest-visible filesystem semantic mismatch.
## Steps to reproduce
1. Minimal reproducer file: tests/02_openat2_prefix_escape.c(I upload it)
[02_openat2_prefix_escape.c](/uploads/8450081e97d3d58dc08de17077c73dc7/02_openat2_prefix_escape.c)
2. Build it with `gcc -O2 -Wall -Wextra -static -o /tmp/test-02 tests/02_openat2_prefix_escape.c`.
3. Prepare the files with `mkdir -p /tmp/qemu-openat2-root/tmp`, `printf 'host-file\n' > /tmp/qemu-openat2-check.txt`, and `printf 'guest-file\n' > /tmp/qemu-openat2-root/tmp/qemu-openat2-check.txt`.
4. Run on native Linux with `/tmp/test-02 /tmp/qemu-openat2-check.txt`.
5. Run under QEMU without prefix rewriting using `qemu-x86_64 /tmp/test-02 /tmp/qemu-openat2-check.txt`.
6. Run under QEMU with prefix rewriting using `qemu-x86_64 -L /tmp/qemu-openat2-root /tmp/test-02 /tmp/qemu-openat2-check.txt`.
7. Compare the data returned by openat() and openat2().
## Additional information
I am a student from Xidian University.
I quickly discovered some bugs through Codex. I know that QEMU does not accept content directly generated by AI, so I have verified them and confirm that this bug is **valid**.
code:
```c
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/openat2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
static void print_result(const char *tag, int fd)
{
char buf[128];
ssize_t n;
if (fd < 0) {
printf("%s errno=%d (%s)\n", tag, errno, strerror(errno));
return;
}
n = read(fd, buf, sizeof(buf) - 1);
if (n < 0) {
printf("%s read_errno=%d (%s)\n", tag, errno, strerror(errno));
close(fd);
return;
}
buf[n] = '\0';
close(fd);
printf("%s ok \"%s\"\n", tag, buf);
}
int main(int argc, char **argv)
{
struct open_how how;
const char *path;
int fd;
if (argc != 2) {
fprintf(stderr, "usage: %s <absolute-path>\n", argv[0]);
return 2;
}
path = argv[1];
memset(&how, 0, sizeof(how));
how.flags = O_RDONLY;
fd = openat(AT_FDCWD, path, O_RDONLY);
print_result("openat", fd);
fd = syscall(SYS_openat2, AT_FDCWD, path, &how, sizeof(how));
print_result("openat2", fd);
return 0;
}
```
issue