diff options
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/Makefile | 11 | ||||
-rw-r--r-- | nptl/allocatestack.c | 2 | ||||
-rw-r--r-- | nptl/cancellation.c | 127 | ||||
-rw-r--r-- | nptl/cleanup_defer.c | 5 | ||||
-rw-r--r-- | nptl/descr-const.sym | 6 | ||||
-rw-r--r-- | nptl/descr.h | 23 | ||||
-rw-r--r-- | nptl/libc-cleanup.c | 5 | ||||
-rw-r--r-- | nptl/pthread_cancel.c | 78 | ||||
-rw-r--r-- | nptl/pthread_create.c | 5 | ||||
-rw-r--r-- | nptl/pthread_exit.c | 4 | ||||
-rw-r--r-- | nptl/pthread_join_common.c | 6 | ||||
-rw-r--r-- | nptl/pthread_kill.c | 11 | ||||
-rw-r--r-- | nptl/pthread_setcancelstate.c | 2 | ||||
-rw-r--r-- | nptl/pthread_setcanceltype.c | 2 | ||||
-rw-r--r-- | nptl/pthread_testcancel.c | 5 | ||||
-rw-r--r-- | nptl/tst-attr4.c | 62 | ||||
-rw-r--r-- | nptl/tst-cancel31.c | 100 | ||||
-rw-r--r-- | nptl/tst-cancel7.c | 12 | ||||
-rw-r--r-- | nptl/tst-setuid2.c | 5 |
19 files changed, 345 insertions, 126 deletions
diff --git a/nptl/Makefile b/nptl/Makefile index c4c27e0..ceb91af 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -204,6 +204,7 @@ routines = \ sem_timedwait \ sem_unlink \ sem_wait \ + syscall_cancel \ tpp \ unwind \ vars \ @@ -235,7 +236,8 @@ CFLAGS-pthread_setcanceltype.c += -fexceptions -fasynchronous-unwind-tables # These are internal functions which similar functionality as setcancelstate # and setcanceltype. -CFLAGS-cancellation.c += -fasynchronous-unwind-tables +CFLAGS-cancellation.c += -fexceptions -fasynchronous-unwind-tables +CFLAGS-syscall_cancel.c += -fexceptions -fasynchronous-unwind-tables # Calling pthread_exit() must cause the registered cancel handlers to # be executed. Therefore exceptions have to be thrown through this @@ -274,11 +276,13 @@ LDLIBS-tst-minstack-throw = -lstdc++ tests = \ tst-attr2 \ tst-attr3 \ + tst-attr4 \ tst-cancel4_1 \ tst-cancel4_2 \ tst-cancel7 \ tst-cancel17 \ tst-cancel24 \ + tst-cancel31 \ tst-cond26 \ tst-context1 \ tst-default-attr \ @@ -404,7 +408,10 @@ xtests += tst-eintr1 test-srcs = tst-oddstacklimit -gen-as-const-headers = unwindbuf.sym +gen-as-const-headers = \ + descr-const.sym \ + unwindbuf.sym \ + # gen-as-const-headers gen-py-const-headers := nptl_lock_constants.pysym pretty-printers := nptl-printers.py diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 2cb562f..d9adb58 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -132,6 +132,8 @@ get_cached_stack (size_t *sizep, void **memp) __libc_lock_init (result->exit_lock); memset (&result->tls_state, 0, sizeof result->tls_state); + result->getrandom_buf = NULL; + /* Clear the DTV. */ dtv_t *dtv = GET_DTV (TLS_TPADJ (result)); for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt) diff --git a/nptl/cancellation.c b/nptl/cancellation.c index 7ce60e7..e71008b 100644 --- a/nptl/cancellation.c +++ b/nptl/cancellation.c @@ -18,74 +18,93 @@ #include <setjmp.h> #include <stdlib.h> #include "pthreadP.h" -#include <futex-internal.h> - -/* The next two functions are similar to pthread_setcanceltype() but - more specialized for the use in the cancelable functions like write(). - They do not need to check parameters etc. These functions must be - AS-safe, with the exception of the actual cancellation, because they - are called by wrappers around AS-safe functions like write().*/ -int -__pthread_enable_asynccancel (void) +/* Called by the INTERNAL_SYSCALL_CANCEL macro, check for cancellation and + returns the syscall value or its negative error code. */ +long int +__internal_syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2, + __syscall_arg_t a3, __syscall_arg_t a4, + __syscall_arg_t a5, __syscall_arg_t a6, + __SYSCALL_CANCEL7_ARG_DEF + __syscall_arg_t nr) { - struct pthread *self = THREAD_SELF; - int oldval = atomic_load_relaxed (&self->cancelhandling); + long int result; + struct pthread *pd = THREAD_SELF; - while (1) + /* If cancellation is not enabled, call the syscall directly and also + for thread terminatation to avoid call __syscall_do_cancel while + executing cleanup handlers. */ + int ch = atomic_load_relaxed (&pd->cancelhandling); + if (SINGLE_THREAD_P || !cancel_enabled (ch) || cancel_exiting (ch)) { - int newval = oldval | CANCELTYPE_BITMASK; - - if (newval == oldval) - break; + result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6 + __SYSCALL_CANCEL7_ARCH_ARG7); + if (INTERNAL_SYSCALL_ERROR_P (result)) + return -INTERNAL_SYSCALL_ERRNO (result); + return result; + } - if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, - &oldval, newval)) - { - if (cancel_enabled_and_canceled_and_async (newval)) - { - self->result = PTHREAD_CANCELED; - __do_cancel (); - } + /* Call the arch-specific entry points that contains the globals markers + to be checked by SIGCANCEL handler. */ + result = __syscall_cancel_arch (&pd->cancelhandling, nr, a1, a2, a3, a4, a5, + a6 __SYSCALL_CANCEL7_ARCH_ARG7); - break; - } - } + /* If the cancellable syscall was interrupted by SIGCANCEL and it has no + side-effect, cancel the thread if cancellation is enabled. */ + ch = atomic_load_relaxed (&pd->cancelhandling); + /* The behaviour here assumes that EINTR is returned only if there are no + visible side effects. POSIX Issue 7 has not yet provided any stronger + language for close, and in theory the close syscall could return EINTR + and leave the file descriptor open (conforming and leaks). It expects + that no such kernel is used with glibc. */ + if (result == -EINTR && cancel_enabled_and_canceled (ch)) + __syscall_do_cancel (); - return oldval; + return result; } -libc_hidden_def (__pthread_enable_asynccancel) -/* See the comment for __pthread_enable_asynccancel regarding - the AS-safety of this function. */ -void -__pthread_disable_asynccancel (int oldtype) +/* Called by the SYSCALL_CANCEL macro, check for cancellation and return the + syscall expected success value (usually 0) or, in case of failure, -1 and + sets errno to syscall return value. */ +long int +__syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2, + __syscall_arg_t a3, __syscall_arg_t a4, + __syscall_arg_t a5, __syscall_arg_t a6, + __SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr) { - /* If asynchronous cancellation was enabled before we do not have - anything to do. */ - if (oldtype & CANCELTYPE_BITMASK) - return; + int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6, + __SYSCALL_CANCEL7_ARG nr); + return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r)) + ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r)) + : r; +} +/* Called by __syscall_cancel_arch or function above start the thread + cancellation. */ +_Noreturn void +__syscall_do_cancel (void) +{ struct pthread *self = THREAD_SELF; - int newval; + + /* Disable thread cancellation to avoid cancellable entrypoints calling + __syscall_do_cancel recursively. We atomic load relaxed to check the + state of cancelhandling, there is no particular ordering requirement + between the syscall call and the other thread setting our cancelhandling + with a atomic store acquire. + + POSIX Issue 7 notes that the cancellation occurs asynchronously on the + target thread, that implies there is no ordering requirements. It does + not need a MO release store here. */ int oldval = atomic_load_relaxed (&self->cancelhandling); - do + while (1) { - newval = oldval & ~CANCELTYPE_BITMASK; + int newval = oldval | CANCELSTATE_BITMASK; + if (oldval == newval) + break; + if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, + &oldval, newval)) + break; } - while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, - &oldval, newval)); - /* We cannot return when we are being canceled. Upon return the - thread might be things which would have to be undone. The - following loop should loop until the cancellation signal is - delivered. */ - while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK)) - == CANCELING_BITMASK)) - { - futex_wait_simple ((unsigned int *) &self->cancelhandling, newval, - FUTEX_PRIVATE); - newval = atomic_load_relaxed (&self->cancelhandling); - } + __do_cancel (PTHREAD_CANCELED); } -libc_hidden_def (__pthread_disable_asynccancel) diff --git a/nptl/cleanup_defer.c b/nptl/cleanup_defer.c index dc08eda..db32c4f 100644 --- a/nptl/cleanup_defer.c +++ b/nptl/cleanup_defer.c @@ -82,10 +82,7 @@ ___pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf) &cancelhandling, newval)); if (cancel_enabled_and_canceled (cancelhandling)) - { - self->result = PTHREAD_CANCELED; - __do_cancel (); - } + __do_cancel (PTHREAD_CANCELED); } } versioned_symbol (libc, ___pthread_unregister_cancel_restore, diff --git a/nptl/descr-const.sym b/nptl/descr-const.sym new file mode 100644 index 0000000..8608248 --- /dev/null +++ b/nptl/descr-const.sym @@ -0,0 +1,6 @@ +#include <tls.h> + +-- Not strictly offsets, these values are using thread cancellation by arch +-- specific cancel entrypoint. +TCB_CANCELED_BIT CANCELED_BIT +TCB_CANCELED_BITMASK CANCELED_BITMASK diff --git a/nptl/descr.h b/nptl/descr.h index 8cef958..a69f5c9 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -404,6 +404,9 @@ struct pthread /* Used on strsignal. */ struct tls_internal_t tls_state; + /* getrandom vDSO per-thread opaque state. */ + void *getrandom_buf; + /* rseq area registered with the kernel. Use a custom definition here to isolate from kernel struct rseq changes. The implementation of sched_getcpu needs acccess to the cpu_id field; @@ -414,6 +417,8 @@ struct pthread { uint32_t cpu_id_start; uint32_t cpu_id; + uint64_t rseq_cs; + uint32_t flags; }; char pad[32]; /* Original rseq area size. */ } rseq_area __attribute__ ((aligned (32))); @@ -426,6 +431,24 @@ struct pthread } __attribute ((aligned (TCB_ALIGNMENT))); static inline bool +cancel_enabled (int value) +{ + return (value & CANCELSTATE_BITMASK) == 0; +} + +static inline bool +cancel_async_enabled (int value) +{ + return (value & CANCELTYPE_BITMASK) != 0; +} + +static inline bool +cancel_exiting (int value) +{ + return (value & EXITING_BITMASK) != 0; +} + +static inline bool cancel_enabled_and_canceled (int value) { return (value & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK diff --git a/nptl/libc-cleanup.c b/nptl/libc-cleanup.c index fe042c8..20d746c 100644 --- a/nptl/libc-cleanup.c +++ b/nptl/libc-cleanup.c @@ -69,10 +69,7 @@ __libc_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer) &cancelhandling, newval)); if (cancel_enabled_and_canceled (cancelhandling)) - { - self->result = PTHREAD_CANCELED; - __do_cancel (); - } + __do_cancel (PTHREAD_CANCELED); } } libc_hidden_def (__libc_cleanup_pop_restore) diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index 69701db..012c4eb 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -23,6 +23,7 @@ #include <sysdep.h> #include <unistd.h> #include <unwind-link.h> +#include <cancellation-pc-check.h> #include <stdio.h> #include <gnu/lib-names.h> #include <sys/single_threaded.h> @@ -40,31 +41,16 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx) || si->si_code != SI_TKILL) return; + /* Check if asynchronous cancellation mode is set or if interrupted + instruction pointer falls within the cancellable syscall bridge. For + interruptable syscalls with external side-effects (i.e. partial reads), + the kernel will set the IP to after __syscall_cancel_arch_end, thus + disabling the cancellation and allowing the process to handle such + conditions. */ struct pthread *self = THREAD_SELF; - int oldval = atomic_load_relaxed (&self->cancelhandling); - while (1) - { - /* We are canceled now. When canceled by another thread this flag - is already set but if the signal is directly send (internally or - from another process) is has to be done here. */ - int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; - - if (oldval == newval || (oldval & EXITING_BITMASK) != 0) - /* Already canceled or exiting. */ - break; - - if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, - &oldval, newval)) - { - self->result = PTHREAD_CANCELED; - - /* Make sure asynchronous cancellation is still enabled. */ - if ((oldval & CANCELTYPE_BITMASK) != 0) - /* Run the registered destructors and terminate the thread. */ - __do_cancel (); - } - } + if (cancel_async_enabled (oldval) || cancellation_pc_check (ctx)) + __syscall_do_cancel (); } int @@ -106,15 +92,13 @@ __pthread_cancel (pthread_t th) /* Some syscalls are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART (they always fail with EINTR). So pthread_cancel cannot send SIGCANCEL unless the cancellation - is enabled and set as asynchronous (in this case the cancellation will - be acted in the cancellation handler instead by the syscall wrapper). - Otherwise the target thread is set as 'cancelling' (CANCELING_BITMASK) + is enabled. + In this case the target thread is set as 'cancelled' (CANCELED_BITMASK) by atomically setting 'cancelhandling' and the cancelation will be acted upon on next cancellation entrypoing in the target thread. - It also requires to atomically check if cancellation is enabled and - asynchronous, so both cancellation state and type are tracked on - 'cancelhandling'. */ + It also requires to atomically check if cancellation is enabled, so the + state are also tracked on 'cancelhandling'. */ int result = 0; int oldval = atomic_load_relaxed (&pd->cancelhandling); @@ -122,19 +106,17 @@ __pthread_cancel (pthread_t th) do { again: - newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; + newval = oldval | CANCELED_BITMASK; if (oldval == newval) break; - /* If the cancellation is handled asynchronously just send a - signal. We avoid this if possible since it's more - expensive. */ - if (cancel_enabled_and_canceled_and_async (newval)) + /* Only send the SIGANCEL signal if cancellation is enabled, since some + syscalls are never restarted even with SA_RESTART. The signal + will act iff async cancellation is enabled. */ + if (cancel_enabled (newval)) { - /* Mark the cancellation as "in progress". */ - int newval2 = oldval | CANCELING_BITMASK; if (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling, - &oldval, newval2)) + &oldval, newval)) goto again; if (pd == THREAD_SELF) @@ -143,9 +125,8 @@ __pthread_cancel (pthread_t th) pthread_create, so the signal handler may not have been set up for a self-cancel. */ { - pd->result = PTHREAD_CANCELED; - if ((newval & CANCELTYPE_BITMASK) != 0) - __do_cancel (); + if (cancel_async_enabled (newval)) + __do_cancel (PTHREAD_CANCELED); } else /* The cancellation handler will take care of marking the @@ -154,19 +135,18 @@ __pthread_cancel (pthread_t th) break; } - - /* A single-threaded process should be able to kill itself, since - there is nothing in the POSIX specification that says that it - cannot. So we set multiple_threads to true so that cancellation - points get executed. */ - THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); -#ifndef TLS_MULTIPLE_THREADS_IN_TCB - __libc_single_threaded_internal = 0; -#endif } while (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling, &oldval, newval)); + /* A single-threaded process should be able to kill itself, since there is + nothing in the POSIX specification that says that it cannot. So we set + multiple_threads to true so that cancellation points get executed. */ + THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); +#ifndef TLS_MULTIPLE_THREADS_IN_TCB + __libc_single_threaded_internal = 0; +#endif + return result; } versioned_symbol (libc, __pthread_cancel, pthread_cancel, GLIBC_2_34); diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index 1d3665d..ef3ec33 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -38,6 +38,7 @@ #include <version.h> #include <clone_internal.h> #include <futex-internal.h> +#include <getrandom-internal.h> #include <shlib-compat.h> @@ -549,6 +550,10 @@ start_thread (void *arg) } #endif + /* Release the vDSO getrandom per-thread buffer with all signal blocked, + to avoid creating a new free-state block during thread release. */ + __getrandom_vdso_release (pd); + if (!pd->user_stack) advise_stack_range (pd->stackblock, pd->stackblock_size, (uintptr_t) pd, pd->guardsize); diff --git a/nptl/pthread_exit.c b/nptl/pthread_exit.c index dc2635f..600ab03 100644 --- a/nptl/pthread_exit.c +++ b/nptl/pthread_exit.c @@ -31,9 +31,7 @@ __pthread_exit (void *value) " must be installed for pthread_exit to work\n"); } - THREAD_SETMEM (THREAD_SELF, result, value); - - __do_cancel (); + __do_cancel (value); } libc_hidden_def (__pthread_exit) weak_alias (__pthread_exit, pthread_exit) diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c index 9c685c7..273db80 100644 --- a/nptl/pthread_join_common.c +++ b/nptl/pthread_join_common.c @@ -49,6 +49,12 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return, /* We cannot wait for the thread. */ return EINVAL; + /* Make sure the clock and time specified are valid. */ + if (abstime + && __glibc_unlikely (!futex_abstimed_supported_clockid (clockid) + || ! valid_nanoseconds (abstime->tv_nsec))) + return EINVAL; + struct pthread *self = THREAD_SELF; int result = 0; diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index 71e5a7b..fa5121a 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -69,6 +69,17 @@ __pthread_kill_implementation (pthread_t threadid, int signo, int no_tid) return ret; } +/* Send the signal SIGNO to the caller. Used by abort and called where the + signals are being already blocked and there is no need to synchronize with + exit_lock. */ +int +__pthread_raise_internal (int signo) +{ + /* Use the gettid syscall so it works after vfork. */ + int ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), __gettid(), signo); + return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; +} + int __pthread_kill_internal (pthread_t threadid, int signo) { diff --git a/nptl/pthread_setcancelstate.c b/nptl/pthread_setcancelstate.c index 18fb42a..787c45a 100644 --- a/nptl/pthread_setcancelstate.c +++ b/nptl/pthread_setcancelstate.c @@ -48,7 +48,7 @@ __pthread_setcancelstate (int state, int *oldstate) &oldval, newval)) { if (cancel_enabled_and_canceled_and_async (newval)) - __do_cancel (); + __do_cancel (PTHREAD_CANCELED); break; } diff --git a/nptl/pthread_setcanceltype.c b/nptl/pthread_setcanceltype.c index cf441ce..3b5b034 100644 --- a/nptl/pthread_setcanceltype.c +++ b/nptl/pthread_setcanceltype.c @@ -48,7 +48,7 @@ __pthread_setcanceltype (int type, int *oldtype) if (cancel_enabled_and_canceled_and_async (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); - __do_cancel (); + __do_cancel (PTHREAD_CANCELED); } break; diff --git a/nptl/pthread_testcancel.c b/nptl/pthread_testcancel.c index a0197b5..be0e8d5 100644 --- a/nptl/pthread_testcancel.c +++ b/nptl/pthread_testcancel.c @@ -25,10 +25,7 @@ ___pthread_testcancel (void) struct pthread *self = THREAD_SELF; int cancelhandling = atomic_load_relaxed (&self->cancelhandling); if (cancel_enabled_and_canceled (cancelhandling)) - { - self->result = PTHREAD_CANCELED; - __do_cancel (); - } + __do_cancel (PTHREAD_CANCELED); } versioned_symbol (libc, ___pthread_testcancel, pthread_testcancel, GLIBC_2_34); libc_hidden_ver (___pthread_testcancel, __pthread_testcancel) diff --git a/nptl/tst-attr4.c b/nptl/tst-attr4.c new file mode 100644 index 0000000..c53781e --- /dev/null +++ b/nptl/tst-attr4.c @@ -0,0 +1,62 @@ +/* Test initial values of pthread attributes. + Copyright (C) 2024 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 <sched.h> +#include <stdio.h> +#include <stdint.h> + +#include <support/check.h> +#include <support/test-driver.h> +#include <support/xthread.h> + + +int +do_test (void) +{ + pthread_attr_t a; + int ret; + + xpthread_attr_init (&a); + + size_t stacksize = 0; + verbose_printf ("testing default stack size\n"); + ret = pthread_attr_getstacksize (&a, &stacksize); + TEST_VERIFY_EXIT (ret == 0); + TEST_VERIFY (stacksize >= PTHREAD_STACK_MIN); + TEST_VERIFY (stacksize <= SIZE_MAX / 2); + + int policy; + verbose_printf ("testing default scheduler parameters\n"); + ret = pthread_attr_getschedpolicy (&a, &policy); + TEST_VERIFY_EXIT (ret == 0); + struct sched_param param; + ret = pthread_attr_getschedparam (&a, ¶m); + TEST_VERIFY_EXIT (ret == 0); + int min = sched_get_priority_min (policy); + TEST_VERIFY (min != -1); + int max = sched_get_priority_max (policy); + TEST_VERIFY (max != -1); + TEST_VERIFY (param.sched_priority >= min); + TEST_VERIFY (param.sched_priority <= max); + + xpthread_attr_destroy (&a); + + return 0; +} + +#include <support/test-driver.c> diff --git a/nptl/tst-cancel31.c b/nptl/tst-cancel31.c new file mode 100644 index 0000000..f9cc824 --- /dev/null +++ b/nptl/tst-cancel31.c @@ -0,0 +1,100 @@ +/* Verify side-effects of cancellable syscalls (BZ #12683). + Copyright (C) 2023 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/>. */ + +/* This testcase checks if there is resource leakage if the syscall has + returned from kernelspace, but before userspace saves the return + value. The 'leaker' thread should be able to close the file descriptor + if the resource is already allocated, meaning that if the cancellation + signal arrives *after* the open syscal return from kernel, the + side-effect should be visible to application. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <support/xunistd.h> +#include <support/xthread.h> +#include <support/check.h> +#include <support/temp_file.h> +#include <support/support.h> +#include <support/descriptors.h> + +static void * +writeopener (void *arg) +{ + int fd; + for (;;) + { + fd = open (arg, O_WRONLY); + xclose (fd); + } + return NULL; +} + +static void * +leaker (void *arg) +{ + int fd = open (arg, O_RDONLY); + TEST_VERIFY_EXIT (fd > 0); + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, 0); + xclose (fd); + return NULL; +} + +static int +do_test (void) +{ + enum { + iter_count = 1000 + }; + + char *dir = support_create_temp_directory ("tst-cancel28"); + char *name = xasprintf ("%s/fifo", dir); + TEST_COMPARE (mkfifo (name, 0600), 0); + add_temp_file (name); + + struct support_descriptors *descrs = support_descriptors_list (); + + srand (1); + + xpthread_create (NULL, writeopener, name); + for (int i = 0; i < iter_count; i++) + { + pthread_t td = xpthread_create (NULL, leaker, name); + struct timespec ts = + { .tv_nsec = rand () % 100000, .tv_sec = 0 }; + nanosleep (&ts, NULL); + /* Ignore pthread_cancel result because it might be the + case when pthread_cancel is called when thread is already + exited. */ + pthread_cancel (td); + xpthread_join (td); + } + + support_descriptors_check (descrs); + + support_descriptors_free (descrs); + + free (name); + + return 0; +} + +#include <support/test-driver.c> diff --git a/nptl/tst-cancel7.c b/nptl/tst-cancel7.c index d2350bd..3f874c2 100644 --- a/nptl/tst-cancel7.c +++ b/nptl/tst-cancel7.c @@ -38,6 +38,8 @@ static char *semfilename; static sem_t *sem; +static void do_cleanup (void); + static void * tf (void *arg) { @@ -57,9 +59,6 @@ sl (void) fprintf (f, "%lld\n", (long long) getpid ()); fflush (f); - if (sem_post (sem) != 0) - FAIL_EXIT1 ("sem_post: %m"); - struct flock fl = { .l_type = F_WRLCK, @@ -70,6 +69,9 @@ sl (void) if (fcntl (fileno (f), F_SETLK, &fl) != 0) FAIL_EXIT1 ("fcntl (F_SETFL): %m"); + if (sem_post (sem) != 0) + FAIL_EXIT1 ("sem_post: %m"); + sigset_t ss; sigfillset (&ss); sigsuspend (&ss); @@ -108,6 +110,8 @@ do_prepare (int argc, char *argv[]) xwrite (fd, " ", 1); xclose (fd); + + atexit (do_cleanup); } @@ -116,7 +120,7 @@ do_test (void) { pthread_t th = xpthread_create (NULL, tf, NULL); - /* Wait to cancel until after the pid is written. */ + /* Wait to cancel until after the pid is written and file locked. */ if (sem_wait (sem) != 0) FAIL_EXIT1 ("sem_wait: %m"); diff --git a/nptl/tst-setuid2.c b/nptl/tst-setuid2.c index 33d4e39..c410423 100644 --- a/nptl/tst-setuid2.c +++ b/nptl/tst-setuid2.c @@ -76,7 +76,12 @@ run_on_thread (void (*func) (void)) static void change_thread_ids (void) { +#ifdef __NR_setresuid32 + /* Prefer 32-bit setresuid32 over 16-bit setresuid. */ + long ret = syscall (__NR_setresuid32, 2001, 2002, 2003); +#else long ret = syscall (__NR_setresuid, 2001, 2002, 2003); +#endif if (ret != 0) FAIL ("setresuid (2001, 2002, 2003): %ld", ret); } |