vm: more defense against multi-faulting

* Clear faulting_p from a safepoint rather than inside general_error, because jumping into unwind-native-frames could blow up.
* Handle multiple faults from fatal_error by breakpointing. Is there anything else we can safely do at that point?
* Verify memory protection faults in the top half of the signal handlers because signal dispatch could fault. Treat memory faults during gc or fep as fatal errors.
* Add a function factor_vm::abort() that restores the default SIGABRT handler and ::abort()s. Use it from fatal_error() so we get useful context from gdb and so the user gets feedback from the system crash reporter that Factor blew up and didn't just disappear.
* In factorbug(), don't proceed with .s .r .c if it would be unsafe to do so.
* Don't pile on signals if we've already called fatal_error().
db4
Joe Groff 2011-11-11 13:29:46 -08:00
parent 6cca0ea468
commit e24400679f
12 changed files with 134 additions and 26 deletions

View File

@ -140,12 +140,17 @@ IN: bootstrap.x86
[ jit-jump-quot ] [ jit-jump-quot ]
\ (call) define-combinator-primitive \ (call) define-combinator-primitive
: (jit-safepoint) ( -- )
0 EAX MOVABS rc-absolute rel-safepoint ;
[ [
! Load ds and rs registers ! Load ds and rs registers
jit-load-vm jit-load-vm
jit-load-context jit-load-context
jit-restore-context jit-restore-context
! Safepoint to clear the faulting flag in the VM
(jit-safepoint)
! Windows-specific setup ! Windows-specific setup
ctx-reg jit-update-seh ctx-reg jit-update-seh
@ -395,9 +400,7 @@ IN: bootstrap.x86
EAX EDX [] MOV EAX EDX [] MOV
jit-jump-quot ; jit-jump-quot ;
[ [ (jit-safepoint) ] \ jit-safepoint jit-define
0 EAX MOVABS rc-absolute rel-safepoint
] \ jit-safepoint jit-define
[ [
jit-start-context-and-delete jit-start-context-and-delete

View File

@ -122,6 +122,9 @@ IN: bootstrap.x86
[ jit-jump-quot ] [ jit-jump-quot ]
\ (call) define-combinator-primitive \ (call) define-combinator-primitive
: (jit-safepoint)
0 [RIP+] EAX MOV rc-relative rel-safepoint ;
[ [
! Unwind stack frames ! Unwind stack frames
RSP arg2 MOV RSP arg2 MOV
@ -134,6 +137,9 @@ IN: bootstrap.x86
jit-load-context jit-load-context
jit-restore-context jit-restore-context
! Safepoint to clear the faulting flag in the VM
(jit-safepoint)
! Call quotation ! Call quotation
jit-jump-quot jit-jump-quot
] \ unwind-native-frames define-sub-primitive ] \ unwind-native-frames define-sub-primitive
@ -336,9 +342,7 @@ IN: bootstrap.x86
jit-push-param jit-push-param
jit-jump-quot ; jit-jump-quot ;
[ [ (jit-safepoint) ] \ jit-safepoint jit-define
0 [RIP+] EAX MOV rc-relative rel-safepoint
] \ jit-safepoint jit-define
[ [
jit-start-context-and-delete jit-start-context-and-delete

View File

@ -2,16 +2,16 @@ USING: arrays byte-arrays kernel kernel.private math memory
namespaces sequences tools.test math.private quotations namespaces sequences tools.test math.private quotations
continuations prettyprint io.streams.string debugger assocs continuations prettyprint io.streams.string debugger assocs
sequences.private accessors locals.backend grouping words sequences.private accessors locals.backend grouping words
system ; system alien alien.accessors ;
IN: kernel.tests IN: kernel.tests
[ 0 ] [ f size ] unit-test [ 0 ] [ f size ] unit-test
[ t ] [ [ \ = \ = ] all-equal? ] unit-test [ t ] [ [ \ = \ = ] all-equal? ] unit-test
! Don't leak extra roots if error is thrown ! Don't leak extra roots if error is thrown
[ ] [ 10000 [ [ 3 throw ] ignore-errors ] times ] unit-test [ ] [ 1000 [ [ 3 throw ] ignore-errors ] times ] unit-test
[ ] [ 10000 [ [ -1 f <array> ] ignore-errors ] times ] unit-test [ ] [ 1000 [ [ -1 f <array> ] ignore-errors ] times ] unit-test
! Make sure we report the correct error on stack underflow ! Make sure we report the correct error on stack underflow
[ clear drop ] [ { "kernel-error" 10 f f } = ] must-fail-with [ clear drop ] [ { "kernel-error" 10 f f } = ] must-fail-with
@ -183,3 +183,7 @@ os windows? [
[ t ] [ { } identity-hashcode fixnum? ] unit-test [ t ] [ { } identity-hashcode fixnum? ] unit-test
[ 123 ] [ 123 identity-hashcode ] unit-test [ 123 ] [ 123 identity-hashcode ] unit-test
[ t ] [ f identity-hashcode fixnum? ] unit-test [ t ] [ f identity-hashcode fixnum? ] unit-test
! Make sure memory protection faults work
[ f 0 alien-unsigned-1 ] [ vm-error? ] must-fail-with
[ 1 <alien> 0 alien-unsigned-1 ] [ vm-error? ] must-fail-with

View File

@ -201,13 +201,19 @@ void factor_vm::print_objects(cell *start, cell *end)
void factor_vm::print_datastack() void factor_vm::print_datastack()
{ {
std::cout << "==== DATA STACK:" << std::endl; std::cout << "==== DATA STACK:" << std::endl;
if (ctx)
print_objects((cell *)ctx->datastack_seg->start,(cell *)ctx->datastack); print_objects((cell *)ctx->datastack_seg->start,(cell *)ctx->datastack);
else
std::cout << "*** Context not initialized" << std::endl;
} }
void factor_vm::print_retainstack() void factor_vm::print_retainstack()
{ {
std::cout << "==== RETAIN STACK:" << std::endl; std::cout << "==== RETAIN STACK:" << std::endl;
if (ctx)
print_objects((cell *)ctx->retainstack_seg->start,(cell *)ctx->retainstack); print_objects((cell *)ctx->retainstack_seg->start,(cell *)ctx->retainstack);
else
std::cout << "*** Context not initialized" << std::endl;
} }
struct stack_frame_printer { struct stack_frame_printer {
@ -238,9 +244,14 @@ struct stack_frame_printer {
void factor_vm::print_callstack() void factor_vm::print_callstack()
{ {
std::cout << "==== CALL STACK:" << std::endl; std::cout << "==== CALL STACK:" << std::endl;
if (ctx)
{
stack_frame_printer printer(this); stack_frame_printer printer(this);
iterate_callstack(ctx,printer); iterate_callstack(ctx,printer);
} }
else
std::cout << "*** Context not initialized" << std::endl;
}
struct padded_address { struct padded_address {
cell value; cell value;
@ -450,6 +461,8 @@ void factor_vm::factorbug_usage(bool advanced_p)
std::cout << " push <addr> -- push object on data stack - NOT SAFE" << std::endl; std::cout << " push <addr> -- push object on data stack - NOT SAFE" << std::endl;
std::cout << " gc -- trigger full GC - NOT SAFE" << std::endl; std::cout << " gc -- trigger full GC - NOT SAFE" << std::endl;
std::cout << " code -- code heap dump" << std::endl; std::cout << " code -- code heap dump" << std::endl;
std::cout << " abort -- call abort()" << std::endl;
std::cout << " breakpoint -- trigger system breakpoint" << std::endl;
} }
else else
{ {
@ -599,6 +612,10 @@ void factor_vm::factorbug()
primitive_full_gc(); primitive_full_gc();
else if(strcmp(cmd,"help") == 0) else if(strcmp(cmd,"help") == 0)
factorbug_usage(true); factorbug_usage(true);
else if(strcmp(cmd,"abort") == 0)
abort();
else if(strcmp(cmd,"breakpoint") == 0)
breakpoint();
else else
std::cout << "unknown command" << std::endl; std::cout << "unknown command" << std::endl;
} }

View File

@ -3,12 +3,26 @@
namespace factor namespace factor
{ {
bool factor_vm::fatal_erroring_p;
static inline void fa_diddly_atal_error()
{
printf("fatal_error in fatal_error!\n");
breakpoint();
exit(86);
}
void fatal_error(const char *msg, cell tagged) void fatal_error(const char *msg, cell tagged)
{ {
if (factor_vm::fatal_erroring_p)
fa_diddly_atal_error();
factor_vm::fatal_erroring_p = true;
std::cout << "fatal_error: " << msg; std::cout << "fatal_error: " << msg;
std::cout << ": " << std::hex << tagged << std::dec; std::cout << ": " << (void*)tagged;
std::cout << std::endl; std::cout << std::endl;
exit(1); abort();
} }
void critical_error(const char *msg, cell tagged) void critical_error(const char *msg, cell tagged)
@ -59,7 +73,9 @@ void factor_vm::general_error(vm_error_type error, cell arg1, cell arg2)
ctx->push(error_object); ctx->push(error_object);
faulting_p = false; /* Guard the safepoint, which will clear faulting_p if unwind-native-frames
succeeds */
code->guard_safepoint();
unwind_native_frames(special_objects[ERROR_HANDLER_QUOT], unwind_native_frames(special_objects[ERROR_HANDLER_QUOT],
ctx->callstack_top); ctx->callstack_top);
} }
@ -71,8 +87,8 @@ void factor_vm::general_error(vm_error_type error, cell arg1, cell arg2)
std::cout << "error: " << error << std::endl; std::cout << "error: " << error << std::endl;
std::cout << "arg 1: "; print_obj(arg1); std::cout << std::endl; std::cout << "arg 1: "; print_obj(arg1); std::cout << std::endl;
std::cout << "arg 2: "; print_obj(arg2); std::cout << std::endl; std::cout << "arg 2: "; print_obj(arg2); std::cout << std::endl;
faulting_p = false;
factorbug(); factorbug();
abort();
} }
} }
@ -86,12 +102,24 @@ void factor_vm::not_implemented_error()
general_error(ERROR_NOT_IMPLEMENTED,false_object,false_object); general_error(ERROR_NOT_IMPLEMENTED,false_object,false_object);
} }
void factor_vm::verify_memory_protection_error(cell addr)
{
/* Called from the OS-specific top halves of the signal handlers to
make sure it's safe to dispatch to memory_protection_error */
if(fatal_erroring_p)
fa_diddly_atal_error();
if(faulting_p && !code->safepoint_p(addr))
fatal_error("Double fault", addr);
else if(fep_p)
fatal_error("Memory protection fault during low-level debugger", addr);
else if(atomic::load(&current_gc_p))
fatal_error("Memory protection fault during gc", addr);
}
void factor_vm::memory_protection_error(cell addr) void factor_vm::memory_protection_error(cell addr)
{ {
if(code->safepoint_p(addr)) if(code->safepoint_p(addr))
safepoint.handle_safepoint(this); safepoint.handle_safepoint(this);
else if(faulting_p)
fatal_error("Double fault", 0);
else if(ctx->datastack_seg->underflow_p(addr)) else if(ctx->datastack_seg->underflow_p(addr))
general_error(ERROR_DATASTACK_UNDERFLOW,false_object,false_object); general_error(ERROR_DATASTACK_UNDERFLOW,false_object,false_object);
else if(ctx->datastack_seg->overflow_p(addr)) else if(ctx->datastack_seg->overflow_p(addr))

View File

@ -40,6 +40,7 @@ void factor_vm::call_fault_handler(
if(exception == EXC_BAD_ACCESS) if(exception == EXC_BAD_ACCESS)
{ {
signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state); signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
verify_memory_protection_error(signal_fault_addr);
handler = (cell)factor::memory_signal_handler_impl; handler = (cell)factor::memory_signal_handler_impl;
} }
else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV) else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)

View File

@ -165,18 +165,23 @@ void factor_vm::end_sampling_profiler_timer()
void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap) void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
{ {
factor_vm *vm = current_vm(); factor_vm *vm = current_vm();
vm->verify_memory_protection_error((cell)siginfo->si_addr);
vm->signal_fault_addr = (cell)siginfo->si_addr; vm->signal_fault_addr = (cell)siginfo->si_addr;
vm->dispatch_signal(uap,factor::memory_signal_handler_impl); vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
} }
void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap) void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
{ {
if (factor_vm::fatal_erroring_p)
return;
factor_vm *vm = current_vm_p(); factor_vm *vm = current_vm_p();
if (vm) if (vm)
{ {
vm->signal_number = signal; vm->signal_number = signal;
vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl); vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
} else }
else
fatal_error("Foreign thread received signal", signal); fatal_error("Foreign thread received signal", signal);
} }
@ -190,6 +195,9 @@ static void enqueue_signal(factor_vm *vm, int signal)
void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap) void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
{ {
if (factor_vm::fatal_erroring_p)
return;
factor_vm *vm = current_vm_p(); factor_vm *vm = current_vm_p();
if (vm) if (vm)
enqueue_signal(vm, signal); enqueue_signal(vm, signal);
@ -199,6 +207,9 @@ void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap) void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
{ {
if (factor_vm::fatal_erroring_p)
return;
factor_vm *vm = current_vm_p(); factor_vm *vm = current_vm_p();
if (vm) if (vm)
{ {
@ -251,7 +262,7 @@ static void sigaction_safe(int signum, const struct sigaction *act, struct sigac
while(ret == -1 && errno == EINTR); while(ret == -1 && errno == EINTR);
if(ret == -1) if(ret == -1)
fatal_error("sigaction failed", 0); fatal_error("sigaction failed", errno);
} }
static void init_sigaction_with_handler(struct sigaction *act, static void init_sigaction_with_handler(struct sigaction *act,
@ -499,4 +510,16 @@ void factor_vm::unlock_console()
pthread_mutex_unlock(&stdin_mutex); pthread_mutex_unlock(&stdin_mutex);
} }
void factor_vm::abort()
{
sig_t ret;
do
{
ret = signal(SIGABRT, SIG_DFL);
}
while(ret == SIG_ERR && errno == EINTR);
::abort();
}
} }

View File

@ -45,4 +45,9 @@ void sleep_nanos(u64 nsec);
void move_file(const vm_char *path1, const vm_char *path2); void move_file(const vm_char *path1, const vm_char *path2);
static inline void breakpoint()
{
__builtin_trap();
}
} }

