public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v3 1/4] support: Add xclone
@ 2020-12-23 16:36 Adhemerval Zanella
  2020-12-23 16:36 ` [PATCH v3 2/4] Linux: Add close_range Adhemerval Zanella
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Adhemerval Zanella @ 2020-12-23 16:36 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

Changes from previous version:

  - Fixed typos and corrected one line file description.

--

It is a wrapper for Linux clone syscall, to simplify the call to the
use only the most common arguments and remove architecture specific
handling (such as ia64 different name and signature).
---
 support/Makefile |  1 +
 support/xclone.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 support/xsched.h | 38 ++++++++++++++++++++++++++++++++++++
 3 files changed, 89 insertions(+)
 create mode 100644 support/xclone.c
 create mode 100644 support/xsched.h

diff --git a/support/Makefile b/support/Makefile
index 3198eb5022..aecd27316e 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -89,6 +89,7 @@ libsupport-routines = \
   xcalloc \
   xchroot \
   xclock_gettime \
+  xclone \
   xclose \
   xconnect \
   xcopy_file_range \
diff --git a/support/xclone.c b/support/xclone.c
new file mode 100644
index 0000000000..027b367ba1
--- /dev/null
+++ b/support/xclone.c
@@ -0,0 +1,50 @@
+/* Auxiliary functions to issue the clone syscall.
+   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/>.  */
+
+#ifdef __linux__
+# include <support/check.h>
+# include <stackinfo.h>  /* For _STACK_GROWS_{UP,DOWN}.  */
+# include <xsched.h>
+
+pid_t
+xclone (int (*fn) (void *arg), void *arg, void *stack, size_t stack_size,
+	int flags)
+{
+  pid_t r = -1;
+
+# ifdef __ia64__
+  extern int __clone2 (int (*fn) (void *arg), void *stack, size_t stack_size,
+		       int flags, void *arg, ...);
+  r = __clone2 (f, stack, stack_size, flags, arg, /* ptid */ NULL,
+		/* tls */ NULL, /* ctid  */ ctid);
+# else
+#  if _STACK_GROWS_DOWN
+  r = clone (fn, stack + stack_size, flags, arg, /* ptid */ NULL,
+	     /* tls */ NULL, /* ctid */  NULL);
+#  elif _STACK_GROWS_UP
+  r = clone (fn, stack, flags, arg, /* ptid */ NULL, /* tls */ NULL,
+	     &ctid);
+#  endif
+# endif
+
+  if (r < 0)
+    FAIL_EXIT1 ("clone: %m");
+
+  return r;
+}
+#endif
diff --git a/support/xsched.h b/support/xsched.h
new file mode 100644
index 0000000000..1b7ce7286f
--- /dev/null
+++ b/support/xsched.h
@@ -0,0 +1,38 @@
+/* Wrapper for sched.h functions.
+   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/>.  */
+
+#ifndef SUPPORT_XSCHED_H
+#define SUPPORT_XSCHED_H
+
+__BEGIN_DECLS
+
+#include <sched.h>
+#include <sys/types.h>
+
+#ifdef __linux__
+# define DEFINE_STACK(name, size) \
+  char name[size] __attribute__ ((aligned))
+
+pid_t
+xclone (int (*fn) (void *arg), void *arg, void *stack, size_t stack_size,
+	int flags);
+#endif
+
+__END_DECLS
+
+#endif
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 2/4] Linux: Add close_range
  2020-12-23 16:36 [PATCH v3 1/4] support: Add xclone Adhemerval Zanella
@ 2020-12-23 16:36 ` Adhemerval Zanella
  2021-03-09  9:47   ` Florian Weimer
  2020-12-23 16:36 ` [PATCH v3 3/4] io: Add closefrom [BZ #10353] Adhemerval Zanella
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Adhemerval Zanella @ 2020-12-23 16:36 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

Changed from previous version:

  - Fixed manual wording.
  - Fixed test lowfd usage and a gaps check.
  - Added a new subtest to check for unsigned argument.
  - Fixed typos.
--

It was added on Linux 5.9 (278a5fbaed89).  Although FreeBSD has added
the same syscall, this only adds the symbol on Linux ports.  This
syscall is required to provided a fail-safe way to implement the
closefrom symbol (BZ #10353).

The tst-close_range-consts.py requires a toolchain with an updated
kernel headers that provides the close_range.h UAPI kernel header.

Checked on x86_64-linux-gnu on kernel v5.9 and v5.4.
---
 NEWS                                          |   2 +
 include/bits/unistd_ext.h                     |   6 +
 manual/llio.texi                              |  38 +++
 sysdeps/unix/sysv/linux/Makefile              |  13 +-
 sysdeps/unix/sysv/linux/Versions              |   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/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/bits/unistd_ext.h     |  11 +
 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 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/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 +
 .../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/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/syscalls.list         |   1 +
 .../unix/sysv/linux/tst-close_range-consts.py |  49 ++++
 sysdeps/unix/sysv/linux/tst-close_range.c     | 222 ++++++++++++++++++
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 35 files changed, 369 insertions(+), 2 deletions(-)
 create mode 100644 include/bits/unistd_ext.h
 create mode 100644 sysdeps/unix/sysv/linux/tst-close_range-consts.py
 create mode 100644 sysdeps/unix/sysv/linux/tst-close_range.c

diff --git a/NEWS b/NEWS
index 0820984547..9e829841f6 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,8 @@ Major new features:
   The 32-bit RISC-V port requires at least Linux 5.4, GCC 7.1 and binutils
   2.28.
 
+* On Linux, the close_range function has been added.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The mallinfo function is marked deprecated.  Callers should call
diff --git a/include/bits/unistd_ext.h b/include/bits/unistd_ext.h
new file mode 100644
index 0000000000..277be05746
--- /dev/null
+++ b/include/bits/unistd_ext.h
@@ -0,0 +1,6 @@
+#include_next <bits/unistd_ext.h>
+
+#ifndef _ISOMAC
+extern int __close_range (unsigned int lowfd, unsigned int highfd, int flags);
+libc_hidden_proto (__close_range);
+#endif
diff --git a/manual/llio.texi b/manual/llio.texi
index c0a53e1a6e..ceb18ac89a 100644
--- a/manual/llio.texi
+++ b/manual/llio.texi
@@ -284,6 +284,44 @@ of trying to close its underlying file descriptor with @code{close}.
 This flushes any buffered output and updates the stream object to
 indicate that it is closed.
 
+@deftypefun int close_range (unsigned int @var{lowfd}, unsigned int @var{maxfd}, int @var{flags})
+@standards{Linux, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
+@c This is a syscall for Linux v5.9.  There is no fallback emulation for
+@c older kernels.
+
+The function @code{clode_range} closes the file descriptor from @var{lowfd}
+to @var{maxfd} (inclusive).  This function is similar to call @code{close} in
+specified file descriptor range depending on the @var{flags}.
+
+The @var{flags} add options on how the files are closes.  Linux currently
+supports:.
+
+@vtable @code
+@item CLOSE_RANGE_UNSHARE
+Unshare the file descriptor table before closing file descriptors.
+@end vtable
+
+The normal return value from @code{close_range} is @math{0}; a value
+of @math{-1} is returned in case of failure.  The following @code{errno} error
+conditions are defined for this function:
+
+@table @code
+@item EINVAL
+The @var{lowfd} value is larger than @var{maxfd} or an unsupported @var{flags}
+is used.
+
+@item ENOMEM
+Either there is not enough memory for the operation, or the process is
+out of address space.
+
+@item EMFILE
+The process has too many files open.
+The maximum number of file descriptors is controlled by the
+@end table
+@end deftypefun
+
+
 @node I/O Primitives
 @section Input and Output Primitives
 
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 09604e128b..e06c2a1f0d 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
+		   time64-support pselect32 close_range
 
 CFLAGS-gethostid.c = -fexceptions
 CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
@@ -103,7 +103,8 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
 	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
 	 tst-rlimit-infinity tst-ofdlocks tst-gettid tst-gettid-kill \
-	 tst-tgkill tst-sysvsem-linux tst-sysvmsg-linux tst-sysvshm-linux
+	 tst-tgkill tst-sysvsem-linux tst-sysvmsg-linux tst-sysvshm-linux \
+	 tst-close_range
 tests-internal += tst-ofdlocks-compat tst-sigcontext-get_pc
 
 CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables
@@ -177,6 +178,14 @@ $(objpfx)tst-mman-consts.out: ../sysdeps/unix/sysv/linux/tst-mman-consts.py
 	  < /dev/null > $@ 2>&1; $(evaluate-test)
 $(objpfx)tst-mman-consts.out: $(sysdeps-linux-python-deps)
 
+tests-special += $(objpfx)tst-close_range-consts.out
+$(objpfx)tst-close_range-consts.out: ../sysdeps/unix/sysv/linux/tst-close_range-consts.py
+	$(sysdeps-linux-python) \
+	../sysdeps/unix/sysv/linux/tst-close_range-consts.py \
+	    $(sysdeps-linux-python-cc) \
+	< /dev/null > $@ 2>&1; $(evaluate-test)
+$(objpfx)tst-close_range-consts.out: $(sysdeps-linux-python-deps)
+
 $(objpfx)tst-gettid: $(shared-thread-library)
 $(objpfx)tst-gettid-kill: $(shared-thread-library)
 $(objpfx)tst-tgkill: $(shared-thread-library)
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index c35f783e2a..e2a6fa763f 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -169,6 +169,9 @@ libc {
   }
   GLIBC_2.32 {
   }
+  GLIBC_2.33 {
+    close_range;
+  }
   GLIBC_PRIVATE {
     # functions used in other libraries
     __syscall_rt_sigqueueinfo;
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 4cc1c6a591..a52decb822 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2160,6 +2160,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 close_range 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 26ad9845e4..ed085c9b3c 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2242,6 +2242,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 close_range 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 bb9dfd4daf..88a17c2fdf 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -1920,6 +1920,7 @@ GLIBC_2.32 wprintf F
 GLIBC_2.32 write F
 GLIBC_2.32 writev F
 GLIBC_2.32 wscanf F
+GLIBC_2.33 close_range 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 9ab3924888..a690962068 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -141,6 +141,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 close_range F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
index c315cc5cb8..799c59512c 100644
--- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
+++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
@@ -33,4 +33,15 @@
    not detached and has not been joined.  */
 extern __pid_t gettid (void) __THROW;
 
+/* Unshare the file descriptor table before closing file descriptors.  */
+#define CLOSE_RANGE_UNSHARE     (1U << 1)
+
+/* Close all file descriptors in the range FD up to MAX_FD.  The flag FLAGS
+   are define by the CLOSE_RANGE prefix.  This function behaves like close
+   on the range, but in a fail-safe where it will either fail and not close
+   any file descriptor or close all of them.  Returns 0 on successor or -1
+   for failure (and sets errno accordingly).  */
+extern int close_range (unsigned int __fd, unsigned int __max_fd,
+			int __flags) __THROW;
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 14a84dac8f..d37882413c 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2104,6 +2104,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 close_range 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 5c8502f3d3..61450e47d5 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2063,6 +2063,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 close_range 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 4f0d3c1eb5..672ff8a002 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2229,6 +2229,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 close_range 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 e3b345b803..53e53005e9 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2095,6 +2095,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 close_range 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 c4891479d3..cc0d93b381 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2175,6 +2175,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 close_range 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 143b0163b4..b4acf52c41 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2155,6 +2155,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 close_range 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 b2295f1937..fadbf99d4b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2146,6 +2146,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 close_range 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 aa9c6a4dca..c456ebcea3 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/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 close_range 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 5939588ad5..5ed54e7c94 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2146,6 +2146,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 close_range 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 92556c4237..67b611ef6d 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2193,6 +2193,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 close_range 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 26c93dff05..ed0f239cd5 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2202,6 +2202,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 close_range 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 c2ca00709e..a4b9900036 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2065,6 +2065,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 close_range 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 0ea50dc851..e5d666dab3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2355,6 +2355,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 close_range 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 0263e284d4..f8c34aca7c 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -595,6 +595,7 @@ GLIBC_2.33 clock_nanosleep F
 GLIBC_2.33 clock_settime F
 GLIBC_2.33 clone F
 GLIBC_2.33 close F
+GLIBC_2.33 close_range F
 GLIBC_2.33 closedir F
 GLIBC_2.33 closelog F
 GLIBC_2.33 confstr F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 1626c5351f..6042d42aba 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2122,6 +2122,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 close_range 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 a66426eb4d..46d2de3030 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2200,6 +2200,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 close_range 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 ab351873ae..9fda4babba 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2101,6 +2101,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 close_range 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 d36f228192..110f7ebf22 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2067,6 +2067,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 close_range 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 59b4313280..1b31685a24 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2191,6 +2191,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 close_range 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 266dcdfa08..9ad88bf7b3 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2118,6 +2118,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 close_range F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
index 01ec2bfa95..53575669c7 100644
--- a/sysdeps/unix/sysv/linux/syscalls.list
+++ b/sysdeps/unix/sysv/linux/syscalls.list
@@ -101,3 +101,4 @@ pkey_alloc	EXTRA	pkey_alloc	i:ii	pkey_alloc
 pkey_free	EXTRA	pkey_free	i:i	pkey_free
 gettid          EXTRA   gettid          Ei:     __gettid	gettid
 tgkill          EXTRA   tgkill          i:iii   __tgkill	tgkill
+close_range     EXTRA   close_range     i:iii   __close_range   close_range
diff --git a/sysdeps/unix/sysv/linux/tst-close_range-consts.py b/sysdeps/unix/sysv/linux/tst-close_range-consts.py
new file mode 100644
index 0000000000..3a0d4423e8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-close_range-consts.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python3
+# Test that glibc's unistd_ext.h constants match the kernel's.
+# 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/>.
+
+import argparse
+import sys
+
+import glibcextract
+import glibcsyscalls
+
+
+def main():
+    """The main entry point."""
+    parser = argparse.ArgumentParser(
+        description="Test that glibc's unistd_ext.h constants match "
+                    "the kernel's.")
+    parser.add_argument('--cc', metavar='CC',
+                        help='C compiler (including options) to use')
+    args = parser.parse_args()
+    linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc)
+    linux_version_glibc = (5, 9)
+    sys.exit(glibcextract.compare_macro_consts(
+        '#define _GNU_SOURCE 1\n'
+        '#include <unistd.h>\n',
+        '#define _GNU_SOURCE 1\n'
+        '#include <linux/close_range.h>\n',
+        args.cc,
+        'CLOSE_RANGE_.*',
+        None,
+        linux_version_glibc > linux_version_headers,
+        linux_version_headers > linux_version_glibc))
+
+if __name__ == '__main__':
+    main()
diff --git a/sysdeps/unix/sysv/linux/tst-close_range.c b/sysdeps/unix/sysv/linux/tst-close_range.c
new file mode 100644
index 0000000000..131cf27c72
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-close_range.c
@@ -0,0 +1,222 @@
+/* Test for the close_range system call.
+   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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <array_length.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/support.h>
+#include <support/xsched.h>
+#include <support/xunistd.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 void
+close_range_test_max_upper_limit (void)
+{
+  struct support_descriptors *descrs = support_descriptors_list ();
+
+  int lowfd = open_multiple_temp_files ();
+
+  {
+    int r = close_range (lowfd, ~0U, 0);
+    if (r == -1 && errno == ENOSYS)
+      FAIL_UNSUPPORTED ("close_range not supported");
+    TEST_COMPARE (r, 0);
+  }
+
+  support_descriptors_check (descrs);
+  support_descriptors_free (descrs);
+}
+
+static void
+close_range_test_common (int lowfd, unsigned int flags)
+{
+  const int maximum_fd = lowfd + NFDS;
+  const int half_fd = maximum_fd / 2;
+  const int gap_1 = maximum_fd - 8;
+
+  /* Close half of the descriptors and check result.  */
+  TEST_COMPARE (close_range (lowfd, half_fd, flags), 0);
+  for (int i = lowfd; i <= half_fd; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  for (int i = half_fd + 1; i < maximum_fd; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+  /* Create some gaps, close up to a threshold, and check result.  */
+  xclose (lowfd + 57);
+  xclose (lowfd + 78);
+  xclose (lowfd + 81);
+  xclose (lowfd + 82);
+  xclose (lowfd + 84);
+  xclose (lowfd + 90);
+
+  TEST_COMPARE (close_range (half_fd + 1, gap_1, flags), 0);
+  for (int i = half_fd + 1; i < gap_1; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  for (int i = gap_1 + 1; i < maximum_fd; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+  /* Close the remaining but the last one.  */
+  TEST_COMPARE (close_range (gap_1 + 1, maximum_fd - 1, flags), 0);
+  for (int i = gap_1 + 1; i < maximum_fd - 1; i++)
+    {
+      TEST_COMPARE (fcntl (i, F_GETFL), -1);
+      TEST_COMPARE (errno, EBADF);
+    }
+  TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1);
+
+  /* Close the last one.  */
+  TEST_COMPARE (close_range (maximum_fd, maximum_fd, flags), 0);
+  TEST_COMPARE (fcntl (maximum_fd, F_GETFL), -1);
+  TEST_COMPARE (errno, EBADF);
+}
+
+/* Basic tests: check if the syscall close ranges with and without gaps.  */
+static void
+close_range_test (void)
+{
+  struct support_descriptors *descrs = support_descriptors_list ();
+
+  /* Check if the temporary file descriptor has no no gaps.  */
+  int lowfd = open_multiple_temp_files ();
+
+  close_range_test_common (lowfd, 0);
+
+  /* Double check by check the /proc.  */
+  support_descriptors_check (descrs);
+  support_descriptors_free (descrs);
+}
+
+_Noreturn static int
+close_range_test_fn (void *arg)
+{
+  int lowfd = (int) ((uintptr_t) arg);
+  close_range_test_common (lowfd, 0);
+  exit (EXIT_SUCCESS);
+}
+
+/* Check if a clone_range on a subprocess created with CLONE_FILES close
+   the shared file descriptor table entries in the parent.  */
+static void
+close_range_test_subprocess (void)
+{
+  struct support_descriptors *descrs = support_descriptors_list ();
+
+  /* Check if the temporary file descriptor has no no gaps.  */
+  int lowfd = open_multiple_temp_files ();
+
+  enum { stack_size = 4096 };
+  DEFINE_STACK (stack, stack_size);
+  pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd, stack,
+		      stack_size, CLONE_FILES | SIGCHLD);
+  TEST_VERIFY_EXIT (pid > 0);
+  int status;
+  xwaitpid (pid, &status, 0);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_COMPARE (WEXITSTATUS(status), 0);
+
+  for (int i = lowfd; i < NFDS; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) < 0);
+
+  support_descriptors_check (descrs);
+  support_descriptors_free (descrs);
+}
+
+
+_Noreturn static int
+close_range_unshare_test_fn (void *arg)
+{
+  int lowfd = (int) ((uintptr_t) arg);
+  close_range_test_common (lowfd, CLOSE_RANGE_UNSHARE);
+  exit (EXIT_SUCCESS);
+}
+
+/* Check if a close_range with CLOSE_RANGE_UNSHARE issued from a subprocess
+   created with CLONE_FILES does not close the parent file descriptor list.  */
+static void
+close_range_unshare_test (void)
+{
+  struct support_descriptors *descrs1 = support_descriptors_list ();
+
+  /* Check if the temporary file descriptor has no no gaps.  */
+  int lowfd = open_multiple_temp_files ();
+
+  struct support_descriptors *descrs2 = support_descriptors_list ();
+
+  enum { stack_size = 4096 };
+  DEFINE_STACK (stack, stack_size);
+  pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
+		      stack, stack_size, CLONE_FILES | SIGCHLD);
+  TEST_VERIFY_EXIT (pid > 0);
+  int status;
+  xwaitpid (pid, &status, 0);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_COMPARE (WEXITSTATUS(status), 0);
+
+  for (int i = 0; i < NFDS; i++)
+    TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+  support_descriptors_check (descrs2);
+  support_descriptors_free (descrs2);
+
+  TEST_COMPARE (close_range (lowfd, lowfd + NFDS, 0), 0);
+
+  support_descriptors_check (descrs1);
+  support_descriptors_free (descrs1);
+}
+
+static int
+do_test (void)
+{
+  close_range_test_max_upper_limit ();
+  close_range_test ();
+  close_range_test_subprocess ();
+  close_range_unshare_test ();
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 4fff61818b..5089a21981 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2076,6 +2076,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 close_range 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 102ed47a9c..c3db0fc7ca 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2173,6 +2173,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 close_range F
 GLIBC_2.33 fstat F
 GLIBC_2.33 fstat64 F
 GLIBC_2.33 fstatat F
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 3/4] io: Add closefrom [BZ #10353]
  2020-12-23 16:36 [PATCH v3 1/4] support: Add xclone Adhemerval Zanella
  2020-12-23 16:36 ` [PATCH v3 2/4] Linux: Add close_range Adhemerval Zanella
@ 2020-12-23 16:36 ` Adhemerval Zanella
  2021-03-09 10:23   ` Florian Weimer
  2020-12-23 16:36 ` [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom Adhemerval Zanella
  2021-03-09  9:19 ` [PATCH v3 1/4] support: Add xclone Florian Weimer
  3 siblings, 1 reply; 16+ messages in thread
From: Adhemerval Zanella @ 2020-12-23 16:36 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

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

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
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom
  2020-12-23 16:36 [PATCH v3 1/4] support: Add xclone Adhemerval Zanella
  2020-12-23 16:36 ` [PATCH v3 2/4] Linux: Add close_range Adhemerval Zanella
  2020-12-23 16:36 ` [PATCH v3 3/4] io: Add closefrom [BZ #10353] Adhemerval Zanella
@ 2020-12-23 16:36 ` Adhemerval Zanella
  2021-03-09 10:45   ` Florian Weimer
  2021-03-09  9:19 ` [PATCH v3 1/4] support: Add xclone Florian Weimer
  3 siblings, 1 reply; 16+ messages in thread
From: Adhemerval Zanella @ 2020-12-23 16:36 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

Changes from previous version:

  - Fixed typos and commit wording.
  - Remove unecessary include on spawni.c.

--

This patch adds a way to close a range of file descriptors on
posix_spawn as a new file action.  The API is similar to the one
provided by Solaris 11 [1], where the file action causes the all open
file descriptors greater than or equal to input on to be closed when
the new process is spawned.

The function The function posix_spawn_file_actions_closefrom_np is safe
to be implemented by interacting over /proc/self/fd, since the Linux
spawni.c helper process does not use CLONE_FILES, so its has own file
descriptor table and any failure (in /proc operation) aborts the process
creation and returns an error to the caller.

I am aware that this file action might be redundant to the current
approach of POSIX in promoting O_CLOEXEC in more interfaces. However
O_CLOEXEC is still not the default and for some specific usages, the
caller needs to close all possible file descriptors to avoid them
leaking.  Some examples are CPython (discussed in BZ#10353) and OpenJDK
jspawnhelper [2] (where OpenJDK spawns a helper process to exactly
closes all file descriptors).  Most likely any environment which calls
functions that might open file descriptor under the hood and aim to use
posix_spawn might face the same requirement.

Checked on x86_64-linux-gnu, i686-linux-gnu, powerpc64le-linux-gnu, and
aarch64-linux-gnu.

[1] https://docs.oracle.com/cd/E36784_01/html/E36874/posix-spawn-file-actions-addclosefrom-np-3c.html
[2] https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/childproc.c#L82
---
 NEWS                                          |   5 +
 posix/Makefile                                |   4 +-
 posix/Versions                                |   3 +
 posix/spawn.h                                 |   8 +
 posix/spawn_faction_addclosefrom.c            |  57 ++++
 posix/spawn_faction_destroy.c                 |   1 +
 posix/spawn_int.h                             |   6 +
 posix/tst-spawn5.c                            | 283 ++++++++++++++++++
 sysdeps/generic/spawn_int_abi.h               |  24 ++
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/mach/hurd/spawni.c                    |   4 +
 sysdeps/posix/spawni.c                        |   4 +
 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/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 +
 sysdeps/unix/sysv/linux/spawn_int_abi.h       |  25 ++
 sysdeps/unix/sysv/linux/spawni.c              |  34 ++-
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 46 files changed, 475 insertions(+), 16 deletions(-)
 create mode 100644 posix/spawn_faction_addclosefrom.c
 create mode 100644 posix/tst-spawn5.c
 create mode 100644 sysdeps/generic/spawn_int_abi.h
 create mode 100644 sysdeps/unix/sysv/linux/spawn_int_abi.h

diff --git a/NEWS b/NEWS
index e6446f0548..60a13edcbe 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,11 @@ Major new features:
   greater than given integer.  This function is a GNU extension, although it
   also present in other systems.
 
+* The posix_spawn_file_actions_closefrom_np has been added, enabling
+  posix_spawn and posix_spawnp to close all file descriptors greater than
+  a giver integer.  This function is a GNU extension, although Solaris also
+  provides a similar function.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The mallinfo function is marked deprecated.  Callers should call
diff --git a/posix/Makefile b/posix/Makefile
index 4bfc8d942c..d40636547e 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -57,6 +57,7 @@ routines :=								      \
 	spawn_faction_init spawn_faction_destroy spawn_faction_addclose	      \
 	spawn_faction_addopen spawn_faction_adddup2 spawn_valid_fd	      \
 	spawn_faction_addchdir spawn_faction_addfchdir			      \
+	spawn_faction_addclosefrom					      \
 	spawnattr_init spawnattr_destroy				      \
 	spawnattr_getdefault spawnattr_setdefault			      \
 	spawnattr_getflags spawnattr_setflags				      \
@@ -102,7 +103,7 @@ tests		:= test-errno tstgetopt testfnm runtests runptests \
 		   tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \
 		   tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \
 		   bug-regex38 tst-regcomp-truncated tst-spawn-chdir \
-		   tst-wordexp-nocmd
+		   tst-wordexp-nocmd tst-spawn5
 tests-internal	:= bug-regex5 bug-regex20 bug-regex33 \
 		   tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
 		   tst-glob_lstat_compat tst-spawn4-compat
@@ -258,6 +259,7 @@ tst-exec-static-ARGS = $(tst-exec-ARGS)
 tst-execvpe5-ARGS = -- $(host-test-program-cmd)
 tst-spawn-ARGS = -- $(host-test-program-cmd)
 tst-spawn-static-ARGS = $(tst-spawn-ARGS)
+tst-spawn5-ARGS = -- $(host-test-program-cmd)
 tst-dir-ARGS = `pwd` `cd $(common-objdir)/$(subdir); pwd` `cd $(common-objdir); pwd` $(objpfx)tst-dir
 tst-chmod-ARGS = $(objdir)
 tst-vfork3-ARGS = --test-dir=$(objpfx)
diff --git a/posix/Versions b/posix/Versions
index 7d06a6d0c0..95d7b01126 100644
--- a/posix/Versions
+++ b/posix/Versions
@@ -147,6 +147,9 @@ libc {
   }
   GLIBC_2.30 {
   }
+  GLIBC_2.33 {
+    posix_spawn_file_actions_addclosefrom_np;
+  }
   GLIBC_PRIVATE {
     __libc_fork; __libc_pread; __libc_pwrite;
     __nanosleep_nocancel; __pause_nocancel;
diff --git a/posix/spawn.h b/posix/spawn.h
index be6bd591a3..e81b4a4e90 100644
--- a/posix/spawn.h
+++ b/posix/spawn.h
@@ -213,6 +213,14 @@ extern int posix_spawn_file_actions_addchdir_np (posix_spawn_file_actions_t *
 extern int posix_spawn_file_actions_addfchdir_np (posix_spawn_file_actions_t *,
 						  int __fd)
      __THROW __nonnull ((1));
+
+/* Add an action to close all file descriptor greater than FROM during
+   spawn.  This affects the subsequent file actions.  */
+extern int
+posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *,
+					  int __from)
+     __THROW __nonnull ((1));
+
 #endif
 
 __END_DECLS
diff --git a/posix/spawn_faction_addclosefrom.c b/posix/spawn_faction_addclosefrom.c
new file mode 100644
index 0000000000..3b5e73fe32
--- /dev/null
+++ b/posix/spawn_faction_addclosefrom.c
@@ -0,0 +1,57 @@
+/* Add a closefrom to a file action list for posix_spawn.
+   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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <spawn.h>
+#include <unistd.h>
+#include <spawn_int.h>
+
+int
+__posix_spawn_file_actions_addclosefrom (posix_spawn_file_actions_t
+					 *file_actions, int from)
+{
+#if __SPAWN_SUPPORT_CLOSEFROM
+  struct __spawn_action *rec;
+
+  if (!__spawn_valid_fd (from))
+    return EBADF;
+
+  /* Allocate more memory if needed.  */
+  if (file_actions->__used == file_actions->__allocated
+      && __posix_spawn_file_actions_realloc (file_actions) != 0)
+    /* This can only mean we ran out of memory.  */
+    return ENOMEM;
+
+  /* Add the new value.  */
+  rec = &file_actions->__actions[file_actions->__used];
+  rec->tag = spawn_do_closefrom;
+  rec->action.closefrom_action.from = from;
+
+  /* Account for the new entry.  */
+  ++file_actions->__used;
+
+  return 0;
+#else
+  return EINVAL;
+#endif
+}
+weak_alias (__posix_spawn_file_actions_addclosefrom,
+	    posix_spawn_file_actions_addclosefrom_np)
+#if !__SPAWN_SUPPORT_CLOSEFROM
+stub_warning (posix_spawn_file_actions_addclosefrom_np)
+#endif
diff --git a/posix/spawn_faction_destroy.c b/posix/spawn_faction_destroy.c
index 098385e1e0..33045e2c7f 100644
--- a/posix/spawn_faction_destroy.c
+++ b/posix/spawn_faction_destroy.c
@@ -39,6 +39,7 @@ __posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions)
 	case spawn_do_close:
 	case spawn_do_dup2:
 	case spawn_do_fchdir:
+	case spawn_do_closefrom:
 	  /* No cleanup required.  */
 	  break;
 	}
diff --git a/posix/spawn_int.h b/posix/spawn_int.h
index 9be54c0416..512dcea0da 100644
--- a/posix/spawn_int.h
+++ b/posix/spawn_int.h
@@ -20,6 +20,7 @@
 #define _SPAWN_INT_H
 
 #include <spawn.h>
+#include <spawn_int_abi.h>
 #include <stdbool.h>
 
 /* Data structure to contain the action information.  */
@@ -32,6 +33,7 @@ struct __spawn_action
     spawn_do_open,
     spawn_do_chdir,
     spawn_do_fchdir,
+    spawn_do_closefrom,
   } tag;
 
   union
@@ -60,6 +62,10 @@ struct __spawn_action
     {
       int fd;
     } fchdir_action;
+    struct
+    {
+      int from;
+    } closefrom_action;
   } action;
 };
 
