1.0.41.15: gencgc: Implement object pinning for non-x86oids.
[sbcl.git] / src / runtime / interrupt.c
index acd1d04..123a576 100644 (file)
 #include "globals.h"
 #include "lispregs.h"
 #include "validate.h"
+#include "interr.h"
 #include "gc.h"
 #include "alloc.h"
 #include "dynbind.h"
-#include "interr.h"
 #include "pseudo-atomic.h"
 #include "genesis/fdefn.h"
 #include "genesis/simple-fun.h"
 #include "genesis/cons.h"
 
+/* When we catch an internal error, should we pass it back to Lisp to
+ * be handled in a high-level way? (Early in cold init, the answer is
+ * 'no', because Lisp is still too brain-dead to handle anything.
+ * After sufficient initialization has been completed, the answer
+ * becomes 'yes'.) */
+boolean internal_errors_enabled = 0;
+
+#ifndef LISP_FEATURE_WIN32
+static
+void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
+#endif
+union interrupt_handler interrupt_handlers[NSIG];
+
 /* Under Linux on some architectures, we appear to have to restore the
  * FPU control word from the context, as after the signal is delivered
  * we appear to have a null FPU control word. */
     os_context_t *context = arch_os_get_context(&void_context);
 #endif
 
+/* Foreign code may want to start some threads on its own.
+ * Non-targetted, truly asynchronous signals can be delivered to
+ * basically any thread, but invoking Lisp handlers in such foregign
+ * threads is really bad, so let's resignal it.
+ *
+ * This should at least bring attention to the problem, but it cannot
+ * work for SIGSEGV and similar. It is good enough for timers, and
+ * maybe all deferrables. */
+
+#ifdef LISP_FEATURE_SB_THREAD
+static void
+add_handled_signals(sigset_t *sigset)
+{
+    int i;
+    for(i = 1; i < NSIG; i++) {
+        if (!(ARE_SAME_HANDLER(interrupt_low_level_handlers[i], SIG_DFL)) ||
+            !(ARE_SAME_HANDLER(interrupt_handlers[i].c, SIG_DFL))) {
+            sigaddset(sigset, i);
+        }
+    }
+}
+
+void block_signals(sigset_t *what, sigset_t *where, sigset_t *old);
+#endif
+
+static boolean
+maybe_resignal_to_lisp_thread(int signal, os_context_t *context)
+{
+#ifdef LISP_FEATURE_SB_THREAD
+    if (!pthread_getspecific(lisp_thread)) {
+        if (!(sigismember(&deferrable_sigset,signal))) {
+            corruption_warning_and_maybe_lose
+                ("Received signal %d in non-lisp thread %lu, resignalling to a lisp thread.",
+                 signal,
+                 pthread_self());
+        }
+        {
+            sigset_t sigset;
+            sigemptyset(&sigset);
+            add_handled_signals(&sigset);
+            block_signals(&sigset, 0, 0);
+            block_signals(&sigset, os_context_sigmask_addr(context), 0);
+            kill(getpid(), signal);
+        }
+        return 1;
+    } else
+#endif
+        return 0;
+}
+
 /* These are to be used in signal handlers. Currently all handlers are
  * called from one of:
  *
  * kernel properly, so we fix it up ourselves in the
  * arch_os_get_context(..) function. -- CSR, 2002-07-23
  */
-#define SAVE_ERRNO(context,void_context)                        \
+#define SAVE_ERRNO(signal,context,void_context)                 \
     {                                                           \
         int _saved_errno = errno;                               \
         RESTORE_FP_CONTROL_WORD(context,void_context);          \
+        if (!maybe_resignal_to_lisp_thread(signal, context))    \
         {
 
 #define RESTORE_ERRNO                                           \
@@ -250,11 +314,13 @@ sigset_t gc_sigset;
 
 #endif
 
+#if !defined(LISP_FEATURE_WIN32)
 boolean
 deferrables_blocked_p(sigset_t *sigset)
 {
     return all_signals_blocked_p(sigset, &deferrable_sigset, "deferrable");
 }
+#endif
 
 void
 check_deferrables_unblocked_or_lose(sigset_t *sigset)
@@ -274,11 +340,13 @@ check_deferrables_blocked_or_lose(sigset_t *sigset)
 #endif
 }
 
+#if !defined(LISP_FEATURE_WIN32)
 boolean
 blockables_blocked_p(sigset_t *sigset)
 {
     return all_signals_blocked_p(sigset, &blockable_sigset, "blockable");
 }
+#endif
 
 void
 check_blockables_unblocked_or_lose(sigset_t *sigset)
@@ -298,11 +366,13 @@ check_blockables_blocked_or_lose(sigset_t *sigset)
 #endif
 }
 
