[PATCH] ntoskrnl.exe: Add tests for KeInsertQueueApc.

Derek Lesho dlesho at codeweavers.com
Fri Jul 17 15:50:38 CDT 2020


Signed-off-by: Derek Lesho <dlesho at 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**)&current_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());
 
-- 
2.27.0




More information about the wine-devel mailing list