diff --git a/posix/tst-spawn5.c b/posix/tst-spawn5.c
new file mode 100644
index 0000000000..a20e9c9ac7
--- /dev/null
+++ b/posix/tst-spawn5.c
@@ -0,0 +1,283 @@
+/* Tests for posix_spawn signal handling.
+   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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <spawn.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <support/support.h>
+
+#include <arch-fd_to_filename.h>
+#include <array_length.h>
+
+/* Nonzero if the program gets called via `exec'.  */
+static int restart;
+
+/* Hold the four initial argument used to respawn the process.  */
+static char *initial_argv[7];
+
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+
+#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;
+}
+
+/* Called on process re-execution.  The arguments are the expected opened
+   file descriptors.  */
+_Noreturn static void
+handle_restart (int argc, char *argv[])
+{
+  size_t nfds = argc > 1 ? argc - 1 : 0;
+  struct fd_t
+  {
+    int fd;
+    _Bool found;
+  } *fds = xmalloc (sizeof (struct fd_t) * nfds);
+  for (int i = 0; i < nfds; i++)
+    {
+      char *endptr;
+      long int fd = strtol (argv[i+1], &endptr, 10);
+      if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
+        FAIL_EXIT1 ("readdir: invalid file descriptor value: %s", argv[i]);
+
+      fds[i].fd = fd;
+      fds[i].found = false;
+    }
+
+  DIR *dirp = opendir (FD_TO_FILENAME_PREFIX);
+  if (dirp == NULL)
+    FAIL_EXIT1 ("opendir (\"" FD_TO_FILENAME_PREFIX "\"): %m");
+
+  while (true)
+    {
+      errno = 0;
+      struct dirent64 *e = readdir64 (dirp);
+      if (e == NULL)
+        {
+          if (errno != 0)
+            FAIL_EXIT1 ("readdir: %m");
+          break;
+        }
+
+      if (e->d_name[0] == '.')
+        continue;
+
+      char *endptr;
+      long int fd = strtol (e->d_name, &endptr, 10);
+      if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
+        FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s",
+                    e->d_name);
+
+      /* Skip the descriptor which is used to enumerate the descriptors.  */
+      if (fd == dirfd (dirp)
+          || fd == STDIN_FILENO
+	  || fd == STDOUT_FILENO
+	  || fd == STDERR_FILENO)
+        continue;
+
+      bool found = false;
+      for (int i = 0; i < nfds; i++)
+	if (fds[i].fd == fd)
+	  fds[i].found = found = true;
+
+      if (!found)
+        FAIL_EXIT1 ("unexpected open file descriptor: %ld", fd);
+    }
+  closedir (dirp);
+
+  for (int i = 0; i < nfds; i++)
+    if (!fds[i].found)
+      FAIL_EXIT1 ("file descriptor %d not opened", fds[i].fd);
+
+  free (fds);
+
+  exit (EXIT_SUCCESS);
+}
+
+static void
+spawn_closefrom_test (posix_spawn_file_actions_t *fa, int lowfd, int highfd,
+		      int *extrafds, size_t nextrafds)
+{
+  /* 6 elements from initial_argv (path to ld.so, '--library-path', the
+     path', application name', '--direct', and '--restart'), up to
+     2 * maximum_fd arguments (the expected open file descriptors), plus
+     NULL.  */
+  enum { argv_size = array_length (initial_argv) + 2 * NFDS + 1 };
+  char *args[argv_size];
+  int argc = 0;
+
+  for (char **arg = initial_argv; *arg != NULL; arg++)
+    args[argc++] = *arg;
+
+  for (int i = lowfd; i < highfd; i++)
+    args[argc++] = xasprintf ("%d", i);
+
+  for (int i = 0; i < nextrafds; i++)
+    args[argc++] = xasprintf ("%d", extrafds[i]);
+
+  args[argc] = NULL;
+  TEST_VERIFY (argc < argv_size);
+
+  pid_t pid;
+  int status;
+
+  TEST_COMPARE (posix_spawn (&pid, args[0], fa, NULL, args, environ), 0);
+  TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_VERIFY (!WIFSIGNALED (status));
+  TEST_COMPARE (WEXITSTATUS (status), 0);
+}
+
+static void
+do_test_closefrom (void)
+{
+  int lowfd = open_multiple_temp_files ();
+  const int half_fd = lowfd + NFDS / 2;
+
+  /* Close half of the descriptors and check result.  */
+  {
+    posix_spawn_file_actions_t fa;
+    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
+
+    int ret = posix_spawn_file_actions_addclosefrom_np (&fa, half_fd);
+    if (ret == EINVAL)
+      /* Hurd currently does not support closefrom fileaction.  */
+      FAIL_UNSUPPORTED ("posix_spawn_file_actions_addclosefrom_np unsupported");
+    TEST_COMPARE (ret, 0);
+
+    spawn_closefrom_test (&fa, lowfd, half_fd, NULL, 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
+  }
+
+  /* Create some gaps, close up to a threshold, and check result.  */
+  xclose (lowfd + 57);
+  xclose (lowfd + 78);
+  xclose (lowfd + 81);
+  xclose (lowfd + 82);
+  xclose (lowfd + 84);
+  xclose (lowfd + 90);
+
+  {
+    posix_spawn_file_actions_t fa;
+    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, half_fd), 0);
+
+    spawn_closefrom_test (&fa, lowfd, half_fd, NULL, 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
+  }
+
+  /* Close the remaining but the last one.  */
+  {
+    posix_spawn_file_actions_t fa;
+    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
+
+    spawn_closefrom_test (&fa, lowfd, lowfd + 1, NULL, 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
+  }
+
+  /* Close everything.  */
+  {
+    posix_spawn_file_actions_t fa;
+    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd), 0);
+
+    spawn_closefrom_test (&fa, lowfd, lowfd, NULL, 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
+  }
+
+  /* Close a range and add some file actions.  */
+  {
+    posix_spawn_file_actions_t fa;
+    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
+
+    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
+    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
+						    0666, O_RDONLY), 0);
+    TEST_COMPARE (posix_spawn_file_actions_adddup2 (&fa, lowfd, lowfd + 1), 0);
+    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
+						    0666, O_RDONLY), 0);
+
+    spawn_closefrom_test (&fa, lowfd, lowfd, (int[]){lowfd, lowfd + 1}, 2);
+
+    TEST_COMPARE (posix_spawn_file_actions_destroy (&fa), 0);
+  }
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+
+     - one or four parameters if called initially:
+       + argv[1]: path for ld.so        optional
+       + argv[2]: "--library-path"      optional
+       + argv[3]: the library path      optional
+       + argv[4]: the application name
+
+     - six parameters left if called through re-execution:
+       + argv[1]: the application name
+       + argv[2]: first expected open file descriptor
+       + argv[n]: last expected open file descritptor
+
+     * When built with --enable-hardcoded-path-in-tests or issued without
+       using the loader directly.  */
+
+  if (restart)
+    handle_restart (argc, argv);
+
+  initial_argv[0] = argv[1]; /* path for ld.so  */
+  initial_argv[1] = argv[2]; /* "--library-path"  */
+  initial_argv[2] = argv[3]; /* the library path  */
+  initial_argv[3] = argv[4]; /* the application name  */
+  initial_argv[4] = (char *) "--direct";
+  initial_argv[5] = (char *) "--restart";
+
+  do_test_closefrom ();
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/spawn_int_abi.h b/sysdeps/generic/spawn_int_abi.h
new file mode 100644
index 0000000000..bfc8961598
--- /dev/null
+++ b/sysdeps/generic/spawn_int_abi.h
@@ -0,0 +1,24 @@
+/* Internal ABI specific for posix_spawn functionality.  Generic 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
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SPAWN_INT_ABI_H
+#define _SPAWN_INT_ABI_H
+
+#define __SPAWN_SUPPORT_CLOSEFROM 0
+
+#endif /* _SPAWN_INT_H */
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index fce67cf3ca..5b23d62b5d 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2201,6 +2201,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/mach/hurd/spawni.c b/sysdeps/mach/hurd/spawni.c
index 178b271e4e..9ea4fc972f 100644
--- a/sysdeps/mach/hurd/spawni.c
+++ b/sysdeps/mach/hurd/spawni.c
@@ -600,6 +600,10 @@ __spawni (pid_t *pid, const char *file,
 	  case spawn_do_fchdir:
 	    err = child_fchdir (action->action.fchdir_action.fd);
 	    break;
+
+	  case spawn_do_closefrom:
+	    err = EINVAL;
+	    break;
 	  }
 
 	if (err)
diff --git a/sysdeps/posix/spawni.c b/sysdeps/posix/spawni.c
index 943a598771..d05d2c8520 100644
--- a/sysdeps/posix/spawni.c
+++ b/sysdeps/posix/spawni.c
@@ -231,6 +231,10 @@ __spawni_child (void *arguments)
 	      if (__fchdir (action->action.fchdir_action.fd) != 0)
 		goto fail;
 	      break;
+
+	    case spawn_do_closefrom:
+	      __set_errno (EINVAL);
+	      goto fail;
 	    }
 	}
     }
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 811d3b3cd9..65d5c3cee4 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2171,5 +2171,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index d0bc011811..ac55ceba70 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2253,6 +2253,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index c4e26b3cf6..24e623875d 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -1931,5 +1931,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 5ccd1fb07f..50086104ea 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -154,6 +154,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 5043e483b8..a4ebfccc74 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -152,6 +152,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 6f3718cd14..4b45f0a8a0 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2115,5 +2115,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 84b474dba4..b574604263 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2074,6 +2074,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 4c1399102e..51795cd925 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2240,6 +2240,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 9fcaa40c34..bafbc98f69 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2106,6 +2106,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index ccb780d9c7..8c3fc11399 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -155,6 +155,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 3afaa1c70e..10a11773f2 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2186,6 +2186,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 266063d0e8..e908a7a19a 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2166,5 +2166,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 5d893ec4d1..d8f3ff6dec 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2162,5 +2162,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 6207a15a09..1c41ce7c83 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2157,6 +2157,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 860e5fecfa..dcec226837 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2154,6 +2154,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index daed5ae2a8..022a7ec721 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2163,6 +2163,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 7b6edfe8f5..a312612703 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2157,6 +2157,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 9dd7d64500..d4e4096a11 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2204,5 +2204,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index c561e5cf36..01705658c0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2213,6 +2213,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index d25bf75ed3..eea881eeae 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2245,6 +2245,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 248fab8212..bb346370ad 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2076,6 +2076,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index d02fa6b3d2..0dad53bc80 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2366,5 +2366,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 56d1a16b2a..7ee1f2e189 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -1304,6 +1304,7 @@ GLIBC_2.33 posix_openpt F
 GLIBC_2.33 posix_spawn F
 GLIBC_2.33 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.33 posix_spawn_file_actions_addclose F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 posix_spawn_file_actions_adddup2 F
 GLIBC_2.33 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.33 posix_spawn_file_actions_addopen F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 8fcd329728..51fc25677d 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2133,5 +2133,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index b06cc94710..61bda38fb0 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2211,6 +2211,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 6ede1579ca..a73bc16ae0 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2112,6 +2112,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 0f426028a1..974090f39b 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2080,6 +2080,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 8637b683d6..910c8e752d 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2078,6 +2078,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index dc7c01f734..fabbdaeef0 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2202,6 +2202,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 67d361c881..0ad7f0b18c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2129,6 +2129,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/spawn_int_abi.h b/sysdeps/unix/sysv/linux/spawn_int_abi.h
