From 22d59ccad2935b7dea7f818793b739343de433a8 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Chaudhary Date: Wed, 17 Jun 2026 07:19:32 +0530 Subject: [PATCH 1/6] kdb: fix memory leak in kdb_setenv() when environment is full When kdb_setenv() fails to find a free slot in __env[], the function returns KDB_ENVFULL without freeing the previously allocated string buffer 'ep', leaking memory on every failed call. Add the missing kfree(ep) before returning KDB_ENVFULL. Fixes: a30d4ff8193e ("kdb: remove usage of static environment buffer") Signed-off-by: Naveen Kumar Chaudhary Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/mbwhul34esbxkdbmlbjaquv5lgyzznn6pdapohry3lsiidbz6n@zucvm5os363q Signed-off-by: Daniel Thompson --- kernel/debug/kdb/kdb_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index ddce56b47b25..31abaea5454e 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -338,6 +338,7 @@ static int kdb_setenv(const char *var, const char *val) } } + kfree(ep); return KDB_ENVFULL; } From 2a052074582d976bfa81e41a0337d8ae6dd1dc65 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Chaudhary Date: Wed, 17 Jun 2026 07:58:20 +0530 Subject: [PATCH 2/6] kdb: unify CMD_BUFLEN definition into kdb_private.h CMD_BUFLEN was defined separately in kdb_io.c (256) and kdb_main.c (200), causing kdb_main.c to use the wrong size when formatting the prompt string into kdb_prompt_str (which is 256 bytes). Move CMD_BUFLEN (256) into kdb_private.h so all users share a single consistent definition, and remove the local definitions from both files. Fixes: 5d5314d6795f ("kdb: core for kgdb back end (1 of 2)") Signed-off-by: Naveen Kumar Chaudhary Link: https://lore.kernel.org/r/l6b6bomrw3bvkehlv73fyghk6loz6jpdwqk5c4nthnhl7j7kzk@4jjhfpzv5zon Signed-off-by: Daniel Thompson --- kernel/debug/kdb/kdb_io.c | 1 - kernel/debug/kdb/kdb_main.c | 6 ++---- kernel/debug/kdb/kdb_private.h | 3 ++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index c399f11740ef..f5b1b7d4c9c8 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -22,7 +22,6 @@ #include #include "kdb_private.h" -#define CMD_BUFLEN 256 char kdb_prompt_str[CMD_BUFLEN]; int kdb_trap_printk; diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 31abaea5454e..40c332d2179d 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -784,8 +784,6 @@ static int kdb_exec_defcmd(int argc, const char **argv) /* Command history */ #define KDB_CMD_HISTORY_COUNT 32 -#define CMD_BUFLEN 200 /* kdb_printf: max printline - * size == 256 */ static unsigned int cmd_head, cmd_tail; static unsigned int cmdptr; static char cmd_hist[KDB_CMD_HISTORY_COUNT][CMD_BUFLEN]; @@ -1266,8 +1264,8 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, do_full_getstr: /* PROMPT can only be set if we have MEM_READ permission. */ - snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"), - raw_smp_processor_id()); + snprintf(kdb_prompt_str, CMD_BUFLEN, + kdbgetenv("PROMPT"), raw_smp_processor_id()); /* * Fetch command from keyboard diff --git a/kernel/debug/kdb/kdb_private.h b/kernel/debug/kdb/kdb_private.h index 92a28b8ab604..722e8aa50724 100644 --- a/kernel/debug/kdb/kdb_private.h +++ b/kernel/debug/kdb/kdb_private.h @@ -225,7 +225,8 @@ extern void kdb_kbd_cleanup_state(void); #define kdb_kbd_cleanup_state() #endif /* ! CONFIG_KDB_KEYBOARD */ -extern char kdb_prompt_str[]; +#define CMD_BUFLEN 256 +extern char kdb_prompt_str[CMD_BUFLEN]; #define KDB_WORD_SIZE ((int)sizeof(unsigned long)) From 1865f1ea3b54bde68da5a68f0f56fbc1ebf69fbf Mon Sep 17 00:00:00 2001 From: Naveen Kumar Chaudhary Date: Tue, 16 Jun 2026 22:13:36 +0530 Subject: [PATCH 3/6] kgdb: remove unreachable return in kgdb_reenter_check() Remove the dead 'return 1' after panic() in kgdb_reenter_check(). panic() is marked __noreturn so the subsequent return statement can never execute. Signed-off-by: Naveen Kumar Chaudhary Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/uddjzmr3oqbf2ly5iyx4ajomreub7firbrzwinhj35ppix6jlo@tt6ejkxmk4tj Signed-off-by: Daniel Thompson --- kernel/debug/debug_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index b276504c1c6b..1c9bc705c32c 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -555,8 +555,6 @@ static int kgdb_reenter_check(struct kgdb_state *ks) #endif dump_stack(); panic("Recursive entry to debugger"); - - return 1; } NOKPROBE_SYMBOL(kgdb_reenter_check); From ac0159129e2cb99a70c4b80cfbf58d03e092dc7b Mon Sep 17 00:00:00 2001 From: Fang Xieyan Date: Tue, 15 Sep 2026 02:03:44 +0800 Subject: [PATCH 4/6] kgdb: Fix buffer overflow in the 'm' packet handler gdb_cmd_memread() passes the length of the 'm' packet straight to kgdb_mem2hex(), which hex-encodes the reply into remcom_out_buffer[BUFMAX] without checking that the reply fits. A client on the KGDB I/O console can ask for a read of any size, and the hex encoding runs off the end of the buffer. kgdb_mem2hex() takes that length as an int count, so 2^31 truncates to a negative value and copy_from_kernel_nofault() is handed buf + count as its destination. On x86 BUFMAX is 1024, so a read of 512 bytes overruns by the single byte that terminates the hex string: BUG: KASAN: global-out-of-bounds in kgdb_mem2hex+0x1dd/0x230 Write of size 1 at addr ffffffff87651780 by task sh/1 ... The buggy address belongs to the variable: remcom_out_buffer+0x400/0x420 Bounds check the length before the hex encoding: if (length > (BUFMAX - 1) / 2) { error_packet(remcom_out_buffer, -EINVAL); return; } gdbstub_msg_write() already bounds the count it hex-encodes against BUFMAX. Reads over the limit never returned valid data, so an error reply loses nothing. Fixes: dc7d55270521 ("kgdb: core") Cc: stable@vger.kernel.org Assisted-by: Hawkeye:GLM-5.3-flash Assisted-by: Qoder:Qwen3.8-Max Signed-off-by: Fang Xieyan Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20260914180344.15485-1-fangxy@xiaopeng.com Signed-off-by: Daniel Thompson --- kernel/debug/gdbstub.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index e271a436d60e..5774292c93ae 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c @@ -563,6 +563,14 @@ static void gdb_cmd_memread(struct kgdb_state *ks) if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && kgdb_hex2long(&ptr, &length) > 0) { + /* + * The hex-encoded reply needs 2 * length + 1 bytes + * in remcom_out_buffer[BUFMAX]. + */ + if (length > (BUFMAX - 1) / 2) { + error_packet(remcom_out_buffer, -EINVAL); + return; + } err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); if (!err) error_packet(remcom_out_buffer, -EINVAL); From 13962ef7021ef2531a4f6650eca87c363e6c9c32 Mon Sep 17 00:00:00 2001 From: Fang Xieyan Date: Tue, 22 Sep 2026 23:49:07 +0800 Subject: [PATCH 5/6] kgdb: Fix buffer overflow in the 'M' and 'X' packet handlers write_mem_msg() passes the length claimed by an 'M' or 'X' packet directly to kgdb_hex2mem() or kgdb_ebin2mem(). A malformed packet can claim more data than was actually received, causing the decoders to read past remcom_in_buffer. Record the received payload length in get_packet() and use it to bound the decoders. 'M' carries two hex characters per byte, while 'X' carries one byte per output byte except that a 0x7d escape consumes an additional input byte. Pass the packet end to kgdb_ebin2mem() so both reads are checked against the received data. Keep zero-length 'X' packets valid, as GDB uses them to probe binary download support. Fixes: dc7d55270521 ("kgdb: core") Cc: stable@vger.kernel.org Assisted-by: Hawkeye:GLM-5.3-flash Assisted-by: Qoder:Qwen3.8-Max Signed-off-by: Fang Xieyan Link: https://lore.kernel.org/r/20260922154907.90535-1-fangxy@xiaopeng.com Signed-off-by: Daniel Thompson --- kernel/debug/gdbstub.c | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index 5774292c93ae..4102e2e68d80 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c @@ -43,6 +43,8 @@ /* Our I/O buffers. */ static char remcom_in_buffer[BUFMAX]; static char remcom_out_buffer[BUFMAX]; +/* Payload bytes get_packet() stored in remcom_in_buffer, excluding the NUL. */ +static int remcom_in_len; static int gdbstub_use_prev_in_buf; static int gdbstub_prev_in_buf_pos; @@ -87,7 +89,7 @@ static int gdbstub_read_wait(void) } #endif /* scan for the sequence $# */ -static void get_packet(char *buffer) +static int get_packet(char *buffer) { unsigned char checksum; unsigned char xmitcsum; @@ -135,6 +137,8 @@ static void get_packet(char *buffer) } buffer[count] = 0; } while (checksum != xmitcsum); + + return count; } /* @@ -321,16 +325,26 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) * Copy the binary array pointed to by buf into mem. Fix $, #, and * 0x7d escaped with 0x7d. Return -EFAULT on failure or 0 on success. * The input buf is overwritten with the result to write to mem. + * + * buf_end points one past the last byte received for this packet. Each + * 0x7d escape consumes a second input byte, so decoding count bytes can + * read up to 2 * count input bytes; stop at buf_end rather than running + * off the end of remcom_in_buffer. */ -static int kgdb_ebin2mem(char *buf, char *mem, int count) +static int kgdb_ebin2mem(char *buf, char *mem, int count, char *buf_end) { int size = 0; char *c = buf; while (count-- > 0) { + if (buf >= buf_end) + return -EINVAL; c[size] = *buf++; - if (c[size] == 0x7d) + if (c[size] == 0x7d) { + if (buf >= buf_end) + return -EINVAL; c[size] = *buf++ ^ 0x20; + } size++; } @@ -367,16 +381,33 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) static int write_mem_msg(int binary) { char *ptr = &remcom_in_buffer[1]; + char *buf_end = &remcom_in_buffer[remcom_in_len]; unsigned long addr; unsigned long length; int err; if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' && kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') { - if (binary) - err = kgdb_ebin2mem(ptr, (char *)addr, length); - else + /* + * Trust only the bytes actually received, not length: the + * client can claim more payload than it sent. 'M' decodes two + * hex chars per byte in place, so it needs 2 * length bytes at + * ptr; 'X' decodes one byte per byte, and kgdb_ebin2mem() bounds + * its extra 0x7d-escape reads against buf_end itself. + */ + if (ptr >= buf_end) + return -EINVAL; + + if (binary) { + if (length > (unsigned long)(buf_end - ptr)) + return -EINVAL; + err = kgdb_ebin2mem(ptr, (char *)addr, length, buf_end); + } else { + if (length > (unsigned long)(buf_end - ptr) / 2) + return -EINVAL; err = kgdb_hex2mem(ptr, (char *)addr, length); + } + if (err) return err; if (CACHE_FLUSH_IS_SAFE) @@ -993,7 +1024,7 @@ int gdb_serial_stub(struct kgdb_state *ks) /* Clear the out buffer. */ memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); - get_packet(remcom_in_buffer); + remcom_in_len = get_packet(remcom_in_buffer); switch (remcom_in_buffer[0]) { case '?': /* gdbserial status */ From 9ca9e8c857b55a081d5a1f8039b27cbb4d646b49 Mon Sep 17 00:00:00 2001 From: Fang Xieyan Date: Wed, 16 Sep 2026 23:17:53 +0800 Subject: [PATCH 6/6] kgdb: Fix buffer overflow in the qRcmd packet handler gdb_cmd_query() decodes the hex payload of a qRcmd packet (the GDB "monitor" command passthrough to kdb) into remcom_out_buffer and hands it to kdb_parse(). It sizes the decode with strlen() of the payload: int len = strlen(remcom_in_buffer + 6); ... kgdb_hex2mem(remcom_in_buffer + 6, remcom_out_buffer, len); len = len / 2; kgdb_hex2mem() takes count as the number of bytes to produce and decodes them in place at buf, reading and writing 2 * count bytes there. len here is the hex character count, twice the decoded size, so the decode runs over 2 * len bytes at remcom_in_buffer + 6, off the end of the buffer. On x86 BUFMAX is 1024, so a 256-byte monitor command (512 hex characters) overruns remcom_in_buffer during the in-place decode: BUG: KASAN: global-out-of-bounds in kgdb_hex2mem+0x131/0x160 Read of size 1 at addr ffffffff8765ef05 by task sh/1 ... kgdb_hex2mem+0x131/0x160 gdb_serial_stub+0x16d4/0x3170 kgdb_cpu_enter+0xb68/0x1520 ... The buggy address belongs to the variable: remcom_in_buffer+0x405/0x420 Halve len before the call so kgdb_hex2mem() gets the decoded byte count. It then reads the len hex characters already in remcom_in_buffer and writes len / 2 bytes, all inside the buffer, and remcom_out_buffer holds the same command as before. Fixes: a0de055cf613 ("kgdb: gdb "monitor" -> kdb passthrough") Cc: stable@vger.kernel.org Suggested-by: Douglas Anderson Link: https://lore.kernel.org/all/CAD=FV=V1-gNZ8yTsC+D8inePWv=A+9H09hWJojQf5NRpcNvK+Q@mail.gmail.com/ Assisted-by: Qoder:Qwen3.8-Max Signed-off-by: Fang Xieyan Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20260916151753.6722-1-fangxy@xiaopeng.com Signed-off-by: Daniel Thompson --- kernel/debug/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index 4102e2e68d80..fc6a7a8c1f6e 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c @@ -817,9 +817,9 @@ static void gdb_cmd_query(struct kgdb_state *ks) strscpy(remcom_out_buffer, "E01"); break; } + len = len / 2; kgdb_hex2mem(remcom_in_buffer + 6, remcom_out_buffer, len); - len = len / 2; remcom_out_buffer[len++] = 0; kdb_common_init_state(ks);