From b639bf5c4277b7f828b3fcbdbf94bad5a4d20060 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 27 May 2026 10:35:39 +0200 Subject: [PATCH] lib/fileutils: add ul_open_no_symlinks() Add a helper that opens a path rejecting symlinks at any component, not just the last one. Uses openat2(RESOLVE_NO_SYMLINKS) when available (Linux >= 5.6), falls back to open(O_NOFOLLOW). Signed-off-by: Karel Zak (cherry picked from commit e01e38b24346a21f1d01498c265486a12c009e61) Upstream: https://github.com/util-linux/util-linux/commit/b639bf5c4277b7f828b3fcbdbf94bad5a4d20060 CVE: CVE-2026-53613 Signed-off-by: Thomas Perale --- configure.ac | 1 + include/fileutils.h | 2 ++ lib/fileutils.c | 24 ++++++++++++++++++++++++ meson.build | 1 + 4 files changed, 28 insertions(+) diff --git a/configure.ac b/configure.ac index 3bbc94488b9..2d9388a3cfd 100644 --- a/configure.ac +++ b/configure.ac @@ -345,6 +345,7 @@ AC_CHECK_HEADERS([ \ linux/kcmp.h \ linux/net_namespace.h \ linux/nsfs.h \ + linux/openat2.h \ linux/pr.h \ linux/raw.h \ linux/securebits.h \ diff --git a/include/fileutils.h b/include/fileutils.h index 6fc93d0db82..996e183221d 100644 --- a/include/fileutils.h +++ b/include/fileutils.h @@ -61,6 +61,8 @@ static inline int is_same_inode(const int fd, const struct stat *st) return 1; } +extern int ul_open_no_symlinks(const char *path, int flags, mode_t mode); + extern int dup_fd_cloexec(int oldfd, int lowfd); extern unsigned int get_fd_tabsize(void); diff --git a/lib/fileutils.c b/lib/fileutils.c index b7acae43082..a9c2022be5f 100644 --- a/lib/fileutils.c +++ b/lib/fileutils.c @@ -11,7 +11,14 @@ #include #include #include +#include #include +#include +#include + +#ifdef HAVE_LINUX_OPENAT2_H +# include +#endif #include "c.h" #include "all-io.h" @@ -343,3 +350,20 @@ char *ul_basename(char *path) return p; } + +int ul_open_no_symlinks(const char *path, int flags, mode_t mode) +{ +#if defined(SYS_openat2) && defined(RESOLVE_NO_SYMLINKS) + struct open_how how = { + .flags = (__u64) flags, + .mode = (__u64) mode, + .resolve = RESOLVE_NO_SYMLINKS, + }; + int fd = syscall(SYS_openat2, AT_FDCWD, path, &how, sizeof(how)); + + /* only fall back to O_NOFOLLOW if the syscall is unavailable */ + if (fd >= 0 || errno != ENOSYS) + return fd; +#endif + return open(path, flags | O_NOFOLLOW, mode); +} diff --git a/meson.build b/meson.build index c79939c0525..7b2b9ee71eb 100644 --- a/meson.build +++ b/meson.build @@ -198,6 +198,7 @@ headers = ''' linux/kcmp.h linux/net_namespace.h linux/nsfs.h + linux/openat2.h linux/mount.h linux/pr.h linux/securebits.h