[libc-commits] [libc] [libc] Implement close_range in unistd (PR #236913)
Aman Maurya via libc-commits
libc-commits at lists.llvm.org
Fri Oct 30 13:55:26 PDT 2026
https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/236913
>From fd60365d6f98ffb53d4c1b50e6edfe67c09a0f00 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Fri, 30 Oct 2026 01:39:38 +0530
Subject: [PATCH] [libc] Implement close_range in unistd
This patch implements the close_range function in LLVM libc as standardized in POSIX.1-2024 and supported in Linux kernel 5.9+.
Fixes #236896.
---
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/arm/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
.../llvm-libc-macros/linux/unistd-macros.h | 4 +-
libc/include/sys/syscall.h.def | 4 +
libc/include/unistd.yaml | 12 ++
.../linux/syscall_wrappers/CMakeLists.txt | 12 ++
.../linux/syscall_wrappers/close_range.h | 34 ++++++
libc/src/unistd/CMakeLists.txt | 7 ++
libc/src/unistd/close_range.h | 25 +++++
libc/src/unistd/linux/CMakeLists.txt | 13 +++
libc/src/unistd/linux/close_range.cpp | 34 ++++++
libc/test/src/unistd/CMakeLists.txt | 19 ++++
libc/test/src/unistd/close_range_test.cpp | 106 ++++++++++++++++++
libc/utils/docgen/unistd.yaml | 2 +
16 files changed, 275 insertions(+), 1 deletion(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/close_range.h
create mode 100644 libc/src/unistd/close_range.h
create mode 100644 libc/src/unistd/linux/close_range.cpp
create mode 100644 libc/test/src/unistd/close_range_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 0283f213be9a60..fe325038c6c98e 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -457,6 +457,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.chown
libc.src.unistd.chroot
libc.src.unistd.close
+ libc.src.unistd.close_range
libc.src.unistd.dup
libc.src.unistd.dup2
libc.src.unistd.dup3
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 3da362d8d0ccc5..9341a929c6a6a5 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -255,6 +255,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.chown
libc.src.unistd.chroot
libc.src.unistd.close
+ libc.src.unistd.close_range
libc.src.unistd.dup
libc.src.unistd.dup2
libc.src.unistd.dup3
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index b37cb66b7e19d0..42d7ac25fbcd68 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -483,6 +483,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.chown
libc.src.unistd.chroot
libc.src.unistd.close
+ libc.src.unistd.close_range
libc.src.unistd.dup
libc.src.unistd.dup2
libc.src.unistd.dup3
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 429300170df533..e7a5b251143c01 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -503,6 +503,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.chown
libc.src.unistd.chroot
libc.src.unistd.close
+ libc.src.unistd.close_range
libc.src.unistd.dup
libc.src.unistd.dup2
libc.src.unistd.dup3
diff --git a/libc/include/llvm-libc-macros/linux/unistd-macros.h b/libc/include/llvm-libc-macros/linux/unistd-macros.h
index e14d44960de0e5..74b1232467ba25 100644
--- a/libc/include/llvm-libc-macros/linux/unistd-macros.h
+++ b/libc/include/llvm-libc-macros/linux/unistd-macros.h
@@ -14,6 +14,8 @@
#ifndef LLVM_LIBC_MACROS_LINUX_UNISTD_MACROS_H
#define LLVM_LIBC_MACROS_LINUX_UNISTD_MACROS_H
+#include <linux/close_range.h>
+
// Values for mode argument to the access(...) function.
#define F_OK 0
#define X_OK 1
@@ -112,6 +114,6 @@
} while (__result == -1L && errno == EINTR); \
__result; \
}))
-#endif
+#endif // TEMP_FAILURE_RETRY
#endif // LLVM_LIBC_MACROS_LINUX_UNISTD_MACROS_H
diff --git a/libc/include/sys/syscall.h.def b/libc/include/sys/syscall.h.def
index f3670a234c638f..a034d5ec5d35e8 100644
--- a/libc/include/sys/syscall.h.def
+++ b/libc/include/sys/syscall.h.def
@@ -209,6 +209,10 @@
#define SYS_close __NR_close
#endif
+#ifdef __NR_close_range
+#define SYS_close_range __NR_close_range
+#endif
+
#ifdef __NR_cmpxchg_badaddr
#define SYS_cmpxchg_badaddr __NR_cmpxchg_badaddr
#endif
diff --git a/libc/include/unistd.yaml b/libc/include/unistd.yaml
index af8fab19c94307..c42a4bd738b5af 100644
--- a/libc/include/unistd.yaml
+++ b/libc/include/unistd.yaml
@@ -96,6 +96,10 @@ macros:
macro_header: unistd-macros.h
- macro_name: F_TEST
macro_header: unistd-macros.h
+ - macro_name: CLOSE_RANGE_UNSHARE
+ macro_header: unistd-macros.h
+ - macro_name: CLOSE_RANGE_CLOEXEC
+ macro_header: unistd-macros.h
types:
- type_name: uid_t
- type_name: gid_t
@@ -185,6 +189,14 @@ functions:
return_type: int
arguments:
- type: int
+ - name: close_range
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: unsigned int
+ - type: unsigned int
+ - type: unsigned int
- name: dup
standards:
- posix
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index e682daff23fb5e..b6508838132d10 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -1254,6 +1254,18 @@ add_header_library(
libc.include.sys_syscall
)
+add_header_library(
+ close_range
+ HDRS
+ close_range.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
add_header_library(
dup
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/close_range.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/close_range.h
new file mode 100644
index 00000000000000..e95affc505e614
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/close_range.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for close_range.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_CLOSE_RANGE_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_CLOSE_RANGE_H
+
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> close_range(unsigned int first, unsigned int last,
+ unsigned int flags) {
+ return syscall_checked<int>(SYS_close_range, first, last, flags);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSCALL_WRAPPERS_CLOSE_RANGE_H
diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt
index 14b59190cb2db4..909f3bbc7ccf93 100644
--- a/libc/src/unistd/CMakeLists.txt
+++ b/libc/src/unistd/CMakeLists.txt
@@ -77,6 +77,13 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.close
)
+add_entrypoint_object(
+ close_range
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.close_range
+)
+
add_entrypoint_object(
dup
ALIAS
diff --git a/libc/src/unistd/close_range.h b/libc/src/unistd/close_range.h
new file mode 100644
index 00000000000000..673d13bcc9ef25
--- /dev/null
+++ b/libc/src/unistd/close_range.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for close_range.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UNISTD_CLOSE_RANGE_H
+#define LLVM_LIBC_SRC_UNISTD_CLOSE_RANGE_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int close_range(unsigned int first, unsigned int last, unsigned int flags);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UNISTD_CLOSE_RANGE_H
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 14930948183757..613708c1e6c32e 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -70,6 +70,19 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ close_range
+ SRCS
+ close_range.cpp
+ HDRS
+ ../close_range.h
+ DEPENDS
+ libc.src.__support.OSUtil.linux.syscall_wrappers.close_range
+ libc.src.__support.common
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
+)
+
add_entrypoint_object(
dup
SRCS
diff --git a/libc/src/unistd/linux/close_range.cpp b/libc/src/unistd/linux/close_range.cpp
new file mode 100644
index 00000000000000..8e1bc1ececd432
--- /dev/null
+++ b/libc/src/unistd/linux/close_range.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of close_range.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/unistd/close_range.h"
+
+#include "src/__support/OSUtil/linux/syscall_wrappers/close_range.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, close_range,
+ (unsigned int first, unsigned int last,
+ unsigned int flags)) {
+ auto ret = linux_syscalls::close_range(first, last, flags);
+ if (!ret.has_value()) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt
index 0d76e4a4b451f4..5fbc41b28687eb 100644
--- a/libc/test/src/unistd/CMakeLists.txt
+++ b/libc/test/src/unistd/CMakeLists.txt
@@ -89,6 +89,25 @@ add_libc_test(
libc.test.UnitTest.ErrnoSetterMatcher
)
+add_libc_test(
+ close_range_test
+ SUITE
+ libc-unistd-tests
+ SRCS
+ close_range_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.hdr.unistd_macros
+ libc.src.__support.CPP.scope
+ libc.src.fcntl.fcntl
+ libc.src.unistd.close
+ libc.src.unistd.close_range
+ libc.src.unistd.pipe
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
add_libc_test(
dup_test
SUITE
diff --git a/libc/test/src/unistd/close_range_test.cpp b/libc/test/src/unistd/close_range_test.cpp
new file mode 100644
index 00000000000000..b2b9f3c007c216
--- /dev/null
+++ b/libc/test/src/unistd/close_range_test.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for close_range.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/CPP/scope.h"
+#include "src/fcntl/fcntl.h"
+#include "src/unistd/close.h"
+#include "src/unistd/close_range.h"
+#include "src/unistd/pipe.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+using LlvmLibcCloseRangeTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+namespace {
+
+bool create_adjacent_fds(int fds[2]) {
+ if (LIBC_NAMESPACE::pipe(fds) != 0)
+ return false;
+ if (fds[0] > fds[1]) {
+ int tmp = fds[0];
+ fds[0] = fds[1];
+ fds[1] = tmp;
+ }
+ while (fds[0] + 1 != fds[1]) {
+ int new_fd = LIBC_NAMESPACE::fcntl(fds[0], F_DUPFD, fds[1]);
+ if (new_fd < 0) {
+ LIBC_NAMESPACE::close(fds[0]);
+ LIBC_NAMESPACE::close(fds[1]);
+ return false;
+ }
+ if (LIBC_NAMESPACE::close(fds[0]) != 0) {
+ LIBC_NAMESPACE::close(fds[1]);
+ LIBC_NAMESPACE::close(new_fd);
+ return false;
+ }
+ fds[0] = fds[1];
+ fds[1] = new_fd;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST_F(LlvmLibcCloseRangeTest, InvalidArgs) {
+ // first > last should fail with EINVAL.
+ ASSERT_THAT(LIBC_NAMESPACE::close_range(10, 5, 0), Fails(EINVAL));
+
+ // Unsupported or invalid flags should fail with EINVAL.
+ ASSERT_THAT(LIBC_NAMESPACE::close_range(10, 10, ~0U), Fails(EINVAL));
+}
+
+TEST_F(LlvmLibcCloseRangeTest, ClosePipes) {
+ int fds[2];
+ ASSERT_TRUE(create_adjacent_fds(fds));
+
+ ASSERT_THAT(LIBC_NAMESPACE::close_range(static_cast<unsigned int>(fds[0]),
+ static_cast<unsigned int>(fds[1]), 0),
+ Succeeds(0));
+
+ // Both should now be closed.
+ // Using fcntl(F_GETFD) is safer than close() as it does not risk closing an
+ // unrelated FD.
+ ASSERT_THAT(LIBC_NAMESPACE::fcntl(fds[0], F_GETFD), Fails(EBADF));
+ ASSERT_THAT(LIBC_NAMESPACE::fcntl(fds[1], F_GETFD), Fails(EBADF));
+}
+
+// CLOSE_RANGE_CLOEXEC was introduced in Linux kernel 5.11.
+TEST_F(LlvmLibcCloseRangeTest, CloexecFlag) {
+#ifdef CLOSE_RANGE_CLOEXEC
+ int fds[2];
+ ASSERT_TRUE(create_adjacent_fds(fds));
+ LIBC_NAMESPACE::cpp::scope_exit cleanup([&] {
+ EXPECT_THAT(LIBC_NAMESPACE::close(fds[0]), Succeeds(0));
+ EXPECT_THAT(LIBC_NAMESPACE::close(fds[1]), Succeeds(0));
+ });
+
+ ASSERT_THAT(LIBC_NAMESPACE::close_range(static_cast<unsigned int>(fds[0]),
+ static_cast<unsigned int>(fds[1]),
+ CLOSE_RANGE_CLOEXEC),
+ Succeeds(0));
+
+ // Descriptors should remain open and have FD_CLOEXEC set.
+ int flags0 = LIBC_NAMESPACE::fcntl(fds[0], F_GETFD);
+ ASSERT_GT(flags0, -1);
+ EXPECT_NE(flags0 & FD_CLOEXEC, 0);
+
+ int flags1 = LIBC_NAMESPACE::fcntl(fds[1], F_GETFD);
+ ASSERT_GT(flags1, -1);
+ EXPECT_NE(flags1 & FD_CLOEXEC, 0);
+#endif
+}
diff --git a/libc/utils/docgen/unistd.yaml b/libc/utils/docgen/unistd.yaml
index 688a21dd7cc319..f623aa738e6462 100644
--- a/libc/utils/docgen/unistd.yaml
+++ b/libc/utils/docgen/unistd.yaml
@@ -515,6 +515,8 @@ functions:
in-latest-posix: ""
close:
in-latest-posix: ""
+ close_range:
+ in-latest-posix: ""
confstr:
in-latest-posix: ""
crypt:
More information about the libc-commits
mailing list