openat2() with O_NOFOLLOW failed on arm64
<!--This is the upstream QEMU issue tracker.
If you are able to, it will greatly facilitate bug triage if you attempt
to reproduce the problem with the latest qemu.git master built from
source. See https://www.qemu.org/download/#source for instructions on
how to do this.
QEMU generally supports the last two releases advertised on
https://www.qemu.org/. Problems with distro-packaged versions of QEMU
older than this should be reported to the distribution instead.
See https://www.qemu.org/contribute/report-a-bug/ for additional
guidance.
If this is a security issue, please consult
https://www.qemu.org/contribute/security-process/-->
## Host environment
- Operating system:
Ubuntu 24.04.3 LTS
- OS/kernel version:
Linux desktop 6.8.0-90-generic # 91-Ubuntu SMP PREEMPT_DYNAMIC Tue Nov 18 14:14:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
- Architecture:
x86_64
- QEMU flavor:
qemu-aarch64
- QEMU version:
binfmt/8bf932d qemu/v10.0.4 go/1.23.12
- QEMU command line:
I use QEMU through Docker(tonistiigi/binfmt)
- OS/kernel version:
Linux ae1bdda701eb 6.8.0-90-generic #91-Ubuntu SMP PREEMPT_DYNAMIC Tue Nov 18 14:14:30 UTC 2025 aarch64 GNU/Linux
- Architecture:
arm64
## Description of problem
`openat2()` with `O_NOFOLLOW` failed on arm64. Looks like `O_NOFOLLOW` got translated wrongly to `O_LARGEFILE`.
## Steps to reproduce
The problem occurred in Github Action using x86_64 to run arm64. I was able to reproduce the problem on my local Ubuntu desktop.
1. Compile and run a c program calling `openat2()` with `O_NOFOLLOW|O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_PATH`
2. QEMU_STRACE=1 shows
```
openat2(-100,"example_dir/",{O_RDONLY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC|O_PATH,RESOLVE_BENEATH},24) = -1 errno=22 (Invalid argument) 249
```
3. Host Env using strace shows
```
$ sudo strace -p <docker_pid> -f -e trace=openat,open,openat2
[pid 85515] openat2(AT_FDCWD, "example_dir/", {flags=O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY, resolve=RESOLVE_BENEATH}, 24) = -1 EINVAL (Invalid argument)
```
## Additional information
```
$ cat mkdirat_openat2.c
#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/openat2.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
/* Wrapper for openat2 syscall */
static int openat2_wrapper(int dirfd, const char *path,
struct open_how *how, size_t size)
{
return syscall(SYS_openat2, dirfd, path, how, size);
}
int main(void)
{
const char *dirname = "example_dir";
const char *dirname2 = "example_dir/";
/* 1. Create directory using mkdirat */
if (mkdirat(AT_FDCWD, dirname, 0755) == -1) {
if (errno != EEXIST) {
perror("mkdirat");
return 1;
}
}
/* 2. Open directory using openat2 */
struct open_how how = {
.flags = O_NOFOLLOW|O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_PATH,
.mode = 0,
.resolve = RESOLVE_BENEATH
};
printf("Octal of O_NOFOLLOW: %o\n", O_NOFOLLOW);
printf("Octal of O_LARGEFILE: %o\n", O_LARGEFILE);
int dfd = openat2_wrapper(
AT_FDCWD,
dirname2,
&how,
sizeof(how)
);
if (dfd == -1) {
fprintf(stderr, "openat2 failed: errno=%d (%s)\n",
errno, strerror(errno));
return 1;
}
printf("Directory opened successfully, fd = %d\n", dfd);
close(dfd);
return 0;
}
```
```
# Guest Env
$ docker run --platform linux/arm64 -v /tmp/code:/tmp/code --rm -it redhat/ubi10-minimal:latest sh
sh-5.2# gcc -Wall -Wextra -O2 mkdirat_openat2.c -o mkdirat_openat2
sh-5.2# QEMU_STRACE=1 ./mkdirat_openat2
...
255 write(1,0x4212a0,28)Octal of O_NOFOLLOW: 100000
= 28
255 write(1,0x4212a0,24)Octal of O_LARGEFILE: 0
= 24
openat2(-100,"example_dir/",{O_RDONLY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC|O_PATH,RESOLVE_BENEATH},24) = -1 errno=22 (Invalid argument) 249 write(2,0x13bff9b8,44)openat2 failed: errno=22 (Invalid argument)
```
```
# Host Env
$ sudo strace -p <docker_pid> -f -e trace=openat,open,openat2
[pid 85515] openat2(AT_FDCWD, "example_dir/", {flags=O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY, resolve=RESOLVE_BENEATH}, 24) = -1 EINVAL (Invalid argument)
```
Original error in Github Action:
New `tar` uses `openat2` to addresses [CVE-2025-45582](https://github.com/advisories/GHSA-f93m-9mq4-2fjj "CVE-2025-45582"), and the syscall failed in QEMU (host: x86_64, guest: arm64) .\
Looks like calling `openat2` with `O_NOFOLLOW` is the root cause.
https://github.com/trinodb/trino/pull/27867#issuecomment-3718768640
https://gist.github.com/oneonestar/d6d86dbe2508e90658860c8bb049fe7c
<!--The line below ensures that proper tags are added to the issue.
Please do not remove it.-->
issue