* [BUG] csi_J oops on VT write after upgrading to 6.19.10 -- NULL pointer dereference in do_con_write path
@ 2026-03-27 16:00 Liav Mordouch
2026-03-27 16:55 ` [PATCH] vt: discard stale unicode buffer on alt screen exit after resize Liav Mordouch
2026-03-27 17:02 ` [PATCH v2] " Liav Mordouch
0 siblings, 2 replies; 4+ messages in thread
From: Liav Mordouch @ 2026-03-27 16:00 UTC (permalink / raw)
To: stable; +Cc: gregkh, npitre, linux-kernel
After upgrading from 6.19.9 to 6.19.10, I'm getting a kernel oops in csi_J()
every time something writes a CSI J (clear screen) escape sequence to a VT
console. In my case it's greetd/tuigreet running `clear` on tty1 at login,
but the crash is in the kernel VT code itself, not specific to greetd.
The result is a completely black screen with no keyboard input accepted -- can't
switch VTs, can't do anything besides a hard power off. It happens consistently
on 4 out of 5 boots with 6.19.10. The one boot that didn't crash was when I
manually switched to tty2 before tuigreet had a chance to write to tty1.
Reproducer:
1. Boot 6.19.10
2. Have a login manager (or anything, really) send a clear/CSI J sequence to a VT
3. Kernel oops in csi_J, system is bricked until reboot
I bisected across my boot history using journalctl -- 6.19.9 is fine:
Kernel Boots checked csi_J crashes
6.19.8 2 0
6.19.9 24 0
6.19.10 5 4
The 6.19.10 changelog includes a backport of 5eb608319bb5 ("vt: save/restore
unicode screen buffer for alternate screen"), which is a fix for 23743ba64709
("vt: add support for smput/rmput escape codes"). That commit modifies
vc_uni_lines handling and adds vc_saved_uni_lines for alternate screen
save/restore. I suspect the backport doesn't apply cleanly or has a missing
dependency -- the faulting address (0x0000002000000020 in RDI during a rep stosd
in csi_J) looks like a corrupted vc_uni_lines pointer.
6.19.9 does not contain this commit and works perfectly.
System:
Gentoo Linux, AMD Ryzen 5 5600X, AMD RX 7800 XT (amdgpu)
Gigabyte B450M DS3H V2, BIOS F65b
Boot cmdline: BOOT_IMAGE=/boot/kernel-6.19.10-gentoo-dist root=UUID=... ro zswap.enabled=1 zswap.compressor=lz4 amdgpu.ppfeaturemask=0xffffffff
Oops from boot -1 (journalctl -b -1):
Oops: Oops: 0002 [#1] SMP NOPTI
CPU: 11 UID: 0 PID: 1037 Comm: greetd Tainted: G S 6.19.10-gentoo-dist #1 PREEMPT(full)
Tainted: [S]=CPU_OUT_OF_SPEC
Hardware name: Gigabyte Technology Co., Ltd. B450M DS3H V2/B450M DS3H V2, BIOS F65b 09/20/2023
RIP: 0010:csi_J+0x133/0x2d0
Code: a4 01 00 00 b8 20 00 00 00 f3 ab 83 fa 01 74 25 48 c1 e2 03 be 08 00 00 00 48 8b 8b 30 03 00 00 48 8b 3c 31 8b 8b a4 01 00 00 <f3> ab 48 83 c6 08 48 39 d6 75 e4 8b 93 a8 01 00 00 0f af 93 a4 01
RSP: 0018:ffffd34941133988 EFLAGS: 00010283
RAX: 0000000000000020 RBX: ffff8b1fc034b800 RCX: 00000000000000f0
RDX: 0000000000000218 RSI: 00000000000000c8 RDI: 0000002000000020
RBP: 0000000000000007 R08: 00000000ffffffff R09: ffff8b1fc034b800
R10: 0000000000000000 R11: ffff8b1ffc17cf7f R12: 000000000000004a
R13: 000000000000004a R14: ffff8b20133c7407 R15: ffff8b1fc034b800
FS: 00007fac7b0e11c0(0000) GS:ffff8b234ce30000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000002000000020 CR3: 0000000108854000 CR4: 0000000000f50ef0
PKRU: 55555554
Call Trace:
<TASK>
do_con_write+0x34c/0x5b0
con_write+0x16/0x50
process_output_block+0x82/0x1a0
n_tty_write+0x1ae/0x3f0
iterate_tty_write+0x116/0x240
file_tty_write.isra.0+0x86/0xb0
vfs_write+0x25d/0x480
ksys_write+0x73/0xf0
do_syscall_64+0x7e/0x6b0
entry_SYSCALL_64_after_hwframe+0x76/0x7e
This then cascades into a second oops during cleanup (NULL pointer deref at
0x2, RIP: 0010:0x2) followed by "Fixing recursive fault but reboot is needed!"
and a "BUG: scheduling while atomic" -- at that point the VT subsystem is
completely dead.
Same crash reproduced on a separate boot (boot -3, different PID):
Oops: Oops: 0002 [#1] SMP NOPTI
CPU: 11 UID: 0 PID: 2485 Comm: greetd Tainted: G S 6.19.10-gentoo-dist #1 PREEMPT(full)
RIP: 0010:csi_J+0x133/0x2d0
RDI: 0000002000000020 (same bogus pointer)
Call Trace:
do_con_write+0x34c/0x5b0
con_write+0x16/0x50
process_output_block+0x82/0x1a0
(same stack)
Workaround: boot 6.19.9 instead.
Note: writing of this report was assisted by AI for grammar and flow.
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] vt: discard stale unicode buffer on alt screen exit after resize
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
2026-03-27 17:02 ` [PATCH v2] " Liav Mordouch
1 sibling, 0 replies; 4+ messages in thread
From: Liav Mordouch @ 2026-03-27 16:55 UTC (permalink / raw)
To: stable; +Cc: gregkh, npitre, linux-kernel
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 */
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2] vt: discard stale unicode buffer on alt screen exit after resize
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 ` [PATCH] vt: discard stale unicode buffer on alt screen exit after resize Liav Mordouch
@ 2026-03-27 17:02 ` Liav Mordouch
2026-03-27 17:26 ` Nicolas Pitre
1 sibling, 1 reply; 4+ messages in thread
From: Liav Mordouch @ 2026-03-27 17:02 UTC (permalink / raw)
To: stable; +Cc: gregkh, npitre, linux-kernel
When enter_alt_screen() saves vc_uni_lines into vc_saved_uni_lines and
sets vc_uni_lines to NULL, a subsequent console resize via vc_do_resize()
skips reallocating the unicode buffer because vc_uni_lines is NULL.
However, vc_saved_uni_lines still points to the old buffer allocated for
the original dimensions.
When leave_alt_screen() later restores vc_saved_uni_lines, the buffer
dimensions no longer match vc_rows/vc_cols. Any operation that iterates
over the unicode buffer using the current dimensions (e.g. csi_J clearing
the screen) will access memory out of bounds, causing a kernel oops:
BUG: unable to handle page fault for address: 0x0000002000000020
RIP: 0010:csi_J+0x133/0x2d0
The faulting address 0x0000002000000020 is two adjacent u32 space
characters (0x20) interpreted as a pointer, read from the row data area
past the end of the 25-entry pointer array in a buffer allocated for
80x25 but accessed with 240x67 dimensions.
Fix this by checking whether the console dimensions changed while in the
alternate screen. If they did, free the stale saved buffer instead of
restoring it. The unicode screen will be lazily rebuilt via
vc_uniscr_check() when next needed.
Fixes: 5eb608319bb5 ("vt: save/restore unicode screen buffer for alternate screen")
Cc: stable@vger.kernel.org
Tested-by: Liav Mordouch <liavmordouch@gmail.com>
Signed-off-by: Liav Mordouch <liavmordouch@gmail.com>
---
v1 -> v2: Reformatted as a proper patch with commit message, Fixes tag,
and Signed-off-by. v1 was sent as an inline analysis + diff.
Note: writing of this patch and analysis was assisted by AI for grammar
and flow. Apologies 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 */
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] vt: discard stale unicode buffer on alt screen exit after resize
2026-03-27 17:02 ` [PATCH v2] " Liav Mordouch
@ 2026-03-27 17:26 ` Nicolas Pitre
0 siblings, 0 replies; 4+ messages in thread
From: Nicolas Pitre @ 2026-03-27 17:26 UTC (permalink / raw)
To: Liav Mordouch; +Cc: stable, gregkh, linux-kernel
On Fri, 27 Mar 2026, Liav Mordouch wrote:
> When enter_alt_screen() saves vc_uni_lines into vc_saved_uni_lines and
> sets vc_uni_lines to NULL, a subsequent console resize via vc_do_resize()
> skips reallocating the unicode buffer because vc_uni_lines is NULL.
> However, vc_saved_uni_lines still points to the old buffer allocated for
> the original dimensions.
>
> When leave_alt_screen() later restores vc_saved_uni_lines, the buffer
> dimensions no longer match vc_rows/vc_cols. Any operation that iterates
> over the unicode buffer using the current dimensions (e.g. csi_J clearing
> the screen) will access memory out of bounds, causing a kernel oops:
>
> BUG: unable to handle page fault for address: 0x0000002000000020
> RIP: 0010:csi_J+0x133/0x2d0
>
> The faulting address 0x0000002000000020 is two adjacent u32 space
> characters (0x20) interpreted as a pointer, read from the row data area
> past the end of the 25-entry pointer array in a buffer allocated for
> 80x25 but accessed with 240x67 dimensions.
>
> Fix this by checking whether the console dimensions changed while in the
> alternate screen. If they did, free the stale saved buffer instead of
> restoring it. The unicode screen will be lazily rebuilt via
> vc_uniscr_check() when next needed.
>
> Fixes: 5eb608319bb5 ("vt: save/restore unicode screen buffer for alternate screen")
> Cc: stable@vger.kernel.org
> Tested-by: Liav Mordouch <liavmordouch@gmail.com>
> Signed-off-by: Liav Mordouch <liavmordouch@gmail.com>
Reviewed-by: Nicolas Pitre <nico@fluxnic.net>
> ---
> v1 -> v2: Reformatted as a proper patch with commit message, Fixes tag,
> and Signed-off-by. v1 was sent as an inline analysis + diff.
>
> Note: writing of this patch and analysis was assisted by AI for grammar
> and flow. Apologies 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 */
>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-03-27 17:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH] vt: discard stale unicode buffer on alt screen exit after resize Liav Mordouch
2026-03-27 17:02 ` [PATCH v2] " Liav Mordouch
2026-03-27 17:26 ` Nicolas Pitre
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.