All of lore.kernel.org
 help / color / mirror / Atom feed
From: Liav Mordouch <liavmordouch@gmail.com>
To: stable@vger.kernel.org
Cc: gregkh@linuxfoundation.org, npitre@baylibre.com,
	linux-kernel@vger.kernel.org
Subject: [PATCH] vt: discard stale unicode buffer on alt screen exit after resize
Date: Fri, 27 Mar 2026 19:55:18 +0300	[thread overview]
Message-ID: <20260327165519.15969-1-liavmordouch@gmail.com> (raw)
In-Reply-To: <20260327160050.31631-1-liavmordouch@gmail.com>

Hi,

Following up on the bug report I sent earlier. I've done some further digging
and found the root cause, and I'm including a patch that fixes it. I've tested
it and confirmed it resolves the crash on my system.

Root cause analysis:

The crash I reported in csi_J is caused by a size mismatch between
vc_uni_lines and the actual console dimensions. Here's what happens:

  1. Console starts at 80x25. vc_uni_lines is allocated for 80x25
     (25 pointers + 25*80 u32s, contiguous).

  2. Something enters the alternate screen (\033[?1049h). enter_alt_screen()
     saves vc_uni_lines (80x25) into vc_saved_uni_lines and sets
     vc_uni_lines = NULL.

  3. Console gets resized to 240x67 (amdgpu fbcon takes over). vc_do_resize()
     checks "if (vc->vc_uni_lines)" -- it's NULL, so it skips reallocation.
     But vc_saved_uni_lines (still 80x25) is never touched by the resize.

  4. Something leaves the alternate screen (\033[?1049l). leave_alt_screen()
     calls vc_uniscr_set(vc, vc->vc_saved_uni_lines), blindly restoring the
     stale 80x25 buffer. Now vc_uni_lines points to an 80x25 allocation but
     vc_rows=67, vc_cols=240.

  5. Next clear screen (csi_J CSI_J_VISIBLE) calls
     vc_uniscr_clear_lines(vc, 0, 67), which iterates 67 rows with
     memset32(..., 240). For rows 0-24, each row only has space for 80 u32s
     so memset32 overflows into adjacent rows. At row 25, vc_uni_lines[25]
     reads from the data area (past the 25-entry pointer array), getting
     0x0000002000000020 (two u32 space values read as a pointer) -- page
     fault, crash.

This is confirmed by the registers from the oops:
  RDI = 0x0000002000000020  (bogus pointer = two space chars)
  RCX = 0xf0 = 240          (vc_cols, count for memset32)
  RSI = 0xc8 = 200 = 25*8   (byte offset = row index 25)
  RDX = 0x218 = 536 = 67*8  (loop end = 67 rows)
  RAX = 0x20                 (space character, fill value)

The same faulting address (0x0000002000000020) showed up across 3 separate
crash boots, which makes sense since the memory layout is deterministic.

The fix: in leave_alt_screen(), check if the console dimensions changed while
in the alternate screen. If they did, discard the stale vc_saved_uni_lines
instead of restoring it. The unicode screen will be lazily rebuilt via
vc_uniscr_check() when next needed. This is consistent with how the regular
screen buffer restore in the same function already handles dimension
mismatches using min(saved_rows, current_rows).

I added a comment in the patch explaining the non-obvious reason for the
conditional -- without it, a future reader would have no context for why
vc_saved_uni_lines isn't unconditionally restored, and might "simplify" the
code back into the buggy version.

Tested on my system (Gentoo, 6.19.10, AMD Ryzen 5 5600X, RX 7800 XT with
amdgpu). Previously crashed 4 out of 5 boots, now boots cleanly with the
patch applied.

Note: writing of this email was assisted by AI for grammar and flow. Sorry in
advance if anything reads off.

---
 drivers/tty/vt/vt.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1907,6 +1907,7 @@
 	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 */
@@ -1915,7 +1916,18 @@
 		dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols;
 		memcpy(dest, src, 2 * cols);
 	}
-	vc_uniscr_set(vc, vc->vc_saved_uni_lines);
+	/*
+	 * 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.
+	 */
+	uni_lines_stale = vc->vc_saved_rows != vc->vc_rows ||
+			  vc->vc_saved_cols != vc->vc_cols;
+	if (uni_lines_stale)
+		vc_uniscr_free(vc->vc_saved_uni_lines);
+	else
+		vc_uniscr_set(vc, vc->vc_saved_uni_lines);
 	vc->vc_saved_uni_lines = NULL;
 	restore_cur(vc);
 	/* Update the entire screen */

  reply	other threads:[~2026-03-27 16:56 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-27 16:00 [BUG] csi_J oops on VT write after upgrading to 6.19.10 -- NULL pointer dereference in do_con_write path Liav Mordouch
2026-03-27 16:55 ` Liav Mordouch [this message]
2026-03-27 17:02 ` [PATCH v2] vt: discard stale unicode buffer on alt screen exit after resize Liav Mordouch
2026-03-27 17:26   ` Nicolas Pitre

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260327165519.15969-1-liavmordouch@gmail.com \
    --to=liavmordouch@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=npitre@baylibre.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.