diff --git a/library/tools/debugger.factor b/library/tools/debugger.factor index 064a148603..e47487f8e3 100644 --- a/library/tools/debugger.factor +++ b/library/tools/debugger.factor @@ -48,6 +48,18 @@ SYMBOL: error-continuation : user-interrupt. ( obj -- ) "User interrupt" print drop ; +: stack-underflow. ( obj -- ) + "Stack underflow" print drop ; + +: stack-overflow. ( obj -- ) + "Stack overflow" print drop ; + +: return-stack-underflow. ( obj -- ) + "Return stack underflow" print drop ; + +: return-stack-overflow. ( obj -- ) + "Return stack overflow" print drop ; + PREDICATE: cons kernel-error ( obj -- ? ) dup first kernel-error = swap second 0 11 between? and ; @@ -66,6 +78,10 @@ M: kernel-error error. ( error -- ) [ heap-scan-error. ] [ undefined-symbol-error. ] [ user-interrupt. ] + [ stack-underflow. ] + [ stack-overflow. ] + [ return-stack-underflow. ] + [ return-stack-overflow . ] } dispatch ; M: no-method summary drop "No suitable method" ; diff --git a/native/error.c b/native/error.c index 9c7581f658..eedb6d18e1 100644 --- a/native/error.c +++ b/native/error.c @@ -70,6 +70,18 @@ void signal_error(int signal) cons(tag_fixnum(signal),F))),false); } +/* called from signal.c when a sigv tells us that we under/overflowed a page. + * The first bool is true if it was the return stack (otherwise it's the data + * stack) and the second bool is true if we overflowed it (otherwise we + * underflowed it) */ +void signal_stack_error(bool is_return_stack, bool is_overflow) +{ + CELL errors[] = { ERROR_STACK_UNDERFLOW, ERROR_STACK_OVERFLOW, + ERROR_RETSTACK_UNDERFLOW, ERROR_RETSTACK_OVERFLOW }; + const CELL error = errors[is_return_stack * 2 + is_overflow]; + throw_error(cons(userenv[ERROR_ENV], cons(error, F)), false); +} + void type_error(CELL type, CELL tagged) { CELL c = cons(tag_fixnum(type),cons(tagged,F)); diff --git a/native/error.h b/native/error.h index f2cde5dad3..d01131760f 100644 --- a/native/error.h +++ b/native/error.h @@ -10,6 +10,10 @@ #define ERROR_HEAP_SCAN (9<<3) #define ERROR_UNDEFINED_SYMBOL (10<<3) #define ERROR_USER_INTERRUPT (11<<3) +#define ERROR_STACK_UNDERFLOW (12<<3) +#define ERROR_STACK_OVERFLOW (13<<3) +#define ERROR_RETSTACK_UNDERFLOW (14<<3) +#define ERROR_RETSTACK_OVERFLOW (15<<3) /* Are we throwing an error? */ bool throwing; diff --git a/native/unix/signal.c b/native/unix/signal.c index 12ab7cbaf3..6984411bf7 100644 --- a/native/unix/signal.c +++ b/native/unix/signal.c @@ -1,6 +1,20 @@ #include "../factor.h" #include "mach_signal.h" +// this function tests if a given faulting location is in a poison page. The +// page address is taken from area + round_up_to_page_size(area_size) + +// pagesize*offset +static bool in_page(void *fault, void *i_area, CELL area_size, int offset) { + const int pagesize = getpagesize(); + intptr_t area = (intptr_t) i_area; + area += pagesize * ((area_size + (pagesize - 1)) / pagesize); + area += offset * pagesize; + + const int page = area / pagesize; + const int fault_page = (intptr_t)fault / pagesize; + return page == fault_page; +} + void signal_handler(int signal, siginfo_t* siginfo, void* uap) { if(nursery.here > nursery.limit) @@ -13,6 +27,27 @@ void signal_handler(int signal, siginfo_t* siginfo, void* uap) fprintf(stderr,"Code space exhausted\n"); factorbug(); } + /* we wish to catch the case where we underflow/overflow the data or + // return stacks. These stacks have poison pages above and below the + // memory so we just need to test if the faulting addresss is in one of + // these pages */ + + /* first, underflowing the data stack */ + else if(in_page(siginfo->si_addr, (void *) ds_bot, 0, -1)) + { + signal_stack_error(false, false); + } + else if(in_page(siginfo->si_addr, (void *) ds_bot, ds_size, 0)) { + signal_stack_error(false, true); + } + else if(in_page(siginfo->si_addr, (void *) cs_bot, 0, -1)) + { + signal_stack_error(true, false); + } + else if(in_page(siginfo->si_addr, (void *) cs_bot, cs_size, 0)) { + signal_stack_error(true, true); + } + else signal_error(signal); }