diff mbox series

[v5,4/5] io: Add closefrom [BZ #10353]

Message ID 20210505214143.3649585-4-adhemerval.zanella@linaro.org
State Superseded
Headers show
Series [v5,1/5] support: Add support_stack_alloc | expand

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Adhemerval Zanella May 5, 2021, 9:41 p.m. UTC
Changes from v4:
* Rebased against master.

--

The function closes all open file descriptors greater than or equal to
input argument.  Negative values are clamped to 0, i.e, it will close
all file descriptors.

As indicated by the bug report, this is a common symbol provided by
different systems (Solaris, OpenBSD, NetBSD, FreeBSD) and, although
its has inherent issues with not taking in consideration internal libc
file descriptors (such as syslog), this is also a common feature used
in multiple projects [1][2][3][4][5].

The Linux fallback implementation iterates over /proc and close all
file descriptors sequentially, and issues a lseek after a file
descriptor is closed after a getdents calls.

Also, the interface is set a fail-safe meaning that a failure in the
fallback results in a process abort.

Checked on x86_64-linux-gnu on kernel v5.11 and v5.4.

[1] https://github.com/systemd/systemd/blob/5238e9575906297608ff802a27e2ff9effa3b338/src/basic/fd-util.c#L217
[2] https://github.com/lxc/lxc/blob/ddf4b77e11a4d08f09b7b9cd13e593f8c047edc5/src/lxc/start.c#L236
[3] https://github.com/python/cpython/blob/9e4f2f3a6b8ee995c365e86d976937c141d867f8/Modules/_posixsubprocess.c#L220
[4] https://github.com/rust-lang/rust/blob/5f47c0613ed4eb46fca3633c1297364c09e5e451/src/libstd/sys/unix/process2.rs#L303-L308
[5] https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/childproc.c#L82
---
 NEWS                                          |   4 +
 include/unistd.h                              |   1 +
 io/Makefile                                   |   4 +-
 io/Versions                                   |   3 +
 io/closefrom.c                                |  34 ++++
 io/tst-closefrom.c                            | 152 ++++++++++++++++++
 manual/llio.texi                              |  10 ++
 posix/unistd.h                                |   6 +
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/Makefile              |   3 +-
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/closefrom.c           |  35 ++++
 sysdeps/unix/sysv/linux/closefrom_fallback.c  |  97 +++++++++++
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 44 files changed, 380 insertions(+), 2 deletions(-)
 create mode 100644 io/closefrom.c
 create mode 100644 io/tst-closefrom.c
 create mode 100644 sysdeps/unix/sysv/linux/closefrom.c
 create mode 100644 sysdeps/unix/sysv/linux/closefrom_fallback.c
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index c151c4b293..d4506f5f4b 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,10 @@  Major new features:
 * On Linux, the close_range function has been added.  It allows efficiently
   closing a range of file descriptors on recent kernels (version 5.9).
 
+* The function closefrom has been added.  It closes all file descriptors
+  greater than given integer.  This function is a GNU extension, although it
+  also present in other systems.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The function pthread_mutex_consistent_np has been deprecated; programs
diff --git a/include/unistd.h b/include/unistd.h
index 1363976161..c596c6fb24 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -152,6 +152,7 @@  extern int __brk (void *__addr) attribute_hidden;
 extern int __close (int __fd);
 libc_hidden_proto (__close)
 extern int __libc_close (int __fd);
+extern _Bool __closefrom_fallback (int __lowfd) attribute_hidden;
 extern ssize_t __read (int __fd, void *__buf, size_t __nbytes);
 libc_hidden_proto (__read)
 extern ssize_t __write (int __fd, const void *__buf, size_t __n);
diff --git a/io/Makefile b/io/Makefile
index a22492f3f3..d574feb3f0 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -54,7 +54,8 @@  routines :=								\
 	posix_fadvise posix_fadvise64					\
 	posix_fallocate posix_fallocate64				\
 	sendfile sendfile64 copy_file_range 				\
-	utimensat futimens file_change_detection
+	utimensat futimens file_change_detection			\
+	closefrom
 
 others		:= pwd
 test-srcs	:= ftwtest
@@ -75,6 +76,7 @@  tests		:= test-utime test-stat test-stat2 test-lfs tst-getcwd \
 		   tst-lutimes \
 		   tst-futimens \
 		   tst-utimensat \
+		   tst-closefrom \
 
 # Likewise for statx, but we do not need static linking here.
 tests-internal += tst-statx
diff --git a/io/Versions b/io/Versions
index 49c4d2d40a..f28bd43773 100644
--- a/io/Versions
+++ b/io/Versions
@@ -136,6 +136,9 @@  libc {
     stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
     mknod; mknodat;
   }
+  GLIBC_2.34 {
+    closefrom;
+  }
   GLIBC_PRIVATE {
     __libc_fcntl64;
     __fcntl_nocancel;
diff --git a/io/closefrom.c b/io/closefrom.c
new file mode 100644
index 0000000000..01660a7531
--- /dev/null
+++ b/io/closefrom.c
@@ -0,0 +1,34 @@ 
+/* Close a range of file descriptors.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <not-cancel.h>
+
+void
+__closefrom (int lowfd)
+{
+  int maxfd = __getdtablesize ();
+  if (maxfd == -1)
+    __fortify_fail ("closefrom failed to get the file descriptor table size");
+
+  for (int i = 0; i < maxfd; i++)
+    if (i >= lowfd)
+      __close_nocancel_nostatus (i);
+}
+weak_alias (__closefrom, closefrom)
diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
new file mode 100644
index 0000000000..d4c187073c
--- /dev/null
+++ b/io/tst-closefrom.c
@@ -0,0 +1,152 @@ 
+/* Smoke test for the closefrom.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/xunistd.h>
+
+#include <array_length.h>
+
+#define NFDS 100
+
+static int
+open_multiple_temp_files (void)
+{
+  /* Check if the temporary file descriptor has no no gaps.  */
+  int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
+  for (int i = 1; i <= NFDS; i++)
+    TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600), lowfd + i);
+  return lowfd;
+}
+
+static int
+closefrom_test (void)
+{
+  struct support_descriptors *descrs = support_descriptors_list ();
+
+  int lowfd = open_multiple_temp_files ();
+
+  const int maximum_fd = lowfd + NFDS;
+  const int half_fd = lowfd + NFDS / 2;
+  const int gap = maximum_fd / 4;
+
+  /* Close half of the descriptors and check result.  */
+  closefrom (half_fd);
+
+  for (int i = half_fd; i <= maximum_fd; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  for (int i = 0; i < half_fd; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+  /* Create some gaps, close up to a threshold, and check result.  */
+  xclose (lowfd + 35);
+  xclose (lowfd + 38);
+  xclose (lowfd + 42);
+  xclose (lowfd + 46);
+
+  /* Close half of the descriptors and check result.  */
+  closefrom (gap);
+  for (int i = gap + 1; i < maximum_fd; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  for (int i = 0; i < gap; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+  /* Close the remmaining but the last one.  */
+  closefrom (lowfd + 1);
+  for (int i = lowfd + 1; i <= maximum_fd; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  TEST_VERIFY (fcntl (lowfd, F_GETFL) > -1);
+
+  /* Close the last one.  */
+  closefrom (lowfd);
+  TEST_COMPARE (fcntl (lowfd, F_GETFL), -1);
+  TEST_COMPARE (errno, EBADF);
+
+  /* Double check by check the /proc.  */
+  support_descriptors_check (descrs);
+  support_descriptors_free (descrs);
+
+  return 0;
+}
+
+/* Check if closefrom works even when no new file descriptors can be
+   created.  */
+static int
+closefrom_test_file_desc_limit (void)
+{
+  int max_fd = NFDS;
+  {
+    struct rlimit rl;
+    if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
+      FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
+
+    max_fd = (rl.rlim_cur < max_fd ? rl.rlim_cur : max_fd);
+    rl.rlim_cur = max_fd;
+
+    if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
+      FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
+  }
+
+  /* Exhauste the file descriptor limit.  */
+  int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
+  for (;;)
+    {
+      int fd = open ("/dev/null", O_RDONLY, 0600);
+      if (fd == -1)
+	{
+	  if (errno != EMFILE)
+	    FAIL_EXIT1 ("open: %m");
+	  break;
+	}
+      TEST_VERIFY_EXIT (fd < max_fd);
+    }
+
+  closefrom (lowfd);
+  for (int i = lowfd; i < NFDS; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  closefrom_test ();
+  closefrom_test_file_desc_limit ();
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/llio.texi b/manual/llio.texi
index 3613ead72c..553e455e67 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -328,6 +328,16 @@  The maximum number of file descriptors is controlled by the
 @end table
 @end deftypefun
 
+@deftypefun void closefrom (int @var{lowfd})
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
+
+The function @code{closefrom} closes all file descriptors larger than or equal
+to @var{lowfd} then @var{lowfd}.  This function is similar to call
+@code{close} applied to the specified file descriptor range.
+
+Already closed file descriptors are ignored.
+@end deftypefun
 
 @node I/O Primitives
 @section Input and Output Primitives
diff --git a/posix/unistd.h b/posix/unistd.h
index d9d8929f71..f3f7cb99c4 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -357,6 +357,12 @@  extern __off64_t lseek64 (int __fd, __off64_t __offset, int __whence)
    __THROW.  */
 extern int close (int __fd);
 
+#ifdef __USE_MISC
+/* Close all open file descriptors greater than or equal to LOWFD.
+   Negative LOWFD is clamped to 0.  */
+extern void closefrom (int __lowfd) __THROW;
+#endif
+
 /* Read NBYTES into BUF from FD.  Return the
    number read, -1 for errors or 0 for EOF.
 
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index fb2683edc2..52d9134a4e 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2207,6 +2207,7 @@  GLIBC_2.33 stat64 F
 GLIBC_2.34 __isnanf128 F
 GLIBC_2.34 __libc_start_main F
 GLIBC_2.34 _hurd_libc_proc_init F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 execveat F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 605db4169f..6803920a6e 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -64,7 +64,8 @@  sysdep_routines += adjtimex clone umount umount2 readahead sysctl \
 		   time64-support pselect32 \
 		   xstat fxstat lxstat xstat64 fxstat64 lxstat64 \
 		   fxstatat fxstatat64 \
-		   xmknod xmknodat
+		   xmknod xmknodat \
+		   closefrom_fallback
 
 CFLAGS-gethostid.c = -fexceptions
 CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 0364c02d91..35772d07da 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2293,6 +2293,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fd6db54689..a51aff6089 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2380,6 +2380,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 6d66c25abd..853caba0e3 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2052,6 +2052,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 34e037b66a..14ddad655b 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -189,6 +189,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index a319d48ae7..313401c458 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -186,6 +186,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
new file mode 100644
index 0000000000..f5d7342c2c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/closefrom.c
@@ -0,0 +1,35 @@ 
+/* Close a range of file descriptors.  Linux version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+void
+__closefrom (int lowfd)
+{
+  int l = MAX (0, lowfd);
+
+  int r = __close_range (l, ~0U, 0);
+  if (r == 0)
+    return;
+
+  if (!__closefrom_fallback (l))
+    __fortify_fail ("closefrom failed to close a file descriptor");
+}
+weak_alias (__closefrom, closefrom)
diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
new file mode 100644
index 0000000000..61e71d388d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
@@ -0,0 +1,97 @@ 
+/* Close a range of file descriptors.  Linux version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <arch-fd_to_filename.h>
+#include <dirent.h>
+#include <not-cancel.h>
+#include <stdbool.h>
+
+/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
+   that fall on the criteria.  */
+_Bool
+__closefrom_fallback (int from)
+{
+  bool ret = false;
+
+  int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
+                               0);
+  if (dirfd == -1)
+    {
+      /* The closefrom should work even when process can't open new files.  */
+      if (errno == ENOENT)
+        goto err;
+
+      for (int i = from; i < INT_MAX; i++)
+        {
+          int r = __close_nocancel (i);
+          if (r == 0 || (r == -1 && errno != EBADF))
+            break;
+        }
+
+      dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
+                               0);
+      if (dirfd == -1)
+        goto err;
+    }
+
+  char buffer[1024];
+  while (true)
+    {
+      ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
+      if (ret == -1)
+        goto err;
+      else if (ret == 0)
+        break;
+
+      /* If any file descriptor is closed it resets the /proc/self position
+         read again from the start (to obtain any possible kernel update).  */
+      bool closed = false;
+      char *begin = buffer, *end = buffer + ret;
+      while (begin != end)
+        {
+          unsigned short int d_reclen;
+          memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
+                  sizeof (d_reclen));
+          const char *dname = begin + offsetof (struct dirent64, d_name);
+          begin += d_reclen;
+
+          if (dname[0] == '.')
+            continue;
+
+          int fd = 0;
+          for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
+            fd = 10 * fd + (*s - '0');
+
+          if (fd == dirfd || fd < from)
+            continue;
+
+          /* We ignore close errors because EBADF, EINTR, and EIO means the
+             descriptor has been released.  */
+          __close_nocancel (fd);
+          closed = true;
+        }
+
+      if (closed && __lseek (dirfd, 0, SEEK_SET) < 0)
+        goto err;
+    }
+
+  ret = true;
+err:
+  __close_nocancel (dirfd);
+  return ret;
+}
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 2d32c1cf9b..68658e3ef8 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2236,6 +2236,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 1ae324809c..c16e7d8f2e 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2187,6 +2187,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index eb7b05cca0..e73a11a8ea 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2370,6 +2370,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 5c275e85d0..84ee3d6243 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2221,6 +2221,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 38e37fb013..297b8cab6c 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -190,6 +190,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index b4e954bf72..c562997d51 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2313,6 +2313,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index f93feaf16e..8fcbcfffeb 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2287,6 +2287,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index e992581a4b..5586d3610a 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2284,6 +2284,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index cb38de9f4c..3d5210a6e3 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2276,6 +2276,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index f573edd1a8..49c1a2c30b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2274,6 +2274,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 2bbd7e604d..238f2b3b21 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2282,6 +2282,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 7fd5172f73..244c8dab63 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2276,6 +2276,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 2e89b434a0..016317653c 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2326,6 +2326,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 5ced3b6daa..e81d40ec43 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2340,6 +2340,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index e74fa1d141..33ba8522fd 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2373,6 +2373,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 1435c63ce2..252a669528 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2188,6 +2188,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index ce3466bc90..19f7bda2a2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2489,6 +2489,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index cb71ce0331..7bbdc8e400 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2054,6 +2054,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index b13248939f..94fd53fd54 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2254,6 +2254,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 0757e54290..43d5f17ef4 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2338,6 +2338,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index e2f3a42a71..418643ba2b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2225,6 +2225,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 8566ae1cb3..b88e4c1b7b 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2194,6 +2194,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 90bfc6f7b3..33c1bb6555 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2191,6 +2191,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 32ec7865c8..3af45b8b0e 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2329,6 +2329,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 4cdbfdb13f..0cc316d932 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2242,6 +2242,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 15fee4a8ff..5d2719da05 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2203,6 +2203,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2c74a510b3..029abb00e4 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2308,6 +2308,7 @@  GLIBC_2.34 __pthread_mutex_unlock F
 GLIBC_2.34 __pthread_unwind_next F
 GLIBC_2.34 call_once F
 GLIBC_2.34 close_range F
+GLIBC_2.34 closefrom F
 GLIBC_2.34 cnd_broadcast F
 GLIBC_2.34 cnd_destroy F
 GLIBC_2.34 cnd_init F