unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* eval proposals
@ 2014-06-06 15:33 Dmitry Antipov
  2014-06-06 16:17 ` Paul Eggert
  2014-06-06 16:58 ` Stefan Monnier
  0 siblings, 2 replies; 5+ messages in thread
From: Dmitry Antipov @ 2014-06-06 15:33 UTC (permalink / raw)
  To: Emacs development discussions

[-- Attachment #1: Type: text/plain, Size: 918 bytes --]

I would like to propose and discuss two small features below:

1) Protection against C stack overflow caused by enormously huge
    `max-lisp-eval-depth'.  Note that the stack size limit is examined
    just once (because doing it each time when eval_sub or funcall is
    called introduces too much overhead), but some OSes are capable to
    change this limit when the program is running (prlimit on Linux,
    for example).

2) Capability to shrink specpdl stack if it becomes too large.  When
    `max-specpdl-size' is 83200000 and `max-lisp-eval-depth' is 640000,
    this extreme example with 10K let bindings:

    (defun f ()
      (let ((x0 0)
            (x1 1)
            ...
            (x9999 9999)) (f)))

    creates 73622255-slots specpdl stack before running out of C stack
    on my system, which results in 2.5G RSS.  And currently there is no
    way to reduce it back to reasonable size.

Dmitry

[-- Attachment #2: eval.patch --]
[-- Type: text/x-patch, Size: 3527 bytes --]

=== modified file 'src/alloc.c'
--- src/alloc.c	2014-06-02 00:18:22 +0000
+++ src/alloc.c	2014-06-06 15:14:47 +0000
@@ -5600,6 +5600,7 @@
   block_input ();
 
   shrink_regexp_cache ();
+  shrink_specpdl ();
 
   gc_in_progress = 1;
 

=== modified file 'src/eval.c'
--- src/eval.c	2014-02-10 09:48:17 +0000
+++ src/eval.c	2014-06-06 15:14:15 +0000
@@ -33,6 +33,10 @@
 #include "xterm.h"
 #endif
 
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
 /* Chain of condition and catch handlers currently in effect.  */
 
 struct handler *handlerlist;
@@ -240,6 +244,34 @@
 
 static struct handler handlerlist_sentinel;
 
+/* C stack overflow protection.  This should protect from SIGSEGV when
+   `max-lisp-eval-depth' is too large.  Note that it doesn't protect
+   from other kinds of overflows, e.g. too deep recursion in mark_object. */
+
+#if defined (HAVE_GETRLIMIT) && defined (RLIMIT_STACK)
+
+/* Extra stack space to reserve.  */
+#define STACK_EXTRA (64 * 1024)
+
+/* Current C stack slimit.  */
+static struct rlimit stack_limit;
+
+#define STACK_GUARD() do {					\
+    ptrdiff_t stack_size;					\
+    if ((char *) &stack_size < stack_bottom)			\
+      stack_size = stack_bottom - (char *) &stack_size;	\
+    else							\
+      stack_size = (char *) &stack_size - stack_bottom;	\
+    if (stack_size + STACK_EXTRA > stack_limit.rlim_cur)	\
+      error ("Attempt to overflow C stack");			\
+  } while (0)
+
+#else /* !HAVE_GETRLIMIT || !RLIMIT_STACK */
+
+#define STACK_GUARD() ((void) 0)
+
+#endif /* HAVE_GETRLIMIT && RLIMIT_STACK */
+
 void
 init_eval (void)
 {
@@ -262,6 +294,10 @@
 #endif
   /* This is less than the initial value of num_nonmacro_input_events.  */
   when_entered_debugger = -1;
+#if defined (HAVE_GETRLIMIT) && defined (RLIMIT_STACK)
+  if (getrlimit (RLIMIT_STACK, &stack_limit))
+    emacs_abort ();
+#endif /* HAVE_GETRLIMIT && RLIMIT_STACK */
 }
 
 /* Unwind-protect function used by call_debugger.  */
@@ -2039,6 +2075,28 @@
     }
 }
 
