]> git.eshelyaron.com Git - emacs.git/commitdiff
Support recovery from C stack overflow on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Sat, 8 Aug 2015 08:12:06 +0000 (11:12 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 8 Aug 2015 08:12:06 +0000 (11:12 +0300)
* src/w32fns.c (w32_reset_stack_overflow_guard)
(stack_overflow_handler): New functions for handling C stack
overflow exceptions.
(my_exception_handler): Handle EXCEPTION_STACK_OVERFLOW exceptions
specially, and zero out except_addr if we do.
(globals_of_w32fns): Initialize dwMainThreadId in non-interactive
mode.
* src/sysdep.c [HAVE_STACK_OVERFLOW_HANDLING]: Add !WINDOWSNT to
the condition, as HAVE_STACK_OVERFLOW_HANDLING is now defined for
the MinGW build, but the code guarded by that is for Posix hosts.
* src/keyboard.c (command_loop) [WINDOWSNT]: Call
w32_reset_stack_overflow_guard.

* nt/inc/ms-w32.h (sigjmp_buf): New typedef.
(sigsetjmp): New macro.
(w32_reset_stack_overflow_guard): Declare the prototype.

* configure.ac (HAVE_STACK_OVERFLOW_HANDLING): Set to 1 for MinGW.

configure.ac
nt/inc/ms-w32.h
src/keyboard.c
src/sysdep.c
src/w32fns.c

index 45008d86807ee7e5eef080ccf2f27fc6857ffe7e..863c9a9fd069223887f2a0db2169e6bd869042c9 100644 (file)
@@ -4563,6 +4563,12 @@ if test "$emacs_cv_func_sigsetjmp" = "yes" &&
     [Define to 1 if C stack overflow can be handled in some cases.])
 fi
 
+# WINDOWSNT can handle C stack overflows even without the above features
+if test "${opsys}" = "mingw32"; then
+  AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], 1,
+    [Define to 1 if C stack overflow can be handled in some cases.])
+fi
+
 case $opsys in
   sol2* | unixware )
     dnl TIOCGPGRP is broken in SysVr4, so we can't send signals to PTY
index 4fb32df0c07d0de848ae9418262ef1441e27c9cb..e7a94e81c2d169170a4bdfcac3277dfe9a283c64 100644 (file)
@@ -187,6 +187,20 @@ extern struct tm * sys_localtime (const time_t *);
 #undef HAVE__SETJMP
 #endif
 
+/* The following is needed for recovery from C stack overflows.  */
+#include <setjmp.h>
+typedef jmp_buf sigjmp_buf;
+#ifdef MINGW_W64
+/* Evidently, MinGW64's longjmp crashes when invoked from an exception
+   handler, see https://sourceforge.net/p/mingw-w64/mailman/message/32421953/.
+   This seems to be an unsolved problem in the MinGW64 runtime.  So we
+   use the GCC intrinsics instead.  FIXME.  */
+#define sigsetjmp(j,m) __builtin_setjmp(j)
+#else
+#define sigsetjmp(j,m) setjmp(j)
+#endif
+extern void w32_reset_stack_overflow_guard (void);
+
 #ifdef _MSC_VER
 #include <sys/timeb.h>
 #include <sys/stat.h>
index 5f8667586c42ab6c40782b53718e434042689eb1..e4fe5b9bb4c1519972709d22624fc1b465595c69 100644 (file)
@@ -1092,7 +1092,11 @@ command_loop (void)
   /* At least on GNU/Linux, saving signal mask is important here.  */
   if (sigsetjmp (return_to_command_loop, 1) != 0)
     {
-      /* Comes here from handle_sigsegv, see sysdep.c.  */
+      /* Comes here from handle_sigsegv (see sysdep.c) and
+        stack_overflow_handler (see w32fns.c).  */
+#ifdef WINDOWSNT
+      w32_reset_stack_overflow_guard ();
+#endif
       init_eval ();
       Vinternal__top_level_message = recover_top_level_message;
     }
index df3e573a6ea740a005b6d6c1e752b2bf93a1a0af..25f111ad0ed12d880092112c285b34708ce92106 100644 (file)
@@ -1612,7 +1612,7 @@ handle_arith_signal (int sig)
   xsignal0 (Qarith_error);
 }
 