+#if !defined(LISP_FEATURE_WIN32)
 boolean
 gc_signals_blocked_p(sigset_t *sigset)
 {
     return all_signals_blocked_p(sigset, &gc_sigset, "gc");
 }
+#endif
 
 void
 check_gc_signals_unblocked_or_lose(sigset_t *sigset)
@@ -525,20 +595,6 @@ check_interrupt_context_or_lose(os_context_t *context)
     }
 #endif
 }
-
-/* When we catch an internal error, should we pass it back to Lisp to
- * be handled in a high-level way? (Early in cold init, the answer is
- * 'no', because Lisp is still too brain-dead to handle anything.
- * After sufficient initialization has been completed, the answer
- * becomes 'yes'.) */
-boolean internal_errors_enabled = 0;
-
-#ifndef LISP_FEATURE_WIN32
-static
-void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
-#endif
-union interrupt_handler interrupt_handlers[NSIG];
-
 \f
 /*
  * utility routines used by various signal handlers
@@ -1094,10 +1150,9 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
 static void
 maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
-
     if(!maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
         interrupt_handle_now(signal, info, context);
     RESTORE_ERRNO;
@@ -1117,7 +1172,7 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info,
 static void
 low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
 
@@ -1187,6 +1242,12 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context)
     set_thread_state(thread,STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"suspended\n"));
 
+    /* While waiting for gc to finish occupy ourselves with zeroing
+     * the unused portion of the control stack to reduce conservatism.
+     * On hypothetic platforms with threads and exact gc it is
+     * actually a must. */
+    scrub_control_stack();
+
     wait_for_thread_state_change(thread, STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"resumed\n"));
 
@@ -1203,14 +1264,14 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context)
 void
 interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
 #ifndef LISP_FEATURE_WIN32
     if ((signal == SIGILL) || (signal == SIGBUS)
 #ifndef LISP_FEATURE_LINUX
         || (signal == SIGEMT)
 #endif
         )
-        corruption_warning_and_maybe_lose("Signal %d recieved", signal);
+        corruption_warning_and_maybe_lose("Signal %d received", signal);
 #endif
     interrupt_handle_now(signal, info, context);
     RESTORE_ERRNO;
@@ -1424,23 +1485,55 @@ undefined_alien_function(void)
     funcall0(StaticSymbolFunction(UNDEFINED_ALIEN_FUNCTION_ERROR));
 }
 
