From 8025f79ce1f787813aa92cd19c999e171e18cf13 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 18 Jan 2007 15:02:55 +0100 Subject: [PATCH] ntdll: Implementation of inter-process RtlCreateUserThread. --- dlls/kernel32/tests/thread.c | 28 +++++------- dlls/ntdll/sync.c | 14 ++++++ dlls/ntdll/thread.c | 23 ++++++++-- include/wine/server_protocol.h | 21 ++++++++- server/protocol.def | 19 ++++++++- server/thread.c | 78 +++++++++++++++++++++++----------- server/trace.c | 11 +++++ 7 files changed, 146 insertions(+), 48 deletions(-) diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index 425c6da008f..98d50f8f266 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -221,48 +221,43 @@ static VOID test_CreateRemoteThread(void) /* create suspended remote thread with entry point SetEvent() */ hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent, hRemoteEvent, CREATE_SUSPENDED, &tid); - todo_wine ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", - GetLastError()); + ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); ok(tid != 0, "null tid\n"); ret = SuspendThread(hThread); - todo_wine ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); + ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); ret = ResumeThread(hThread); - todo_wine ok(ret == 2, "ret=%u, err=%u\n", ret, GetLastError()); + ok(ret == 2, "ret=%u, err=%u\n", ret, GetLastError()); /* thread still suspended, so wait times out */ ret = WaitForSingleObject(hEvent, 100); ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret); ret = ResumeThread(hThread); - todo_wine ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); + ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError()); /* wait that doesn't time out */ ret = WaitForSingleObject(hEvent, 100); - todo_wine ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%u\n", ret); + ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%u\n", ret); /* wait for thread end */ ret = WaitForSingleObject(hThread, 100); - todo_wine ok(ret == WAIT_OBJECT_0, - "waiting for thread failed, ret=%u\n", ret); + ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret); CloseHandle(hThread); /* create and wait for remote thread with entry point CloseHandle() */ hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_CloseHandle, hRemoteEvent, 0, &tid); - todo_wine ok(hThread != NULL, - "CreateRemoteThread failed, err=%u\n", GetLastError()); + ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); ret = WaitForSingleObject(hThread, 100); - todo_wine ok(ret == WAIT_OBJECT_0, - "waiting for thread failed, ret=%u\n", ret); + ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret); CloseHandle(hThread); /* create remote thread with entry point SetEvent() */ hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent, hRemoteEvent, 0, &tid); - todo_wine ok(hThread != NULL, - "CreateRemoteThread failed, err=%u\n", GetLastError()); + ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError()); /* closed handle, so wait times out */ ret = WaitForSingleObject(hEvent, 100); @@ -270,9 +265,8 @@ static VOID test_CreateRemoteThread(void) /* check that remote SetEvent() failed */ ret = GetExitCodeThread(hThread, &exitcode); - todo_wine ok(ret != 0, - "GetExitCodeThread failed, err=%u\n", GetLastError()); - if (ret) todo_wine ok(exitcode == 0, "SetEvent succeeded, expected to fail\n"); + ok(ret != 0, "GetExitCodeThread failed, err=%u\n", GetLastError()); + if (ret) ok(exitcode == 0, "SetEvent succeeded, expected to fail\n"); CloseHandle(hThread); TerminateProcess(hProcess, 0); diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 1778bb2f729..0c4a95a80ee 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -780,6 +780,20 @@ static BOOL call_apcs( BOOL alertable ) &result.virtual_unlock.addr, &result.virtual_unlock.size, 0 ); break; + case APC_CREATE_THREAD: + { + CLIENT_ID id; + result.type = call.type; + result.create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL, + call.create_thread.suspend, NULL, + call.create_thread.reserve, + call.create_thread.commit, + call.create_thread.func, + call.create_thread.arg, + &result.create_thread.handle, &id ); + result.create_thread.tid = (thread_id_t)id.UniqueThread; + break; + } default: server_protocol_error( "get_apc_request: bad type %d\n", call.type ); break; diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index b06d1a3e149..96563312b34 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -473,10 +473,27 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * NTSTATUS status; SIZE_T size, page_size = getpagesize(); - if( ! is_current_process( process ) ) + if (process != NtCurrentProcess()) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + apc_call_t call; + apc_result_t result; + + call.create_thread.type = APC_CREATE_THREAD; + call.create_thread.func = start; + call.create_thread.arg = param; + call.create_thread.reserve = stack_reserve; + call.create_thread.commit = stack_commit; + call.create_thread.suspend = suspended; + status = NTDLL_queue_process_apc( process, &call, &result ); + if (status != STATUS_SUCCESS) return status; + + if (result.create_thread.status == STATUS_SUCCESS) + { + if (id) id->UniqueThread = (HANDLE)result.create_thread.tid; + if (handle_ptr) *handle_ptr = result.create_thread.handle; + else NtClose( result.create_thread.handle ); + } + return result.create_thread.status; } if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 6f7a652611c..ab90a50945f 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -221,7 +221,8 @@ enum apc_type APC_VIRTUAL_PROTECT, APC_VIRTUAL_FLUSH, APC_VIRTUAL_LOCK, - APC_VIRTUAL_UNLOCK + APC_VIRTUAL_UNLOCK, + APC_CREATE_THREAD }; typedef union @@ -294,6 +295,15 @@ typedef union void *addr; unsigned long size; } virtual_unlock; + struct + { + enum apc_type type; + void (__stdcall *func)(void*); + void *arg; + unsigned long reserve; + unsigned long commit; + int suspend; + } create_thread; } apc_call_t; typedef union @@ -354,6 +364,13 @@ typedef union void *addr; unsigned long size; } virtual_unlock; + struct + { + enum apc_type type; + unsigned int status; + thread_id_t tid; + obj_handle_t handle; + } create_thread; } apc_result_t; @@ -4576,6 +4593,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 271 +#define SERVER_PROTOCOL_VERSION 272 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 535ef12ffde..9bfdbd0c561 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -237,7 +237,8 @@ enum apc_type APC_VIRTUAL_PROTECT, APC_VIRTUAL_FLUSH, APC_VIRTUAL_LOCK, - APC_VIRTUAL_UNLOCK + APC_VIRTUAL_UNLOCK, + APC_CREATE_THREAD }; typedef union @@ -310,6 +311,15 @@ typedef union void *addr; /* requested address */ unsigned long size; /* requested address */ } virtual_unlock; + struct + { + enum apc_type type; /* APC_CREATE_THREAD */ + void (__stdcall *func)(void*); /* start function */ + void *arg; /* argument for start function */ + unsigned long reserve; /* reserve size for thread stack */ + unsigned long commit; /* commit size for thread stack */ + int suspend; /* suspended thread? */ + } create_thread; } apc_call_t; typedef union @@ -370,6 +380,13 @@ typedef union void *addr; /* resulting address */ unsigned long size; /* resulting size */ } virtual_unlock; + struct + { + enum apc_type type; /* APC_CREATE_THREAD */ + unsigned int status; /* status returned by call */ + thread_id_t tid; /* thread id */ + obj_handle_t handle; /* handle to new thread */ + } create_thread; } apc_result_t; /****************************************************************/ diff --git a/server/thread.c b/server/thread.c index 5b0268ab7b1..bdc163cd4a9 100644 --- a/server/thread.c +++ b/server/thread.c @@ -70,6 +70,7 @@ struct thread_apc { struct object obj; /* object header */ struct list entry; /* queue linked list */ + struct thread *caller; /* thread that queued this apc */ struct object *owner; /* object that queued this apc */ int executed; /* has it been executed by the client? */ apc_call_t call; /* call arguments */ @@ -78,6 +79,7 @@ struct thread_apc static void dump_thread_apc( struct object *obj, int verbose ); static int thread_apc_signaled( struct object *obj, struct thread *thread ); +static void thread_apc_destroy( struct object *obj ); static void clear_apc_queue( struct list *queue ); static const struct object_ops thread_apc_ops = @@ -93,7 +95,7 @@ static const struct object_ops thread_apc_ops = no_map_access, /* map_access */ no_lookup_name, /* lookup_name */ no_close_handle, /* close_handle */ - no_destroy /* destroy */ + thread_apc_destroy /* destroy */ }; @@ -312,6 +314,12 @@ static int thread_apc_signaled( struct object *obj, struct thread *thread ) return apc->executed; } +static void thread_apc_destroy( struct object *obj ) +{ + struct thread_apc *apc = (struct thread_apc *)obj; + if (apc->caller) release_object( apc->caller ); +} + /* queue an async procedure call */ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *call_data ) { @@ -320,6 +328,7 @@ static struct thread_apc *create_apc( struct object *owner, const apc_call_t *ca if ((apc = alloc_object( &thread_apc_ops ))) { apc->call = *call_data; + apc->caller = NULL; apc->owner = owner; apc->executed = 0; apc->result.type = APC_NONE; @@ -1137,10 +1146,9 @@ DECL_HANDLER(select) /* queue an APC for a thread or process */ DECL_HANDLER(queue_apc) { - struct thread *thread; - struct process *process; + struct thread *thread = NULL; + struct process *process = NULL; struct thread_apc *apc; - unsigned int access; if (!(apc = create_apc( NULL, &req->call ))) return; @@ -1148,39 +1156,51 @@ DECL_HANDLER(queue_apc) { case APC_NONE: case APC_USER: - if ((thread = get_thread_from_handle( req->thread, THREAD_SET_CONTEXT ))) - { - if (!queue_apc( NULL, thread, apc )) set_error( STATUS_THREAD_IS_TERMINATING ); - release_object( thread ); - } + thread = get_thread_from_handle( req->thread, THREAD_SET_CONTEXT ); break; case APC_VIRTUAL_ALLOC: case APC_VIRTUAL_FREE: - case APC_VIRTUAL_QUERY: case APC_VIRTUAL_PROTECT: case APC_VIRTUAL_FLUSH: case APC_VIRTUAL_LOCK: case APC_VIRTUAL_UNLOCK: - access = (apc->call.type == APC_VIRTUAL_QUERY) ? PROCESS_QUERY_INFORMATION : PROCESS_VM_OPERATION; - if ((process = get_process_from_handle( req->process, access ))) - { - obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 ); - if (handle) - { - if (queue_apc( process, NULL, apc )) reply->handle = handle; - else - { - close_handle( current->process, handle ); - set_error( STATUS_PROCESS_IS_TERMINATING ); - } - } - release_object( process ); - } + process = get_process_from_handle( req->process, PROCESS_VM_OPERATION ); + break; + case APC_VIRTUAL_QUERY: + process = get_process_from_handle( req->process, PROCESS_QUERY_INFORMATION ); + break; + case APC_CREATE_THREAD: + process = get_process_from_handle( req->process, PROCESS_CREATE_THREAD ); break; default: set_error( STATUS_INVALID_PARAMETER ); break; } + + if (thread) + { + if (!queue_apc( NULL, thread, apc )) set_error( STATUS_THREAD_IS_TERMINATING ); + release_object( thread ); + } + else if (process) + { + obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 ); + if (handle) + { + if (queue_apc( process, NULL, apc )) + { + apc->caller = (struct thread *)grab_object( current ); + reply->handle = handle; + } + else + { + close_handle( current->process, handle ); + set_error( STATUS_PROCESS_IS_TERMINATING ); + } + } + release_object( process ); + } + release_object( apc ); } @@ -1196,6 +1216,14 @@ DECL_HANDLER(get_apc) 0, &thread_apc_ops ))) return; apc->result = req->result; apc->executed = 1; + if (apc->result.type == APC_CREATE_THREAD) /* transfer the handle to the caller process */ + { + obj_handle_t handle = duplicate_handle( current->process, apc->result.create_thread.handle, + apc->caller->process, 0, 0, DUP_HANDLE_SAME_ACCESS ); + close_handle( current->process, apc->result.create_thread.handle ); + apc->result.create_thread.handle = handle; + clear_error(); /* ignore errors from the above calls */ + } wake_up( &apc->obj, 0 ); close_handle( current->process, req->prev ); release_object( apc ); diff --git a/server/trace.c b/server/trace.c index 4e630933efb..c2e55c6fad6 100644 --- a/server/trace.c +++ b/server/trace.c @@ -150,6 +150,12 @@ static void dump_apc_call( const apc_call_t *call ) fprintf( stderr, "APC_VIRTUAL_UNLOCK,addr=%p,size=%lu", call->virtual_unlock.addr, call->virtual_unlock.size ); break; + case APC_CREATE_THREAD: + fprintf( stderr, "APC_CREATE_THREAD,func=%p,arg=%p,reserve=%lx,commit=%lx,suspend=%u", + call->create_thread.func, call->create_thread.arg, + call->create_thread.reserve, call->create_thread.commit, + call->create_thread.suspend ); + break; default: fprintf( stderr, "type=%u", call->type ); break; @@ -203,6 +209,11 @@ static void dump_apc_result( const apc_result_t *result ) get_status_name( result->virtual_unlock.status ), result->virtual_unlock.addr, result->virtual_unlock.size ); break; + case APC_CREATE_THREAD: + fprintf( stderr, "APC_CREATE_THREAD,status=%s,tid=%04x,handle=%p", + get_status_name( result->create_thread.status ), + result->create_thread.tid, result->create_thread.handle ); + break; default: fprintf( stderr, "type=%u", result->type ); break;