From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/virtual.c | 19 +++++---- dlls/ntdll/tests/wow64.c | 4 +- server/ptrace.c | 72 ++++++++++++++++++++++++++++------- 3 files changed, 69 insertions(+), 26 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c index ecce5683064..5aa147eb16a 100644 --- a/dlls/kernel32/tests/virtual.c +++ b/dlls/kernel32/tests/virtual.c @@ -151,8 +151,7 @@ static void test_VirtualAllocEx(void) bytes_written = 0xdeadbeef; b = WriteProcessMemory(hProcess, addr1, src, alloc_size, &bytes_written); ok( !b, "WriteProcessMemory succeeded\n" ); - ok( GetLastError() == ERROR_NOACCESS || - GetLastError() == ERROR_PARTIAL_COPY, /* vista */ + ok( GetLastError() == ERROR_PARTIAL_COPY, /* vista */ "wrong error %lu\n", GetLastError() ); ok( bytes_written == 0, "%Iu bytes written\n", bytes_written ); bytes_read = 0xdeadbeef; @@ -211,10 +210,10 @@ static void test_VirtualAllocEx(void) if (!b) ok( GetLastError() == ERROR_NOACCESS, "wrong error %lu\n", GetLastError() ); ok( bytes_written == 0xdeadbeef, "%Iu bytes written\n", bytes_written ); status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_ACCESS_VIOLATION), "wrong status %lx\n", status ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( bytes_written == 0, "%Iu bytes written\n", bytes_written );
b = VirtualProtectEx( hProcess, addr1, alloc_size, PAGE_EXECUTE_READ, &old_prot ); @@ -225,21 +224,21 @@ static void test_VirtualAllocEx(void) ok( bytes_written == alloc_size, "%Iu bytes written\n", bytes_written ); bytes_written = 0xdeadbeef; status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_ACCESS_VIOLATION), "wrong status %lx\n", status ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( bytes_written == 0, "%Iu bytes written\n", bytes_written );
b = VirtualProtectEx( hProcess, addr1, 0x2000, PAGE_EXECUTE_READWRITE, &old_prot ); ok( b, "VirtualProtectEx, error %lu\n", GetLastError() ); bytes_written = 0xdeadbeef; b = WriteProcessMemory(hProcess, addr1, src, alloc_size, &bytes_written); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( !b || broken(b), /* <= win10 1507 */ "WriteProcessMemory succeeded\n" ); bytes_written = 0xdeadbeef; status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_SUCCESS), /* <= win10 1507 */ "wrong status %lx\n", status ); ok( bytes_written == (status ? 0x2000 : alloc_size), "%Iu bytes written\n", bytes_written ); @@ -248,10 +247,10 @@ static void test_VirtualAllocEx(void) ok( b, "VirtualProtectEx, error %lu\n", GetLastError() ); bytes_written = 0xdeadbeef; b = WriteProcessMemory(hProcess, addr1, src, alloc_size, &bytes_written); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( !b || broken(b), /* <= win10 1507 */ "WriteProcessMemory succeeded\n" ); status = pNtWriteVirtualMemory( hProcess, addr1, src, alloc_size, &bytes_written ); - todo_wine + todo_wine_if(b) ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_SUCCESS), /* <= win10 1507 */ "wrong status %lx\n", status ); ok( bytes_written == (status ? 0x2000 : alloc_size), "%Iu bytes written\n", bytes_written ); diff --git a/dlls/ntdll/tests/wow64.c b/dlls/ntdll/tests/wow64.c index 544290c3273..824fc45fe9f 100644 --- a/dlls/ntdll/tests/wow64.c +++ b/dlls/ntdll/tests/wow64.c @@ -2492,10 +2492,10 @@ static void test_nt_wow64(void) MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); ok( !status, "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); status = pNtWow64WriteVirtualMemory64( process, ptr, str, sizeof(str), &res ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( status == STATUS_PARTIAL_COPY || broken( status == STATUS_ACCESS_VIOLATION ), "NtWow64WriteVirtualMemory64 failed %lx\n", status ); - todo_wine + todo_wine_if(status == STATUS_SUCCESS) ok( !res || broken(res) /* win10 1709 */, "wrong size %s\n", wine_dbgstr_longlong(res) ); } ptr = 0x9876543210ull; diff --git a/server/ptrace.c b/server/ptrace.c index e065da1f183..16ec91ee978 100644 --- a/server/ptrace.c +++ b/server/ptrace.c @@ -43,7 +43,7 @@ #ifdef HAVE_SYS_THR_H # include <sys/thr.h> #endif -#if defined(HAVE_SYS_UIO_H) && defined(__NR_process_vm_readv) +#if defined(HAVE_SYS_UIO_H) && defined(__NR_process_vm_readv) && defined(__NR_process_vm_writev) # include <sys/uio.h> #define USE_PROCESS_VM #endif @@ -381,11 +381,46 @@ static int read_process_memory_vm( struct thread *thread, client_ptr_t ptr, data set_error( len >= 0 ? STATUS_PARTIAL_COPY : STATUS_ACCESS_DENIED ); return 0; } + +static int write_process_memory_vm( struct thread *thread, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ) +{ + static int not_supported; + struct iovec local, remote; + ssize_t len; + + 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 = (void *)src; + remote.iov_base = (void *)(unsigned long)ptr; + len = syscall( __NR_process_vm_writev, thread->unix_pid, &local, 1, &remote, 1, 0 ); + if (len < 0 && errno == ENOSYS) + { + not_supported = 1; + return -1; + } + + if (written) *written = max( len, 0 ); + if (len != size) set_error( STATUS_PARTIAL_COPY ); + return len == size; +} #else static int read_process_memory_vm( struct thread *thread, client_ptr_t ptr, data_size_t size, char *dest ) { return -1; } + +static int write_process_memory_vm( struct thread *thread, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ) +{ + return -1; +} #endif
static int read_process_memory_ptrace( struct thread *thread, client_ptr_t ptr, data_size_t size, char *dest ) @@ -479,25 +514,15 @@ static int check_process_write_access( struct thread *thread, long *addr, data_s return (write_thread_long( thread, addr + len - 1, 0, 0 ) != -1); }
-/* write data to a process memory space */ -int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src, - data_size_t *written ) +static int write_process_memory_ptrace( struct thread *thread, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ) { - struct thread *thread = get_ptrace_thread( process ); int ret = 0; long data = 0; data_size_t len; long *addr; unsigned long first_mask, first_offset, last_mask, last_offset;
- if (!thread) return 0; - - if ((unsigned long)ptr != ptr) - { - set_error( STATUS_ACCESS_DENIED ); - return 0; - } - /* compute the mask for the first long */ first_mask = ~0; first_offset = ptr % sizeof(long); @@ -525,7 +550,7 @@ int write_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_WRONLY )) != -1) { ssize_t r = pwrite( fd, src, size, ptr ); @@ -569,6 +594,25 @@ int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t return ret; }
+/* write data to a process memory space */ +int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ) +{ + 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 = write_process_memory_vm( thread, ptr, size, src, written )) != -1) return ret; + return write_process_memory_ptrace( thread, ptr, size, src, written ); +} + /* retrieve an LDT selector entry */ void get_selector_entry( struct thread *thread, int entry, unsigned int *base, unsigned int *limit, unsigned char *flags )