mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-25 10:47:00 +00:00
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.
This commit is contained in:
parent
35656b6fa4
commit
7afa4f300b
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
61
src/w32fns.c
61
src/w32fns.c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user