summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 13:24:49 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 13:24:49 -1000
commit5fade975d5632280855d03699edd22c0689e3a3f (patch)
tree53d5071d92a69c18bf577829fa30f79a5cd46e0e /kernel
parent9c7e3d59107e2daf204bf6bf87b6c448ca89defe (diff)
parent9ca9e8c857b55a081d5a1f8039b27cbb4d646b49 (diff)
Merge tag 'kgdb-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux
Pull kgdb updates from Daniel Thompson: "Varied updates this cycle. Naveen Kumar Chaudhary fixed a memory leak when the environment is already full, eliminated to weird historical cruft (CMD_BUFLEN having different values in different files) and removed some dead code. Fang Xieyan tackled a family of related buffer overflows in the gdb remote protocol parsing (great fixes from a defensive coding point of view but not security relavent; gdb can already do anything it wants to)" * tag 'kgdb-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/danielt/linux: kgdb: Fix buffer overflow in the qRcmd packet handler kgdb: Fix buffer overflow in the 'M' and 'X' packet handlers kgdb: Fix buffer overflow in the 'm' packet handler kgdb: remove unreachable return in kgdb_reenter_check() kdb: unify CMD_BUFLEN definition into kdb_private.h kdb: fix memory leak in kdb_setenv() when environment is full
Diffstat (limited to 'kernel')
-rw-r--r--kernel/debug/debug_core.c2
-rw-r--r--kernel/debug/gdbstub.c55
-rw-r--r--kernel/debug/kdb/kdb_io.c1
-rw-r--r--kernel/debug/kdb/kdb_main.c7
-rw-r--r--kernel/debug/kdb/kdb_private.h3
5 files changed, 52 insertions, 16 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);
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index e271a436d60e..fc6a7a8c1f6e 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 $<data>#<checksum> */
-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)
@@ -563,6 +594,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);
@@ -778,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);
@@ -985,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 */
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 <linux/kallsyms.h>
#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 ddce56b47b25..40c332d2179d 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;
}
@@ -783,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];
@@ -1265,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))