+ if(addr >= CONTROL_STACK_HARD_GUARD_PAGE(th) &&
+ addr < CONTROL_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+ lose("Control stack exhausted");
+ }
+ else if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
+ addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+ /* We hit the end of the control stack: disable guard page
+ * protection so the error handler has some headroom, protect the
+ * previous page so that we can catch returns from the guard page
+ * and restore it. */
+ if (th->control_stack_guard_page_protected == NIL)
+ lose("control_stack_guard_page_protected NIL");
+ lower_control_stack_guard_page();
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+ /* For the unfortunate case, when the control stack is
+ * exhausted in a signal handler. */
+ unblock_signals_in_context_and_maybe_warn(context);
+#endif
+ arrange_return_to_lisp_function
+ (context, StaticSymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
+ return 1;
+ }
+ else if(addr >= CONTROL_STACK_RETURN_GUARD_PAGE(th) &&
+ addr < CONTROL_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+ /* We're returning from the guard page: reprotect it, and
+ * unprotect this one. This works even if we somehow missed
+ * the return-guard-page, and hit it on our way to new
+ * exhaustion instead. */
+ if (th->control_stack_guard_page_protected != NIL)
+ lose("control_stack_guard_page_protected not NIL");
+ reset_control_stack_guard_page();
+ return 1;
+ }
+ else if(addr >= BINDING_STACK_HARD_GUARD_PAGE(th) &&
+ addr < BINDING_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+ lose("Binding stack exhausted");
+ }
+ else if(addr >= BINDING_STACK_GUARD_PAGE(th) &&
+ addr < BINDING_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+ protect_binding_stack_guard_page(0, NULL);
+ protect_binding_stack_return_guard_page(1, NULL);
+ fprintf(stderr, "INFO: Binding stack guard page unprotected\n");
+
+ /* For the unfortunate case, when the binding stack is
+ * exhausted in a signal handler. */
+ unblock_signals_in_context_and_maybe_warn(context);
+ arrange_return_to_lisp_function
+ (context, StaticSymbolFunction(BINDING_STACK_EXHAUSTED_ERROR));
+ return 1;
+ }
+ else if(addr >= BINDING_STACK_RETURN_GUARD_PAGE(th) &&
+ addr < BINDING_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+ protect_binding_stack_guard_page(1, NULL);
+ protect_binding_stack_return_guard_page(0, NULL);
+ fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
+ return 1;
+ }
+ else if(addr >= ALIEN_STACK_HARD_GUARD_PAGE(th) &&
+ addr < ALIEN_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+ lose("Alien stack exhausted");
+ }
+ else if(addr >= ALIEN_STACK_GUARD_PAGE(th) &&
+ addr < ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+ protect_alien_stack_guard_page(0, NULL);
+ protect_alien_stack_return_guard_page(1, NULL);
+ fprintf(stderr, "INFO: Alien stack guard page unprotected\n");
+
+ /* For the unfortunate case, when the alien stack is
+ * exhausted in a signal handler. */
+ unblock_signals_in_context_and_maybe_warn(context);
+ arrange_return_to_lisp_function
+ (context, StaticSymbolFunction(ALIEN_STACK_EXHAUSTED_ERROR));
+ return 1;
+ }
+ else if(addr >= ALIEN_STACK_RETURN_GUARD_PAGE(th) &&
+ addr < ALIEN_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+ protect_alien_stack_guard_page(1, NULL);
+ protect_alien_stack_return_guard_page(0, NULL);
+ fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
+ return 1;
+ }
+ else if (addr >= undefined_alien_address &&
+ addr < undefined_alien_address + os_vm_page_size) {
+ arrange_return_to_lisp_function
+ (context, StaticSymbolFunction(UNDEFINED_ALIEN_VARIABLE_ERROR));
+ return 1;
+ }
+ else return 0;
+}
+\f
+/*
+ * noise to install handlers
+ */
+
+#ifndef LISP_FEATURE_WIN32
+/* In Linux 2.4 synchronous signals (sigtrap & co) can be delivered if
+ * they are blocked, in Linux 2.6 the default handler is invoked
+ * instead that usually coredumps. One might hastily think that adding
+ * SA_NODEFER helps, but until ~2.6.13 if SA_NODEFER is specified then
+ * the whole sa_mask is ignored and instead of not adding the signal
+ * in question to the mask. That means if it's not blockable the
+ * signal must be unblocked at the beginning of signal handlers.
+ *
+ * It turns out that NetBSD's SA_NODEFER doesn't DTRT in a different
+ * way: if SA_NODEFER is set and the signal is in sa_mask, the signal
+ * will be unblocked in the sigmask during the signal handler. -- RMK
+ * X-mas day, 2005
+ */
+static volatile int sigaction_nodefer_works = -1;
+
+#define SA_NODEFER_TEST_BLOCK_SIGNAL SIGABRT
+#define SA_NODEFER_TEST_KILL_SIGNAL SIGUSR1
+
+static void
+sigaction_nodefer_test_handler(int signal, siginfo_t *info, void *void_context)
+{
+ sigset_t current;
+ int i;
+ get_current_sigmask(¤t);
+ /* There should be exactly two blocked signals: the two we added
+ * to sa_mask when setting up the handler. NetBSD doesn't block
+ * the signal we're handling when SA_NODEFER is set; Linux before
+ * 2.6.13 or so also doesn't block the other signal when
+ * SA_NODEFER is set. */
+ for(i = 1; i < NSIG; i++)
+ if (sigismember(¤t, i) !=
+ (((i == SA_NODEFER_TEST_BLOCK_SIGNAL) || (i == signal)) ? 1 : 0)) {
+ FSHOW_SIGNAL((stderr, "SA_NODEFER doesn't work, signal %d\n", i));
+ sigaction_nodefer_works = 0;
+ }
+ if (sigaction_nodefer_works == -1)
+ sigaction_nodefer_works = 1;
+}
+
+static void
+see_if_sigaction_nodefer_works(void)
+{
+ struct sigaction sa, old_sa;
+
+ sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+ sa.sa_sigaction = sigaction_nodefer_test_handler;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SA_NODEFER_TEST_BLOCK_SIGNAL);
+ sigaddset(&sa.sa_mask, SA_NODEFER_TEST_KILL_SIGNAL);
+ sigaction(SA_NODEFER_TEST_KILL_SIGNAL, &sa, &old_sa);
+ /* Make sure no signals are blocked. */
+ {
+ sigset_t empty;
+ sigemptyset(&empty);
+ thread_sigmask(SIG_SETMASK, &empty, 0);