Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions runtime/include/software_interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ software_interrupt_deferred_sigalrm_replay()
* Exports from software_interrupt.c *
************************/

/* Size of each worker's alternate signal stack. The SA_ONSTACK handlers (SIGSEGV, SIGFPE) only siglongjmp out,
* so this needs little space; keep it comfortably above MINSIGSTKSZ. */
#define SOFTWARE_INTERRUPT_ALT_STACK_SIZE (64 * 1024)

void software_interrupt_alt_stack_initialize(void);
void software_interrupt_arm_timer(void);
void software_interrupt_cleanup(void);
void software_interrupt_disarm_timer(void);
Expand Down
35 changes: 35 additions & 0 deletions runtime/src/software_interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,19 @@ software_interrupt_initialize(void)
/* Mask this signal on the listener thread */
software_interrupt_mask_signal(signal);

/* Deliver the synchronous fault signals (SIGSEGV, SIGFPE) on the per-worker alternate signal
* stack registered by software_interrupt_alt_stack_initialize(). A sandbox stack overflow faults
* on the stack's guard page, leaving no room to deliver the signal on the overflowed stack; running
* the handler on the alternate stack keeps that overflow recoverable (the handler siglongjmps back
* to the sandbox entry on a valid part of the stack). The asynchronous preemption signals (SIGALRM,
* SIGUSR1) stay on the normal stack: they context-switch away rather than returning, which does not
* compose with an alternate stack. */
if (signal == SIGSEGV || signal == SIGFPE) {
signal_action.sa_flags |= SA_ONSTACK;
} else {
signal_action.sa_flags &= ~SA_ONSTACK;
}

/* But register the handler for this signal for the process */
if (sigaction(signal, &signal_action, NULL)) {
perror("sigaction");
Expand All @@ -288,6 +301,28 @@ software_interrupt_initialize(void)
software_interrupt_counts_alloc();
}

/**
* Registers a per-thread alternate signal stack so that handlers flagged SA_ONSTACK (SIGSEGV, SIGFPE) can run
* even when the active sandbox stack is exhausted. Must be called by each worker before it unmasks those signals.
*/
void
software_interrupt_alt_stack_initialize(void)
{
stack_t alt_stack = { 0 };
alt_stack.ss_flags = 0;
alt_stack.ss_size = SOFTWARE_INTERRUPT_ALT_STACK_SIZE;
alt_stack.ss_sp = malloc(alt_stack.ss_size);
if (alt_stack.ss_sp == NULL) {
perror("malloc alternate signal stack");
exit(1);
}

if (sigaltstack(&alt_stack, NULL) != 0) {
perror("sigaltstack");
exit(1);
}
}

void
software_interrupt_cleanup()
{
Expand Down
4 changes: 4 additions & 0 deletions runtime/src/worker_thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ worker_thread_main(void *argument)
tenant_timeout_get_priority);
}

/* Register this worker's alternate signal stack before unmasking the fault signals, so a sandbox stack
* overflow (which faults on the stack guard page) can still be delivered and recovered from. */
software_interrupt_alt_stack_initialize();

software_interrupt_unmask_signal(SIGFPE);
software_interrupt_unmask_signal(SIGSEGV);

Expand Down
Loading