[PATCH 01/24] vt: serialise the producers of the flip buffer

From: Greg Kroah-Hartman

Date: Tue Oct 27 2026 - 07:12:57 EST


From: Chris Mason <mason@xxxxxxxxxx>

Typing on a virtual console while the console replies to an escape
sequence can make the kernel write past the end of a buffer. A user who
can write to the foreground console can cause this on purpose if they
can also inject key events, through /dev/uinput or by writing to the
/dev/input/event* node of a keyboard.

Input for a tty goes through the flip buffer of its port. A driver
inserts the data with tty_insert_flip_*() and then calls
tty_flip_buffer_push() to pass it on to the line discipline. The insert
functions take no lock: they check that the current struct tty_buffer
has room, copy the data to offset ->used, and then advance ->used. So a
flip buffer supports only one producer at a time, and a driver that has
several must serialise them.

Four functions feed the flip buffer of a virtual console:

- put_queue(), puts_queue() and fn_send_intr() insert typed characters
and breaks for the keyboard handler. They run under kbd_event_lock.
- respond_string() inserts the reply when a program writes an escape
sequence that requests one, such as a cursor position report. It runs
under the console lock.

The keyboard handler gets its events from the input layer and feeds the
foreground console. Key events that a program writes to /dev/uinput or
to an event node reach it in the same way as typed keys.

The keyboard handler and respond_string() share no lock, so they can
interleave:

keyboard handler respond_string()
================ ================
room check passes
room check passes
memcpy() to offset used
used += len
memcpy() to offset used
used += len
/* used > size of the buffer */

The room check subtracts ->used from the size, and both are unsigned.
Once ->used exceeds the size, the result wraps, so the check always
passes, and each later insert lands further past the buffer.

Reproduced as root, with keys from a uinput keyboard and cursor position
reports on the same console: KFENCE reported an out-of-bounds write
1,024 bytes past the buffer in under 10 s.

Fix this by taking port->lock, with interrupts disabled, around the
insert and the push in these four functions. kbd_keycode() already takes
port->lock under kbd_event_lock, so this lock ordering is not new.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable <stable@xxxxxxxxxx>
Cc: Jiri Slaby <jirislaby@xxxxxxxxxx>
Cc: Nicolas Pitre <npitre@xxxxxxxxxxxx>
Cc: Kees Cook <kees+treewide@xxxxxxxxxx>
Cc: Joshua Rogers <linux@xxxxxxxxx>
Cc: Chris Mason <mason@xxxxxxxxxx>
Cc: Helge Deller <deller@xxxxxx>
Cc: Thomas Zimmermann <tzimmermann@xxxxxxx>
Cc: Mingyu Wang <25181214217@xxxxxxxxxxxxxxxxx>
Cc: Randy Dunlap <rdunlap@xxxxxxxxxxxxx>
Cc: Ronan Pigott <ronan@xxxxxx>
Cc: Liav Mordouch <liavmordouch@xxxxxxxxx>
Signed-off-by: Chris Mason <mason@xxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
drivers/tty/vt/keyboard.c | 6 ++++++
drivers/tty/vt/vt.c | 2 ++
2 files changed, 8 insertions(+)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index ef4a19aeb3ce..f4b742e091fb 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -322,15 +322,20 @@ int kbd_rate(struct kbd_repeat *rpt)

/*
* Helper Functions.
+ *
+ * A flip buffer allows one producer at a time, and respond_string() in vt.c
+ * feeds the same one under the console lock. So take the lock of the port.
*/
static void put_queue(struct vc_data *vc, int ch)
{
+ guard(spinlock_irqsave)(&vc->port.lock);
tty_insert_flip_char(&vc->port, ch, 0);
tty_flip_buffer_push(&vc->port);
}

static void puts_queue(struct vc_data *vc, const char *cp)
{
+ guard(spinlock_irqsave)(&vc->port.lock);
tty_insert_flip_string(&vc->port, cp, strlen(cp));
tty_flip_buffer_push(&vc->port);
}
@@ -587,6 +592,7 @@ static void fn_inc_console(struct vc_data *vc)

static void fn_send_intr(struct vc_data *vc)
{
+ guard(spinlock_irqsave)(&vc->port.lock);
tty_insert_flip_char(&vc->port, 0, TTY_BREAK);
tty_flip_buffer_push(&vc->port);
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 8415e274e4be..5c2c761aa0c1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1971,6 +1971,8 @@ static void csi_m(struct vc_data *vc)

static void respond_string(const char *p, size_t len, struct tty_port *port)
{
+ /* The keyboard handler feeds the same flip buffer. */
+ guard(spinlock_irqsave)(&port->lock);
tty_insert_flip_string(port, p, len);
tty_flip_buffer_push(port);
}

--
2.56.0