new file mode 100644
index 0000000000..84b31104c5
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/spawn_int_abi.h
@@ -0,0 +1,25 @@
+/* Internal ABI specific for posix_spawn functionality.  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
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SPAWN_INT_ABI_H
+#define _SPAWN_INT_ABI_H
+
+/* spawni.c implements closefrom by interacting over /proc/self/fd.  */
+#define __SPAWN_SUPPORT_CLOSEFROM 1
+
+#endif /* _SPAWN_INT_H */
diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
index f157bfffd2..ea87d8b5e6 100644
--- a/sysdeps/unix/sysv/linux/spawni.c
+++ b/sysdeps/unix/sysv/linux/spawni.c
@@ -16,22 +16,16 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <spawn.h>
-#include <fcntl.h>
-#include <paths.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <sys/mman.h>
-#include <not-cancel.h>
+#include <internal-signals.h>
+#include <ldsodefs.h>
 #include <local-setxid.h>
+#include <not-cancel.h>
+#include <paths.h>
 #include <shlib-compat.h>
-#include <nptl/pthreadP.h>
-#include <dl-sysdep.h>
-#include <libc-pointer-arith.h>
-#include <ldsodefs.h>
-#include "spawn_int.h"
+#include <spawn.h>
+#include <spawn_int.h>
+#include <sysdep.h>
+#include <sys/resource.h>
 
 /* The Linux implementation of posix_spawn{p} uses the clone syscall directly
    with CLONE_VM and CLONE_VFORK flags and an allocated stack.  The new stack
@@ -280,6 +274,14 @@ __spawni_child (void *arguments)
 	      if (__fchdir (action->action.fchdir_action.fd) != 0)
 		goto fail;
 	      break;
+
+	    case spawn_do_closefrom:
+	      {
+		int lowfd = action->action.closefrom_action.from;
+	        int r = INLINE_SYSCALL_CALL (close_range, lowfd, ~0U, 0);
+		if (r != 0 && !__closefrom_fallback (lowfd))
+		  goto fail;
+	      } break;
 	    }
 	}
     }
@@ -344,7 +346,9 @@ __spawnix (pid_t * pid, const char *file,
   /* We need at least a few pages in case the compiler's stack checking is
      enabled.  In some configs, it is known to use at least 24KiB.  We use
      32KiB to be "safe" from anything the compiler might do.  Besides, the
-     extra pages won't actually be allocated unless they get used.  */
+     extra pages won't actually be allocated unless they get used.
+     It also acts the slack for spawn_closefrom (including MIPS64 getdents64
+     where it might use about 1k extra stack space.  */
   argv_size += (32 * 1024);
   size_t stack_size = ALIGN_UP (argv_size, GLRO(dl_pagesize));
   void *stack = __mmap (NULL, stack_size, prot,
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 94bb5a0fa7..c17d5d93c3 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2087,6 +2087,7 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index e94ce733ab..e8ff4b7cc9 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2184,5 +2184,6 @@ GLIBC_2.33 lstat64 F
 GLIBC_2.33 mallinfo2 F
 GLIBC_2.33 mknod F
 GLIBC_2.33 mknodat F
+GLIBC_2.33 posix_spawn_file_actions_addclosefrom_np F
 GLIBC_2.33 stat F
 GLIBC_2.33 stat64 F
-- 
2.25.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 1/4] support: Add xclone
  2020-12-23 16:36 [PATCH v3 1/4] support: Add xclone Adhemerval Zanella
                   ` (2 preceding siblings ...)
  2020-12-23 16:36 ` [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom Adhemerval Zanella
@ 2021-03-09  9:19 ` Florian Weimer
  2021-03-09 16:17   ` Adhemerval Zanella
  3 siblings, 1 reply; 16+ messages in thread
From: Florian Weimer @ 2021-03-09  9:19 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> +#ifdef __linux__
> +# define DEFINE_STACK(name, size) \
> +  char name[size] __attribute__ ((aligned))

In the light of the MINSIGSTKSZ problems, is this really an appropriate
interface?  Should there be separate functions for stack allocation and
deallocation?

The actual xclone implementation looks okay to me.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 2/4] Linux: Add close_range
  2020-12-23 16:36 ` [PATCH v3 2/4] Linux: Add close_range Adhemerval Zanella
@ 2021-03-09  9:47   ` Florian Weimer
  2021-03-09 10:02     ` Christian Brauner
  2021-03-09 17:53     ` Adhemerval Zanella
  0 siblings, 2 replies; 16+ messages in thread
From: Florian Weimer @ 2021-03-09  9:47 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> diff --git a/manual/llio.texi b/manual/llio.texi
> index c0a53e1a6e..ceb18ac89a 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -284,6 +284,44 @@ of trying to close its underlying file descriptor with @code{close}.
>  This flushes any buffered output and updates the stream object to
>  indicate that it is closed.
>  
> +@deftypefun int close_range (unsigned int @var{lowfd}, unsigned int @var{maxfd}, int @var{flags})
> +@standards{Linux, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
> +@c This is a syscall for Linux v5.9.  There is no fallback emulation for
> +@c older kernels.
> +
> +The function @code{clode_range} closes the file descriptor from @var{lowfd}
> +to @var{maxfd} (inclusive).  This function is similar to call @code{close} in
> +specified file descriptor range depending on the @var{flags}.
> +
> +The @var{flags} add options on how the files are closes.  Linux currently
> +supports:.

Spurios “.” at end of line.

> +@vtable @code
> +@item CLOSE_RANGE_UNSHARE
> +Unshare the file descriptor table before closing file descriptors.
> +@end vtable
> +
> +The normal return value from @code{close_range} is @math{0}; a value
> +of @math{-1} is returned in case of failure.  The following @code{errno} error
> +conditions are defined for this function:
> +
> +@table @code
> +@item EINVAL
> +The @var{lowfd} value is larger than @var{maxfd} or an unsupported @var{flags}
> +is used.
> +
> +@item ENOMEM
> +Either there is not enough memory for the operation, or the process is
> +out of address space.
> +
> +@item EMFILE
> +The process has too many files open.
> +The maximum number of file descriptors is controlled by the
> +@end table
> +@end deftypefun

The ENOMEM and EMFILE descriptions are not really clear in this context.
Are these failures only possible with CLOSE_RANGE_UNSHARE?

It may make sense to mention the lack of emulation (so that callers have
to be aware of ENOSYS for the time being) and that the function is
specific to Linux.

Based on the description, it is not clear what happens if the range
contains closed descriptors.  Maybe mention that already-closed
descriptors are simply skipped?

> index c35f783e2a..e2a6fa763f 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -169,6 +169,9 @@ libc {
>    }
>    GLIBC_2.32 {
>    }
> +  GLIBC_2.33 {
> +    close_range;
> +  }

Needs to be GLIBC_2.34 now.  Also the copyright year needs adjusting.

> diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> index c315cc5cb8..799c59512c 100644
> --- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> +++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
> @@ -33,4 +33,15 @@
>     not detached and has not been joined.  */
>  extern __pid_t gettid (void) __THROW;
>  
> +/* Unshare the file descriptor table before closing file descriptors.  */
> +#define CLOSE_RANGE_UNSHARE     (1U << 1)

Needs to be indented.

Please include <linux/close_range.h> if available.  It's a separate
header so that we can use it.

I think if you do that, the consistency check won't add much value
because it can't really test anything.

> +/* Close all file descriptors in the range FD up to MAX_FD.  The flag FLAGS
> +   are define by the CLOSE_RANGE prefix.  This function behaves like close
> +   on the range, but in a fail-safe where it will either fail and not close
> +   any file descriptor or close all of them.  Returns 0 on successor or -1
> +   for failure (and sets errno accordingly).  */
> +extern int close_range (unsigned int __fd, unsigned int __max_fd,
> +			int __flags) __THROW;
> +
>  #endif

Maybe add /* __USE_GNU */ to the #endif, given that it's now at a
distance from the #ifdef.

> diff --git a/sysdeps/unix/sysv/linux/tst-close_range.c b/sysdeps/unix/sysv/linux/tst-close_range.c
> new file mode 100644
> index 0000000000..131cf27c72
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-close_range.c
> @@ -0,0 +1,222 @@

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

Okay, this ensures that there are no gaps.

> +
> +static void
> +close_range_test_common (int lowfd, unsigned int flags)
> +{
> +  const int maximum_fd = lowfd + NFDS;
> +  const int half_fd = maximum_fd / 2;
> +  const int gap_1 = maximum_fd - 8;

I think half_fd should be should be lowfd + NFDS / 2.

> +_Noreturn static int
> +close_range_test_fn (void *arg)
> +{
> +  int lowfd = (int) ((uintptr_t) arg);
> +  close_range_test_common (lowfd, 0);
> +  exit (EXIT_SUCCESS);
> +}
> +
> +/* Check if a clone_range on a subprocess created with CLONE_FILES close
> +   the shared file descriptor table entries in the parent.  */
> +static void
> +close_range_test_subprocess (void)
> +{
> +  struct support_descriptors *descrs = support_descriptors_list ();
> +
> +  /* Check if the temporary file descriptor has no no gaps.  */
> +  int lowfd = open_multiple_temp_files ();
> +
> +  enum { stack_size = 4096 };
> +  DEFINE_STACK (stack, stack_size);
> +  pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd, stack,
> +		      stack_size, CLONE_FILES | SIGCHLD);

stack_size is too small, I think.  Future error checking (possible with
clone3) would lead to failures.

> +static void
> +close_range_unshare_test (void)
> +{
> +  struct support_descriptors *descrs1 = support_descriptors_list ();
> +
> +  /* Check if the temporary file descriptor has no no gaps.  */
> +  int lowfd = open_multiple_temp_files ();
> +
> +  struct support_descriptors *descrs2 = support_descriptors_list ();
> +
> +  enum { stack_size = 4096 };
> +  DEFINE_STACK (stack, stack_size);
> +  pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
> +		      stack, stack_size, CLONE_FILES | SIGCHLD);

Likewise.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 2/4] Linux: Add close_range
  2021-03-09  9:47   ` Florian Weimer
@ 2021-03-09 10:02     ` Christian Brauner
  2021-03-09 17:53     ` Adhemerval Zanella
  1 sibling, 0 replies; 16+ messages in thread
From: Christian Brauner @ 2021-03-09 10:02 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Adhemerval Zanella via Libc-alpha

On Tue, Mar 09, 2021 at 10:47:59AM +0100, Florian Weimer via Libc-alpha wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
> > diff --git a/manual/llio.texi b/manual/llio.texi
> > index c0a53e1a6e..ceb18ac89a 100644
> > --- a/manual/llio.texi
> > +++ b/manual/llio.texi
> > @@ -284,6 +284,44 @@ of trying to close its underlying file descriptor with @code{close}.
> >  This flushes any buffered output and updates the stream object to
> >  indicate that it is closed.
> >  
> > +@deftypefun int close_range (unsigned int @var{lowfd}, unsigned int @var{maxfd}, int @var{flags})
> > +@standards{Linux, unistd.h}
> > +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
> > +@c This is a syscall for Linux v5.9.  There is no fallback emulation for
> > +@c older kernels.
> > +
> > +The function @code{clode_range} closes the file descriptor from @var{lowfd}
> > +to @var{maxfd} (inclusive).  This function is similar to call @code{close} in
> > +specified file descriptor range depending on the @var{flags}.
> > +
> > +The @var{flags} add options on how the files are closes.  Linux currently
> > +supports:.
> 
> Spurios “.” at end of line.
> 
> > +@vtable @code
> > +@item CLOSE_RANGE_UNSHARE
> > +Unshare the file descriptor table before closing file descriptors.
> > +@end vtable
> > +
> > +The normal return value from @code{close_range} is @math{0}; a value
> > +of @math{-1} is returned in case of failure.  The following @code{errno} error
> > +conditions are defined for this function:
> > +
> > +@table @code
> > +@item EINVAL
> > +The @var{lowfd} value is larger than @var{maxfd} or an unsupported @var{flags}
> > +is used.
> > +
> > +@item ENOMEM
> > +Either there is not enough memory for the operation, or the process is
> > +out of address space.
> > +
> > +@item EMFILE
> > +The process has too many files open.
> > +The maximum number of file descriptors is controlled by the
> > +@end table
> > +@end deftypefun
> 
> The ENOMEM and EMFILE descriptions are not really clear in this context.
> Are these failures only possible with CLOSE_RANGE_UNSHARE?

Hey, just to confirm: Yes, these errors can only occur with
CLOSE_RANGE_UNSHARE when the new file descriptor table is created.

Christian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 3/4] io: Add closefrom [BZ #10353]
  2020-12-23 16:36 ` [PATCH v3 3/4] io: Add closefrom [BZ #10353] Adhemerval Zanella
@ 2021-03-09 10:23   ` Florian Weimer
  2021-03-09 19:07     ` Adhemerval Zanella
  0 siblings, 1 reply; 16+ messages in thread
From: Florian Weimer @ 2021-03-09 10:23 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* 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


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom
  2020-12-23 16:36 ` [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom Adhemerval Zanella
@ 2021-03-09 10:45   ` Florian Weimer
  2021-03-09 19:48     ` Adhemerval Zanella
  0 siblings, 1 reply; 16+ messages in thread
From: Florian Weimer @ 2021-03-09 10:45 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> This patch adds a way to close a range of file descriptors on
> posix_spawn as a new file action.  The API is similar to the one
> provided by Solaris 11 [1], where the file action causes the all open
> file descriptors greater than or equal to input on to be closed when
> the new process is spawned.

Commit subject should say posix_spawn_file_actions_closefrom_np.

> The function The function posix_spawn_file_actions_closefrom_np is safe
> to be implemented by interacting over /proc/self/fd, since the Linux
> spawni.c helper process does not use CLONE_FILES, so its has own file
> descriptor table and any failure (in /proc operation) aborts the process
> creation and returns an error to the caller.

Duplicate “The function”, ”iterating” instead of “interacting”.

>
> diff --git a/NEWS b/NEWS
> index e6446f0548..60a13edcbe 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -34,6 +34,11 @@ Major new features:
>    greater than given integer.  This function is a GNU extension, although it
>    also present in other systems.
>  
> +* The posix_spawn_file_actions_closefrom_np has been added, enabling
> +  posix_spawn and posix_spawnp to close all file descriptors greater than
> +  a giver integer.  This function is a GNU extension, although Solaris also
> +  provides a similar function.

Missing “function”.  “great than or equal to”.

> diff --git a/posix/Versions b/posix/Versions
> index 7d06a6d0c0..95d7b01126 100644
> --- a/posix/Versions
> +++ b/posix/Versions
> @@ -147,6 +147,9 @@ libc {
>    }
>    GLIBC_2.30 {
>    }
> +  GLIBC_2.33 {
> +    posix_spawn_file_actions_addclosefrom_np;
> +  }

Needs to be GLIBC_2.34 now.  Copyright years need adjusting as well.

> diff --git a/posix/spawn.h b/posix/spawn.h
> index be6bd591a3..e81b4a4e90 100644
> --- a/posix/spawn.h
> +++ b/posix/spawn.h
> @@ -213,6 +213,14 @@ extern int posix_spawn_file_actions_addchdir_np (posix_spawn_file_actions_t *
>  extern int posix_spawn_file_actions_addfchdir_np (posix_spawn_file_actions_t *,
>  						  int __fd)
>       __THROW __nonnull ((1));
> +
> +/* Add an action to close all file descriptor greater than FROM during
> +   spawn.  This affects the subsequent file actions.  */
> +extern int
> +posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *,
> +					  int __from)
> +     __THROW __nonnull ((1));
> +
>  #endif

Likewise, “greater than or equal to”.

> diff --git a/posix/tst-spawn5.c b/posix/tst-spawn5.c
> new file mode 100644
> index 0000000000..a20e9c9ac7
> --- /dev/null
> +++ b/posix/tst-spawn5.c
> @@ -0,0 +1,283 @@

> +/* Nonzero if the program gets called via `exec'.  */
> +static int restart;

“was called”?

> +/* Hold the four initial argument used to respawn the process.  */
> +static char *initial_argv[7];

four != 7?

> +/* Called on process re-execution.  The arguments are the expected opened
> +   file descriptors.  */
> +_Noreturn static void
> +handle_restart (int argc, char *argv[])
> +{

> +      char *endptr;
> +      long int fd = strtol (e->d_name, &endptr, 10);
> +      if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
> +        FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s",
> +                    e->d_name);

Maybe use strtoul here?

> +static void
> +do_test_closefrom (void)
> +{

> +  /* Close a range and add some file actions.  */
> +  {
> +    posix_spawn_file_actions_t fa;
> +    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
> +
> +    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
> +    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
> +						    0666, O_RDONLY), 0);
> +    TEST_COMPARE (posix_spawn_file_actions_adddup2 (&fa, lowfd, lowfd + 1), 0);
> +    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
> +						    0666, O_RDONLY), 0);
> +
> +    spawn_closefrom_test (&fa, lowfd, lowfd, (int[]){lowfd, lowfd + 1}, 2);

I believe the closefrom should start add lowfd here, to check that the
closing is ordered with regards to the other operations.

> diff --git a/sysdeps/generic/spawn_int_abi.h b/sysdeps/generic/spawn_int_abi.h
> new file mode 100644
> index 0000000000..bfc8961598
> --- /dev/null
> +++ b/sysdeps/generic/spawn_int_abi.h
> @@ -0,0 +1,24 @@
> +/* Internal ABI specific for posix_spawn functionality.  Generic version.

> +#ifndef _SPAWN_INT_ABI_H
> +#define _SPAWN_INT_ABI_H
> +
> +#define __SPAWN_SUPPORT_CLOSEFROM 0
> +
> +#endif /* _SPAWN_INT_H */

Final comment is wrong.  The header is misnamed because the macro does
not actually impact ABI.

> diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
> index f157bfffd2..ea87d8b5e6 100644
> --- a/sysdeps/unix/sysv/linux/spawni.c
> +++ b/sysdeps/unix/sysv/linux/spawni.c

>  /* The Linux implementation of posix_spawn{p} uses the clone syscall directly
>     with CLONE_VM and CLONE_VFORK flags and an allocated stack.  The new stack
> @@ -280,6 +274,14 @@ __spawni_child (void *arguments)
>  	      if (__fchdir (action->action.fchdir_action.fd) != 0)
>  		goto fail;
>  	      break;
> +
> +	    case spawn_do_closefrom:
> +	      {
> +		int lowfd = action->action.closefrom_action.from;
> +	        int r = INLINE_SYSCALL_CALL (close_range, lowfd, ~0U, 0);
> +		if (r != 0 && !__closefrom_fallback (lowfd))
> +		  goto fail;
> +	      } break;

If you implement the agressive up-to-INT_MAX closing in
__closefrom_fallback, this probably needs a flag that it disables it
when called from here.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 1/4] support: Add xclone
  2021-03-09  9:19 ` [PATCH v3 1/4] support: Add xclone Florian Weimer
@ 2021-03-09 16:17   ` Adhemerval Zanella
  0 siblings, 0 replies; 16+ messages in thread
From: Adhemerval Zanella @ 2021-03-09 16:17 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 09/03/2021 06:19, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> +#ifdef __linux__
>> +# define DEFINE_STACK(name, size) \
>> +  char name[size] __attribute__ ((aligned))
> 
> In the light of the MINSIGSTKSZ problems, is this really an appropriate
> interface?  Should there be separate functions for stack allocation and
> deallocation?

Indeed, I think routines similar to xalloc_sigstack should be better
than this ad hoc routine to make the stack correctly aligned.

> 
> The actual xclone implementation looks okay to me.
> 
> Thanks,
> Florian
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 2/4] Linux: Add close_range
  2021-03-09  9:47   ` Florian Weimer
  2021-03-09 10:02     ` Christian Brauner
@ 2021-03-09 17:53     ` Adhemerval Zanella
  2021-03-09 18:30       ` Florian Weimer
  1 sibling, 1 reply; 16+ messages in thread
From: Adhemerval Zanella @ 2021-03-09 17:53 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 09/03/2021 06:47, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> diff --git a/manual/llio.texi b/manual/llio.texi
>> index c0a53e1a6e..ceb18ac89a 100644
>> --- a/manual/llio.texi
>> +++ b/manual/llio.texi
>> @@ -284,6 +284,44 @@ of trying to close its underlying file descriptor with @code{close}.
>>  This flushes any buffered output and updates the stream object to
>>  indicate that it is closed.
>>  
>> +@deftypefun int close_range (unsigned int @var{lowfd}, unsigned int @var{maxfd}, int @var{flags})
>> +@standards{Linux, unistd.h}
>> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
>> +@c This is a syscall for Linux v5.9.  There is no fallback emulation for
>> +@c older kernels.
>> +
>> +The function @code{clode_range} closes the file descriptor from @var{lowfd}

I will fix this 'clode_range' as well.

>> +to @var{maxfd} (inclusive).  This function is similar to call @code{close} in
>> +specified file descriptor range depending on the @var{flags}.
>> +
>> +The @var{flags} add options on how the files are closes.  Linux currently
>> +supports:.
> 
> Spurios “.” at end of line.

Ack.

> 
>> +@vtable @code
>> +@item CLOSE_RANGE_UNSHARE
>> +Unshare the file descriptor table before closing file descriptors.
>> +@end vtable
>> +
>> +The normal return value from @code{close_range} is @math{0}; a value
>> +of @math{-1} is returned in case of failure.  The following @code{errno} error
>> +conditions are defined for this function:
>> +
>> +@table @code
>> +@item EINVAL
>> +The @var{lowfd} value is larger than @var{maxfd} or an unsupported @var{flags}
>> +is used.
>> +
>> +@item ENOMEM
>> +Either there is not enough memory for the operation, or the process is
>> +out of address space.
>> +
>> +@item EMFILE
>> +The process has too many files open.
>> +The maximum number of file descriptors is controlled by the
>> +@end table
>> +@end deftypefun
> 
> The ENOMEM and EMFILE descriptions are not really clear in this context.
> Are these failures only possible with CLOSE_RANGE_UNSHARE?

Yes, as Christian noted.

> 
> It may make sense to mention the lack of emulation (so that callers have
> to be aware of ENOSYS for the time being) and that the function is
> specific to Linux.

Ack.

> 
> Based on the description, it is not clear what happens if the range
> contains closed descriptors.  Maybe mention that already-closed
> descriptors are simply skipped?
> 
>> index c35f783e2a..e2a6fa763f 100644
>> --- a/sysdeps/unix/sysv/linux/Versions
>> +++ b/sysdeps/unix/sysv/linux/Versions
>> @@ -169,6 +169,9 @@ libc {
>>    }
>>    GLIBC_2.32 {
>>    }
>> +  GLIBC_2.33 {
>> +    close_range;
>> +  }
> 
> Needs to be GLIBC_2.34 now.  Also the copyright year needs adjusting.



> 
>> diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>> index c315cc5cb8..799c59512c 100644
>> --- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>> +++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>> @@ -33,4 +33,15 @@
>>     not detached and has not been joined.  */
>>  extern __pid_t gettid (void) __THROW;
>>  
>> +/* Unshare the file descriptor table before closing file descriptors.  */I d
>> +#define CLOSE_RANGE_UNSHARE     (1U << 1)
> 
> Needs to be indented.
> 
> Please include <linux/close_range.h> if available.  It's a separate
> header so that we can use it.
> 
> I think if you do that, the consistency check won't add much value
> because it can't really test anything.

I am not very found of adding Linux headers, specially on installed
headers; but it seems we are already doing it for statx.h. I have 
added:

--
#ifdef __has_include
# if __has_include ("linux/close_range.h")
#  include "linux/close_range.h"
# endif
#endif
#ifndef CLOSE_RANGE_UNSHARE
# define CLOSE_RANGE_UNSHARE (1U << 1)
#endif
--

This is very recent kernel and it will take some time until we get
the minimum kernel near the one that starts to provide the close_range
flags (v5.9).  So I think the consistent check would be useful to
system that do not use use the latest kernel headers.

> 
>> +/* Close all file descriptors in the range FD up to MAX_FD.  The flag FLAGS
>> +   are define by the CLOSE_RANGE prefix.  This function behaves like close
>> +   on the range, but in a fail-safe where it will either fail and not close
>> +   any file descriptor or close all of them.  Returns 0 on successor or -1
>> +   for failure (and sets errno accordingly).  */
>> +extern int close_range (unsigned int __fd, unsigned int __max_fd,
>> +			int __flags) __THROW;
>> +
>>  #endif
> 
> Maybe add /* __USE_GNU */ to the #endif, given that it's now at a
> distance from the #ifdef.

Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/tst-close_range.c b/sysdeps/unix/sysv/linux/tst-close_range.c
>> new file mode 100644
>> index 0000000000..131cf27c72
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/tst-close_range.c
>> @@ -0,0 +1,222 @@
> 
>> +#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;
>> +}
> 
> Okay, this ensures that there are no gaps.
> 
>> +
>> +static void
>> +close_range_test_common (int lowfd, unsigned int flags)
>> +{
>> +  const int maximum_fd = lowfd + NFDS;
>> +  const int half_fd = maximum_fd / 2;
>> +  const int gap_1 = maximum_fd - 8;
> 
> I think half_fd should be should be lowfd + NFDS / 2.

Ack.

> 
>> +_Noreturn static int
>> +close_range_test_fn (void *arg)
>> +{
>> +  int lowfd = (int) ((uintptr_t) arg);
>> +  close_range_test_common (lowfd, 0);
>> +  exit (EXIT_SUCCESS);
>> +}
>> +
>> +/* Check if a clone_range on a subprocess created with CLONE_FILES close
>> +   the shared file descriptor table entries in the parent.  */
>> +static void
>> +close_range_test_subprocess (void)
>> +{
>> +  struct support_descriptors *descrs = support_descriptors_list ();
>> +
>> +  /* Check if the temporary file descriptor has no no gaps.  */
>> +  int lowfd = open_multiple_temp_files ();
>> +
>> +  enum { stack_size = 4096 };
>> +  DEFINE_STACK (stack, stack_size);
>> +  pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd, stack,
>> +		      stack_size, CLONE_FILES | SIGCHLD);
> 
> stack_size is too small, I think.  Future error checking (possible with
> clone3) would lead to failures.

Ack, I will change to use a new way similar to the xsigaltstack.

> 
>> +static void
>> +close_range_unshare_test (void)
>> +{
>> +  struct support_descriptors *descrs1 = support_descriptors_list ();
>> +
>> +  /* Check if the temporary file descriptor has no no gaps.  */
>> +  int lowfd = open_multiple_temp_files ();
>> +
>> +  struct support_descriptors *descrs2 = support_descriptors_list ();
>> +
>> +  enum { stack_size = 4096 };
>> +  DEFINE_STACK (stack, stack_size);
>> +  pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
>> +		      stack, stack_size, CLONE_FILES | SIGCHLD);
> 
> Likewise.
> 
> Thanks,
> Florian
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 2/4] Linux: Add close_range
  2021-03-09 17:53     ` Adhemerval Zanella
@ 2021-03-09 18:30       ` Florian Weimer
  0 siblings, 0 replies; 16+ messages in thread
