* 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 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.