[libc-commits] [libc] [libc] Reject unsupported flags in fchmodat with ENOTSUP (PR #223165)
Aman Maurya via libc-commits
libc-commits at lists.llvm.org
Sat Sep 12 11:54:03 PDT 2026
https://github.com/amanmaurya92 created https://github.com/llvm/llvm-project/pull/223165
### Summary
The Linux kernel's `SYS_fchmodat` system call only takes 3 arguments (`dirfd`, `pathname`, `mode`), but our syscall wrapper was passing a 4th `flags` argument that the kernel simply dropped.
This PR cleans up the wrapper to pass the 3 arguments expected by the kernel, and adds a check to `fchmodat` to return `-1` with `ENOTSUP` whenever non-zero flags (like `AT_SYMLINK_NOFOLLOW`) are passed.
Fixes #223072
### Changes
- Cleaned up `linux_syscalls::fchmodat` to pass only 3 arguments to `SYS_fchmodat`.
- Added a check in `fchmodat` entrypoint to set `libc_errno = ENOTSUP` and return `-1` if `flags != 0`.
- Added `libc.hdr.errno_macros` to the CMake dependencies for `fchmodat` and its unit test.
- Added tests in `fchmodat_test.cpp` to verify that passing non-zero flags fails with `ENOTSUP`.
### Testing
- Ran `clang-format` on touched files.
- Built and ran hermetic tests: `libc.test.src.sys.stat.fchmodat_test` (all 3 tests pass: `ChangeAndOpen`, `NonExistentFile`, `UnsupportedFlags`).
>From 2483b9f02f448859f588a0af02162cb7f969871c Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Sat, 12 Sep 2026 23:41:12 +0530
Subject: [PATCH] [libc] Reject unsupported flags in fchmodat with ENOTSUP
The Linux kernel syscall SYS_fchmodat takes only 3 arguments:
(int dirfd, const char *pathname, mode_t mode).
Previously, linux_syscalls::fchmodat accepted a flags argument and
passed it to SYS_fchmodat, which was silently ignored by the kernel.
This patch:
- Updates linux_syscalls::fchmodat to pass only the 3 arguments
expected by the Linux kernel.
- Updates the fchmodat entrypoint to return -1 and set errno to
ENOTSUP when non-zero flags (e.g., AT_SYMLINK_NOFOLLOW) are passed.
- Adds unit tests to verify that unsupported flags fail with ENOTSUP.
Fixes #223072
---
.../__support/OSUtil/linux/syscall_wrappers/fchmodat.h | 5 ++---
libc/src/sys/stat/linux/CMakeLists.txt | 1 +
libc/src/sys/stat/linux/fchmodat.cpp | 7 ++++++-
libc/test/src/sys/stat/CMakeLists.txt | 1 +
libc/test/src/sys/stat/fchmodat_test.cpp | 10 ++++++++++
5 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fchmodat.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fchmodat.h
index 7b8ee1a079b99..b62a040e9fe54 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/fchmodat.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fchmodat.h
@@ -24,9 +24,8 @@
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
-LIBC_INLINE ErrorOr<int> fchmodat(int fd, const char *path, mode_t mode,
- int flags) {
- int ret = syscall_impl<int>(SYS_fchmodat, fd, path, mode, flags);
+LIBC_INLINE ErrorOr<int> fchmodat(int fd, const char *path, mode_t mode) {
+ int ret = syscall_impl<int>(SYS_fchmodat, fd, path, mode);
if (ret < 0)
return Error(-ret);
return ret;
diff --git a/libc/src/sys/stat/linux/CMakeLists.txt b/libc/src/sys/stat/linux/CMakeLists.txt
index 52ae4566a2b87..953e18be159d8 100644
--- a/libc/src/sys/stat/linux/CMakeLists.txt
+++ b/libc/src/sys/stat/linux/CMakeLists.txt
@@ -30,6 +30,7 @@ add_entrypoint_object(
HDRS
../fchmodat.h
DEPENDS
+ libc.hdr.errno_macros
libc.hdr.types.mode_t
libc.src.__support.OSUtil.linux.syscall_wrappers.fchmodat
libc.src.errno.errno
diff --git a/libc/src/sys/stat/linux/fchmodat.cpp b/libc/src/sys/stat/linux/fchmodat.cpp
index 38e02b5d61617..29bee319ed0ad 100644
--- a/libc/src/sys/stat/linux/fchmodat.cpp
+++ b/libc/src/sys/stat/linux/fchmodat.cpp
@@ -8,6 +8,7 @@
#include "src/sys/stat/fchmodat.h"
+#include "hdr/errno_macros.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/fchmodat.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
@@ -17,7 +18,11 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, fchmodat,
(int dirfd, const char *path, mode_t mode, int flags)) {
- auto result = linux_syscalls::fchmodat(dirfd, path, mode, flags);
+ if (flags != 0) {
+ libc_errno = ENOTSUP;
+ return -1;
+ }
+ auto result = linux_syscalls::fchmodat(dirfd, path, mode);
if (!result) {
libc_errno = result.error();
return -1;
diff --git a/libc/test/src/sys/stat/CMakeLists.txt b/libc/test/src/sys/stat/CMakeLists.txt
index 3e87a11b05e6e..ec083d5372971 100644
--- a/libc/test/src/sys/stat/CMakeLists.txt
+++ b/libc/test/src/sys/stat/CMakeLists.txt
@@ -28,6 +28,7 @@ add_libc_test(
SRCS
fchmodat_test.cpp
DEPENDS
+ libc.hdr.errno_macros
libc.hdr.fcntl_macros
libc.hdr.sys_stat_macros
libc.hdr.types.struct_stat
diff --git a/libc/test/src/sys/stat/fchmodat_test.cpp b/libc/test/src/sys/stat/fchmodat_test.cpp
index 8a1a9379aca71..868cdb2bc817a 100644
--- a/libc/test/src/sys/stat/fchmodat_test.cpp
+++ b/libc/test/src/sys/stat/fchmodat_test.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
#include "hdr/fcntl_macros.h"
#include "hdr/sys_stat_macros.h"
#include "hdr/types/struct_stat.h"
@@ -65,3 +66,12 @@ TEST_F(LlvmLibcFchmodatTest, NonExistentFile) {
LIBC_NAMESPACE::fchmodat(AT_FDCWD, "non-existent-file", S_IRUSR, 0),
Fails(ENOENT));
}
+
+TEST_F(LlvmLibcFchmodatTest, UnsupportedFlags) {
+ ASSERT_THAT(LIBC_NAMESPACE::fchmodat(AT_FDCWD, "non-existent-file", S_IRUSR,
+ AT_SYMLINK_NOFOLLOW),
+ Fails(ENOTSUP));
+ ASSERT_THAT(
+ LIBC_NAMESPACE::fchmodat(AT_FDCWD, "non-existent-file", S_IRUSR, -1),
+ Fails(ENOTSUP));
+}
More information about the libc-commits
mailing list