Parcourir la source

[LibOS] Fix SIGABRT, SIGTERM, SIGINT to kill the whole process

Previously, there was a bug that SIGTERM and SIGINT sent from the host
OS killed only one thread but not the whole process. Additionally,
SIGABRT of the application also killed only one thread instead of the
whole process. This commit fixes this.
Dmitrii Kuvaiskii il y a 5 ans
Parent
commit
b152970e86

+ 24 - 7
LibOS/shim/src/bookkeep/shim_signal.c

@@ -794,19 +794,36 @@ void append_signal(struct shim_thread* thread, int sig, siginfo_t* info, bool ne
 
 static void sighandler_kill (int sig, siginfo_t * info, void * ucontext)
 {
-    int sig_without_coredump_bit = sig & ~(__WCOREDUMP_BIT);
+    struct shim_thread* cur_thread = get_cur_thread();
+    int sig_without_coredump_bit   = sig & ~(__WCOREDUMP_BIT);
 
     __UNUSED(ucontext);
     debug("killed by %s\n", signal_name(sig_without_coredump_bit));
 
-    if (!info->si_pid)
-        switch(sig) {
-            case SIGTERM:
-            case SIGINT:
-                shim_do_kill(-1, sig_without_coredump_bit);
-                break;
+    if (sig_without_coredump_bit == SIGABRT ||
+        (!info->si_pid && /* signal is sent from host OS, not from another process */
+         (sig_without_coredump_bit == SIGTERM || sig_without_coredump_bit == SIGINT))) {
+        /* Received signal to kill the process:
+         *   - SIGABRT must always kill the whole process (even if sent by Graphene itself),
+         *   - SIGTERM/SIGINT must kill the whole process if signal sent from host OS. */
+
+        /* If several signals arrive simultaneously, only one signal proceeds past this
+         * point. For more information, see shim_do_exit_group(). */
+        static struct atomic_int first = ATOMIC_INIT(0);
+        if (atomic_cmpxchg(&first, 0, 1) == 1) {
+            while (1)
+                DkThreadYieldExecution();
         }
 
+        do_kill_proc(cur_thread->tgid, cur_thread->tgid, SIGKILL, false);
+
+        /* Ensure that the current thread wins in setting the process code/signal.
+         * For more information, see shim_do_exit_group(). */
+        while (check_last_thread(cur_thread)) {
+            DkThreadYieldExecution();
+        }
+    }
+
     try_process_exit(0, sig);
     DkThreadExit();
 }

+ 1 - 0
LibOS/shim/test/regression/Makefile

@@ -21,6 +21,7 @@ CFLAGS-syscall += -I$(PALDIR)/../include -I$(PALDIR)/host/$(PAL_HOST)
 CFLAGS-openmp = -fopenmp
 CFLAGS-multi_pthread = -pthread
 CFLAGS-exit_group = -pthread
+CFLAGS-abort_multithread = -pthread
 
 %: %.c
 	$(call cmd,csingle)

+ 20 - 0
LibOS/shim/test/regression/abort_multithread.c

@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+/* Test if abort() in a child thread kills the whole process. This should report
+ * 134 (128 + 6 where 6 is SIGABRT) as its return code. */
+
+void* thread_abort(void* arg) {
+    abort();
+    return NULL;  /* not reached */
+}
+
+int main(int argc, char* arvg[]) {
+    pthread_t thread;
+    pthread_create(&thread, NULL, thread_abort, NULL);
+    pthread_join(thread, NULL);
+
+    printf("Main thread returns successfully (must not happen)\n");
+    return 0;
+}

+ 5 - 1
LibOS/shim/test/regression/test_libos.py

@@ -126,10 +126,14 @@ class TC_00_Bootstrap(RegressionTestCase):
         except subprocess.CalledProcessError as e:
             self.assertTrue(1 <= e.returncode and e.returncode <= 4)
 
-    def test_401_signalexit(self):
+    def test_402_signalexit(self):
         with self.expect_returncode(134):
             self.run_binary(['abort'])
 
+    def test_403_signalexit_multithread(self):
+        with self.expect_returncode(134):
+            self.run_binary(['abort_multithread'])
+
     def test_500_init_fail(self):
         try:
             self.run_binary(['init_fail'])