diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2021-11-08 10:20:23 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2021-11-24 09:09:37 -0300 |
commit | 456b3c08b6fe78938af5d12b6869dc8c704696d6 (patch) | |
tree | bea5ff2432a7f589afccc126efb5ac8ab3b2c2b9 /sysdeps/unix | |
parent | e186fc5a31e46f2cbf5ea1a75223b4412907f3d8 (diff) | |
download | glibc-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')
-rw-r--r-- | sysdeps/unix/sysv/linux/Makefile | 1 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/bits/unistd_ext.h | 9 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/closefrom.c | 36 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/closefrom_fallback.c | 4 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/kernel-features.h | 8 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/syscalls.list | 2 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-close_range.c | 292 |
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> |