From: Florian Weimer @ 2021-03-09 18:30 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella:

>>> diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>>> index c315cc5cb8..799c59512c 100644
>>> --- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>>> +++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
>>> @@ -33,4 +33,15 @@
>>>     not detached and has not been joined.  */
>>>  extern __pid_t gettid (void) __THROW;
>>>  
>>> +/* Unshare the file descriptor table before closing file descriptors.  */I d
>>> +#define CLOSE_RANGE_UNSHARE     (1U << 1)
>> 
>> Needs to be indented.
>> 
>> Please include <linux/close_range.h> if available.  It's a separate
>> header so that we can use it.
>> 
>> I think if you do that, the consistency check won't add much value
>> because it can't really test anything.
>
> I am not very found of adding Linux headers, specially on installed
> headers; but it seems we are already doing it for statx.h. I have 
> added:
>
> --
> #ifdef __has_include
> # if __has_include ("linux/close_range.h")
> #  include "linux/close_range.h"
> # endif
> #endif
> #ifndef CLOSE_RANGE_UNSHARE
> # define CLOSE_RANGE_UNSHARE (1U << 1)
> #endif
> --
>
> This is very recent kernel and it will take some time until we get
> the minimum kernel near the one that starts to provide the close_range
> flags (v5.9).  So I think the consistent check would be useful to
> system that do not use use the latest kernel headers.

