diff options
| author | Dmitry Antipov | 2014-08-26 10:25:59 +0400 |
|---|---|---|
| committer | Dmitry Antipov | 2014-08-26 10:25:59 +0400 |
| commit | ebd31792b292f63f09efa498b5df73bf86107259 (patch) | |
| tree | 8cd196b0c4c03a3e76e00ed6c0fa010af49d3aea /src | |
| parent | 940ac42ae3d5c5c5d80f984278446ab34c0bb26a (diff) | |
| download | emacs-ebd31792b292f63f09efa498b5df73bf86107259.tar.gz emacs-ebd31792b292f63f09efa498b5df73bf86107259.zip | |
Handle C stack overflow caused by too nested Lisp evaluation.
* configure.ac: Check for sigaltstack and related sigaction
support. Unconditionally check for sigsetjmp and siglongjmp.
(HAVE_STACK_OVERFLOW_HANDLING): Define if we can support it.
* src/lisp.h (toplevel) [HAVE_STACK_OVERFLOW_HANDLING]: Declare
siglongjmp point to transfer control from SIGSEGV handler.
* src/keyboard.c (return_to_command_loop, recover_top_level_message)
[HAVE_STACK_OVERFLOW_HANDLING]: New variables.
(regular_top_level_message): New variable.
(command_loop) [HAVE_STACK_OVERFLOW_HANDLING]: Handle non-local
exit from SIGSEGV handler and adjust message displayed by Vtop_level
if appropriate.
(syms_of_keyboard): DEFVAR Vtop_level_message and initialize
new variables described above.
* src/sysdep.c [HAVE_SYS_RESOURCE_H]: Include sys/resource.h as such.
(stack_grows_down, sigsegv_stack, handle_sigsegv)
[HAVE_STACK_OVERFLOW_HANDLING]: New variables and function.
(init_sigsegv): New function.
(init_signals): Use it.
* lisp/startup.el (normal-top-level): Use top-level-message.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 19 | ||||
| -rw-r--r-- | src/keyboard.c | 33 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/sysdep.c | 73 |
4 files changed, 126 insertions, 2 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 391d2e52100..f618ba02448 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,22 @@ | |||
| 1 | 2014-08-26 Dmitry Antipov <dmantipov@yandex.ru> | ||
| 2 | |||
| 3 | Handle C stack overflow caused by too nested Lisp evaluation. | ||
| 4 | * lisp.h (toplevel) [HAVE_STACK_OVERFLOW_HANDLING]: Declare | ||
| 5 | siglongjmp point to transfer control from SIGSEGV handler. | ||
| 6 | * keyboard.c (return_to_command_loop, recover_top_level_message) | ||
| 7 | [HAVE_STACK_OVERFLOW_HANDLING]: New variables. | ||
| 8 | (regular_top_level_message): New variable. | ||
| 9 | (command_loop) [HAVE_STACK_OVERFLOW_HANDLING]: Handle non-local | ||
| 10 | exit from SIGSEGV handler and adjust message displayed by Vtop_level | ||
| 11 | if appropriate. | ||
| 12 | (syms_of_keyboard): DEFVAR Vtop_level_message and initialize | ||
| 13 | new variables described above. | ||
| 14 | * sysdep.c [HAVE_SYS_RESOURCE_H]: Include sys/resource.h as such. | ||
| 15 | (stack_grows_down, sigsegv_stack, handle_sigsegv) | ||
| 16 | [HAVE_STACK_OVERFLOW_HANDLING]: New variables and function. | ||
| 17 | (init_sigsegv): New function. | ||
| 18 | (init_signals): Use it. | ||
| 19 | |||
| 1 | 2014-08-25 Ken Brown <kbrown@cornell.edu> | 20 | 2014-08-25 Ken Brown <kbrown@cornell.edu> |
| 2 | 21 | ||
| 3 | * emacs.c (main): Remove use of obsolete macro | 22 | * emacs.c (main): Remove use of obsolete macro |
diff --git a/src/keyboard.c b/src/keyboard.c index 9b0b19ada2f..ed00b38a7db 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -133,6 +133,19 @@ static ptrdiff_t this_single_command_key_start; | |||
| 133 | static ptrdiff_t before_command_key_count; | 133 | static ptrdiff_t before_command_key_count; |
| 134 | static ptrdiff_t before_command_echo_length; | 134 | static ptrdiff_t before_command_echo_length; |
| 135 | 135 | ||
| 136 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | ||
| 137 | |||
| 138 | /* For longjmp to recover from C stack overflow. */ | ||
| 139 | sigjmp_buf return_to_command_loop; | ||
| 140 | |||
| 141 | /* Message displayed by Vtop_level when recovering from C stack overflow. */ | ||
| 142 | static Lisp_Object recover_top_level_message; | ||
| 143 | |||
| 144 | #endif /* HAVE_STACK_OVERFLOW_HANDLING */ | ||
| 145 | |||
| 146 | /* Message normally displayed by Vtop_level. */ | ||
| 147 | static Lisp_Object regular_top_level_message; | ||
| 148 | |||
| 136 | /* For longjmp to where kbd input is being done. */ | 149 | /* For longjmp to where kbd input is being done. */ |
| 137 | 150 | ||
| 138 | static sys_jmp_buf getcjmp; | 151 | static sys_jmp_buf getcjmp; |
| @@ -1134,6 +1147,17 @@ static Lisp_Object top_level_1 (Lisp_Object); | |||
| 1134 | Lisp_Object | 1147 | Lisp_Object |
| 1135 | command_loop (void) | 1148 | command_loop (void) |
| 1136 | { | 1149 | { |
| 1150 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | ||
| 1151 | /* At least on GNU/Linux, saving signal mask is important here. */ | ||
| 1152 | if (sigsetjmp (return_to_command_loop, 1) != 0) | ||
| 1153 | { | ||
| 1154 | /* Comes here from handle_sigsegv, see sysdep.c. */ | ||
| 1155 | init_eval (); | ||
| 1156 | Vtop_level_message = recover_top_level_message; | ||
| 1157 | } | ||
| 1158 | else | ||
| 1159 | Vtop_level_message = regular_top_level_message; | ||
| 1160 | #endif /* HAVE_STACK_OVERFLOW_HANDLING */ | ||
| 1137 | if (command_loop_level > 0 || minibuf_level > 0) | 1161 | if (command_loop_level > 0 || minibuf_level > 0) |
| 1138 | { | 1162 | { |
| 1139 | Lisp_Object val; | 1163 | Lisp_Object val; |
| @@ -11000,6 +11024,15 @@ syms_of_keyboard (void) | |||
| 11000 | Vlispy_mouse_stem = build_pure_c_string ("mouse"); | 11024 | Vlispy_mouse_stem = build_pure_c_string ("mouse"); |
| 11001 | staticpro (&Vlispy_mouse_stem); | 11025 | staticpro (&Vlispy_mouse_stem); |
| 11002 | 11026 | ||
| 11027 | regular_top_level_message = build_pure_c_string ("Back to top level"); | ||
| 11028 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | ||
| 11029 | recover_top_level_message | ||
| 11030 | = build_pure_c_string ("Re-entering top level after C stack overflow"); | ||
| 11031 | #endif | ||
| 11032 | DEFVAR_LISP ("top-level-message", Vtop_level_message, | ||
| 11033 | doc: /* Message displayed by `normal-top-level'. */); | ||
| 11034 | Vtop_level_message = regular_top_level_message; | ||
| 11035 | |||
| 11003 | /* Tool-bars. */ | 11036 | /* Tool-bars. */ |
| 11004 | DEFSYM (QCimage, ":image"); | 11037 | DEFSYM (QCimage, ":image"); |
| 11005 | DEFSYM (Qhelp_echo, "help-echo"); | 11038 | DEFSYM (Qhelp_echo, "help-echo"); |
diff --git a/src/lisp.h b/src/lisp.h index 1e2a8b6535f..012e5fad18d 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4093,6 +4093,9 @@ extern Lisp_Object Qdisabled, QCfilter; | |||
| 4093 | extern Lisp_Object Qup, Qdown; | 4093 | extern Lisp_Object Qup, Qdown; |
| 4094 | extern Lisp_Object last_undo_boundary; | 4094 | extern Lisp_Object last_undo_boundary; |
| 4095 | extern bool input_pending; | 4095 | extern bool input_pending; |
| 4096 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | ||
| 4097 | extern sigjmp_buf return_to_command_loop; | ||
| 4098 | #endif | ||
| 4096 | extern Lisp_Object menu_bar_items (Lisp_Object); | 4099 | extern Lisp_Object menu_bar_items (Lisp_Object); |
| 4097 | extern Lisp_Object tool_bar_items (Lisp_Object, int *); | 4100 | extern Lisp_Object tool_bar_items (Lisp_Object, int *); |
| 4098 | extern void discard_mouse_events (void); | 4101 | extern void discard_mouse_events (void); |
diff --git a/src/sysdep.c b/src/sysdep.c index 25bec264f46..f32bd5d1bb6 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -46,7 +46,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 46 | # include <sys/user.h> | 46 | # include <sys/user.h> |
| 47 | # undef frame | 47 | # undef frame |
| 48 | 48 | ||
| 49 | # include <sys/resource.h> | ||
| 50 | # include <math.h> | 49 | # include <math.h> |
| 51 | #endif | 50 | #endif |
| 52 | 51 | ||
| @@ -72,6 +71,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 72 | #include "msdos.h" | 71 | #include "msdos.h" |
| 73 | #endif | 72 | #endif |
| 74 | 73 | ||
| 74 | #ifdef HAVE_SYS_RESOURCE_H | ||
| 75 | #include <sys/resource.h> | ||
| 76 | #endif | ||
| 75 | #include <sys/param.h> | 77 | #include <sys/param.h> |
| 76 | #include <sys/file.h> | 78 | #include <sys/file.h> |
| 77 | #include <fcntl.h> | 79 | #include <fcntl.h> |
| @@ -1716,6 +1718,72 @@ handle_arith_signal (int sig) | |||
| 1716 | xsignal0 (Qarith_error); | 1718 | xsignal0 (Qarith_error); |
| 1717 | } | 1719 | } |
| 1718 | 1720 | ||
| 1721 | #ifdef HAVE_STACK_OVERFLOW_HANDLING | ||
| 1722 | |||
| 1723 | /* True if stack grows down as expected on most OS/ABI variants. */ | ||
| 1724 | |||
| 1725 | static bool stack_grows_down; | ||
| 1726 | |||
| 1727 | /* Alternate stack used by SIGSEGV handler below. */ | ||
| 1728 | |||
| 1729 | static unsigned char sigsegv_stack[SIGSTKSZ]; | ||
| 1730 | |||
| 1731 | /* Attempt to recover from SIGSEGV caused by C stack overflow. */ | ||
| 1732 | |||
| 1733 | static void | ||
| 1734 | handle_sigsegv (int sig, siginfo_t *siginfo, void *arg) | ||
| 1735 | { | ||
| 1736 | /* Hard GC error may lead to stack overflow caused by | ||
| 1737 | too nested calls to mark_object. No way to survive. */ | ||
| 1738 | if (!gc_in_progress) | ||
| 1739 | { | ||
| 1740 | struct rlimit rlim; | ||
| 1741 | |||
| 1742 | if (!getrlimit (RLIMIT_STACK, &rlim)) | ||
| 1743 | { | ||
| 1744 | enum { STACK_EXTRA = 16 * 1024 }; | ||
| 1745 | char *fault_addr = (char *) siginfo->si_addr; | ||
| 1746 | unsigned long used = (stack_grows_down | ||
| 1747 | ? stack_bottom - fault_addr | ||
| 1748 | : fault_addr - stack_bottom); | ||
| 1749 | |||
| 1750 | if (used + STACK_EXTRA > rlim.rlim_cur) | ||
| 1751 | /* Most likely this is it. */ | ||
| 1752 | siglongjmp (return_to_command_loop, 1); | ||
| 1753 | } | ||
| 1754 | } | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | static bool | ||
| 1758 | init_sigsegv (void) | ||
| 1759 | { | ||
| 1760 | struct sigaction sa; | ||
| 1761 | stack_t ss; | ||
| 1762 | |||
| 1763 | stack_grows_down = ((char *) &ss < stack_bottom); | ||
| 1764 | |||
| 1765 | ss.ss_sp = sigsegv_stack; | ||
| 1766 | ss.ss_size = sizeof (sigsegv_stack); | ||
| 1767 | ss.ss_flags = 0; | ||
| 1768 | if (sigaltstack (&ss, NULL) < 0) | ||
| 1769 | return 0; | ||
| 1770 | |||
| 1771 | sigfillset (&sa.sa_mask); | ||
| 1772 | sa.sa_sigaction = handle_sigsegv; | ||
| 1773 | sa.sa_flags = SA_SIGINFO | SA_ONSTACK | emacs_sigaction_flags (); | ||
| 1774 | return sigaction (SIGSEGV, &sa, NULL) < 0 ? 0 : 1; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | #else /* not HAVE_STACK_OVERFLOW_HANDLING */ | ||
| 1778 | |||
| 1779 | static bool | ||
| 1780 | init_sigsegv (void) | ||
| 1781 | { | ||
| 1782 | return 0; | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | #endif /* HAVE_STACK_OVERFLOW_HANDLING */ | ||
| 1786 | |||
| 1719 | static void | 1787 | static void |
| 1720 | deliver_arith_signal (int sig) | 1788 | deliver_arith_signal (int sig) |
| 1721 | { | 1789 | { |
| @@ -1982,7 +2050,8 @@ init_signals (bool dumping) | |||
| 1982 | #ifdef SIGBUS | 2050 | #ifdef SIGBUS |
| 1983 | sigaction (SIGBUS, &thread_fatal_action, 0); | 2051 | sigaction (SIGBUS, &thread_fatal_action, 0); |
| 1984 | #endif | 2052 | #endif |
| 1985 | sigaction (SIGSEGV, &thread_fatal_action, 0); | 2053 | if (!init_sigsegv ()) |
| 2054 | sigaction (SIGSEGV, &thread_fatal_action, 0); | ||
| 1986 | #ifdef SIGSYS | 2055 | #ifdef SIGSYS |
| 1987 | sigaction (SIGSYS, &thread_fatal_action, 0); | 2056 | sigaction (SIGSYS, &thread_fatal_action, 0); |
| 1988 | #endif | 2057 | #endif |