master 9a431e431ad: (Fcall_interactively): Fix use-after-free bug#81110
Stefan Monnier via Mailing list for Emacs changes <[email protected]> Mon, 27 Jul 2026 19:20:11 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: master commit 9a431e431ad92f94f4290c3f0bf043b0f97a7b56 Author: Stefan Monnier <[email protected]> Commit: Stefan Monnier <[email protected]> (Fcall_interactively): Fix use-after-free bug#81110 * src/callint.c (Fcall_interactively): Don't use alloca'd vars after we `unbind_to` and don't bind `current-minibuffer-command`. * src/minibuf.c (read_minibuf): Bind `current-minibuffer-command` here. * test/src/callint-tests.el (test-many-interactive-args): New test (authored by Pip Cet <[email protected]>). --- src/callint.c | 18 ++++++++---------- src/minibuf.c | 4 ++++ test/src/callint-tests.el | 9 +++++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/callint.c b/src/callint.c index 1746dd57704..dd7e546df99 100644 --- a/src/callint.c +++ b/src/callint.c @@ -272,22 +272,17 @@ invoke it (via an `interactive' spec that contains, for instance, an `this-command-keys-vector' is used. */) (Lisp_Object function, Lisp_Object record_flag, Lisp_Object keys) { - specpdl_ref speccount = SPECPDL_INDEX (); - bool arg_from_tty = false; ptrdiff_t key_count; bool record_then_fail = false; + /* FIXME: We should probably specbind these vars in recursive + _edit instead so they're automatically saved&restored as needed. */ Lisp_Object save_this_command = Vthis_command; Lisp_Object save_this_original_command = Vthis_original_command; Lisp_Object save_real_this_command = Vreal_this_command; Lisp_Object save_last_command = KVAR (current_kboard, Vlast_command); - /* Bound recursively so that code can check the current command from - code running from minibuffer hooks (and the like), without being - overwritten by subsequent minibuffer calls. */ - specbind (Qcurrent_minibuffer_command, Vthis_command); - if (NILP (keys)) keys = this_command_keys, key_count = this_command_key_count; else @@ -342,8 +337,7 @@ invoke it (via an `interactive' spec that contains, for instance, an Vreal_this_command = save_real_this_command; kset_last_command (current_kboard, save_last_command); - return unbind_to (speccount, CALLN (Fapply, Qfuncall_interactively, - function, specs)); + return CALLN (Fapply, Qfuncall_interactively, function, specs); } /* SPECS is set to a string; use it as an interactive prompt. @@ -448,6 +442,8 @@ invoke it (via an `interactive' spec that contains, for instance, an memclear (args, nargs * (2 * word_size + 1)); + specpdl_ref speccount = SPECPDL_INDEX (); + if (!NILP (enable)) specbind (Qenable_recursive_minibuffers, Qt); @@ -801,7 +797,9 @@ invoke it (via an `interactive' spec that contains, for instance, an specbind (Qcommand_debug_status, Qnil); Lisp_Object val = Ffuncall (nargs, args); - return SAFE_FREE_UNBIND_TO (speccount, val); + unbind_to (speccount, Qnil); + SAFE_FREE (); + return val; } DEFUN ("prefix-numeric-value", Fprefix_numeric_value, Sprefix_numeric_value, diff --git a/src/minibuf.c b/src/minibuf.c index 8f0297adf0b..c03ac7ac098 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -590,6 +590,10 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, specbind (Qminibuffer_default, defalt); specbind (Qinhibit_read_only, Qnil); + /* Bound recursively so that code can check the current command from + code running from minibuffer hooks (and the like), without being + overwritten by subsequent minibuffer calls. */ + specbind (Qcurrent_minibuffer_command, Vthis_command); /* If Vminibuffer_completing_file_name is `lambda' on entry, it was t in previous recursive minibuffer, but was not set explicitly diff --git a/test/src/callint-tests.el b/test/src/callint-tests.el index a09fb40b3f8..91890500344 100644 --- a/test/src/callint-tests.el +++ b/test/src/callint-tests.el @@ -65,4 +65,13 @@ (should (= (call-interactively 'callint-test-int-args t) 3)) (should (equal command-history '((callint-test-int-args 1 10 11)))))) +(ert-deftest test-many-interactive-args () + "Test that `'call-interactively' does not crash due to bug#81110" + (dotimes (_ 10) + (let ((str (apply #'concat (make-list 4096 "pp\n")))) + (call-interactively (eval `(lambda (&rest args) + (interactive ,str) + (length args)) + t))))) + ;;; callint-tests.el ends here