[v3,3/4] io: Add closefrom [BZ #10353]

Message ID 20201223163651.2634504-3-adhemerval.zanella@linaro.org
State Superseded
Headers
Series [v3,1/4] support: Add xclone |

Commit Message

Adhemerval Zanella Netto Dec. 23, 2020, 4:36 p.m. UTC
  Changes from previous version [1]:

  - Fixed typos.
  - Add file descriptor gap check on test.
  - Fixed manual entry for possible errors.
  - Export as __USE_MISC instead of __USE_GNU.
  - Fixed __closefrom_fallback to bail on error different than ENOENT
    and rewind each time a file descriptor is closed.

--

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.  Although it was raised the questioning
whether getdents on /proc/self/fd might return disjointed entries
when file descriptor are closed; it does not seems the case on my
testing on multiple kernel (v4.18, v5.4, v5.9) and the same strategy
is used on different projects [1][2][3][5].

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.9 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                                   |   5 +-
 io/Versions                                   |   1 +
 io/closefrom.c                                |  34 ++++
 io/tst-closefrom.c                            | 152 ++++++++++++++++++
 manual/llio.texi                              |   8 +
 posix/unistd.h                                |   6 +
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/Makefile              |   2 +-
 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  |  93 +++++++++++
 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, 371 insertions(+), 3 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
  

Comments

Florian Weimer March 9, 2021, 10:23 a.m. UTC | #1
* Adhemerval Zanella via Libc-alpha:

> diff --git a/io/Versions b/io/Versions
> index 49c4d2d40a..fa33452881 100644
> --- a/io/Versions
> +++ b/io/Versions
> @@ -135,6 +135,7 @@ libc {
>    GLIBC_2.33 {
>      stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
>      mknod; mknodat;
> +    closefrom;
>    }

Needs to be GLIBC_2.34 now.  Copyright years need adjusting, too.

> diff --git a/io/closefrom.c b/io/closefrom.c
> new file mode 100644
> index 0000000000..7833935b16
> --- /dev/null
> +++ b/io/closefrom.c

> +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);
> +}

Still okay for Hurd.

> +weak_alias (__closefrom, closefrom)
> diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
> new file mode 100644
> index 0000000000..6f6fbd270f
> --- /dev/null
> +++ b/io/tst-closefrom.c

> +#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;
> +}

Spurious line wrap.

> +
> +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 = maximum_fd / 2;
> +  const int gap = maximum_fd / 4;

See the other message about the half_fd initialization.

> +/* 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 ("create_temp_file: %m");

Wrong error message.

Maybe add TEST_VERIFY_EXIT (fd < max_fd) to the loop?  I believe the
setrlimit call will ensure that.

> diff --git a/manual/llio.texi b/manual/llio.texi
> index ceb18ac89a..777993d207 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -321,6 +321,14 @@ 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 large or equal
> +then @var{lowfd}.  This function is similar to call @code{close} in specified
> +file descriptor range.
> +@end deftypefun

“larger than or equal to @var{lowfd}”

“@code{close} applied to the specified file descriptor range”

Please also mention that already-closed descriptors are ignored.

> diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
> new file mode 100644
> index 0000000000..ba98fccd39
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/closefrom.c
> +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");
> +}

This ignores EPERM.  I guess that's okay.

> diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
> new file mode 100644
> index 0000000000..78182bc5f0
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
> @@ -0,0 +1,93 @@

> +/* 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.
> +	 In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
> +	 a file descriptor to iterate over /proc.  */
> +      if (errno == ENOENT)
> +	goto err;
> +
> +      int maxfd = __getdtablesize ();
> +      for (int i = from; i < maxfd; i++)
> +	if (__close_nocancel (i) == 0)
> +	  break;

This should check for errno != EBADF.  EINTR, EIO etc. are okay as well
because a descriptor has been released.

