Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/ntoskrnl.exe/tests/driver.c | 133 ++++++++++++++++++++++++++++- dlls/ntoskrnl.exe/tests/driver.h | 2 + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 39 +++++++++ 3 files changed, 173 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index c1b19b7ed8b..481035557ba 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -47,7 +47,7 @@ static const WCHAR driver_link[] = {'\','D','o','s','D','e','v','i','c','e','s' static DRIVER_OBJECT *driver_obj; static DEVICE_OBJECT *lower_device, *upper_device;
-static POBJECT_TYPE *pExEventObjectType, *pIoFileObjectType, *pPsThreadType, *pIoDriverObjectType; +static POBJECT_TYPE *pExEventObjectType, *pIoFileObjectType, *pPsThreadType, *pPsProcessType, *pIoDriverObjectType; static PEPROCESS *pPsInitialSystemProcess; static void *create_caller_thread;
@@ -1970,6 +1970,133 @@ static void test_dpc(void) KeRevertToUserAffinityThread(); }
+PKTHREAD current_apc_thread; + +void WINAPI apc_receiver(PVOID context) +{ + /* kernel mode APCs don't require an alertable wait */ + wait_single((PKEVENT)context, 50 * -10000000); + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +enum +{ + SPECIAL_KERNEL, + NORMAL_KERNEL, + NORMAL_USER, +} current_apc_type; + +static void WINAPI kernel_routine(PKAPC apc, PKNORMAL_ROUTINE *nrml_routine, PVOID *normal_ctx, PVOID *sysarg1, PVOID *sysarg2) +{ + ok(KeGetCurrentThread() == current_apc_thread, "Got unexpected current thread: %p!=%p\n", KeGetCurrentThread(), current_apc_thread); + ok(apc && nrml_routine && normal_ctx && sysarg1 && sysarg2, "Missing parameter pointer in kernel_routine\n"); + if (current_apc_type == SPECIAL_KERNEL) + { + ok(*nrml_routine == NULL, "Got non-null normal routine value %p in special APC.\n", *nrml_routine); + ok(*normal_ctx == NULL, "Got unexpected normal context %p in APC.\n", *normal_ctx); + KeSetEvent((PKEVENT)*sysarg2, 0, FALSE); + } + else + { + ok(*nrml_routine == (PVOID)(ULONG_PTR)0xdeadbeef, "Got unexpected initial normal routine value: %p\n", *normal_ctx); + ok(*normal_ctx == (PVOID)(ULONG_PTR)0xdeadbeef, "Got unexpected normal context: %p\n", *normal_ctx); + } + /* in a special kernel APC, the normal routine we set should be ignored */ + /* in a normal routine, the special section has the final say on the normal routine and parameters */ + *nrml_routine = *sysarg1; +} + +static void WINAPI rundown_routine(PKAPC apc) +{ + ok(FALSE, "Unexpected execution of rundown routine\n"); +} + +static void WINAPI normal_routine(PVOID normal_ctx, PVOID sysarg1, PVOID sysarg2) +{ + ok(KeGetCurrentThread() == current_apc_thread, "Got unexpected current thread: %p!=%p\n", KeGetCurrentThread(), current_apc_thread); + ok (current_apc_type == NORMAL_KERNEL, "Got unexpected current APC type: %u\n", current_apc_type); + KeSetEvent((PKEVENT)sysarg2, 0, FALSE); +} + +static void test_apc(const struct test_input *test_input) +{ + void (WINAPI *pKeInitializeApc)(PRKAPC,PRKTHREAD,KAPC_ENVIRONMENT,PKKERNEL_ROUTINE,PKRUNDOWN_ROUTINE,PKNORMAL_ROUTINE,KPROCESSOR_MODE,PVOID); + BOOLEAN (WINAPI *pKeInsertQueueApc)(PKAPC,PVOID,PVOID,KPRIORITY); + OBJECT_ATTRIBUTES attr; + PEPROCESS user_process; + PKTHREAD user_apc_thread; + HANDLE user_process_handle; + HANDLE done_event_system_handle, done_event_user_handle; + KEVENT *done_event, terminate_event; + HANDLE apc_reciever_thread; + KAPC special_apc, kernel_apc, user_apc; + NTSTATUS stat; + + pKeInitializeApc = get_proc_address("KeInitializeApc"); + pKeInsertQueueApc = get_proc_address("KeInsertQueueApc"); + +todo_wine + ok(pKeInitializeApc && pKeInsertQueueApc, "Unable to find KeInitializeApc and KeInsertQueueApc\n"); + if (!pKeInitializeApc || !pKeInsertQueueApc) + return; + + InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + ZwCreateEvent(&done_event_system_handle, SYNCHRONIZE|EVENT_MODIFY_STATE, &attr, SynchronizationEvent, FALSE); + ObReferenceObjectByHandle(done_event_system_handle, 0, *pExEventObjectType, KernelMode, (void**)&done_event, NULL); + + KeInitializeEvent(&terminate_event, NotificationEvent, FALSE); + + apc_reciever_thread = create_thread(apc_receiver, &terminate_event); + ObReferenceObjectByHandle(apc_reciever_thread, 0, *pPsThreadType, KernelMode, (void**)¤t_apc_thread, NULL); + ZwClose(apc_reciever_thread); + + current_apc_type = SPECIAL_KERNEL; + pKeInitializeApc(&special_apc, current_apc_thread, OriginalApcEnvironment, kernel_routine, rundown_routine, + NULL, KernelMode, (PVOID)(ULONG_PTR)0xdeadbeef); + pKeInsertQueueApc(&special_apc, normal_routine, done_event, 0); + stat = wait_single(done_event, 5 * -10000000); + ok(stat == STATUS_WAIT_0, "Waiting on special kernel APC to complete failed: %#x\n", stat); + + current_apc_type = NORMAL_KERNEL; + pKeInitializeApc(&kernel_apc, current_apc_thread, OriginalApcEnvironment, kernel_routine, rundown_routine, + (PVOID)(ULONG_PTR)0xdeadbeef, KernelMode, (PVOID)(ULONG_PTR)0xdeadbeef); + pKeInsertQueueApc(&kernel_apc, normal_routine, done_event, 0); + stat = wait_single(done_event, 5 * -10000000); + ok(stat == STATUS_WAIT_0, "Waiting on normal kernel APC to complete failed: %#x\n", stat); + + KeSetEvent(&terminate_event, 0, FALSE); + ObDereferenceObject(current_apc_thread); + + /* Get usermode thread accepting APCs */ + stat = PsLookupThreadByThreadId((HANDLE)(ULONG_PTR)test_input->apc_thread_id, (PETHREAD *)&user_apc_thread); + ok(stat == STATUS_SUCCESS, "Got unexpected status %#x.\n", stat); + + /* create handle for done event (once user mode handles are supported, this won't be necessary) */ + stat = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)test_input->process_id, &user_process); + ok(stat == STATUS_SUCCESS, "Got unexpected status %#x.\n", stat); + + stat = ObOpenObjectByPointer(user_process, OBJ_KERNEL_HANDLE, NULL, PROCESS_DUP_HANDLE, *pPsProcessType, KernelMode, &user_process_handle); + ok(stat == STATUS_SUCCESS, "Got unexpected status %#x.\n", stat); + ObDereferenceObject(user_process); + + stat = ZwDuplicateObject(NtCurrentProcess(), done_event_system_handle, user_process_handle, &done_event_user_handle, SYNCHRONIZE|EVENT_MODIFY_STATE, 0, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE); + ok(stat == STATUS_SUCCESS, "Got unexpected status %#x.\n", stat); + + current_apc_type = NORMAL_USER; + current_apc_thread = user_apc_thread; + pKeInitializeApc(&user_apc, user_apc_thread, OriginalApcEnvironment, kernel_routine, rundown_routine, + (PVOID)(ULONG_PTR)0xdeadbeef, UserMode, (PVOID)(ULONG_PTR)0xdeadbeef); + pKeInsertQueueApc(&user_apc, test_input->apc_func, done_event_user_handle, 0); + + stat = wait_single(done_event, 5 * -10000000); + ok(stat == STATUS_WAIT_0, "Waiting on user APC to complete failed: %#x\n", stat); + + ZwClose(done_event_user_handle); + ObDereferenceObject(done_event); + ObDereferenceObject(current_apc_thread); +} + static void test_process_memory(const struct test_input *test_input) { NTSTATUS (WINAPI *pMmCopyVirtualMemory)(PEPROCESS fromprocess, void *fromaddress, PEPROCESS toprocess, @@ -2115,6 +2242,9 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st pPsThreadType = get_proc_address("PsThreadType"); ok(!!pPsThreadType, "IofileObjectType not found\n");
+ pPsProcessType = get_proc_address("PsProcessType"); + ok(!!pPsProcessType, "PsProcessType not found\n"); + pPsInitialSystemProcess = get_proc_address("PsInitialSystemProcess"); ok(!!pPsInitialSystemProcess, "PsInitialSystemProcess not found\n");
@@ -2138,6 +2268,7 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st #endif test_affinity(); test_dpc(); + test_apc(test_input); test_process_memory(test_input); test_permanence();
diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index 58a92d4838e..d0a609329e0 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -44,6 +44,8 @@ struct test_input int winetest_report_success; int winetest_debug; DWORD process_id; + DWORD apc_thread_id; + PKNORMAL_ROUTINE apc_func; SIZE_T teststr_offset; ULONG64 *modified_value; WCHAR path[1]; diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index b4ef9d0dcb7..269acb7f43d 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -30,6 +30,7 @@ #include "winsock2.h" #include "wine/test.h" #include "wine/heap.h" +#include "ddk/wdm.h"
#include "driver.h"
@@ -153,6 +154,26 @@ static BOOL start_driver(HANDLE service, BOOL vista_plus) }
static ULONG64 modified_value; +static BOOL apc_ran = FALSE; + +DWORD WINAPI apc_thread_func(PVOID param) +{ + HANDLE done_event = param; + DWORD ret; + + WaitForSingleObjectEx(done_event, INFINITE, TRUE); + ok (apc_ran == TRUE, "Driver failed to queue user mode APC\n"); + return 0; +} + +static void WINAPI apc_func(PVOID normal_ctx, PVOID sysarg1, PVOID sysarg2) +{ + HANDLE done_event = sysarg2; + + ok(normal_ctx == (PVOID)(ULONG_PTR)0xdeadbeef, "Unexpected normal context %p\n", normal_ctx); + apc_ran = TRUE; + ok(SetEvent(done_event), "Setting done event failed: %u\n", GetLastError()); +}
static void main_test(void) { @@ -160,8 +181,11 @@ static void main_test(void) WCHAR temppathW[MAX_PATH], pathW[MAX_PATH]; struct test_input *test_input; DWORD len, written, read; + DWORD apc_thread_id = 0; UNICODE_STRING pathU; LONG new_failures; + HANDLE apc_thread; + HANDLE done_event; char buffer[512]; HANDLE okfile; BOOL res; @@ -171,12 +195,24 @@ static void main_test(void) GetTempFileNameW(temppathW, dokW, 0, pathW); pRtlDosPathNameToNtPathName_U( pathW, &pathU, NULL, NULL );
+ done_event = CreateEventW(NULL, TRUE, FALSE, NULL); + if (done_event) + { + if ((apc_thread = CreateThread(NULL, 0, apc_thread_func, done_event, 0, &apc_thread_id))) + CloseHandle(apc_thread); + else + CloseHandle(done_event); + } + ok (apc_thread_id, "APC thread creation failed: %u\n", GetLastError()); + len = pathU.Length + sizeof(WCHAR); test_input = heap_alloc( offsetof( struct test_input, path[len / sizeof(WCHAR)]) ); test_input->running_under_wine = !strcmp(winetest_platform, "wine"); test_input->winetest_report_success = winetest_report_success; test_input->winetest_debug = winetest_debug; test_input->process_id = GetCurrentProcessId(); + test_input->apc_thread_id = apc_thread_id; + test_input->apc_func = apc_func; test_input->teststr_offset = (SIZE_T)((BYTE *)&teststr - (BYTE *)NtCurrentTeb()->Peb->ImageBaseAddress); test_input->modified_value = &modified_value; modified_value = 0; @@ -188,6 +224,9 @@ static void main_test(void) ok(res, "DeviceIoControl failed: %u\n", GetLastError()); ok(written == sizeof(new_failures), "got size %x\n", written);
+ if (apc_thread_id) + SetEvent(done_event); + okfile = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); ok(okfile != INVALID_HANDLE_VALUE, "failed to create %s: %u\n", wine_dbgstr_w(pathW), GetLastError());