+
+Tue Jul 31 17:34:42 CEST 2007 Paolo Molaro <lupus@ximian.com>
+
+ * mini.c, mini.h, mini-exceptions.c: added a bit of documentation
+ on the stack overflow handling and made the managed stack overflows
+ catchable in most cases using soft guard pages.
+ * exceptions-x86.c: added code to restore the protection in the soft
+ guard pages at the end of exception handling.
+
2007-07-31 Zoltan Varga <vargaz@gmail.com>
* mini.c (SIG_HANDLER_SIGNATURE): Fix a warning.
#include <mono/metadata/exception.h>
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/mono-debug.h>
+#include <mono/utils/mono-mmap.h>
#include "mini.h"
#include "mini-x86.h"
}
static void
-altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean test_only)
+restore_soft_guard_pages (void)
+{
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ if (jit_tls->stack_ovf_guard_base)
+ mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
+}
+
+/*
+ * this function modifies mctx so that when it is restored, it
+ * won't execcute starting at mctx.eip, but in a function that
+ * will restore the protection on the soft-guard pages and return back to
+ * continue at mctx.eip.
+ */
+static void
+prepare_for_guard_pages (MonoContext *mctx)
+{
+ gpointer *sp;
+ sp = (gpointer)(mctx->esp);
+ sp -= 1;
+ /* the resturn addr */
+ sp [0] = (gpointer)(mctx->eip);
+ mctx->eip = (unsigned long)restore_soft_guard_pages;
+ mctx->esp = (unsigned long)sp;
+}
+
+static void
+altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
{
void (*restore_context) (MonoContext *);
MonoContext mctx;
restore_context = mono_arch_get_restore_context ();
mono_arch_sigctx_to_monoctx (sigctx, &mctx);
- mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
+ mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
+ if (stack_ovf)
+ prepare_for_guard_pages (&mctx);
restore_context (&mctx);
}
mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
{
#ifdef MONO_ARCH_USE_SIGACTION
+ MonoException *exc = NULL;
ucontext_t *ctx = (ucontext_t*)sigctx;
MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx));
gpointer *sp;
int frame_size;
- if (stack_ovf) {
- const char *method;
- /* we don't do much now, but we can warn the user with a useful message */
- fprintf (stderr, "Stack overflow: IP: %p, SP: %p\n", (gpointer)UCONTEXT_REG_EIP (ctx), (gpointer)UCONTEXT_REG_ESP (ctx));
- if (ji && ji->method)
- method = mono_method_full_name (ji->method, TRUE);
- else
- method = "Unmanaged";
- fprintf (stderr, "At %s\n", method);
- abort ();
- }
+ if (stack_ovf)
+ exc = mono_domain_get ()->stack_overflow_ex;
if (!ji)
mono_handle_native_sigsegv (SIGSEGV, sigctx);
/* setup a call frame on the real stack so that control is returned there
* and exception handling can continue.
+ * If this was a stack overflow the caller already ensured the stack pages
+ * needed have been unprotected.
* The frame looks like:
* ucontext struct
* test_only arg
*/
sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx);
sp [0] = sp + 4;
- sp [1] = NULL;
- sp [2] = NULL;
+ sp [1] = exc;
+ sp [2] = (gpointer)stack_ovf;
/* may need to adjust pointers in the new struct copy, depending on the OS */
memcpy (sp + 4, ctx, sizeof (ucontext_t));
/* at the return form the signal handler execution starts in altstack_handle_and_restore() */
gboolean stack_overflow = FALSE;
MonoContext initial_ctx;
int frame_count = 0;
- gboolean gc_disabled = FALSE;
gboolean has_dynamic_methods = FALSE;
gint32 filter_idx, first_filter_idx;
- /*
- * This function might execute on an alternate signal stack, and Boehm GC
- * can't handle that.
- * Also, since the altstack is small, stack space intensive operations like
- * JIT compilation should be avoided.
- */
- if (IS_ON_SIGALTSTACK (jit_tls)) {
- /*
- * FIXME: disabling/enabling GC while already on a signal stack might
- * not be safe either.
- */
- /* Have to reenable it later */
- gc_disabled = TRUE;
- mono_gc_disable ();
- }
-
g_assert (ctx != NULL);
if (!obj) {
MonoException *ex = mono_get_exception_null_reference ();
}
g_list_free (trace_ips);
- if (gc_disabled)
- mono_gc_enable ();
return TRUE;
}
if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
*(mono_get_lmf_addr ()) = lmf;
- if (gc_disabled)
- mono_gc_enable ();
return 0;
}
if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
*ctx = new_ctx;
if (ji == (gpointer)-1) {
- if (gc_disabled)
- mono_gc_enable ();
if (!test_only) {
*(mono_get_lmf_addr ()) = lmf;
tls->end_of_stack = staddr + stsize;
+ /*g_print ("thread %p, stack_base: %p, stack_size: %d\n", (gpointer)pthread_self (), staddr, stsize);*/
+
+ tls->stack_ovf_guard_base = staddr + mono_pagesize ();
+ tls->stack_ovf_guard_size = mono_pagesize () * 8;
+
+ if (mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE)) {
+ /* mprotect can fail for the main thread stack */
+ gpointer gaddr = mono_valloc (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON|MONO_MMAP_FIXED);
+ g_assert (gaddr == tls->stack_ovf_guard_base);
+ }
+
/*
* threads created by nptl does not seem to have a guard page, and
* since the main thread is not created by us, we can't even set one.
* Increasing stsize fools the SIGSEGV signal handler into thinking this
* is a stack overflow exception.
*/
- tls->stack_size = stsize + getpagesize ();
+ tls->stack_size = stsize + mono_pagesize ();
/* Setup an alternate signal stack */
tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC|MONO_MMAP_PRIVATE|MONO_MMAP_ANON);
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-logger.h>
+#include <mono/utils/mono-mmap.h>
#include <mono/os/gc_wrapper.h>
#include "mini.h"
{
#ifndef MONO_ARCH_SIGSEGV_ON_ALTSTACK
MonoException *exc = NULL;
- MonoJitInfo *ji;
#endif
+ MonoJitInfo *ji;
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
}
#endif
+ ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context (ctx));
+
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
- /* Can't allocate memory using Boehm GC on altstack */
+ /* we got a stack overflow in the soft-guard pages
+ * There are two cases:
+ * 1) managed code caused the overflow: we unprotect the soft-guard page
+ * and let the arch-specific code trigger the exception handling mechanism
+ * in the thread stack. The soft-guard pages will be protected again as the stack is unwound.
+ * 2) unmanaged code caused the overflow: we unprotect the soft-guard page
+ * and hope we can continue with those enabled, at least until the hard-guard page
+ * is hit. The alternative to continuing here is to just print a message and abort.
+ * We may add in the future the code to protect the pages again in the codepath
+ * when we return from unmanaged to managed code.
+ */
+ if (jit_tls->stack_ovf_guard_size && (guint8*)info->si_addr >= (guint8*)jit_tls->stack_ovf_guard_base &&
+ (guint8*)info->si_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) {
+ mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
+ if (ji) {
+ mono_arch_handle_altstack_exception (ctx, info->si_addr, TRUE);
+ } else {
+ /* We print a message: after this even managed stack overflows
+ * may crash the runtime
+ */
+ fprintf (stderr, "Stack overflow in unmanaged: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), (gpointer)info->si_addr);
+ }
+ return;
+ }
+ /* The hard-guard page has been hit: there is not much we can do anymore
+ * Print a hopefully clear message and abort.
+ */
if (jit_tls->stack_size &&
- ABS ((guint8*)info->si_addr - ((guint8*)jit_tls->end_of_stack - jit_tls->stack_size)) < 32768)
- mono_arch_handle_altstack_exception (ctx, info->si_addr, TRUE);
- else
+ ABS ((guint8*)info->si_addr - ((guint8*)jit_tls->end_of_stack - jit_tls->stack_size)) < 32768) {
+ const char *method;
+ /* we don't do much now, but we can warn the user with a useful message */
+ fprintf (stderr, "Stack overflow: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), (gpointer)info->si_addr);
+ if (ji && ji->method)
+ method = mono_method_full_name (ji->method, TRUE);
+ else
+ method = "Unmanaged";
+ fprintf (stderr, "At %s\n", method);
+ abort ();
+ } else {
mono_arch_handle_altstack_exception (ctx, info->si_addr, FALSE);
+ }
#else
- ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
if (!ji) {
mono_handle_native_sigsegv (SIGSEGV, ctx);
}
MonoLMF *first_lmf;
gpointer signal_stack;
guint32 signal_stack_size;
+ gpointer stack_ovf_guard_base;
+ guint32 stack_ovf_guard_size;
void (*abort_func) (MonoObject *object);
} MonoJitTlsData;