[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**)¤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());
--
2.27.0
More information about the wine-devel
mailing list