I wouldn't mind iterating up to INT_MAX, removing the __getdtablesize
call.  It may take a minute or two, but it removes some of the error
scenarios.

> +
> +      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;
> +
> +      bool closed = false;

Maybe add a comment here about restarting on close?

> +      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');

Hmm.  I had to think a bit about it, but it seems okay whether *s is
signed or not.

> +	  if (fd == dirfd || fd < from)
> +	    continue;
> +
> +	  __close_nocancel (fd);

Maybe add a comment why we aren't adding an error here?

> +	  closed = true;
> +	}
> +
> +      if (closed)
> +        __lseek (dirfd, 0, SEEK_SET);
> +    }

Missing error check?

> +  ret = true;
> +err:
> +  __close_nocancel (dirfd);
> +  return ret;
> +}

The algorithm looks fine to me.  Thanks for adding the restart-on-close
part.

Thanks,
Florian
  
Adhemerval Zanella Netto March 9, 2021, 7:07 p.m. UTC | #2
On 09/03/2021 07:23, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/io/Versions b/io/Versions
>> index 49c4d2d40a..fa33452881 100644
>> --- a/io/Versions
>> +++ b/io/Versions
>> @@ -135,6 +135,7 @@ libc {
>>    GLIBC_2.33 {
>>      stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
>>      mknod; mknodat;
>> +    closefrom;
>>    }
> 
> Needs to be GLIBC_2.34 now.  Copyright years need adjusting, too.

Ack.

> 
>> diff --git a/io/closefrom.c b/io/closefrom.c
>> new file mode 100644
>> index 0000000000..7833935b16
>> --- /dev/null
>> +++ b/io/closefrom.c
> 
>> +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);
>> +}
> 
> Still okay for Hurd.
> 
>> +weak_alias (__closefrom, closefrom)
>> diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
>> new file mode 100644
>> index 0000000000..6f6fbd270f
>> --- /dev/null
>> +++ b/io/tst-closefrom.c
> 
>> +#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;
>> +}
> 
> Spurious line wrap.

Ack.

> 
>> +
>> +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 = maximum_fd / 2;
>> +  const int gap = maximum_fd / 4;
> 
> See the other message about the half_fd initialization.

Ack.

> 
>> +/* 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 ("create_temp_file: %m");
> 
> Wrong error message.
> 
> Maybe add TEST_VERIFY_EXIT (fd < max_fd) to the loop?  I believe the
> setrlimit call will ensure that.

Ack.

> 
>> diff --git a/manual/llio.texi b/manual/llio.texi
>> index ceb18ac89a..777993d207 100644
>> --- a/manual/llio.texi
>> +++ b/manual/llio.texi
>> @@ -321,6 +321,14 @@ 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 large or equal
>> +then @var{lowfd}.  This function is similar to call @code{close} in specified
>> +file descriptor range.
>> +@end deftypefun
> 
> “larger than or equal to @var{lowfd}”
> 
> “@code{close} applied to the specified file descriptor range”
> 
> Please also mention that already-closed descriptors are ignored.

Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
>> new file mode 100644
>> index 0000000000..ba98fccd39
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/closefrom.c
>> +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");
>> +}
> 
> This ignores EPERM.  I guess that's okay.

Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
>> new file mode 100644
>> index 0000000000..78182bc5f0
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
>> @@ -0,0 +1,93 @@
> 
>> +/* 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.
>> +	 In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
>> +	 a file descriptor to iterate over /proc.  */
>> +      if (errno == ENOENT)
>> +	goto err;
>> +
>> +      int maxfd = __getdtablesize ();
>> +      for (int i = from; i < maxfd; i++)
>> +	if (__close_nocancel (i) == 0)
>> +	  break;
> 
> This should check for errno != EBADF.  EINTR, EIO etc. are okay as well
> because a descriptor has been released.

Ack.

> 
> I wouldn't mind iterating up to INT_MAX, removing the __getdtablesize
> call.  It may take a minute or two, but it removes some of the error
> scenarios.

