ntdll: Implementation of inter-process RtlCreateUserThread.
This commit is contained in:
parent
7a383cf83f
commit
8025f79ce1
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
/****************************************************************/
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue