[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