+/* If the specpdl stack is larger than number of currently used
+   slots multiplied by SPECPDL_SHRINK_FACTOR, shrink it to this
+   size.  Called by GC.  */
+
+void
+shrink_specpdl (void)
+{
+  enum { SPECPDL_SHRINK_FACTOR = 50 };
+  ptrdiff_t newsize = (specpdl_ptr - specpdl) * SPECPDL_SHRINK_FACTOR;
+
+  if (newsize < specpdl_size)
+    {
+      ptrdiff_t count = SPECPDL_INDEX ();
+      union specbinding *pdlvec = specpdl - 1;
+
+      pdlvec = xrealloc (pdlvec, (newsize + 1) * sizeof *specpdl);
+      specpdl = pdlvec + 1;
+      specpdl_size = newsize;
+      specpdl_ptr = specpdl + count;
+    }
+}
+
 void
 record_in_backtrace (Lisp_Object function, Lisp_Object *args, ptrdiff_t nargs)
 {
@@ -2060,6 +2118,8 @@
   Lisp_Object funcar;
   struct gcpro gcpro1, gcpro2, gcpro3;
 
+  STACK_GUARD ();
+
   if (SYMBOLP (form))
     {
       /* Look up its binding in the lexical environment.
@@ -2749,6 +2809,8 @@
   register Lisp_Object *internal_args;
   ptrdiff_t i;
 
+  STACK_GUARD ();
+
   QUIT;
 
   if (++lisp_eval_depth > max_lisp_eval_depth)

=== modified file 'src/lisp.h'
--- src/lisp.h	2014-06-02 06:08:49 +0000
+++ src/lisp.h	2014-06-06 15:13:23 +0000
@@ -3907,6 +3907,7 @@
 extern _Noreturn void xsignal3 (Lisp_Object, Lisp_Object, Lisp_Object,
 				Lisp_Object);
 extern _Noreturn void signal_error (const char *, Lisp_Object);
+extern void shrink_specpdl (void);
 extern Lisp_Object eval_sub (Lisp_Object form);
 extern Lisp_Object apply1 (Lisp_Object, Lisp_Object);
 extern Lisp_Object call0 (Lisp_Object);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: eval proposals
  2014-06-06 15:33 eval proposals Dmitry Antipov
@ 2014-06-06 16:17 ` Paul Eggert
  2014-06-06 16:59   ` Eli Zaretskii
  2014-06-06 17:14   ` Stefan Monnier
  2014-06-06 16:58 ` Stefan Monnier
  1 sibling, 2 replies; 5+ messages in thread
From: Paul Eggert @ 2014-06-06 16:17 UTC (permalink / raw)
  To: Dmitry Antipov, Emacs development discussions

Dmitry Antipov wrote:

> 1) Protection against C stack overflow caused by enormously huge
>     `max-lisp-eval-depth'.

Can't we rely on the virtual memory hardware to have a guard page, so 
that Emacs doesn't need to check for stack overflow in software?  That 
is, Emacs could trap the SIGSEGV and do the right thing.

Perhaps we do need a software check on some platforms, but in that case 
'stack_guard' should be a static function, not a macro.  Also, it does 
some computation that could be done once, so that checking for stack 
overflow is just a compare-and-branch.

> 2) Capability to shrink specpdl stack if it becomes too large.

Multiplying (specpdl_ptr - specpdl) by SPECPDL_SHRINK_FACTOR could have 
problems if the multiplication overflows.  Choose a power-of-two 
SPECPDL_SHRINK_FACTOR so that you can do the overflow test quickly by 
division (unsigned right shift) instead.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: eval proposals
  2014-06-06 15:33 eval proposals Dmitry Antipov
  2014-06-06 16:17 ` Paul Eggert
@ 2014-06-06 16:58 ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Stefan Monnier @ 2014-06-06 16:58 UTC (permalink / raw)
  To: Dmitry Antipov; +Cc: Emacs development discussions

> 1) Protection against C stack overflow caused by enormously huge
>    `max-lisp-eval-depth'.  Note that the stack size limit is examined
>    just once (because doing it each time when eval_sub or funcall is
>    called introduces too much overhead), but some OSes are capable to
>    change this limit when the program is running (prlimit on Linux,
>    for example).

max-lisp-eval-depth exists in large part in order to avoid such stack
overflows, so if we do like you suggest, we might want to get rid
of max-lisp-eval-depth (tho this would have the disadvantage that
infinite-recursion would get caught much later).

Also we want some way to get into the debugger when hitting that error,
which requires more stack space.

Doing both max-lisp-eval-depth and stack_guard at every eval_sub and
Ffuncall sounds too costly.

Maybe a better mechanism would be one that checks the value of
max-lisp-eval-depth (which only needs to be done when max-lisp-eval-depth
changes, which is very rare).  Of course, the correlation between
lisp_eval_depth and actual stack depth is not perfect, but it should be
good enough if we include enough fudge factor.

> 2) Capability to shrink specpdl stack if it becomes too large.
>    When `max-specpdl-size' is 83200000 and `max-lisp-eval-depth' is
>    640000, this extreme example with 10K let bindings:
>
>    (defun f ()
>      (let ((x0 0)
>            (x1 1)
>            ...
>            (x9999 9999)) (f)))
>
>    creates 73622255-slots specpdl stack before running out of C stack
>    on my system, which results in 2.5G RSS.  And currently there is no
>    way to reduce it back to reasonable size.

I'm not necessarily opposed to shrinking the specpdl, but this example
is *really* not compelling:
- The code doesn't look like anything I've seen.
- max-specpdl-size defaults to 1300, IIRC, which is pretty damn very far
  from 83200000.
- max-lisp-eval-depth defaults to 600 IIRC, which is pretty damn very far
  from 640000.
- Your example runs out of C stack, so it doesn't work anyway.


        Stefan



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: eval proposals
  2014-06-06 16:17 ` Paul Eggert
@ 2014-06-06 16:59   ` Eli Zaretskii
  2014-06-06 17:14   ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Eli Zaretskii @ 2014-06-06 16:59 UTC (permalink / raw)
  To: Paul Eggert; +Cc: dmantipov, emacs-devel

> Date: Fri, 06 Jun 2014 09:17:13 -0700
> From: Paul Eggert <eggert@cs.ucla.edu>
> 
> Dmitry Antipov wrote:
> 
> > 1) Protection against C stack overflow caused by enormously huge
> >     `max-lisp-eval-depth'.
> 
> Can't we rely on the virtual memory hardware to have a guard page, so 
> that Emacs doesn't need to check for stack overflow in software?

At least on Windows, this is what happens on a stack overflow.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: eval proposals
  2014-06-06 16:17 ` Paul Eggert
  2014-06-06 16:59   ` Eli Zaretskii
@ 2014-06-06 17:14   ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Stefan Monnier @ 2014-06-06 17:14 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Dmitry Antipov, Emacs development discussions

> Choose a power-of-two SPECPDL_SHRINK_FACTOR so that you can do the
> overflow test quickly by division (unsigned right shift) instead.

IIUC this is done once per GC, so speed of this division should be
a complete non-issue.


        Stefan



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-06-06 17:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-06 15:33 eval proposals Dmitry Antipov
2014-06-06 16:17 ` Paul Eggert
2014-06-06 16:59   ` Eli Zaretskii
2014-06-06 17:14   ` Stefan Monnier
2014-06-06 16:58 ` Stefan Monnier

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).