ntdll: Support THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER thread creation flag.
Signed-off-by: Paul Gofman <pgofman@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
4f5f15c206
commit
c338a6ff99
|
@ -22,6 +22,7 @@ C_SRCS = \
|
||||||
rtlstr.c \
|
rtlstr.c \
|
||||||
string.c \
|
string.c \
|
||||||
sync.c \
|
sync.c \
|
||||||
|
thread.c \
|
||||||
threadpool.c \
|
threadpool.c \
|
||||||
time.c \
|
time.c \
|
||||||
virtual.c \
|
virtual.c \
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Unit test suite for ntdll thread functions
|
||||||
|
*
|
||||||
|
* Copyright 2021 Paul Gofman for CodeWeavers
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ntdll_test.h"
|
||||||
|
|
||||||
|
static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *,
|
||||||
|
HANDLE, PRTL_THREAD_START_ROUTINE, void *,
|
||||||
|
ULONG, ULONG_PTR, SIZE_T, SIZE_T, PS_ATTRIBUTE_LIST * );
|
||||||
|
|
||||||
|
static void init_function_pointers(void)
|
||||||
|
{
|
||||||
|
HMODULE hntdll = GetModuleHandleA( "ntdll.dll" );
|
||||||
|
#define GET_FUNC(name) p##name = (void *)GetProcAddress( hntdll, #name );
|
||||||
|
GET_FUNC( NtCreateThreadEx );
|
||||||
|
#undef GET_FUNC
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CALLBACK test_NtCreateThreadEx_proc(void *param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dbg_hidden_thread_creation(void)
|
||||||
|
{
|
||||||
|
RTL_USER_PROCESS_PARAMETERS *params;
|
||||||
|
PS_CREATE_INFO create_info;
|
||||||
|
PS_ATTRIBUTE_LIST ps_attr;
|
||||||
|
WCHAR path[MAX_PATH + 4];
|
||||||
|
HANDLE process, thread;
|
||||||
|
UNICODE_STRING imageW;
|
||||||
|
BOOLEAN dbg_hidden;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (!pNtCreateThreadEx)
|
||||||
|
{
|
||||||
|
win_skip( "NtCreateThreadEx is not available.\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc,
|
||||||
|
NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, 0, 0, 0, NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
|
||||||
|
dbg_hidden = 0xcc;
|
||||||
|
status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
ok( !dbg_hidden, "Got unexpected dbg_hidden %#x.\n", dbg_hidden );
|
||||||
|
|
||||||
|
status = NtResumeThread( thread, NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
WaitForSingleObject( thread, INFINITE );
|
||||||
|
CloseHandle( thread );
|
||||||
|
|
||||||
|
status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc,
|
||||||
|
NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER,
|
||||||
|
0, 0, 0, NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
|
||||||
|
dbg_hidden = 0xcc;
|
||||||
|
status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
ok( dbg_hidden == 1, "Got unexpected dbg_hidden %#x.\n", dbg_hidden );
|
||||||
|
|
||||||
|
status = NtResumeThread( thread, NULL );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
WaitForSingleObject( thread, INFINITE );
|
||||||
|
CloseHandle( thread );
|
||||||
|
|
||||||
|
lstrcpyW( path, L"\\??\\" );
|
||||||
|
GetModuleFileNameW( NULL, path + 4, MAX_PATH );
|
||||||
|
|
||||||
|
RtlInitUnicodeString( &imageW, path );
|
||||||
|
|
||||||
|
memset( &ps_attr, 0, sizeof(ps_attr) );
|
||||||
|
ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
|
||||||
|
ps_attr.Attributes[0].Size = lstrlenW(path) * sizeof(WCHAR);
|
||||||
|
ps_attr.Attributes[0].ValuePtr = path;
|
||||||
|
ps_attr.TotalLength = sizeof(ps_attr);
|
||||||
|
|
||||||
|
status = RtlCreateProcessParametersEx( ¶ms, &imageW, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, PROCESS_PARAMS_FLAG_NORMALIZED );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
|
||||||
|
/* NtCreateUserProcess() may return STATUS_INVALID_PARAMATER with some unitialized data in create_info. */
|
||||||
|
memset( &create_info, 0, sizeof(create_info) );
|
||||||
|
create_info.Size = sizeof(create_info);
|
||||||
|
|
||||||
|
status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
|
||||||
|
NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
|
||||||
|
| THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, params,
|
||||||
|
&create_info, &ps_attr );
|
||||||
|
ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status );
|
||||||
|
status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
|
||||||
|
NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, params,
|
||||||
|
&create_info, &ps_attr );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
status = NtTerminateProcess( process, 0 );
|
||||||
|
ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
|
||||||
|
CloseHandle( process );
|
||||||
|
CloseHandle( thread );
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(thread)
|
||||||
|
{
|
||||||
|
init_function_pointers();
|
||||||
|
|
||||||
|
test_dbg_hidden_thread_creation();
|
||||||
|
}
|
|
@ -705,6 +705,16 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
|
||||||
data_size_t handles_size, jobs_size;
|
data_size_t handles_size, jobs_size;
|
||||||
obj_handle_t *handles, *jobs;
|
obj_handle_t *handles, *jobs;
|
||||||
|
|
||||||
|
if (thread_flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER)
|
||||||
|
{
|
||||||
|
WARN( "Invalid thread flags %#x.\n", thread_flags );
|
||||||
|
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_flags & ~THREAD_CREATE_FLAGS_CREATE_SUSPENDED)
|
||||||
|
FIXME( "Unsupported thread flags %#x.\n", thread_flags );
|
||||||
|
|
||||||
for (i = 0; i < attr_count; i++)
|
for (i = 0; i < attr_count; i++)
|
||||||
{
|
{
|
||||||
switch (ps_attr->Attributes[i].Attribute)
|
switch (ps_attr->Attributes[i].Attribute)
|
||||||
|
@ -845,7 +855,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
|
||||||
{
|
{
|
||||||
req->process = wine_server_obj_handle( process_handle );
|
req->process = wine_server_obj_handle( process_handle );
|
||||||
req->access = thread_access;
|
req->access = thread_access;
|
||||||
req->suspend = !!(thread_flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED);
|
req->flags = thread_flags;
|
||||||
req->request_fd = -1;
|
req->request_fd = -1;
|
||||||
wine_server_add_data( req, objattr, attr_len );
|
wine_server_add_data( req, objattr, attr_len );
|
||||||
if (!(status = wine_server_call( req )))
|
if (!(status = wine_server_call( req )))
|
||||||
|
|
|
@ -1243,6 +1243,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
|
||||||
ULONG flags, ULONG_PTR zero_bits, SIZE_T stack_commit,
|
ULONG flags, ULONG_PTR zero_bits, SIZE_T stack_commit,
|
||||||
SIZE_T stack_reserve, PS_ATTRIBUTE_LIST *attr_list )
|
SIZE_T stack_reserve, PS_ATTRIBUTE_LIST *attr_list )
|
||||||
{
|
{
|
||||||
|
static const ULONG supported_flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER;
|
||||||
sigset_t sigset;
|
sigset_t sigset;
|
||||||
pthread_t pthread_id;
|
pthread_t pthread_id;
|
||||||
pthread_attr_t pthread_attr;
|
pthread_attr_t pthread_attr;
|
||||||
|
@ -1254,6 +1255,9 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
|
||||||
TEB *teb;
|
TEB *teb;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (flags & ~supported_flags)
|
||||||
|
FIXME( "Unsupported flags %#x.\n", flags );
|
||||||
|
|
||||||
if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3;
|
if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3;
|
||||||
#ifndef _WIN64
|
#ifndef _WIN64
|
||||||
if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3;
|
if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3;
|
||||||
|
@ -1303,7 +1307,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
|
||||||
{
|
{
|
||||||
req->process = wine_server_obj_handle( process );
|
req->process = wine_server_obj_handle( process );
|
||||||
req->access = access;
|
req->access = access;
|
||||||
req->suspend = flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
|
req->flags = flags;
|
||||||
req->request_fd = request_pipe[0];
|
req->request_fd = request_pipe[0];
|
||||||
wine_server_add_data( req, objattr, len );
|
wine_server_add_data( req, objattr, len );
|
||||||
if (!(status = wine_server_call( req )))
|
if (!(status = wine_server_call( req )))
|
||||||
|
|
|
@ -870,7 +870,7 @@ struct new_thread_request
|
||||||
struct request_header __header;
|
struct request_header __header;
|
||||||
obj_handle_t process;
|
obj_handle_t process;
|
||||||
unsigned int access;
|
unsigned int access;
|
||||||
int suspend;
|
unsigned int flags;
|
||||||
int request_fd;
|
int request_fd;
|
||||||
/* VARARG(objattr,object_attributes); */
|
/* VARARG(objattr,object_attributes); */
|
||||||
char __pad_28[4];
|
char __pad_28[4];
|
||||||
|
@ -6279,7 +6279,7 @@ union generic_reply
|
||||||
|
|
||||||
/* ### protocol_version begin ### */
|
/* ### protocol_version begin ### */
|
||||||
|
|
||||||
#define SERVER_PROTOCOL_VERSION 737
|
#define SERVER_PROTOCOL_VERSION 738
|
||||||
|
|
||||||
/* ### protocol_version end ### */
|
/* ### protocol_version end ### */
|
||||||
|
|
||||||
|
|
|
@ -871,7 +871,7 @@ typedef struct
|
||||||
@REQ(new_thread)
|
@REQ(new_thread)
|
||||||
obj_handle_t process; /* process in which to create thread */
|
obj_handle_t process; /* process in which to create thread */
|
||||||
unsigned int access; /* wanted access rights */
|
unsigned int access; /* wanted access rights */
|
||||||
int suspend; /* new thread should be suspended on creation */
|
unsigned int flags; /* thread creation flags */
|
||||||
int request_fd; /* fd for request pipe */
|
int request_fd; /* fd for request pipe */
|
||||||
VARARG(objattr,object_attributes); /* object attributes */
|
VARARG(objattr,object_attributes); /* object attributes */
|
||||||
@REPLY
|
@REPLY
|
||||||
|
|
|
@ -729,7 +729,7 @@ C_ASSERT( FIELD_OFFSET(struct get_new_process_info_reply, exit_code) == 12 );
|
||||||
C_ASSERT( sizeof(struct get_new_process_info_reply) == 16 );
|
C_ASSERT( sizeof(struct get_new_process_info_reply) == 16 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct new_thread_request, process) == 12 );
|
C_ASSERT( FIELD_OFFSET(struct new_thread_request, process) == 12 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct new_thread_request, access) == 16 );
|
C_ASSERT( FIELD_OFFSET(struct new_thread_request, access) == 16 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct new_thread_request, suspend) == 20 );
|
C_ASSERT( FIELD_OFFSET(struct new_thread_request, flags) == 20 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct new_thread_request, request_fd) == 24 );
|
C_ASSERT( FIELD_OFFSET(struct new_thread_request, request_fd) == 24 );
|
||||||
C_ASSERT( sizeof(struct new_thread_request) == 32 );
|
C_ASSERT( sizeof(struct new_thread_request) == 32 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct new_thread_reply, tid) == 8 );
|
C_ASSERT( FIELD_OFFSET(struct new_thread_reply, tid) == 8 );
|
||||||
|
|
|
@ -1355,7 +1355,8 @@ DECL_HANDLER(new_thread)
|
||||||
if ((thread = create_thread( request_fd, process, sd )))
|
if ((thread = create_thread( request_fd, process, sd )))
|
||||||
{
|
{
|
||||||
thread->system_regs = current->system_regs;
|
thread->system_regs = current->system_regs;
|
||||||
if (req->suspend) thread->suspend++;
|
if (req->flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED) thread->suspend++;
|
||||||
|
thread->dbg_hidden = !!(req->flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER);
|
||||||
reply->tid = get_thread_id( thread );
|
reply->tid = get_thread_id( thread );
|
||||||
if ((reply->handle = alloc_handle_no_access_check( current->process, thread,
|
if ((reply->handle = alloc_handle_no_access_check( current->process, thread,
|
||||||
req->access, objattr->attributes )))
|
req->access, objattr->attributes )))
|
||||||
|
|
|
@ -1472,7 +1472,7 @@ static void dump_new_thread_request( const struct new_thread_request *req )
|
||||||
{
|
{
|
||||||
fprintf( stderr, " process=%04x", req->process );
|
fprintf( stderr, " process=%04x", req->process );
|
||||||
fprintf( stderr, ", access=%08x", req->access );
|
fprintf( stderr, ", access=%08x", req->access );
|
||||||
fprintf( stderr, ", suspend=%d", req->suspend );
|
fprintf( stderr, ", flags=%08x", req->flags );
|
||||||
fprintf( stderr, ", request_fd=%d", req->request_fd );
|
fprintf( stderr, ", request_fd=%d", req->request_fd );
|
||||||
dump_varargs_object_attributes( ", objattr=", cur_size );
|
dump_varargs_object_attributes( ", objattr=", cur_size );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue