Modifying signal handlers is a process-global operation. When two
threads run coroutine-sigaltstack's qemu_coroutine_new() concurrently,
they may interfere with each other: One of them may revert the SIGUSR2
handler back to the default between the other thread setting up
coroutine_trampoline() as the handler and raising SIGUSR2. That SIGUSR2
will then lead to the process exiting.
Outside of coroutine-sigaltstack, qemu does not use SIGUSR2. We can
thus keep the signal handler installed all the time.
CoroutineThreadState.tr_handler tells coroutine_trampoline() whether its
stack is set up so a new coroutine is to be launched (i.e., it should
invoke sigsetjmp()), or not (i.e., the signal came from an external
source and we should just perform the default action, which is to exit
the process).
Note that in user-mode emulation, the guest can register signal handlers
for any signal but SIGSEGV and SIGBUS, so if it registers a SIGUSR2
handler, sigaltstack coroutines will break from then on. However, we do
not use coroutines for user-mode emulation, so that is fine.
Suggested-by: Laszlo Ersek <ler...@redhat.com>
Signed-off-by: Max Reitz <mre...@redhat.com>
---
util/coroutine-sigaltstack.c | 56 +++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c
index aade82afb8..2d32afc322 100644
--- a/util/coroutine-sigaltstack.c
+++ b/util/coroutine-sigaltstack.c
@@ -59,6 +59,8 @@ typedef struct {
static pthread_key_t thread_state_key;
+static void coroutine_trampoline(int signal);
+
static CoroutineThreadState *coroutine_get_thread_state(void)
{
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
@@ -80,6 +82,7 @@ static void qemu_coroutine_thread_cleanup(void *opaque)
static void __attribute__((constructor)) coroutine_init(void)
{
+ struct sigaction sa;
int ret;
ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
@@ -87,6 +90,20 @@ static void __attribute__((constructor)) coroutine_init(void)
fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
abort();
}
+
+ /*
+ * Establish the SIGUSR2 signal handler. This is a process-wide
+ * operation, and so will apply to all threads from here on.
+ */
+ sa = (struct sigaction) {
+ .sa_handler = coroutine_trampoline,
+ .sa_flags = SA_ONSTACK,
+ };
+
+ if (sigaction(SIGUSR2, &sa, NULL) != 0) {
+ perror("Unable to install SIGUSR2 handler");
+ abort();
+ }
}
/* "boot" function
@@ -121,7 +138,17 @@ static void coroutine_trampoline(int signal)
/* Get the thread specific information */
coTS = coroutine_get_thread_state();
self = coTS->tr_handler;
+
+ if (!self) {
+ /*
+ * This SIGUSR2 came from an external source, not from
+ * qemu_coroutine_new(), so perform the default action.
+ */
+ exit(0);
+ }
+
coTS->tr_called = 1;
+ coTS->tr_handler = NULL;
co = &self->base;
/*