From 456b3c08b6fe78938af5d12b6869dc8c704696d6 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Mon, 8 Nov 2021 10:20:23 -0300 Subject: 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. --- include/unistd.h | 10 + io/Makefile | 3 +- io/close_range.c | 44 ++++ io/closefrom.c | 16 +- io/tst-close_range.c | 300 +++++++++++++++++++++++++++ posix/unistd.h | 10 + sysdeps/mach/hurd/Makefile | 2 +- sysdeps/mach/hurd/bits/unistd_ext.h | 6 - sysdeps/mach/hurd/closefrom.c | 29 --- sysdeps/mach/hurd/kernel-features.h | 2 + sysdeps/unix/sysv/linux/Makefile | 1 - sysdeps/unix/sysv/linux/bits/unistd_ext.h | 9 - sysdeps/unix/sysv/linux/closefrom.c | 36 ---- sysdeps/unix/sysv/linux/closefrom_fallback.c | 4 + sysdeps/unix/sysv/linux/kernel-features.h | 8 + sysdeps/unix/sysv/linux/syscalls.list | 2 +- sysdeps/unix/sysv/linux/tst-close_range.c | 292 -------------------------- 17 files changed, 391 insertions(+), 383 deletions(-) create mode 100644 io/close_range.c create mode 100644 io/tst-close_range.c delete mode 100644 sysdeps/mach/hurd/closefrom.c delete mode 100644 sysdeps/unix/sysv/linux/closefrom.c delete mode 100644 sysdeps/unix/sysv/linux/tst-close_range.c diff --git a/include/unistd.h b/include/unistd.h index 7849562c42..2bcdd494e1 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -3,6 +3,9 @@ # ifndef _ISOMAC +# include +# include + libc_hidden_proto (_exit, __noreturn__) # ifndef NO_RTLD_HIDDEN rtld_hidden_proto (_exit, __noreturn__) @@ -158,7 +161,14 @@ extern int __brk (void *__addr) attribute_hidden; extern int __close (int __fd); libc_hidden_proto (__close) extern int __libc_close (int __fd); +# if __ASSUME_CLOSE_RANGE +static inline _Bool __closefrom_fallback (int __lowfd, _Bool dirfd_fallback) +{ + return false; +} +# else extern _Bool __closefrom_fallback (int __lowfd, _Bool) attribute_hidden; +# endif 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 ecf65aba60..83f6ffdb76 100644 --- a/io/Makefile +++ b/io/Makefile @@ -57,7 +57,7 @@ routines := \ utimensat futimens file_change_detection \ fts64-time64 \ ftw64-time64 \ - closefrom + closefrom close_range others := pwd test-srcs := ftwtest ftwtest-time64 @@ -79,6 +79,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ tst-futimens \ tst-utimensat \ tst-closefrom \ + tst-close_range \ tst-ftw-bz28126 tests-time64 := \ diff --git a/io/close_range.c b/io/close_range.c new file mode 100644 index 0000000000..26a615d8b9 --- /dev/null +++ b/io/close_range.c @@ -0,0 +1,44 @@ +/* Close a range of file descriptors. + 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 + . */ + +#include +#include +#include + +/* Close the file descriptors from FIRST up to LAST, inclusive. */ +int +__close_range (unsigned int first, unsigned int last, + int flags) +{ + if (first > last || flags != 0) + { + __set_errno (EINVAL); + return -1; + } + + int maxfd = __getdtablesize (); + if (maxfd == -1) + return -1; + + for (int i = first; i <= last && i < maxfd; i++) + __close_nocancel_nostatus (i); + + return 0; +} +libc_hidden_def (__close_range) +weak_alias (__close_range, close_range) diff --git a/io/closefrom.c b/io/closefrom.c index 01660a7531..e9167687bc 100644 --- a/io/closefrom.c +++ b/io/closefrom.c @@ -16,19 +16,21 @@ License along with the GNU C Library; if not, see . */ +#include #include +#include #include -#include void __closefrom (int lowfd) { - int maxfd = __getdtablesize (); - if (maxfd == -1) - __fortify_fail ("closefrom failed to get the file descriptor table size"); + int l = MAX (0, lowfd); - for (int i = 0; i < maxfd; i++) - if (i >= lowfd) - __close_nocancel_nostatus (i); + 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/io/tst-close_range.c b/io/tst-close_range.c new file mode 100644 index 0000000000..f05b4ff6ae --- /dev/null +++ b/io/tst-close_range.c @@ -0,0 +1,300 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} + +#ifdef __linux__ +_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); +} +#endif + + +#ifdef CLOSE_RANGE_UNSHARE +_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); +} +#endif + +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 (); +#ifdef __linux__ + close_range_test_subprocess (); +#endif +#ifdef CLOSE_RANGE_UNSHARE + close_range_unshare_test (); +#endif + close_range_cloexec_test (); + + return 0; +} + +#include diff --git a/posix/unistd.h b/posix/unistd.h index 7a61ff5e86..3c8a7ced6a 100644 --- a/posix/unistd.h +++ b/posix/unistd.h @@ -1199,6 +1199,16 @@ int getentropy (void *__buffer, size_t __length) __wur __attr_access ((__write_only__, 1, 2)); #endif +#ifdef __USE_GNU +/* 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 and gaps where the file descriptor is invalid or errors + encountered while closing file descriptors 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 + /* Define some macros helping to catch buffer overflows. */ #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function # include diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile index 9acbe80f26..17bb643c18 100644 --- a/sysdeps/mach/hurd/Makefile +++ b/sysdeps/mach/hurd/Makefile @@ -196,7 +196,7 @@ sysdep_routines += cthreads endif ifeq (io, $(subdir)) -sysdep_routines += f_setlk close_nocancel close_nocancel_nostatus close_range \ +sysdep_routines += f_setlk close_nocancel close_nocancel_nostatus \ fcntl_nocancel open_nocancel openat_nocancel read_nocancel \ pread64_nocancel write_nocancel pwrite64_nocancel \ wait4_nocancel \ diff --git a/sysdeps/mach/hurd/bits/unistd_ext.h b/sysdeps/mach/hurd/bits/unistd_ext.h index 288f504a3c..14f85539d5 100644 --- a/sysdeps/mach/hurd/bits/unistd_ext.h +++ b/sysdeps/mach/hurd/bits/unistd_ext.h @@ -25,10 +25,4 @@ /* Set the FD_CLOEXEC bit instead of closing the file descriptor. */ #define CLOSE_RANGE_CLOEXEC (1U << 2) -/* Close the file descriptors from FIRST up to LAST, inclusive. - If CLOSE_RANGE_CLOEXEC is set in FLAGS, set the FD_CLOEXEC flag - instead of closing. */ -extern int close_range (unsigned int __first, unsigned int __last, - int __flags) __THROW; - #endif /* __USE_GNU */ diff --git a/sysdeps/mach/hurd/closefrom.c b/sysdeps/mach/hurd/closefrom.c deleted file mode 100644 index 5d667cf6c4..0000000000 --- a/sysdeps/mach/hurd/closefrom.c +++ /dev/null @@ -1,29 +0,0 @@ -/* Close a range of file descriptors. Hurd 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 - . */ - -#include -#include - -void -__closefrom (int lowfd) -{ - int l = MAX (0, lowfd); - - (void) __close_range (l, ~0U, 0); -} -weak_alias (__closefrom, closefrom) diff --git a/sysdeps/mach/hurd/kernel-features.h b/sysdeps/mach/hurd/kernel-features.h index 7d4eaee0a6..5fd37a6d7b 100644 --- a/sysdeps/mach/hurd/kernel-features.h +++ b/sysdeps/mach/hurd/kernel-features.h @@ -19,3 +19,5 @@ /* This file can define __ASSUME_* macros checked by certain source files. Almost none of these are used outside of sysdeps/unix/sysv/linux code. But those referring to POSIX-level features like O_* flags can be. */ + +#define __ASSUME_CLOSE_RANGE 1 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 - . */ - -#include -#include -#include -#include - -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 #include +#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 - . */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 -- cgit v1.2.3