all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* [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.