+void lower_thread_control_stack_guard_page(struct thread *th)
+{
+    protect_control_stack_guard_page(0, th);
+    protect_control_stack_return_guard_page(1, th);
+    th->control_stack_guard_page_protected = NIL;
+    fprintf(stderr, "INFO: Control stack guard page unprotected\n");
+}
+
+void reset_thread_control_stack_guard_page(struct thread *th)
+{
+    memset(CONTROL_STACK_GUARD_PAGE(th), 0, os_vm_page_size);
+    protect_control_stack_guard_page(1, th);
+    protect_control_stack_return_guard_page(0, th);
+    th->control_stack_guard_page_protected = T;
+    fprintf(stderr, "INFO: Control stack guard page reprotected\n");
+}
+
+/* Called from the REPL, too. */
+void reset_control_stack_guard_page(void)
+{
+    struct thread *th=arch_os_get_current_thread();
+    if (th->control_stack_guard_page_protected == NIL) {
+        reset_thread_control_stack_guard_page(th);
+    }
+}
+
+void lower_control_stack_guard_page(void)
+{
+    lower_thread_control_stack_guard_page(arch_os_get_current_thread());
+}
+
 boolean
 handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
 {
     struct thread *th=arch_os_get_current_thread();
 
-    /* note the os_context hackery here.  When the signal handler returns,
-     * it won't go back to what it was doing ... */
-    if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
-       addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+    if(addr >= CONTROL_STACK_HARD_GUARD_PAGE(th) &&
+       addr < CONTROL_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Control stack exhausted");
+    }
+    else if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
+            addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
         /* We hit the end of the control stack: disable guard page
          * protection so the error handler has some headroom, protect the
          * previous page so that we can catch returns from the guard page
          * and restore it. */
-        corruption_warning_and_maybe_lose("Control stack exhausted");
-        protect_control_stack_guard_page(0, NULL);
-        protect_control_stack_return_guard_page(1, NULL);
-
+        if (th->control_stack_guard_page_protected == NIL)
+            lose("control_stack_guard_page_protected NIL");
+        lower_control_stack_guard_page();
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
         /* For the unfortunate case, when the control stack is
          * exhausted in a signal handler. */
@@ -1456,16 +1549,20 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * unprotect this one. This works even if we somehow missed
          * the return-guard-page, and hit it on our way to new
          * exhaustion instead. */
-        fprintf(stderr, "INFO: Control stack guard page reprotected\n");
-        protect_control_stack_guard_page(1, NULL);
-        protect_control_stack_return_guard_page(0, NULL);
+        if (th->control_stack_guard_page_protected != NIL)
+            lose("control_stack_guard_page_protected not NIL");
+        reset_control_stack_guard_page();
         return 1;
     }
+    else if(addr >= BINDING_STACK_HARD_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Binding stack exhausted");
+    }
     else if(addr >= BINDING_STACK_GUARD_PAGE(th) &&
             addr < BINDING_STACK_GUARD_PAGE(th) + os_vm_page_size) {
-        corruption_warning_and_maybe_lose("Binding stack exhausted");
         protect_binding_stack_guard_page(0, NULL);
         protect_binding_stack_return_guard_page(1, NULL);
+        fprintf(stderr, "INFO: Binding stack guard page unprotected\n");
 
         /* For the unfortunate case, when the binding stack is
          * exhausted in a signal handler. */
@@ -1476,16 +1573,20 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
     }
     else if(addr >= BINDING_STACK_RETURN_GUARD_PAGE(th) &&
             addr < BINDING_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
-        fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
         protect_binding_stack_guard_page(1, NULL);
         protect_binding_stack_return_guard_page(0, NULL);
+        fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
         return 1;
     }
+    else if(addr >= ALIEN_STACK_HARD_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Alien stack exhausted");
+    }
     else if(addr >= ALIEN_STACK_GUARD_PAGE(th) &&
             addr < ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size) {
-        corruption_warning_and_maybe_lose("Alien stack exhausted");
         protect_alien_stack_guard_page(0, NULL);
         protect_alien_stack_return_guard_page(1, NULL);
+        fprintf(stderr, "INFO: Alien stack guard page unprotected\n");
 
         /* For the unfortunate case, when the alien stack is
          * exhausted in a signal handler. */
@@ -1496,9 +1597,9 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
     }
     else if(addr >= ALIEN_STACK_RETURN_GUARD_PAGE(th) &&
             addr < ALIEN_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
-        fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
         protect_alien_stack_guard_page(1, NULL);
         protect_alien_stack_return_guard_page(0, NULL);
+        fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
         return 1;
     }
     else if (addr >= undefined_alien_address &&
@@ -1582,7 +1683,7 @@ see_if_sigaction_nodefer_works(void)
 static void
 unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
@@ -1595,7 +1696,7 @@ unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 static void
 low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
@@ -1608,7 +1709,7 @@ low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 static void
 low_level_handle_now_handler(int signal, siginfo_t *info, void *void_context)
 {
-    SAVE_ERRNO(context,void_context);
+    SAVE_ERRNO(signal,context,void_context);
     (*interrupt_low_level_handlers[signal])(signal, info, context);
     RESTORE_ERRNO;
 }