Tue Jul 31 17:34:42 CEST 2007 Paolo Molaro <lupus@ximian.com>
authorPaolo Molaro <lupus@oddwiz.org>
Tue, 31 Jul 2007 15:23:40 +0000 (15:23 -0000)
committerPaolo Molaro <lupus@oddwiz.org>
Tue, 31 Jul 2007 15:23:40 +0000 (15:23 -0000)
* 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.

svn path=/trunk/mono/; revision=83092

mono/mini/ChangeLog
mono/mini/exceptions-x86.c
mono/mini/mini-exceptions.c
mono/mini/mini.c
mono/mini/mini.h

index ad8e67a94a09fc156da717b768b38bc49349b8ad..c34ab550466a317ff7a51fafc2c60263b1b2d90e 100644 (file)
@@ -1,3 +1,12 @@
+
+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.
index 7712128c0a78b531e84114b2243a777376c4dbfd..f2d5f84f52eca213db4f17733006ee3a7eb67ab1 100644 (file)
@@ -20,6 +20,7 @@
 #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"
@@ -788,14 +789,42 @@ mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
 }
 
 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);
 }
 
@@ -803,26 +832,20 @@ void
 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
@@ -840,8 +863,8 @@ mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean
         */
        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() */
index 7b16600905909f4ba4403dec67c482003b876737..1beaba2a8ce4a0086792a20ffc29229d1148eb2c 100644 (file)
@@ -595,26 +595,9 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
        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 ();
@@ -773,8 +756,6 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                                }
                                                                g_list_free (trace_ips);
 
-                                                               if (gc_disabled)
-                                                                       mono_gc_enable ();
                                                                return TRUE;
                                                        }
                                                        if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
@@ -784,8 +765,6 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                        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) && 
@@ -817,8 +796,6 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                *ctx = new_ctx;
 
                if (ji == (gpointer)-1) {
-                       if (gc_disabled)
-                               mono_gc_enable ();
 
                        if (!test_only) {
                                *(mono_get_lmf_addr ()) = lmf;
@@ -964,13 +941,24 @@ mono_setup_altstack (MonoJitTlsData *tls)
 
        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);
index 1d47f67f7af29dad16c6ddef28be971d4526cfdc..7704e8df05264f9ac5b2b9b5ee10d944188e5fd9 100644 (file)
@@ -64,6 +64,7 @@
 #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"
@@ -11077,8 +11078,8 @@ SIG_HANDLER_SIGNATURE (sigsegv_signal_handler)
 {
 #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);
@@ -11098,16 +11099,52 @@ SIG_HANDLER_SIGNATURE (sigsegv_signal_handler)
        }
 #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);
        }
index 1fc3c4786867136927bee83a6906b20fb919767b..c1ec58ac18bd467a0b28c371f214e03b3ff98fed 100644 (file)
@@ -448,6 +448,8 @@ typedef struct {
        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;