diff options
-rw-r--r-- | ChangeLog | 32 | ||||
-rwxr-xr-x | configure | 22 | ||||
-rw-r--r-- | gc.h | 2 | ||||
-rw-r--r-- | signal.c | 77 | ||||
-rw-r--r-- | signal.h | 9 |
5 files changed, 141 insertions, 1 deletions
@@ -1,5 +1,37 @@ 2014-03-13 Kaz Kylheku <kaz@kylheku.com> + On platforms with sigaltstack, TXR programs can now catch the + segmentation fault that occurs when running out of stack space, + and escape by throwing an exception. + + Also, bugfix: save and restore the gc enable/disable state. Without + this, if we are ever running cod in a gc disabled state and it jumps + out, gc stays disabled. + + * configure: added check for sigaltstack. + + * gc.h (gc_enabled): Declaration added for existing variable. + + * signal.c (is_cpu_exception): New static function. + (sig_handler): For cpu_exception type signals that pertain + to the execution of some instruction, turn on async_sig_enabled, + so that the signal is not deferred. Otherwise we will just + return without calling the user-defined handler, restart + the instruction and get into a loop. Also, disable gc around + the handler just in case. One issue is that we might be on + an alternate stack, which gc won't like. + (setup_alt_stack, teardown_alt_stack): New static functions. + (set_sig_handler): If we have sigaltstack, and are asked + to set up a SEGV handler, then set it up on the alternate + stack. + + * signal.h (extended_jmp_buf): Adding new member, gc. + (extended_setjmp, extended_longjmp): use gc member + to save and restore the gc_enable state across + setjmp and longjmp. + +2014-03-13 Kaz Kylheku <kaz@kylheku.com> + * stream.c (open_process): If execvp fails, use errno as the exit status. (sh, run): New static functions. @@ -1611,6 +1611,28 @@ else printf "no\n" fi +printf "Checking for sigaltstack ... " + +cat > conftest.c <<! +#include <signal.h> +#include <stdlib.h> + +int main(void) +{ + stack_t ss; + ss.ss_sp = malloc(SIGSTKSZ); + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + return sigaltstack(&ss, 0); +} +! +if conftest ; then + printf "yes\n" + printf "#define HAVE_SIGALTSTACK 1\n" >> config.h +else + printf "no\n" +fi + printf "Checking for makedev ... " cat > conftest.c <<! @@ -43,6 +43,8 @@ val gc_mutated(val); void unmark(void); void gc_hint_func(val *); +extern int gc_enabled; + #if EXTRA_DEBUGGING val break_obj; #endif @@ -57,9 +57,31 @@ val sig_ttou, sig_urg, sig_xcpu, sig_xfsz, sigtalrm, sig_prof; val sig_poll, sig_sys, sig_winch, sig_iot, sig_stkflt; val sig_io, sig_lost, sig_pwr; +static int is_cpu_exception(int sig) +{ + switch (sig) { + case SIGFPE: case SIGILL: + case SIGSEGV: case SIGBUS: + case SIGTRAP: + return 1; + default: + return 0; + } +} + static void sig_handler(int sig) { val lambda = sig_lambda[sig]; + int gc = 0; + int as = 0; + int exc = is_cpu_exception(sig); + + if (exc) { + gc = gc_state(0); + as = async_sig_enabled; + async_sig_enabled = 1; + } + if (lambda) { if (async_sig_enabled) { async_sig_enabled = 0; @@ -70,6 +92,11 @@ static void sig_handler(int sig) sig_deferred |= (1UL << sig); } } + + if (exc) { + async_sig_enabled = as; + gc_state(gc); + } } void sig_init(void) @@ -135,9 +162,49 @@ void sig_init(void) reg_fun(intern(lit("set-sig-handler"), user_package), func_n2(set_sig_handler)); reg_fun(intern(lit("get-sig-handler"), user_package), func_n1(get_sig_handler)); reg_fun(intern(lit("sig-check"), user_package), func_n0(sig_check)); +} + +#if HAVE_SIGALTSTACK + +static void *stack; + +static void setup_alt_stack(void) +{ + stack_t ss; + + if (!stack) + stack = chk_malloc(SIGSTKSZ); + ss.ss_sp = stack; + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + + if (sigaltstack(&ss, NULL) == -1) { + free(stack); + stack = 0; + } +} + +static void teardown_alt_stack(void) +{ + stack_t ss; + + if (!stack) + return; + + ss.ss_sp = stack; + ss.ss_size = SIGSTKSZ; + ss.ss_flags = SS_DISABLE; + + if (sigaltstack(&ss, NULL) == -1) + return; + + free(stack); + stack = 0; } +#endif + val set_sig_handler(val signo, val lambda) { static struct sigaction blank; @@ -170,9 +237,19 @@ val set_sig_handler(val signo, val lambda) sa.sa_flags = SA_RESTART; sa.sa_handler = sig_handler; sigfillset(&sa.sa_mask); +#if HAVE_SIGALTSTACK + if (sig == SIGSEGV) + setup_alt_stack(); + sa.sa_flags |= SA_ONSTACK; +#endif sigaction(sig, &sa, 0); } +#if HAVE_SIGALTSTACK + if (sig == SIGSEGV && (lambda == nil || lambda == t)) + teardown_alt_stack(); +#endif + sig_lambda[sig] = lambda; } @@ -62,6 +62,7 @@ typedef struct { sig_atomic_t se; sigset_t blocked; val de; + int gc; int rv; } extended_jmp_buf; @@ -69,10 +70,12 @@ typedef struct { (setjmp((EJB).jb) \ ? (async_sig_enabled = (EJB).se, \ dyn_env = (EJB).de, \ + gc_enabled = ((EJB).gc), \ sig_mask(SIG_SETMASK, &(EJB).blocked, 0), \ (EJB).rv) \ : ((EJB).se = async_sig_enabled, \ (EJB).de = dyn_env, \ + (EJB).gc = gc_enabled, \ (EJB).blocked = sig_blocked_cache, 0)) #define extended_longjmp(EJB, ARG) \ @@ -92,14 +95,18 @@ extern volatile sig_atomic_t async_sig_enabled; typedef struct { jmp_buf jb; val de; + int gc; int rv; } extended_jmp_buf; #define extended_setjmp(EJB) \ (setjmp((EJB).jb) \ ? (dyn_env = (EJB).de, \ + gc_enabled = ((EJB).gc), \ (EJB).rv) \ - : ((EJB).de = dyn_env, 0)) + : ((EJB).de = dyn_env, \ + (EJB).gc = gc_enabled, \ + 0)) #define extended_longjmp(EJB, ARG) \ ((EJB).rv = (ARG), longjmp((EJB).jb, 1)) |