summary refs log tree commit diff
path: root/sysdeps/unix/sysv
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2021-11-08 10:20:23 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2021-11-24 09:09:37 -0300
commit456b3c08b6fe78938af5d12b6869dc8c704696d6 (patch)
treebea5ff2432a7f589afccc126efb5ac8ab3b2c2b9 /sysdeps/unix/sysv
parente186fc5a31e46f2cbf5ea1a75223b4412907f3d8 (diff)
downloadglibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.tar.gz
glibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.tar.xz
glibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.zip
io: Refactor close_range and closefrom
Now that Hurd implementis both close_range and closefrom (f2c996597d),
we can make close_range() a base ABI, and make the default closefrom()
implementation on top of close_range().

The generic closefrom() implementation based on __getdtablesize() is
moved to generic close_range().  On Linux it will be overriden by
the auto-generation syscall while on Hurd it will be a system specific
implementation.

The closefrom() now calls close_range() and __closefrom_fallback().
Since on Hurd close_range() does not fail, __closefrom_fallback() is an
empty static inline function set by__ASSUME_CLOSE_RANGE.

The __ASSUME_CLOSE_RANGE also allows optimize Linux
__closefrom_fallback() implementation when --enable-kernel=5.9 or
higher is used.

Finally the Linux specific tst-close_range.c is moved to io and
enabled as default.  The Linuxism and CLOSE_RANGE_UNSHARE are
guarded so it can be built for Hurd (I have not actually test it).

Checked on x86_64-linux-gnu, i686-linux-gnu, and with a i686-gnu
build.
Diffstat (limited to 'sysdeps/unix/sysv')
-rw-r--r--sysdeps/unix/sysv/linux/Makefile1
-rw-r--r--sysdeps/unix/sysv/linux/bits/unistd_ext.h9
-rw-r--r--sysdeps/unix/sysv/linux/closefrom.c36
-rw-r--r--sysdeps/unix/sysv/linux/closefrom_fallback.c4
-rw-r--r--sysdeps/unix/sysv/linux/kernel-features.h8
-rw-r--r--sysdeps/unix/sysv/linux/syscalls.list2
-rw-r--r--sysdeps/unix/sysv/linux/tst-close_range.c292
7 files changed, 13 insertions, 339 deletions
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 76ad06361c..76042a6019 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -120,7 +120,6 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-timerfd tst-ppoll \
 	 tst-clock_adjtime tst-adjtimex tst-ntp_adjtime tst-ntp_gettime \
 	 tst-ntp_gettimex tst-sigtimedwait tst-misalign-clone \
-  tst-close_range \
   tst-prctl \
   tst-scm_rights \
   # tests
diff --git a/sysdeps/unix/sysv/linux/bits/unistd_ext.h b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
index ae9994403c..8f422e60da 100644
--- a/sysdeps/unix/sysv/linux/bits/unistd_ext.h
+++ b/sysdeps/unix/sysv/linux/bits/unistd_ext.h
@@ -47,13 +47,4 @@ extern __pid_t gettid (void) __THROW;
 # define CLOSE_RANGE_CLOEXEC (1U << 2)
 #endif
 
-/* 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.  Gaps where the file descriptor
-   is invalid are ignored.   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 /* __USE_GNU  */
diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
deleted file mode 100644
index 372896b775..0000000000
--- a/sysdeps/unix/sysv/linux/closefrom.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Close a range of file descriptors.  Linux version.
-   Copyright (C) 2021 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <stdbool.h>
-#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, true))
-    __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
index f215fd2c09..a377ebc544 100644
--- a/sysdeps/unix/sysv/linux/closefrom_fallback.c
+++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
@@ -21,6 +21,8 @@
 #include <not-cancel.h>
 #include <stdbool.h>
 
+#if !__ASSUME_CLOSE_RANGE
+
 /* Fallback code: iterates over /proc/self/fd, closing each file descriptor
    that fall on the criteria.  If DIRFD_FALLBACK is set, a failure on
    /proc/self/fd open will trigger a fallback that tries to close a file
@@ -97,3 +99,5 @@ err:
   __close_nocancel (dirfd);
   return ret;
 }
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index ffb6af196b..690db275ac 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -220,6 +220,14 @@
 # define __ASSUME_FACCESSAT2 0
 #endif
 
+/* The close_range system call was introduced across all architectures
+   in Linux 5.9.  */
+#if __LINUX_KERNEL_VERSION >= 0x050900
+# define __ASSUME_CLOSE_RANGE 1
+#else
+# define __ASSUME_CLOSE_RANGE 0
+#endif
+
 /* The FUTEX_LOCK_PI2 operation was introduced across all architectures in Linux
    5.14.  */
 #if __LINUX_KERNEL_VERSION >= 0x050e00
diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
index 29899eb264..c38dbb34a1 100644
--- a/sysdeps/unix/sysv/linux/syscalls.list
+++ b/sysdeps/unix/sysv/linux/syscalls.list
@@ -99,4 +99,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
+close_range     -       close_range     i:iii   __close_range   close_range
diff --git a/sysdeps/unix/sysv/linux/tst-close_range.c b/sysdeps/unix/sysv/linux/tst-close_range.c
deleted file mode 100644
index f5069d1b8a..0000000000
--- a/sysdeps/unix/sysv/linux/tst-close_range.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/* Test for the close_range system call.
-   Copyright (C) 2021 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <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 void
-close_range_test_max_upper_limit (void)
-{
-  struct support_descriptors *descrs = support_descriptors_list ();
-
-  int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
-
-  {
-    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 - 1;
-  const int half_fd = lowfd + NFDS / 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 = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
-
-  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 = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
-
-  struct support_stack stack = support_stack_alloc (4096);
-
-  pid_t pid = xclone (close_range_test_fn, (void*) (uintptr_t) lowfd,
-		      stack.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);
-
-  support_stack_free (&stack);
-
-  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 = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
-
-  struct support_descriptors *descrs2 = support_descriptors_list ();
-
-  struct support_stack stack = support_stack_alloc (4096);
-
-  pid_t pid = xclone (close_range_unshare_test_fn, (void*) (uintptr_t) lowfd,
-		      stack.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);
-
-  support_stack_free (&stack);
-
-  for (int i = lowfd; i < lowfd + 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 bool
-is_in_array (int *arr, size_t len, int fd)
-{
-  bool r = false;
-  for (int i = 0; i < len; i++)
-    if (arr[i] == fd)
-      return true;
-  return r;
-}
-
-static void
-close_range_cloexec_test (void)
-{
-  /* Check if the temporary file descriptor has no no gaps.  */
-  int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600);
-
-  const int maximum_fd = lowfd + NFDS - 1;
-  const int half_fd = lowfd + NFDS / 2;
-  const int gap_1 = maximum_fd - 8;
-
-  /* Close half of the descriptors and check result.  */
-  int r = close_range (lowfd, half_fd, CLOSE_RANGE_CLOEXEC);
-  if (r == -1 && errno == EINVAL)
-    {
-      printf ("%s: CLOSE_RANGE_CLOEXEC not supported\n", __func__);
-      return;
-    }
-  for (int i = lowfd; i <= half_fd; i++)
-    {
-      int flags = fcntl (i, F_GETFD);
-      TEST_VERIFY (flags > -1);
-      TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
-    }
-  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.  */
-  static int gap_close[] = { 57, 78, 81, 82, 84, 90 };
-  for (int i = 0; i < array_length (gap_close); i++)
-    xclose (lowfd + gap_close[i]);
-
-  TEST_COMPARE (close_range (half_fd + 1, gap_1, CLOSE_RANGE_CLOEXEC), 0);
-  for (int i = half_fd + 1; i < gap_1; i++)
-    {
-      int flags = fcntl (i, F_GETFD);
-      if (is_in_array (gap_close, array_length (gap_close), i - lowfd))
-        TEST_COMPARE (flags, -1);
-      else
-        {
-          TEST_VERIFY (flags > -1);
-          TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
-        }
-    }
-  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, CLOSE_RANGE_CLOEXEC),
-                0);
-  for (int i = gap_1 + 1; i < maximum_fd - 1; i++)
-    {
-      int flags = fcntl (i, F_GETFD);
-      TEST_VERIFY (flags > -1);
-      TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
-    }
-  TEST_VERIFY (fcntl (maximum_fd, F_GETFL) > -1);
-
-  /* Close the last one.  */
-  TEST_COMPARE (close_range (maximum_fd, maximum_fd, CLOSE_RANGE_CLOEXEC), 0);
-  {
-    int flags = fcntl (maximum_fd, F_GETFD);
-    TEST_VERIFY (flags > -1);
-    TEST_COMPARE (flags & FD_CLOEXEC, FD_CLOEXEC);
-  }
-}
-
-static int
-do_test (void)
-{
-  close_range_test_max_upper_limit ();
-  close_range_test ();
-  close_range_test_subprocess ();
-  close_range_unshare_test ();
-  close_range_cloexec_test ();
-
-  return 0;
-}
-
-#include <support/test-driver.c>