about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2019-12-09 14:33:33 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2020-02-19 13:46:31 -0300
commit27d83441a2f900059d0421339d0e8c27220ffa36 (patch)
tree22b8a2d04bfc965462d96a99b32da18fc17eb2b5 /nptl
parent8d42bf859a289944749d9f978c076cd318119867 (diff)
downloadglibc-27d83441a2f900059d0421339d0e8c27220ffa36.tar.gz
glibc-27d83441a2f900059d0421339d0e8c27220ffa36.tar.xz
glibc-27d83441a2f900059d0421339d0e8c27220ffa36.zip
Block all signals on timer_create thread (BZ#10815)
The behavior of the signal mask on threads created by timer_create
for SIGEV_THREAD timers are implementation-defined and glibc explicit
unblocks all signals before calling the user-defined function.

This behavior, although not incorrect standard-wise, opens a race if a
program using a blocked rt-signal plus sigwaitinfo (and without an
installed signal handler for the rt-signal) receives a signal while
executing the used-defined function for SIGEV_THREAD.

A better alternative discussed in bug report is to rather block all
signals (besides the internal ones not available to application
usage).

This patch fixes this issue by only unblocking SIGSETXID (used on
set*uid function) and SIGCANCEL (used for thread cancellation).

Checked on x86_64-linux-gnu and i686-linux-gnu.
Diffstat (limited to 'nptl')
-rw-r--r--nptl/Makefile5
-rw-r--r--nptl/tst-cancel28.c79
2 files changed, 83 insertions, 1 deletions
diff --git a/nptl/Makefile b/nptl/Makefile
index 9c90af78f1..2911a3de37 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -266,7 +266,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-cancel26 tst-cancel27 tst-cancel28 \
 	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 \
@@ -574,6 +574,9 @@ $(objpfx)tst-tls6.out: tst-tls6.sh $(objpfx)tst-tls5 \
 	$(BASH) $< $(common-objpfx) '$(test-via-rtld-prefix)' \
 	  '$(test-wrapper-env)' '$(run-program-env)' > $@; \
 	$(evaluate-test)
+$(objpfx)tst-cancel28: $(common-objpfx)rt/librt.so
+else
+$(objpfx)tst-cancel28: $(common-objpfx)rt/librt.a
 endif
 
 $(objpfx)tst-dlsym1: $(libdl) $(shared-thread-library)
diff --git a/nptl/tst-cancel28.c b/nptl/tst-cancel28.c
new file mode 100644
index 0000000000..11beb0168c
--- /dev/null
+++ b/nptl/tst-cancel28.c
@@ -0,0 +1,79 @@
+/* Check if the thread created by POSIX timer using SIGEV_THREAD is
+   cancellable.
+   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/>.  */
+
+#include <stdio.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+
+static pthread_barrier_t barrier;
+static pthread_t timer_thread;
+
+static void
+cl (void *arg)
+{
+  xpthread_barrier_wait (&barrier);
+}
+
+static void
+thread_handler (union sigval sv)
+{
+  timer_thread = pthread_self ();
+
+  xpthread_barrier_wait (&barrier);
+
+  pthread_cleanup_push (cl, NULL);
+  while (1)
+    clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 1, 0 }, NULL);
+  pthread_cleanup_pop (0);
+}
+
+static int
+do_test (void)
+{
+  struct sigevent sev = { 0 };
+  sev.sigev_notify = SIGEV_THREAD;
+  sev.sigev_notify_function = &thread_handler;
+
+  timer_t timerid;
+  TEST_COMPARE (timer_create (CLOCK_REALTIME, &sev, &timerid), 0);
+
+  xpthread_barrier_init (&barrier, NULL, 2);
+
+  struct itimerspec trigger = { 0 };
+  trigger.it_value.tv_nsec = 1000000;
+  TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0);
+
+  xpthread_barrier_wait (&barrier);
+
+  xpthread_cancel (timer_thread);
+
+  xpthread_barrier_init (&barrier, NULL, 2);
+  xpthread_barrier_wait (&barrier);
+
+  return 0;
+}
+
+/* A stall in cancellation is a regression.  */
+#include <support/test-driver.c>