From c352434ab89617b48c7c1f29342a22e5a5685504 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Thu, 15 Feb 2018 22:13:51 -0500 Subject: [PATCH] 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. --- src/eval.c | 5 ++++- test/src/eval-tests.el | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index e05a17f7b4b..ca1eb84ff3f 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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) diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el index 201382da9c4..e68fd136113 100644 --- a/test/src/eval-tests.el +++ b/test/src/eval-tests.el @@ -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 -- 2.39.2