1.0.25.50: detect binding and alien stack exhaustion
authorGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 22:23:08 +0000 (22:23 +0000)
committerGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 22:23:08 +0000 (22:23 +0000)
Alien stack exhaustion machinery only works on x86oids.

13 files changed:
src/code/error.lisp
src/code/exhaust.lisp
src/code/interr.lisp
src/compiler/generic/parms.lisp
src/pcl/methods.lisp
src/runtime/interrupt.c
src/runtime/thread.c
src/runtime/validate.c
src/runtime/validate.h
src/runtime/x86-64-darwin-os.c
src/runtime/x86-darwin-os.c
tests/exhaust.impure.lisp
version.lisp-expr

index 085264c..db40332 100644 (file)
@@ -164,6 +164,28 @@ calls, or a tail call that SBCL cannot or has not optimized away.
 
 PROCEED WITH CAUTION."))))
 
+(define-condition binding-stack-exhausted (storage-condition)
+  ()
+  (:report
+    (lambda (condition stream)
+      (declare (ignore condition))
+      (format stream
+              ;; no pretty-printing, because that would use a lot of stack.
+              "Binding stack exhausted.
+
+PROCEED WITH CAUTION."))))
+
+(define-condition alien-stack-exhausted (storage-condition)
+  ()
+  (:report
+    (lambda (condition stream)
+      (declare (ignore condition))
+      (format stream
+              ;; no pretty-printing, because that would use a lot of stack.
+              "Alien stack exhausted.
+
+PROCEED WITH CAUTION."))))
+
 (define-condition heap-exhausted-error (storage-condition)
   ()
   (:report
index a96112b..98fa6bc 100644 (file)
@@ -14,6 +14,7 @@
 (define-alien-routine ("protect_control_stack_guard_page"
                        %protect-control-stack-guard-page)
     sb!alien:void
-  (protect-p sb!alien:int))
+  (protect-p sb!alien:int)
+  (thread sb!alien:int))
 (defun protect-control-stack-guard-page (n)
-  (%protect-control-stack-guard-page (if n 1 0)))
+  (%protect-control-stack-guard-page (if n 1 0) 0))
index 8ac4f7a..9014b56 100644 (file)
              "Control stack guard page temporarily disabled: proceed with caution~%")
      (error 'control-stack-exhausted))))
 
+(defun binding-stack-exhausted-error ()
+  (let ((sb!debug:*stack-top-hint* nil))
+    (infinite-error-protect
+     (format *error-output*
+             "Binding stack guard page temporarily disabled: proceed with caution~%")
+     (error 'binding-stack-exhausted))))
+
+(defun alien-stack-exhausted-error ()
+  (let ((sb!debug:*stack-top-hint* nil))
+    (infinite-error-protect
+     (format *error-output*
+             "Alien stack guard page temporarily disabled: proceed with caution~%")
+     (error 'alien-stack-exhausted))))
+
 ;;; KLUDGE: we keep a single HEAP-EXHAUSTED-ERROR object around, so
 ;;; that we don't need to allocate it when running out of
 ;;; memory. Similarly we pass the amounts in special variables as
index 7954fc4..9b9a3a6 100644 (file)
@@ -17,6 +17,8 @@
     sb!kernel::post-gc
     sb!kernel::internal-error
     sb!kernel::control-stack-exhausted-error
+    sb!kernel::binding-stack-exhausted-error
+    sb!kernel::alien-stack-exhausted-error
     sb!kernel::heap-exhausted-error
     sb!kernel::undefined-alien-variable-error
     sb!kernel::undefined-alien-function-error
index e6b08db..d7d40b0 100644 (file)
                             (list (list (find-class
                                          'sb-kernel::control-stack-exhausted))
                                   (list (find-class
+                                         'sb-kernel::binding-stack-exhausted))
+                                  (list (find-class
+                                         'sb-kernel::alien-stack-exhausted))
+                                  (list (find-class
                                          'sb-kernel::heap-exhausted-error))
                                   (list (find-class 'restart))))
                          (setq po-cache cache)
index 921f3b7..11e8f19 100644 (file)
@@ -1382,8 +1382,8 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * previous page so that we can catch returns from the guard page
          * and restore it. */
         corruption_warning_and_maybe_lose("Control stack exhausted");
-        protect_control_stack_guard_page(0);
-        protect_control_stack_return_guard_page(1);
+        protect_control_stack_guard_page(0, NULL);
+        protect_control_stack_return_guard_page(1, NULL);
 
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
         /* For the unfortunate case, when the control stack is
@@ -1401,8 +1401,48 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * the return-guard-page, and hit it on our way to new
          * exhaustion instead. */
         fprintf(stderr, "INFO: Control stack guard page reprotected\n");
-        protect_control_stack_guard_page(1);
-        protect_control_stack_return_guard_page(0);
+        protect_control_stack_guard_page(1, NULL);
+        protect_control_stack_return_guard_page(0, NULL);
+        return 1;
+    }
+    else if(addr >= BINDING_STACK_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        corruption_warning_and_maybe_lose("Binding stack exhausted");
+        protect_binding_stack_guard_page(0, NULL);
+        protect_binding_stack_return_guard_page(1, NULL);
+
+        /* 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) {
+        fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
+        protect_binding_stack_guard_page(1, NULL);
+        protect_binding_stack_return_guard_page(0, NULL);
+        return 1;
+    }
+    else if(addr >= ALIEN_STACK_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        corruption_warning_and_maybe_lose("Alien stack exhausted");
+        protect_alien_stack_guard_page(0, NULL);
+        protect_alien_stack_return_guard_page(1, NULL);
+
+        /* 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) {
+        fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
+        protect_alien_stack_guard_page(1, NULL);
+        protect_alien_stack_return_guard_page(0, NULL);
         return 1;
     }
     else if (addr >= undefined_alien_address &&
index 038f16b..e158680 100644 (file)
@@ -63,8 +63,6 @@
 #define LOCK_CREATE_THREAD
 #endif
 
-#define ALIEN_STACK_SIZE (1*1024*1024) /* 1Mb size chosen at random */
-
 #ifdef LISP_FEATURE_SB_THREAD
 struct thread_post_mortem {
 #ifdef DELAY_THREAD_POST_MORTEM
@@ -135,7 +133,9 @@ initial_thread_trampoline(struct thread *th)
     link_thread(th);
     th->os_thread=thread_self();
 #ifndef LISP_FEATURE_WIN32
-    protect_control_stack_guard_page(1);
+    protect_control_stack_guard_page(1, NULL);
+    protect_binding_stack_guard_page(1, NULL);
+    protect_alien_stack_guard_page(1, NULL);
 #endif
 
 #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
@@ -268,7 +268,9 @@ new_thread_trampoline(struct thread *th)
     }
 
     th->os_thread=thread_self();
-    protect_control_stack_guard_page(1);
+    protect_control_stack_guard_page(1, NULL);
+    protect_binding_stack_guard_page(1, NULL);
+    protect_alien_stack_guard_page(1, NULL);
     /* Since GC can only know about this thread from the all_threads
      * list and we're just adding this thread to it, there is no
      * danger of deadlocking even with SIG_STOP_FOR_GC blocked (which
index d901625..967775a 100644 (file)
@@ -83,37 +83,35 @@ validate(void)
 #endif
 }
 
-void
-protect_control_stack_guard_page(int protect_p) {
-    struct thread *th = arch_os_get_current_thread();
-    os_protect(CONTROL_STACK_GUARD_PAGE(th),
-               os_vm_page_size,protect_p ?
-               (OS_VM_PROT_READ|OS_VM_PROT_EXECUTE) : OS_VM_PROT_ALL);
+static inline void
+protect_page(void *page, int protect_p, os_vm_prot_t flags) {
+    os_protect(page, os_vm_page_size, protect_p ?
+               flags : OS_VM_PROT_ALL);
 }
 
-void
-protect_control_stack_return_guard_page(int protect_p) {
-    struct thread *th = arch_os_get_current_thread();
-    os_protect(CONTROL_STACK_RETURN_GUARD_PAGE(th),
-               os_vm_page_size,protect_p ?
-               (OS_VM_PROT_READ|OS_VM_PROT_EXECUTE) : OS_VM_PROT_ALL);
-}
-
-/* these OOAO violations are here because with mach exception handlers
- * we need to protect the stack guard pages from the mach exception
- * handlers which run on a different thread, so we take a thread
- * argument here. Too bad we don't have keywords args in C. */
-void
-protect_control_stack_guard_page_thread(int protect_p, struct thread *th) {
-    os_protect(CONTROL_STACK_GUARD_PAGE(th),
-               os_vm_page_size,protect_p ?
-               (OS_VM_PROT_READ|OS_VM_PROT_EXECUTE) : OS_VM_PROT_ALL);
-}
+#define DEF_PROTECT_PAGE(name,page_name,flags)                               \
+    void                                                                     \
+    protect_##name(int protect_p, struct thread *thread) {                   \
+        if (!thread)                                                         \
+           thread = arch_os_get_current_thread();                            \
+        protect_page(page_name(thread), protect_p, flags);                   \
+    }
 
-void
-protect_control_stack_return_guard_page_thread(int protect_p,
-                                               struct thread* th) {
-    os_protect(CONTROL_STACK_RETURN_GUARD_PAGE(th),
-               os_vm_page_size,protect_p ?
-               (OS_VM_PROT_READ|OS_VM_PROT_EXECUTE) : OS_VM_PROT_ALL);
-}
+DEF_PROTECT_PAGE(control_stack_guard_page,
+                 CONTROL_STACK_GUARD_PAGE,
+                 OS_VM_PROT_READ|OS_VM_PROT_EXECUTE)
+DEF_PROTECT_PAGE(control_stack_return_guard_page,
+                 CONTROL_STACK_RETURN_GUARD_PAGE,
+                 OS_VM_PROT_READ|OS_VM_PROT_EXECUTE)
+DEF_PROTECT_PAGE(binding_stack_guard_page,
+                 BINDING_STACK_GUARD_PAGE,
+                 OS_VM_PROT_NONE)
+DEF_PROTECT_PAGE(binding_stack_return_guard_page,
+                 BINDING_STACK_RETURN_GUARD_PAGE,
+                 OS_VM_PROT_NONE)
+DEF_PROTECT_PAGE(alien_stack_guard_page,
+                 ALIEN_STACK_GUARD_PAGE,
+                 OS_VM_PROT_NONE)
+DEF_PROTECT_PAGE(alien_stack_return_guard_page,
+                 ALIEN_STACK_RETURN_GUARD_PAGE,
+                 OS_VM_PROT_NONE)
index b0f9122..de4e345 100644 (file)
@@ -18,6 +18,8 @@
 #endif
 
 #define BINDING_STACK_SIZE (1024*1024)   /* chosen at random */
+#define ALIEN_STACK_SIZE (1024*1024)     /* chosen at random */
+
 /* eventually choosable per-thread: */
 #define DEFAULT_CONTROL_STACK_SIZE (2*1024*1024)
 
     ((os_vm_address_t)(th->control_stack_start))
 #define CONTROL_STACK_RETURN_GUARD_PAGE(th) \
     (CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size)
+#define ALIEN_STACK_GUARD_PAGE(th) \
+    ((os_vm_address_t)(th->alien_stack_start))
+#define ALIEN_STACK_RETURN_GUARD_PAGE(th) \
+    (ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size)
 #else
 #define CONTROL_STACK_GUARD_PAGE(th) \
     (((os_vm_address_t)(th->control_stack_end)) - os_vm_page_size)
 #define CONTROL_STACK_RETURN_GUARD_PAGE(th) \
     (CONTROL_STACK_GUARD_PAGE(th) - os_vm_page_size)
+#define ALIEN_STACK_GUARD_PAGE(th)                                 \
+    (((os_vm_address_t)th->alien_stack_start) + ALIEN_STACK_SIZE - \
+     os_vm_page_size)
+#define ALIEN_STACK_RETURN_GUARD_PAGE(th) \
+    (ALIEN_STACK_GUARD_PAGE(th) - os_vm_page_size)
 #endif
 
+#define BINDING_STACK_GUARD_PAGE(th)                                   \
+    (((os_vm_address_t)th->binding_stack_start) + BINDING_STACK_SIZE - \
+     os_vm_page_size)
+#define BINDING_STACK_RETURN_GUARD_PAGE(th) \
+    (BINDING_STACK_GUARD_PAGE(th) - os_vm_page_size)
+
 extern void validate(void);
-extern void protect_control_stack_guard_page(int protect_p);
-extern void protect_control_stack_return_guard_page(int protect_p);
-extern void protect_control_stack_guard_page_thread(int protect_p,
-                                                    struct thread *th);
-extern void protect_control_stack_return_guard_page_thread(int protect_p,
-                                                           struct thread* th);
+
+extern void
+protect_control_stack_guard_page(int protect_p, struct thread *thread);
+extern void
+protect_control_stack_return_guard_page(int protect_p, struct thread *thread);
+extern void
+protect_binding_stack_guard_page(int protect_p, struct thread *thread);
+extern void
+protect_binding_stack_return_guard_page(int protect_p, struct thread *thread);
+extern void
+protect_alien_stack_guard_page(int protect_p, struct thread *thread);
+extern void
+protect_alien_stack_return_guard_page(int protect_p, struct thread *thread);
 extern os_vm_address_t undefined_alien_address;
 #endif
 
index 6360e00..2b987dc 100644 (file)
@@ -355,8 +355,8 @@ catch_exception_raise(mach_port_t exception_port,
              * 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. */
-            protect_control_stack_guard_page_thread(0, th);
-            protect_control_stack_return_guard_page_thread(1, th);
+            protect_control_stack_guard_page(0, th);
+            protect_control_stack_return_guard_page(1, th);
 
             backup_thread_state = thread_state;
             open_stack_allocation(&thread_state);
@@ -397,8 +397,8 @@ catch_exception_raise(mach_port_t exception_port,
              * unprotect this one. This works even if we somehow missed
              * the return-guard-page, and hit it on our way to new
              * exhaustion instead. */
-            protect_control_stack_guard_page_thread(1, th);
-            protect_control_stack_return_guard_page_thread(0, th);
+            protect_control_stack_guard_page(1, th);
+            protect_control_stack_return_guard_page(0, th);
         }
         else if (addr >= undefined_alien_address &&
                  addr < undefined_alien_address + os_vm_page_size) {
index f1d7b6c..3c32ec4 100644 (file)
@@ -432,15 +432,15 @@ catch_exception_raise(mach_port_t exception_port,
         }
         /* At stack guard */
         if (os_trunc_to_page(addr) == CONTROL_STACK_GUARD_PAGE(th)) {
-            protect_control_stack_guard_page_thread(0, th);
-            protect_control_stack_return_guard_page_thread(1, th);
+            protect_control_stack_guard_page(0, th);
+            protect_control_stack_return_guard_page(1, th);
             handler = control_stack_exhausted_handler;
             break;
         }
         /* Return from stack guard */
         if (os_trunc_to_page(addr) == CONTROL_STACK_RETURN_GUARD_PAGE(th)) {
-            protect_control_stack_guard_page_thread(1, th);
-            protect_control_stack_return_guard_page_thread(0, th);
+            protect_control_stack_guard_page(1, th);
+            protect_control_stack_return_guard_page(0, th);
             break;
         }
         /* Regular memory fault */
index 79f44a4..ef21e7c 100644 (file)
          (recurse)))))
   (assert (= exhaust-count recurse-count *count*)))
 
+(with-test (:name (:exhaust :binding-stack))
+  (let ((ok nil)
+        (symbols (loop repeat 1024 collect (gensym)))
+        (values (loop repeat 1024 collect nil)))
+    (gc :full t)
+    (labels ((exhaust-binding-stack (i)
+               (progv symbols values
+                 (exhaust-binding-stack (1+ i)))))
+      (handler-case
+          (exhaust-binding-stack 0)
+        (sb-kernel::binding-stack-exhausted ()
+          (setq ok t)))
+      (assert ok))))
+
+#+c-stack-is-control-stack
+(with-test (:name (:exhaust :alien-stack))
+  (let ((ok nil))
+    (labels ((exhaust-alien-stack (i)
+               (with-alien ((integer-array (array int 500)))
+                 (+ (deref integer-array 0)
+                    (exhaust-alien-stack (1+ i))))))
+      (handler-case
+          (exhaust-alien-stack 0)
+        (sb-kernel::alien-stack-exhausted ()
+          (setq ok t)))
+      (assert ok))))
+
 ;;; OK!
index dcc475a..1bfc001 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.25.49"
+"1.0.25.50"