Commit 8025f79c authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Implementation of inter-process RtlCreateUserThread.

parent 7a383cf8
...@@ -221,48 +221,43 @@ static VOID test_CreateRemoteThread(void) ...@@ -221,48 +221,43 @@ static VOID test_CreateRemoteThread(void)
/* create suspended remote thread with entry point SetEvent() */ /* create suspended remote thread with entry point SetEvent() */
hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent, hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent,
hRemoteEvent, CREATE_SUSPENDED, &tid); hRemoteEvent, CREATE_SUSPENDED, &tid);
todo_wine ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
GetLastError());
ok(tid != 0, "null tid\n"); ok(tid != 0, "null tid\n");
ret = SuspendThread(hThread); 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); 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 */ /* thread still suspended, so wait times out */
ret = WaitForSingleObject(hEvent, 100); ret = WaitForSingleObject(hEvent, 100);
ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret); ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret);
ret = ResumeThread(hThread); 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 */ /* wait that doesn't time out */
ret = WaitForSingleObject(hEvent, 100); 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 */ /* wait for thread end */
ret = WaitForSingleObject(hThread, 100); ret = WaitForSingleObject(hThread, 100);
todo_wine ok(ret == WAIT_OBJECT_0, ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret);
"waiting for thread failed, ret=%u\n", ret);
CloseHandle(hThread); CloseHandle(hThread);
/* create and wait for remote thread with entry point CloseHandle() */ /* create and wait for remote thread with entry point CloseHandle() */
hThread = CreateRemoteThread(hProcess, NULL, 0, hThread = CreateRemoteThread(hProcess, NULL, 0,
threadFunc_CloseHandle, threadFunc_CloseHandle,
hRemoteEvent, 0, &tid); hRemoteEvent, 0, &tid);
todo_wine ok(hThread != NULL, ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
"CreateRemoteThread failed, err=%u\n", GetLastError());
ret = WaitForSingleObject(hThread, 100); ret = WaitForSingleObject(hThread, 100);
todo_wine ok(ret == WAIT_OBJECT_0, ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret);
"waiting for thread failed, ret=%u\n", ret);
CloseHandle(hThread); CloseHandle(hThread);
/* create remote thread with entry point SetEvent() */ /* create remote thread with entry point SetEvent() */
hThread = CreateRemoteThread(hProcess, NULL, 0, hThread = CreateRemoteThread(hProcess, NULL, 0,
threadFunc_SetEvent, threadFunc_SetEvent,
hRemoteEvent, 0, &tid); hRemoteEvent, 0, &tid);
todo_wine ok(hThread != NULL, ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
"CreateRemoteThread failed, err=%u\n", GetLastError());
/* closed handle, so wait times out */ /* closed handle, so wait times out */
ret = WaitForSingleObject(hEvent, 100); ret = WaitForSingleObject(hEvent, 100);
...@@ -270,9 +265,8 @@ static VOID test_CreateRemoteThread(void) ...@@ -270,9 +265,8 @@ static VOID test_CreateRemoteThread(void)
/* check that remote SetEvent() failed */ /* check that remote SetEvent() failed */
ret = GetExitCodeThread(hThread, &exitcode); ret = GetExitCodeThread(hThread, &exitcode);
todo_wine ok(ret != 0, ok(ret != 0, "GetExitCodeThread failed, err=%u\n", GetLastError());
"GetExitCodeThread failed, err=%u\n", GetLastError()); if (ret) ok(exitcode == 0, "SetEvent succeeded, expected to fail\n");
if (ret) todo_wine ok(exitcode == 0, "SetEvent succeeded, expected to fail\n");
CloseHandle(hThread); CloseHandle(hThread);
TerminateProcess(hProcess, 0); TerminateProcess(hProcess, 0);
......
...@@ -780,6 +780,20 @@ static BOOL call_apcs( BOOL alertable ) ...@@ -780,6 +780,20 @@ static BOOL call_apcs( BOOL alertable )
&result.virtual_unlock.addr, &result.virtual_unlock.addr,
&result.virtual_unlock.size, 0 ); &result.virtual_unlock.size, 0 );
break; 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: default:
server_protocol_error( "get_apc_request: bad type %d\n", call.type ); server_protocol_error( "get_apc_request: bad type %d\n", call.type );
break; break;
......
...@@ -473,10 +473,27 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * ...@@ -473,10 +473,27 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
NTSTATUS status; NTSTATUS status;
SIZE_T size, page_size = getpagesize(); SIZE_T size, page_size = getpagesize();
if( ! is_current_process( process ) ) if (process != NtCurrentProcess())
{
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)
{ {
ERR("Unsupported on other process\n"); if (id) id->UniqueThread = (HANDLE)result.create_thread.tid;
return STATUS_ACCESS_DENIED; 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; if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
......
...@@ -221,7 +221,8 @@ enum apc_type ...@@ -221,7 +221,8 @@ enum apc_type
APC_VIRTUAL_PROTECT, APC_VIRTUAL_PROTECT,
APC_VIRTUAL_FLUSH, APC_VIRTUAL_FLUSH,
APC_VIRTUAL_LOCK, APC_VIRTUAL_LOCK,
APC_VIRTUAL_UNLOCK APC_VIRTUAL_UNLOCK,
APC_CREATE_THREAD
}; };
typedef union typedef union
...@@ -294,6 +295,15 @@ typedef union ...@@ -294,6 +295,15 @@ typedef union
void *addr; void *addr;
unsigned long size; unsigned long size;
} virtual_unlock; } 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; } apc_call_t;
typedef union typedef union
...@@ -354,6 +364,13 @@ typedef union ...@@ -354,6 +364,13 @@ typedef union
void *addr; void *addr;
unsigned long size; unsigned long size;
} virtual_unlock; } virtual_unlock;
struct
{
enum apc_type type;
unsigned int status;
thread_id_t tid;
obj_handle_t handle;
} create_thread;
} apc_result_t; } apc_result_t;
...@@ -4576,6 +4593,6 @@ union generic_reply ...@@ -4576,6 +4593,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply; struct query_symlink_reply query_symlink_reply;
}; };
#define SERVER_PROTOCOL_VERSION 271 #define SERVER_PROTOCOL_VERSION 272
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
...@@ -237,7 +237,8 @@ enum apc_type ...@@ -237,7 +237,8 @@ enum apc_type
APC_VIRTUAL_PROTECT, APC_VIRTUAL_PROTECT,
APC_VIRTUAL_FLUSH, APC_VIRTUAL_FLUSH,
APC_VIRTUAL_LOCK, APC_VIRTUAL_LOCK,
APC_VIRTUAL_UNLOCK APC_VIRTUAL_UNLOCK,
APC_CREATE_THREAD
}; };
typedef union typedef union
...@@ -310,6 +311,15 @@ typedef union ...@@ -310,6 +311,15 @@ typedef union
void *addr; /* requested address */ void *addr; /* requested address */
unsigned long size; /* requested address */ unsigned long size; /* requested address */
} virtual_unlock; } 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; } apc_call_t;
typedef union typedef union
...@@ -370,6 +380,13 @@ typedef union ...@@ -370,6 +380,13 @@ typedef union
void *addr; /* resulting address */ void *addr; /* resulting address */
unsigned long size; /* resulting size */ unsigned long size; /* resulting size */
} virtual_unlock; } 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; } apc_result_t;
/****************************************************************/ /****************************************************************/
......
...@@ -70,6 +70,7 @@ struct thread_apc ...@@ -70,6 +70,7 @@ struct thread_apc
{ {
struct object obj; /* object header */ struct object obj; /* object header */
struct list entry; /* queue linked list */ struct list entry; /* queue linked list */
struct thread *caller; /* thread that queued this apc */
struct object *owner; /* object that queued this apc */ struct object *owner; /* object that queued this apc */
int executed; /* has it been executed by the client? */ int executed; /* has it been executed by the client? */
apc_call_t call; /* call arguments */ apc_call_t call; /* call arguments */
...@@ -78,6 +79,7 @@ struct thread_apc ...@@ -78,6 +79,7 @@ struct thread_apc
static void dump_thread_apc( struct object *obj, int verbose ); static void dump_thread_apc( struct object *obj, int verbose );
static int thread_apc_signaled( struct object *obj, struct thread *thread ); 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 void clear_apc_queue( struct list *queue );
static const struct object_ops thread_apc_ops = static const struct object_ops thread_apc_ops =
...@@ -93,7 +95,7 @@ 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_map_access, /* map_access */
no_lookup_name, /* lookup_name */ no_lookup_name, /* lookup_name */
no_close_handle, /* close_handle */ 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 ) ...@@ -312,6 +314,12 @@ static int thread_apc_signaled( struct object *obj, struct thread *thread )
return apc->executed; 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 */ /* queue an async procedure call */
static struct thread_apc *create_apc( struct object *owner, const apc_call_t *call_data ) 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 ...@@ -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 ))) if ((apc = alloc_object( &thread_apc_ops )))
{ {
apc->call = *call_data; apc->call = *call_data;
apc->caller = NULL;
apc->owner = owner; apc->owner = owner;
apc->executed = 0; apc->executed = 0;
apc->result.type = APC_NONE; apc->result.type = APC_NONE;
...@@ -1137,10 +1146,9 @@ DECL_HANDLER(select) ...@@ -1137,10 +1146,9 @@ DECL_HANDLER(select)
/* queue an APC for a thread or process */ /* queue an APC for a thread or process */
DECL_HANDLER(queue_apc) DECL_HANDLER(queue_apc)
{ {
struct thread *thread; struct thread *thread = NULL;
struct process *process; struct process *process = NULL;
struct thread_apc *apc; struct thread_apc *apc;
unsigned int access;
if (!(apc = create_apc( NULL, &req->call ))) return; if (!(apc = create_apc( NULL, &req->call ))) return;
...@@ -1148,26 +1156,42 @@ DECL_HANDLER(queue_apc) ...@@ -1148,26 +1156,42 @@ DECL_HANDLER(queue_apc)
{ {
case APC_NONE: case APC_NONE:
case APC_USER: case APC_USER:
if ((thread = get_thread_from_handle( req->thread, THREAD_SET_CONTEXT ))) 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 );
}
break; break;
case APC_VIRTUAL_ALLOC: case APC_VIRTUAL_ALLOC:
case APC_VIRTUAL_FREE: case APC_VIRTUAL_FREE:
case APC_VIRTUAL_QUERY:
case APC_VIRTUAL_PROTECT: case APC_VIRTUAL_PROTECT:
case APC_VIRTUAL_FLUSH: case APC_VIRTUAL_FLUSH:
case APC_VIRTUAL_LOCK: case APC_VIRTUAL_LOCK:
case APC_VIRTUAL_UNLOCK: case APC_VIRTUAL_UNLOCK:
access = (apc->call.type == APC_VIRTUAL_QUERY) ? PROCESS_QUERY_INFORMATION : PROCESS_VM_OPERATION; process = get_process_from_handle( req->process, PROCESS_VM_OPERATION );
if ((process = get_process_from_handle( req->process, access ))) 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 ); obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 );
if (handle) if (handle)
{ {
if (queue_apc( process, NULL, apc )) reply->handle = handle; if (queue_apc( process, NULL, apc ))
{
apc->caller = (struct thread *)grab_object( current );
reply->handle = handle;
}
else else
{ {
close_handle( current->process, handle ); close_handle( current->process, handle );
...@@ -1176,11 +1200,7 @@ DECL_HANDLER(queue_apc) ...@@ -1176,11 +1200,7 @@ DECL_HANDLER(queue_apc)
} }
release_object( process ); release_object( process );
} }
break;
default:
set_error( STATUS_INVALID_PARAMETER );
break;
}
release_object( apc ); release_object( apc );
} }
...@@ -1196,6 +1216,14 @@ DECL_HANDLER(get_apc) ...@@ -1196,6 +1216,14 @@ DECL_HANDLER(get_apc)
0, &thread_apc_ops ))) return; 0, &thread_apc_ops ))) return;
apc->result = req->result; apc->result = req->result;
apc->executed = 1; 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 ); wake_up( &apc->obj, 0 );
close_handle( current->process, req->prev ); close_handle( current->process, req->prev );
release_object( apc ); release_object( apc );
......
...@@ -150,6 +150,12 @@ static void dump_apc_call( const apc_call_t *call ) ...@@ -150,6 +150,12 @@ static void dump_apc_call( const apc_call_t *call )
fprintf( stderr, "APC_VIRTUAL_UNLOCK,addr=%p,size=%lu", fprintf( stderr, "APC_VIRTUAL_UNLOCK,addr=%p,size=%lu",
call->virtual_unlock.addr, call->virtual_unlock.size ); call->virtual_unlock.addr, call->virtual_unlock.size );
break; 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: default:
fprintf( stderr, "type=%u", call->type ); fprintf( stderr, "type=%u", call->type );
break; break;
...@@ -203,6 +209,11 @@ static void dump_apc_result( const apc_result_t *result ) ...@@ -203,6 +209,11 @@ static void dump_apc_result( const apc_result_t *result )
get_status_name( result->virtual_unlock.status ), get_status_name( result->virtual_unlock.status ),
result->virtual_unlock.addr, result->virtual_unlock.size ); result->virtual_unlock.addr, result->virtual_unlock.size );
break; 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: default:
fprintf( stderr, "type=%u", result->type ); fprintf( stderr, "type=%u", result->type );
break; break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment