Browse Source

Don't suppress prev signal handler in hw bound check (#1268)

Enhance the hw bound check reported in #1262:

When registering signal handlers for SIGSEGV & SIGBUS in boundary
check with hardware trap, preserve the previous handlers for signal
SIGSEGV and SIGBUS, and forward the signal to the preserved signal
handlers if it isn't handled by hw bound check.
Wenyong Huang 3 years ago
parent
commit
daeb7a4265
1 changed files with 40 additions and 15 deletions
  1. 40 15
      core/shared/platform/common/posix/posix_thread.c

+ 40 - 15
core/shared/platform/common/posix/posix_thread.c

@@ -448,31 +448,53 @@ mask_signals(int how)
     pthread_sigmask(how, &set, NULL);
 }
 
-__attribute__((noreturn)) static void
+static os_thread_local_attribute struct sigaction prev_sig_act_SIGSEGV;
+static os_thread_local_attribute struct sigaction prev_sig_act_SIGBUS;
+
+static void
 signal_callback(int sig_num, siginfo_t *sig_info, void *sig_ucontext)
 {
     void *sig_addr = sig_info->si_addr;
+    struct sigaction *prev_sig_act = NULL;
 
     mask_signals(SIG_BLOCK);
 
+    /* Try to handle signal with the registered signal handler */
     if (signal_handler && (sig_num == SIGSEGV || sig_num == SIGBUS)) {
         signal_handler(sig_addr);
     }
 
-    /* signal unhandled */
-    switch (sig_num) {
-        case SIGSEGV:
-            os_printf("unhandled SIGSEGV, si_addr: %p\n", sig_addr);
-            break;
-        case SIGBUS:
-            os_printf("unhandled SIGBUS, si_addr: %p\n", sig_addr);
-            break;
-        default:
-            os_printf("unhandle signal %d, si_addr: %p\n", sig_num, sig_addr);
-            break;
+    if (sig_num == SIGSEGV)
+        prev_sig_act = &prev_sig_act_SIGSEGV;
+    else if (sig_num == SIGBUS)
+        prev_sig_act = &prev_sig_act_SIGBUS;
+
+    /* Forward the signal to next handler if found */
+    if (prev_sig_act && (prev_sig_act->sa_flags & SA_SIGINFO)) {
+        prev_sig_act->sa_sigaction(sig_num, sig_info, sig_ucontext);
     }
+    else if (prev_sig_act
+             && ((void *)prev_sig_act->sa_sigaction == SIG_DFL
+                 || (void *)prev_sig_act->sa_sigaction == SIG_IGN)) {
+        sigaction(sig_num, prev_sig_act, NULL);
+    }
+    /* Output signal info and then crash if signal is unhandled */
+    else {
+        switch (sig_num) {
+            case SIGSEGV:
+                os_printf("unhandled SIGSEGV, si_addr: %p\n", sig_addr);
+                break;
+            case SIGBUS:
+                os_printf("unhandled SIGBUS, si_addr: %p\n", sig_addr);
+                break;
+            default:
+                os_printf("unhandle signal %d, si_addr: %p\n", sig_num,
+                          sig_addr);
+                break;
+        }
 
-    abort();
+        abort();
+    }
 }
 
 int
@@ -508,12 +530,15 @@ os_thread_signal_init(os_signal_handler handler)
         goto fail2;
     }
 
+    memset(&prev_sig_act_SIGSEGV, 0, sizeof(struct sigaction));
+    memset(&prev_sig_act_SIGBUS, 0, sizeof(struct sigaction));
+
     /* Install signal hanlder */
     sig_act.sa_sigaction = signal_callback;
     sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER;
     sigemptyset(&sig_act.sa_mask);
-    if (sigaction(SIGSEGV, &sig_act, NULL) != 0
-        || sigaction(SIGBUS, &sig_act, NULL) != 0) {
+    if (sigaction(SIGSEGV, &sig_act, &prev_sig_act_SIGSEGV) != 0
+        || sigaction(SIGBUS, &sig_act, &prev_sig_act_SIGBUS) != 0) {
         os_printf("Failed to register signal handler\n");
         goto fail3;
     }