* [RFC] sigaltstack + longjmp to protect from C stack overflow
@ 2014-08-25 14:46 Dmitry Antipov
2014-08-25 15:06 ` Paul Eggert
0 siblings, 1 reply; 2+ messages in thread
From: Dmitry Antipov @ 2014-08-25 14:46 UTC (permalink / raw)
To: Emacs development discussions; +Cc: Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 348 bytes --]
This is an experimental and most probably non-portable method to handle
C stack overflow by using well-known sigaltstack/siglongjmp approach.
This can help if someone do something like:
(setq max-specpdl-size 83200000
max-lisp-eval-depth 640000)
(defun f1 () (f1))
(f1) ==> Re-entering top level after C stack overflow
Comments?
Dmitry
[-- Attachment #2: c_stack_overflow.patch --]
[-- Type: text/x-patch, Size: 4908 bytes --]
=== modified file 'lisp/startup.el'
--- lisp/startup.el 2014-07-27 13:21:30 +0000
+++ lisp/startup.el 2014-08-25 13:33:17 +0000
@@ -497,7 +497,7 @@
reads the initialization files, etc.
It is the default value of the variable `top-level'."
(if command-line-processed
- (message "Back to top level.")
+ (message top-level-message)
(setq command-line-processed t)
;; Look in each dir in load-path for a subdirs.el file. If we
=== modified file 'src/keyboard.c'
--- src/keyboard.c 2014-08-10 08:26:28 +0000
+++ src/keyboard.c 2014-08-25 14:11:14 +0000
@@ -133,6 +133,15 @@
static ptrdiff_t before_command_key_count;
static ptrdiff_t before_command_echo_length;
+/* For longjmp to recover from C stack overflow. */
+sys_jmp_buf return_to_command_loop;
+
+/* Message displayed by Vtop_level when we recover from C stack overflow. */
+static Lisp_Object recover_top_level_message;
+
+/* Message normally displayed by Vtop_level. */
+static Lisp_Object regular_top_level_message;
+
/* For longjmp to where kbd input is being done. */
static sys_jmp_buf getcjmp;
@@ -1134,6 +1143,16 @@
Lisp_Object
command_loop (void)
{
+ /* Saving signal mask looks important here. */
+ if (sigsetjmp (return_to_command_loop, 1) != 0)
+ {
+ /* Comes here after recovering from C stack overflow. */
+ init_eval ();
+ Vtop_level_message = recover_top_level_message;
+ }
+ else
+ Vtop_level_message = regular_top_level_message;
+
if (command_loop_level > 0 || minibuf_level > 0)
{
Lisp_Object val;
@@ -11000,6 +11019,13 @@
Vlispy_mouse_stem = build_pure_c_string ("mouse");
staticpro (&Vlispy_mouse_stem);
+ regular_top_level_message = build_pure_c_string ("Back to top level");
+ recover_top_level_message
+ = build_pure_c_string ("Re-entering top level after C stack overflow");
+ DEFVAR_LISP ("top-level-message", Vtop_level_message,
+ doc: /* Message displayed by `normal-top-level'. */);
+ Vtop_level_message = regular_top_level_message;
+
/* Tool-bars. */
DEFSYM (QCimage, ":image");
DEFSYM (Qhelp_echo, "help-echo");
=== modified file 'src/lisp.h'
--- src/lisp.h 2014-08-25 05:44:57 +0000
+++ src/lisp.h 2014-08-25 14:11:23 +0000
@@ -4093,6 +4093,7 @@
extern Lisp_Object Qup, Qdown;
extern Lisp_Object last_undo_boundary;
extern bool input_pending;
+extern sys_jmp_buf return_to_command_loop;
extern Lisp_Object menu_bar_items (Lisp_Object);
extern Lisp_Object tool_bar_items (Lisp_Object, int *);
extern void discard_mouse_events (void);
=== modified file 'src/sysdep.c'
--- src/sysdep.c 2014-08-25 05:44:57 +0000
+++ src/sysdep.c 2014-08-25 14:43:34 +0000
@@ -46,7 +46,6 @@
# include <sys/user.h>
# undef frame
-# include <sys/resource.h>
# include <math.h>
#endif
@@ -72,6 +71,7 @@
#include "msdos.h"
#endif
+#include <sys/resource.h>
#include <sys/param.h>
#include <sys/file.h>
#include <fcntl.h>
@@ -1716,6 +1716,31 @@
xsignal0 (Qarith_error);
}
+/* Attempt to recover from SIGSEGV caused by C stack overflow. */
+
+static void
+handle_sigsegv (int sig, siginfo_t *siginfo, void *arg)
+{
+ /* Hard GC error may lead to C stack overflow caused by
+ too nested calls to mark_object. No way to survive. */
+ if (!gc_in_progress)
+ {
+ struct rlimit rlim;
+ unsigned long used;
+ enum { STACK_EXTRA = 16 * 1024 };
+ char *fault_addr = (char *) siginfo->si_addr;
+
+ getrlimit (RLIMIT_STACK, &rlim);
+ if (fault_addr > stack_bottom)
+ used = fault_addr - stack_bottom;
+ else
+ used = stack_bottom - fault_addr;
+ if (used + STACK_EXTRA > rlim.rlim_cur)
+ /* Most likely this is it. */
+ sys_longjmp (return_to_command_loop, 1);
+ }
+}
+
static void
deliver_arith_signal (int sig)
{
@@ -1761,7 +1786,9 @@
init_signals (bool dumping)
{
struct sigaction thread_fatal_action;
+ struct sigaction sigsegv_action;
struct sigaction action;
+ stack_t altstack;
sigemptyset (&empty_mask);
@@ -1912,6 +1939,18 @@
if (dumping)
return;
+ /* Alternate stack used to handle SIGSEGV. */
+ altstack.ss_sp = xmalloc (SIGSTKSZ);
+ altstack.ss_size = SIGSTKSZ;
+ altstack.ss_flags = 0;
+ if (sigaltstack (&altstack, NULL) < 0)
+ emacs_abort ();
+
+ /* This is special because we need fault address. */
+ sigfillset (&sigsegv_action.sa_mask);
+ sigsegv_action.sa_sigaction = handle_sigsegv;
+ sigsegv_action.sa_flags = emacs_sigaction_flags () | SA_SIGINFO | SA_ONSTACK;
+
sigfillset (&process_fatal_action.sa_mask);
process_fatal_action.sa_handler = deliver_fatal_signal;
process_fatal_action.sa_flags = emacs_sigaction_flags ();
@@ -1982,7 +2021,7 @@
#ifdef SIGBUS
sigaction (SIGBUS, &thread_fatal_action, 0);
#endif
- sigaction (SIGSEGV, &thread_fatal_action, 0);
+ sigaction (SIGSEGV, &sigsegv_action, 0);
#ifdef SIGSYS
sigaction (SIGSYS, &thread_fatal_action, 0);
#endif
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [RFC] sigaltstack + longjmp to protect from C stack overflow
2014-08-25 14:46 [RFC] sigaltstack + longjmp to protect from C stack overflow Dmitry Antipov
@ 2014-08-25 15:06 ` Paul Eggert
0 siblings, 0 replies; 2+ messages in thread
From: Paul Eggert @ 2014-08-25 15:06 UTC (permalink / raw)
To: Dmitry Antipov, Emacs development discussions
Thanks for looking into this. A few comments:
'handle_sigsegv' tries to guess direction of stack growth, but shouldn't
stack growth direction be determined at startup? That should be more
reliable than guessing from what may be a stray pointer reference.
init_signals need not use xmalloc to allocate the alternate stack, since
it's of fixed size and Emacs always needs it. We should be able to
allocate the stack statically.
sys/resource.h is not available everywhere; include it only if
HAVE_SYS_RESOURCE_H is defined. Similarly, use struct rlimit and
getrlimit only if HAVE_GETRLIMIT is defined, and use sigaltstack only if
HAVE_SIGALTSTACK is defined (you'll need to modify configure.ac for
that). And the code should do the right thing (i.e., stick with
thread_fatal_action instead of sigsegv_action) if SA_SIGINFO and/or
SA_ONSTACK are not defined. Come to think of it, check for all the
above issues and do the sigsegv_action stuff only if all the macros
check out.
There's no need to abort if sigaltstack fails. Just let Emacs go on as
it does now, and use thread_fatal_action.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-08-25 15:06 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-08-25 14:46 [RFC] sigaltstack + longjmp to protect from C stack overflow Dmitry Antipov
2014-08-25 15:06 ` Paul Eggert
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.