mirror of git://sourceware.org/git/glibc.git
nptl: Check if thread is already terminated in sigcancel_handler (BZ 32782)
The SIGCANCEL signal handler should not issue __syscall_do_cancel, which calls __do_cancel and __pthread_unwind, if the cancellation is already in proces (and libgcc unwind is not reentrant). Any cancellation signal received after is ignored. Checked on x86_64-linux-gnu and aarch64-linux-gnu. Tested-by: Aurelien Jarno <aurelien@aurel32.net> Reviewed-by: Florian Weimer <fweimer@redhat.com>
This commit is contained in:
parent
dbc5a50d12
commit
360cce0b06
|
@ -1,6 +1,7 @@
|
||||||
## args: double
|
## args: double
|
||||||
## ret: double
|
## ret: double
|
||||||
## includes: math.h
|
## includes: math.h
|
||||||
|
## name: workload-random
|
||||||
-0x1.79ea722d33e33p-9
|
-0x1.79ea722d33e33p-9
|
||||||
-0x1.94d4e0c3df9bcp3
|
-0x1.94d4e0c3df9bcp3
|
||||||
-0x1.b63e91ff711e0p0
|
-0x1.b63e91ff711e0p0
|
||||||
|
|
|
@ -41,15 +41,17 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx)
|
||||||
|| si->si_code != SI_TKILL)
|
|| si->si_code != SI_TKILL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Check if asynchronous cancellation mode is set or if interrupted
|
/* Check if asynchronous cancellation mode is set and cancellation is not
|
||||||
instruction pointer falls within the cancellable syscall bridge. For
|
already in progress, or if interrupted instruction pointer falls within
|
||||||
interruptable syscalls with external side-effects (i.e. partial reads),
|
the cancellable syscall bridge.
|
||||||
the kernel will set the IP to after __syscall_cancel_arch_end, thus
|
For interruptable syscalls with external side-effects (i.e. partial
|
||||||
disabling the cancellation and allowing the process to handle such
|
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. */
|
conditions. */
|
||||||
struct pthread *self = THREAD_SELF;
|
struct pthread *self = THREAD_SELF;
|
||||||
int oldval = atomic_load_relaxed (&self->cancelhandling);
|
int oldval = atomic_load_relaxed (&self->cancelhandling);
|
||||||
if (cancel_async_enabled (oldval) || cancellation_pc_check (ctx))
|
if (cancel_enabled_and_canceled_and_async (oldval)
|
||||||
|
|| cancellation_pc_check (ctx))
|
||||||
__syscall_do_cancel ();
|
__syscall_do_cancel ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ tests += \
|
||||||
tst-cancel28 \
|
tst-cancel28 \
|
||||||
tst-cancel29 \
|
tst-cancel29 \
|
||||||
tst-cancel30 \
|
tst-cancel30 \
|
||||||
|
tst-cancel32 \
|
||||||
tst-cleanup0 \
|
tst-cleanup0 \
|
||||||
tst-cleanup1 \
|
tst-cleanup1 \
|
||||||
tst-cleanup2 \
|
tst-cleanup2 \
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* Check if pthread_setcanceltype disables asynchronous cancellation
|
||||||
|
once cancellation happens (BZ 32782)
|
||||||
|
|
||||||
|
Copyright (C) 2025 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/>. */
|
||||||
|
|
||||||
|
/* The pthread_setcanceltype is a cancellation entrypoint, and if
|
||||||
|
asynchronous is enabled and the cancellation starts (on the second
|
||||||
|
pthread_setcanceltype call), the asynchronous should not restart
|
||||||
|
the process. */
|
||||||
|
|
||||||
|
#include <support/xthread.h>
|
||||||
|
|
||||||
|
#define NITER 1000
|
||||||
|
#define NTHREADS 8
|
||||||
|
|
||||||
|
static void
|
||||||
|
tf_cleanup (void *arg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
tf (void *closure)
|
||||||
|
{
|
||||||
|
pthread_cleanup_push (tf_cleanup, NULL);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* The only possible failure for pthread_setcanceltype is an
|
||||||
|
invalid state type. */
|
||||||
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||||
|
pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL);
|
||||||
|
}
|
||||||
|
pthread_cleanup_pop (1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
poll_threads (int nthreads)
|
||||||
|
{
|
||||||
|
pthread_t thr[nthreads];
|
||||||
|
for (int i = 0; i < nthreads; i++)
|
||||||
|
thr[i] = xpthread_create (NULL, tf, NULL);
|
||||||
|
for (int i = 0; i < nthreads; i++)
|
||||||
|
xpthread_cancel (thr[i]);
|
||||||
|
for (int i = 0; i < nthreads; i++)
|
||||||
|
xpthread_join (thr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_test (void)
|
||||||
|
{
|
||||||
|
for (int k = 0; k < NITER; k++)
|
||||||
|
poll_threads (NTHREADS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <support/test-driver.c>
|
Loading…
Reference in New Issue