Avoid memory corruption with specpdl overflow + edebug (Bug#30481)

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.
This commit is contained in:
Noam Postavsky 2018-02-15 22:13:51 -05:00
parent 593bbdaf0b
commit c352434ab8
2 changed files with 24 additions and 1 deletions

View 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)

View 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