@@ -2534,13 +2534,13 @@ aiocb64}, since the LFS transparently replaces the old interface.
@c sigemptyset ok
@c sigaddset ok
@c setjmp ok
-@c CANCEL_ASYNC -> pthread_enable_asynccancel ok
+@c __pthread_setcanceltype ok
@c do_cancel ok
@c pthread_unwind ok
@c Unwind_ForcedUnwind or longjmp ok [@ascuheap @acsmem?]
@c lll_lock @asulock @aculock
@c lll_unlock @asulock @aculock
-@c CANCEL_RESET -> pthread_disable_asynccancel ok
+@c __pthread_setcanceltype ok
@c lll_futex_wait ok
@c ->start_routine ok -----
@c call_tls_dtors @asulock @ascuheap @aculock @acsmem
@@ -60,6 +60,7 @@ routines = \
pthread_self \
pthread_setschedparam \
register-atfork \
+ syscall_cancel
shared-only-routines = forward
static-only-routines = pthread_atfork
@@ -123,7 +124,8 @@ libpthread-routines = nptl-init nptlfreeres vars events version pt-interp \
pthread_barrierattr_setpshared \
pthread_key_create pthread_key_delete \
pthread_getspecific pthread_setspecific \
- pthread_sigmask pthread_kill pthread_sigqueue \
+ pthread_sigmask pthread_kill pthread_kill_internal \
+ pthread_sigqueue \
pthread_cancel pthread_testcancel \
pthread_setcancelstate pthread_setcanceltype \
pthread_once \
@@ -137,7 +139,6 @@ libpthread-routines = nptl-init nptlfreeres vars events version pt-interp \
cleanup cleanup_defer cleanup_compat \
cleanup_defer_compat unwind \
pt-longjmp pt-cleanup\
- cancellation \
lowlevellock \
lll_timedlock_wait \
pt-fork pt-fcntl \
@@ -187,8 +188,7 @@ 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-libc-cancellation.c += -fasynchronous-unwind-tables
+CFLAGS-libc-cancellation.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
@@ -286,7 +286,7 @@ tests = tst-attr2 tst-attr3 tst-default-attr \
tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \
tst-cancel21 tst-cancel22 tst-cancel23 tst-cancel24 \
- tst-cancel26 tst-cancel27 tst-cancel28 \
+ tst-cancel26 tst-cancel27 tst-cancel28 tst-cancel29 \
tst-cancel-self tst-cancel-self-cancelstate \
tst-cancel-self-canceltype tst-cancel-self-testcancel \
tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 tst-cleanup4 \
@@ -41,6 +41,10 @@ libc {
__libc_allocate_rtsig_private;
# Used by the C11 threads implementation.
__pthread_cond_destroy; __pthread_cond_init;
+ # Used by pthread cancellation.
+ __syscall_cancel;
+ __syscall_cancel_arch_start;
+ __syscall_cancel_arch_end;
}
}
deleted file mode 100644
@@ -1,78 +0,0 @@
-/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
-
- 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 <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
-attribute_hidden
-__pthread_enable_asynccancel (void)
-{
- struct pthread *self = THREAD_SELF;
-
- int oldval = THREAD_GETMEM (self, canceltype);
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
-
- int ch = THREAD_GETMEM (self, cancelhandling);
-
- if (self->cancelstate == PTHREAD_CANCEL_ENABLE
- && (ch & (CANCELED_BITMASK | EXITING_BITMASK | TERMINATED_BITMASK))
- == CANCELED_BITMASK)
- {
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
- __do_cancel ();
- }
-
- return oldval;
-}
-
-/* See the comment for __pthread_enable_asynccancel regarding
- the AS-safety of this function. */
-void
-attribute_hidden
-__pthread_disable_asynccancel (int oldtype)
-{
- /* If asynchronous cancellation was enabled before we do not have
- anything to do. */
- if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS)
- return;
-
- struct pthread *self = THREAD_SELF;
- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED);
-
- /* 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. */
- int ch = THREAD_GETMEM (self, cancelhandling);
- while (__glibc_unlikely ((ch & (CANCELING_BITMASK | CANCELED_BITMASK))
- == CANCELING_BITMASK))
- {
- futex_wait_simple ((unsigned int *) &self->cancelhandling, ch,
- FUTEX_PRIVATE);
- ch = THREAD_GETMEM (self, cancelhandling);
- }
-}
@@ -269,9 +269,6 @@ struct pthread
/* Flags determining processing of cancellation. */
int cancelhandling;
- /* Bit set if canceling has been initiated. */
-#define CANCELING_BIT 2
-#define CANCELING_BITMASK (0x01 << CANCELING_BIT)
/* Bit set if canceled. */
#define CANCELED_BIT 3
#define CANCELED_BITMASK (0x01 << CANCELED_BIT)
@@ -18,7 +18,44 @@
#include "pthreadP.h"
+/* Cancellation function called by all cancellable syscalls. */
+long int
+__syscall_cancel (__syscall_arg_t nr, __syscall_arg_t a1,
+ __syscall_arg_t a2, __syscall_arg_t a3,
+ __syscall_arg_t a4, __syscall_arg_t a5,
+ __syscall_arg_t a6)
+{
+ struct pthread *pd = THREAD_SELF;
+ long int result;
-#define __pthread_enable_asynccancel __libc_enable_asynccancel
-#define __pthread_disable_asynccancel __libc_disable_asynccancel
-#include <nptl/cancellation.c>
+ /* If cancellation is not enabled, call the syscall directly. */
+ if (pd->cancelstate == PTHREAD_CANCEL_DISABLE)
+ {
+ result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6);
+ if (INTERNAL_SYSCALL_ERROR_P (result))
+ return -INTERNAL_SYSCALL_ERRNO (result);
+ return result;
+ }
+
+ /* 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);
+
+ if (result == -EINTR
+ && __pthread_self_cancelled ()
+ && pd->cancelstate == PTHREAD_CANCEL_ENABLE)
+ __do_cancel (PTHREAD_CANCELED);
+
+ return result;
+}
+libc_hidden_def (__syscall_cancel)
+
+/* Since __do_cancel is a always inline function, this creates a symbol the
+ arch-specific symbol can call to cancel the thread. */
+_Noreturn void
+attribute_hidden
+__syscall_do_cancel (void)
+{
+ __do_cancel (PTHREAD_CANCELED);
+}
@@ -39,6 +39,9 @@
#include <libc-pointer-arith.h>
#include <pthread-pids.h>
#include <pthread_mutex_conf.h>
+#include <sigcontextinfo.h>
+#include <cancellation-sigmask.h>
+#include <cancellation-pc-check.h>
#ifndef TLS_MULTIPLE_THREADS_IN_TCB
/* Pointer to the corresponding variable in libc. */
@@ -137,35 +140,23 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
struct pthread *self = THREAD_SELF;
- int oldval = THREAD_GETMEM (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;
-
- int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
- oldval);
- if (curval == oldval)
- {
- /* Set the return value. */
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
-
- /* Make sure asynchronous cancellation is still enabled. */
- if (self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
- /* Run the registered destructors and terminate the thread. */
- __do_cancel ();
-
- break;
- }
-
- oldval = curval;
- }
+ if (!__pthread_self_cancelled ()
+ || self->cancelstate == PTHREAD_CANCEL_DISABLE)
+ return;
+
+ /* Add SIGCANCEL on ignored sigmask to avoid the handler to be called
+ again. */
+ ucontext_block_sigcancel (ctx);
+
+ /* Check if asynchronous cancellation mode is set or if interrupted
+ instruction pointer falls within the cancellable syscall bridge. For
+ interruptable syscalls that might generate external side-effects (partial
+ reads or writes, for instance), the kernel will set the IP to after
+ '__syscall_cancel_arch_end', thus disabling the cancellation and allowing
+ the process to handle such conditions. */
+ if (self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS
+ || cancellation_pc_check (ctx))
+ __do_cancel (PTHREAD_CANCELED);
}
@@ -260,28 +251,39 @@ __pthread_initialize_minimal_internal (void)
had to set __nptl_initial_report_events. Propagate its setting. */
THREAD_SETMEM (pd, report_events, __nptl_initial_report_events);
- struct sigaction sa;
- __sigemptyset (&sa.sa_mask);
-
/* Install the cancellation signal handler. If for some reason we
cannot install the handler we do not abort. Maybe we should, but
it is only asynchronous cancellation which is affected. */
- sa.sa_sigaction = sigcancel_handler;
- sa.sa_flags = SA_SIGINFO;
- (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
+ {
+ struct sigaction sa;
+ sa.sa_sigaction = sigcancel_handler;
+ /* The signal handle should be non-interruptible to avoid the risk of
+ spurious EINTR caused by SIGCANCEL sent to process or if pthread_cancel
+ is called while cancellation is disabled in the target thread. */
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ sa.sa_mask = sigall_set;
+ __libc_sigaction (SIGCANCEL, &sa, NULL);
+ }
- /* Install the handle to change the threads' uid/gid. */
- sa.sa_sigaction = sighandler_setxid;
- sa.sa_flags = SA_SIGINFO | SA_RESTART;
- (void) __libc_sigaction (SIGSETXID, &sa, NULL);
+ {
+ /* Install the handle to change the threads' uid/gid. */
+ struct sigaction sa;
+ __sigemptyset (&sa.sa_mask);
+ sa.sa_sigaction = sighandler_setxid;
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ __libc_sigaction (SIGSETXID, &sa, NULL);
+ }
/* The parent process might have left the signals blocked. Just in
- case, unblock it. We reuse the signal mask in the sigaction
- structure. It is already cleared. */
- __sigaddset (&sa.sa_mask, SIGCANCEL);
- __sigaddset (&sa.sa_mask, SIGSETXID);
- INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask,
- NULL, _NSIG / 8);
+ case, unblock it. */
+ {
+ struct sigaction sa;
+ __sigemptyset (&sa.sa_mask);
+ __sigaddset (&sa.sa_mask, SIGCANCEL);
+ __sigaddset (&sa.sa_mask, SIGSETXID);
+ INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sa.sa_mask,
+ NULL, _NSIG / 8);
+ }
/* Get the size of the static and alignment requirements for the TLS
block. */
@@ -286,20 +286,13 @@ extern void __nptl_unwind_freeres (void) attribute_hidden;
#endif
-/* Called when a thread reacts on a cancellation request. */
-static inline void
-__attribute ((noreturn, always_inline))
-__do_cancel (void)
-{
- struct pthread *self = THREAD_SELF;
-
- /* Make sure we get no more cancellations. */
- THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
-
- __pthread_unwind ((__pthread_unwind_buf_t *)
- THREAD_GETMEM (self, cleanup_jmp_buf));
-}
+extern long int __syscall_cancel_arch (volatile int *, __syscall_arg_t nr,
+ __syscall_arg_t arg1, __syscall_arg_t arg2, __syscall_arg_t arg3,
+ __syscall_arg_t arg4, __syscall_arg_t arg5, __syscall_arg_t arg6);
+libc_hidden_proto (__syscall_cancel_arch);
+extern _Noreturn void __syscall_do_cancel (void)
+ attribute_hidden;
/* Internal prototypes. */
@@ -461,12 +454,13 @@ extern int __pthread_equal (pthread_t thread1, pthread_t thread2);
extern int __pthread_detach (pthread_t th);
extern int __pthread_cancel (pthread_t th);
extern int __pthread_kill (pthread_t threadid, int signo);
-extern void __pthread_exit (void *value) __attribute__ ((__noreturn__));
+extern int __pthread_kill_internal (pthread_t threadid, int signo)
+ attribute_hidden;
+extern void _Noreturn __pthread_exit (void *value);
extern int __pthread_join (pthread_t threadid, void **thread_return);
extern int __pthread_setcanceltype (int type, int *oldtype);
-extern int __pthread_enable_asynccancel (void) attribute_hidden;
-extern void __pthread_disable_asynccancel (int oldtype) attribute_hidden;
extern void __pthread_testcancel (void);
+extern void __pthread_exit (void *value);
extern int __pthread_clockjoin_ex (pthread_t, void **, clockid_t,
const struct timespec *, bool)
attribute_hidden;
@@ -487,10 +481,41 @@ hidden_proto (__pthread_setspecific)
hidden_proto (__pthread_once)
hidden_proto (__pthread_setcancelstate)
hidden_proto (__pthread_testcancel)
+hidden_proto (__pthread_exit)
hidden_proto (__pthread_mutexattr_init)
hidden_proto (__pthread_mutexattr_settype)
#endif
+/* Called when a thread reacts on a cancellation request. */
+_Noreturn static inline void
+__do_cancel (void *value)
+{
+ struct pthread *self = THREAD_SELF;
+
+ /* Make sure we get no more cancellations by clearing the cancel
+ state. */
+ THREAD_SETMEM (self, cancelstate, PTHREAD_CANCEL_DISABLE);
+ THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED);
+
+ THREAD_SETMEM (self, result, value);
+
+ THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
+
+ __pthread_unwind ((__pthread_unwind_buf_t *)
+ THREAD_GETMEM (self, cleanup_jmp_buf));
+}
+
+static inline bool
+__pthread_self_cancelled (void)
+{
+ struct pthread *self = THREAD_SELF;
+ int cancelhandling = THREAD_GETMEM (self, cancelhandling);
+ return self->cancelstate == PTHREAD_CANCEL_ENABLE
+ && (cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
+ | TERMINATED_BITMASK))
+ == CANCELED_BITMASK;
+}
+
extern int __pthread_cond_broadcast_2_0 (pthread_cond_2_0_t *cond);
extern int __pthread_cond_destroy_2_0 (pthread_cond_2_0_t *cond);
extern int __pthread_cond_init_2_0 (pthread_cond_2_0_t *cond,
@@ -37,63 +37,29 @@ __pthread_cancel (pthread_t th)
#ifdef SHARED
pthread_cancel_init ();
#endif
- int result = 0;
- int oldval;
- int newval;
- do
- {
- again:
- oldval = pd->cancelhandling;
- newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
-
- /* Avoid doing unnecessary work. The atomic operation can
- potentially be expensive if the bug has to be locked and
- remote cache lines have to be invalidated. */
- 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 (pd->cancelstate == PTHREAD_CANCEL_ENABLE
- && pd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS
- && (newval & (CANCELED_BITMASK | EXITING_BITMASK
- | TERMINATED_BITMASK))
- == CANCELED_BITMASK)
- {
- /* Mark the cancellation as "in progress". */
- if (atomic_compare_and_exchange_bool_acq (&pd->cancelhandling,
- oldval | CANCELING_BITMASK,
- oldval))
- goto again;
- /* The cancellation handler will take care of marking the
- thread as canceled. */
- pid_t pid = __getpid ();
+ THREAD_ATOMIC_BIT_SET (pd, cancelhandling, CANCELED_BIT);
- int val = INTERNAL_SYSCALL_CALL (tgkill, pid, pd->tid,
- SIGCANCEL);
- if (INTERNAL_SYSCALL_ERROR_P (val))
- result = INTERNAL_SYSCALL_ERRNO (val);
+ /* 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);
- 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
- __pthread_multiple_threads = *__libc_multiple_threads_ptr = 1;
+ __pthread_multiple_threads = *__libc_multiple_threads_ptr = 1;
#endif
+
+ /* Avoid signaling when thread attempts cancel itself (pthread_kill
+ is expensive). */
+ if (pd == THREAD_SELF)
+ {
+ if (pd->cancelstate == PTHREAD_CANCEL_ENABLE
+ && pd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+ __pthread_exit (PTHREAD_CANCELED);
+ return 0;
}
- /* Mark the thread as canceled. This has to be done
- atomically since other bits could be modified as well. */
- while (atomic_compare_and_exchange_bool_acq (&pd->cancelhandling, newval,
- oldval));
- return result;
+ return __pthread_kill_internal (th, SIGCANCEL);
}
weak_alias (__pthread_cancel, pthread_cancel)
@@ -402,7 +402,7 @@ START_THREAD_DEFN
/* If the parent was running cancellation handlers while creating
the thread the new thread inherited the signal mask. Reset the
cancellation signal mask. */
- if (__glibc_unlikely (pd->parent_cancelhandling & CANCELING_BITMASK))
+ if (__glibc_unlikely (pd->parent_cancelhandling & CANCELED_BITMASK))
{
sigset_t mask;
__sigemptyset (&mask);
@@ -443,7 +443,8 @@ START_THREAD_DEFN
have ownership (see CONCURRENCY NOTES above). */
if (__glibc_unlikely (pd->stopped_start))
{
- int oldtype = CANCEL_ASYNC ();
+ int ct;
+ __pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &ct);
/* Get the lock the parent locked to force synchronization. */
lll_lock (pd->lock, LLL_PRIVATE);
@@ -453,7 +454,7 @@ START_THREAD_DEFN
/* And give it up right away. */
lll_unlock (pd->lock, LLL_PRIVATE);
- CANCEL_RESET (oldtype);
+ __pthread_setcanceltype (ct, NULL);
}
LIBC_PROBE (pthread_start, 3, (pthread_t) pd, pd->start_routine, pd->arg);
@@ -16,18 +16,15 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <stdlib.h>
#include "pthreadP.h"
-
-void
+_Noreturn void
__pthread_exit (void *value)
{
- THREAD_SETMEM (THREAD_SELF, result, value);
-
- __do_cancel ();
+ __do_cancel (value);
}
weak_alias (__pthread_exit, pthread_exit)
+hidden_def (__pthread_exit)
/* After a thread terminates, __libc_start_main decrements
__nptl_nthreads defined in pthread_create.c. */
@@ -70,7 +70,8 @@ clockwait_tid (pid_t *tidp, clockid_t clockid, const struct timespec *abstime)
/* If *tidp == tid, wait until thread terminates or the wait times out.
The kernel up to version 3.16.3 does not use the private futex
operations for futex wake-up when the clone terminates. */
- if (lll_futex_timed_wait_cancel (tidp, tid, &rt, LLL_SHARED)
+ if (lll_futex_timed_wait_cancel ((unsigned int *) tidp, tid, &rt,
+ LLL_SHARED)
== -ETIMEDOUT)
return ETIMEDOUT;
}
@@ -103,7 +104,7 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
if ((pd == self
|| (self->joinid == pd
&& (pd->cancelhandling
- & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
+ & (CANCELED_BITMASK | EXITING_BITMASK
| TERMINATED_BITMASK)) == 0))
&& !(self->cancelstate == PTHREAD_CANCEL_ENABLE
&& (pd->cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
@@ -145,7 +146,7 @@ __pthread_clockjoin_ex (pthread_t threadid, void **thread_return,
/* We need acquire MO here so that we synchronize with the
kernel's store to 0 when the clone terminates. (see above) */
while ((tid = atomic_load_acquire (&pd->tid)) != 0)
- lll_futex_wait_cancel (&pd->tid, tid, LLL_SHARED);
+ lll_futex_wait_cancel ((unsigned int *) &pd->tid, tid, LLL_SHARED);
}
pthread_cleanup_pop (0);
@@ -31,8 +31,9 @@ __pthread_kill (pthread_t threadid, int signo)
/* Not a valid thread handle. */
return ESRCH;
- return ENOSYS;
+ if (__is_internal_signal (signo))
+ return EINVAL;
+
+ return __pthread_kill_internal (threadid, signo);
}
strong_alias (__pthread_kill, pthread_kill)
-
-stub_warning (pthread_kill)
similarity index 75%
rename from sysdeps/unix/sysv/linux/pthread_kill.c
rename to nptl/pthread_kill_internal.c
@@ -16,24 +16,15 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <errno.h>
-#include <signal.h>
-#include <pthreadP.h>
-#include <tls.h>
-#include <sysdep.h>
#include <unistd.h>
+#include <pthreadP.h>
-
+/* Used internally by pthread_cancel, so we can't filter SIGCANCEL. */
int
-__pthread_kill (pthread_t threadid, int signo)
+__pthread_kill_internal (pthread_t threadid, int signo)
{
struct pthread *pd = (struct pthread *) threadid;
- /* Make sure the descriptor is valid. */
- if (DEBUGGING_P && INVALID_TD_P (pd))
- /* Not a valid thread handle. */
- return ESRCH;
-
/* Force load of pd->tid into local variable or register. Otherwise
if a thread exits between ESRCH test and tgkill, we might return
EINVAL, because pd->tid would be cleared by the kernel. */
@@ -42,11 +33,6 @@ __pthread_kill (pthread_t threadid, int signo)
/* Not a valid thread handle. */
return ESRCH;
- /* Disallow sending the signal we use for cancellation, timers,
- for the setxid implementation. */
- if (signo == SIGCANCEL || signo == SIGTIMER || signo == SIGSETXID)
- return EINVAL;
-
/* We have a special syscall to do the work. */
pid_t pid = __getpid ();
@@ -54,4 +40,3 @@ __pthread_kill (pthread_t threadid, int signo)
return (INTERNAL_SYSCALL_ERROR_P (val)
? INTERNAL_SYSCALL_ERRNO (val) : 0);
}
-strong_alias (__pthread_kill, pthread_kill)
@@ -37,4 +37,4 @@ __pthread_setcanceltype (int type, int *oldtype)
return 0;
}
-strong_alias (__pthread_setcanceltype, pthread_setcanceltype)
+weak_alias (__pthread_setcanceltype, pthread_setcanceltype)
@@ -23,16 +23,8 @@
void
__pthread_testcancel (void)
{
- struct pthread *self = THREAD_SELF;
- int cancelhandling = THREAD_GETMEM (self, cancelhandling);
- if (self->cancelstate == PTHREAD_CANCEL_ENABLE
- && (cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK
- | TERMINATED_BITMASK))
- == CANCELED_BITMASK)
- {
- THREAD_SETMEM (self, result, PTHREAD_CANCELED);
- __do_cancel ();
- }
+ if (__pthread_self_cancelled ())
+ __do_cancel (PTHREAD_CANCELED);
}
weak_alias (__pthread_testcancel, pthread_testcancel)
hidden_def (__pthread_testcancel)
@@ -58,7 +58,7 @@ __old_sem_wait (sem_t *sem)
return 0;
/* Always assume the semaphore is shared. */
- err = lll_futex_wait_cancel (futex, 0, LLL_SHARED);
+ err = lll_futex_wait_cancel ((unsigned int *) futex, 0, LLL_SHARED);
}
while (err == 0 || err == -EWOULDBLOCK);
new file mode 100644
@@ -0,0 +1,100 @@
+/* Check side-effect act for cancellable syscalls (BZ #12683).
+ Copyright (C) 2020 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>
@@ -57,7 +57,7 @@ include ../Rules
CFLAGS-aio_suspend.c += -fexceptions
CFLAGS-mq_timedreceive.c += -fexceptions -fasynchronous-unwind-tables
CFLAGS-mq_timedsend.c += -fexceptions -fasynchronous-unwind-tables
-CFLAGS-librt-cancellation.c += -fasynchronous-unwind-tables
+CFLAGS-clock_nanosleep.c += -fexceptions -fasynchronous-unwind-tables
LDFLAGS-rt.so = -Wl,--enable-new-dtags,-z,nodelete
similarity index 70%
rename from sysdeps/nptl/librt-cancellation.c
rename to sysdeps/generic/syscall_types.h
@@ -1,6 +1,6 @@
-/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+/* Types and macros used for syscall issuing.
+ Copyright (C) 2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
- Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -16,9 +16,10 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <nptl/pthreadP.h>
+#ifndef _SYSCALL_TYPES_H
+#define _SYSCALL_TYPES_H
+typedef long int __syscall_arg_t;
+#define __SSC(__x) ((__syscall_arg_t) (__x))
-#define __pthread_enable_asynccancel __librt_enable_asynccancel
-#define __pthread_disable_asynccancel __librt_disable_asynccancel
-#include <nptl/cancellation.c>
+#endif
@@ -3,5 +3,3 @@
/* No multi-thread handling enabled. */
#define SINGLE_THREAD_P (1)
#define RTLD_SINGLE_THREAD_P (1)
-#define LIBC_CANCEL_ASYNC() 0 /* Just a dummy value. */
-#define LIBC_CANCEL_RESET(val) ((void)(val)) /* Nothing, but evaluate it. */
@@ -21,8 +21,7 @@ libpthread-sysdep_routines += errno-loc
endif
ifeq ($(subdir),rt)
-librt-sysdep_routines += timer_routines librt-cancellation
-CFLAGS-librt-cancellation.c += -fexceptions -fasynchronous-unwind-tables
+librt-sysdep_routines += timer_routines
tests += tst-mqueue8x
CFLAGS-tst-mqueue8x.c += -fexceptions
new file mode 100644
@@ -0,0 +1,53 @@
+/* Architecture specific code for pthread cancellation handling.
+ Copyright (C) 2020 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _NPTL_CANCELLATION_PC_CHECK
+#define _NPTL_CANCELLATION_PC_CHECK
+
+#include <sigcontextinfo.h>
+
+/* For syscalls with side-effects, the kernel cannot restart the syscall; when
+ it is interrupted by a signal, the kernel must cause the syscall to return
+ with whatever partial result is obtained (e.g. partial read or write). In
+ this case, the saved program counter points just after the syscall
+ instruction, so the SIGCANCEL handler should not act on cancellation.
+
+ The __syscall_cancel_arch function, used for all cancellable syscalls,
+ contains two extra markers, __syscall_cancel_arch_start and
+ __syscall_cancel_arch_end. The former points to just before the initial
+ conditional branch that checks if the thread has received a cancellation
+ request, while former points to the instruction after the one responsible
+ to issue the syscall.
+
+ The function check if the program counter (PC) from ucontext_t CTX is
+ within the start and then end boundary from the __syscall_cancel_arch
+ bridge. Return TRUE if the PC is within the boundary, meaning the
+ syscall does not have any side effects; or FALSE otherwise. */
+static bool
+cancellation_pc_check (void *ctx)
+{
+ /* Both are defined in syscall_cancel.S. */
+ extern const char __syscall_cancel_arch_start[1];
+ extern const char __syscall_cancel_arch_end[1];
+
+ uintptr_t pc = sigcontext_get_pc (ctx);
+ return pc >= (uintptr_t) __syscall_cancel_arch_start
+ && pc < (uintptr_t) __syscall_cancel_arch_end;
+}
+
+#endif
new file mode 100644
@@ -0,0 +1,30 @@
+/* Architecture specific code for pthread cancellation handling.
+ Copyright (C) 2020 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _NPTL_CANCELLATION_SIGMASK_H
+#define _NPTL_CANCELLATION_SIGMASK_H
+
+/* Add the SIGCANCEL signal on sigmask set at the ucontext_t CTX obtained from
+ the sigaction handler. */
+static void
+ucontext_block_sigcancel (void *ctx)
+{
+ __sigaddset (&((ucontext_t*) ctx)->uc_sigmask, SIGCANCEL);
+}
+
+#endif
@@ -178,10 +178,7 @@ static __always_inline int
futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
int private)
{
- int oldtype;
- oldtype = __pthread_enable_asynccancel ();
- int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
- __pthread_disable_asynccancel (oldtype);
+ int err = lll_futex_timed_wait_cancel (futex_word, expected, NULL, private);
switch (err)
{
case 0:
@@ -239,10 +236,8 @@ futex_reltimed_wait_cancelable (unsigned int* futex_word,
unsigned int expected,
const struct timespec* reltime, int private)
{
- int oldtype;
- oldtype = LIBC_CANCEL_ASYNC ();
- int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
- LIBC_CANCEL_RESET (oldtype);
+ int err = lll_futex_timed_wait_cancel (futex_word, expected, reltime,
+ private);
switch (err)
{
case 0:
@@ -315,12 +310,8 @@ futex_abstimed_wait_cancelable (unsigned int* futex_word,
despite them being valid. */
if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
return ETIMEDOUT;
- int oldtype;
- oldtype = __pthread_enable_asynccancel ();
- int err = lll_futex_clock_wait_bitset (futex_word, expected,
- clockid, abstime,
- private);
- __pthread_disable_asynccancel (oldtype);
+ int err = lll_futex_clock_wait_bitset_cancel (futex_word, expected, clockid,
+ abstime, private);
switch (err)
{
case 0:
@@ -174,21 +174,36 @@
nr_wake, nr_move, mutex, val)
/* Like lll_futex_wait, but acting as a cancellable entrypoint. */
-# define lll_futex_wait_cancel(futexp, val, private) \
- ({ \
- int __oldtype = CANCEL_ASYNC (); \
- long int __err = lll_futex_wait (futexp, val, LLL_SHARED); \
- CANCEL_RESET (__oldtype); \
- __err; \
- })
+# define lll_futex_wait_cancel(futexp, val, private) \
+ lll_futex_timed_wait_cancel (futexp, val, NULL, private)
/* Like lll_futex_timed_wait, but acting as a cancellable entrypoint. */
-# define lll_futex_timed_wait_cancel(futexp, val, timeout, private) \
- ({ \
- int __oldtype = CANCEL_ASYNC (); \
- long int __err = lll_futex_timed_wait (futexp, val, timeout, private); \
- CANCEL_RESET (__oldtype); \
- __err; \
+# define lll_futex_timed_wait_cancel(futexp, val, timeout, private) \
+ ({ \
+ int __op = __lll_private_flag (FUTEX_WAIT, private); \
+ INTERNAL_SYSCALL_CANCEL (futex, futexp, __op, val, timeout); \
+ })
+
+/* Like lll_futex_clock_wait_bitset, but acting as a cancellable
+ entrypoint. */
+# define lll_futex_clock_wait_bitset_cancel(futexp, val, clockid, timeout, \
+ private) \
+ ({ \
+ long int __ret; \
+ if (lll_futex_supported_clockid (clockid)) \
+ { \
+ const unsigned int __clockbit = \
+ (clockid == CLOCK_REALTIME) ? FUTEX_CLOCK_REALTIME : 0; \
+ const int __op = \
+ __lll_private_flag (FUTEX_WAIT_BITSET | __clockbit, private); \
+ \
+ __ret = INTERNAL_SYSCALL_CANCEL (futex, futexp, __op, val, \
+ timeout, NULL, \
+ FUTEX_BITSET_MATCH_ANY); \
+ } \
+ else \
+ __ret = -EINVAL; \
+ __ret; \
})
#endif /* !__ASSEMBLER__ */
@@ -15,6 +15,9 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
+#ifndef _SYSDEP_UNIX_H
+#define _SYSDEP_UNIX_H 1
+
#include <sysdeps/generic/sysdep.h>
#include <single-thread.h>
#include <sys/syscall.h>
@@ -24,6 +27,9 @@
#define SYSCALL__(name, args) PSEUDO (__##name, name, args)
#define SYSCALL(name, args) PSEUDO (name, name, args)
+#ifndef __ASSEMBLER__
+# include <errno.h>
+
#define __SYSCALL_CONCAT_X(a,b) a##b
#define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X (a, b)
@@ -57,6 +63,29 @@
#define INTERNAL_SYSCALL_CALL(...) \
__INTERNAL_SYSCALL_DISP (__INTERNAL_SYSCALL, __VA_ARGS__)
+#define __INTERNAL_SYSCALL_NCS0(name) \
+ INTERNAL_SYSCALL_NCS (name, 0)
+#define __INTERNAL_SYSCALL_NCS1(name, a1) \
+ INTERNAL_SYSCALL_NCS (name, 1, a1)
+#define __INTERNAL_SYSCALL_NCS2(name, a1, a2) \
+ INTERNAL_SYSCALL_NCS (name, 2, a1, a2)
+#define __INTERNAL_SYSCALL_NCS3(name, a1, a2, a3) \
+ INTERNAL_SYSCALL_NCS (name, 3, a1, a2, a3)
+#define __INTERNAL_SYSCALL_NCS4(name, a1, a2, a3, a4) \
+ INTERNAL_SYSCALL_NCS (name, 4, a1, a2, a3, a4)
+#define __INTERNAL_SYSCALL_NCS5(name, a1, a2, a3, a4, a5) \
+ INTERNAL_SYSCALL_NCS (name, 5, a1, a2, a3, a4, a5)
+#define __INTERNAL_SYSCALL_NCS6(name, a1, a2, a3, a4, a5, a6) \
+ INTERNAL_SYSCALL_NCS (name, 6, a1, a2, a3, a4, a5, a6)
+#define __INTERNAL_SYSCALL_NCS7(name, a1, a2, a3, a4, a5, a6, a7) \
+ INTERNAL_SYSCALL_NCS (name, 7, a1, a2, a3, a4, a5, a6, a7)
+
+/* Issue a syscall defined by syscall number plus any other argument required.
+ It is similar to INTERNAL_SYSCALL_NCS macro, but without the need to pass
+ the expected argument number as third parameter. */
+#define INTERNAL_SYSCALL_NCS_CALL(...) \
+ __INTERNAL_SYSCALL_DISP (__INTERNAL_SYSCALL_NCS, __VA_ARGS__)
+
#define __INLINE_SYSCALL0(name) \
INLINE_SYSCALL (name, 0)
#define __INLINE_SYSCALL1(name, a1) \
@@ -88,35 +117,68 @@
#define INLINE_SYSCALL_CALL(...) \
__INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__)
-#define SYSCALL_CANCEL(...) \
- ({ \
- long int sc_ret; \
- if (SINGLE_THREAD_P) \
- sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
- else \
- { \
- int sc_cancel_oldtype = LIBC_CANCEL_ASYNC (); \
- sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
- LIBC_CANCEL_RESET (sc_cancel_oldtype); \
- } \
- sc_ret; \
- })
-/* Issue a syscall defined by syscall number plus any other argument
- required. Any error will be returned unmodified (including errno). */
-#define INTERNAL_SYSCALL_CANCEL(...) \
- ({ \
- long int sc_ret; \
- if (SINGLE_THREAD_P) \
- sc_ret = INTERNAL_SYSCALL_CALL (__VA_ARGS__); \
- else \
- { \
- int sc_cancel_oldtype = LIBC_CANCEL_ASYNC (); \
- sc_ret = INTERNAL_SYSCALL_CALL (__VA_ARGS__); \
- LIBC_CANCEL_RESET (sc_cancel_oldtype); \
- } \
- sc_ret; \
+/* Cancellation macros. */
+#include <syscall_types.h>
+
+long int __syscall_cancel (__syscall_arg_t nr, __syscall_arg_t arg1,
+ __syscall_arg_t arg2, __syscall_arg_t arg3,
+ __syscall_arg_t arg4, __syscall_arg_t arg5,
+ __syscall_arg_t arg6);
+libc_hidden_proto (__syscall_cancel);
+
+#define __SYSCALL_CANCEL0(name) \
+ __syscall_cancel (__NR_##name, 0, 0, 0, 0, 0, 0)
+#define __SYSCALL_CANCEL1(name, a1) \
+ __syscall_cancel (__NR_##name, __SSC (a1), 0, 0, 0, 0, 0)
+#define __SYSCALL_CANCEL2(name, a1, a2) \
+ __syscall_cancel (__NR_##name, __SSC (a1), __SSC (a2), 0, 0, 0, 0)
+#define __SYSCALL_CANCEL3(name, a1, a2, a3) \
+ __syscall_cancel (__NR_##name, __SSC (a1), __SSC (a2), __SSC (a3), \
+ 0, 0, 0)
+#define __SYSCALL_CANCEL4(name, a1, a2, a3, a4) \
+ __syscall_cancel (__NR_##name, __SSC (a1), __SSC (a2), __SSC (a3), \
+ __SSC(a4), 0, 0)
+#define __SYSCALL_CANCEL5(name, a1, a2, a3, a4, a5) \
+ __syscall_cancel (__NR_##name, __SSC (a1), __SSC (a2), __SSC (a3), \
+ __SSC(a4), __SSC (a5), 0)
+#define __SYSCALL_CANCEL6(name, a1, a2, a3, a4, a5, a6) \
+ __syscall_cancel (__NR_##name, __SSC (a1), __SSC (a2), __SSC (a3), \
+ __SSC (a4), __SSC (a5), __SSC (a6))
+
+#define __SYSCALL_CANCEL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
+#define __SYSCALL_CANCEL_NARGS(...) \
+ __SYSCALL_CANCEL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)
+#define __SYSCALL_CANCEL_CONCAT_X(a,b) a##b
+#define __SYSCALL_CANCEL_CONCAT(a,b) __SYSCALL_CANCEL_CONCAT_X (a, b)
+#define __SYSCALL_CANCEL_DISP(b,...) \
+ __SYSCALL_CANCEL_CONCAT (b,__SYSCALL_CANCEL_NARGS(__VA_ARGS__))(__VA_ARGS__)
+
+#define __SYSCALL_CANCEL_CALL(...) \
+ __SYSCALL_CANCEL_DISP (__SYSCALL_CANCEL, __VA_ARGS__)
+
+/* Issue a cancellable syscall defined by syscall number NAME plus any other
+ argument required. If an error occurs its value is returned as an negative
+ number unmodified and errno is not set. */
+#define INTERNAL_SYSCALL_CANCEL(name, args...) \
+ __SYSCALL_CANCEL_CALL (name, args)
+
+/* Issue a cancellable syscall defined first argument plus any other argument
+ required. If and error occurs its value, the macro returns -1 and sets
+ errno accordingly. */
+#if IS_IN (rtld)
+/* The loader does not need to handle thread cancellation, use direct
+ syscall instead. */
+# define SYSCALL_CANCEL(...) INLINE_SYSCALL_CALL (__VA_ARGS__)
+#else
+# define SYSCALL_CANCEL(...) \
+ ({ \
+ long int sc_ret = __SYSCALL_CANCEL_CALL (__VA_ARGS__); \
+ SYSCALL_CANCEL_RET ((sc_ret)); \
})
+#endif
+
+#endif /* __ASSEMBLER__ */
/* Machine-dependent sysdep.h files are expected to define the macro
PSEUDO (function_name, syscall_name) to emit assembly code to define the
@@ -146,3 +208,5 @@
#ifndef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...) __syscall_##name (args)
#endif
+
+#endif /* _SYSDEP_UNIX_H */
@@ -87,18 +87,32 @@
})
-#if IS_IN (libc)
-# define __pthread_enable_asynccancel __libc_enable_asynccancel
-# define __pthread_disable_asynccancel __libc_disable_asynccancel
-#endif
-
-#define SOCKETCALL_CANCEL(name, args...) \
- ({ \
- int oldtype = LIBC_CANCEL_ASYNC (); \
- long int sc_ret = __SOCKETCALL (SOCKOP_##name, args); \
- LIBC_CANCEL_RESET (oldtype); \
- sc_ret; \
- })
-
+#define __SOCKETCALL_CANCEL1(__name, __a1) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [1]) { (long int) __a1 }))
+#define __SOCKETCALL_CANCEL2(__name, __a1, __a2) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [2]) { (long int) __a1, (long int) __a2 }))
+#define __SOCKETCALL_CANCEL3(__name, __a1, __a2, __a3) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [3]) { (long int) __a1, (long int) __a2, (long int) __a3 }))
+#define __SOCKETCALL_CANCEL4(__name, __a1, __a2, __a3, __a4) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [4]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+ (long int) __a4 }))
+#define __SOCKETCALL_CANCEL5(__name, __a1, __a2, __a3, __a4, __a5) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [5]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+ (long int) __a4, (long int) __a5 }))
+#define __SOCKETCALL_CANCEL6(__name, __a1, __a2, __a3, __a4, __a5, __a6) \
+ SYSCALL_CANCEL (socketcall, __name, \
+ ((long int [6]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+ (long int) __a4, (long int) __a5, (long int) __a6 }))
+
+#define __SOCKETCALL_CANCEL(...) __SOCKETCALL_DISP (__SOCKETCALL_CANCEL,\
+ __VA_ARGS__)
+
+#define SOCKETCALL_CANCEL(name, args...) \
+ __SOCKETCALL_CANCEL (SOCKOP_##name, args)
#endif /* sys/socketcall.h */
new file mode 100644
@@ -0,0 +1,62 @@
+/* Default cancellation syscall bridge.
+ Copyright (C) 2020 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <sysdep.h>
+#include <pthreadP.h>
+
+#warning "This implementation should be use just as reference or for bootstrapping"
+
+/* This is the generic version of the cancellable syscall code which
+ adds the label guards (__syscall_cancel_arch_{start,end}) used
+ on SIGCANCEL sigcancel_handler (nptl-init.c) to check if the cancelled
+ syscall have side-effects that need to be signaled to program.
+
+ This implementation should be used a reference one to document the
+ implementation constraints: the __syscall_cancel_arch_end should point
+ to the immediate next instruction after the syscall one. This is because
+ kernel will signal interrupted syscall with side effects by setting
+ the signal frame program counter (on the ucontext_t third argument from
+ SA_SIGINFO signal handler) right after the syscall instruction.
+
+ If the INTERNAL_SYSCALL_NCS macro use more instructions to get the
+ error condition from kernel (as for powerpc and sparc), uses an
+ out of the line helper (as for ARM thumb), or uses a kernel helper
+ gate (as for i686 or ia64) the architecture should adjust the
+ macro or provide a custom __syscall_cancel_arch implementation. */
+long int
+__syscall_cancel_arch (volatile int *ch, __syscall_arg_t nr,
+ __syscall_arg_t a1, __syscall_arg_t a2,
+ __syscall_arg_t a3, __syscall_arg_t a4,
+ __syscall_arg_t a5, __syscall_arg_t a6)
+{
+#define ADD_LABEL(__label) \
+ asm volatile ( \
+ ".global " __label "\t\n" \
+ __label ":\n");
+
+ ADD_LABEL ("__syscall_cancel_arch_start");
+ if (__glibc_unlikely (*ch & CANCELED_BITMASK))
+ __syscall_do_cancel();
+
+ long int result = INTERNAL_SYSCALL_NCS_CALL (nr, a1, a2, a3, a4, a5, a6);
+ ADD_LABEL ("__syscall_cancel_arch_end");
+ if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result)))
+ return -INTERNAL_SYSCALL_ERRNO (result);
+ return result;
+}
+libc_hidden_def (__syscall_cancel_arch)
@@ -21,47 +21,5 @@
#define _SYSDEP_CANCEL_H
#include <sysdep.h>
-#include <tls.h>
-#include <errno.h>
-
-/* The two functions are in libc.so and not exported. */
-extern int __libc_enable_asynccancel (void) attribute_hidden;
-extern void __libc_disable_asynccancel (int oldtype) attribute_hidden;
-
-/* The two functions are in librt.so and not exported. */
-extern int __librt_enable_asynccancel (void) attribute_hidden;
-extern void __librt_disable_asynccancel (int oldtype) attribute_hidden;
-
-/* The two functions are in libpthread.so and not exported. */
-extern int __pthread_enable_asynccancel (void) attribute_hidden;
-extern void __pthread_disable_asynccancel (int oldtype) attribute_hidden;
-
-/* Set cancellation mode to asynchronous. */
-#define CANCEL_ASYNC() \
- __pthread_enable_asynccancel ()
-/* Reset to previous cancellation mode. */
-#define CANCEL_RESET(oldtype) \
- __pthread_disable_asynccancel (oldtype)
-
-#if IS_IN (libc)
-/* Same as CANCEL_ASYNC, but for use in libc.so. */
-# define LIBC_CANCEL_ASYNC() \
- __libc_enable_asynccancel ()
-/* Same as CANCEL_RESET, but for use in libc.so. */
-# define LIBC_CANCEL_RESET(oldtype) \
- __libc_disable_asynccancel (oldtype)
-#elif IS_IN (libpthread)
-# define LIBC_CANCEL_ASYNC() CANCEL_ASYNC ()
-# define LIBC_CANCEL_RESET(val) CANCEL_RESET (val)
-#elif IS_IN (librt)
-# define LIBC_CANCEL_ASYNC() \
- __librt_enable_asynccancel ()
-# define LIBC_CANCEL_RESET(val) \
- __librt_disable_asynccancel (val)
-#else
-# define LIBC_CANCEL_ASYNC() 0 /* Just a dummy value. */
-# define LIBC_CANCEL_RESET(val) ((void)(val)) /* Nothing, but evaluate it. */
-#endif
-
#endif
@@ -58,6 +58,15 @@
-1l; \
})
+/* The return error from cancellable syscall has the same semantic as non
+ cancellable ones. */
+#define SYSCALL_CANCEL_RET(__ret) \
+ ({ \
+ __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (__ret)) \
+ ? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (__ret)) \
+ : __ret; \
+ })
+
/* Provide a dummy argument that can be used to force register
alignment for register pairs if required by the syscall ABI. */
#ifdef __ASSUME_ALIGNED_REGISTER_PAIRS