openat2() with O_NOFOLLOW failed on arm64
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 (closed)-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.
-
Compile and run a c program calling
openat2()withO_NOFOLLOW|O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_PATH -
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 -
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, 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