[PATCH] kgdb: Fix buffer overflow in the 'M' and 'X' packet handlers

From: Fang Xieyan

Date: Wed Sep 16 2026 - 04:28:30 EST


write_mem_msg() passes the length of an 'M' or 'X' packet straight to
kgdb_hex2mem() or kgdb_ebin2mem(). Both decode the payload in place at a
pointer into remcom_in_buffer[BUFMAX], and neither checks that the length
fits. A client on the KGDB I/O console can claim any length, regardless of
how much payload it actually sent, so the decode runs off the end of the
buffer and then copies the decoded bytes to the address the client chose.

On x86 BUFMAX is 1024, so a write that claims 512 bytes overruns
remcom_in_buffer during the in-place hex decode:

BUG: KASAN: global-out-of-bounds in kgdb_hex2mem+0x131/0x160
Read of size 1 at addr ffffffff87651bb5 by task sh/1
...
kgdb_hex2mem+0x131/0x160
write_mem_msg+0x308/0x3e0
gdb_serial_stub+0xd8b/0x3490
...
The buggy address belongs to the variable:
remcom_in_buffer+0x415/0x420

Bounds check the length against the space left in the input buffer:

if (length > (BUFMAX - (ptr - remcom_in_buffer)) / 2)
return -EINVAL;

gdb_cmd_memwrite() and gdb_cmd_binwrite() already turn a non-zero return
into an error reply, and a write over the limit never decoded valid data,
so an error reply loses nothing.

Fixes: dc7d55270521 ("kgdb: core")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Hawkeye:GLM-5.3-flash
Assisted-by: Qoder:Qwen3.8-Max
Signed-off-by: Fang Xieyan <fangxy@xxxxxxxxxxxx>
---

This is the write-side sibling of the 'm' memread overflow in
gdb_cmd_memread() (remcom_out_buffer); both handlers trust the same
unvalidated length field. The read-side fix was posted separately
(<20260914180344.15485-1-fangxy@xxxxxxxxxxxx>).

Reproducer: attach to a kgdboc console and send an 'M' packet whose length
is above the (BUFMAX - header) / 2 limit but whose payload is short, e.g.
"M<addr>,200:4142434445464748" on x86. The stub decodes 2 * 512 bytes at
ptr regardless of the 8 bytes sent, so the in-place hex conversion runs off
remcom_in_buffer[1024]. Before this change KASAN reports a
global-out-of-bounds read in kgdb_hex2mem and the guest dies; after it the
stub answers E22 and the guest resumes.

The 'X' (binary) path through kgdb_ebin2mem() needs the same / 2 divisor,
not a looser one. It decodes in place at ptr and reads a second input byte
for every 0x7d escape marker it sees, so a claimed length of N bytes reads
up to 2 * N bytes at ptr, exactly like the hex path. Giving 'X' a divisor
of 1 would let an escaped payload run off remcom_in_buffer, which is the
overflow this patch removes.

Both cases were run on 704340f1cd0d (v7.3-rc4): x86_64 defconfig plus
CONFIG_KASAN_GENERIC, gcc 13.2.0, QEMU under TCG. The two kernels are built
from byte-identical .config files and differ only by this patch.

CONFIG_KGDB_KDB has to be off: dbg_kdb_mode starts at 1, so with kdb built
in a sysrq-g break lands in kdb_stub() and the packet is never parsed.

A minimal over-the-limit length reproduces better than a large one. The
trailing copy_to_kernel_nofault() copies through raw asm accessors that
KASAN does not instrument, so a huge length smashes .bss past the buffer
with no report and the kernel simply stops answering. length = 0x200 keeps
the in-place decode just past remcom_in_buffer[1024], which gives the
report above.

TCG note: an unrelated early-boot "wild-memory-access in _raw_spin_lock"
sometimes fires under TCG + KASAN during identify_cpu and reboots the guest
before it reaches the break-in. That is not this bug; re-running the boot
clears it and the packet then lands as described.

kernel/debug/gdbstub.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index e271a43..a67ca1a 100644
--- a/kernel/debug/gdbstub.c
+++ b/kernel/debug/gdbstub.c
@@ -373,6 +373,12 @@ static int write_mem_msg(int binary)

if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&
kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {
+ /*
+ * The in-place decode touches 2 * length bytes at ptr,
+ * inside remcom_in_buffer[BUFMAX].
+ */
+ if (length > (BUFMAX - (ptr - remcom_in_buffer)) / 2)
+ return -EINVAL;
if (binary)
err = kgdb_ebin2mem(ptr, (char *)addr, length);
else
--
2.50.1 (Apple Git-155)