My point was that if the kernel headers are too old, there is nothing to
check, and if they contain CLOSE_RANGE_UNSHARE, the built-in definition
is not checked either.  But then the risk of divergence is greatly
reduced with this approach.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 3/4] io: Add closefrom [BZ #10353]
  2021-03-09 10:23   ` Florian Weimer
@ 2021-03-09 19:07     ` Adhemerval Zanella
  0 siblings, 0 replies; 16+ messages in thread
From: Adhemerval Zanella @ 2021-03-09 19:07 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



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
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom
  2021-03-09 10:45   ` Florian Weimer
@ 2021-03-09 19:48     ` Adhemerval Zanella
  2021-03-09 20:00       ` Florian Weimer
  0 siblings, 1 reply; 16+ messages in thread
From: Adhemerval Zanella @ 2021-03-09 19:48 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 09/03/2021 07:45, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> This patch adds a way to close a range of file descriptors on
>> posix_spawn as a new file action.  The API is similar to the one
>> provided by Solaris 11 [1], where the file action causes the all open
>> file descriptors greater than or equal to input on to be closed when
>> the new process is spawned.
> 
> Commit subject should say posix_spawn_file_actions_closefrom_np.

Ack.

> 
>> The function The function posix_spawn_file_actions_closefrom_np is safe
>> to be implemented by interacting over /proc/self/fd, since the Linux
>> spawni.c helper process does not use CLONE_FILES, so its has own file
>> descriptor table and any failure (in /proc operation) aborts the process
>> creation and returns an error to the caller.
> 
> Duplicate “The function”, ”iterating” instead of “interacting”.

Ack.

> 
>>
>> diff --git a/NEWS b/NEWS
>> index e6446f0548..60a13edcbe 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -34,6 +34,11 @@ Major new features:
>>    greater than given integer.  This function is a GNU extension, although it
>>    also present in other systems.
>>  
>> +* The posix_spawn_file_actions_closefrom_np has been added, enabling
>> +  posix_spawn and posix_spawnp to close all file descriptors greater than
>> +  a giver integer.  This function is a GNU extension, although Solaris also
>> +  provides a similar function.
> 
> Missing “function”.  “great than or equal to”.
> 

Ack.

>> diff --git a/posix/Versions b/posix/Versions
>> index 7d06a6d0c0..95d7b01126 100644
>> --- a/posix/Versions
>> +++ b/posix/Versions
>> @@ -147,6 +147,9 @@ libc {
>>    }
>>    GLIBC_2.30 {
>>    }
>> +  GLIBC_2.33 {
>> +    posix_spawn_file_actions_addclosefrom_np;
>> +  }
> 
> Needs to be GLIBC_2.34 now.  Copyright years need adjusting as well.

Ack.

> 
>> diff --git a/posix/spawn.h b/posix/spawn.h
>> index be6bd591a3..e81b4a4e90 100644
>> --- a/posix/spawn.h
>> +++ b/posix/spawn.h
>> @@ -213,6 +213,14 @@ extern int posix_spawn_file_actions_addchdir_np (posix_spawn_file_actions_t *
>>  extern int posix_spawn_file_actions_addfchdir_np (posix_spawn_file_actions_t *,
>>  						  int __fd)
>>       __THROW __nonnull ((1));
>> +
>> +/* Add an action to close all file descriptor greater than FROM during
>> +   spawn.  This affects the subsequent file actions.  */
>> +extern int
>> +posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *,
>> +					  int __from)
>> +     __THROW __nonnull ((1));
>> +
>>  #endif
> 
> Likewise, “greater than or equal to”.
> 

Ack.

>> diff --git a/posix/tst-spawn5.c b/posix/tst-spawn5.c
>> new file mode 100644
>> index 0000000000..a20e9c9ac7
>> --- /dev/null
>> +++ b/posix/tst-spawn5.c
>> @@ -0,0 +1,283 @@
> 
>> +/* Nonzero if the program gets called via `exec'.  */
>> +static int restart;
> 
> “was called”?
> 
>> +/* Hold the four initial argument used to respawn the process.  */
>> +static char *initial_argv[7];
> 
> four != 7?

I changed to:

/* Hold the four initial argument used to respawn the process, plus                                   
   the extra '--direct' and '--restart', and a final NULL.  */  

> 
>> +/* Called on process re-execution.  The arguments are the expected opened
>> +   file descriptors.  */
>> +_Noreturn static void
>> +handle_restart (int argc, char *argv[])
>> +{
> 
>> +      char *endptr;
>> +      long int fd = strtol (e->d_name, &endptr, 10);
>> +      if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
>> +        FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s",
>> +                    e->d_name);
> 
> Maybe use strtoul here?

Ack.

> 
>> +static void
>> +do_test_closefrom (void)
>> +{
> 
>> +  /* Close a range and add some file actions.  */
>> +  {
>> +    posix_spawn_file_actions_t fa;
>> +    TEST_COMPARE (posix_spawn_file_actions_init (&fa), 0);
>> +
>> +    TEST_COMPARE (posix_spawn_file_actions_addclosefrom_np (&fa, lowfd + 1), 0);
>> +    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
>> +						    0666, O_RDONLY), 0);
>> +    TEST_COMPARE (posix_spawn_file_actions_adddup2 (&fa, lowfd, lowfd + 1), 0);
>> +    TEST_COMPARE (posix_spawn_file_actions_addopen (&fa, lowfd, "/dev/null",
>> +						    0666, O_RDONLY), 0);
>> +
>> +    spawn_closefrom_test (&fa, lowfd, lowfd, (int[]){lowfd, lowfd + 1}, 2);
> 
> I believe the closefrom should start add lowfd here, to check that the
> closing is ordered with regards to the other operations.

It is already doing in, doesn't it? The {lowfd, lowfd+1} only exists if closefrom
is ordered regarding addopen and adddup2.

> 
>> diff --git a/sysdeps/generic/spawn_int_abi.h b/sysdeps/generic/spawn_int_abi.h
>> new file mode 100644
>> index 0000000000..bfc8961598
>> --- /dev/null
>> +++ b/sysdeps/generic/spawn_int_abi.h
>> @@ -0,0 +1,24 @@
>> +/* Internal ABI specific for posix_spawn functionality.  Generic version.
> 
>> +#ifndef _SPAWN_INT_ABI_H
>> +#define _SPAWN_INT_ABI_H
>> +
>> +#define __SPAWN_SUPPORT_CLOSEFROM 0
>> +
>> +#endif /* _SPAWN_INT_H */
> 
> Final comment is wrong.  The header is misnamed because the macro does
> not actually impact ABI.

I renamed to spawn_int_def.h.

> 
>> diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
>> index f157bfffd2..ea87d8b5e6 100644
>> --- a/sysdeps/unix/sysv/linux/spawni.c
>> +++ b/sysdeps/unix/sysv/linux/spawni.c
> 
>>  /* The Linux implementation of posix_spawn{p} uses the clone syscall directly
>>     with CLONE_VM and CLONE_VFORK flags and an allocated stack.  The new stack
>> @@ -280,6 +274,14 @@ __spawni_child (void *arguments)
>>  	      if (__fchdir (action->action.fchdir_action.fd) != 0)
>>  		goto fail;
>>  	      break;
>> +
>> +	    case spawn_do_closefrom:
>> +	      {
>> +		int lowfd = action->action.closefrom_action.from;
>> +	        int r = INLINE_SYSCALL_CALL (close_range, lowfd, ~0U, 0);
>> +		if (r != 0 && !__closefrom_fallback (lowfd))
>> +		  goto fail;
>> +	      } break;
> 
> If you implement the agressive up-to-INT_MAX closing in
> __closefrom_fallback, this probably needs a flag that it disables it
> when called from here.

Ack, although I do not see much gain since it should be a fallback to
a fallback.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom
  2021-03-09 19:48     ` Adhemerval Zanella
@ 2021-03-09 20:00       ` Florian Weimer
  2021-03-09 20:04         ` Adhemerval Zanella
  0 siblings, 1 reply; 16+ messages in thread
From: Florian Weimer @ 2021-03-09 20:00 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella:

>> I believe the closefrom should start add lowfd here, to check that the
>> closing is ordered with regards to the other operations.
>
> It is already doing in, doesn't it? The {lowfd, lowfd+1} only exists
> if closefrom is ordered regarding addopen and adddup2.

Okay, fair enough.  I would have expected that the closefrom call would
be in the middle of the action list.

>> If you implement the agressive up-to-INT_MAX closing in
>> __closefrom_fallback, this probably needs a flag that it disables it
>> when called from here.
>
> Ack, although I do not see much gain since it should be a fallback to
> a fallback.

Oh, I think we should fail if the open of /proc/self/fd fails.  There is
no reason to try very hard to avoid process termination.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom
  2021-03-09 20:00       ` Florian Weimer
@ 2021-03-09 20:04         ` Adhemerval Zanella
  0 siblings, 0 replies; 16+ messages in thread
From: Adhemerval Zanella @ 2021-03-09 20:04 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Adhemerval Zanella via Libc-alpha



On 09/03/2021 17:00, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>> I believe the closefrom should start add lowfd here, to check that the
>>> closing is ordered with regards to the other operations.
>>
>> It is already doing in, doesn't it? The {lowfd, lowfd+1} only exists
>> if closefrom is ordered regarding addopen and adddup2.
> 
> Okay, fair enough.  I would have expected that the closefrom call would
> be in the middle of the action list.
> 
>>> If you implement the agressive up-to-INT_MAX closing in
>>> __closefrom_fallback, this probably needs a flag that it disables it
>>> when called from here.
>>
>> Ack, although I do not see much gain since it should be a fallback to
>> a fallback.
> 
> Oh, I think we should fail if the open of /proc/self/fd fails.  There is
> no reason to try very hard to avoid process termination.

Alright.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2021-03-09 20:04 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-23 16:36 [PATCH v3 1/4] support: Add xclone Adhemerval Zanella
2020-12-23 16:36 ` [PATCH v3 2/4] Linux: Add close_range Adhemerval Zanella
2021-03-09  9:47   ` Florian Weimer
2021-03-09 10:02     ` Christian Brauner
2021-03-09 17:53     ` Adhemerval Zanella
2021-03-09 18:30       ` Florian Weimer
2020-12-23 16:36 ` [PATCH v3 3/4] io: Add closefrom [BZ #10353] Adhemerval Zanella
2021-03-09 10:23   ` Florian Weimer
2021-03-09 19:07     ` Adhemerval Zanella
2020-12-23 16:36 ` [PATCH v3 4/4] posix: Add posix_spawn_file_actions_closefrom Adhemerval Zanella
2021-03-09 10:45   ` Florian Weimer
2021-03-09 19:48     ` Adhemerval Zanella
2021-03-09 20:00       ` Florian Weimer
2021-03-09 20:04         ` Adhemerval Zanella
2021-03-09  9:19 ` [PATCH v3 1/4] support: Add xclone Florian Weimer
2021-03-09 16:17   ` Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).