ntdll: CreateRemoteThread and RtlCreateUserThread for remote
processes, take 2
Thomas Kho
tkho at ucla.edu
Tue Aug 1 20:41:54 CDT 2006
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 */
+ at 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;
+ at REPLY
+ at END
+
+
+/* Set current process service thread */
+ at REQ(set_remote_op_mutex)
+ obj_handle_t mutex; /* global handle for mutex */
+ at REPLY
+ at END
+
+
+/* Get service thread mutex */
+ at REQ(get_remote_op_mutex)
+ obj_handle_t process;
+ at REPLY
+ obj_handle_t mutex;
+ at 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;
More information about the wine-devel
mailing list