commit 7afa4f300b9dc38bf3f33b18fa83bfe35e21a479 (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Sat Aug 8 11:12:06 2015 +0300 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. diff --git a/configure.ac b/configure.ac index 45008d8..863c9a9 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index 4fb32df..e7a94e8 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 *); #undef HAVE__SETJMP #endif +/* The following is needed for recovery from C stack overflows. */ +#include +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 #include diff --git a/src/keyboard.c b/src/keyboard.c index 5f86675..e4fe5b9 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -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; } diff --git a/src/sysdep.c b/src/sysdep.c index df3e573..25f111a 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -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) diff --git a/src/w32fns.c b/src/w32fns.c index ad93bd4..4532fb9 100644 --- a/src/w32fns.c +++ b/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 commit 35656b6fa473a4c422875a61d24ebb736c1be4e9 Author: Jürgen Hötzel Date: Fri Aug 7 20:24:45 2015 +0200 Improve error checking in tramp-adb.el * lisp/net/tramp-adb.el (tramp-adb-ls-output-name-less-p): Improve error checking. "ls -l" on Android in Enforcing mode can print "lstat './FILENAME failed: Permission denied". diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 6305921..b81f597 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -497,12 +497,11 @@ Emacs dired can't find files." (defun tramp-adb-ls-output-name-less-p (a b) "Sort \"ls\" output by name, ascending." - (let (posa posb) - (string-match directory-listing-before-filename-regexp a) - (setq posa (match-end 0)) - (string-match directory-listing-before-filename-regexp b) - (setq posb (match-end 0)) - (string-lessp (substring a posa) (substring b posb)))) + (if (string-match directory-listing-before-filename-regexp a) + (let ((posa (match-end 0))) + (if (string-match directory-listing-before-filename-regexp b) + (let ((posb (match-end 0))) + (string-lessp (substring a posa) (substring b posb))))))) (defun tramp-adb-handle-make-directory (dir &optional parents) "Like `make-directory' for Tramp files." commit c5565ce5b00e4234339ef6a4bdea50d8b6b6aec7 Author: Stefan Monnier Date: Fri Aug 7 13:34:34 2015 -0400 * lisp/emacs-lisp/cl-generic.el (cl--generic-struct-tag): Don't burp on non-struct vectors. diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 619428d..63cd910 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -986,8 +986,9 @@ The value returned is a list of elements of the form `(and (vectorp ,name) (> (length ,name) 0) (let ((tag (aref ,name 0))) - (if (eq (symbol-function tag) :quick-object-witness-check) - tag)))) + (and (symbolp tag) + (eq (symbol-function tag) :quick-object-witness-check) + tag)))) (defun cl--generic-class-parents (class) (let ((parents ()) commit 19154876b9b2315deb48fce4c47623cf62d09c59 Author: Stephen Leake Date: Fri Aug 7 10:32:05 2015 -0500 lisp/window.el: fix typo, more `display-buffer-use-some-frame' * lisp/window.el: fix typo that broke build (display-buffer--action-function-custom-type): add `display-buffer-use-some-frame' (display-buffer): add `display-buffer-use-some-frame' to doc string diff --git a/lisp/window.el b/lisp/window.el index 22fcd98..d9c0d0a 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1,4 +1,4 @@ -e;;; window.el --- GNU Emacs window commands aside from those written in C +;;; window.el --- GNU Emacs window commands aside from those written in C ;; Copyright (C) 1985, 1989, 1992-1994, 2000-2015 Free Software ;; Foundation, Inc. @@ -6244,6 +6244,7 @@ The actual non-nil value of this variable will be copied to the (const display-buffer-at-bottom) (const display-buffer-in-previous-window) (const display-buffer-use-some-window) + (const display-buffer-use-some-frame) (function :tag "Other function")) "Custom type for `display-buffer' action functions.") @@ -6388,6 +6389,7 @@ Available action functions include: `display-buffer-pop-up-window' `display-buffer-in-previous-window' `display-buffer-use-some-window' + `display-buffer-use-some-frame' Recognized alist entries include: commit 4daa09e499b10e6250302341027e39ff5242fa04 Author: Stephen Leake Date: Fri Aug 7 10:03:59 2015 -0500 Add support for 'inhibit-same-window in 'display-buffer-use-some-frame' * lisp/window.el (display-buffer-use-some-frame): Add support for 'inhibit-same-window in alist. * doc/windows.texi (display-buffer-use-some-frame): Doc support for 'inhibit-same-window in alist. diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 41f02aa..ccfe7ff 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2306,16 +2306,18 @@ This function tries to ``display'' @var{buffer} by trying to find a frame that meets a predicate (by default any frame other than the current frame). -If @var{alist} has a non-@code{nil} @code{`inhibit-switch-frame} entry, -avoid raising the frame. +If this function chooses a window on another frame, it makes that frame +visible and, unless @var{alist} contains an @code{inhibit-switch-frame} +entry (@pxref{Choosing Window Options}), raises that frame if necessary. If @var{alist} has a non-nil @code{frame-predicate} entry, its value is a function taking one argument (a frame), returning non-nil if the frame is a candidate; this function replaces the default predicate. -If this function chooses a window on another frame, it makes that frame -visible and, unless @var{alist} contains an @code{inhibit-switch-frame} -entry (@pxref{Choosing Window Options}), raises that frame if necessary. +If @var{alist} has a non-@code{nil} @code{inhibit-same-window} entry, +the selected window is used; thus if the selected frame has a single +window, it is not used. + @end defun @defun display-buffer-pop-up-window buffer alist diff --git a/lisp/window.el b/lisp/window.el index 238e53c..22fcd98 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1,4 +1,4 @@ -;;; window.el --- GNU Emacs window commands aside from those written in C +e;;; window.el --- GNU Emacs window commands aside from those written in C ;; Copyright (C) 1985, 1989, 1992-1994, 2000-2015 Free Software ;; Foundation, Inc. @@ -6490,7 +6490,7 @@ its documentation for additional customization information." (defun display-buffer-use-some-frame (buffer alist) "Display BUFFER in an existing frame that meets a predicate -(by default any frame other than the current frame). If +\(by default any frame other than the current frame). If successful, return the window used; otherwise return nil. If ALIST has a non-nil `inhibit-switch-frame' entry, avoid @@ -6499,8 +6499,12 @@ raising the frame. If ALIST has a non-nil `frame-predicate' entry, its value is a function taking one argument (a frame), returning non-nil if the frame is a candidate; this function replaces the default -predicate." - (let* ((predicate (or (cdr (assoc 'frame-predicate alist)) +predicate. + +If ALIST has a non-nil `inhibit-same-window' entry, avoid using +the currently selected window (only useful with a frame-predicate +that allows the selected frame)." + (let* ((predicate (or (cdr (assq 'frame-predicate alist)) (lambda (frame) (and (not (eq frame (selected-frame))) @@ -6510,7 +6514,7 @@ predicate." (frame-first-window frame))))) ))) (frame (car (filtered-frame-list predicate))) - (window (and frame (get-lru-window frame)))) + (window (and frame (get-lru-window frame nil (cdr (assq 'inhibit-same-window alist)))))) (when window (prog1 (window--display-buffer commit 701484d524835e3461f521138399893366229ae5 Author: Eli Zaretskii Date: Fri Aug 7 16:42:24 2015 +0300 Avoid infinite loop in display of invisible text in strings * src/xdisp.c (handle_invisible_prop): If the next change of invisibility spec does not mean the beginning of a visible text, update the string position from which to start the search for the next invisibility change. This avoids an infinite loop when we have more than one invisibility spec that are made inactive by buffer-invisibility-spec. Simplify code. (Bug#21200) * test/redisplay-testsuite.el (test-redisplay-4): Add a test case for the situation that caused bug #21200. diff --git a/src/xdisp.c b/src/xdisp.c index e45cb87..e7626d1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -4187,13 +4187,13 @@ handle_invisible_prop (struct it *it) if (STRINGP (it->string)) { - Lisp_Object end_charpos, limit, charpos; + Lisp_Object end_charpos, limit; /* Get the value of the invisible text property at the current position. Value will be nil if there is no such property. */ - charpos = make_number (IT_STRING_CHARPOS (*it)); - prop = Fget_text_property (charpos, Qinvisible, it->string); + end_charpos = make_number (IT_STRING_CHARPOS (*it)); + prop = Fget_text_property (end_charpos, Qinvisible, it->string); invis = TEXT_PROP_MEANS_INVISIBLE (prop); if (invis != 0 && IT_STRING_CHARPOS (*it) < it->end_charpos) @@ -4211,8 +4211,12 @@ handle_invisible_prop (struct it *it) XSETINT (limit, len); do { - end_charpos = Fnext_single_property_change (charpos, Qinvisible, - it->string, limit); + end_charpos + = Fnext_single_property_change (end_charpos, Qinvisible, + it->string, limit); + /* Since LIMIT is always an integer, so should be the + value returned by Fnext_single_property_change. */ + eassert (INTEGERP (end_charpos)); if (INTEGERP (end_charpos)) { endpos = XFASTINT (end_charpos); @@ -4221,6 +4225,8 @@ handle_invisible_prop (struct it *it) if (invis == 2) display_ellipsis_p = true; } + else /* Should never happen; but if it does, exit the loop. */ + endpos = len; } while (invis != 0 && endpos < len); @@ -4256,7 +4262,7 @@ handle_invisible_prop (struct it *it) } else { - IT_STRING_CHARPOS (*it) = XFASTINT (end_charpos); + IT_STRING_CHARPOS (*it) = endpos; compute_string_pos (&it->current.string_pos, old, it->string); } } diff --git a/test/redisplay-testsuite.el b/test/redisplay-testsuite.el index 357ab08..40a21b7 100644 --- a/test/redisplay-testsuite.el +++ b/test/redisplay-testsuite.el @@ -251,6 +251,18 @@ static unsigned char x_bits[] = {0xff, 0x81, 0xbd, 0xa5, 0xa5, 0xbd, 0x81, 0xff (str "ABC")) (put-text-property 1 2 'invisible 'test-redisplay--ellipsis-invis str) (overlay-put ov 'display str))) + ;; Overlay string with 2 adjacent and different invisible + ;; properties. This caused an infloop before Emacs 25. + (insert "\n Expected: ABC") + (insert "\n Result: ") + (let ((opoint (point))) + (insert "ABC\n") + (let ((ov (make-overlay (1+ opoint) (+ 2 opoint))) + (str (concat (propertize "X" + 'invisible 'test-redisplay--simple-invis) + (propertize "Y" + 'invisible 'test-redisplay--simple-invis2)))) + (overlay-put ov 'after-string str))) (insert "\n")) @@ -264,6 +276,7 @@ static unsigned char x_bits[] = {0xff, 0x81, 0xbd, 0xa5, 0xa5, 0xbd, 0x81, 0xff (erase-buffer) (setq buffer-invisibility-spec '(test-redisplay--simple-invis + test-redisplay--simple-invis2 (test-redisplay--ellipsis-invis . t))) (test-redisplay-1) (test-redisplay-2)