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