commit 9a431e431ad92f94f4290c3f0bf043b0f97a7b56 Author: Stefan Monnier Date: Mon Jul 27 19:19:53 2026 -0400 (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 ). 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 commit eac013a7857079db5c68ef8e8eacc956c2870f6b Author: Stefan Monnier Date: Mon Jul 27 19:12:30 2026 -0400 (custom-initialize-changed): Don't catch errors in `custom-set` * lisp/custom.el (custom-initialize-reset): Simplify. (custom-initialize-changed): Don't catch errors in `custom-set` (bug#81396). (custom-initialize-delay, custom-initialize-after-file-load): Adjust arg name to align with other `custom-initialize-*`. diff --git a/lisp/custom.el b/lisp/custom.el index 59b15032de1..8417dab2fe3 100644 --- a/lisp/custom.el +++ b/lisp/custom.el @@ -108,9 +108,8 @@ or (last of all) the value of EXP." (funcall (or (get symbol 'custom-set) #'set-default-toplevel-value) symbol (condition-case nil - (let ((def (default-toplevel-value symbol)) - (getter (get symbol 'custom-get))) - (if getter (funcall getter symbol) def)) + (funcall (or (get symbol 'custom-get) #'default-toplevel-value) + symbol) (error (eval (let ((sv (get symbol 'saved-value))) (if sv (car sv) exp))))))) @@ -120,26 +119,26 @@ or (last of all) the value of EXP." Like `custom-initialize-reset', but only use the `:set' function if not using the standard setting. For the standard setting, use `set-default-toplevel-value'." - (condition-case nil - (let ((def (default-toplevel-value symbol))) - (funcall (or (get symbol 'custom-set) #'set-default-toplevel-value) - symbol - (let ((getter (get symbol 'custom-get))) - (if getter (funcall getter symbol) def)))) - (error - (cond - ((get symbol 'saved-value) - (funcall (or (get symbol 'custom-set) #'set-default-toplevel-value) - symbol - (eval (car (get symbol 'saved-value))))) - (t - (set-default-toplevel-value symbol (eval exp))))))) + (let ((set-exp + (condition-case nil + (let ((val (funcall (or (get symbol 'custom-get) + #'default-toplevel-value) + symbol))) + (list (list 'quote val))) + (error (get symbol 'saved-value))))) + (cond + (set-exp + (funcall (or (get symbol 'custom-set) #'set-default-toplevel-value) + symbol + (eval (car set-exp)))) + (t + (set-default-toplevel-value symbol (eval exp)))))) (defvar custom-delayed-init-variables nil "List of variables whose initialization is pending until startup. Once this list has been processed, this var is set to a non-list value.") -(defun custom-initialize-delay (symbol value) +(defun custom-initialize-delay (symbol exp) ;; FIXME: Rename to `custom-initialize-after-dump'? "Delay initialization of SYMBOL to the next Emacs start. This is used in files that are preloaded (or for autoloaded @@ -152,15 +151,15 @@ the :set function." ;; Until the var is actually initialized, it is kept unbound. ;; This seemed to be at least as good as setting it to an arbitrary - ;; value like nil (evaluating `value' is not an option because it + ;; value like nil (evaluating `exp' is not an option because it ;; may have undesirable side-effects). (if (listp custom-delayed-init-variables) (push symbol custom-delayed-init-variables) ;; In case this is called after startup, there is no "later" to which to ;; delay it, so initialize it "normally" (bug#47072). - (custom-initialize-reset symbol value))) + (custom-initialize-reset symbol exp))) -(defun custom-initialize-after-file-load (symbol value) +(defun custom-initialize-after-file-load (symbol exp) "Delay initialization to after the current file is loaded. This is handy when the initialization needs functions defined after the variable, such as for global minor modes." @@ -169,16 +168,16 @@ variable, such as for global minor modes." ;; Until the var is actually initialized, it is kept unbound. ;; This seemed to be at least as good as setting it to an arbitrary - ;; value like nil (evaluating `value' is not an option because it + ;; value like nil (evaluating `exp' is not an option because it ;; may have undesirable side-effects). (if (not load-file-name) ;; There's no "after file" to speak of. - (custom-initialize-set symbol value) + (custom-initialize-set symbol exp) (let ((thisfile load-file-name)) (letrec ((f (lambda (file) (when (equal file thisfile) (remove-hook 'after-load-functions f) - (custom-initialize-set symbol value))))) + (custom-initialize-set symbol exp))))) (add-hook 'after-load-functions f))))) (defun custom-declare-variable (symbol default doc &rest args) commit 75441af2c5a9168114836ec8c4e2cfd80ed2f1c5 Author: Stephen Berman Date: Mon Jul 27 13:55:04 2026 +0200 ; Fix last change to 'insert-directory' * lisp/files.el (insert-directory): Reuse an existing "*ls error*" buffer or, if there is none, generate a new one and make it read-only (bug#80499, Message #305). diff --git a/lisp/files.el b/lisp/files.el index 27a69b3fc81..9d5cd3fa30d 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -8502,9 +8502,12 @@ normally equivalent short `-D' option is just passed on to ;; error. (when (> (file-attribute-size (file-attributes errfile)) 0) (defvar dired--ls-error-buffer) ; Pacify byte-compiler. - (let ((errbuf (get-buffer-create "*ls error*"))) + (let ((errbuf (or (get-buffer "*ls error*") + (let ((buf (generate-new-buffer "*ls error*"))) + (with-current-buffer buf + (setq buffer-read-only t)) + buf)))) (with-current-buffer errbuf - (setq buffer-read-only t) (let ((inhibit-read-only t)) (erase-buffer) (insert-file-contents errfile)))