diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in index 8c6a86fb965..90deb5865f8 100644 --- a/dlls/ntdll/tests/Makefile.in +++ b/dlls/ntdll/tests/Makefile.in @@ -22,6 +22,7 @@ C_SRCS = \ rtlstr.c \ string.c \ sync.c \ + thread.c \ threadpool.c \ time.c \ virtual.c \ diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c new file mode 100644 index 00000000000..09b9bcc8497 --- /dev/null +++ b/dlls/ntdll/tests/thread.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(); +} diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index c834ef85c79..b2457905698 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -705,6 +705,16 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ data_size_t handles_size, jobs_size; 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++) { 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->access = thread_access; - req->suspend = !!(thread_flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED); + req->flags = thread_flags; req->request_fd = -1; wine_server_add_data( req, objattr, attr_len ); if (!(status = wine_server_call( req ))) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 455272e07e0..00fe2b6fa1b 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1243,6 +1243,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT ULONG flags, ULONG_PTR zero_bits, SIZE_T stack_commit, 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; pthread_t pthread_id; pthread_attr_t pthread_attr; @@ -1254,6 +1255,9 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT TEB *teb; NTSTATUS status; + if (flags & ~supported_flags) + FIXME( "Unsupported flags %#x.\n", flags ); + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; #ifndef _WIN64 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->access = access; - req->suspend = flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED; + req->flags = flags; req->request_fd = request_pipe[0]; wine_server_add_data( req, objattr, len ); if (!(status = wine_server_call( req ))) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index b37a8e8e056..55f84c54156 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -870,7 +870,7 @@ struct new_thread_request struct request_header __header; obj_handle_t process; unsigned int access; - int suspend; + unsigned int flags; int request_fd; /* VARARG(objattr,object_attributes); */ char __pad_28[4]; @@ -6279,7 +6279,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 737 +#define SERVER_PROTOCOL_VERSION 738 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index c83e6a2ef7c..efe0d22cbc4 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -871,7 +871,7 @@ typedef struct @REQ(new_thread) obj_handle_t process; /* process in which to create thread */ 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 */ VARARG(objattr,object_attributes); /* object attributes */ @REPLY diff --git a/server/request.h b/server/request.h index 6a63e842357..708083a02f8 100644 --- a/server/request.h +++ b/server/request.h @@ -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( 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, 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( sizeof(struct new_thread_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct new_thread_reply, tid) == 8 ); diff --git a/server/thread.c b/server/thread.c index 55eb8513af8..e9240a05659 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1355,7 +1355,8 @@ DECL_HANDLER(new_thread) if ((thread = create_thread( request_fd, process, sd ))) { 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 ); if ((reply->handle = alloc_handle_no_access_check( current->process, thread, req->access, objattr->attributes ))) diff --git a/server/trace.c b/server/trace.c index 99c5d3996ab..f7c9cbd975e 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1472,7 +1472,7 @@ static void dump_new_thread_request( const struct new_thread_request *req ) { fprintf( stderr, " process=%04x", req->process ); 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 ); dump_varargs_object_attributes( ", objattr=", cur_size ); }