Alien stack exhaustion machinery only works on x86oids.
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
(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))
"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
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
(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)
* 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
* 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 &&
#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
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)
}
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
#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)
#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
* 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);
* 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) {
}
/* 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 */
(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!
;;; 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"