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
parent
6cca0ea468
commit
e24400679f
|
@ -140,12 +140,17 @@ IN: bootstrap.x86
|
|||
[ jit-jump-quot ]
|
||||
\ (call) define-combinator-primitive
|
||||
|
||||
: (jit-safepoint) ( -- )
|
||||
0 EAX MOVABS rc-absolute rel-safepoint ;
|
||||
[
|
||||
! Load ds and rs registers
|
||||
jit-load-vm
|
||||
jit-load-context
|
||||
jit-restore-context
|
||||
|
||||
! Safepoint to clear the faulting flag in the VM
|
||||
(jit-safepoint)
|
||||
|
||||
! Windows-specific setup
|
||||
ctx-reg jit-update-seh
|
||||
|
||||
|
@ -395,9 +400,7 @@ IN: bootstrap.x86
|
|||
EAX EDX [] MOV
|
||||
jit-jump-quot ;
|
||||
|
||||
[
|
||||
0 EAX MOVABS rc-absolute rel-safepoint
|
||||
] \ jit-safepoint jit-define
|
||||
[ (jit-safepoint) ] \ jit-safepoint jit-define
|
||||
|
||||
[
|
||||
jit-start-context-and-delete
|
||||
|
|
|
@ -122,6 +122,9 @@ IN: bootstrap.x86
|
|||
[ jit-jump-quot ]
|
||||
\ (call) define-combinator-primitive
|
||||
|
||||
: (jit-safepoint)
|
||||
0 [RIP+] EAX MOV rc-relative rel-safepoint ;
|
||||
|
||||
[
|
||||
! Unwind stack frames
|
||||
RSP arg2 MOV
|
||||
|
@ -134,6 +137,9 @@ IN: bootstrap.x86
|
|||
jit-load-context
|
||||
jit-restore-context
|
||||
|
||||
! Safepoint to clear the faulting flag in the VM
|
||||
(jit-safepoint)
|
||||
|
||||
! Call quotation
|
||||
jit-jump-quot
|
||||
] \ unwind-native-frames define-sub-primitive
|
||||
|
@ -336,9 +342,7 @@ IN: bootstrap.x86
|
|||
jit-push-param
|
||||
jit-jump-quot ;
|
||||
|
||||
[
|
||||
0 [RIP+] EAX MOV rc-relative rel-safepoint
|
||||
] \ jit-safepoint jit-define
|
||||
[ (jit-safepoint) ] \ jit-safepoint jit-define
|
||||
|
||||
[
|
||||
jit-start-context-and-delete
|
||||
|
|
|
@ -2,16 +2,16 @@ USING: arrays byte-arrays kernel kernel.private math memory
|
|||
namespaces sequences tools.test math.private quotations
|
||||
continuations prettyprint io.streams.string debugger assocs
|
||||
sequences.private accessors locals.backend grouping words
|
||||
system ;
|
||||
system alien alien.accessors ;
|
||||
IN: kernel.tests
|
||||
|
||||
[ 0 ] [ f size ] unit-test
|
||||
[ t ] [ [ \ = \ = ] all-equal? ] unit-test
|
||||
|
||||
! 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
|
||||
[ clear drop ] [ { "kernel-error" 10 f f } = ] must-fail-with
|
||||
|
@ -183,3 +183,7 @@ os windows? [
|
|||
[ t ] [ { } identity-hashcode fixnum? ] unit-test
|
||||
[ 123 ] [ 123 identity-hashcode ] 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
|
||||
|
|
25
vm/debug.cpp
25
vm/debug.cpp
|
@ -201,13 +201,19 @@ void factor_vm::print_objects(cell *start, cell *end)
|
|||
void factor_vm::print_datastack()
|
||||
{
|
||||
std::cout << "==== DATA STACK:" << std::endl;
|
||||
print_objects((cell *)ctx->datastack_seg->start,(cell *)ctx->datastack);
|
||||
if (ctx)
|
||||
print_objects((cell *)ctx->datastack_seg->start,(cell *)ctx->datastack);
|
||||
else
|
||||
std::cout << "*** Context not initialized" << std::endl;
|
||||
}
|
||||
|
||||
void factor_vm::print_retainstack()
|
||||
{
|
||||
std::cout << "==== RETAIN STACK:" << std::endl;
|
||||
print_objects((cell *)ctx->retainstack_seg->start,(cell *)ctx->retainstack);
|
||||
if (ctx)
|
||||
print_objects((cell *)ctx->retainstack_seg->start,(cell *)ctx->retainstack);
|
||||
else
|
||||
std::cout << "*** Context not initialized" << std::endl;
|
||||
}
|
||||
|
||||
struct stack_frame_printer {
|
||||
|
@ -238,8 +244,13 @@ struct stack_frame_printer {
|
|||
void factor_vm::print_callstack()
|
||||
{
|
||||
std::cout << "==== CALL STACK:" << std::endl;
|
||||
stack_frame_printer printer(this);
|
||||
iterate_callstack(ctx,printer);
|
||||
if (ctx)
|
||||
{
|
||||
stack_frame_printer printer(this);
|
||||
iterate_callstack(ctx,printer);
|
||||
}
|
||||
else
|
||||
std::cout << "*** Context not initialized" << std::endl;
|
||||
}
|
||||
|
||||
struct padded_address {
|
||||
|
@ -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 << " gc -- trigger full GC - NOT SAFE" << 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
|
||||
{
|
||||
|
@ -599,6 +612,10 @@ void factor_vm::factorbug()
|
|||
primitive_full_gc();
|
||||
else if(strcmp(cmd,"help") == 0)
|
||||
factorbug_usage(true);
|
||||
else if(strcmp(cmd,"abort") == 0)
|
||||
abort();
|
||||
else if(strcmp(cmd,"breakpoint") == 0)
|
||||
breakpoint();
|
||||
else
|
||||
std::cout << "unknown command" << std::endl;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,26 @@
|
|||
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)
|
||||
{
|
||||
if (factor_vm::fatal_erroring_p)
|
||||
fa_diddly_atal_error();
|
||||
|
||||
factor_vm::fatal_erroring_p = true;
|
||||
|
||||
std::cout << "fatal_error: " << msg;
|
||||
std::cout << ": " << std::hex << tagged << std::dec;
|
||||
std::cout << ": " << (void*)tagged;
|
||||
std::cout << std::endl;
|
||||
exit(1);
|
||||
abort();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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],
|
||||
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 << "arg 1: "; print_obj(arg1); std::cout << std::endl;
|
||||
std::cout << "arg 2: "; print_obj(arg2); std::cout << std::endl;
|
||||
faulting_p = false;
|
||||
factorbug();
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,12 +102,24 @@ void factor_vm::not_implemented_error()
|
|||
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(¤t_gc_p))
|
||||
fatal_error("Memory protection fault during gc", addr);
|
||||
}
|
||||
|
||||
void factor_vm::memory_protection_error(cell addr)
|
||||
{
|
||||
if(code->safepoint_p(addr))
|
||||
safepoint.handle_safepoint(this);
|
||||
else if(faulting_p)
|
||||
fatal_error("Double fault", 0);
|
||||
else if(ctx->datastack_seg->underflow_p(addr))
|
||||
general_error(ERROR_DATASTACK_UNDERFLOW,false_object,false_object);
|
||||
else if(ctx->datastack_seg->overflow_p(addr))
|
||||
|
|
|
@ -40,6 +40,7 @@ void factor_vm::call_fault_handler(
|
|||
if(exception == EXC_BAD_ACCESS)
|
||||
{
|
||||
signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
|
||||
verify_memory_protection_error(signal_fault_addr);
|
||||
handler = (cell)factor::memory_signal_handler_impl;
|
||||
}
|
||||
else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)
|
||||
|
|
|
@ -165,18 +165,23 @@ void factor_vm::end_sampling_profiler_timer()
|
|||
void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
|
||||
{
|
||||
factor_vm *vm = current_vm();
|
||||
vm->verify_memory_protection_error((cell)siginfo->si_addr);
|
||||
vm->signal_fault_addr = (cell)siginfo->si_addr;
|
||||
vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
|
||||
}
|
||||
|
||||
void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
|
||||
{
|
||||
if (factor_vm::fatal_erroring_p)
|
||||
return;
|
||||
|
||||
factor_vm *vm = current_vm_p();
|
||||
if (vm)
|
||||
{
|
||||
vm->signal_number = signal;
|
||||
vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
|
||||
} else
|
||||
}
|
||||
else
|
||||
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)
|
||||
{
|
||||
if (factor_vm::fatal_erroring_p)
|
||||
return;
|
||||
|
||||
factor_vm *vm = current_vm_p();
|
||||
if (vm)
|
||||
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)
|
||||
{
|
||||
if (factor_vm::fatal_erroring_p)
|
||||
return;
|
||||
|
||||
factor_vm *vm = current_vm_p();
|
||||
if (vm)
|
||||
{
|
||||
|
@ -251,7 +262,7 @@ static void sigaction_safe(int signum, const struct sigaction *act, struct sigac
|
|||
while(ret == -1 && errno == EINTR);
|
||||
|
||||
if(ret == -1)
|
||||
fatal_error("sigaction failed", 0);
|
||||
fatal_error("sigaction failed", errno);
|
||||
}
|
||||
|
||||
static void init_sigaction_with_handler(struct sigaction *act,
|
||||
|
@ -499,4 +510,16 @@ void factor_vm::unlock_console()
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,4 +45,9 @@ void sleep_nanos(u64 nsec);
|
|||
|
||||
void move_file(const vm_char *path1, const vm_char *path2);
|
||||
|
||||
static inline void breakpoint()
|
||||
{
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -214,12 +214,21 @@ void sleep_nanos(u64 nsec)
|
|||
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)
|
||||
{
|
||||
switch (e->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
signal_fault_addr = e->ExceptionInformation[1];
|
||||
verify_memory_protection_error(signal_fault_addr);
|
||||
dispatch_signal_handler(
|
||||
(cell*)&c->ESP,
|
||||
(cell*)&c->EIP,
|
||||
|
@ -261,19 +270,19 @@ LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void *frame, PCONTEXT c,
|
|||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ExceptionContinueExecution;
|
||||
}
|
||||
|
||||
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();
|
||||
if (vm)
|
||||
return vm->exception_handler(e,frame,c,dispatch);
|
||||
else
|
||||
{
|
||||
fatal_error("Foreign thread received exception", e->ExceptionCode);
|
||||
return 0; // to placate MSVC
|
||||
}
|
||||
return ExceptionContinueSearch;
|
||||
}
|
||||
|
||||
static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
|
||||
|
@ -380,4 +389,9 @@ void factor_vm::end_sampling_profiler_timer()
|
|||
sampler_thread = NULL;
|
||||
}
|
||||
|
||||
void factor_vm::abort()
|
||||
{
|
||||
::abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -86,6 +86,11 @@ inline static THREADHANDLE thread_id()
|
|||
return threadHandle;
|
||||
}
|
||||
|
||||
inline static breakpoint()
|
||||
{
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
#define CODE_TO_FUNCTION_POINTER(code) (void)0
|
||||
#define CODE_TO_FUNCTION_POINTER_CALLBACK(vm, code) (void)0
|
||||
#define FUNCTION_CODE_POINTER(ptr) ptr
|
||||
|
|
|
@ -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
|
||||
{
|
||||
parent->code->unguard_safepoint();
|
||||
parent->faulting_p = false;
|
||||
|
||||
if (atomic::load(&fep_p))
|
||||
{
|
||||
|
|
|
@ -143,6 +143,7 @@ struct factor_vm
|
|||
|
||||
/* Are we already handling a fault? Used to catch double memory faults */
|
||||
bool faulting_p;
|
||||
static bool fatal_erroring_p;
|
||||
|
||||
/* Safepoint state */
|
||||
volatile safepoint_state safepoint;
|
||||
|
@ -213,6 +214,7 @@ struct factor_vm
|
|||
void general_error(vm_error_type error, cell arg1, cell arg2);
|
||||
void type_error(cell type, cell tagged);
|
||||
void not_implemented_error();
|
||||
void verify_memory_protection_error(cell addr);
|
||||
void memory_protection_error(cell addr);
|
||||
void signal_error(cell signal);
|
||||
void divide_by_zero_error();
|
||||
|
@ -730,6 +732,7 @@ struct factor_vm
|
|||
void open_console();
|
||||
void lock_console();
|
||||
void unlock_console();
|
||||
static void abort();
|
||||
|
||||
// os-windows
|
||||
#if defined(WINDOWS)
|
||||
|
|
Loading…
Reference in New Issue