mirror of https://lore.kernel.org/lkml/
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox