ntdll: CreateRemoteThread and RtlCreateUserThread for remote processes, take 2
Hello,
This is an implementation of CreateRemoteThread and RtlCreateUserThread for remote processes that re-introduces a service thread for each process and uses a signal to flag pending operations. Operations are guarded by a global mutex for each process.
A recent thread for the implementation of the functions via thread hijacking with ptrace is at http://winehq.org/pipermail/wine-devel/2006-July/049593.html
I have created a wiki page at http://wiki.winehq.org/RemoteProcessOperations to document the discussion of this class of functions.
Note: With the service thread, this mutex-and-signals approach could be exchanged for queued APCs on the service thread. In addition, there are unaddressed issues with this implementation (thread attach notifications, etc).
Is this a step in the right direction?
Thomas Kho
---
dlls/kernel/thread.c | 42 +++++- dlls/ntdll/Makefile.in | 1 dlls/ntdll/loader.c | 1 dlls/ntdll/ntdll.spec | 4 + dlls/ntdll/ntdll_misc.h | 73 +++++++++++ dlls/ntdll/server.c | 3 dlls/ntdll/service.c | 267 ++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/signal_i386.c | 19 +++ dlls/ntdll/thread.c | 102 +++++++++++++++ dlls/ntdll/virtual.c | 55 ++++++++ include/thread.h | 3 include/wine/server_protocol.h | 54 ++++++++ include/winternl.h | 1 server/handle.c | 2 server/process.c | 97 +++++++++++++++ server/process.h | 1 server/protocol.def | 28 ++++ server/request.h | 6 + server/thread.c | 9 + server/trace.c | 36 +++++ 20 files changed, 788 insertions(+), 16 deletions(-)
diff --git a/dlls/kernel/thread.c b/dlls/kernel/thread.c index bf29aac..162b898 100644 --- a/dlls/kernel/thread.c +++ b/dlls/kernel/thread.c @@ -47,12 +47,12 @@ #include "kernel_private.h" WINE_DEFAULT_DEBUG_CHANNEL(thread); WINE_DECLARE_DEBUG_CHANNEL(relay);
- struct new_thread_info { LPTHREAD_START_ROUTINE func; void *arg; }; +static struct new_thread_info threadInfo;
/*********************************************************************** @@ -62,11 +62,12 @@ struct new_thread_info */ static void CALLBACK THREAD_Start( void *ptr ) { - struct new_thread_info *info = ptr; + struct new_thread_info *info = ptr ? ptr : &threadInfo; LPTHREAD_START_ROUTINE func = info->func; void *arg = info->arg;
- RtlFreeHeap( GetProcessHeap(), 0, info ); + if (ptr) + RtlFreeHeap( GetProcessHeap(), 0, info );
if (TRACE_ON(relay)) DPRINTF("%04lx:Starting thread (entryproc=%p)\n", GetCurrentThreadId(), func ); @@ -116,11 +117,11 @@ HANDLE WINAPI CreateRemoteThread( HANDLE LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD id ) { - HANDLE handle; + HANDLE handle, hServiceThreadMutex; CLIENT_ID client_id; NTSTATUS status; SIZE_T stack_reserve = 0, stack_commit = 0; - struct new_thread_info *info; + struct new_thread_info *info = NULL;
if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) ))) { @@ -130,12 +131,29 @@ HANDLE WINAPI CreateRemoteThread( HANDLE info->func = start; info->arg = param;
+ if (hProcess != GetCurrentProcess()) + { + status = __wine_get_remote_op_mutex(hProcess, &hServiceThreadMutex); + if (status) return status; + + WriteProcessMemory(hProcess, &threadInfo, info, + sizeof(struct new_thread_info), NULL); + } + if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack; else stack_commit = stack;
- status = RtlCreateUserThread( hProcess, NULL, TRUE, - NULL, stack_reserve, stack_commit, - THREAD_Start, info, &handle, &client_id ); + if (hProcess != GetCurrentProcess()) + status = RtlCreateUserThread( hProcess, NULL, TRUE, + NULL, stack_reserve, stack_commit, + THREAD_Start, NULL, &handle, + &client_id ); + else + status = RtlCreateUserThread( hProcess, NULL, TRUE, + NULL, stack_reserve, stack_commit, + THREAD_Start, info, &handle, + &client_id ); + if (status == STATUS_SUCCESS) { if (id) *id = (DWORD)client_id.UniqueThread; @@ -149,6 +167,7 @@ HANDLE WINAPI CreateRemoteThread( HANDLE NtClose( handle ); RtlFreeHeap( GetProcessHeap(), 0, info ); SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + if (hProcess != GetCurrentProcess()) handle = 0; } } @@ -157,8 +176,15 @@ HANDLE WINAPI CreateRemoteThread( HANDLE { RtlFreeHeap( GetProcessHeap(), 0, info ); SetLastError( RtlNtStatusToDosError(status) ); + if (hProcess != GetCurrentProcess()) handle = 0; } + + if (hProcess != GetCurrentProcess()) + { + RtlFreeHeap( GetProcessHeap(), 0, info ); + __wine_release_remote_op_mutex(hServiceThreadMutex); + } return handle; }
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 646808f..ea843d2 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -38,6 +38,7 @@ C_SRCS = \ sec.c \ serial.c \ server.c \ + service.c \ signal_i386.c \ signal_powerpc.c \ signal_sparc.c \ diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 184f64f..d093e59 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -2151,6 +2151,7 @@ void WINAPI LdrInitializeThunk( ULONG un RtlLeaveCriticalSection( &loader_section );
if (nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) VIRTUAL_UseLargeAddressSpace(); + SERVICE_init(); return;
error: diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 4b5549f..2e26a6e 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1390,3 +1390,7 @@ # Filesystem @ cdecl wine_nt_to_unix_file_name(ptr ptr long long) @ cdecl wine_unix_to_nt_file_name(ptr ptr) @ cdecl __wine_init_windows_dir(wstr wstr) + +# Remote process locking +@ cdecl __wine_get_remote_op_mutex(long ptr) +@ cdecl __wine_release_remote_op_mutex(long) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 03110a4..cfd37e7 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -177,4 +177,77 @@ static inline struct ntdll_thread_regs * return (struct ntdll_thread_regs *)NtCurrentTeb()->SpareBytes1; }
+extern void SERVICE_init(void); + +struct RemoteOp { + enum enum_op { + RO_NEW_THREAD, + RO_ALLOCATE, + RO_FREE, + RO_LOCK, + RO_PROTECT, + RO_QUERY, + RO_UNLOCK + } op; + + /* in */ + HANDLE hDoneEvent; + /* out */ + NTSTATUS status; + + union { + struct { + PVOID ret; + ULONG zero_bits; + SIZE_T size; + ULONG type; + ULONG protect; + } alloc; + struct { + PVOID addr; + SIZE_T size; + ULONG type; + } free; + struct { + PVOID addr; + SIZE_T size; + ULONG new_prot; + ULONG old_prot; + } protect; + struct { + LPVOID addr; + MEMORY_INFORMATION_CLASS info_class; + PVOID buffer; + SIZE_T len; + SIZE_T res_len; + } query; + struct { + PVOID addr; + SIZE_T size; + ULONG unknown; + } lock; + struct { + PVOID addr; + SIZE_T size; + ULONG unknown; + } unlock; + struct { + /* in */ + HANDLE src_process; + BOOLEAN suspended; + PVOID stack_addr; + SIZE_T stack_reserve; + SIZE_T stack_commit; + PRTL_THREAD_START_ROUTINE start; + void *param; + /* out */ + HANDLE *handle_ptr; + CLIENT_ID *id_ptr; + } thread; + }; +}; + +extern struct RemoteOp ro; +extern volatile int pending_service_req; + #endif diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c index 841604a..9124751 100644 --- a/dlls/ntdll/server.c +++ b/dlls/ntdll/server.c @@ -880,6 +880,7 @@ void server_init_process(void) sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR2 ); sigaddset( &block_set, SIGCHLD ); + sigaddset( &block_set, SIGRTMIN );
/* receive the first thread request fd on the main socket */ ntdll_get_thread_data()->request_fd = receive_fd( &dummy_handle ); @@ -934,6 +935,8 @@ #endif req->reply_fd = reply_pipe[1]; req->wait_fd = ntdll_get_thread_data()->wait_fd[1]; req->debug_level = (TRACE_ON(server) != 0); + req->event = ro.hDoneEvent; + ro.hDoneEvent = 0; /* creation mutex released after server call */ ret = wine_server_call( req ); NtCurrentTeb()->ClientId.UniqueProcess = (HANDLE)reply->pid; NtCurrentTeb()->ClientId.UniqueThread = (HANDLE)reply->tid; diff --git a/dlls/ntdll/service.c b/dlls/ntdll/service.c new file mode 100644 index 0000000..ba6c895 --- /dev/null +++ b/dlls/ntdll/service.c @@ -0,0 +1,267 @@ +/* + * Service thread + * + * Copyright 2006 Google (Thomas Kho) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* FIXME remove extraneous includes */ +#include "config.h" +#include "wine/port.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#ifdef HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif + +#define NONAMELESSUNION +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "thread.h" +#include "winternl.h" +#include "wine/library.h" +#include "wine/server.h" +#include "wine/pthread.h" +#include "wine/debug.h" +#include "ntdll_misc.h" +#include "wine/exception.h" + +WINE_DEFAULT_DEBUG_CHANNEL(thread); + + +#define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') + +volatile int pending_service_req = 0; + +/*********************************************************************** + * use_service_thread + */ +static BOOL use_service_thread(void) +{ + static const WCHAR WineW[] = {'S','o','f','t','w','a','r','e','\', + 'W','i','n','e',0}; + static const WCHAR UseServiceThreadW[] = {'U','s','e', + 'S','e','r','v','i','c','e', 'T','h','r','e','a','d',0}; + + char tmp[80]; + HANDLE root, hkey; + DWORD dummy; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + BOOL ret = FALSE; + + attr.Length = sizeof (OBJECT_ATTRIBUTES); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlOpenCurrentUser( KEY_ALL_ACCESS, &root ); + attr.RootDirectory = root; + RtlInitUnicodeString( &nameW, WineW ); + + /* @@ Wine registry key: HKCU\Software\Wine\Network */ + if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) + { + RtlInitUnicodeString( &nameW, UseServiceThreadW ); + if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, + sizeof(tmp), &dummy )) + { + WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; + ret = IS_OPTION_TRUE( str[0] ); + } + NtClose( hkey ); + } + NtClose( root ); + return ret; +} + + +/*********************************************************************** + * create_global_mutex + */ +static HANDLE create_global_mutex(void) +{ + HANDLE hServiceThreadMutex = NULL; + HANDLE hGlobalServiceThreadMutex = NULL; + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = NULL; + attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + status = NtCreateMutant (&hServiceThreadMutex, MUTEX_ALL_ACCESS, &attr, + FALSE); + //printf("created mutant %x, status=%x\n", hServiceThreadMutex, status); + NtDuplicateObject(NtCurrentProcess(), hServiceThreadMutex, + NULL, &hGlobalServiceThreadMutex, + 0, FALSE, DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS| + DUP_HANDLE_MAKE_GLOBAL); + //printf("new mutant %x, status=%x\n", hGlobalServiceThreadMutex, status); + return hGlobalServiceThreadMutex; +} + + +/*********************************************************************** + * SERVICE_thread + * + * Service thread loop + */ +static DWORD WINAPI SERVICE_thread(LPVOID arg) +{ + static unsigned int i = 0; + sigset_t sigset; + + /* unblock SIGRTMIN only for service thread */ + sigemptyset( &sigset ); + sigaddset( &sigset, SIGRTMIN ); + sigprocmask( SIG_UNBLOCK, &sigset, NULL ); + + //printf("setting event inside SERVICE_thread\n"); + /* signal that the service thread has been created */ + NtSetEvent(*(HANDLE *) arg, NULL); + + /* all work for this thread is done in the rt0 signal handler in + * signal_i386.c */ + while (TRUE) + { + struct timeval timeout = {1, 0}; + + select(0, NULL, NULL, NULL, &timeout); + + if (!pending_service_req) + continue; + pending_service_req = 0; + + switch( ro.op ) + { + case RO_NEW_THREAD: + { + HANDLE hNewThread; + CLIENT_ID cidLocal; + NTSTATUS ret; + HANDLE hServiceThreadMutex = NULL; + /* + printf("inside wine_cloned_thread_creator, " + "params (%x, %x, %x, %x, %x, %x, %x, %x, %x, %x)\n", + ro.thread.suspended, ro.thread.stack_addr, + ro.thread.stack_reserve, ro.thread.stack_commit, + ro.thread.start, ro.thread.param, + ro.hDoneEvent, ro.thread.src_process, + ro.thread.handle_ptr, ro.thread.id_ptr); + */ + + ret = RtlCreateUserThread( NtCurrentProcess(), NULL, + ro.thread.suspended, + ro.thread.stack_addr, + ro.thread.stack_reserve, + ro.thread.stack_commit, ro.thread.start, + ro.thread.param, + ro.thread.handle_ptr ? &hNewThread : 0, + ro.thread.id_ptr ? &cidLocal : 0); + //printf("RtlCreateUserThread status %x in service thread\n", ret); + + if (ro.thread.handle_ptr) + { + HANDLE hRemoteNewThread; + ret = NtDuplicateObject(NtCurrentProcess(), hNewThread, + ro.thread.src_process, + &hRemoteNewThread, 0, 0, + DUPLICATE_SAME_ACCESS| + DUPLICATE_CLOSE_SOURCE); + if (ret) printf("error 1 %x\n", ret); + /* + printf("writing handle %x (dup of %x)\n", hRemoteNewThread, + hNewThread); + */ + ret = NtWriteVirtualMemory(ro.thread.src_process, + ro.thread.handle_ptr, + &hRemoteNewThread, sizeof(HANDLE), + NULL); + if (ret) printf("error 2 %x\n", ret); + } + if (ro.thread.id_ptr) + { + ret = NtWriteVirtualMemory(ro.thread.src_process, ro.thread.id_ptr, + &cidLocal, + sizeof(CLIENT_ID), NULL); + if (ret) printf("error 5 %x\n", ret); + } + + NtClose(ro.thread.src_process); + break; + } + case RO_ALLOCATE: + { + ro.status = NtAllocateVirtualMemory( NtCurrentProcess(), + &ro.alloc.ret, + ro.alloc.zero_bits, + &ro.alloc.size, + ro.alloc.type, + ro.alloc.protect ); + NtSetEvent( ro.hDoneEvent, NULL ); + NtClose( ro.hDoneEvent ); + break; + } + } + } +} + + +/*********************************************************************** + * SERVICE_init + * + * Initialize the service thread + * + * FIXME check return codes + */ +void SERVICE_init() +{ + HANDLE hNewThread, hGlobalServiceThreadMutex, hThreadCreatedEvent; + + if (!use_service_thread()) + { + ERR("service thread disabled\n"); + return; + } + + hGlobalServiceThreadMutex = create_global_mutex(); + + NtCreateEvent(&hThreadCreatedEvent, EVENT_ALL_ACCESS, + NULL, FALSE, FALSE); + + RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, NULL, + 0, 0, SERVICE_thread, &hThreadCreatedEvent, + &hNewThread, NULL ); + NtClose(hNewThread); + + NtWaitForSingleObject(hThreadCreatedEvent, FALSE, NULL); + NtClose(hThreadCreatedEvent); + + //ERR("hNewThread=%x, mutex=%x\n", hNewThread, hGlobalServiceThreadMutex); + SERVER_START_REQ( set_remote_op_mutex ) + { + req->mutex = hGlobalServiceThreadMutex; + wine_server_call( req ); + } + SERVER_END_REQ; +} diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 99685f3..508b4dd 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -1209,6 +1209,17 @@ #endif /* __HAVE_VM86 */
/********************************************************************** + * rt0_handler + * + * Handler for SIGRTMIN. + */ +static HANDLER_DEF(rt0_handler) +{ + pending_service_req = 1; +} + + +/********************************************************************** * segv_handler * * Handler for SIGSEGV and related errors. @@ -1439,6 +1450,7 @@ #endif /* linux */ sigaddset( &sig_act.sa_mask, SIGINT ); sigaddset( &sig_act.sa_mask, SIGUSR1 ); sigaddset( &sig_act.sa_mask, SIGUSR2 ); + sigaddset( &sig_act.sa_mask, SIGRTMIN );
#if defined(linux) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) sig_act.sa_flags = SA_RESTART; @@ -1473,6 +1485,7 @@ int __wine_set_signal_handler(unsigned i BOOL SIGNAL_Init(void) { int have_sigaltstack = 0; + sigset_t sigset;
#ifdef HAVE_SIGALTSTACK struct sigaltstack ss; @@ -1505,6 +1518,12 @@ #endif #ifdef __HAVE_VM86 if (set_handler( SIGUSR2, have_sigaltstack, (void (*)())usr2_handler ) == -1) goto error; #endif + if (set_handler( SIGRTMIN, 0, (void (*)())rt0_handler ) == -1) goto error; + + /* block SIGRTMIN by default */ + sigemptyset( &sigset ); + sigaddset( &sigset, SIGRTMIN ); + sigprocmask( SIG_BLOCK, &sigset, NULL );
return TRUE;
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index dbcc5a6..9cf8407 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -62,6 +62,7 @@ static size_t sigstack_total_size; static ULONG sigstack_zero_bits;
struct wine_pthread_functions pthread_functions = { NULL }; +struct RemoteOp ro = { 0 };
/*********************************************************************** * init_teb @@ -76,6 +77,7 @@ static inline NTSTATUS init_teb( TEB *te teb->Tib.Self = &teb->Tib; teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); + teb->GotThreadMutex = 0;
if (!(thread_regs->fs = wine_ldt_alloc_fs())) return STATUS_TOO_MANY_THREADS; thread_data->request_fd = -1; @@ -385,6 +387,52 @@ static void start_thread( struct wine_pt func( arg ); }
+/*********************************************************************** + * __wine_get_remote_op_mutex (NTDLL.@) Not a Windows API + * + * return the service mutex, or a null handle with success if the thread + * already has the mutex + */ +NTSTATUS WINAPI __wine_get_remote_op_mutex(HANDLE hProcess, HANDLE *hMutex) +{ + NTSTATUS status; + + if (NtCurrentTeb()->GotThreadMutex) + { + *hMutex = NULL; + return STATUS_SUCCESS; + } + + SERVER_START_REQ( get_remote_op_mutex ) + { + req->process = hProcess; + if (!(status = wine_server_call( req ))) + *hMutex = reply->mutex; + } + SERVER_END_REQ; + + if (status || *hMutex == NULL) + return status ? status : STATUS_NOT_IMPLEMENTED; + + status = NtWaitForSingleObject(*hMutex, FALSE, 0); + NtCurrentTeb()->GotThreadMutex = 1; + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * __wine_release_remote_op_mutex (NTDLL.@) Not a Windows API + */ +NTSTATUS WINAPI __wine_release_remote_op_mutex(HANDLE hMutex) +{ + NTSTATUS status; + + if (hMutex == NULL) return STATUS_SUCCESS; + + NtCurrentTeb()->GotThreadMutex = 0; + status = NtReleaseMutant(hMutex, NULL); + return status; +}
/*********************************************************************** * RtlCreateUserThread (NTDLL.@) @@ -408,8 +456,58 @@ NTSTATUS WINAPI RtlCreateUserThread( HAN
if( ! is_current_process( process ) ) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + HANDLE hThreadCreatedEvent; + HANDLE hServiceThreadMutex; + struct RemoteOp n; + + status = __wine_get_remote_op_mutex(process, &hServiceThreadMutex); + if (status) return status; + + status = NtCreateEvent(&hThreadCreatedEvent, EVENT_ALL_ACCESS, NULL, + FALSE, FALSE); /* FIXME check this */ + + n.op = RO_NEW_THREAD; + n.thread.suspended = suspended; + n.thread.stack_addr = stack_addr; + n.thread.stack_reserve = stack_reserve; + n.thread.stack_commit = stack_commit; + n.thread.start = start; + n.thread.param = param; + n.thread.handle_ptr = handle_ptr; + n.thread.id_ptr = id; + n.thread.src_process = NULL; + n.hDoneEvent = NULL; + + status = NtWriteVirtualMemory(process, &ro, &n, + sizeof(struct RemoteOp), NULL); + if (status) + { + NtClose( hThreadCreatedEvent ); + return status; + } + + SERVER_START_REQ( remote_op ) + { + req->handle = process; + /* This pair of handles is duplicated and forwarded to the remote + * process. The done event is actually set in the remote process + * during the init_thread server call because it is right after + * this that the thread is suspended. */ + req->event = hThreadCreatedEvent; + req->event_ptr = &ro.hDoneEvent; + req->src_process = NtCurrentProcess(); + req->src_process_ptr = &ro.thread.src_process; + + status = wine_server_call( req ); + } + SERVER_END_REQ; + if (status) return status; + + NtWaitForSingleObject(hThreadCreatedEvent, FALSE, NULL); + __wine_release_remote_op_mutex(hServiceThreadMutex); + + NtClose(hThreadCreatedEvent); + return status; }
if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES; diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index f7b829f..0600170 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -1317,8 +1317,59 @@ NTSTATUS WINAPI NtAllocateVirtualMemory(
if (!is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + HANDLE hThreadCreatedEvent; + HANDLE hServiceThreadMutex; + struct RemoteOp n; + + status = __wine_get_remote_op_mutex(process, &hServiceThreadMutex); + if (status) return status; + + status = NtCreateEvent(&hThreadCreatedEvent, EVENT_ALL_ACCESS, NULL, + FALSE, FALSE); /* FIXME check this */ + + n.op = RO_ALLOCATE; + n.alloc.ret = *ret; + n.alloc.zero_bits = zero_bits; + n.alloc.size = *size_ptr; + n.alloc.type = type; + n.alloc.protect = protect; + n.hDoneEvent = NULL; + + status = NtWriteVirtualMemory(process, &ro, &n, + sizeof(struct RemoteOp), NULL); + if (status) + { + NtClose( hThreadCreatedEvent ); + return status; + } + + SERVER_START_REQ( remote_op ) + { + req->handle = process; + + req->event = hThreadCreatedEvent; + req->event_ptr = &ro.hDoneEvent; + req->src_process = NULL; + req->src_process_ptr = NULL; + + status = wine_server_call( req ); + } + SERVER_END_REQ; + + if (status) return status; + + NtWaitForSingleObject(hThreadCreatedEvent, FALSE, NULL); + + NtReadVirtualMemory(process, &ro, &n, sizeof(struct RemoteOp), + NULL); + + *ret = n.alloc.ret; + *size_ptr = n.alloc.size; + + __wine_release_remote_op_mutex(hServiceThreadMutex); + + NtClose(hThreadCreatedEvent); + return n.status; }
/* Round parameters to a page boundary */ diff --git a/include/thread.h b/include/thread.h index 68d7ae2..d6a36e5 100644 --- a/include/thread.h +++ b/include/thread.h @@ -57,7 +57,8 @@ typedef struct _TEB ULONG_PTR dpmi_vif; /* 200 protected mode virtual interrupt flag */ DWORD vm86_pending; /* 204 data for vm86 mode */ /* here is plenty space for wine specific fields (don't forget to change pad6!!) */ - DWORD pad6[309]; /* 208 */ + DWORD GotThreadMutex; /* 208 */ + DWORD pad6[308]; /* 20c */
ULONG gdiRgn; /* 6dc */ ULONG gdiPen; /* 6e0 */ diff --git a/server/process.c b/server/process.c index 3e5ef97..651ded6 100644 --- a/server/process.c +++ b/server/process.c @@ -22,12 +22,15 @@ #include "config.h" #include "wine/port.h"
#include <assert.h> +#include <errno.h> #include <limits.h> +#include <linux/user.h> #include <signal.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> +#include <sys/ptrace.h> #include <sys/time.h> #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> @@ -36,6 +39,10 @@ #include <unistd.h> #ifdef HAVE_POLL_H #include <poll.h> #endif +#include <sys/wait.h> +#include <sys/types.h> +#include <linux/unistd.h> +#include <errno.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -250,6 +257,7 @@ struct thread *create_process( int fd, s process->winstation = 0; process->desktop = 0; process->token = token_create_admin(); + process->service_mutex = NULL; list_init( &process->thread_list ); list_init( &process->locks ); list_init( &process->classes ); @@ -1055,3 +1063,92 @@ DECL_HANDLER(get_process_idle_event) release_object( process ); } } + +/* Signal a remote operation it */ +DECL_HANDLER(remote_op) +{ + int access; + struct process *src = NULL, *dst = NULL; + struct thread *thread = NULL; + int pid; + obj_handle_t dupevent = NULL, dupprocess = NULL; + + /* get process object */ + access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE; + if (!(dst = get_process_from_handle( req->handle, access ))) + { + set_error( STATUS_ACCESS_DENIED ); + goto cleanup; + } + + if (!(thread = get_process_first_thread( dst ))) + { + set_error( STATUS_ACCESS_DENIED ); + goto cleanup; + } + + pid = thread->unix_pid; + + /* clone event and process handles and write to target process */ + if (!(src = get_process_from_handle((obj_handle_t) 0xffffffff, 0))) + { + set_error( STATUS_ACCESS_DENIED ); + goto cleanup; + } + if (req->event) + { + dupevent = duplicate_handle(src, req->event, dst, 0, 0, + DUPLICATE_SAME_ACCESS); + write_process_memory(dst, req->event_ptr, sizeof(obj_handle_t), + (char *) &dupevent); + } + if (req->src_process) + { + dupprocess = duplicate_handle(src, req->src_process, dst, 0, 0, + DUPLICATE_SAME_ACCESS); + write_process_memory(dst, req->src_process_ptr, + sizeof(obj_handle_t), (char *) &dupprocess); + } + + kill(pid, SIGRTMIN); + +cleanup: + if (src) + release_object( src ); + if (dst) + release_object( dst ); +} + +/* FIXME We can actually put the mutex data in a specific place and read + * from it. */ + +/* Set service thread mutex */ +DECL_HANDLER(set_remote_op_mutex) +{ + struct process *p; + if (!(p = get_process_from_handle((obj_handle_t) 0xffffffff, 0))) + { + set_error( STATUS_INVALID_HANDLE ); + return; + } + p->service_mutex = req->mutex; + release_object( p ); +} + +/* Get service thread mutex */ +DECL_HANDLER(get_remote_op_mutex) +{ + struct process *p; + if (!(p = get_process_from_handle(req->process, 0))) + { + set_error( STATUS_INVALID_HANDLE ); + return; + } + if (!(reply->mutex = p->service_mutex)) + { + set_error( STATUS_NOT_IMPLEMENTED ); + return; + } + release_object( p ); +} diff --git a/server/process.h b/server/process.h index 6edb1e6..eb13043 100644 --- a/server/process.h +++ b/server/process.h @@ -78,6 +78,7 @@ struct process struct list dlls; /* list of loaded dlls */ void *peb; /* PEB address in client address space */ void *ldt_copy; /* pointer to LDT copy in client addr space */ + obj_handle_t service_mutex; /* mutex of service thread */ };
struct process_snapshot diff --git a/server/protocol.def b/server/protocol.def index e64cf43..3dcf500 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -280,6 +280,7 @@ struct token_groups int reply_fd; /* fd for reply pipe */ int wait_fd; /* fd for blocking calls pipe */ int debug_level; /* new debug level */ + obj_handle_t event; /* set to notify remote process */ @REPLY process_id_t pid; /* process id of the new thread's process */ thread_id_t tid; /* thread id of the new thread */ @@ -2618,3 +2619,30 @@ #define MAILSLOT_SET_READ_TIMEOUT 1 @REPLY VARARG(target_name,unicode_str); /* target name */ @END + + +/* Signal a remote operation */ +@REQ(remote_op) + obj_handle_t handle; /* process handle */ + + obj_handle_t event; + void* event_ptr; + obj_handle_t src_process; + void* src_process_ptr; +@REPLY +@END + + +/* Set current process service thread */ +@REQ(set_remote_op_mutex) + obj_handle_t mutex; /* global handle for mutex */ +@REPLY +@END + + +/* Get service thread mutex */ +@REQ(get_remote_op_mutex) + obj_handle_t process; +@REPLY + obj_handle_t mutex; +@END diff --git a/server/thread.c b/server/thread.c index feb3259..f610d28 100644 --- a/server/thread.c +++ b/server/thread.c @@ -886,6 +886,15 @@ DECL_HANDLER(init_thread) } debug_level = max( debug_level, req->debug_level );
+ if (req->event) + { + struct event *event; + if (event = get_event_obj( current->process, req->event, + EVENT_MODIFY_STATE )) + set_event( event ); + close_handle( current->process, req->event, NULL ); + } + reply->pid = get_process_id( process ); reply->tid = get_thread_id( current ); reply->version = SERVER_PROTOCOL_VERSION;