Ok, I think it should be ok (the __getdtablesize call should be issue
only when open fails).

> 
>> +
>> +      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;
>> +
>> +      bool closed = false;
> 
> Maybe add a comment here about restarting on close?

Ack.

> 
>> +      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');
> 
> Hmm.  I had to think a bit about it, but it seems okay whether *s is
> signed or not.> 
>> +	  if (fd == dirfd || fd < from)
>> +	    continue;
>> +
>> +	  __close_nocancel (fd);
> 
> Maybe add a comment why we aren't adding an error here?

Ack.

> 
>> +	  closed = true;
>> +	}
>> +
>> +      if (closed)
>> +        __lseek (dirfd, 0, SEEK_SET);
>> +    }
> 
> Missing error check?

Ack, lseek failure should be reported.

> 
>> +  ret = true;
>> +err:
>> +  __close_nocancel (dirfd);
>> +  return ret;
>> +}
> 
> The algorithm looks fine to me.  Thanks for adding the restart-on-close
> part.
> 
> Thanks,
> Florian
>
  

Patch

diff --git a/NEWS b/NEWS
index 9e829841f6..e6446f0548 100644
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,10 @@  Major new features:
 
 * On Linux, the close_range function has been added.
 
+* 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 mallinfo function is marked deprecated.  Callers should call
diff --git a/include/unistd.h b/include/unistd.h
index 54becbc9eb..d48ad6c799 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -153,6 +153,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 33f5b0b867..56a788c2d5 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -55,7 +55,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
@@ -69,7 +70,7 @@  tests		:= test-utime test-stat test-stat2 test-lfs tst-getcwd \
 		   tst-fts tst-fts-lfs tst-open-tmpfile \
 		   tst-copy_file_range tst-getcwd-abspath tst-lockf \
 		   tst-ftw-lnk tst-file_change_detection tst-lchmod \
