X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Finterrupt.c;h=123a57635e9068881e97cb449567ff1482bd853b;hb=c553e4be6da2d18f0827f190589c88e837b8b8a6;hp=e469d8f60b02dc8b832356599d17533ea2427608;hpb=6c339c2b71bf4ae48eb7a8135b5aa16c7187f1e9;p=sbcl.git diff --git a/src/runtime/interrupt.c b/src/runtime/interrupt.c index e469d8f..123a576 100644 --- a/src/runtime/interrupt.c +++ b/src/runtime/interrupt.c @@ -59,15 +59,28 @@ #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. */ @@ -80,6 +93,56 @@ 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: * @@ -98,10 +161,11 @@ * 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 \ @@ -531,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]; - /* * utility routines used by various signal handlers @@ -1100,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; @@ -1123,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; @@ -1193,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")); @@ -1209,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; @@ -1430,6 +1485,37 @@ 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) { @@ -1445,10 +1531,9 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr) * 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. */ - protect_control_stack_guard_page(0, NULL); - protect_control_stack_return_guard_page(1, NULL); - fprintf(stderr, "INFO: Control stack guard page unprotected\n"); - + 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. */ @@ -1464,9 +1549,9 @@ 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. */ - protect_control_stack_guard_page(1, NULL); - protect_control_stack_return_guard_page(0, NULL); - fprintf(stderr, "INFO: Control stack guard page reprotected\n"); + 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) && @@ -1598,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); @@ -1611,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); @@ -1624,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; }