|
|
6c0556 |
commit f0419e6a10740a672b28e112c409ae24f5e890ab
|
|
|
6c0556 |
Author: Jakub Jelinek <jakub@redhat.com>
|
|
|
6c0556 |
Date: Thu Mar 4 15:15:33 2021 +0100
|
|
|
6c0556 |
|
|
|
6c0556 |
[PATCH] pthread_once hangs when init routine throws an exception [BZ #18435]
|
|
|
6c0556 |
|
|
|
6c0556 |
This is another attempt at making pthread_once handle throwing exceptions
|
|
|
6c0556 |
from the init routine callback. As the new testcases show, just switching
|
|
|
6c0556 |
to the cleanup attribute based cleanup does fix the tst-once5 test, but
|
|
|
6c0556 |
breaks the new tst-oncey3 test. That is because when throwing exceptions,
|
|
|
6c0556 |
only the unwind info registered cleanups (i.e. C++ destructors or cleanup
|
|
|
6c0556 |
attribute), when cancelling threads and there has been unwind info from the
|
|
|
6c0556 |
cancellation point up to whatever needs cleanup both unwind info registered
|
|
|
6c0556 |
cleanups and THREAD_SETMEM (self, cleanup, ...) registered cleanups are
|
|
|
6c0556 |
invoked, but once we hit some frame with no unwind info, only the
|
|
|
6c0556 |
THREAD_SETMEM (self, cleanup, ...) registered cleanups are invoked.
|
|
|
6c0556 |
So, to stay fully backwards compatible (allow init routines without
|
|
|
6c0556 |
unwind info which encounter cancellation points) and handle exception throwing
|
|
|
6c0556 |
we actually need to register the pthread_once cleanups in both unwind info
|
|
|
6c0556 |
and in the THREAD_SETMEM (self, cleanup, ...) way.
|
|
|
6c0556 |
If an exception is thrown, only the former will happen and we in that case
|
|
|
6c0556 |
need to also unregister the THREAD_SETMEM (self, cleanup, ...) registered
|
|
|
6c0556 |
handler, because otherwise after catching the exception the user code could
|
|
|
6c0556 |
call deeper into the stack some cancellation point, get cancelled and then
|
|
|
6c0556 |
a stale cleanup handler would clobber stack and probably crash.
|
|
|
6c0556 |
If a thread calling init routine is cancelled and unwind info ends before
|
|
|
6c0556 |
the pthread_once frame, it will be cleaned up through self->cleanup as
|
|
|
6c0556 |
before. And if unwind info is present, unwind_stop first calls the
|
|
|
6c0556 |
self->cleanup registered handler for the frame, then it will call the
|
|
|
6c0556 |
unwind info registered handler but that will already see __do_it == 0
|
|
|
6c0556 |
and do nothing.
|
|
|
6c0556 |
|
|
|
6c0556 |
# Conflicts:
|
|
|
6c0556 |
# nptl/Makefile
|
|
|
6c0556 |
# (The usual cleanups because they don't match.)
|
|
|
6c0556 |
# sysdeps/pthread/Makefile
|
|
|
6c0556 |
# (The usual cleanups because all the other tests aren't moved.)
|
|
|
6c0556 |
|
|
|
6c0556 |
diff --git a/nptl/Makefile b/nptl/Makefile
|
|
|
6c0556 |
index dcf3868869767015..70a3be23ecfcd9c9 100644
|
|
|
6c0556 |
--- a/nptl/Makefile
|
|
|
6c0556 |
+++ b/nptl/Makefile
|
|
|
6c0556 |
@@ -334,10 +334,6 @@ xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
|
|
|
6c0556 |
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
|
|
|
6c0556 |
test-srcs = tst-oddstacklimit
|
|
|
6c0556 |
|
|
|
6c0556 |
-# Test expected to fail on most targets (except x86_64) due to bug
|
|
|
6c0556 |
-# 18435 - pthread_once hangs when init routine throws an exception.
|
|
|
6c0556 |
-test-xfail-tst-once5 = yes
|
|
|
6c0556 |
-
|
|
|
6c0556 |
# Files which must not be linked with libpthread.
|
|
|
6c0556 |
tests-nolibpthread = tst-unload
|
|
|
6c0556 |
|
|
|
6c0556 |
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
|
|
|
6c0556 |
index a2d48b2015cd385c..7ddc166cf32414c4 100644
|
|
|
6c0556 |
--- a/nptl/pthreadP.h
|
|
|
6c0556 |
+++ b/nptl/pthreadP.h
|
|
|
6c0556 |
@@ -571,6 +571,67 @@ extern void __pthread_cleanup_pop (struct _pthread_cleanup_buffer *buffer,
|
|
|
6c0556 |
# undef pthread_cleanup_pop
|
|
|
6c0556 |
# define pthread_cleanup_pop(execute) \
|
|
|
6c0556 |
__pthread_cleanup_pop (&_buffer, (execute)); }
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+# if defined __EXCEPTIONS && !defined __cplusplus
|
|
|
6c0556 |
+/* Structure to hold the cleanup handler information. */
|
|
|
6c0556 |
+struct __pthread_cleanup_combined_frame
|
|
|
6c0556 |
+{
|
|
|
6c0556 |
+ void (*__cancel_routine) (void *);
|
|
|
6c0556 |
+ void *__cancel_arg;
|
|
|
6c0556 |
+ int __do_it;
|
|
|
6c0556 |
+ struct _pthread_cleanup_buffer __buffer;
|
|
|
6c0556 |
+};
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+/* Special cleanup macros which register cleanup both using
|
|
|
6c0556 |
+ __pthread_cleanup_{push,pop} and using cleanup attribute. This is needed
|
|
|
6c0556 |
+ for pthread_once, so that it supports both throwing exceptions from the
|
|
|
6c0556 |
+ pthread_once callback (only cleanup attribute works there) and cancellation
|
|
|
6c0556 |
+ of the thread running the callback if the callback or some routines it
|
|
|
6c0556 |
+ calls don't have unwind information. */
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+static __always_inline void
|
|
|
6c0556 |
+__pthread_cleanup_combined_routine (struct __pthread_cleanup_combined_frame
|
|
|
6c0556 |
+ *__frame)
|
|
|
6c0556 |
+{
|
|
|
6c0556 |
+ if (__frame->__do_it)
|
|
|
6c0556 |
+ {
|
|
|
6c0556 |
+ __frame->__cancel_routine (__frame->__cancel_arg);
|
|
|
6c0556 |
+ __frame->__do_it = 0;
|
|
|
6c0556 |
+ __pthread_cleanup_pop (&__frame->__buffer, 0);
|
|
|
6c0556 |
+ }
|
|
|
6c0556 |
+}
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+static inline void
|
|
|
6c0556 |
+__pthread_cleanup_combined_routine_voidptr (void *__arg)
|
|
|
6c0556 |
+{
|
|
|
6c0556 |
+ struct __pthread_cleanup_combined_frame *__frame
|
|
|
6c0556 |
+ = (struct __pthread_cleanup_combined_frame *) __arg;
|
|
|
6c0556 |
+ if (__frame->__do_it)
|
|
|
6c0556 |
+ {
|
|
|
6c0556 |
+ __frame->__cancel_routine (__frame->__cancel_arg);
|
|
|
6c0556 |
+ __frame->__do_it = 0;
|
|
|
6c0556 |
+ }
|
|
|
6c0556 |
+}
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+# define pthread_cleanup_combined_push(routine, arg) \
|
|
|
6c0556 |
+ do { \
|
|
|
6c0556 |
+ void (*__cancel_routine) (void *) = (routine); \
|
|
|
6c0556 |
+ struct __pthread_cleanup_combined_frame __clframe \
|
|
|
6c0556 |
+ __attribute__ ((__cleanup__ (__pthread_cleanup_combined_routine))) \
|
|
|
6c0556 |
+ = { .__cancel_routine = __cancel_routine, .__cancel_arg = (arg), \
|
|
|
6c0556 |
+ .__do_it = 1 }; \
|
|
|
6c0556 |
+ __pthread_cleanup_push (&__clframe.__buffer, \
|
|
|
6c0556 |
+ __pthread_cleanup_combined_routine_voidptr, \
|
|
|
6c0556 |
+ &__clframe);
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+# define pthread_cleanup_combined_pop(execute) \
|
|
|
6c0556 |
+ __pthread_cleanup_pop (&__clframe.__buffer, 0); \
|
|
|
6c0556 |
+ __clframe.__do_it = 0; \
|
|
|
6c0556 |
+ if (execute) \
|
|
|
6c0556 |
+ __cancel_routine (__clframe.__cancel_arg); \
|
|
|
6c0556 |
+ } while (0)
|
|
|
6c0556 |
+
|
|
|
6c0556 |
+# endif
|
|
|
6c0556 |
#endif
|
|
|
6c0556 |
|
|
|
6c0556 |
extern void __pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer,
|
|
|
6c0556 |
diff --git a/nptl/pthread_once.c b/nptl/pthread_once.c
|
|
|
6c0556 |
index 1653226286dc3539..45e965e8743d9412 100644
|
|
|
6c0556 |
--- a/nptl/pthread_once.c
|
|
|
6c0556 |
+++ b/nptl/pthread_once.c
|
|
|
6c0556 |
@@ -111,11 +111,11 @@ __pthread_once_slow (pthread_once_t *once_control, void (*init_routine) (void))
|
|
|
6c0556 |
/* This thread is the first here. Do the initialization.
|
|
|
6c0556 |
Register a cleanup handler so that in case the thread gets
|
|
|
6c0556 |
interrupted the initialization can be restarted. */
|
|
|
6c0556 |
- pthread_cleanup_push (clear_once_control, once_control);
|
|
|
6c0556 |
+ pthread_cleanup_combined_push (clear_once_control, once_control);
|
|
|
6c0556 |
|
|
|
6c0556 |
init_routine ();
|
|
|
6c0556 |
|
|
|
6c0556 |
- pthread_cleanup_pop (0);
|
|
|
6c0556 |
+ pthread_cleanup_combined_pop (0);
|
|
|
6c0556 |
|
|
|
6c0556 |
|
|
|
6c0556 |
/* Mark *once_control as having finished the initialization. We need
|
|
|
6c0556 |
diff --git a/nptl/tst-once5.cc b/nptl/tst-once5.cc
|
|
|
6c0556 |
index d232266c3ace89d9..dda18e610c9114bc 100644
|
|
|
6c0556 |
--- a/nptl/tst-once5.cc
|
|
|
6c0556 |
+++ b/nptl/tst-once5.cc
|
|
|
6c0556 |
@@ -59,7 +59,7 @@ do_test (void)
|
|
|
6c0556 |
" throwing an exception", stderr);
|
|
|
6c0556 |
}
|
|
|
6c0556 |
catch (OnceException) {
|
|
|
6c0556 |
- if (1 < niter)
|
|
|
6c0556 |
+ if (niter > 1)
|
|
|
6c0556 |
fputs ("pthread_once unexpectedly threw", stderr);
|
|
|
6c0556 |
result = 0;
|
|
|
6c0556 |
}
|
|
|
6c0556 |
@@ -75,7 +75,5 @@ do_test (void)
|
|
|
6c0556 |
return result;
|
|
|
6c0556 |
}
|
|
|
6c0556 |
|
|
|
6c0556 |
-// The test currently hangs and is XFAILed. Reduce the timeout.
|
|
|
6c0556 |
-#define TIMEOUT 1
|
|
|
6c0556 |
#define TEST_FUNCTION do_test ()
|
|
|
6c0556 |
#include "../test-skeleton.c"
|
|
|
6c0556 |
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
|
|
|
6c0556 |
index 14ef04247cb84ad3..80a71f3f9f0e72ae 100644
|
|
|
6c0556 |
--- a/sysdeps/pthread/Makefile
|
|
|
6c0556 |
+++ b/sysdeps/pthread/Makefile
|
|
|
6c0556 |
@@ -35,7 +35,7 @@ tst-create1mod.so-no-z-defs = yes
|
|
|
6c0556 |
|
|
|
6c0556 |
tests += tst-once1 tst-once2 tst-once3 tst-once4
|
|
|
6c0556 |
|
|
|
6c0556 |
-tests += tst-oncex3 tst-oncex4
|
|
|
6c0556 |
+tests += tst-oncex3 tst-oncex4 tst-oncey3 tst-oncey4
|
|
|
6c0556 |
|
|
|
6c0556 |
ifeq ($(build-shared),yes)
|
|
|
6c0556 |
# Build all the modules even when not actually running test programs.
|
|
|
6c0556 |
@@ -44,6 +44,8 @@ endif
|
|
|
6c0556 |
|
|
|
6c0556 |
CFLAGS-tst-oncex3.c += -fexceptions
|
|
|
6c0556 |
CFLAGS-tst-oncex4.c += -fexceptions
|
|
|
6c0556 |
+CFLAGS-tst-oncey3.c += -fno-exceptions -fno-asynchronous-unwind-tables
|
|
|
6c0556 |
+CFLAGS-tst-oncey4.c += -fno-exceptions -fno-asynchronous-unwind-tables
|
|
|
6c0556 |
|
|
|
6c0556 |
modules-names += tst-create1mod
|
|
|
6c0556 |
test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
|
|
|
6c0556 |
diff --git a/sysdeps/pthread/tst-oncey3.c b/sysdeps/pthread/tst-oncey3.c
|
|
|
6c0556 |
new file mode 100644
|
|
|
6c0556 |
index 0000000000000000..08225b88dc06b979
|
|
|
6c0556 |
--- /dev/null
|
|
|
6c0556 |
+++ b/sysdeps/pthread/tst-oncey3.c
|
|
|
6c0556 |
@@ -0,0 +1 @@
|
|
|
6c0556 |
+#include "tst-once3.c"
|
|
|
6c0556 |
diff --git a/sysdeps/pthread/tst-oncey4.c b/sysdeps/pthread/tst-oncey4.c
|
|
|
6c0556 |
new file mode 100644
|
|
|
6c0556 |
index 0000000000000000..9b4d98f3f13c265a
|
|
|
6c0556 |
--- /dev/null
|
|
|
6c0556 |
+++ b/sysdeps/pthread/tst-oncey4.c
|
|
|
6c0556 |
@@ -0,0 +1 @@
|
|
|
6c0556 |
+#include "tst-once4.c"
|