View File

@ -214,12 +214,21 @@ void sleep_nanos(u64 nsec)
Sleep((DWORD)(nsec/1000000)); Sleep((DWORD)(nsec/1000000));
} }
typedef enum _EXCEPTION_DISPOSITION
{
ExceptionContinueExecution = 0,
ExceptionContinueSearch = 1,
ExceptionNestedException = 2,
ExceptionCollidedUnwind = 3
} EXCEPTION_DISPOSITION;
LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch) LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch)
{ {
switch (e->ExceptionCode) switch (e->ExceptionCode)
{ {
case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ACCESS_VIOLATION:
signal_fault_addr = e->ExceptionInformation[1]; signal_fault_addr = e->ExceptionInformation[1];
verify_memory_protection_error(signal_fault_addr);
dispatch_signal_handler( dispatch_signal_handler(
(cell*)&c->ESP, (cell*)&c->ESP,
(cell*)&c->EIP, (cell*)&c->EIP,
@ -261,19 +270,19 @@ LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c,
break; break;
} }
return 0; return ExceptionContinueExecution;
} }
VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch) VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c, void *dispatch)
{ {
if (factor_vm::fatal_erroring_p)
return ExceptionContinueSearch;
factor_vm *vm = current_vm_p(); factor_vm *vm = current_vm_p();
if (vm) if (vm)
return vm->exception_handler(e,frame,c,dispatch); return vm->exception_handler(e,frame,c,dispatch);
else else
{ return ExceptionContinueSearch;
fatal_error("Foreign thread received exception", e->ExceptionCode);
return 0; // to placate MSVC
}
} }
static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
@ -380,4 +389,9 @@ void factor_vm::end_sampling_profiler_timer()
sampler_thread = NULL; sampler_thread = NULL;
} }
void factor_vm::abort()
{
::abort();
}
} }