-		   tst-ftw-bz26353
+		   tst-ftw-bz26353 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..fa33452881 100644
--- a/io/Versions
+++ b/io/Versions
@@ -135,6 +135,7 @@  libc {
   GLIBC_2.33 {
     stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
     mknod; mknodat;
+    closefrom;
   }
   GLIBC_PRIVATE {
     __libc_fcntl64;
diff --git a/io/closefrom.c b/io/closefrom.c
new file mode 100644
index 0000000000..7833935b16
--- /dev/null
+++ b/io/closefrom.c
@@ -0,0 +1,34 @@ 
+/* Close a range of file descriptors.
+   Copyright (C) 2020 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..6f6fbd270f
--- /dev/null
+++ b/io/tst-closefrom.c
@@ -0,0 +1,152 @@ 
+/* Smoke test for the closefrom.
+   Copyright (C) 2020 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 = maximum_fd / 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 ("create_temp_file: %m");
+	  break;
+	}
+    }
+
+  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 ceb18ac89a..777993d207 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -321,6 +321,14 @@  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 large or equal
+then @var{lowfd}.  This function is similar to call @code{close} in specified
+file descriptor range.
+@end deftypefun
 
 @node I/O Primitives
 @section Input and Output Primitives
diff --git a/posix/unistd.h b/posix/unistd.h
index 32b8161619..4e9e4f478e 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -352,6 +352,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 7a5eb66b85..fce67cf3ca 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2191,6 +2191,7 @@  GLIBC_2.32 thrd_current F
 GLIBC_2.32 thrd_equal F
 GLIBC_2.32 thrd_sleep F
 GLIBC_2.32 thrd_yield F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index e06c2a1f0d..59ee6b5b5a 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -61,7 +61,7 @@  sysdep_routines += adjtimex clone umount umount2 readahead sysctl \
 		   open_by_handle_at mlock2 pkey_mprotect pkey_set pkey_get \
 		   timerfd_gettime timerfd_settime prctl \
 		   process_vm_readv process_vm_writev clock_adjtime \
-		   time64-support pselect32 close_range
+		   time64-support pselect32 close_range 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 a52decb822..811d3b3cd9 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2161,6 +2161,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index ed085c9b3c..d0bc011811 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2243,6 +2243,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 88a17c2fdf..c4e26b3cf6 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -1921,6 +1921,7 @@  GLIBC_2.32 write F
 GLIBC_2.32 writev F
 GLIBC_2.32 wscanf F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 3b0a47e967..5ccd1fb07f 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -144,6 +144,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index a690962068..5043e483b8 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -142,6 +142,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
new file mode 100644
index 0000000000..ba98fccd39
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/closefrom.c
@@ -0,0 +1,35 @@ 
+/* Close a range of file descriptors.  Linux version.
+   Copyright (C) 2020 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..78182bc5f0
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
@@ -0,0 +1,93 @@ 
+/* Close a range of file descriptors.  Linux version.
+   Copyright (C) 2020 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.
+	 In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
+	 a file descriptor to iterate over /proc.  */
+      if (errno == ENOENT)
+	goto err;
+
+      int maxfd = __getdtablesize ();
+      for (int i = from; i < maxfd; i++)
+	if (__close_nocancel (i) == 0)
+	  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;
+
+      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;
+
+	  __close_nocancel (fd);
+	  closed = true;
+	}
+
+      if (closed)
+        __lseek (dirfd, 0, SEEK_SET);
+    }
+
+  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 d37882413c..6f3718cd14 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2105,6 +2105,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 61450e47d5..84b474dba4 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2064,6 +2064,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 672ff8a002..4c1399102e 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2230,6 +2230,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 53e53005e9..9fcaa40c34 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2096,6 +2096,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 25f2d1c08f..ccb780d9c7 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -145,6 +145,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index cc0d93b381..3afaa1c70e 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2176,6 +2176,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index b4acf52c41..266063d0e8 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2156,6 +2156,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 13d374a031..5d893ec4d1 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2152,6 +2152,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index fadbf99d4b..6207a15a09 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2147,6 +2147,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 4c786070d0..860e5fecfa 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2144,6 +2144,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index c456ebcea3..daed5ae2a8 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2153,6 +2153,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 5ed54e7c94..7b6edfe8f5 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2147,6 +2147,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 67b611ef6d..9dd7d64500 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2194,6 +2194,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index ed0f239cd5..c561e5cf36 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2203,6 +2203,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index f04b167788..d25bf75ed3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2235,6 +2235,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index a4b9900036..248fab8212 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2066,6 +2066,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index e5d666dab3..d02fa6b3d2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2356,6 +2356,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index f8c34aca7c..56d1a16b2a 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -597,6 +597,7 @@  GLIBC_2.33 clone F
 GLIBC_2.33 close F
 GLIBC_2.33 close_range F
 GLIBC_2.33 closedir F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 closelog F
 GLIBC_2.33 confstr F
 GLIBC_2.33 connect F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 6042d42aba..8fcd329728 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2123,6 +2123,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 46d2de3030..b06cc94710 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2201,6 +2201,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 9fda4babba..6ede1579ca 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2102,6 +2102,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 22ceaa3d87..0f426028a1 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2070,6 +2070,7 @@  GLIBC_2.32 sigabbrev_np F
 GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 110f7ebf22..8637b683d6 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2068,6 +2068,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 1b31685a24..dc7c01f734 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2192,6 +2192,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9ad88bf7b3..67d361c881 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2119,6 +2119,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 5089a21981..94bb5a0fa7 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2077,6 +2077,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index c3db0fc7ca..e94ce733ab 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2174,6 +2174,7 @@  GLIBC_2.32 sigdescr_np F
 GLIBC_2.32 strerrordesc_np F
 GLIBC_2.32 strerrorname_np F
 GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F