aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--elf/Makefile5
-rw-r--r--nptl/Makefile10
-rw-r--r--nptl/cancellation.c127
-rw-r--r--nptl/cleanup_defer.c5
-rw-r--r--nptl/descr-const.sym6
-rw-r--r--nptl/descr.h18
-rw-r--r--nptl/libc-cleanup.c5
-rw-r--r--nptl/pthread_cancel.c78
-rw-r--r--nptl/pthread_exit.c4
-rw-r--r--nptl/pthread_setcancelstate.c2
-rw-r--r--nptl/pthread_setcanceltype.c2
-rw-r--r--nptl/pthread_testcancel.c5
-rw-r--r--nptl/tst-cancel31.c100
-rw-r--r--sysdeps/generic/syscall_types.h25
-rw-r--r--sysdeps/nptl/cancellation-pc-check.h54
-rw-r--r--sysdeps/nptl/lowlevellock-futex.h20
-rw-r--r--sysdeps/nptl/pthreadP.h11
-rw-r--r--sysdeps/powerpc/powerpc32/sysdep.h3
-rw-r--r--sysdeps/powerpc/powerpc64/sysdep.h19
-rw-r--r--sysdeps/pthread/tst-cancel2.c4
-rw-r--r--sysdeps/sh/sysdep.h1
-rw-r--r--sysdeps/unix/sysdep.h173
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S59
-rw-r--r--sysdeps/unix/sysv/linux/alpha/syscall_cancel.S80
-rw-r--r--sysdeps/unix/sysv/linux/arc/syscall_cancel.S56
-rw-r--r--sysdeps/unix/sysv/linux/arm/syscall_cancel.S78
-rw-r--r--sysdeps/unix/sysv/linux/csky/syscall_cancel.S114
-rw-r--r--sysdeps/unix/sysv/linux/hppa/syscall_cancel.S81
-rw-r--r--sysdeps/unix/sysv/linux/i386/syscall_cancel.S104
-rw-r--r--sysdeps/unix/sysv/linux/loongarch/syscall_cancel.S50
-rw-r--r--sysdeps/unix/sysv/linux/m68k/syscall_cancel.S84
-rw-r--r--sysdeps/unix/sysv/linux/microblaze/syscall_cancel.S61
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S128
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips32/sysdep.h4
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/n32/syscall_types.h28
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/syscall_cancel.S108
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/sysdep.h52
-rw-r--r--sysdeps/unix/sysv/linux/nios2/syscall_cancel.S95
-rw-r--r--sysdeps/unix/sysv/linux/or1k/syscall_cancel.S63
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/cancellation-pc-check.h65
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/syscall_cancel.S86
-rw-r--r--sysdeps/unix/sysv/linux/riscv/syscall_cancel.S67
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/syscall_cancel.S62
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/syscall_cancel.S62
-rw-r--r--sysdeps/unix/sysv/linux/sh/syscall_cancel.S126
-rw-r--r--sysdeps/unix/sysv/linux/socketcall.h35
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc32/syscall_cancel.S71
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc64/syscall_cancel.S74
-rw-r--r--sysdeps/unix/sysv/linux/syscall_cancel.c73
-rw-r--r--sysdeps/unix/sysv/linux/sysdep-cancel.h12
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/syscall_cancel.S57
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/x32/syscall_types.h34
-rw-r--r--sysdeps/x86_64/nptl/tcb-offsets.sym3
53 files changed, 2521 insertions, 228 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 0792b57678..00622ace9d 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1328,11 +1328,8 @@ $(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os)
# discovery mechanism is not compatible with the libc implementation
# when compiled for libc.
rtld-stubbed-symbols = \
- __GI___pthread_disable_asynccancel \
- __GI___pthread_enable_asynccancel \
__libc_assert_fail \
- __pthread_disable_asynccancel \
- __pthread_enable_asynccancel \
+ __syscall_cancel \
calloc \
free \
malloc \
diff --git a/nptl/Makefile b/nptl/Makefile
index c4c27e0d23..bf4c29b6ed 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
@@ -279,6 +281,7 @@ tests = \
tst-cancel7 \
tst-cancel17 \
tst-cancel24 \
+ tst-cancel31 \
tst-cond26 \
tst-context1 \
tst-default-attr \
@@ -404,7 +407,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/cancellation.c b/nptl/cancellation.c
index 7ce60e70d0..e71008b58b 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 dc08eda003..db32c4fc26 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 0000000000..8608248354
--- /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 8cef95810c..65d3baaee3 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -426,6 +426,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 fe042c85aa..20d746cbb7 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 69701db6f9..012c4ebeb8 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