From 8d2697777cef42c9611a015ee13292ca612b8f89 Mon Sep 17 00:00:00 2001 From: Gabor Melis Date: Mon, 16 Feb 2009 21:56:20 +0000 Subject: [PATCH] 1.0.25.36: unblock signals on low level errors 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 | 2 +- src/runtime/interrupt.c | 78 ++++++++++++++++++++++++++++++++++++++++ src/runtime/interrupt.h | 1 + src/runtime/x86-64-darwin-os.c | 1 + src/runtime/x86-darwin-os.c | 1 + version.lisp-expr | 2 +- 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/code/target-signal.lisp b/src/code/target-signal.lisp index 53344e1..da471d1 100644 --- a/src/code/target-signal.lisp +++ b/src/code/target-signal.lisp @@ -177,8 +177,8 @@ (ignore-interrupt sigpipe) (enable-interrupt sigalrm #'sigalrm-handler) #!+hpux (ignore-interrupt sigxcpu) - (unblock-deferrable-signals) (unblock-gc-signals) + (unblock-deferrable-signals) (values)) ;;;; etc. diff --git a/src/runtime/interrupt.c b/src/runtime/interrupt.c index ff62022..b9e8b11 100644 --- a/src/runtime/interrupt.c +++ b/src/runtime/interrupt.c @@ -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 +} + /* * 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)); } diff --git a/src/runtime/interrupt.h b/src/runtime/interrupt.h index e2b24c1..bf9fe42 100644 --- a/src/runtime/interrupt.h +++ b/src/runtime/interrupt.h @@ -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); diff --git a/src/runtime/x86-64-darwin-os.c b/src/runtime/x86-64-darwin-os.c index 6542978..6360e00 100644 --- a/src/runtime/x86-64-darwin-os.c +++ b/src/runtime/x86-64-darwin-os.c @@ -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)); } diff --git a/src/runtime/x86-darwin-os.c b/src/runtime/x86-darwin-os.c index bb9212e..f1d7b6c 100644 --- a/src/runtime/x86-darwin-os.c +++ b/src/runtime/x86-darwin-os.c @@ -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)); } diff --git a/version.lisp-expr b/version.lisp-expr index 4b69e45..a77e414 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -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" -- 1.7.10.4