vm: fix foreign segfaults and callstack overflows

db4
Joe Groff 2011-10-25 15:42:08 -07:00
parent 402e1155a5
commit f284ac2b48
4 changed files with 75 additions and 37 deletions

View File

@ -18,6 +18,7 @@ callstack *factor_vm::allot_callstack(cell size)
return stack; return stack;
} }
// XXX move somewhere more appropriate
struct word_finder { struct word_finder {
cell address; cell address;
cell found_word; cell found_word;
@ -49,47 +50,72 @@ static cell find_word_for_address(factor_vm *vm, cell pc)
void factor_vm::dispatch_signal_handler(cell *sp, cell *pc, cell handler) void factor_vm::dispatch_signal_handler(cell *sp, cell *pc, cell handler)
{ {
/* True stack frames are always 16-byte aligned. Leaf procedures if (!code->seg->in_segment_p(*pc) || *sp < ctx->callstack_seg->start + stack_reserved)
that don't create a stack frame will be out of alignment by sizeof(cell)
bytes. */
/* XXX horribly x86-centric */
/* XXX check if exception came from C code */
/* XXX handle callstack overflow */
cell offset = *sp % 16;
signal_handler_addr = handler;
tagged<word> handler_word = tagged<word>(special_objects[SIGNAL_HANDLER_WORD]);
if (offset == 0)
{ {
// should use FRAME_RETURN_ADDRESS here to be platform-agnostic /* Fault came from foreign code, a callstack overflow, or we would probably
signal_from_leaf = false; overflow if we tried the resumable handler. We can't resume, so cut the
cell newsp = *sp - sizeof(cell); callstack down to the shallowest Factor stack frame that leaves room for
the signal handler to do its thing and launch the handler without going
through the resumable subprimitive. */
signal_resumable = false;
stack_frame *frame = ctx->callstack_bottom - 1;
while((cell)frame >= *sp
&& frame >= ctx->callstack_top
&& (cell)frame >= ctx->callstack_seg->start + stack_reserved)
{
frame = frame_successor(frame);
}
// XXX FRAME_RETURN_ADDRESS
cell newsp = (cell)(frame+1);
*sp = newsp; *sp = newsp;
*(cell*)newsp = *pc; ctx->callstack_top = (stack_frame*)newsp;
} *pc = handler;
// should check the PC since leaf procs on RISC architectures won't touch the } else {
// stack at all signal_resumable = true;
else if (offset == 16 - sizeof(cell)) // Fault came from Factor, and we've got a good callstack. Route the signal
{ // handler through the resumable signal handler subprimitive.
signal_from_leaf = true; cell offset = *sp % 16;
// Make a fake frame for the leaf procedure signal_handler_addr = handler;
cell leaf_word = find_word_for_address(this, *pc); tagged<word> handler_word = tagged<word>(special_objects[SIGNAL_HANDLER_WORD]);
cell newsp = *sp + 4 * sizeof(cell); // XXX platform-appropriate stack size /* XXX horribly x86-centric */
*(cell*)(newsp + 3*sizeof(cell)) = 4*sizeof(cell); /* True stack frames are always 16-byte aligned. Leaf procedures
*(cell*)(newsp + 2*sizeof(cell)) = leaf_word; that don't create a stack frame will be out of alignment by sizeof(cell)
*(cell*) newsp = *pc; bytes. */
*sp = newsp; /* On architectures with a link register we would have to check for leafness
handler_word = tagged<word>(special_objects[LEAF_SIGNAL_HANDLER_WORD]); by matching the PC to a word. We should also use FRAME_RETURN_ADDRESS instead
} of assuming the stack pointer is the right place to put the resume address. */
else if (offset == 0)
{ {
fatal_error("Invalid stack frame during signal handler", *sp); signal_from_leaf = false; // XXX remove this once we're sure leaf works
} cell newsp = *sp - sizeof(cell);
*sp = newsp;
*(cell*)newsp = *pc;
}
else if (offset == 16 - sizeof(cell))
{
signal_from_leaf = true; // XXX remove this once we're sure leaf works
*pc = (cell)handler_word->code->entry_point(); // Make a fake frame for the leaf procedure
cell leaf_word = find_word_for_address(this, *pc);
cell newsp = *sp + 4 * sizeof(cell);
*(cell*)(newsp + 3*sizeof(cell)) = 4*sizeof(cell);
*(cell*)(newsp + 2*sizeof(cell)) = leaf_word;
*(cell*) newsp = *pc;
*sp = newsp;
handler_word = tagged<word>(special_objects[LEAF_SIGNAL_HANDLER_WORD]);
}
else
{
fatal_error("Invalid stack frame during signal handler", *sp);
}
*pc = (cell)handler_word->code->entry_point();
}
} }
/* We ignore the two topmost frames, the 'callstack' primitive /* We ignore the two topmost frames, the 'callstack' primitive

View File

@ -126,6 +126,12 @@ void factor_vm::primitive_unimplemented()
void factor_vm::memory_signal_handler_impl() void factor_vm::memory_signal_handler_impl()
{ {
memory_protection_error(signal_fault_addr); memory_protection_error(signal_fault_addr);
if (!signal_resumable)
{
/* In theory we should only get here if the callstack overflowed during a
safepoint */
general_error(ERROR_CALLSTACK_OVERFLOW,false_object,false_object);
}
} }
void memory_signal_handler_impl() void memory_signal_handler_impl()

View File

@ -25,6 +25,11 @@ struct segment {
{ {
return (addr >= end && addr < end + getpagesize()); return (addr >= end && addr < end + getpagesize());
} }
bool in_segment_p(cell addr)
{
return (addr >= start && addr < end);
}
}; };
} }

View File

@ -67,7 +67,8 @@ struct factor_vm
/* Global variables used to pass fault handler state from signal handler /* Global variables used to pass fault handler state from signal handler
to VM */ to VM */
bool signal_from_leaf; bool signal_resumable;
bool signal_from_leaf; // XXX remove this once we're sure leaf works
cell signal_number; cell signal_number;
cell signal_fault_addr; cell signal_fault_addr;
unsigned int signal_fpu_status; unsigned int signal_fpu_status;