]> git.eshelyaron.com Git - emacs.git/commitdiff
Avoid memory corruption with specpdl overflow + edebug (Bug#30481)
authorNoam Postavsky <npostavs@gmail.com>
Fri, 16 Feb 2018 03:13:51 +0000 (22:13 -0500)
committerNoam Postavsky <npostavs@gmail.com>
Sat, 17 Feb 2018 03:13:34 +0000 (22:13 -0500)
If grow_specpdl fails due to outgrowing max_specpdl_size, it will
signal an error *before* growing the specpdl array.  Therefore, when
handling the signal, specpdl_ptr points past the end of the specpdl
array and any further use of of specpdl before unwinding (e.g., if
edebug binds signal-hook-function) will cause memory corruption.
* src/eval.c (signal_or_quit): Don't call `signal-hook-function' if
the specpdl_ptr is already past the end of the specpdl array.
* test/src/eval-tests.el (eval-tests--exceed-specbind-limit)
(eval-exceed-specbind-with-signal-hook): New test & helper function.

src/eval.c
test/src/eval-tests.el

index e05a17f7b4b208e3a2630ca7f1a1f6620ce43cb7..ca1eb84ff3faa87e18889c676c3516341ef5d67a 100644 (file)
@@ -1553,7 +1553,10 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
 
   /* This hook is used by edebug.  */
   if (! NILP (Vsignal_hook_function)
-      && ! NILP (error_symbol))
+      && ! NILP (error_symbol)
+      /* Don't try to call a lisp function if we've already overflowed
+         the specpdl stack.  */
+      && specpdl_ptr < specpdl + specpdl_size)
     {
       /* Edebug takes care of restoring these variables when it exits.  */
       if (lisp_eval_depth + 20 > max_lisp_eval_depth)
index 201382da9c4545513b55318e27ff20a856e910f7..e68fd136113e94a6b5adbb07fca65b799ceb6748 100644 (file)
@@ -79,4 +79,24 @@ Bug#24912 and Bug#24913."
   (let ((clauses (list '((progn (setcdr clauses "ouch") nil)))))
     (should-error (eval (cons 'cond clauses)))))
 
+(defun eval-tests--exceed-specbind-limit ()
+  (defvar eval-tests--var1)
+  (defvar eval-tests--var2)
+  ;; Bind two variables, to make extra sure we hit the
+  ;; `max-specpdl-size' limit before the `max-lisp-eval-depth' limit.
+  (let ((eval-tests--var1 1)
+        (eval-tests--var2 2))
+    ;; Recurse until we hit the limit.
+    (eval-tests--exceed-specbind-limit)))
+
+(ert-deftest eval-exceed-specbind-with-signal-hook ()
+  "Test for Bug#30481.
+Check that Emacs doesn't crash when exceeding specbind limit with
+`signal-hook-function' bound.  NOTE: Without the fix for
+Bug#30481, this test can appear to pass, but cause a
+crash/abort/malloc assert failure on the next test."
+  (let ((max-specpdl-size (/ max-lisp-eval-depth 2))
+        (signal-hook-function #'ignore))
+    (should-error (eval-tests--exceed-specbind-limit))))
+
 ;;; eval-tests.el ends here