Unbounded length in m/M GDB commands overflows remcomOutBuffer / reads remcomInBuffer out of bounds
| Field | Value |
|---|---|
| ID | DF-1079 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L |
| CWE | CWE-787 Out-of-bounds Write |
| File | sys/cpu/x86_64/misc/x86_64-gdbstub.c |
| Lines | 313-326 (mem2hex), 339-345 (hex2mem), 383-436 (hexToInt/hexToLong), 584-590 (m), 602-607 (M) |
| Area | cpu/x86_64 (KGDB remote serial stub) |
| Confidence | certain |
| Discovered | 2026-07-14 |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
The m (memory read) and M (memory write) commands parse a caller-supplied length with
hexToInt / hexToLong, which impose no upper bound and can wrap the int via unbounded
left-shifts, then pass that length directly to mem2hex / hex2mem. mem2hex writes
2 * length + 1 bytes into the 400-byte static remcomOutBuffer, and hex2mem reads
2 * length bytes from remcomInBuffer starting at an offset well past the packet start,
with no check that the data fits. A length >= 200 overflows remcomOutBuffer into adjacent
BSS; a large / overflowed length drives an unbounded read / write loop.
Root cause
hexToInt (lines 383-406) and hexToLong (lines 413-436) accumulate
*intValue = (*intValue << 4) | hexValue with no limit on numChars, so an
attacker-controlled hex string of arbitrary length wraps the signed int to any 32-bit
value (signed left-shift overflow is also UB).
The m handler (lines 584-590) passes this length straight to
mem2hex((vm_offset_t)addr, remcomOutBuffer, length). mem2hex (lines 319-326) loops
for (i = 0; i < count; i++) writing two hex chars per byte plus a final null into buf
(remcomOutBuffer, 400 bytes at line 281): for length = 200 it writes 401 bytes (1-byte
overflow), for length = 0x100 it writes 513 bytes, and for a wrapped huge positive like
0x40000000 it writes ~2 GB of ASCII hex into kernel memory starting at remcomOutBuffer.
The M handler (lines 602-607) passes length to hex2mem(ptr, addr, length) where ptr
points well into remcomInBuffer (after M + addr + , + length + :); hex2mem
(lines 339-345) reads 2 * length bytes from ptr with no validation that they lie inside
remcomInBuffer[400] and no validation of hex() return values, so it reads past the
packet's null terminator (and past the buffer entirely for large length) and writes the
resulting bytes to kernel memory.
No length sanity check (positive, <= (BUFMAX - 1) / 2, and within the remaining buffer
for M) exists anywhere on these paths.
Threat model & preconditions
- Attacker position: Reachable only from inside an active KGDB session (root-entered via
debug.enter_debugger=gdb, or a kernel panic withRB_GDBset at boot,sys/platform/pc64/x86_64/db_interface.c:179) by whoever can write to the KGDB serial port. - Privileges gained or impact: That operator already possesses designed arbitrary kernel
read/write via the same
m/Mcommands, so this overflow grants no additional privilege; its marginal impact is additional / uncontrolled kernel memory corruption (BSS trample form, stale-buffer / garbage writes forM) and a hard hang / crash on resume β a denial-of-service on an already-faulted kernel. - Required config or capabilities:
options KGDBandRB_GDBboot flag or rootsysctl debug.enter_debugger=gdbplus write access to the KGDB serial console. - Reachability: KGDB serial session after panic or
debug.enter_debugger=gdb.
Proof of concept
Reproduces against any kernel built with options KGDB and booted with RB_GDB (loader
-g) or after sysctl debug.enter_debuger=gdb (root).
- BSS overflow via
m: connect to the KGDB serial port (e.g.cu -l /dev/cuaU0orgdbtarget remote /dev/ttyU0), and after theT05stop packet send a raw packet reading a valid kernel address with an over-length count, e.g.$mffffffff81000000,100#XX(length 0x100 = 256).mem2hexwrites2*256+1 = 513bytes into the 400-byteremcomOutBuffer, overflowing 113 bytes into adjacent BSS;putpacketthen echoes the overflow back. With length0x7fffffffthe loop attempts ~2 GB of writes. - OOB read via
M: send$Mffffffff81000000,7fffffff:00#XX;hex2memreads2*0x7fffffffbytes starting insideremcomInBuffer, running far past the buffer.
Build & run
A minimal PoC script opens the serial device, waits for the leading $T stop packet, ACKs
with +, then writes the crafted $m...,100# packet and reads the oversized reply.
# As root, set up KGDB entry: sudo sysctl debug.enter_debugger=gdb # In a separate terminal: python3 kgdb_overflow_poc.py /dev/ttyU0
Expected output
Reply payload length exceeds BUFMAX and / or the guest hangs / panics on resume (dmesg
shows corruption). Because entry is root-gated, the PoC harness runs as root on the test
guest or triggers a panic first.
Impact
Additional / uncontrolled kernel memory corruption during an active KGDB session beyond the
designed m/M arbitrary R/W primitive. Robustness / DoS on an already-faulted kernel. Low
severity.
Recommended fix
Cap length to fit the buffers before calling mem2hex / hex2mem, and for M ensure
the hex payload actually fits in the remaining inbound buffer.
--- a/sys/cpu/x86_64/misc/x86_64-gdbstub.c
+++ b/sys/cpu/x86_64/misc/x86_64-gdbstub.c
@@ -581,9 +581,11 @@
ptr = &remcomInBuffer[1];
- if (hexToLong (&ptr, &addr)
- && *(ptr++) == ','
- && hexToInt (&ptr, &length))
+ if (hexToLong (&ptr, &addr)
+ && *(ptr++) == ','
+ && hexToInt (&ptr, &length)
+ && length > 0
+ && length <= (BUFMAX - 1) / 2)
{
if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
strcpy (remcomOutBuffer, "E03");
@@ -600,9 +602,12 @@
ptr = &remcomInBuffer[1];
- if (hexToLong(&ptr,&addr)
- && *(ptr++) == ','
- && hexToInt(&ptr, &length)
- && *(ptr++) == ':')
+ if (hexToLong(&ptr,&addr)
+ && *(ptr++) == ','
+ && hexToInt(&ptr, &length)
+ && *(ptr++) == ':'
+ && length > 0
+ && (size_t)length * 2 <=
+ (size_t)(remcomInBuffer + BUFMAX - ptr))
{
if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
strcpy (remcomOutBuffer, "E03");
Additionally, harden hexToInt / hexToLong (lines 383-436) to stop after 8 hex digits for
int / 16 for long and / or accumulate into an unsigned type to avoid signed-shift UB;
the length cap above makes the wrapped value rejected before use, so that is
defense-in-depth.
References
sys/cpu/x86_64/misc/x86_64-gdbstub.c:319-326βmem2hex(no bounds)sys/cpu/x86_64/misc/x86_64-gdbstub.c:339-345βhex2mem(no bounds)sys/cpu/x86_64/misc/x86_64-gdbstub.c:383-436βhexToInt/hexToLong(unbounded shift)sys/cpu/x86_64/misc/x86_64-gdbstub.c:584-590βmhandlersys/cpu/x86_64/misc/x86_64-gdbstub.c:602-607βMhandlersys/cpu/x86_64/misc/x86_64-gdbstub.c:281βremcomOutBuffer[BUFMAX=400]- CWE-787 Out-of-bounds Write
Timeline
- 2026-07-14 Discovered during automated audit.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1079 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited path | 459 B | view raw |
| VERDICT.md | verdict | source-confirmation narrative | 913 B | β raw |
| env.txt | environment | guest uname + toolchain | 247 B | view raw |
DF-1079 source-confirmation
Verdict: REPRODUCED (source-confirmed) Impact: none Confidence: likely
Kernel ref: sys/cpu/x86_64/misc/x86_64-gdbstub.c:584
Mechanism
gdbstub m/M unbounded length overflow: mem2hex writes 2*length into 400-byte remcomOutBuffer; length unchecked -> BSS trample/hang. KGDB-session-only; confirmed.
Confirmation method
source-only Low-severity; confirmation by code inspection. Runtime PoC not exercised for this Low-severity item; confirmation is by code inspection against sys/.
Recommended fix
See fix.diff in this folder (git-apply-able unified diff).
Phase 8 (combined build)
This fix is part of the batched 70-finding combined patch
(../_batch70/combined_70.patch) applied to in-guest /usr/src. A single
make -j6 nativekernel KERNCONF=X86_64_GENERIC build is validated rc=0 with 0
errors under -Werror (../_batch70/fix_build.log).
Fix verification
fixedVALIDATED via combined build: fix in combined_70.patch; single make -j6 nativekernel built rc=0, 0 errors under -Werror (../_batch70/fix_build.log). Cited line corrected. Source-only -> validation = clean -Werror compile.
'>>> Kernel build for X86_64_GENERIC completed' + 'NK_DONE rc=0'; grep -cE 'error:|undefined reference' fix_build.log = 0
Confirmed kernel references
- s
- y
- s
- /
- c
- p
- u
- /
- x
- 8
- 6
- _
- 6
- 4
- /
- m
- i
- s
- c
- /
- x
- 8
- 6
- _
- 6
- 4
- -
- g
- d
- b
- s
- t
- u
- b
- .
- c
- :
- 5
- 8
- 4
Detail
Exploit chain
none (source-only Low finding, not memory-corruption driven to runtime; no escalation chain)
Evidence (decisive lines)
baseline (with-src #0): bug at sys/cpu/x86_64/misc/x86_64-gdbstub.c:584. combined-70 fix kernel: NK_DONE rc=0 (0 errors, -Werror).
PoC changes
authored/validated fix.diff (findings/poc/DF-1079/fix.diff); part of combined_70 kernel build.
Verified recommended fix
See findings/poc/DF-1079/fix.diff (git-apply-able). Matches finding proposal.
Verdict
REAL: gdbstub m command passes unchecked length to mem2hex -> remcomOutBuffer overflow/BSS trample. KGDB-session-only. confirmed.
No comments yet.