View File

@ -86,6 +86,11 @@ inline static THREADHANDLE thread_id()
return threadHandle; return threadHandle;
} }
inline static breakpoint()
{
DebugBreak();
}
#define CODE_TO_FUNCTION_POINTER(code) (void)0 #define CODE_TO_FUNCTION_POINTER(code) (void)0
#define CODE_TO_FUNCTION_POINTER_CALLBACK(vm, code) (void)0 #define CODE_TO_FUNCTION_POINTER_CALLBACK(vm, code) (void)0
#define FUNCTION_CODE_POINTER(ptr) ptr #define FUNCTION_CODE_POINTER(ptr) ptr

View File

@ -38,6 +38,7 @@ void safepoint_state::enqueue_samples(factor_vm *parent, cell samples, cell pc,
void safepoint_state::handle_safepoint(factor_vm *parent) volatile void safepoint_state::handle_safepoint(factor_vm *parent) volatile
{ {
parent->code->unguard_safepoint(); parent->code->unguard_safepoint();
parent->faulting_p = false;
if (atomic::load(&fep_p)) if (atomic::load(&fep_p))
{ {

View File

@ -143,6 +143,7 @@ struct factor_vm
/* Are we already handling a fault? Used to catch double memory faults */ /* Are we already handling a fault? Used to catch double memory faults */
bool faulting_p; bool faulting_p;
static bool fatal_erroring_p;
/* Safepoint state */ /* Safepoint state */
volatile safepoint_state safepoint; volatile safepoint_state safepoint;
@ -213,6 +214,7 @@ struct factor_vm
void general_error(vm_error_type error, cell arg1, cell arg2); void general_error(vm_error_type error, cell arg1, cell arg2);
void type_error(cell type, cell tagged); void type_error(cell type, cell tagged);
void not_implemented_error(); void not_implemented_error();
void verify_memory_protection_error(cell addr);
void memory_protection_error(cell addr); void memory_protection_error(cell addr);
void signal_error(cell signal); void signal_error(cell signal);
void divide_by_zero_error(); void divide_by_zero_error();
@ -730,6 +732,7 @@ struct factor_vm
void open_console(); void open_console();
void lock_console(); void lock_console();
void unlock_console(); void unlock_console();
static void abort();
// os-windows // os-windows
#if defined(WINDOWS) #if defined(WINDOWS)