ntdll: Implementation of inter-process RtlCreateUserThread.

This commit is contained in:
Alexandre Julliard 2007-01-18 15:02:55 +01:00
parent 7a383cf83f
commit 8025f79ce1
7 changed files with 146 additions and 48 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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;
/****************************************************************/

View File

@ -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 );

View File

@ -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;