[PATCH PR gdb/15236] gdbserver write to linux memory with zero length corrupts stack

Jeremy Bennett
2013-03-06T18:04:48Z

PROBLEM:

The function linux_write_memory () in linux-low.c allocates a buffer on
the stack to hold a copy of the data to be written.

register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *)
alloca (count * sizeof (PTRACE_XFER_TYPE));

"count" is the number of bytes to be written, rounded up to the nearest
multiple of sizeof (PTRACE_XFER_TYPE) and allowing for not being an
aligned address. The function later uses

buffer[0] = ptrace (PTRACE_PEEKTEXT, pid,
(PTRACE_ARG3_TYPE) (uintptr_t) addr, 0);

The problem is that this function can be called to write zero bytes on
an aligned address, for example when receiving an X packet of length 0
(used to test if 8-bit write is supported). Under these circumstances,
count can be zero.

Since in this case, buffer[0] may never have been allocated, the stack
is corrupted and gdbserver may crash.

Demonstrated with the port of GDB 7.5.1 for the Synopsys
arc-linux-uclibc- target, currently under development at:

https://github.com/foss-for-synopsys-dwc-arc-processors/gdb

(to be submitted to the FSF in due course).

SOLUTION:

Writing zero bytes should always succeed. The patch below returns
successfully early if the length is zero, so avoiding the stack
corruption.

Verified on the ARC GDB 7.5.1 port.

CHANGELOG ENTRY:

2013-03-06 Jeremy Bennett <jeremy.bennett@embecosm.com>

PR gdb/15236
* linux-low.c (linux_write_memory): Return early success if len is
zero.

PATCH:

I'm working from a git mirror and have produced this using git diff.
Advice on how to do this properly appreciated!

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 7b79cd1..450e2db 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,9 @@
+2013-03-06 Jeremy Bennett <jeremy.bennett@embecosm.com>
+
+ PR gdb/15236
+ * linux-low.c (linux_write_memory): Return early success if len is
+ zero.
+
2012-04-29 Yao Qi <yao@codesourcery.com>

* server.h: Move some code to ...
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index bbb0693..8e576bd 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -4421,7 +4421,14 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)

/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's
memory at MEMADDR. On failure (cannot write to the inferior)
- returns the value of errno. */
+ returns the value of errno.
+
+ 6-Mar-13, Jeremy Bennett: [PR gdb/15236] This function can be called with
+ length 0 (for example with a zero length X packet). If memaddr is aligned
+ to sizeof (PTRACE_XFER_TYPE), then count will be zero and nothing may be
+ allocated for buffer (architecture dependent). The function must return
+ early in this circumstance, to avoid stack corruption when assigning
+ to buffer[0]. */

static int
linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
@@ -4440,6 +4447,10 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)

int pid = lwpid_of (get_thread_lwp (current_inferior));

+ if (0 == len) {
+ return 0; /* Zero length write always succeeds. */