aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux
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/linux
parente186fc5a31e46f2cbf5ea1a75223b4412907f3d8 (diff)
downloadglibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.tar
glibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.tar.gz
glibc-456b3c08b6fe78938af5d12b6869dc8c704696d6.tar.bz2
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/linux')
-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>