1.0.25.36: unblock signals on low level errors
authorGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 21:56:20 +0000 (21:56 +0000)
committerGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 21:56:20 +0000 (21:56 +0000)
Low level errors (control stack exhausted, memory fault) may trigger
at inconvenient times such as in signal handlers still running with
deferrables blocked. Due to the condition handling mechanism this
leads to executing user code at unsafe places and the image may very
well become corrupted. To allow continuing anyway with fingers crossed
to diagnose the problem, deferrable and/or gc signals are unblocked
before arranging for returning to the Lisp function that signals the
appropriate condition. Before that is done, however, a warning is
fprintf'ed to stderr to that this important piece of information is
not lost in some generic condition handler (or in a interrupt handler
async unwinding before anyone gets a chance to handle the condition)
which makes diagnosis of subsequent problems very hard.

This was brought to light by the checks added in the previous commit.

src/code/target-signal.lisp
src/runtime/interrupt.c
src/runtime/interrupt.h
src/runtime/x86-64-darwin-os.c
src/runtime/x86-darwin-os.c
version.lisp-expr

index 53344e1..da471d1 100644 (file)
   (ignore-interrupt sigpipe)
   (enable-interrupt sigalrm #'sigalrm-handler)
   #!+hpux (ignore-interrupt sigxcpu)
-  (unblock-deferrable-signals)
   (unblock-gc-signals)
+  (unblock-deferrable-signals)
   (values))
 \f
 ;;;; etc.
index ff62022..b9e8b11 100644 (file)
@@ -111,6 +111,31 @@ sigaddset_deferrable(sigset_t *s)
 }
 
 void
+sigdelset_deferrable(sigset_t *s)
+{
+    sigdelset(s, SIGHUP);
+    sigdelset(s, SIGINT);
+    sigdelset(s, SIGQUIT);
+    sigdelset(s, SIGPIPE);
+    sigdelset(s, SIGALRM);
+    sigdelset(s, SIGURG);
+    sigdelset(s, SIGTSTP);
+    sigdelset(s, SIGCHLD);
+    sigdelset(s, SIGIO);
+#ifndef LISP_FEATURE_HPUX
+    sigdelset(s, SIGXCPU);
+    sigdelset(s, SIGXFSZ);
+#endif
+    sigdelset(s, SIGVTALRM);
+    sigdelset(s, SIGPROF);
+    sigdelset(s, SIGWINCH);
+
+#ifdef LISP_FEATURE_SB_THREAD
+    sigdelset(s, SIG_INTERRUPT_THREAD);
+#endif
+}
+
+void
 sigaddset_blockable(sigset_t *sigset)
 {
     sigaddset_deferrable(sigset);
@@ -125,6 +150,14 @@ sigaddset_gc(sigset_t *sigset)
 #endif
 }
 
+void
+sigdelset_gc(sigset_t *sigset)
+{
+#ifdef LISP_FEATURE_SB_THREAD
+    sigdelset(sigset,SIG_STOP_FOR_GC);
+#endif
+}
+
 /* initialized in interrupt_init */
 sigset_t deferrable_sigset;
 sigset_t blockable_sigset;
@@ -310,9 +343,23 @@ block_deferrable_signals(void)
 }
 
 void
+unblock_deferrable_signals_in_sigset(sigset_t *sigset)
+{
+#ifndef LISP_FEATURE_WIN32
+    if (interrupt_handler_pending_p())
+        lose("unblock_deferrable_signals_in_sigset: losing proposition\n");
+    check_gc_signals_unblocked_in_sigset_or_lose(sigset);
+    sigdelset_deferrable(sigset);
+#endif
+}
+
+void
 unblock_deferrable_signals(void)
 {
 #ifndef LISP_FEATURE_WIN32
+    if (interrupt_handler_pending_p())
+        lose("unblock_deferrable_signals: losing proposition\n");
+    check_gc_signals_unblocked_or_lose();
     thread_sigmask(SIG_UNBLOCK, &deferrable_sigset, 0);
 #endif
 }
@@ -325,6 +372,30 @@ unblock_gc_signals(void)
 #endif
 }
 
+void
+unblock_signals_in_context_and_maybe_warn(os_context_t *context)
+{
+#ifndef LISP_FEATURE_WIN32
+    int i, oops=0;
+    sigset_t *sigset=os_context_sigmask_addr(context);
+    for(i = 1; i < NSIG; i++) {
+        if (sigismember(&gc_sigset, i) && sigismember(sigset, i)) {
+            if (!oops) {
+                fprintf(stderr,
+"Enabling blocked gc signals to allow returning to Lisp without risking\n\
+gc deadlocks. Since GC signals are only blocked in signal handlers when \n\
+they are not safe to interrupt at all, this is a pretty severe occurrence.\n");
+            }
+            oops=1;
+        }
+    }
+    sigdelset_gc(sigset);
+    if (!interrupt_handler_pending_p()) {
+        unblock_deferrable_signals_in_sigset(sigset);
+    }
+#endif
+}
+
 \f
 /*
  * utility routines used by various signal handlers
@@ -1180,6 +1251,11 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
         protect_control_stack_guard_page(0);
         protect_control_stack_return_guard_page(1);
 
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+        /* For the unfortunate case, when the control stack is
+         * exhausted in a signal handler. */
+        unblock_signals_in_context_and_maybe_warn(context);
+#endif
         arrange_return_to_lisp_function
             (context, StaticSymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
         return 1;
@@ -1190,6 +1266,7 @@ 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);
         protect_control_stack_return_guard_page(0);
         return 1;
@@ -1444,6 +1521,7 @@ lisp_memory_fault_error(os_context_t *context, os_vm_address_t addr)
     current_memory_fault_address = addr;
     /* To allow debugging memory faults in signal handlers and such. */
     corruption_warning_and_maybe_lose("Memory fault");
+    unblock_signals_in_context_and_maybe_warn(context);
     arrange_return_to_lisp_function(context,
                                     StaticSymbolFunction(MEMORY_FAULT_ERROR));
 }
index e2b24c1..bf9fe42 100644 (file)
@@ -40,6 +40,7 @@ extern sigset_t gc_sigset;
 extern void block_blockable_signals(void);
 extern void unblock_deferrable_signals(void);
 extern void unblock_gc_signals(void);
+extern void unblock_signals_in_context_and_maybe_warn(os_context_t *context);
 
 extern void check_deferrables_blocked_or_lose(void);
 extern void check_blockables_blocked_or_lose(void);
index 6542978..6360e00 100644 (file)
@@ -280,6 +280,7 @@ void
 control_stack_exhausted_handler(int signal, siginfo_t *siginfo, void *void_context) {
     os_context_t *context = arch_os_get_context(&void_context);
 
+    unblock_signals_in_context_and_maybe_warn(context);
     arrange_return_to_lisp_function
         (context, StaticSymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
 }
index bb9212e..f1d7b6c 100644 (file)
@@ -374,6 +374,7 @@ void
 control_stack_exhausted_handler(int signal, siginfo_t *siginfo, void *void_context) {
     os_context_t *context = arch_os_get_context(&void_context);
 
+    unblock_signals_in_context_and_maybe_warn(context);
     arrange_return_to_lisp_function
         (context, StaticSymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
 }
index 4b69e45..a77e414 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.25.35"
+"1.0.25.36"