diff options
| author | Eli Zaretskii | 2015-08-08 11:12:06 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2015-08-08 11:12:06 +0300 |
| commit | 7afa4f300b9dc38bf3f33b18fa83bfe35e21a479 (patch) | |
| tree | 032ab5a75ec1ff00cee51ffe6216fceb16a53e6c | |
| parent | 35656b6fa473a4c422875a61d24ebb736c1be4e9 (diff) | |
| download | emacs-7afa4f300b9dc38bf3f33b18fa83bfe35e21a479.tar.gz emacs-7afa4f300b9dc38bf3f33b18fa83bfe35e21a479.zip | |
Support recovery from C stack overflow on MS-Windows
* 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.
| -rw-r--r-- | configure.ac | 6 | ||||
| -rw-r--r-- | nt/inc/ms-w32.h | 14 | ||||
| -rw-r--r-- | src/keyboard.c | 6 | ||||
| -rw-r--r-- | src/sysdep.c | 6 | ||||
| -rw-r--r-- | src/w32fns.c | 61 |
5 files changed, 87 insertions, 6 deletions
diff --git a/configure.ac b/configure.ac index 45008d86807..863c9a9fd06 100644 --- a/configure.ac +++ b/configure.ac | |||
| @@ -4563,6 +4563,12 @@ if test "$emacs_cv_func_sigsetjmp" = "yes" && | |||
| 4563 | [Define to 1 if C stack overflow can be handled in some cases.]) | 4563 | [Define to 1 if C stack overflow can be handled in some cases.]) |
| 4564 | fi | 4564 | fi |
| 4565 | 4565 | ||
| 4566 | # WINDOWSNT can handle C stack overflows even without the above features | ||
| 4567 | if test "${opsys}" = "mingw32"; then | ||
| 4568 | AC_DEFINE([HAVE_STACK_OVERFLOW_HANDLING], 1, | ||
| 4569 | [Define to 1 if C stack overflow can be handled in some cases.]) | ||
| 4570 | fi | ||
| 4571 | |||
| 4566 | case $opsys in | 4572 | case $opsys in |
| 4567 | sol2* | unixware ) | 4573 | sol2* | unixware ) |
| 4568 | dnl TIOCGPGRP is broken in SysVr4, so we can't send signals to PTY | 4574 | dnl TIOCGPGRP is broken in SysVr4, so we can't send signals to PTY |
diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index 4fb32df0c07..e7a94e81c2d 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h | |||
| @@ -187,6 +187,20 @@ extern struct tm * sys_localtime (const time_t *); | |||
| 187 | #undef HAVE__SETJMP | 187 | #undef HAVE__SETJMP |
| 188 | #endif | 188 | #endif |
| 189 | 189 | ||
| 190 | /* The following is needed for recovery from C stack overflows. */ | ||
| 191 | #include <setjmp.h> | ||
| 192 | typedef jmp_buf sigjmp_buf; | ||
| 193 | #ifdef MINGW_W64 | ||
| 194 | /* Evidently, MinGW64's longjmp crashes when invoked from an exception | ||
| 195 | handler, see https://sourceforge.net/p/mingw-w64/mailman/message/32421953/. | ||
| 196 | This seems to be an unsolved problem in the MinGW64 runtime. So we | ||
| 197 | use the GCC intrinsics instead. FIXME. */ | ||
| 198 | #define sigsetjmp(j,m) __builtin_setjmp(j) | ||
| 199 | #else | ||
| 200 | #define sigsetjmp(j,m) setjmp(j) | ||
| 201 | #endif | ||
| 202 | extern void w32_reset_stack_overflow_guard (void); | ||
| 203 | |||
| 190 | #ifdef _MSC_VER | 204 | #ifdef _MSC_VER |
| 191 | #include <sys/timeb.h> | 205 | #include <sys/timeb.h> |
| 192 | #include <sys/stat.h> | 206 | #include <sys/stat.h> |
diff --git a/src/keyboard.c b/src/keyboard.c index 5f8667586c4..e4fe5b9bb4c 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -1092,7 +1092,11 @@ command_loop (void) | |||
| 1092 | /* At least on GNU/Linux, saving signal mask is important here. */ | 1092 | /* At least on GNU/Linux, saving signal mask is important here. */ |
| 1093 | if (sigsetjmp (return_to_command_loop, 1) != 0) | 1093 | if (sigsetjmp (return_to_command_loop, 1) != 0) |
| 1094 | { | 1094 | { |
| 1095 | /* Comes here from handle_sigsegv, see sysdep.c. */ | 1095 | /* Comes here from handle_sigsegv (see sysdep.c) and |
| 1096 | stack_overflow_handler (see w32fns.c). */ | ||
| 1097 | #ifdef WINDOWSNT | ||
| 1098 | w32_reset_stack_overflow_guard (); | ||
| 1099 | #endif | ||
| 1096 | init_eval (); | 1100 | init_eval (); |
| 1097 | Vinternal__top_level_message = recover_top_level_message; | 1101 | Vinternal__top_level_message = recover_top_level_message; |
| 1098 | } | 1102 | } |
diff --git a/src/sysdep.c b/src/sysdep.c index df3e573a6ea..25f111ad0ed 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -1612,7 +1612,7 @@ handle_arith_signal (int sig) | |||
| 1612 | xsignal0 (Qarith_error); | 1612 | xsignal0 (Qarith_error); |
| 1613 | } | 1613 | } |
| 1614 | 1614 | ||
| 1615 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | 1615 | #if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT |
| 1616 | 1616 | ||
| 1617 | /* Alternate stack used by SIGSEGV handler below. */ | 1617 | /* Alternate stack used by SIGSEGV handler below. */ |
| 1618 | 1618 | ||
| @@ -1708,7 +1708,7 @@ init_sigsegv (void) | |||
| 1708 | return sigaction (SIGSEGV, &sa, NULL) < 0 ? 0 : 1; | 1708 | return sigaction (SIGSEGV, &sa, NULL) < 0 ? 0 : 1; |
| 1709 | } | 1709 | } |
| 1710 | 1710 | ||
| 1711 | #else /* not HAVE_STACK_OVERFLOW_HANDLING */ | 1711 | #else /* not HAVE_STACK_OVERFLOW_HANDLING or WINDOWSNT */ |
| 1712 | 1712 | ||
| 1713 | static bool | 1713 | static bool |
| 1714 | init_sigsegv (void) | 1714 | init_sigsegv (void) |
| @@ -1716,7 +1716,7 @@ init_sigsegv (void) | |||
| 1716 | return 0; | 1716 | return 0; |
| 1717 | } | 1717 | } |
| 1718 | 1718 | ||
| 1719 | #endif /* HAVE_STACK_OVERFLOW_HANDLING */ | 1719 | #endif /* HAVE_STACK_OVERFLOW_HANDLING && !WINDOWSNT */ |
| 1720 | 1720 | ||
| 1721 | static void | 1721 | static void |
| 1722 | deliver_arith_signal (int sig) | 1722 | deliver_arith_signal (int sig) |
diff --git a/src/w32fns.c b/src/w32fns.c index ad93bd41851..4532fb9f469 100644 --- a/src/w32fns.c +++ b/src/w32fns.c | |||
| @@ -9239,18 +9239,71 @@ static DWORD except_code; | |||
| 9239 | static PVOID except_addr; | 9239 | static PVOID except_addr; |
| 9240 | 9240 | ||
| 9241 | #ifndef CYGWIN | 9241 | #ifndef CYGWIN |
| 9242 | |||
| 9243 | /* Stack overflow recovery. */ | ||
| 9244 | |||
| 9245 | /* Re-establish the guard page at stack limit. This is needed because | ||
| 9246 | when a stack overflow is detected, Windows removes the guard bit | ||
| 9247 | from the guard page, so if we don't re-establish that protection, | ||
| 9248 | the next stack overflow will cause a crash. */ | ||
| 9249 | void | ||
| 9250 | w32_reset_stack_overflow_guard (void) | ||
| 9251 | { | ||
| 9252 | /* MinGW headers don't declare this (should be in malloc.h). */ | ||
| 9253 | _CRTIMP int __cdecl _resetstkoflw (void); | ||
| 9254 | |||
| 9255 | /* We ignore the return value. If _resetstkoflw fails, the next | ||
| 9256 | stack overflow will crash the program. */ | ||
| 9257 | (void)_resetstkoflw (); | ||
| 9258 | } | ||
| 9259 | |||
| 9260 | static void | ||
| 9261 | stack_overflow_handler (void) | ||
| 9262 | { | ||
| 9263 | /* Hard GC error may lead to stack overflow caused by | ||
| 9264 | too nested calls to mark_object. No way to survive. */ | ||
| 9265 | if (gc_in_progress) | ||
| 9266 | terminate_due_to_signal (SIGSEGV, 40); | ||
| 9267 | #ifdef _WIN64 | ||
| 9268 | /* See ms-w32.h: MinGW64's longjmp crashes if invoked in this context. */ | ||
| 9269 | __builtin_longjmp (return_to_command_loop, 1); | ||
| 9270 | #else | ||
| 9271 | sys_longjmp (return_to_command_loop, 1); | ||
| 9272 | #endif | ||
| 9273 | } | ||
| 9274 | |||
| 9242 | /* This handler records the exception code and the address where it | 9275 | /* This handler records the exception code and the address where it |
| 9243 | was triggered so that this info could be included in the backtrace. | 9276 | was triggered so that this info could be included in the backtrace. |
| 9244 | Without that, the backtrace in some cases has no information | 9277 | Without that, the backtrace in some cases has no information |
| 9245 | whatsoever about the offending code, and looks as if the top-level | 9278 | whatsoever about the offending code, and looks as if the top-level |
| 9246 | exception handler in the MinGW startup code di the one that | 9279 | exception handler in the MinGW startup code was the one that |
| 9247 | crashed. */ | 9280 | crashed. We also recover from stack overflow, by calling our stack |
| 9281 | overflow handler that jumps back to top level. */ | ||
| 9248 | static LONG CALLBACK | 9282 | static LONG CALLBACK |
| 9249 | my_exception_handler (EXCEPTION_POINTERS * exception_data) | 9283 | my_exception_handler (EXCEPTION_POINTERS * exception_data) |
| 9250 | { | 9284 | { |
| 9251 | except_code = exception_data->ExceptionRecord->ExceptionCode; | 9285 | except_code = exception_data->ExceptionRecord->ExceptionCode; |
| 9252 | except_addr = exception_data->ExceptionRecord->ExceptionAddress; | 9286 | except_addr = exception_data->ExceptionRecord->ExceptionAddress; |
| 9253 | 9287 | ||
| 9288 | /* If this is a stack overflow exception, attempt to recover. */ | ||
| 9289 | if (exception_data->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW | ||
| 9290 | && exception_data->ExceptionRecord->NumberParameters == 2 | ||
| 9291 | /* We can only longjmp to top level from the main thread. */ | ||
| 9292 | && GetCurrentThreadId () == dwMainThreadId) | ||
| 9293 | { | ||
| 9294 | /* Call stack_overflow_handler (). */ | ||
| 9295 | #ifdef _WIN64 | ||
| 9296 | exception_data->ContextRecord->Rip = (DWORD_PTR) &stack_overflow_handler; | ||
| 9297 | #else | ||
| 9298 | exception_data->ContextRecord->Eip = (DWORD_PTR) &stack_overflow_handler; | ||
| 9299 | #endif | ||
| 9300 | /* Zero this out, so the stale address of the stack overflow | ||
| 9301 | exception we handled is not displayed in some future | ||
| 9302 | unrelated crash. */ | ||
| 9303 | except_addr = 0; | ||
| 9304 | return EXCEPTION_CONTINUE_EXECUTION; | ||
| 9305 | } | ||
| 9306 | |||
| 9254 | if (prev_exception_handler) | 9307 | if (prev_exception_handler) |
| 9255 | return prev_exception_handler (exception_data); | 9308 | return prev_exception_handler (exception_data); |
| 9256 | return EXCEPTION_EXECUTE_HANDLER; | 9309 | return EXCEPTION_EXECUTE_HANDLER; |
| @@ -9448,6 +9501,10 @@ globals_of_w32fns (void) | |||
| 9448 | InitCommonControls (); | 9501 | InitCommonControls (); |
| 9449 | 9502 | ||
| 9450 | syms_of_w32uniscribe (); | 9503 | syms_of_w32uniscribe (); |
| 9504 | |||
| 9505 | /* Needed for recovery from C stack overflows in batch mode. */ | ||
| 9506 | if (noninteractive) | ||
| 9507 | dwMainThreadId = GetCurrentThreadId (); | ||
| 9451 | } | 9508 | } |
| 9452 | 9509 | ||
| 9453 | #ifdef NTGUI_UNICODE | 9510 | #ifdef NTGUI_UNICODE |