]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vt: resize saved unicode buffer on alt screen exit after resize
authorNicolas Pitre <nico@fluxnic.net>
Sat, 28 Mar 2026 03:09:47 +0000 (23:09 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 30 Mar 2026 15:39:44 +0000 (17:39 +0200)
Instead of discarding the saved unicode buffer when the console was
resized while in the alternate screen, resize it to the current
dimensions using vc_uniscr_copy_area() to preserve its content. This
properly restores the unicode screen on alt screen exit rather than
lazily rebuilding it from a lossy reverse glyph translation.

On allocation failure the stale buffer is freed and vc_uni_lines is
set to NULL so it gets lazily rebuilt via vc_uniscr_check() when next
needed.

Fixes: 40014493cece ("vt: discard stale unicode buffer on alt screen exit after resize")
Cc: stable <stable@kernel.org>
Signed-off-by: Nicolas Pitre <nico@fluxnic.net>
Link: https://patch.msgid.link/3nsr334n-079q-125n-7807-n4nq818758ns@syhkavp.arg
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/vt/vt.c

index 99f15b3e9544787ada97ce67a362dddee2cf051b..b4b19157f05c2b6f8a0d794f8bcf348d19aa24ff 100644 (file)
@@ -1901,7 +1901,6 @@ static void leave_alt_screen(struct vc_data *vc)
        unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows);
        unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols);
        u16 *src, *dest;
-       bool uni_lines_stale;
 
        if (vc->vc_saved_screen == NULL)
                return; /* Not inside an alt-screen */
@@ -1912,16 +1911,23 @@ static void leave_alt_screen(struct vc_data *vc)
        }
        /*
         * If the console was resized while in the alternate screen,
-        * vc_saved_uni_lines was allocated for the old dimensions.
-        * Restoring it would cause out-of-bounds accesses. Discard it
-        * and let the unicode screen be lazily rebuilt.
+        * resize the saved unicode buffer to the current dimensions.
+        * On allocation failure new_uniscr is NULL, causing the old
+        * buffer to be freed and vc_uni_lines to be lazily rebuilt
+        * via vc_uniscr_check() when next needed.
         */
-       uni_lines_stale = vc->vc_saved_rows != vc->vc_rows ||
-                         vc->vc_saved_cols != vc->vc_cols;
-       if (uni_lines_stale)
+       if (vc->vc_saved_uni_lines &&
+           (vc->vc_saved_rows != vc->vc_rows ||
+            vc->vc_saved_cols != vc->vc_cols)) {
+               u32 **new_uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+
+               if (new_uniscr)
+                       vc_uniscr_copy_area(new_uniscr, vc->vc_cols, vc->vc_rows,
+                                           vc->vc_saved_uni_lines, cols, 0, rows);
                vc_uniscr_free(vc->vc_saved_uni_lines);
-       else
-               vc_uniscr_set(vc, vc->vc_saved_uni_lines);
+               vc->vc_saved_uni_lines = new_uniscr;
+       }
+       vc_uniscr_set(vc, vc->vc_saved_uni_lines);
        vc->vc_saved_uni_lines = NULL;
        restore_cur(vc);
        /* Update the entire screen */