From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/ntdll.spec | 2 + dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/tests/thread.c | 87 ++++++++++++++++++++++++++++++++++++- dlls/ntdll/unix/thread.c | 32 +++++++++++--- dlls/wow64/process.c | 20 ++++++++- include/processthreadsapi.h | 7 +++ include/winternl.h | 1 + 7 files changed, 142 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 8d14ae393be..d8eef731eff 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -346,6 +346,7 @@ @ stdcall -syscall=0x0049 NtQueryVolumeInformationFile(long ptr ptr long long) @ stdcall -syscall=0x0045 NtQueueApcThread(long ptr long long long) @ stdcall -syscall NtQueueApcThreadEx(long long ptr long long long) +@ stdcall -syscall NtQueueApcThreadEx2(long long long ptr long long long) @ stdcall -syscall NtRaiseException(ptr ptr long) @ stdcall -syscall NtRaiseHardError(long long long ptr long ptr) @ stdcall -syscall=0x0006 NtReadFile(long long ptr ptr ptr ptr long ptr ptr) @@ -1420,6 +1421,7 @@ @ stdcall -private ZwQueryVolumeInformationFile(long ptr ptr long long) NtQueryVolumeInformationFile @ stdcall -private ZwQueueApcThread(long ptr long long long) NtQueueApcThread @ stdcall -private ZwQueueApcThreadEx(long long ptr long long long) NtQueueApcThreadEx +@ stdcall -private ZwQueueApcThreadEx2(long long long ptr long long long) NtQueueApcThreadEx2 @ stdcall -private ZwRaiseException(ptr ptr long) NtRaiseException @ stdcall -private ZwRaiseHardError(long long long ptr long ptr) NtRaiseHardError @ stdcall -private ZwReadFile(long long ptr ptr ptr ptr long ptr ptr) NtReadFile diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 1cb587fa447..a15cb74da1f 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -506,6 +506,7 @@ DEFINE_SYSCALL(NtQueryVirtualMemory, (HANDLE process, LPCVOID addr, MEMORY_INFOR DEFINE_SYSCALL(NtQueryVolumeInformationFile, (HANDLE handle, IO_STATUS_BLOCK *io, void *buffer, ULONG length, FS_INFORMATION_CLASS info_class)) DEFINE_SYSCALL(NtQueueApcThread, (HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)) DEFINE_SYSCALL(NtQueueApcThreadEx, (HANDLE handle, HANDLE reserve_handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)) +DEFINE_SYSCALL(NtQueueApcThreadEx2, (HANDLE handle, HANDLE reserve_handle, ULONG flags, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)) DEFINE_WRAPPED_SYSCALL(NtRaiseException, (EXCEPTION_RECORD *rec, ARM64_NT_CONTEXT *context, BOOL first_chance)) DEFINE_SYSCALL(NtRaiseHardError, (NTSTATUS status, ULONG count, ULONG params_mask, void **params, HARDERROR_RESPONSE_OPTION option, HARDERROR_RESPONSE *response)) DEFINE_WRAPPED_SYSCALL(NtReadFile, (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, void *buffer, ULONG length, LARGE_INTEGER *offset, ULONG *key)) diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 436b1bf89e0..a9b2b3db888 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -28,17 +28,34 @@ #include "winternl.h" #include "wine/test.h"
+static BOOL is_wow64; + +static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); 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 NTSTATUS (WINAPI *pNtQueueApcThreadEx)(HANDLE handle, HANDLE reserve_handle, PNTAPCFUNC func, + ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +static NTSTATUS (WINAPI *pNtQueueApcThreadEx2)(HANDLE handle, HANDLE reserve_handle, ULONG flags, PNTAPCFUNC func, + ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); + static int * (CDECL *p_errno)(void);
+static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); + static void init_function_pointers(void) { - HMODULE hntdll = GetModuleHandleA( "ntdll.dll" ); -#define GET_FUNC(name) p##name = (void *)GetProcAddress( hntdll, #name ); + HMODULE hdll; +#define GET_FUNC(name) p##name = (void *)GetProcAddress( hdll, #name ); + hdll = GetModuleHandleA( "ntdll.dll" ); + GET_FUNC( NtAllocateReserveObject ); GET_FUNC( NtCreateThreadEx ); + GET_FUNC( NtQueueApcThreadEx ); + GET_FUNC( NtQueueApcThreadEx2 ); GET_FUNC( _errno ); + + hdll = GetModuleHandleA( "kernel32.dll" ); + GET_FUNC( IsWow64Process ); #undef GET_FUNC }
@@ -242,12 +259,78 @@ static void test_NtCreateUserProcess(void) CloseHandle( thread ); }
+static int apc_count; + +static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + ++apc_count; +} + +static void test_NtQueueApcThreadEx(void) +{ + NTSTATUS status, expected; + HANDLE reserve; + + if (!pNtQueueApcThreadEx) + { + win_skip( "NtQueueApcThreadEx is not available.\n" ); + return; + } + + status = pNtQueueApcThreadEx( GetCurrentThread(), (HANDLE)QUEUE_USER_APC_CALLBACK_DATA_CONTEXT, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + todo_wine_if(!status) ok( status == STATUS_INVALID_HANDLE, "got %#lx, expected %#lx.\n", status, STATUS_INVALID_HANDLE ); + + status = pNtQueueApcThreadEx( GetCurrentThread(), (HANDLE)QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( status == STATUS_SUCCESS || status == STATUS_INVALID_HANDLE /* wow64 and win64 on Win version before Win10 1809 */, + "got %#lx.\n", status ); + + status = pNtQueueApcThreadEx( GetCurrentThread(), GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + todo_wine_if(!status) ok( status == STATUS_OBJECT_TYPE_MISMATCH, "got %#lx.\n", status ); + + status = pNtAllocateReserveObject( &reserve, NULL, MemoryReserveObjectTypeUserApc ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + status = pNtQueueApcThreadEx( GetCurrentThread(), reserve, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); + status = pNtQueueApcThreadEx( GetCurrentThread(), reserve, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + todo_wine_if(!status) ok( status == STATUS_INVALID_PARAMETER_2, "got %#lx.\n", status ); + SleepEx( 0, TRUE ); + status = pNtQueueApcThreadEx( GetCurrentThread(), reserve, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); + + NtClose( reserve ); + + status = pNtAllocateReserveObject( &reserve, NULL, MemoryReserveObjectTypeIoCompletion ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + status = pNtQueueApcThreadEx( GetCurrentThread(), reserve, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + todo_wine_if(!status) ok( status == STATUS_OBJECT_TYPE_MISMATCH, "got %#lx.\n", status ); + NtClose( reserve ); + + SleepEx( 0, TRUE ); + + if (!pNtQueueApcThreadEx2) + { + win_skip( "NtQueueApcThreadEx2 is not available.\n" ); + return; + } + expected = is_wow64 ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS; + status = pNtQueueApcThreadEx2( GetCurrentThread(), NULL, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( status == expected, "got %#lx, expected %#lx.\n", status, expected ); + + status = pNtQueueApcThreadEx2( GetCurrentThread(), (HANDLE)QUEUE_USER_APC_CALLBACK_DATA_CONTEXT, 0, apc_func, 0x1234, 0x5678, 0xdeadbeef ); + todo_wine_if(!status) ok( status == STATUS_INVALID_HANDLE, "got %#lx.\n", status ); + + SleepEx( 0, TRUE ); +} + START_TEST(thread) { init_function_pointers();
+ if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + test_dbg_hidden_thread_creation(); test_unique_teb(); test_errno(); test_NtCreateUserProcess(); + test_NtQueueApcThreadEx(); } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index ce6683e075f..e554b06fe1c 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1720,14 +1720,19 @@ NTSTATUS WINAPI NtTerminateThread( HANDLE handle, LONG exit_code )
/****************************************************************************** - * NtQueueApcThread (NTDLL.@) + * NtQueueApcThreadEx2 (NTDLL.@) */ -NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, - ULONG_PTR arg2, ULONG_PTR arg3 ) +NTSTATUS WINAPI NtQueueApcThreadEx2( HANDLE handle, HANDLE reserve_handle, ULONG flags, + PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) { unsigned int ret; union apc_call call;
+ TRACE( "%p %p %#x %p %p %p %p.\n", handle, reserve_handle, flags, func, (void *)arg1, (void *)arg2, (void *)arg3 ); + if (reserve_handle || flags) FIXME( "Unsupported reserve_handle %p, flags %#x.\n", reserve_handle, flags ); + + if (flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC && is_wow64()) return STATUS_NOT_SUPPORTED; + SERVER_START_REQ( queue_apc ) { req->handle = wine_server_obj_handle( handle ); @@ -1753,8 +1758,25 @@ NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1 NTSTATUS WINAPI NtQueueApcThreadEx( HANDLE handle, HANDLE reserve_handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) { - FIXME( "reserve handle should be used: %p\n", reserve_handle ); - return NtQueueApcThread( handle, func, arg1, arg2, arg3 ); + ULONG flags = 0; + + if (!is_wow64()) + { + flags = (ULONG_PTR)reserve_handle & (ULONG_PTR)3; + reserve_handle = (HANDLE)((ULONG_PTR)reserve_handle & ~(ULONG_PTR)3); + } + return NtQueueApcThreadEx2( handle, (HANDLE)((ULONG_PTR)reserve_handle & ~(ULONG_PTR)3), + flags, func, arg1, arg2, arg3 ); +} + + +/****************************************************************************** + * NtQueueApcThread (NTDLL.@) + */ +NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, + ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + return NtQueueApcThreadEx2( handle, NULL, QUEUE_USER_APC_FLAGS_NONE, func, arg1, arg2, arg3 ); }
diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 957ca0d2691..5e9a662feee 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -835,7 +835,25 @@ NTSTATUS WINAPI wow64_NtQueueApcThreadEx( UINT *args ) ULONG arg3 = get_ulong( &args );
return NtQueueApcThreadEx( handle, reserve_handle, apc_32to64( func ), - (ULONG_PTR)apc_param_32to64( func, arg1 ), arg2, arg3 ); + (ULONG_PTR)apc_param_32to64( func, arg1 ), arg2, arg3 ); +} + + +/********************************************************************** + * wow64_NtQueueApcThreadEx + */ +NTSTATUS WINAPI wow64_NtQueueApcThreadEx2( UINT *args ) +{ + HANDLE handle = get_handle( &args ); + HANDLE reserve_handle = get_handle( &args ); + ULONG flags = get_ulong( &args ); + ULONG func = get_ulong( &args ); + ULONG arg1 = get_ulong( &args ); + ULONG arg2 = get_ulong( &args ); + ULONG arg3 = get_ulong( &args ); + + return NtQueueApcThreadEx2( handle, reserve_handle, flags, apc_32to64( func ), + (ULONG_PTR)apc_param_32to64( func, arg1 ), arg2, arg3 ); }
diff --git a/include/processthreadsapi.h b/include/processthreadsapi.h index 44cb2513980..f5f033c99b2 100644 --- a/include/processthreadsapi.h +++ b/include/processthreadsapi.h @@ -49,6 +49,13 @@ WINBASEAPI HRESULT WINAPI GetThreadDescription(HANDLE,PWSTR *); WINBASEAPI HRESULT WINAPI SetThreadDescription(HANDLE,PCWSTR); WINBASEAPI BOOL WINAPI SetThreadInformation(HANDLE,THREAD_INFORMATION_CLASS,LPVOID,DWORD);
+typedef enum _QUEUE_USER_APC_FLAGS +{ + QUEUE_USER_APC_FLAGS_NONE, + QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC = 0x00000001, + QUEUE_USER_APC_CALLBACK_DATA_CONTEXT = 0x00010000, +} QUEUE_USER_APC_FLAGS; + #ifdef __cplusplus } #endif diff --git a/include/winternl.h b/include/winternl.h index 0bf166b8996..65113165d41 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4569,6 +4569,7 @@ NTSYSAPI NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE,PVOID*,SIZE_T*,ULONG,ULO NTSYSAPI NTSTATUS WINAPI NtPulseEvent(HANDLE,LONG*); NTSYSAPI NTSTATUS WINAPI NtQueueApcThread(HANDLE,PNTAPCFUNC,ULONG_PTR,ULONG_PTR,ULONG_PTR); NTSYSAPI NTSTATUS WINAPI NtQueueApcThreadEx(HANDLE,HANDLE,PNTAPCFUNC,ULONG_PTR,ULONG_PTR,ULONG_PTR); +NTSYSAPI NTSTATUS WINAPI NtQueueApcThreadEx2(HANDLE,HANDLE,ULONG,PNTAPCFUNC,ULONG_PTR,ULONG_PTR,ULONG_PTR); NTSYSAPI NTSTATUS WINAPI NtQueryAttributesFile(const OBJECT_ATTRIBUTES*,FILE_BASIC_INFORMATION*); NTSYSAPI NTSTATUS WINAPI NtQueryDefaultLocale(BOOLEAN,LCID*); NTSYSAPI NTSTATUS WINAPI NtQueryDefaultUILanguage(LANGID*);