bug#81562: [PATCH] Restore current_buffer when window_text_pixel_size fails

Maxim Wayne <[email protected]> Wed, 5 Aug 2026 20:26:45 +0800
Newsgroups gmane.emacs.bugs
Message-ID <CADtsJB2b1WTQjLWZJjtpU3ap6h1XKQ6iiPZXf7juUfN5t9Djpw@mail.gmail.com>
The window-text-pixel-size function temporarily switches the
current_buffer pointer; if the function fails, the pointer is not
switched back.
To reproduce: When current_buffer = buf_a while the window displays
buf_b, giving a cons whose cdr is not an integer
as the FROM argument to window-text-pixel-size, the function fails and
current_buffer doesn't switch back to buf_a.
The included test fails on Emacs 30.2 and passes on the patched code.
Please see the attachment for the patch. The patch is against current
master (commit 602cde3).
0001-1.patch (application/octet-stream, 2.9 KB)
From ce3d004ba030d7fb3086842b33f9bb973e8a5e44 Mon Sep 17 00:00:00 2001
From: SkyCanvas <[email protected]>
Date: Tue, 4 Aug 2026 21:17:47 +0800
Subject: [PATCH] Restore current_buffer when window_text_pixel_size fails

In the b != current_buffer branch, if window_text_pixel_size encounters
an error (such as receiving an argument of the wrong type), the
current_buffer pointer remains at b and is not correctly switched back.

The specpdl unwind mechanism effectively resolves this issue. The call
to window_text_pixel_size is now wrapped between two specpdl statements.
This ensures that the current_buffer pointer is restored regardless of
whether the function exits normally or an exception occurs.

* src/xdisp.c (Fwindow_text_pixel_size): Use the unwind mechanism
instead of direct pointer manipulation.

* test/src/xdisp-tests.el
(xdisp-tests-window-text-pixel-size-buffer-restore): Add exception
testing.
---
 src/xdisp.c             | 11 +++++------
 test/src/xdisp-tests.el | 17 +++++++++++++++++
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/src/xdisp.c b/src/xdisp.c
index caea8e7..ada3ef5 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -12107,20 +12107,19 @@ DEFUN ("window-text-pixel-size", Fwindow_text_pixel_size, Swindow_text_pixel_siz
 {
   struct window *w = decode_live_window (window);
   struct buffer *b = XBUFFER (w->contents);
-  struct buffer *old_b = NULL;
   Lisp_Object value;
+  specpdl_ref count = SPECPDL_INDEX ();
 
   if (b != current_buffer)
     {
-      old_b = current_buffer;
+      record_unwind_current_buffer ();
       set_buffer_internal_1 (b);
     }
 
-  value = window_text_pixel_size (window, from, to, x_limit, y_limit, mode_lines,
-				  ignore_line_at_end);
+  value = window_text_pixel_size (window, from, to, x_limit, y_limit,
+				  mode_lines, ignore_line_at_end);
 
-  if (old_b)
-    set_buffer_internal_1 (old_b);
+  unbind_to (count, Qnil);
 
   return value;
 }
diff --git a/test/src/xdisp-tests.el b/test/src/xdisp-tests.el
index 8eb5725..e6da534 100644
--- a/test/src/xdisp-tests.el
+++ b/test/src/xdisp-tests.el
@@ -213,4 +213,21 @@ xdisp-test-format-mode-line
       (should (equal m1 m2))
       (should (equal s1 s2)))))
 
+(ert-deftest xdisp-tests-window-text-pixel-size-buffer-restore ()
+  (let ((buf-a (generate-new-buffer "buf-a"))
+        (buf-b (generate-new-buffer "buf-b")))
+    (unwind-protect
+        (progn
+          (with-current-buffer buf-b
+            (insert "test content for window-text-pixel-size"))
+          (set-window-buffer (selected-window) buf-b)
+          (set-buffer buf-a)
+          (should-error
+           (window-text-pixel-size (selected-window)
+                                   (cons 1 "not-a-fixnum")
+                                   nil nil nil nil nil))
+          (should (eq (current-buffer) buf-a)))
+      (ignore-errors (kill-buffer buf-a))
+      (ignore-errors (kill-buffer buf-b)))))
+
 ;;; xdisp-tests.el ends here
-- 
2.55.0