[PATCH 04/13] nptl: Fix Race conditions in pthread cancellation (BZ#12683)
Szabolcs Nagy
szabolcs.nagy@arm.com
Fri Oct 9 12:08:00 GMT 2015
On 07/10/15 17:23, Adhemerval Zanella wrote:
> diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
> index 79bcaab..3c5e3b7 100644
> --- a/nptl/nptl-init.c
> +++ b/nptl/nptl-init.c
> @@ -38,6 +38,7 @@
> #include <kernel-features.h>
> #include <libc-internal.h>
> #include <pthread-pids.h>
> +#include <sysdep-cancel.h>
>
> #ifndef TLS_MULTIPLE_THREADS_IN_TCB
> /* Pointer to the corresponding variable in libc. */
> @@ -200,36 +201,41 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
> return;
>
> struct pthread *self = THREAD_SELF;
> + volatile struct pthread *pd = (volatile struct pthread *) self;
> + ucontext_t *uc = ctx;
> + const char *tip = (const char *)__pthread_get_ip (ctx);
>
i assume *pd is volatile here (and elsewhere) because
pd->cancelhandling is modified in a signal handler (here).
i think marking the cancelhandling field in the pthread
struct volatile would be cleaner (to avoid volatile casts).
> - int oldval = THREAD_GETMEM (self, cancelhandling);
> - while (1)
> + extern const char __syscall_cancel_arch_start[1];
> + extern const char __syscall_cancel_arch_end[1];
> +
> + if (((pd->cancelhandling & (CANCELSTATE_BITMASK)) != 0)
> + || ((pd->cancelhandling & CANCELED_BITMASK) == 0))
> + return;
> +
> + __sigaddset (&uc->uc_sigmask, SIGCANCEL);
> +
> + /* Check if asynchronous cancellation mode is set and if interrupted
> + instruction pointer falls within the cancellable syscall code. 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 (pd->cancelhandling & CANCELTYPE_BITMASK ||
> + (tip >= __syscall_cancel_arch_start && tip < __syscall_cancel_arch_end))
> {
> - /* 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 ((newval & CANCELTYPE_BITMASK) != 0)
> - /* Run the registered destructors and terminate the thread. */
> - __do_cancel ();
> -
> - break;
> - }
> -
> - oldval = curval;
> + THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT);
> + THREAD_SETMEM (self, result, PTHREAD_CANCELED);
> +
the atomic bit set here uses self->cancelhandling,
i assume atomic is ok in an async-signal-handler
and implies voltile, but is atomicity required here?
> + /* __pthread_sigmask removes SIGCANCEL from the set. */
> + INTERNAL_SYSCALL_DECL (err);
> + INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIGCANCEL, &uc->uc_sigmask, 0,
> + _NSIG / 8);
> +
> + __do_cancel ();
> }
> +
> + INLINE_SYSCALL (tgkill, 3, THREAD_GETMEM (THREAD_SELF, pid), pd->tid,
> + SIGCANCEL);
does pd->tid have to be a volatile access here?
> }
> #endif
>
> @@ -400,7 +406,10 @@ __pthread_initialize_minimal_internal (void)
> 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;
> + /* 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;
> (void) __libc_sigaction (SIGCANCEL, &sa, NULL);
> # endif
More information about the Libc-alpha
mailing list