/* Step 5: who owns SIGSEGV. * * The OCaml runtime installs a SIGSEGV handler to turn a stack-guard-page hit * into the Stack_overflow exception. The break loop wants SIGSEGV for the * crash case. This is the one real collision, so it is measured in both * directions: * * a. what the disposition is before caml_startup, and after it; * b. whether a handler installed AFTER caml_startup actually receives a * genuine fault in program memory -- i.e. whether the break loop can have * what it wants by installing last. * * SIGPIPE is not probed: flan_agent.c sends with MSG_NOSIGNAL throughout and * does not rely on a disposition. */ #include #include #include #include #include #include static void describe(const char *when, int sig) { struct sigaction old; memset(&old, 0, sizeof old); sigaction(sig, NULL, &old); printf(" %-22s %-8s handler=%p flags=%#x %s%s\n", when, sig == SIGSEGV ? "SIGSEGV" : sig == SIGINT ? "SIGINT" : "SIGFPE", (old.sa_flags & SA_SIGINFO) ? (void *)old.sa_sigaction : (void *)old.sa_handler, (unsigned)old.sa_flags, (old.sa_flags & SA_ONSTACK) ? "ONSTACK " : "", old.sa_handler == SIG_DFL ? "(SIG_DFL)" : old.sa_handler == SIG_IGN ? "(SIG_IGN)" : "(custom)"); } static sigjmp_buf escape; static volatile sig_atomic_t ours_ran = 0; static void our_segv(int sig, siginfo_t *info, void *ctx) { (void)sig; (void)ctx; ours_ran = 1; /* What a break loop would do here is stop and serve; the spike just proves the handler was reached, with the faulting address in hand. */ printf(" our SIGSEGV handler ran, fault address = %p\n", info->si_addr); siglongjmp(escape, 1); } int main(int argc, char **argv) { struct sigaction sa, ocaml_segv; volatile int *bad = (int *)0x10; (void)argc; printf("before caml_startup:\n"); describe("before startup", SIGSEGV); describe("before startup", SIGINT); describe("before startup", SIGFPE); caml_startup(argv); printf("after caml_startup:\n"); describe("after startup", SIGSEGV); describe("after startup", SIGINT); describe("after startup", SIGFPE); memset(&ocaml_segv, 0, sizeof ocaml_segv); sigaction(SIGSEGV, NULL, &ocaml_segv); /* Now install ours last, the way the break loop would. */ memset(&sa, 0, sizeof sa); sa.sa_sigaction = our_segv; sa.sa_flags = SA_SIGINFO | SA_ONSTACK; sigemptyset(&sa.sa_mask); sigaction(SIGSEGV, &sa, NULL); printf("break loop installs last:\n"); describe("after break loop", SIGSEGV); if (sigsetjmp(escape, 1) == 0) { printf(" dereferencing %p ...\n", (void *)bad); *bad = 1; printf(" no fault -- UNEXPECTED\n"); } else { printf(" recovered; a handler installed after caml_startup does receive " "a real fault: %s\n", ours_ran ? "yes" : "no"); } /* And the cost of taking it: OCaml's own handler is now displaced, so its stack-overflow detection is gone unless ours chains to the saved one. */ printf(" OCaml's displaced SIGSEGV handler was %p -- chaining to it is what " "keeps Stack_overflow working\n", (void *)ocaml_segv.sa_sigaction); return 0; }