From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/virtual.c | 4 ++- server/ptrace.c | 64 ++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c index d0553b19966..ecce5683064 100644 --- a/dlls/kernel32/tests/virtual.c +++ b/dlls/kernel32/tests/virtual.c @@ -4546,8 +4546,10 @@ static void test_ReadProcessMemory(void) ok(ret, "ReadProcessMemory failed %lu\n", GetLastError()); ok(copied == si.dwPageSize, "copied = %Id\n", copied);
+ copied = 1; ret = ReadProcessMemory(hproc, ptr, buf, 2 * si.dwPageSize, &copied); - todo_wine ok(!ret, "ReadProcessMemory succeeded\n"); + todo_wine_if(ret) ok(!ret, "ReadProcessMemory succeeded\n"); + todo_wine_if(ret) ok(!copied, "copied = %Id\n", copied);
ret = ReadProcessMemory(hproc, ptr, buf, si.dwPageSize, &copied); ok(ret, "ReadProcessMemory failed %lu\n", GetLastError()); diff --git a/server/ptrace.c b/server/ptrace.c index 955567a704f..404c0a8d12f 100644 --- a/server/ptrace.c +++ b/server/ptrace.c @@ -43,6 +43,10 @@ #ifdef HAVE_SYS_THR_H # include <sys/thr.h> #endif +#if defined(HAVE_SYS_UIO_H) && defined(__NR_process_vm_readv) +# include <sys/uio.h> +#define USE_PROCESS_VM +#endif #include <unistd.h>
#include "ntstatus.h" @@ -350,21 +354,45 @@ static struct thread *get_ptrace_thread( struct process *process ) return NULL; }
-/* read data from a process memory space */ -int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, char *dest ) +#ifdef USE_PROCESS_VM +static int read_process_memory_vm( struct thread *thread, client_ptr_t ptr, data_size_t size, char *dest ) { - struct thread *thread = get_ptrace_thread( process ); - unsigned int first_offset, last_offset, len; - unsigned long data, *addr; + static int not_supported; + struct iovec local, remote; + ssize_t len;
- if (!thread) return 0; - - if ((unsigned long)ptr != ptr) + if (not_supported) return -1; + if (thread->unix_pid == -1 || !is_process_init_done( thread->process )) { set_error( STATUS_ACCESS_DENIED ); return 0; }
+ local.iov_len = remote.iov_len = size; + local.iov_base = dest; + remote.iov_base = (void *)(unsigned long)ptr; + len = syscall( __NR_process_vm_readv, thread->unix_pid, &local, 1, &remote, 1, 0 ); + if (len < 0 && errno == ENOSYS) + { + not_supported = 1; + return -1; + } + if (len == size) return 1; + set_error( len >= 0 ? STATUS_PARTIAL_COPY : STATUS_ACCESS_DENIED ); + return 0; +} +#else +static int read_process_memory_vm( struct thread *thread, client_ptr_t ptr, data_size_t size, char *dest ) +{ + return -1; +} +#endif + +static int read_process_memory_ptrace( struct thread *thread, client_ptr_t ptr, data_size_t size, char *dest ) +{ + unsigned int first_offset, last_offset, len; + unsigned long data, *addr; + first_offset = ptr % sizeof(long); last_offset = (size + first_offset) % sizeof(long); if (!last_offset) last_offset = sizeof(long); @@ -379,7 +407,7 @@ int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t char procmem[24]; int fd;
- snprintf( procmem, sizeof(procmem), "/proc/%u/mem", process->unix_pid ); + snprintf( procmem, sizeof(procmem), "/proc/%u/mem", thread->process->unix_pid ); if ((fd = open( procmem, O_RDONLY )) != -1) { ssize_t ret = pread( fd, dest, size, ptr ); @@ -417,6 +445,24 @@ int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t return !len; }
+/* read data from a process memory space */ +int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, char *dest ) +{ + struct thread *thread = get_ptrace_thread( process ); + int ret; + + if (!thread) return 0; + + if ((unsigned long)ptr != ptr) + { + set_error( STATUS_ACCESS_DENIED ); + return 0; + } + + if ((ret = read_process_memory_vm( thread, ptr, size, dest )) != -1) return ret; + return read_process_memory_ptrace( thread, ptr, size, dest ); +} + /* make sure we can write to the whole address range */ /* len is the total size (in longs) */ static int check_process_write_access( struct thread *thread, long *addr, data_size_t len )