-- v2: kernelbase: Implement QueueUserAPC2(). ntdll: Use NtContinueEx in KiUserApcDispatcher on x64. ntdll: Implement QUEUE_USER_APC_CALLBACK_DATA_CONTEXT in NtQueueApcThreadEx2() on x64. ntdll: Pass user APC flags to call_user_apc_dispatcher(). ntdll: Validate reserve handle in NtQueueApcThreadEx2(). ntdll: Add semi-stub for NtQueueApcThreadEx2().
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*);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/thread.c | 10 +++++----- dlls/ntdll/unix/thread.c | 3 ++- server/object.c | 29 ++++++++++++++++++++++++++++- server/object.h | 3 +++ server/protocol.def | 1 + server/thread.c | 9 +++++++++ 6 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index a9b2b3db888..b47d43776f9 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -278,21 +278,21 @@ static void test_NtQueueApcThreadEx(void) }
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 ); + 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 ); + 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 ); + 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 ); @@ -302,7 +302,7 @@ static void test_NtQueueApcThreadEx(void) 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 ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "got %#lx.\n", status ); NtClose( reserve );
SleepEx( 0, TRUE ); @@ -317,7 +317,7 @@ static void test_NtQueueApcThreadEx(void) 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 ); + ok( status == STATUS_INVALID_HANDLE, "got %#lx.\n", status );
SleepEx( 0, TRUE ); } diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index e554b06fe1c..71d1b62803c 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1729,13 +1729,14 @@ NTSTATUS WINAPI NtQueueApcThreadEx2( HANDLE handle, HANDLE reserve_handle, ULONG 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) FIXME( "Unsupported flags %#x.\n", 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 ); + req->reserve_handle = wine_server_obj_handle( reserve_handle ); if (func) { call.type = APC_USER; diff --git a/server/object.c b/server/object.c index 28bda0db77b..25589b4bd2b 100644 --- a/server/object.c +++ b/server/object.c @@ -68,6 +68,7 @@ struct reserve { struct object obj; /* object header */ int type; /* reserve object type. See MEMORY_RESERVE_OBJECT_TYPE */ + struct object *bound_obj; /* object using reserve object */ /* BYTE *memory */; /* reserved memory */ };
@@ -857,7 +858,11 @@ static struct reserve *create_reserve( struct object *root, const struct unicode return NULL; }
- if (reserve && get_error() != STATUS_OBJECT_NAME_EXISTS) reserve->type = type; + if (reserve && get_error() != STATUS_OBJECT_NAME_EXISTS) + { + reserve->type = type; + reserve->bound_obj = NULL; + }
return reserve; } @@ -867,6 +872,28 @@ struct reserve *get_completion_reserve_obj( struct process *process, obj_handle_ return (struct reserve *)get_handle_obj( process, handle, access, &completion_reserve_ops ); }
+struct reserve *reserve_obj_associate_apc( struct process *process, obj_handle_t handle, struct object *apc ) +{ + struct reserve *reserve; + + if (!(reserve = (struct reserve *)get_handle_obj( process, handle, 0, &apc_reserve_ops ))) return NULL; + if (reserve->bound_obj) + { + release_object( reserve ); + set_error( STATUS_INVALID_PARAMETER_2 ); + return NULL; + } + reserve->bound_obj = apc; + return reserve; +} + +void reserve_obj_unbind( struct reserve *reserve ) +{ + if (!reserve) return; + reserve->bound_obj = NULL; + release_object( reserve ); +} + /* Allocate a reserve object for pre-allocating memory for object types */ DECL_HANDLER(allocate_reserve_object) { diff --git a/server/object.h b/server/object.h index 1bf439338d4..1622e93f784 100644 --- a/server/object.h +++ b/server/object.h @@ -194,6 +194,9 @@ extern void dump_objects(void); extern void close_objects(void); #endif
+struct reserve *reserve_obj_associate_apc( struct process *process, obj_handle_t handle, struct object *apc ); +void reserve_obj_unbind( struct reserve *reserve ); + static inline void make_object_permanent( struct object *obj ) { obj->is_permanent = 1; } static inline void make_object_temporary( struct object *obj ) { obj->is_permanent = 0; }
diff --git a/server/protocol.def b/server/protocol.def index 22470e33ae0..09b6f189162 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1297,6 +1297,7 @@ struct obj_locator /* Queue an APC for a thread or process */ @REQ(queue_apc) obj_handle_t handle; /* thread or process handle */ + obj_handle_t reserve_handle; /* APC object optional reserve handle */ VARARG(call,apc_call); /* call arguments */ @REPLY obj_handle_t handle; /* APC handle */ diff --git a/server/thread.c b/server/thread.c index 05ec6a4ec00..f154c1ed075 100644 --- a/server/thread.c +++ b/server/thread.c @@ -89,6 +89,7 @@ struct thread_apc struct list entry; /* queue linked list */ struct thread *caller; /* thread that queued this apc */ struct object *owner; /* object that queued this apc */ + struct reserve *reserve; /* reserve object associated with apc object */ int executed; /* has it been executed by the client? */ union apc_call call; /* call arguments */ union apc_result result; /* call results once executed */ @@ -701,6 +702,7 @@ static void thread_apc_destroy( struct object *obj ) release_object( apc->owner ); } if (apc->sync) release_object( apc->sync ); + reserve_obj_unbind( apc->reserve ); }
/* queue an async procedure call */ @@ -715,6 +717,7 @@ static struct thread_apc *create_apc( struct object *owner, const union apc_call else apc->call.type = APC_NONE; apc->caller = NULL; apc->owner = owner; + apc->reserve = NULL; apc->executed = 0; apc->result.type = APC_NONE; if (owner) grab_object( owner ); @@ -2016,6 +2019,12 @@ DECL_HANDLER(queue_apc) { case APC_NONE: case APC_USER: + if (req->reserve_handle && + !(apc->reserve = reserve_obj_associate_apc( current->process, req->reserve_handle, &apc->obj ))) + { + release_object( apc ); + return; + } thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ); break; case APC_VIRTUAL_ALLOC:
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/server.c | 2 +- dlls/ntdll/unix/signal_arm.c | 4 +++- dlls/ntdll/unix/signal_arm64.c | 4 +++- dlls/ntdll/unix/signal_i386.c | 4 +++- dlls/ntdll/unix/signal_x86_64.c | 4 +++- dlls/ntdll/unix/thread.c | 4 +++- dlls/ntdll/unix/unix_private.h | 4 ++-- server/protocol.def | 5 ++++- 8 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 3969c2bb898..0c99e9c2e66 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -385,7 +385,7 @@ static int wait_select_reply( void *cookie ) */ static NTSTATUS invoke_user_apc( CONTEXT *context, const struct user_apc *apc, NTSTATUS status ) { - return call_user_apc_dispatcher( context, apc->args[0], apc->args[1], apc->args[2], + return call_user_apc_dispatcher( context, apc->flags, apc->args[0], apc->args[1], apc->args[2], wine_server_get_ptr( apc->func ), status ); }
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 45ca9f02c58..e429750925c 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -562,13 +562,15 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) /*********************************************************************** * call_user_apc_dispatcher */ -NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, +NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status ) { struct syscall_frame *frame = get_syscall_frame(); ULONG sp = context ? context->Sp : frame->sp; struct apc_stack_layout *stack;
+ if (flags) FIXME( "flags %#x are not supported.\n", flags ); + sp &= ~7; stack = (struct apc_stack_layout *)sp - 1; if (context) diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 9964d0a9224..f5bd47d597a 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -747,13 +747,15 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) /*********************************************************************** * call_user_apc_dispatcher */ -NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, +NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status ) { struct syscall_frame *frame = get_syscall_frame(); ULONG64 sp = context ? context->Sp : frame->sp; struct apc_stack_layout *stack;
+ if (flags) FIXME( "flags %#x are not supported.\n", flags ); + sp &= ~15; stack = (struct apc_stack_layout *)sp - 1; if (context) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 6eaffbc39c9..db72829bd1f 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1508,13 +1508,15 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) /*********************************************************************** * call_user_apc_dispatcher */ -NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, +NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status ) { struct syscall_frame *frame = get_syscall_frame(); ULONG esp = context ? context->Esp : frame->esp; struct apc_stack_layout *stack = (struct apc_stack_layout *)esp - 1;
+ if (flags) FIXME( "flags %#x are not supported.\n", flags ); + if (!context) { stack->context.ContextFlags = CONTEXT_FULL; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 58496685398..59815a47497 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1532,13 +1532,15 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec ) /*********************************************************************** * call_user_apc_dispatcher */ -NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, +NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status ) { struct syscall_frame *frame = get_syscall_frame(); ULONG64 rsp = context ? context->Rsp : frame->rsp; struct apc_stack_layout *stack;
+ if (flags) FIXME( "flags %#x are not supported.\n", flags ); + rsp &= ~15; stack = (struct apc_stack_layout *)rsp - 1; if (context) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 71d1b62803c..e93d75a4bc8 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1729,7 +1729,6 @@ NTSTATUS WINAPI NtQueueApcThreadEx2( HANDLE handle, HANDLE reserve_handle, ULONG 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 (flags) FIXME( "Unsupported flags %#x.\n", flags );
if (flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC && is_wow64()) return STATUS_NOT_SUPPORTED;
@@ -1741,6 +1740,9 @@ NTSTATUS WINAPI NtQueueApcThreadEx2( HANDLE handle, HANDLE reserve_handle, ULONG { call.type = APC_USER; call.user.func = wine_server_client_ptr( func ); + call.user.flags = 0; + if (flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC) call.user.flags |= SERVER_USER_APC_SPECIAL; + if (flags & QUEUE_USER_APC_CALLBACK_DATA_CONTEXT) call.user.flags |= SERVER_USER_APC_CALLBACK_DATA_CONTEXT; call.user.args[0] = arg1; call.user.args[1] = arg2; call.user.args[2] = arg3; diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 7773c84e333..9a6584efacf 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -387,8 +387,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args );
extern void dbg_init(void);
-extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, - PNTAPCFUNC func, NTSTATUS status ); +extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, unsigned int flags, ULONG_PTR arg1, ULONG_PTR arg2, + ULONG_PTR arg3, PNTAPCFUNC func, NTSTATUS status ); extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); extern void call_raise_user_exception_dispatcher(void);
diff --git a/server/protocol.def b/server/protocol.def index 09b6f189162..4e604cc7084 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -507,11 +507,14 @@ enum apc_type struct user_apc { enum apc_type type; /* APC_USER */ - int __pad; + unsigned int flags; /* SERVER_USER_APC_* flags */ client_ptr_t func; /* void (__stdcall *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR); */ apc_param_t args[3]; /* arguments for user function */ };
+#define SERVER_USER_APC_SPECIAL 0x01 +#define SERVER_USER_APC_CALLBACK_DATA_CONTEXT 0x02 + union apc_call { enum apc_type type;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/signal_x86_64.c | 6 +- dlls/ntdll/tests/exception.c | 119 ++++++++++++++++++++++++++++++-- dlls/ntdll/unix/signal_x86_64.c | 28 ++++++-- include/processthreadsapi.h | 8 +++ 4 files changed, 148 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index ad9557ca906..31070566d40 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -348,7 +348,7 @@ __ASM_GLOBAL_FUNC( KiUserExceptionDispatcher, */ __ASM_GLOBAL_FUNC( KiUserApcDispatcher, __ASM_SEH(".seh_pushframe\n\t") - __ASM_SEH(".seh_stackalloc 0x4d0\n\t") /* sizeof(CONTEXT) */ + __ASM_SEH(".seh_stackalloc 0x530\n\t") /* machine_frame offset */ __ASM_SEH(".seh_savereg %rbx,0x90\n\t") __ASM_SEH(".seh_savereg %rbp,0xa0\n\t") __ASM_SEH(".seh_savereg %rsi,0xa8\n\t") @@ -368,8 +368,8 @@ __ASM_GLOBAL_FUNC( KiUserApcDispatcher, __ASM_CFI(".cfi_offset %r13,0xe0\n\t") __ASM_CFI(".cfi_offset %r14,0xe8\n\t") __ASM_CFI(".cfi_offset %r15,0xf0\n\t") - __ASM_CFI(".cfi_offset %rip,0x4d0\n\t") - __ASM_CFI(".cfi_offset %rsp,0x4e8\n\t") + __ASM_CFI(".cfi_offset %rip,0x530\n\t") + __ASM_CFI(".cfi_offset %rsp,0x548\n\t") "movq 0x00(%rsp),%rcx\n\t" /* context->P1Home = arg1 */ "movq 0x08(%rsp),%rdx\n\t" /* context->P2Home = arg2 */ "movq 0x10(%rsp),%r8\n\t" /* context->P3Home = arg3 */ diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 00c428e5071..f3cca611968 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -44,6 +44,10 @@ static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pNtQueueApcThread)(HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +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 NTSTATUS (WINAPI *pNtContinueEx)(CONTEXT*,KCONTINUE_ARGUMENT*); static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec); static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID); @@ -178,11 +182,31 @@ static BOOL old_wow64; /* Wine old-style wow64 */ static UINT apc_count; static BOOL have_vectored_api; static enum debugger_stages test_stage; +static QUEUE_USER_APC_FLAGS apc_flags;
static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) { + if (apc_flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC && ((ULONG64)arg1 >> 48) == 0xffff /* Win 10 1809v2 */) + { + win_skip( "Broken APC layout, skipping tests.\n" ); + apc_count++; + return; + } + ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 ); - ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 ); + if (apc_flags & QUEUE_USER_APC_CALLBACK_DATA_CONTEXT) + { + APC_CALLBACK_DATA *d = (APC_CALLBACK_DATA *)arg2; + + ok( !!d->ContextRecord, "got NULL.\n" ); + ok( d->Parameter == 0x5678, "got %#Ix\n", arg2 ); + ok( !d->Reserved0, "got %#Ix.\n", d->Reserved0 ); + ok( !d->Reserved1, "got %#Ix.\n", d->Reserved1 ); + } + else + { + ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 ); + } ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 ); apc_count++; } @@ -5230,8 +5254,14 @@ static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context) context, context->Rip, context->Rsp, (char *)context->Rsp - (char *)context, context->ContextFlags );
- ok( context->P1Home == 0x1234, "wrong p1 %#Ix\n", context->P1Home ); - ok( context->P2Home == 0x5678, "wrong p2 %#Ix\n", context->P2Home ); + ok( context->P1Home == 0x1234 || + broken(apc_flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC && (context->P1Home >> 48) == 0xffff) /* Win 10 1809v2 */, + "wrong p1 %#Ix\n", context->P1Home ); + if (context->P1Home != 0x1234) + { + win_skip( "Broken APC layout, skipping tests.\n" ); + goto done; + } ok( context->P3Home == 0xdeadbeef, "wrong p3 %#Ix\n", context->P3Home ); ok( context->P4Home == (ULONG_PTR)apc_func, "wrong p4 %#Ix / %p\n", context->P4Home, apc_func );
@@ -5241,9 +5271,38 @@ static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context) if (frame->rip == context->Rip) break; frame = (struct machine_frame *)((ULONG64 *)frame + 2); } + + if ((char *)frame - (char *)context == 0x530) + { + KCONTINUE_ARGUMENT *continue_arg; + APC_CALLBACK_DATA *data; + + continue_arg = (KCONTINUE_ARGUMENT *)((char *)context + sizeof(CONTEXT) + sizeof(CONTEXT_EX)); + data = (APC_CALLBACK_DATA *)((char *)continue_arg + sizeof(*continue_arg) + sizeof(void *)); + ok( (char *)frame == (char *)(data + 1), "got %p, %p.\n", data, frame ); + if (apc_flags & QUEUE_USER_APC_CALLBACK_DATA_CONTEXT) + ok( data == (void *)context->P2Home, "got %p, %p.\n", data, (void *)context->P2Home); + + ok( continue_arg->ContinueType == KCONTINUE_RESUME, "got %d.\n", continue_arg->ContinueType ); + if (apc_flags & QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC) + todo_wine ok( continue_arg->ContinueFlags == KCONTINUE_FLAG_DELIVER_APC, + "got %d.\n", continue_arg->ContinueType ); + else + ok( continue_arg->ContinueFlags == (KCONTINUE_FLAG_TEST_ALERT | KCONTINUE_FLAG_DELIVER_APC), + "got %d.\n", continue_arg->ContinueType ); + } + else + { + if (is_arm64ec) + skip( "Unsupported stack layout, skipping exact layout test.\n" ); + else + win_skip( "Unsupported stack layout, skipping exact layout test.\n" ); + } + ok( frame->rip == context->Rip, "wrong rip %#Ix / %#Ix\n", frame->rip, context->Rip ); ok( frame->rsp == context->Rsp, "wrong rsp %#Ix / %#Ix\n", frame->rsp, context->Rsp );
+done: hook_called = TRUE; memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher)); return pKiUserApcDispatcher; @@ -5262,6 +5321,7 @@ static void test_KiUserApcDispatcher(void)
BYTE patched_KiUserApcDispatcher[12]; DWORD old_protect; + NTSTATUS status; BYTE *ptr; BOOL ret;
@@ -5286,12 +5346,61 @@ static void test_KiUserApcDispatcher(void)
hook_called = FALSE; apc_count = 0; - pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + apc_flags = 0; + status = pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); SleepEx( 0, TRUE ); ok( apc_count == 1, "APC was not called\n" ); /* hooking is bypassed on arm64ec */ ok( is_arm64ec ? !hook_called : hook_called, "hook was not called\n" );
+ if (!pNtQueueApcThreadEx) + { + win_skip( "NtQueueApcThreadEx is not available.\n" ); + goto done; + } + + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + hook_called = FALSE; + apc_count = 0; + apc_flags = QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC; + status = pNtQueueApcThreadEx( GetCurrentThread(), (HANDLE)QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, apc_func, + 0x1234, 0x5678, 0xdeadbeef ); + ok( !status || broken( status == STATUS_INVALID_HANDLE ) /* before Win10 1809 */, "got %#lx.\n", status ); + if (!status) + { + todo_wine ok( apc_count == 1, "got %u.\n", apc_count ); + SleepEx( 0, TRUE ); + ok( apc_count == 1, "APC was not called\n" ); + /* hooking is bypassed on arm64ec */ + ok( is_arm64ec ? !hook_called : hook_called, "hook was not called\n" ); + } + + if (!pNtQueueApcThreadEx2) + { + win_skip( "NtQueueApcThreadEx2 is not available.\n" ); + goto done; + } + + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + hook_called = FALSE; + apc_count = 0; + apc_flags = QUEUE_USER_APC_CALLBACK_DATA_CONTEXT; + status = pNtQueueApcThreadEx2( GetCurrentThread(), NULL, QUEUE_USER_APC_CALLBACK_DATA_CONTEXT, + apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status || broken(status == STATUS_INVALID_PARAMETER /* Before Win11 22H2 */), "got %#lx.\n", status ); + ok( apc_count == 0, "got %u.\n", apc_count ); + if (!status) + { + SleepEx( 0, TRUE ); + ok( apc_count == 1, "APC was not called\n" ); + /* hooking is bypassed on arm64ec */ + ok( is_arm64ec ? !hook_called : hook_called, "hook was not called\n" ); + } + +done: + apc_flags = 0; + VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect ); }
@@ -12201,6 +12310,8 @@ START_TEST(exception) X(NtGetContextThread); X(NtSetContextThread); X(NtQueueApcThread); + X(NtQueueApcThreadEx); + X(NtQueueApcThreadEx2); X(NtContinueEx); X(NtReadVirtualMemory); X(NtClose); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 59815a47497..ddfa0381256 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -376,11 +376,16 @@ C_ASSERT( sizeof(struct exc_stack_layout) == 0x5c0 ); struct apc_stack_layout { CONTEXT context; /* 000 */ - struct machine_frame machine_frame; /* 4d0 */ - ULONG64 align; /* 4f8 */ + CONTEXT_EX context_ex; /* 4d0 */ + KCONTINUE_ARGUMENT continue_arg; /* 4f0 */ + ULONG64 align1; /* 4f8 */ + APC_CALLBACK_DATA callback_data; /* 510 */ + struct machine_frame machine_frame; /* 530 */ + ULONG64 align2; /* 558 */ }; -C_ASSERT( offsetof(struct apc_stack_layout, machine_frame) == 0x4d0 ); -C_ASSERT( sizeof(struct apc_stack_layout) == 0x500 ); +C_ASSERT( offsetof(struct apc_stack_layout, continue_arg) == 0x4f0 ); +C_ASSERT( offsetof(struct apc_stack_layout, machine_frame) == 0x530 ); +C_ASSERT( sizeof(struct apc_stack_layout) == 0x560 );
/* stack layout when calling KiUserCallbackDispatcher */ struct callback_stack_layout @@ -1539,7 +1544,7 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_P ULONG64 rsp = context ? context->Rsp : frame->rsp; struct apc_stack_layout *stack;
- if (flags) FIXME( "flags %#x are not supported.\n", flags ); + if (flags & ~SERVER_USER_APC_CALLBACK_DATA_CONTEXT) FIXME( "flags %#x are not supported.\n", flags );
rsp &= ~15; stack = (struct apc_stack_layout *)rsp - 1; @@ -1554,8 +1559,19 @@ NTSTATUS call_user_apc_dispatcher( CONTEXT *context, unsigned int flags, ULONG_P NtGetContextThread( GetCurrentThread(), &stack->context ); stack->context.Rax = status; } + memset( &stack->continue_arg, 0, sizeof(stack->continue_arg) ); + stack->continue_arg.ContinueType = KCONTINUE_RESUME; + stack->continue_arg.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT | KCONTINUE_FLAG_DELIVER_APC; stack->context.P1Home = arg1; - stack->context.P2Home = arg2; + if (flags & SERVER_USER_APC_CALLBACK_DATA_CONTEXT) + { + stack->callback_data.ContextRecord = &stack->context; + stack->callback_data.Parameter = arg2; + stack->callback_data.Reserved0 = 0; + stack->callback_data.Reserved1 = 0; + stack->context.P2Home = (ULONG_PTR)&stack->callback_data; + } + else stack->context.P2Home = arg2; stack->context.P3Home = arg3; stack->context.P4Home = (ULONG64)func; stack->machine_frame.rip = stack->context.Rip; diff --git a/include/processthreadsapi.h b/include/processthreadsapi.h index f5f033c99b2..c2058245469 100644 --- a/include/processthreadsapi.h +++ b/include/processthreadsapi.h @@ -56,6 +56,14 @@ typedef enum _QUEUE_USER_APC_FLAGS QUEUE_USER_APC_CALLBACK_DATA_CONTEXT = 0x00010000, } QUEUE_USER_APC_FLAGS;
+typedef struct _APC_CALLBACK_DATA +{ + ULONG_PTR Parameter; + CONTEXT *ContextRecord; + ULONG_PTR Reserved0; + ULONG_PTR Reserved1; +} APC_CALLBACK_DATA, *PAPC_CALLBACK_DATA; + #ifdef __cplusplus } #endif
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/signal_x86_64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 31070566d40..1f184f4ea58 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -377,8 +377,8 @@ __ASM_GLOBAL_FUNC( KiUserApcDispatcher, "movq %rsp,%r9\n\t" /* context */ "callq *%rax\n\t" "movq %rsp,%rcx\n\t" /* context */ - "movl $1,%edx\n\t" /* alertable */ - "call " __ASM_NAME("NtContinue") "\n\t" + "leaq 0x4f0(%rsp),%rdx\n\t" /* continue_arg */ + "call " __ASM_NAME("NtContinueEx") "\n\t" "int3" )
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/kernel32.spec | 1 + dlls/kernel32/tests/sync.c | 30 ++++++++++++++++++++++++++++++ dlls/kernelbase/kernelbase.spec | 1 + dlls/kernelbase/thread.c | 9 +++++++++ include/winbase.h | 1 + 5 files changed, 42 insertions(+)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 83c374d2254..3bb49206c6b 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -1213,6 +1213,7 @@ @ stdcall -import QueryUnbiasedInterruptTime(ptr) @ stub QueryWin31IniFilesMappedToRegistry @ stdcall -import QueueUserAPC(ptr long long) +@ stdcall -import QueueUserAPC2(ptr long long long) @ stdcall -import QueueUserWorkItem(ptr ptr long) @ stdcall -import RaiseException(long long long ptr) @ stdcall -import RaiseFailFastException(ptr ptr long) diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index 25ed4aec6a0..49f5002f651 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -56,6 +56,8 @@ static VOID (WINAPI *pReleaseSRWLockShared)(PSRWLOCK); static BOOLEAN (WINAPI *pTryAcquireSRWLockExclusive)(PSRWLOCK); static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK);
+static DWORD (WINAPI *pQueueUserAPC2)(PAPCFUNC,HANDLE,ULONG_PTR,QUEUE_USER_APC_FLAGS); + static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG_PTR, SIZE_T *, ULONG, ULONG); static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG); static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *); @@ -2959,6 +2961,33 @@ static void test_QueueUserAPC(void) status = pNtTestAlert(); ok(!status, "got %lx\n", status); ok(apc_count == 1, "APC count %u\n", apc_count); + + if (!pQueueUserAPC2) + { + win_skip("QueueUserAPC2 is not available.\n"); + return; + } + + apc_count = 0; + ret = pQueueUserAPC2(user_apc, GetCurrentThread(), 0, QUEUE_USER_APC_FLAGS_NONE); + ok(ret, "QueueUserAPC failed err %lu\n", GetLastError()); + ok(!apc_count, "got %d.\n", apc_count); + SleepEx( 0, TRUE ); + ok(apc_count == 1, "got %d.\n", apc_count); + + apc_count = 0; + ret = pQueueUserAPC2(user_apc, GetCurrentThread(), 0, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC); + if (sizeof(void *) == 4) + ok(!ret && GetLastError() == ERROR_NOT_SUPPORTED, "got ret %lu, error %lu.\n", ret, GetLastError()); + else + ok(ret, "got error %lu.\n", GetLastError()); + + if (ret) + { + todo_wine ok(apc_count == 1, "got %d.\n", apc_count); + SleepEx( 0, TRUE ); + ok(apc_count == 1, "got %d.\n", apc_count); + } }
START_TEST(sync) @@ -2985,6 +3014,7 @@ START_TEST(sync) pReleaseSRWLockShared = (void *)GetProcAddress(hdll, "ReleaseSRWLockShared"); pTryAcquireSRWLockExclusive = (void *)GetProcAddress(hdll, "TryAcquireSRWLockExclusive"); pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared"); + pQueueUserAPC2 = (void *)GetProcAddress(hdll, "QueueUserAPC2"); pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory"); pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory"); pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject"); diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 545c30a1eb8..7681815b7e7 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1264,6 +1264,7 @@ @ stdcall QueryWorkingSet(long ptr long) @ stdcall QueryWorkingSetEx(long ptr long) @ stdcall QueueUserAPC(ptr long long) +@ stdcall QueueUserAPC2(ptr long long long) @ stdcall QueueUserWorkItem(ptr ptr long) # @ stub QuirkGetData # @ stub QuirkGetData2 diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index bc8210bd43a..dbde4dcfaf3 100644 --- a/dlls/kernelbase/thread.c +++ b/dlls/kernelbase/thread.c @@ -369,6 +369,15 @@ DWORD WINAPI DECLSPEC_HOTPATCH QueueUserAPC( PAPCFUNC func, HANDLE thread, ULONG }
+/*********************************************************************** + * QueueUserAPC2 (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH QueueUserAPC2( PAPCFUNC func, HANDLE thread, ULONG_PTR data, QUEUE_USER_APC_FLAGS flags ) +{ + return set_ntstatus( NtQueueApcThreadEx2( thread, NULL, flags, call_user_apc, (ULONG_PTR)func, data, 0 )); +} + + /*********************************************************************** * QueryThreadCycleTime (kernelbase.@) */ diff --git a/include/winbase.h b/include/winbase.h index 29a3ea8fbce..65499319704 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -2293,6 +2293,7 @@ WINBASEAPI BOOL WINAPI QueryPerformanceCounter(LARGE_INTEGER*); WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER*); WINBASEAPI BOOL WINAPI QueryUmsThreadInformation(PUMS_CONTEXT,UMS_THREAD_INFO_CLASS,PVOID,ULONG,PULONG); WINBASEAPI DWORD WINAPI QueueUserAPC(PAPCFUNC,HANDLE,ULONG_PTR); +WINBASEAPI DWORD WINAPI QueueUserAPC2(PAPCFUNC,HANDLE,ULONG_PTR,QUEUE_USER_APC_FLAGS); WINBASEAPI BOOL WINAPI QueueUserWorkItem(LPTHREAD_START_ROUTINE,PVOID,ULONG); WINBASEAPI void WINAPI RaiseException(DWORD,DWORD,DWORD,const ULONG_PTR *); WINBASEAPI HANDLE WINAPI ReOpenFile(HANDLE,DWORD,DWORD,DWORD);