-#ifdef HAVE_STACK_OVERFLOW_HANDLING
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
 
 /* Alternate stack used by SIGSEGV handler below.  */
 
@@ -1708,7 +1708,7 @@ init_sigsegv (void)
   return sigaction (SIGSEGV, &sa, NULL) < 0 ? 0 : 1;
 }
 
-#else /* not HAVE_STACK_OVERFLOW_HANDLING */
+#else /* not HAVE_STACK_OVERFLOW_HANDLING or WINDOWSNT */
 
 static bool
 init_sigsegv (void)
@@ -1716,7 +1716,7 @@ init_sigsegv (void)
   return 0;
 }
 
-#endif /* HAVE_STACK_OVERFLOW_HANDLING */
+#endif /* HAVE_STACK_OVERFLOW_HANDLING && !WINDOWSNT */
 
 static void
 deliver_arith_signal (int sig)
index ad93bd41851ad49cadee31fcef833f7401392b20..4532fb9f469b50dea4dbbc70fd5a856e6d819fc7 100644 (file)
@@ -9239,18 +9239,71 @@ static DWORD except_code;
 static PVOID except_addr;
 
 #ifndef CYGWIN
+
+/* Stack overflow recovery.  */
+
+/* Re-establish the guard page at stack limit.  This is needed because
+   when a stack overflow is detected, Windows removes the guard bit
+   from the guard page, so if we don't re-establish that protection,
+   the next stack overflow will cause a crash.  */
+void
+w32_reset_stack_overflow_guard (void)
+{
+  /* MinGW headers don't declare this (should be in malloc.h).  */
+  _CRTIMP int __cdecl _resetstkoflw (void);
+
+  /* We ignore the return value.  If _resetstkoflw fails, the next
+     stack overflow will crash the program.  */
+  (void)_resetstkoflw ();
+}
+
+static void
+stack_overflow_handler (void)
+{
+  /* Hard GC error may lead to stack overflow caused by
+     too nested calls to mark_object.  No way to survive.  */
+  if (gc_in_progress)
+    terminate_due_to_signal (SIGSEGV, 40);
+#ifdef _WIN64
+  /* See ms-w32.h: MinGW64's longjmp crashes if invoked in this context.  */
+  __builtin_longjmp (return_to_command_loop, 1);
+#else
+  sys_longjmp (return_to_command_loop, 1);
+#endif
+}
+
 /* This handler records the exception code and the address where it
    was triggered so that this info could be included in the backtrace.
    Without that, the backtrace in some cases has no information
    whatsoever about the offending code, and looks as if the top-level
-   exception handler in the MinGW startup code di the one that
-   crashed.  */
+   exception handler in the MinGW startup code was the one that
+   crashed.  We also recover from stack overflow, by calling our stack
+   overflow handler that jumps back to top level.  */
 static LONG CALLBACK
 my_exception_handler (EXCEPTION_POINTERS * exception_data)
 {
   except_code = exception_data->ExceptionRecord->ExceptionCode;
   except_addr = exception_data->ExceptionRecord->ExceptionAddress;
 
+  /* If this is a stack overflow exception, attempt to recover.  */
+  if (exception_data->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW
+      && exception_data->ExceptionRecord->NumberParameters == 2
+      /* We can only longjmp to top level from the main thread.  */
+      && GetCurrentThreadId () == dwMainThreadId)
+    {
+      /* Call stack_overflow_handler ().  */
+#ifdef _WIN64
+      exception_data->ContextRecord->Rip = (DWORD_PTR) &stack_overflow_handler;
+#else
+      exception_data->ContextRecord->Eip = (DWORD_PTR) &stack_overflow_handler;
+#endif
+      /* Zero this out, so the stale address of the stack overflow
+        exception we handled is not displayed in some future
+        unrelated crash.  */
+      except_addr = 0;
+      return EXCEPTION_CONTINUE_EXECUTION;
+    }
+
   if (prev_exception_handler)
     return prev_exception_handler (exception_data);
   return EXCEPTION_EXECUTE_HANDLER;
@@ -9448,6 +9501,10 @@ globals_of_w32fns (void)
   InitCommonControls ();
 
   syms_of_w32uniscribe ();
+
+  /* Needed for recovery from C stack overflows in batch mode.  */
+  if (noninteractive)
+    dwMainThreadId = GetCurrentThreadId ();
 }
 
 #ifdef NTGUI_UNICODE