2702 lines
97 KiB
C
2702 lines
97 KiB
C
/*
|
|
* ntoskrnl.exe testing framework
|
|
*
|
|
* Copyright 2015 Sebastian Lackner
|
|
* Copyright 2015 Michael Müller
|
|
* Copyright 2015 Christian Costa
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winternl.h"
|
|
#include "winioctl.h"
|
|
#include "ddk/ntddk.h"
|
|
#include "ddk/ntifs.h"
|
|
#include "ddk/wdm.h"
|
|
|
|
#include "driver.h"
|
|
|
|
#include "utils.h"
|
|
|
|
/* memcmp() isn't exported from ntoskrnl on i386 */
|
|
static int kmemcmp( const void *ptr1, const void *ptr2, size_t n )
|
|
{
|
|
const unsigned char *p1, *p2;
|
|
|
|
for (p1 = ptr1, p2 = ptr2; n; n--, p1++, p2++)
|
|
{
|
|
if (*p1 < *p2) return -1;
|
|
if (*p1 > *p2) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static DRIVER_OBJECT *driver_obj;
|
|
static DEVICE_OBJECT *lower_device, *upper_device;
|
|
|
|
static POBJECT_TYPE *pExEventObjectType, *pIoFileObjectType, *pPsThreadType, *pIoDriverObjectType;
|
|
static PEPROCESS *pPsInitialSystemProcess;
|
|
static void *create_caller_thread;
|
|
|
|
static PETHREAD create_irp_thread;
|
|
|
|
NTSTATUS WINAPI ZwQueryInformationProcess(HANDLE,PROCESSINFOCLASS,void*,ULONG,ULONG*);
|
|
|
|
struct file_context
|
|
{
|
|
DWORD id;
|
|
ULONG namelen;
|
|
WCHAR name[10];
|
|
};
|
|
|
|
static void *get_proc_address(const char *name)
|
|
{
|
|
UNICODE_STRING name_u;
|
|
ANSI_STRING name_a;
|
|
NTSTATUS status;
|
|
void *ret;
|
|
|
|
RtlInitAnsiString(&name_a, name);
|
|
status = RtlAnsiStringToUnicodeString(&name_u, &name_a, TRUE);
|
|
ok (!status, "RtlAnsiStringToUnicodeString failed: %#x\n", status);
|
|
if (status) return NULL;
|
|
|
|
ret = MmGetSystemRoutineAddress(&name_u);
|
|
RtlFreeUnicodeString(&name_u);
|
|
return ret;
|
|
}
|
|
|
|
static FILE_OBJECT *last_created_file;
|
|
static unsigned int create_count, close_count;
|
|
|
|
static NTSTATUS WINAPI test_irp_struct_completion_routine(DEVICE_OBJECT *reserved, IRP *irp, void *context)
|
|
{
|
|
unsigned int *result = context;
|
|
|
|
*result = 1;
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
static void test_irp_struct(IRP *irp, DEVICE_OBJECT *device)
|
|
{
|
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
|
|
unsigned int irp_completion_result;
|
|
|
|
ok(device == upper_device, "Expected device %p, got %p.\n", upper_device, device);
|
|
ok(last_created_file != NULL, "last_created_file = NULL\n");
|
|
ok(irpsp->FileObject == last_created_file, "FileObject != last_created_file\n");
|
|
ok(irpsp->DeviceObject == upper_device, "unexpected DeviceObject\n");
|
|
ok(irpsp->FileObject->DeviceObject == lower_device, "unexpected FileObject->DeviceObject\n");
|
|
ok(!irp->UserEvent, "UserEvent = %p\n", irp->UserEvent);
|
|
ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(),
|
|
"IRP thread is not the current thread\n");
|
|
|
|
ok(IoGetRequestorProcess(irp) == IoGetCurrentProcess(), "processes didn't match\n");
|
|
|
|
irp = IoAllocateIrp(1, FALSE);
|
|
ok(irp->AllocationFlags == IRP_ALLOCATED_FIXED_SIZE, "Got unexpected irp->AllocationFlags %#x.\n",
|
|
irp->AllocationFlags);
|
|
ok(irp->CurrentLocation == 2,
|
|
"Got unexpected irp->CurrentLocation %u.\n", irp->CurrentLocation);
|
|
IoSetCompletionRoutine(irp, test_irp_struct_completion_routine, &irp_completion_result,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
irp_completion_result = 0;
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
--irp->CurrentLocation;
|
|
--irp->Tail.Overlay.CurrentStackLocation;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ok(irp->CurrentLocation == 2,
|
|
"Got unexpected irp->CurrentLocation %u.\n", irp->CurrentLocation);
|
|
ok(irp_completion_result, "IRP completion was not called.\n");
|
|
|
|
--irp->CurrentLocation;
|
|
--irp->Tail.Overlay.CurrentStackLocation;
|
|
IoReuseIrp(irp, STATUS_UNSUCCESSFUL);
|
|
ok(irp->CurrentLocation == 2,
|
|
"Got unexpected irp->CurrentLocation %u.\n", irp->CurrentLocation);
|
|
ok(irp->AllocationFlags == IRP_ALLOCATED_FIXED_SIZE, "Got unexpected irp->AllocationFlags %#x.\n",
|
|
irp->AllocationFlags);
|
|
|
|
IoFreeIrp(irp);
|
|
}
|
|
|
|
static int cancel_queue_cnt;
|
|
|
|
static void WINAPI cancel_queued_irp(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(!irp->CancelRoutine, "CancelRoutine = %p\n", irp->CancelRoutine);
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irp->IoStatus.Information = 0;
|
|
cancel_queue_cnt++;
|
|
}
|
|
|
|
static void test_queue(void)
|
|
{
|
|
KDEVICE_QUEUE_ENTRY *entry;
|
|
KDEVICE_QUEUE queue;
|
|
BOOLEAN ret;
|
|
KIRQL irql;
|
|
IRP *irp;
|
|
|
|
irp = IoAllocateIrp(1, FALSE);
|
|
|
|
memset(&queue, 0xcd, sizeof(queue));
|
|
KeInitializeDeviceQueue(&queue);
|
|
ok(!queue.Busy, "unexpected Busy state\n");
|
|
ok(queue.Size == sizeof(queue), "unexpected Size %x\n", queue.Size);
|
|
ok(queue.Type == IO_TYPE_DEVICE_QUEUE, "unexpected Type %x\n", queue.Type);
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(!ret, "expected KeInsertDeviceQueue to not insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected not inserted\n");
|
|
|
|
entry = KeRemoveDeviceQueue(&queue);
|
|
ok(!entry, "expected KeRemoveDeviceQueue to return NULL\n");
|
|
ok(!queue.Busy, "unexpected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected not inserted\n");
|
|
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(!ret, "expected KeInsertDeviceQueue to not insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected not inserted\n");
|
|
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(ret, "expected KeInsertDeviceQueue to insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(!IsListEmpty(&queue.DeviceListHead), "unexpected empty queue list\n");
|
|
ok(irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
ok(queue.DeviceListHead.Flink == &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry,
|
|
"unexpected queue list head\n");
|
|
|
|
entry = KeRemoveDeviceQueue(&queue);
|
|
ok(entry != NULL, "expected KeRemoveDeviceQueue to return non-NULL\n");
|
|
ok(CONTAINING_RECORD(entry, IRP, Tail.Overlay.DeviceQueueEntry) == irp,
|
|
"unexpected IRP returned\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected not inserted\n");
|
|
|
|
entry = KeRemoveDeviceQueue(&queue);
|
|
ok(entry == NULL, "expected KeRemoveDeviceQueue to return NULL\n");
|
|
ok(!queue.Busy, "unexpected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected not inserted\n");
|
|
|
|
IoCancelIrp(irp);
|
|
ok(irp->Cancel, "unexpected non-cancelled state\n");
|
|
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(!ret, "expected KeInsertDeviceQueue to not insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(ret, "expected KeInsertDeviceQueue to insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(!IsListEmpty(&queue.DeviceListHead), "unexpected empty queue list\n");
|
|
ok(irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
ok(queue.DeviceListHead.Flink == &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry,
|
|
"unexpected queue list head\n");
|
|
|
|
entry = KeRemoveDeviceQueue(&queue);
|
|
ok(entry != NULL, "expected KeRemoveDeviceQueue to return non-NULL\n");
|
|
ok(CONTAINING_RECORD(entry, IRP, Tail.Overlay.DeviceQueueEntry) == irp,
|
|
"unexpected IRP returned\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
ok(irp->Cancel, "unexpected non-cancelled state\n");
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
irp = IoAllocateIrp(1, FALSE);
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
IoSetCancelRoutine(irp, cancel_queued_irp);
|
|
IoReleaseCancelSpinLock(irql);
|
|
|
|
ret = KeInsertDeviceQueue(&queue, &irp->Tail.Overlay.DeviceQueueEntry);
|
|
ok(ret, "expected KeInsertDeviceQueue to insert IRP\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(!IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
ok(queue.DeviceListHead.Flink == &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry,
|
|
"unexpected queue list head\n");
|
|
|
|
IoCancelIrp(irp);
|
|
ok(irp->Cancel, "unexpected non-cancelled state\n");
|
|
ok(cancel_queue_cnt, "expected cancel routine to be called\n");
|
|
|
|
entry = KeRemoveDeviceQueue(&queue);
|
|
ok(entry != NULL, "expected KeRemoveDeviceQueue to return non-NULL\n");
|
|
ok(CONTAINING_RECORD(entry, IRP, Tail.Overlay.DeviceQueueEntry) == irp,
|
|
"unexpected IRP returned\n");
|
|
ok(irp->Cancel, "unexpected non-cancelled state\n");
|
|
ok(cancel_queue_cnt, "expected cancel routine to be called\n");
|
|
ok(queue.Busy, "expected Busy state\n");
|
|
ok(IsListEmpty(&queue.DeviceListHead), "expected empty queue list\n");
|
|
ok(!irp->Tail.Overlay.DeviceQueueEntry.Inserted, "expected inserted\n");
|
|
|
|
IoFreeIrp(irp);
|
|
}
|
|
|
|
static void test_mdl_map(void)
|
|
{
|
|
char buffer[20] = "test buffer";
|
|
void *addr;
|
|
MDL *mdl;
|
|
|
|
mdl = IoAllocateMdl(buffer, sizeof(buffer), FALSE, FALSE, NULL);
|
|
ok(mdl != NULL, "IoAllocateMdl failed\n");
|
|
|
|
MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);
|
|
|
|
addr = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, NULL, FALSE, NormalPagePriority);
|
|
todo_wine
|
|
ok(addr != NULL, "MmMapLockedPagesSpecifyCache failed\n");
|
|
|
|
MmUnmapLockedPages(addr, mdl);
|
|
|
|
IoFreeMdl(mdl);
|
|
}
|
|
|
|
static void test_init_funcs(void)
|
|
{
|
|
KTIMER timer, timer2;
|
|
|
|
KeInitializeTimerEx(&timer, NotificationTimer);
|
|
ok(timer.Header.Type == 8, "got: %u\n", timer.Header.Type);
|
|
ok(timer.Header.Size == 0 || timer.Header.Size == 10, "got: %u\n", timer.Header.Size);
|
|
ok(timer.Header.SignalState == 0, "got: %u\n", timer.Header.SignalState);
|
|
|
|
KeInitializeTimerEx(&timer2, SynchronizationTimer);
|
|
ok(timer2.Header.Type == 9, "got: %u\n", timer2.Header.Type);
|
|
ok(timer2.Header.Size == 0 || timer2.Header.Size == 10, "got: %u\n", timer2.Header.Size);
|
|
ok(timer2.Header.SignalState == 0, "got: %u\n", timer2.Header.SignalState);
|
|
}
|
|
|
|
static const WCHAR driver2_path[] =
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WineTestDriver2";
|
|
|
|
static IMAGE_INFO test_image_info;
|
|
static int test_load_image_notify_count;
|
|
static WCHAR test_load_image_name[MAX_PATH];
|
|
|
|
static void WINAPI test_load_image_notify_routine(UNICODE_STRING *image_name, HANDLE process_id,
|
|
IMAGE_INFO *image_info)
|
|
{
|
|
if (test_load_image_notify_count == -1
|
|
|| (image_name->Buffer && wcsstr(image_name->Buffer, L".tmp")))
|
|
{
|
|
++test_load_image_notify_count;
|
|
test_image_info = *image_info;
|
|
wcscpy(test_load_image_name, image_name->Buffer);
|
|
}
|
|
}
|
|
|
|
static void test_load_driver(void)
|
|
{
|
|
char full_name_buffer[300];
|
|
OBJECT_NAME_INFORMATION *full_name = (OBJECT_NAME_INFORMATION *)full_name_buffer;
|
|
static WCHAR image_path_key_name[] = L"ImagePath";
|
|
RTL_QUERY_REGISTRY_TABLE query_table[2];
|
|
UNICODE_STRING name, image_path;
|
|
OBJECT_ATTRIBUTES attr;
|
|
IO_STATUS_BLOCK io;
|
|
NTSTATUS ret;
|
|
HANDLE file;
|
|
|
|
ret = PsSetLoadImageNotifyRoutine(test_load_image_notify_routine);
|
|
ok(ret == STATUS_SUCCESS, "Got unexpected status %#x.\n", ret);
|
|
|
|
/* Routine gets registered twice on Windows. */
|
|
ret = PsSetLoadImageNotifyRoutine(test_load_image_notify_routine);
|
|
ok(ret == STATUS_SUCCESS, "Got unexpected status %#x.\n", ret);
|
|
|
|
RtlInitUnicodeString(&image_path, NULL);
|
|
memset(query_table, 0, sizeof(query_table));
|
|
query_table[0].QueryRoutine = NULL;
|
|
query_table[0].Name = image_path_key_name;
|
|
query_table[0].EntryContext = &image_path;
|
|
query_table[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
|
|
query_table[0].DefaultType = REG_EXPAND_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT;
|
|
|
|
ret = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, driver2_path, query_table, NULL, NULL);
|
|
ok(ret == STATUS_SUCCESS, "Got unexpected status %#x.\n", ret);
|
|
ok(!!image_path.Buffer, "image_path.Buffer is NULL.\n");
|
|
|
|
/* The image path name in the registry may contain NT symlinks (e.g. DOS
|
|
* drives), which are resolved before the callback is called on Windows 10. */
|
|
InitializeObjectAttributes(&attr, &image_path, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
ret = ZwOpenFile(&file, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
|
todo_wine ok(!ret, "Got unexpected status %#x.\n", ret);
|
|
if (!ret)
|
|
{
|
|
ret = ZwQueryObject(file, ObjectNameInformation, full_name_buffer, sizeof(full_name_buffer), NULL);
|
|
ok(!ret, "Got unexpected status %#x.\n", ret);
|
|
ZwClose(file);
|
|
}
|
|
|
|
RtlInitUnicodeString(&name, driver2_path);
|
|
|
|
ret = ZwLoadDriver(&name);
|
|
ok(!ret, "got %#x\n", ret);
|
|
|
|
ok(test_load_image_notify_count == 2, "Got unexpected test_load_image_notify_count %u.\n",
|
|
test_load_image_notify_count);
|
|
ok(test_image_info.ImageAddressingMode == IMAGE_ADDRESSING_MODE_32BIT,
|
|
"Got unexpected ImageAddressingMode %#x.\n", test_image_info.ImageAddressingMode);
|
|
ok(test_image_info.SystemModeImage,
|
|
"Got unexpected SystemModeImage %#x.\n", test_image_info.SystemModeImage);
|
|
ok(!wcscmp(test_load_image_name, image_path.Buffer) /* Win < 10 */
|
|
|| !wcscmp(test_load_image_name, full_name->Name.Buffer),
|
|
"Expected image path name %ls, got %ls.\n", full_name->Name.Buffer, test_load_image_name);
|
|
|
|
test_load_image_notify_count = -1;
|
|
|
|
ret = ZwLoadDriver(&name);
|
|
ok(ret == STATUS_IMAGE_ALREADY_LOADED, "got %#x\n", ret);
|
|
|
|
ret = ZwUnloadDriver(&name);
|
|
ok(!ret, "got %#x\n", ret);
|
|
|
|
ret = PsRemoveLoadImageNotifyRoutine(test_load_image_notify_routine);
|
|
ok(ret == STATUS_SUCCESS, "Got unexpected status %#x.\n", ret);
|
|
ret = PsRemoveLoadImageNotifyRoutine(test_load_image_notify_routine);
|
|
ok(ret == STATUS_SUCCESS, "Got unexpected status %#x.\n", ret);
|
|
ret = PsRemoveLoadImageNotifyRoutine(test_load_image_notify_routine);
|
|
ok(ret == STATUS_PROCEDURE_NOT_FOUND, "Got unexpected status %#x.\n", ret);
|
|
|
|
ok(test_load_image_notify_count == -1, "Got unexpected test_load_image_notify_count %u.\n",
|
|
test_load_image_notify_count);
|
|
RtlFreeUnicodeString(&image_path);
|
|
}
|
|
|
|
static NTSTATUS wait_single(void *obj, ULONGLONG timeout)
|
|
{
|
|
LARGE_INTEGER integer;
|
|
|
|
integer.QuadPart = timeout;
|
|
return KeWaitForSingleObject(obj, Executive, KernelMode, FALSE, &integer);
|
|
}
|
|
|
|
static NTSTATUS wait_multiple(ULONG count, void *objs[], WAIT_TYPE wait_type, ULONGLONG timeout)
|
|
{
|
|
LARGE_INTEGER integer;
|
|
|
|
integer.QuadPart = timeout;
|
|
return KeWaitForMultipleObjects(count, objs, wait_type, Executive, KernelMode, FALSE, &integer, NULL);
|
|
}
|
|
|
|
static NTSTATUS wait_single_handle(HANDLE handle, ULONGLONG timeout)
|
|
{
|
|
LARGE_INTEGER integer;
|
|
|
|
integer.QuadPart = timeout;
|
|
return ZwWaitForSingleObject(handle, FALSE, &integer);
|
|
}
|
|
|
|
static void test_current_thread(BOOL is_system)
|
|
{
|
|
PROCESS_BASIC_INFORMATION info;
|
|
DISPATCHER_HEADER *header;
|
|
HANDLE process_handle, id;
|
|
PEPROCESS current;
|
|
PETHREAD thread;
|
|
NTSTATUS ret;
|
|
|
|
current = IoGetCurrentProcess();
|
|
ok(current != NULL, "Expected current process to be non-NULL\n");
|
|
|
|
header = (DISPATCHER_HEADER*)current;
|
|
ok(header->Type == 3, "header->Type != 3, = %u\n", header->Type);
|
|
ret = wait_single(current, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
if (is_system)
|
|
ok(current == *pPsInitialSystemProcess, "current != PsInitialSystemProcess\n");
|
|
else
|
|
ok(current != *pPsInitialSystemProcess, "current == PsInitialSystemProcess\n");
|
|
|
|
ok(PsGetProcessId(current) == PsGetCurrentProcessId(), "process IDs don't match\n");
|
|
ok(PsGetThreadProcessId((PETHREAD)KeGetCurrentThread()) == PsGetCurrentProcessId(), "process IDs don't match\n");
|
|
|
|
thread = PsGetCurrentThread();
|
|
ret = wait_single( thread, 0 );
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ok(PsGetThreadId((PETHREAD)KeGetCurrentThread()) == PsGetCurrentThreadId(), "thread IDs don't match\n");
|
|
ok(PsIsSystemThread((PETHREAD)KeGetCurrentThread()) == is_system, "unexpected system thread\n");
|
|
if (!is_system)
|
|
{
|
|
ok(create_caller_thread == KeGetCurrentThread(), "thread is not create caller thread\n");
|
|
ok(create_irp_thread == (PETHREAD)KeGetCurrentThread(), "thread of create request is not current thread\n");
|
|
}
|
|
|
|
ret = ObOpenObjectByPointer(current, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_INFORMATION, NULL, KernelMode, &process_handle);
|
|
ok(!ret, "ObOpenObjectByPointer failed: %#x\n", ret);
|
|
|
|
ret = ZwQueryInformationProcess(process_handle, ProcessBasicInformation, &info, sizeof(info), NULL);
|
|
ok(!ret, "ZwQueryInformationProcess failed: %#x\n", ret);
|
|
|
|
id = PsGetProcessInheritedFromUniqueProcessId(current);
|
|
ok(id == (HANDLE)info.InheritedFromUniqueProcessId, "unexpected process id %p\n", id);
|
|
|
|
ret = ZwClose(process_handle);
|
|
ok(!ret, "ZwClose failed: %#x\n", ret);
|
|
}
|
|
|
|
static void test_critical_region(BOOL is_dispatcher)
|
|
{
|
|
BOOLEAN result;
|
|
|
|
KeEnterCriticalRegion();
|
|
KeEnterCriticalRegion();
|
|
|
|
result = KeAreApcsDisabled();
|
|
ok(result == TRUE, "KeAreApcsDisabled returned %x\n", result);
|
|
KeLeaveCriticalRegion();
|
|
|
|
result = KeAreApcsDisabled();
|
|
ok(result == TRUE, "KeAreApcsDisabled returned %x\n", result);
|
|
KeLeaveCriticalRegion();
|
|
|
|
result = KeAreApcsDisabled();
|
|
ok(result == is_dispatcher || broken(is_dispatcher && !result),
|
|
"KeAreApcsDisabled returned %x\n", result);
|
|
}
|
|
|
|
static void sleep_1ms(void)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
|
|
timeout.QuadPart = -1 * 10000;
|
|
KeDelayExecutionThread( KernelMode, FALSE, &timeout );
|
|
}
|
|
|
|
static void sleep(void)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
timeout.QuadPart = -20 * 10000;
|
|
KeDelayExecutionThread( KernelMode, FALSE, &timeout );
|
|
}
|
|
|
|
static HANDLE create_thread(PKSTART_ROUTINE proc, void *arg)
|
|
{
|
|
OBJECT_ATTRIBUTES attr = {0};
|
|
HANDLE thread;
|
|
NTSTATUS ret;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.Attributes = OBJ_KERNEL_HANDLE;
|
|
ret = PsCreateSystemThread(&thread, THREAD_ALL_ACCESS, &attr, NULL, NULL, proc, arg);
|
|
ok(!ret, "got %#x\n", ret);
|
|
|
|
return thread;
|
|
}
|
|
|
|
static void join_thread(HANDLE thread)
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
ret = ZwWaitForSingleObject(thread, FALSE, NULL);
|
|
ok(!ret, "got %#x\n", ret);
|
|
ret = ZwClose(thread);
|
|
ok(!ret, "got %#x\n", ret);
|
|
}
|
|
|
|
static void run_thread(PKSTART_ROUTINE proc, void *arg)
|
|
{
|
|
HANDLE thread = create_thread(proc, arg);
|
|
join_thread(thread);
|
|
}
|
|
|
|
static KMUTEX test_mutex;
|
|
|
|
static void WINAPI mutex_thread(void *arg)
|
|
{
|
|
NTSTATUS ret, expect = (NTSTATUS)(DWORD_PTR)arg;
|
|
|
|
ret = wait_single(&test_mutex, 0);
|
|
ok(ret == expect, "expected %#x, got %#x\n", expect, ret);
|
|
|
|
if (!ret) KeReleaseMutex(&test_mutex, FALSE);
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
static KEVENT remove_lock_ready;
|
|
|
|
static void WINAPI remove_lock_thread(void *arg)
|
|
{
|
|
IO_REMOVE_LOCK *lock = arg;
|
|
NTSTATUS ret;
|
|
|
|
ret = IoAcquireRemoveLockEx(lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
|
|
KeSetEvent(&remove_lock_ready, 0, FALSE);
|
|
|
|
IoReleaseRemoveLockAndWaitEx(lock, NULL, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
struct test_sync_dpc_context
|
|
{
|
|
BOOL called;
|
|
};
|
|
|
|
static void WINAPI test_sync_dpc(KDPC *dpc, void *context, void *system_argument1, void *system_argument2)
|
|
{
|
|
struct test_sync_dpc_context *c = context;
|
|
|
|
c->called = TRUE;
|
|
}
|
|
|
|
static void test_sync(void)
|
|
{
|
|
static const ULONG wine_tag = 0x454e4957; /* WINE */
|
|
struct test_sync_dpc_context dpc_context;
|
|
KEVENT manual_event, auto_event, *event;
|
|
KSEMAPHORE semaphore, semaphore2;
|
|
IO_REMOVE_LOCK remove_lock;
|
|
LARGE_INTEGER timeout;
|
|
OBJECT_ATTRIBUTES attr;
|
|
HANDLE handle, thread;
|
|
void *objs[2];
|
|
KTIMER timer;
|
|
NTSTATUS ret;
|
|
KDPC dpc;
|
|
int i;
|
|
|
|
KeInitializeEvent(&manual_event, NotificationEvent, FALSE);
|
|
|
|
ret = wait_single(&manual_event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = KeReadStateEvent(&manual_event);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
|
|
KeSetEvent(&manual_event, 0, FALSE);
|
|
|
|
ret = KeReadStateEvent(&manual_event);
|
|
ok(ret == 1, "got %d\n", ret);
|
|
|
|
ret = wait_single(&manual_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&manual_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
KeResetEvent(&manual_event);
|
|
|
|
ret = wait_single(&manual_event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeInitializeEvent(&auto_event, SynchronizationEvent, FALSE);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeInitializeEvent(&auto_event, SynchronizationEvent, TRUE);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
objs[0] = &manual_event;
|
|
objs[1] = &auto_event;
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(&manual_event, 0, FALSE);
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
KeResetEvent(&manual_event);
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 1, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(&manual_event, 0, FALSE);
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAll, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAll, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
KeResetEvent(&manual_event);
|
|
|
|
ret = wait_multiple(2, objs, WaitAll, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&auto_event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
objs[0] = &auto_event;
|
|
objs[1] = &manual_event;
|
|
KeSetEvent(&manual_event, 0, FALSE);
|
|
KeSetEvent(&auto_event, 0, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 1, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 1, "got %#x\n", ret);
|
|
|
|
InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
ret = ZwCreateEvent(&handle, SYNCHRONIZE, &attr, NotificationEvent, TRUE);
|
|
ok(!ret, "ZwCreateEvent failed: %#x\n", ret);
|
|
|
|
ret = ObReferenceObjectByHandle(handle, SYNCHRONIZE, *pExEventObjectType, KernelMode, (void **)&event, NULL);
|
|
ok(!ret, "ObReferenceObjectByHandle failed: %#x\n", ret);
|
|
|
|
|
|
ret = wait_single(event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
ret = KeReadStateEvent(event);
|
|
ok(ret == 1, "got %d\n", ret);
|
|
KeResetEvent(event);
|
|
ret = KeReadStateEvent(event);
|
|
ok(ret == 0, "got %d\n", ret);
|
|
ret = wait_single(event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
ret = wait_single_handle(handle, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(event, 0, FALSE);
|
|
ret = wait_single(event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
ret = wait_single_handle(handle, 0);
|
|
ok(!ret, "got %#x\n", ret);
|
|
|
|
ZwClose(handle);
|
|
ObDereferenceObject(event);
|
|
|
|
event = IoCreateSynchronizationEvent(NULL, &handle);
|
|
ok(event != NULL, "IoCreateSynchronizationEvent failed\n");
|
|
|
|
ret = wait_single(event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
KeResetEvent(event);
|
|
ret = wait_single(event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
ret = wait_single_handle(handle, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = ZwSetEvent(handle, NULL);
|
|
ok(!ret, "NtSetEvent returned %#x\n", ret);
|
|
ret = wait_single(event, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
ret = wait_single_handle(handle, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeSetEvent(event, 0, FALSE);
|
|
ret = wait_single_handle(handle, 0);
|
|
ok(!ret, "got %#x\n", ret);
|
|
ret = wait_single(event, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = ZwClose(handle);
|
|
ok(!ret, "ZwClose returned %#x\n", ret);
|
|
|
|
/* test semaphores */
|
|
KeInitializeSemaphore(&semaphore, 0, 5);
|
|
|
|
ret = wait_single(&semaphore, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %u\n", ret);
|
|
|
|
ret = KeReleaseSemaphore(&semaphore, 0, 1, FALSE);
|
|
ok(ret == 0, "got prev %d\n", ret);
|
|
|
|
ret = KeReleaseSemaphore(&semaphore, 0, 2, FALSE);
|
|
ok(ret == 1, "got prev %d\n", ret);
|
|
|
|
ret = KeReleaseSemaphore(&semaphore, 0, 1, FALSE);
|
|
ok(ret == 3, "got prev %d\n", ret);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
ret = wait_single(&semaphore, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
}
|
|
|
|
ret = wait_single(&semaphore, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeInitializeSemaphore(&semaphore2, 3, 5);
|
|
|
|
ret = KeReleaseSemaphore(&semaphore2, 0, 1, FALSE);
|
|
ok(ret == 3, "got prev %d\n", ret);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
ret = wait_single(&semaphore2, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
}
|
|
|
|
objs[0] = &semaphore;
|
|
objs[1] = &semaphore2;
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeReleaseSemaphore(&semaphore, 0, 1, FALSE);
|
|
KeReleaseSemaphore(&semaphore2, 0, 1, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == 1, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeReleaseSemaphore(&semaphore, 0, 1, FALSE);
|
|
KeReleaseSemaphore(&semaphore2, 0, 1, FALSE);
|
|
|
|
ret = wait_multiple(2, objs, WaitAll, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_multiple(2, objs, WaitAny, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
/* test mutexes */
|
|
KeInitializeMutex(&test_mutex, 0);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
ret = wait_single(&test_mutex, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
}
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
ret = KeReleaseMutex(&test_mutex, FALSE);
|
|
ok(ret == i - 9, "expected %d, got %d\n", i - 9, ret);
|
|
}
|
|
|
|
run_thread(mutex_thread, (void *)0);
|
|
|
|
ret = wait_single(&test_mutex, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
run_thread(mutex_thread, (void *)STATUS_TIMEOUT);
|
|
|
|
ret = KeReleaseMutex(&test_mutex, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
run_thread(mutex_thread, (void *)0);
|
|
|
|
/* test timers */
|
|
KeInitializeTimerEx(&timer, NotificationTimer);
|
|
|
|
timeout.QuadPart = -20 * 10000;
|
|
KeSetTimerEx(&timer, timeout, 0, NULL);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
KeCancelTimer(&timer);
|
|
KeInitializeTimerEx(&timer, SynchronizationTimer);
|
|
|
|
memset(&dpc_context, 0, sizeof(dpc_context));
|
|
KeInitializeDpc(&dpc, test_sync_dpc, &dpc_context);
|
|
|
|
KeSetTimerEx(&timer, timeout, 0, &dpc);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
ok(!dpc_context.called, "DPC was called unexpectedly.\n");
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
sleep_1ms();
|
|
ok(dpc_context.called, "DPC was not called.\n");
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeCancelTimer(&timer);
|
|
KeSetTimerEx(&timer, timeout, 20, NULL);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
/* aliasing makes it sometimes succeeds, try again in that case */
|
|
if (ret == 0) ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
KeCancelTimer(&timer);
|
|
|
|
/* Test cancelling timer. */
|
|
dpc_context.called = 0;
|
|
KeSetTimerEx(&timer, timeout, 0, &dpc);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
ok(!dpc_context.called, "DPC was called.\n");
|
|
|
|
KeCancelTimer(&timer);
|
|
dpc_context.called = 0;
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
ok(!dpc_context.called, "DPC was called.\n");
|
|
|
|
KeSetTimerEx(&timer, timeout, 20, &dpc);
|
|
KeSetTimerEx(&timer, timeout, 0, &dpc);
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
|
|
KeCancelTimer(&timer);
|
|
/* Test reinitializing timer. */
|
|
KeSetTimerEx(&timer, timeout, 0, &dpc);
|
|
KeInitializeTimerEx(&timer, SynchronizationTimer);
|
|
dpc_context.called = 0;
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
sleep_1ms();
|
|
todo_wine ok(dpc_context.called, "DPC was not called.\n");
|
|
|
|
ret = wait_single(&timer, 0);
|
|
ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
|
|
sleep_1ms();
|
|
todo_wine ok(dpc_context.called, "DPC was not called.\n");
|
|
|
|
dpc_context.called = 0;
|
|
KeSetTimerEx(&timer, timeout, 0, &dpc);
|
|
ret = wait_single(&timer, -40 * 10000);
|
|
ok(ret == 0, "got %#x\n", ret);
|
|
sleep_1ms();
|
|
ok(dpc_context.called, "DPC was not called.\n");
|
|
|
|
KeCancelTimer(&timer);
|
|
/* remove locks */
|
|
|
|
IoInitializeRemoveLockEx(&remove_lock, wine_tag, 0, 0, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
|
|
ret = IoAcquireRemoveLockEx(&remove_lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
|
|
|
|
IoReleaseRemoveLockEx(&remove_lock, NULL, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
|
|
ret = IoAcquireRemoveLockEx(&remove_lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
|
|
|
|
ret = IoAcquireRemoveLockEx(&remove_lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
|
|
|
|
KeInitializeEvent(&remove_lock_ready, SynchronizationEvent, FALSE);
|
|
thread = create_thread(remove_lock_thread, &remove_lock);
|
|
ret = wait_single(&remove_lock_ready, -1000 * 10000);
|
|
ok(!ret, "got %#x\n", ret);
|
|
ret = wait_single_handle(thread, -50 * 10000);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
ret = IoAcquireRemoveLockEx(&remove_lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_DELETE_PENDING, "got %#x\n", ret);
|
|
|
|
IoReleaseRemoveLockEx(&remove_lock, NULL, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ret = wait_single_handle(thread, 0);
|
|
ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
|
|
|
IoReleaseRemoveLockEx(&remove_lock, NULL, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ret = wait_single_handle(thread, -10000 * 10000);
|
|
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
|
|
|
|
ret = IoAcquireRemoveLockEx(&remove_lock, NULL, "", 1, sizeof(IO_REMOVE_LOCK_COMMON_BLOCK));
|
|
ok(ret == STATUS_DELETE_PENDING, "got %#x\n", ret);
|
|
}
|
|
|
|
static void test_call_driver(DEVICE_OBJECT *device)
|
|
{
|
|
IO_STACK_LOCATION *irpsp;
|
|
IO_STATUS_BLOCK iosb;
|
|
IRP *irp = NULL;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
iosb.Status = 0xdeadbeef;
|
|
iosb.Information = 0xdeadbeef;
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
ok(irp->UserIosb == &iosb, "unexpected UserIosb\n");
|
|
ok(!irp->Cancel, "Cancel = %x\n", irp->Cancel);
|
|
ok(!irp->CancelRoutine, "CancelRoutine = %x\n", irp->CancelRoutine);
|
|
ok(!irp->UserEvent, "UserEvent = %p\n", irp->UserEvent);
|
|
ok(irp->CurrentLocation == 2, "CurrentLocation = %u\n", irp->CurrentLocation);
|
|
ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(),
|
|
"IRP thread is not the current thread\n");
|
|
ok(!irp->IoStatus.Status, "got status %#x\n", irp->IoStatus.Status);
|
|
ok(!irp->IoStatus.Information, "got information %#x\n", irp->IoStatus.Information);
|
|
ok(iosb.Status == 0xdeadbeef, "got status %#x\n", iosb.Status);
|
|
ok(iosb.Information == 0xdeadbeef, "got information %#x\n", iosb.Information);
|
|
|
|
irpsp = IoGetNextIrpStackLocation(irp);
|
|
ok(irpsp->MajorFunction == IRP_MJ_FLUSH_BUFFERS, "MajorFunction = %u\n", irpsp->MajorFunction);
|
|
ok(!irpsp->DeviceObject, "DeviceObject = %u\n", irpsp->DeviceObject);
|
|
ok(!irpsp->FileObject, "FileObject = %u\n", irpsp->FileObject);
|
|
ok(!irpsp->CompletionRoutine, "CompletionRoutine = %p\n", irpsp->CompletionRoutine);
|
|
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
ok(!irp->IoStatus.Status, "got status %#x\n", irp->IoStatus.Status);
|
|
ok(!irp->IoStatus.Information, "got information %#x\n", irp->IoStatus.Information);
|
|
ok(iosb.Status == 0xdeadbeef, "got status %#x\n", iosb.Status);
|
|
ok(iosb.Information == 0xdeadbeef, "got information %#x\n", iosb.Information);
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
irp->IoStatus.Information = 123;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ok(iosb.Status == STATUS_SUCCESS, "got status %#x\n", iosb.Status);
|
|
ok(iosb.Information == 123, "got information %#x\n", iosb.Information);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &event, &iosb);
|
|
ok(irp->UserIosb == &iosb, "unexpected UserIosb\n");
|
|
ok(!irp->Cancel, "Cancel = %x\n", irp->Cancel);
|
|
ok(!irp->CancelRoutine, "CancelRoutine = %x\n", irp->CancelRoutine);
|
|
ok(irp->UserEvent == &event, "UserEvent = %p\n", irp->UserEvent);
|
|
ok(irp->CurrentLocation == 2, "CurrentLocation = %u\n", irp->CurrentLocation);
|
|
ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(),
|
|
"IRP thread is not the current thread\n");
|
|
|
|
irpsp = IoGetNextIrpStackLocation(irp);
|
|
ok(irpsp->MajorFunction == IRP_MJ_FLUSH_BUFFERS, "MajorFunction = %u\n", irpsp->MajorFunction);
|
|
ok(!irpsp->DeviceObject, "DeviceObject = %u\n", irpsp->DeviceObject);
|
|
ok(!irpsp->FileObject, "FileObject = %u\n", irpsp->FileObject);
|
|
ok(!irpsp->CompletionRoutine, "CompletionRoutine = %p\n", irpsp->CompletionRoutine);
|
|
|
|
status = wait_single(&event, 0);
|
|
ok(status == STATUS_TIMEOUT, "got %#x\n", status);
|
|
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
|
|
status = wait_single(&event, 0);
|
|
ok(status == STATUS_TIMEOUT, "got %#x\n", status);
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
status = wait_single(&event, 0);
|
|
ok(status == STATUS_SUCCESS, "got %#x\n", status);
|
|
}
|
|
|
|
static int cancel_cnt;
|
|
|
|
static void WINAPI cancel_irp(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(!irp->CancelRoutine, "CancelRoutine = %p\n", irp->CancelRoutine);
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irp->IoStatus.Information = 0;
|
|
cancel_cnt++;
|
|
}
|
|
|
|
static void WINAPI cancel_ioctl_irp(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irp->IoStatus.Information = 0;
|
|
cancel_cnt++;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
static NTSTATUS WINAPI cancel_test_completion(DEVICE_OBJECT *device, IRP *irp, void *context)
|
|
{
|
|
ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt);
|
|
*(BOOL*)context = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static void test_cancel_irp(DEVICE_OBJECT *device)
|
|
{
|
|
IO_STACK_LOCATION *irpsp;
|
|
IO_STATUS_BLOCK iosb;
|
|
IRP *irp = NULL;
|
|
BOOL completion_called;
|
|
BOOLEAN r;
|
|
NTSTATUS status;
|
|
|
|
/* cancel IRP with no cancel routine */
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
|
|
r = IoCancelIrp(irp);
|
|
ok(!r, "IoCancelIrp returned %x\n", r);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
|
|
r = IoCancelIrp(irp);
|
|
ok(!r, "IoCancelIrp returned %x\n", r);
|
|
IoFreeIrp(irp);
|
|
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
|
|
/* cancel IRP with cancel routine */
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
|
|
ok(irp->CurrentLocation == 1, "CurrentLocation = %u\n", irp->CurrentLocation);
|
|
irpsp = IoGetCurrentIrpStackLocation(irp);
|
|
ok(irpsp->DeviceObject == device, "DeviceObject = %u\n", irpsp->DeviceObject);
|
|
|
|
IoSetCancelRoutine(irp, cancel_irp);
|
|
cancel_cnt = 0;
|
|
r = IoCancelIrp(irp);
|
|
ok(r == TRUE, "IoCancelIrp returned %x\n", r);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt);
|
|
|
|
cancel_cnt = 0;
|
|
r = IoCancelIrp(irp);
|
|
ok(!r, "IoCancelIrp returned %x\n", r);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(!cancel_cnt, "cancel_cnt = %d\n", cancel_cnt);
|
|
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
/* cancel IRP with cancel and completion routines with no SL_INVOKE_ON_ERROR */
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, FALSE, TRUE);
|
|
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
|
|
IoSetCancelRoutine(irp, cancel_irp);
|
|
cancel_cnt = 0;
|
|
r = IoCancelIrp(irp);
|
|
ok(r == TRUE, "IoCancelIrp returned %x\n", r);
|
|
ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
|
|
completion_called = FALSE;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ok(completion_called, "completion not called\n");
|
|
|
|
/* cancel IRP with cancel and completion routines with no SL_INVOKE_ON_CANCEL flag */
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, TRUE, FALSE);
|
|
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
|
|
IoSetCancelRoutine(irp, cancel_irp);
|
|
cancel_cnt = 0;
|
|
r = IoCancelIrp(irp);
|
|
ok(r == TRUE, "IoCancelIrp returned %x\n", r);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt);
|
|
|
|
completion_called = FALSE;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ok(completion_called, "completion not called\n");
|
|
|
|
/* cancel IRP with cancel and completion routines, but no SL_INVOKE_ON_ERROR nor SL_INVOKE_ON_CANCEL flag */
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, device, NULL, 0, NULL, &iosb);
|
|
IoSetCompletionRoutine(irp, cancel_test_completion, &completion_called, TRUE, FALSE, FALSE);
|
|
|
|
status = IoCallDriver(device, irp);
|
|
ok(status == STATUS_PENDING, "IoCallDriver returned %#x\n", status);
|
|
|
|
IoSetCancelRoutine(irp, cancel_irp);
|
|
cancel_cnt = 0;
|
|
r = IoCancelIrp(irp);
|
|
ok(r == TRUE, "IoCancelIrp returned %x\n", r);
|
|
ok(irp->Cancel == TRUE, "Cancel = %x\n", irp->Cancel);
|
|
ok(cancel_cnt == 1, "cancel_cnt = %d\n", cancel_cnt);
|
|
|
|
completion_called = FALSE;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
ok(!completion_called, "completion not called\n");
|
|
}
|
|
|
|
static int callout_cnt;
|
|
|
|
static void WINAPI callout(void *parameter)
|
|
{
|
|
ok(parameter == (void*)0xdeadbeef, "parameter = %p\n", parameter);
|
|
callout_cnt++;
|
|
}
|
|
|
|
static void test_stack_callout(void)
|
|
{
|
|
NTSTATUS (WINAPI *pKeExpandKernelStackAndCallout)(PEXPAND_STACK_CALLOUT,void*,SIZE_T);
|
|
NTSTATUS (WINAPI *pKeExpandKernelStackAndCalloutEx)(PEXPAND_STACK_CALLOUT,void*,SIZE_T,BOOLEAN,void*);
|
|
NTSTATUS ret;
|
|
|
|
pKeExpandKernelStackAndCallout = get_proc_address("KeExpandKernelStackAndCallout");
|
|
if (pKeExpandKernelStackAndCallout)
|
|
{
|
|
callout_cnt = 0;
|
|
ret = pKeExpandKernelStackAndCallout(callout, (void*)0xdeadbeef, 4096);
|
|
ok(ret == STATUS_SUCCESS, "KeExpandKernelStackAndCallout failed: %#x\n", ret);
|
|
ok(callout_cnt == 1, "callout_cnt = %u\n", callout_cnt);
|
|
}
|
|
else win_skip("KeExpandKernelStackAndCallout is not available\n");
|
|
|
|
pKeExpandKernelStackAndCalloutEx = get_proc_address("KeExpandKernelStackAndCalloutEx");
|
|
if (pKeExpandKernelStackAndCalloutEx)
|
|
{
|
|
callout_cnt = 0;
|
|
ret = pKeExpandKernelStackAndCalloutEx(callout, (void*)0xdeadbeef, 4096, FALSE, NULL);
|
|
ok(ret == STATUS_SUCCESS, "KeExpandKernelStackAndCalloutEx failed: %#x\n", ret);
|
|
ok(callout_cnt == 1, "callout_cnt = %u\n", callout_cnt);
|
|
}
|
|
else win_skip("KeExpandKernelStackAndCalloutEx is not available\n");
|
|
}
|
|
|
|
static void test_lookaside_list(void)
|
|
{
|
|
NPAGED_LOOKASIDE_LIST list;
|
|
PAGED_LOOKASIDE_LIST paged_list;
|
|
ULONG tag = 0x454e4957; /* WINE */
|
|
|
|
ExInitializeNPagedLookasideList(&list, NULL, NULL, POOL_NX_ALLOCATION, LOOKASIDE_MINIMUM_BLOCK_SIZE, tag, 0);
|
|
ok(list.L.Depth == 4, "Expected 4 got %u\n", list.L.Depth);
|
|
ok(list.L.MaximumDepth == 256, "Expected 256 got %u\n", list.L.MaximumDepth);
|
|
ok(list.L.TotalAllocates == 0, "Expected 0 got %u\n", list.L.TotalAllocates);
|
|
ok(list.L.AllocateMisses == 0, "Expected 0 got %u\n", list.L.AllocateMisses);
|
|
ok(list.L.TotalFrees == 0, "Expected 0 got %u\n", list.L.TotalFrees);
|
|
ok(list.L.FreeMisses == 0, "Expected 0 got %u\n", list.L.FreeMisses);
|
|
ok(list.L.Type == (NonPagedPool|POOL_NX_ALLOCATION),
|
|
"Expected NonPagedPool|POOL_NX_ALLOCATION got %u\n", list.L.Type);
|
|
ok(list.L.Tag == tag, "Expected %x got %x\n", tag, list.L.Tag);
|
|
ok(list.L.Size == LOOKASIDE_MINIMUM_BLOCK_SIZE,
|
|
"Expected %u got %u\n", LOOKASIDE_MINIMUM_BLOCK_SIZE, list.L.Size);
|
|
ok(list.L.LastTotalAllocates == 0,"Expected 0 got %u\n", list.L.LastTotalAllocates);
|
|
ok(list.L.LastAllocateMisses == 0,"Expected 0 got %u\n", list.L.LastAllocateMisses);
|
|
ExDeleteNPagedLookasideList(&list);
|
|
|
|
list.L.Depth = 0;
|
|
ExInitializeNPagedLookasideList(&list, NULL, NULL, 0, LOOKASIDE_MINIMUM_BLOCK_SIZE, tag, 20);
|
|
ok(list.L.Depth == 4, "Expected 4 got %u\n", list.L.Depth);
|
|
ok(list.L.MaximumDepth == 256, "Expected 256 got %u\n", list.L.MaximumDepth);
|
|
ok(list.L.Type == NonPagedPool, "Expected NonPagedPool got %u\n", list.L.Type);
|
|
ExDeleteNPagedLookasideList(&list);
|
|
|
|
ExInitializePagedLookasideList(&paged_list, NULL, NULL, POOL_NX_ALLOCATION, LOOKASIDE_MINIMUM_BLOCK_SIZE, tag, 0);
|
|
ok(paged_list.L.Depth == 4, "Expected 4 got %u\n", paged_list.L.Depth);
|
|
ok(paged_list.L.MaximumDepth == 256, "Expected 256 got %u\n", paged_list.L.MaximumDepth);
|
|
ok(paged_list.L.TotalAllocates == 0, "Expected 0 got %u\n", paged_list.L.TotalAllocates);
|
|
ok(paged_list.L.AllocateMisses == 0, "Expected 0 got %u\n", paged_list.L.AllocateMisses);
|
|
ok(paged_list.L.TotalFrees == 0, "Expected 0 got %u\n", paged_list.L.TotalFrees);
|
|
ok(paged_list.L.FreeMisses == 0, "Expected 0 got %u\n", paged_list.L.FreeMisses);
|
|
ok(paged_list.L.Type == (PagedPool|POOL_NX_ALLOCATION),
|
|
"Expected PagedPool|POOL_NX_ALLOCATION got %u\n", paged_list.L.Type);
|
|
ok(paged_list.L.Tag == tag, "Expected %x got %x\n", tag, paged_list.L.Tag);
|
|
ok(paged_list.L.Size == LOOKASIDE_MINIMUM_BLOCK_SIZE,
|
|
"Expected %u got %u\n", LOOKASIDE_MINIMUM_BLOCK_SIZE, paged_list.L.Size);
|
|
ok(paged_list.L.LastTotalAllocates == 0,"Expected 0 got %u\n", paged_list.L.LastTotalAllocates);
|
|
ok(paged_list.L.LastAllocateMisses == 0,"Expected 0 got %u\n", paged_list.L.LastAllocateMisses);
|
|
ExDeletePagedLookasideList(&paged_list);
|
|
|
|
paged_list.L.Depth = 0;
|
|
ExInitializePagedLookasideList(&paged_list, NULL, NULL, 0, LOOKASIDE_MINIMUM_BLOCK_SIZE, tag, 20);
|
|
ok(paged_list.L.Depth == 4, "Expected 4 got %u\n", paged_list.L.Depth);
|
|
ok(paged_list.L.MaximumDepth == 256, "Expected 256 got %u\n", paged_list.L.MaximumDepth);
|
|
ok(paged_list.L.Type == PagedPool, "Expected PagedPool got %u\n", paged_list.L.Type);
|
|
ExDeletePagedLookasideList(&paged_list);
|
|
}
|
|
|
|
static void test_version(void)
|
|
{
|
|
USHORT *pNtBuildNumber;
|
|
ULONG build;
|
|
|
|
pNtBuildNumber = get_proc_address("NtBuildNumber");
|
|
ok(!!pNtBuildNumber, "Could not get pointer to NtBuildNumber\n");
|
|
|
|
PsGetVersion(NULL, NULL, &build, NULL);
|
|
ok(*pNtBuildNumber == build, "Expected build number %u, got %u\n", build, *pNtBuildNumber);
|
|
}
|
|
|
|
static void WINAPI thread_proc(void *arg)
|
|
{
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
static void test_ob_reference(void)
|
|
{
|
|
POBJECT_TYPE (WINAPI *pObGetObjectType)(void*);
|
|
OBJECT_ATTRIBUTES attr = { sizeof(attr) };
|
|
HANDLE event_handle, file_handle, file_handle2, thread_handle, handle;
|
|
DISPATCHER_HEADER *header;
|
|
FILE_OBJECT *file;
|
|
void *obj1, *obj2;
|
|
POBJECT_TYPE obj1_type;
|
|
UNICODE_STRING pathU;
|
|
IO_STATUS_BLOCK io;
|
|
NTSTATUS status;
|
|
|
|
pObGetObjectType = get_proc_address("ObGetObjectType");
|
|
if (!pObGetObjectType)
|
|
win_skip("ObGetObjectType not found\n");
|
|
|
|
InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
status = ZwCreateEvent(&event_handle, SYNCHRONIZE, &attr, NotificationEvent, TRUE);
|
|
ok(!status, "ZwCreateEvent failed: %#x\n", status);
|
|
|
|
RtlInitUnicodeString(&pathU, L"\\??\\C:\\windows\\winetest_ntoskrnl_file.tmp");
|
|
attr.ObjectName = &pathU;
|
|
attr.Attributes = OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE;
|
|
status = ZwCreateFile(&file_handle, DELETE | FILE_WRITE_DATA | SYNCHRONIZE, &attr, &io, NULL, 0, 0, FILE_CREATE,
|
|
FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
|
ok(!status, "ZwCreateFile failed: %#x\n", status);
|
|
|
|
status = ZwDuplicateObject(NtCurrentProcess(), file_handle, NtCurrentProcess(), &file_handle2,
|
|
0, OBJ_KERNEL_HANDLE, DUPLICATE_SAME_ACCESS);
|
|
ok(!status, "ZwDuplicateObject failed: %#x\n", status);
|
|
|
|
InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
status = PsCreateSystemThread(&thread_handle, SYNCHRONIZE, &attr, NULL, NULL, thread_proc, NULL);
|
|
ok(!status, "PsCreateSystemThread returned: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(NULL, SYNCHRONIZE, *pExEventObjectType, KernelMode, &obj1, NULL);
|
|
ok(status == STATUS_INVALID_HANDLE, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(event_handle, SYNCHRONIZE, *pIoFileObjectType, KernelMode, &obj1, NULL);
|
|
ok(status == STATUS_OBJECT_TYPE_MISMATCH, "ObReferenceObjectByHandle returned: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(event_handle, SYNCHRONIZE, NULL, KernelMode, &obj1, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
|
|
if (pObGetObjectType)
|
|
{
|
|
obj1_type = pObGetObjectType(obj1);
|
|
ok(obj1_type == *pExEventObjectType, "ObGetObjectType returned %p\n", obj1_type);
|
|
}
|
|
|
|
if (sizeof(void *) != 4) /* avoid dealing with fastcall */
|
|
{
|
|
ObfReferenceObject(obj1);
|
|
ObDereferenceObject(obj1);
|
|
}
|
|
|
|
status = ObReferenceObjectByHandle(event_handle, SYNCHRONIZE, *pIoFileObjectType, KernelMode, &obj2, NULL);
|
|
ok(status == STATUS_OBJECT_TYPE_MISMATCH, "ObReferenceObjectByHandle returned: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(event_handle, SYNCHRONIZE, *pExEventObjectType, KernelMode, &obj2, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
ok(obj1 == obj2, "obj1 != obj2\n");
|
|
|
|
ObDereferenceObject(obj2);
|
|
|
|
status = ObReferenceObjectByHandle(event_handle, SYNCHRONIZE, NULL, KernelMode, &obj2, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
ok(obj1 == obj2, "obj1 != obj2\n");
|
|
|
|
ObDereferenceObject(obj2);
|
|
ObDereferenceObject(obj1);
|
|
|
|
status = ObReferenceObjectByHandle(file_handle, SYNCHRONIZE, *pIoFileObjectType, KernelMode, &obj1, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(file_handle2, SYNCHRONIZE, *pIoFileObjectType, KernelMode, &obj2, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
ok(obj1 == obj2, "obj1 != obj2\n");
|
|
|
|
file = obj1;
|
|
ok(file->Type == 5, "Type = %u\n", file->Type);
|
|
|
|
ObDereferenceObject(obj1);
|
|
ObDereferenceObject(obj2);
|
|
|
|
status = ObReferenceObjectByHandle(thread_handle, SYNCHRONIZE, *pPsThreadType, KernelMode, &obj1, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(thread_handle, SYNCHRONIZE, *pPsThreadType, KernelMode, &obj2, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
ok(obj1 == obj2, "obj1 != obj2\n");
|
|
|
|
header = obj1;
|
|
ok(header->Type == 6, "Type = %u\n", header->Type);
|
|
|
|
status = wait_single(header, 0);
|
|
ok(status == 0 || status == STATUS_TIMEOUT, "got %#x\n", status);
|
|
|
|
ObDereferenceObject(obj2);
|
|
|
|
status = ObOpenObjectByPointer(obj1, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &handle);
|
|
ok(status == STATUS_SUCCESS, "ObOpenObjectByPointer failed: %#x\n", status);
|
|
|
|
status = ZwClose(handle);
|
|
ok(!status, "ZwClose failed: %#x\n", status);
|
|
|
|
status = ObReferenceObjectByHandle(thread_handle, SYNCHRONIZE, *pPsThreadType, KernelMode, &obj2, NULL);
|
|
ok(!status, "ObReferenceObjectByHandle failed: %#x\n", status);
|
|
ok(obj1 == obj2, "obj1 != obj2\n");
|
|
ObDereferenceObject(obj2);
|
|
|
|
status = ObOpenObjectByPointer(obj1, OBJ_KERNEL_HANDLE, NULL, 0, *pIoFileObjectType, KernelMode, &handle);
|
|
ok(status == STATUS_OBJECT_TYPE_MISMATCH, "ObOpenObjectByPointer returned: %#x\n", status);
|
|
|
|
ObDereferenceObject(obj1);
|
|
|
|
status = ZwClose(thread_handle);
|
|
ok(!status, "ZwClose failed: %#x\n", status);
|
|
|
|
status = ZwClose(event_handle);
|
|
ok(!status, "ZwClose failed: %#x\n", status);
|
|
|
|
status = ZwClose(file_handle);
|
|
ok(!status, "ZwClose failed: %#x\n", status);
|
|
|
|
status = ZwClose(file_handle2);
|
|
ok(!status, "ZwClose failed: %#x\n", status);
|
|
}
|
|
|
|
static void check_resource_(int line, ERESOURCE *resource, ULONG exclusive_waiters,
|
|
ULONG shared_waiters, BOOLEAN exclusive, ULONG shared_count)
|
|
{
|
|
BOOLEAN ret;
|
|
ULONG count;
|
|
|
|
count = ExGetExclusiveWaiterCount(resource);
|
|
ok_(__FILE__, line, count == exclusive_waiters,
|
|
"expected %u exclusive waiters, got %u\n", exclusive_waiters, count);
|
|
count = ExGetSharedWaiterCount(resource);
|
|
ok_(__FILE__, line, count == shared_waiters,
|
|
"expected %u shared waiters, got %u\n", shared_waiters, count);
|
|
ret = ExIsResourceAcquiredExclusiveLite(resource);
|
|
ok_(__FILE__, line, ret == exclusive,
|
|
"expected exclusive %u, got %u\n", exclusive, ret);
|
|
count = ExIsResourceAcquiredSharedLite(resource);
|
|
ok_(__FILE__, line, count == shared_count,
|
|
"expected shared %u, got %u\n", shared_count, count);
|
|
}
|
|
#define check_resource(a,b,c,d,e) check_resource_(__LINE__,a,b,c,d,e)
|
|
|
|
static KEVENT resource_shared_ready, resource_shared_done, resource_exclusive_ready, resource_exclusive_done;
|
|
|
|
static void WINAPI resource_shared_thread(void *arg)
|
|
{
|
|
ERESOURCE *resource = arg;
|
|
BOOLEAN ret;
|
|
|
|
check_resource(resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceSharedLite(resource, TRUE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
|
|
check_resource(resource, 0, 0, FALSE, 1);
|
|
|
|
KeSetEvent(&resource_shared_ready, IO_NO_INCREMENT, FALSE);
|
|
KeWaitForSingleObject(&resource_shared_done, Executive, KernelMode, FALSE, NULL);
|
|
|
|
ExReleaseResourceForThreadLite(resource, (ULONG_PTR)PsGetCurrentThread());
|
|
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
static void WINAPI resource_exclusive_thread(void *arg)
|
|
{
|
|
ERESOURCE *resource = arg;
|
|
BOOLEAN ret;
|
|
|
|
check_resource(resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(resource, TRUE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
|
|
check_resource(resource, 0, 0, TRUE, 1);
|
|
|
|
KeSetEvent(&resource_exclusive_ready, IO_NO_INCREMENT, FALSE);
|
|
KeWaitForSingleObject(&resource_exclusive_done, Executive, KernelMode, FALSE, NULL);
|
|
|
|
ExReleaseResourceForThreadLite(resource, (ULONG_PTR)PsGetCurrentThread());
|
|
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
static void test_resource(void)
|
|
{
|
|
ERESOURCE resource;
|
|
NTSTATUS status;
|
|
BOOLEAN ret;
|
|
HANDLE thread, thread2;
|
|
|
|
memset(&resource, 0xcc, sizeof(resource));
|
|
|
|
status = ExInitializeResourceLite(&resource);
|
|
ok(status == STATUS_SUCCESS, "got status %#x\n", status);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
KeEnterCriticalRegion();
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, TRUE, 1);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, TRUE, 2);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, TRUE, 3);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, TRUE, 2);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, TRUE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 2);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 2);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedStarveExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedWaitForExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
/* Do not acquire the resource ourselves, but spawn a shared thread holding it. */
|
|
|
|
KeInitializeEvent(&resource_shared_ready, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&resource_shared_done, SynchronizationEvent, FALSE);
|
|
thread = create_thread(resource_shared_thread, &resource);
|
|
KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedStarveExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedWaitForExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
/* Acquire the resource as exclusive, and then spawn a shared thread. */
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, TRUE, 1);
|
|
|
|
thread = create_thread(resource_shared_thread, &resource);
|
|
sleep();
|
|
check_resource(&resource, 0, 1, TRUE, 1);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 1, TRUE, 2);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL);
|
|
KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
/* Do not acquire the resource ourselves, but spawn an exclusive thread holding it. */
|
|
|
|
KeInitializeEvent(&resource_exclusive_ready, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&resource_exclusive_done, SynchronizationEvent, FALSE);
|
|
thread = create_thread(resource_exclusive_thread, &resource);
|
|
KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedStarveExclusive(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedWaitForExclusive(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
/* Acquire the resource as shared, and then spawn an exclusive waiter. */
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 0, 0, FALSE, 1);
|
|
|
|
thread = create_thread(resource_exclusive_thread, &resource);
|
|
sleep();
|
|
check_resource(&resource, 1, 0, FALSE, 1);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 2);
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
|
|
ret = ExAcquireSharedStarveExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 2);
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
|
|
ret = ExAcquireSharedWaitForExclusive(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 1);
|
|
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL);
|
|
KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
/* Spawn a shared and then exclusive waiter. */
|
|
|
|
KeInitializeEvent(&resource_shared_ready, SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&resource_shared_done, SynchronizationEvent, FALSE);
|
|
thread = create_thread(resource_shared_thread, &resource);
|
|
KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
thread2 = create_thread(resource_exclusive_thread, &resource);
|
|
sleep();
|
|
check_resource(&resource, 1, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceExclusiveLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireResourceSharedLite(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 0);
|
|
|
|
ret = ExAcquireSharedStarveExclusive(&resource, FALSE);
|
|
ok(ret == TRUE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 1);
|
|
ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread());
|
|
|
|
ret = ExAcquireSharedWaitForExclusive(&resource, FALSE);
|
|
ok(ret == FALSE, "got ret %u\n", ret);
|
|
check_resource(&resource, 1, 0, FALSE, 0);
|
|
|
|
KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread);
|
|
KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL);
|
|
KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE);
|
|
join_thread(thread2);
|
|
check_resource(&resource, 0, 0, FALSE, 0);
|
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
status = ExDeleteResourceLite(&resource);
|
|
ok(status == STATUS_SUCCESS, "got status %#x\n", status);
|
|
}
|
|
|
|
static void test_lookup_thread(void)
|
|
{
|
|
NTSTATUS status;
|
|
PETHREAD thread = NULL;
|
|
|
|
status = PsLookupThreadByThreadId(PsGetCurrentThreadId(), &thread);
|
|
ok(!status, "PsLookupThreadByThreadId failed: %#x\n", status);
|
|
ok((PKTHREAD)thread == KeGetCurrentThread(), "thread != KeGetCurrentThread\n");
|
|
if (thread) ObDereferenceObject(thread);
|
|
|
|
status = PsLookupThreadByThreadId(NULL, &thread);
|
|
ok(status == STATUS_INVALID_CID || broken(status == STATUS_INVALID_PARAMETER) /* winxp */,
|
|
"PsLookupThreadByThreadId returned %#x\n", status);
|
|
}
|
|
|
|
static void test_stack_limits(void)
|
|
{
|
|
ULONG_PTR low = 0, high = 0;
|
|
|
|
IoGetStackLimits(&low, &high);
|
|
ok(low, "low = 0\n");
|
|
ok(low < high, "low >= high\n");
|
|
ok(low < (ULONG_PTR)&low && (ULONG_PTR)&low < high, "stack variable is not in stack limits\n");
|
|
}
|
|
|
|
static unsigned int got_completion;
|
|
|
|
static NTSTATUS WINAPI completion_cb(DEVICE_OBJECT *device, IRP *irp, void *context)
|
|
{
|
|
ok(device == context, "Got device %p; expected %p.\n", device, context);
|
|
++got_completion;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static void test_completion(void)
|
|
{
|
|
IO_STATUS_BLOCK io;
|
|
NTSTATUS ret;
|
|
KEVENT event;
|
|
IRP *irp;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_WINETEST_COMPLETION, upper_device,
|
|
NULL, 0, NULL, 0, FALSE, &event, &io);
|
|
|
|
IoSetCompletionRoutine(irp, completion_cb, NULL, TRUE, TRUE, TRUE);
|
|
ret = IoCallDriver(upper_device, irp);
|
|
ok(ret == STATUS_SUCCESS, "IoCallDriver returned %#x\n", ret);
|
|
ok(got_completion == 2, "got %u calls to completion routine\n", got_completion);
|
|
}
|
|
|
|
static void test_IoAttachDeviceToDeviceStack(void)
|
|
{
|
|
DEVICE_OBJECT *dev1, *dev2, *dev3, *ret;
|
|
NTSTATUS status;
|
|
|
|
status = IoCreateDevice(driver_obj, 0, NULL, FILE_DEVICE_UNKNOWN,
|
|
FILE_DEVICE_SECURE_OPEN, FALSE, &dev1);
|
|
ok(status == STATUS_SUCCESS, "IoCreateDevice failed\n");
|
|
status = IoCreateDevice(driver_obj, 0, NULL, FILE_DEVICE_UNKNOWN,
|
|
FILE_DEVICE_SECURE_OPEN, FALSE, &dev2);
|
|
ok(status == STATUS_SUCCESS, "IoCreateDevice failed\n");
|
|
status = IoCreateDevice(driver_obj, 0, NULL, FILE_DEVICE_UNKNOWN,
|
|
FILE_DEVICE_SECURE_OPEN, FALSE, &dev3);
|
|
ok(status == STATUS_SUCCESS, "IoCreateDevice failed\n");
|
|
|
|
/* TODO: initialize devices properly */
|
|
dev1->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
dev2->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
ret = IoAttachDeviceToDeviceStack(dev2, dev1);
|
|
ok(ret == dev1, "IoAttachDeviceToDeviceStack returned %p, expected %p\n", ret, dev1);
|
|
ok(dev1->AttachedDevice == dev2, "dev1->AttachedDevice = %p, expected %p\n",
|
|
dev1->AttachedDevice, dev2);
|
|
ok(!dev2->AttachedDevice, "dev2->AttachedDevice = %p\n", dev2->AttachedDevice);
|
|
ok(dev1->StackSize == 1, "dev1->StackSize = %d\n", dev1->StackSize);
|
|
ok(dev2->StackSize == 2, "dev2->StackSize = %d\n", dev2->StackSize);
|
|
|
|
ret = IoAttachDeviceToDeviceStack(dev3, dev1);
|
|
ok(ret == dev2, "IoAttachDeviceToDeviceStack returned %p, expected %p\n", ret, dev2);
|
|
ok(dev1->AttachedDevice == dev2, "dev1->AttachedDevice = %p, expected %p\n",
|
|
dev1->AttachedDevice, dev2);
|
|
ok(dev2->AttachedDevice == dev3, "dev2->AttachedDevice = %p, expected %p\n",
|
|
dev2->AttachedDevice, dev3);
|
|
ok(!dev3->AttachedDevice, "dev3->AttachedDevice = %p\n", dev3->AttachedDevice);
|
|
ok(dev1->StackSize == 1, "dev1->StackSize = %d\n", dev1->StackSize);
|
|
ok(dev2->StackSize == 2, "dev2->StackSize = %d\n", dev2->StackSize);
|
|
ok(dev3->StackSize == 3, "dev3->StackSize = %d\n", dev3->StackSize);
|
|
|
|
IoDetachDevice(dev1);
|
|
ok(!dev1->AttachedDevice, "dev1->AttachedDevice = %p\n", dev1->AttachedDevice);
|
|
ok(dev2->AttachedDevice == dev3, "dev2->AttachedDevice = %p\n", dev2->AttachedDevice);
|
|
|
|
IoDetachDevice(dev2);
|
|
ok(!dev2->AttachedDevice, "dev2->AttachedDevice = %p\n", dev2->AttachedDevice);
|
|
ok(dev1->StackSize == 1, "dev1->StackSize = %d\n", dev1->StackSize);
|
|
ok(dev2->StackSize == 2, "dev2->StackSize = %d\n", dev2->StackSize);
|
|
ok(dev3->StackSize == 3, "dev3->StackSize = %d\n", dev3->StackSize);
|
|
|
|
IoDeleteDevice(dev1);
|
|
IoDeleteDevice(dev2);
|
|
IoDeleteDevice(dev3);
|
|
}
|
|
|
|
static void test_object_name(void)
|
|
{
|
|
static const WCHAR event_nameW[] = L"\\wine_test_event";
|
|
static const WCHAR device_nameW[] = L"\\Device\\WineTestDriver";
|
|
char buffer[1024];
|
|
OBJECT_NAME_INFORMATION *name = (OBJECT_NAME_INFORMATION *)buffer;
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING string;
|
|
ULONG ret_size;
|
|
HANDLE handle;
|
|
KEVENT *event;
|
|
NTSTATUS ret;
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(lower_device, name, 0, &ret_size);
|
|
ok(ret == STATUS_INFO_LENGTH_MISMATCH, "got status %#x\n", ret);
|
|
ok(ret_size == sizeof(*name) + sizeof(device_nameW), "got size %u\n", ret_size);
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(lower_device, name, sizeof(buffer), &ret_size);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ok(!wcscmp(name->Name.Buffer, device_nameW), "got name %ls\n", name->Name.Buffer);
|
|
ok(ret_size == sizeof(*name) + sizeof(device_nameW), "got size %u\n", ret_size);
|
|
ok(name->Name.Length == wcslen(device_nameW) * sizeof(WCHAR), "got length %u\n", name->Name.Length);
|
|
ok(name->Name.MaximumLength == sizeof(device_nameW), "got maximum length %u\n", name->Name.MaximumLength);
|
|
|
|
event = IoCreateSynchronizationEvent(NULL, &handle);
|
|
ok(!!event, "failed to create event\n");
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(event, name, sizeof(buffer), &ret_size);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ok(!name->Name.Buffer, "got name %ls\n", name->Name.Buffer);
|
|
ok(ret_size == sizeof(*name), "got size %u\n", ret_size);
|
|
ok(!name->Name.Length, "got length %u\n", name->Name.Length);
|
|
ok(!name->Name.MaximumLength, "got maximum length %u\n", name->Name.MaximumLength);
|
|
|
|
ret = ZwClose(handle);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
|
|
RtlInitUnicodeString(&string, event_nameW);
|
|
InitializeObjectAttributes(&attr, &string, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
ret = ZwCreateEvent(&handle, 0, &attr, NotificationEvent, TRUE);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ret = ObReferenceObjectByHandle(handle, 0, *pExEventObjectType, KernelMode, (void **)&event, NULL);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(event, name, sizeof(buffer), &ret_size);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ok(!wcscmp(name->Name.Buffer, event_nameW), "got name %ls\n", name->Name.Buffer);
|
|
ok(ret_size == sizeof(*name) + sizeof(event_nameW), "got size %u\n", ret_size);
|
|
ok(name->Name.Length == wcslen(event_nameW) * sizeof(WCHAR), "got length %u\n", name->Name.Length);
|
|
ok(name->Name.MaximumLength == sizeof(event_nameW), "got maximum length %u\n", name->Name.MaximumLength);
|
|
|
|
ObDereferenceObject(event);
|
|
ret = ZwClose(handle);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(KeGetCurrentThread(), name, sizeof(buffer), &ret_size);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ok(!name->Name.Buffer, "got name %ls\n", name->Name.Buffer);
|
|
ok(ret_size == sizeof(*name), "got size %u\n", ret_size);
|
|
ok(!name->Name.Length, "got length %u\n", name->Name.Length);
|
|
ok(!name->Name.MaximumLength, "got maximum length %u\n", name->Name.MaximumLength);
|
|
|
|
ret_size = 0;
|
|
ret = ObQueryNameString(IoGetCurrentProcess(), name, sizeof(buffer), &ret_size);
|
|
ok(!ret, "got status %#x\n", ret);
|
|
ok(!name->Name.Buffer, "got name %ls\n", name->Name.Buffer);
|
|
ok(ret_size == sizeof(*name), "got size %u\n", ret_size);
|
|
ok(!name->Name.Length, "got length %u\n", name->Name.Length);
|
|
ok(!name->Name.MaximumLength, "got maximum length %u\n", name->Name.MaximumLength);
|
|
}
|
|
|
|
static PIO_WORKITEM main_test_work_item;
|
|
|
|
static void WINAPI main_test_task(DEVICE_OBJECT *device, void *context)
|
|
{
|
|
IRP *irp = context;
|
|
|
|
IoFreeWorkItem(main_test_work_item);
|
|
main_test_work_item = NULL;
|
|
|
|
test_current_thread(TRUE);
|
|
test_critical_region(FALSE);
|
|
test_call_driver(device);
|
|
test_cancel_irp(device);
|
|
test_stack_limits();
|
|
test_completion();
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
static void test_executable_pool(void)
|
|
{
|
|
static const unsigned char bytes[] =
|
|
{ 0xb8, 0xef, 0xbe, 0xad, 0xde, 0xc3 }; /* mov $0xdeadbeef,%eax ; ret */
|
|
static const ULONG tag = 0x74736574; /* test */
|
|
int (*func)(void);
|
|
int ret;
|
|
|
|
func = ExAllocatePoolWithTag(NonPagedPool, sizeof(bytes), tag);
|
|
ok(!!func, "Got NULL memory.\n");
|
|
|
|
memcpy(func, bytes, sizeof(bytes));
|
|
ret = func();
|
|
ok(ret == 0xdeadbeef, "Got %#x.\n", ret);
|
|
|
|
ExFreePoolWithTag(func, tag);
|
|
}
|
|
#endif
|
|
|
|
static void test_affinity(void)
|
|
{
|
|
KAFFINITY (WINAPI *pKeSetSystemAffinityThreadEx)(KAFFINITY affinity);
|
|
void (WINAPI *pKeRevertToUserAffinityThreadEx)(KAFFINITY affinity);
|
|
ULONG (WINAPI *pKeQueryActiveProcessorCountEx)(USHORT);
|
|
KAFFINITY (WINAPI *pKeQueryActiveProcessors)(void);
|
|
KAFFINITY mask, mask_all_cpus;
|
|
ULONG cpu_count, count;
|
|
|
|
pKeQueryActiveProcessorCountEx = get_proc_address("KeQueryActiveProcessorCountEx");
|
|
if (!pKeQueryActiveProcessorCountEx)
|
|
{
|
|
win_skip("KeQueryActiveProcessorCountEx is not available.\n");
|
|
return;
|
|
}
|
|
|
|
pKeQueryActiveProcessors = get_proc_address("KeQueryActiveProcessors");
|
|
ok(!!pKeQueryActiveProcessors, "KeQueryActiveProcessors is not available.\n");
|
|
|
|
pKeSetSystemAffinityThreadEx = get_proc_address("KeSetSystemAffinityThreadEx");
|
|
ok(!!pKeSetSystemAffinityThreadEx, "KeSetSystemAffinityThreadEx is not available.\n");
|
|
|
|
pKeRevertToUserAffinityThreadEx = get_proc_address("KeRevertToUserAffinityThreadEx");
|
|
ok(!!pKeRevertToUserAffinityThreadEx, "KeRevertToUserAffinityThreadEx is not available.\n");
|
|
|
|
count = pKeQueryActiveProcessorCountEx(1);
|
|
ok(!count, "Got unexpected count %u.\n", count);
|
|
|
|
cpu_count = pKeQueryActiveProcessorCountEx(0);
|
|
ok(cpu_count, "Got unexpected cpu_count %u.\n", cpu_count);
|
|
|
|
count = pKeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
|
|
ok(count == cpu_count, "Got unexpected count %u.\n", count);
|
|
|
|
mask_all_cpus = ~((~0u) << cpu_count);
|
|
|
|
mask = pKeQueryActiveProcessors();
|
|
ok(mask == mask_all_cpus, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
pKeRevertToUserAffinityThreadEx(0x2);
|
|
|
|
mask = pKeSetSystemAffinityThreadEx(0);
|
|
ok(!mask, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
pKeRevertToUserAffinityThreadEx(0x2);
|
|
|
|
mask = pKeSetSystemAffinityThreadEx(0x1);
|
|
ok(mask == 0x2, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
mask = pKeSetSystemAffinityThreadEx(~(KAFFINITY)0);
|
|
ok(mask == 0x1, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
pKeRevertToUserAffinityThreadEx(~(KAFFINITY)0);
|
|
mask = pKeSetSystemAffinityThreadEx(0x1);
|
|
ok(mask == mask_all_cpus, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
pKeRevertToUserAffinityThreadEx(0);
|
|
|
|
mask = pKeSetSystemAffinityThreadEx(0x1);
|
|
ok(!mask, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
KeRevertToUserAffinityThread();
|
|
|
|
mask = pKeSetSystemAffinityThreadEx(0x1);
|
|
ok(!mask, "Got unexpected mask %#lx.\n", mask);
|
|
|
|
KeRevertToUserAffinityThread();
|
|
}
|
|
|
|
struct test_dpc_func_context
|
|
{
|
|
volatile LONG call_count;
|
|
volatile LONG selected_count;
|
|
volatile DEFERRED_REVERSE_BARRIER sync_barrier_start_value, sync_barrier_mid_value, sync_barrier_end_value;
|
|
volatile LONG done_barrier_start_value;
|
|
};
|
|
|
|
static BOOLEAN (WINAPI *pKeSignalCallDpcSynchronize)(void *barrier);
|
|
static void (WINAPI *pKeSignalCallDpcDone)(void *barrier);
|
|
|
|
static void WINAPI test_dpc_func(PKDPC Dpc, void *context, void *cpu_count,
|
|
void *reverse_barrier)
|
|
{
|
|
DEFERRED_REVERSE_BARRIER *barrier = reverse_barrier;
|
|
struct test_dpc_func_context *data = context;
|
|
|
|
InterlockedIncrement(&data->call_count);
|
|
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_start_value.Barrier,
|
|
*(volatile LONG *)&barrier->Barrier, 0);
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_start_value.TotalProcessors,
|
|
*(volatile LONG *)&barrier->TotalProcessors, 0);
|
|
|
|
if (pKeSignalCallDpcSynchronize(reverse_barrier))
|
|
InterlockedIncrement(&data->selected_count);
|
|
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_mid_value.Barrier,
|
|
*(volatile LONG *)&barrier->Barrier, 0);
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_mid_value.TotalProcessors,
|
|
*(volatile LONG *)&barrier->TotalProcessors, 0);
|
|
|
|
data->done_barrier_start_value = *(volatile LONG *)cpu_count;
|
|
|
|
if (pKeSignalCallDpcSynchronize(reverse_barrier))
|
|
InterlockedIncrement(&data->selected_count);
|
|
|
|
pKeSignalCallDpcSynchronize(reverse_barrier);
|
|
pKeSignalCallDpcSynchronize(reverse_barrier);
|
|
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_end_value.Barrier,
|
|
*(volatile LONG *)&barrier->Barrier, 0);
|
|
InterlockedCompareExchange((volatile LONG*)&data->sync_barrier_end_value.TotalProcessors,
|
|
*(volatile LONG *)&barrier->TotalProcessors, 0);
|
|
|
|
pKeSignalCallDpcDone(cpu_count);
|
|
}
|
|
|
|
static void test_dpc(void)
|
|
{
|
|
void (WINAPI *pKeGenericCallDpc)(PKDEFERRED_ROUTINE routine, void *context);
|
|
struct test_dpc_func_context data;
|
|
KAFFINITY cpu_mask;
|
|
ULONG cpu_count;
|
|
|
|
pKeGenericCallDpc = get_proc_address("KeGenericCallDpc");
|
|
if (!pKeGenericCallDpc)
|
|
{
|
|
win_skip("KeGenericCallDpc is not available.\n");
|
|
return;
|
|
}
|
|
|
|
pKeSignalCallDpcDone = get_proc_address("KeSignalCallDpcDone");
|
|
ok(!!pKeSignalCallDpcDone, "KeSignalCallDpcDone is not available.\n");
|
|
pKeSignalCallDpcSynchronize = get_proc_address("KeSignalCallDpcSynchronize");
|
|
ok(!!pKeSignalCallDpcSynchronize, "KeSignalCallDpcSynchronize is not available.\n");
|
|
|
|
|
|
cpu_mask = KeQueryActiveProcessors();
|
|
cpu_count = 0;
|
|
while (cpu_mask)
|
|
{
|
|
if (cpu_mask & 1)
|
|
++cpu_count;
|
|
|
|
cpu_mask >>= 1;
|
|
}
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
KeSetSystemAffinityThread(0x1);
|
|
|
|
pKeGenericCallDpc(test_dpc_func, &data);
|
|
ok(data.call_count == cpu_count, "Got unexpected call_count %u.\n", data.call_count);
|
|
ok(data.selected_count == 2, "Got unexpected selected_count %u.\n", data.selected_count);
|
|
ok(data.sync_barrier_start_value.Barrier == cpu_count,
|
|
"Got unexpected sync_barrier_start_value.Barrier %d.\n",
|
|
data.sync_barrier_start_value.Barrier);
|
|
ok(data.sync_barrier_start_value.TotalProcessors == cpu_count,
|
|
"Got unexpected sync_barrier_start_value.TotalProcessors %d.\n",
|
|
data.sync_barrier_start_value.TotalProcessors);
|
|
|
|
ok(data.sync_barrier_mid_value.Barrier == (0x80000000 | cpu_count),
|
|
"Got unexpected sync_barrier_mid_value.Barrier %d.\n",
|
|
data.sync_barrier_mid_value.Barrier);
|
|
ok(data.sync_barrier_mid_value.TotalProcessors == cpu_count,
|
|
"Got unexpected sync_barrier_mid_value.TotalProcessors %d.\n",
|
|
data.sync_barrier_mid_value.TotalProcessors);
|
|
|
|
ok(data.sync_barrier_end_value.Barrier == cpu_count,
|
|
"Got unexpected sync_barrier_end_value.Barrier %d.\n",
|
|
data.sync_barrier_end_value.Barrier);
|
|
ok(data.sync_barrier_end_value.TotalProcessors == cpu_count,
|
|
"Got unexpected sync_barrier_end_value.TotalProcessors %d.\n",
|
|
data.sync_barrier_end_value.TotalProcessors);
|
|
|
|
ok(data.done_barrier_start_value == cpu_count, "Got unexpected done_barrier_start_value %d.\n", data.done_barrier_start_value);
|
|
|
|
KeRevertToUserAffinityThread();
|
|
}
|
|
|
|
static void test_process_memory(const struct main_test_input *test_input)
|
|
{
|
|
NTSTATUS (WINAPI *pMmCopyVirtualMemory)(PEPROCESS fromprocess, void *fromaddress, PEPROCESS toprocess,
|
|
void *toaddress, SIZE_T bufsize, KPROCESSOR_MODE mode, SIZE_T *copied);
|
|
char buffer[sizeof(teststr)];
|
|
ULONG64 modified_value;
|
|
PEPROCESS process;
|
|
KAPC_STATE state;
|
|
NTSTATUS status;
|
|
SIZE_T size;
|
|
BYTE *base;
|
|
|
|
pMmCopyVirtualMemory = get_proc_address("MmCopyVirtualMemory");
|
|
|
|
status = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)test_input->process_id, &process);
|
|
ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
if (0) /* Crashes on Windows. */
|
|
PsGetProcessSectionBaseAddress(NULL);
|
|
|
|
base = PsGetProcessSectionBaseAddress(process);
|
|
ok(!!base, "Got NULL base address.\n");
|
|
|
|
ok(process == PsGetCurrentProcess(), "Got unexpected process %p, PsGetCurrentProcess() %p.\n",
|
|
process, PsGetCurrentProcess());
|
|
|
|
modified_value = 0xdeadbeeffeedcafe;
|
|
if (pMmCopyVirtualMemory)
|
|
{
|
|
size = 0xdeadbeef;
|
|
status = pMmCopyVirtualMemory(process, base + test_input->teststr_offset, PsGetCurrentProcess(),
|
|
buffer, sizeof(buffer), UserMode, &size);
|
|
todo_wine ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#x.\n", status);
|
|
ok(!size, "Got unexpected size %#lx.\n", size);
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
size = 0xdeadbeef;
|
|
if (0) /* Passing NULL for the copied size address hangs Windows. */
|
|
pMmCopyVirtualMemory(process, base + test_input->teststr_offset, PsGetCurrentProcess(),
|
|
buffer, sizeof(buffer), KernelMode, NULL);
|
|
status = pMmCopyVirtualMemory(process, base + test_input->teststr_offset, PsGetCurrentProcess(),
|
|
buffer, sizeof(buffer), KernelMode, &size);
|
|
todo_wine ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status);
|
|
todo_wine ok(size == sizeof(buffer), "Got unexpected size %lu.\n", size);
|
|
todo_wine ok(!strcmp(buffer, teststr), "Got unexpected test string.\n");
|
|
}
|
|
else
|
|
{
|
|
win_skip("MmCopyVirtualMemory is not available.\n");
|
|
}
|
|
|
|
if (!running_under_wine)
|
|
{
|
|
KeStackAttachProcess((PKPROCESS)process, &state);
|
|
todo_wine ok(!strcmp(teststr, (char *)(base + test_input->teststr_offset)),
|
|
"Strings do not match.\n");
|
|
*test_input->modified_value = modified_value;
|
|
KeUnstackDetachProcess(&state);
|
|
}
|
|
ObDereferenceObject(process);
|
|
}
|
|
|
|
static void test_permanence(void)
|
|
{
|
|
OBJECT_ATTRIBUTES attr;
|
|
HANDLE handle, handle2;
|
|
UNICODE_STRING str;
|
|
NTSTATUS status;
|
|
|
|
RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\wine_test_dir");
|
|
InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
|
|
status = ZwCreateDirectoryObject( &handle, GENERIC_ALL, &attr );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwClose( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwOpenDirectoryObject( &handle, 0, &attr );
|
|
ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", status);
|
|
|
|
attr.Attributes = OBJ_PERMANENT;
|
|
status = ZwCreateDirectoryObject( &handle, GENERIC_ALL, &attr );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwClose( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
|
|
attr.Attributes = 0;
|
|
status = ZwOpenDirectoryObject( &handle, 0, &attr );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwMakeTemporaryObject( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwMakeTemporaryObject( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwClose( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwOpenDirectoryObject( &handle, 0, &attr );
|
|
ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", status);
|
|
|
|
status = ZwCreateDirectoryObject( &handle, GENERIC_ALL, &attr );
|
|
ok(!status, "got %#x\n", status);
|
|
attr.Attributes = OBJ_PERMANENT;
|
|
status = ZwOpenDirectoryObject( &handle2, 0, &attr );
|
|
ok(status == STATUS_SUCCESS, "got %#x\n", status);
|
|
status = ZwClose( handle2 );
|
|
ok(!status, "got %#x\n", status);
|
|
status = ZwClose( handle );
|
|
ok(!status, "got %#x\n", status);
|
|
attr.Attributes = 0;
|
|
status = ZwOpenDirectoryObject( &handle, 0, &attr );
|
|
ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", status);
|
|
}
|
|
|
|
static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack)
|
|
{
|
|
void *buffer = irp->AssociatedIrp.SystemBuffer;
|
|
struct main_test_input *test_input = buffer;
|
|
|
|
if (!buffer)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
pExEventObjectType = get_proc_address("ExEventObjectType");
|
|
ok(!!pExEventObjectType, "ExEventObjectType not found\n");
|
|
|
|
pIoFileObjectType = get_proc_address("IoFileObjectType");
|
|
ok(!!pIoFileObjectType, "IofileObjectType not found\n");
|
|
|
|
pPsThreadType = get_proc_address("PsThreadType");
|
|
ok(!!pPsThreadType, "IofileObjectType not found\n");
|
|
|
|
pPsInitialSystemProcess = get_proc_address("PsInitialSystemProcess");
|
|
ok(!!pPsInitialSystemProcess, "PsInitialSystemProcess not found\n");
|
|
|
|
test_irp_struct(irp, device);
|
|
test_current_thread(FALSE);
|
|
test_critical_region(TRUE);
|
|
test_mdl_map();
|
|
test_init_funcs();
|
|
test_load_driver();
|
|
test_sync();
|
|
test_queue();
|
|
test_version();
|
|
test_stack_callout();
|
|
test_lookaside_list();
|
|
test_ob_reference();
|
|
test_resource();
|
|
test_lookup_thread();
|
|
test_IoAttachDeviceToDeviceStack();
|
|
test_object_name();
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
test_executable_pool();
|
|
#endif
|
|
test_affinity();
|
|
test_dpc();
|
|
test_process_memory(test_input);
|
|
test_permanence();
|
|
|
|
if (main_test_work_item) return STATUS_UNEXPECTED_IO_ERROR;
|
|
|
|
main_test_work_item = IoAllocateWorkItem(lower_device);
|
|
ok(main_test_work_item != NULL, "main_test_work_item = NULL\n");
|
|
|
|
IoMarkIrpPending(irp);
|
|
IoQueueWorkItem(main_test_work_item, main_test_task, DelayedWorkQueue, irp);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
static NTSTATUS test_basic_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
|
{
|
|
ULONG length = min(stack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(teststr));
|
|
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (!buffer)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
memcpy(buffer, teststr, length);
|
|
*info = length;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS get_dword(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info, DWORD value)
|
|
{
|
|
ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (!buffer)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
if (length < sizeof(DWORD))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
*(DWORD*)buffer = value;
|
|
*info = sizeof(DWORD);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS get_fscontext(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
|
{
|
|
ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
|
struct file_context *context = stack->FileObject->FsContext;
|
|
|
|
if (!buffer)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
if (length < sizeof(DWORD))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
*(DWORD*)buffer = context->id;
|
|
*info = sizeof(DWORD);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS return_status(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
|
{
|
|
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
|
NTSTATUS ret;
|
|
|
|
if (!buffer)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DWORD)
|
|
|| stack->Parameters.DeviceIoControl.OutputBufferLength < 3)
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
ret = *(DWORD *)irp->AssociatedIrp.SystemBuffer;
|
|
memcpy(buffer, "ghi", 3);
|
|
*info = 3;
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
|
{
|
|
BOOL *load = irp->AssociatedIrp.SystemBuffer;
|
|
UNICODE_STRING name;
|
|
|
|
if (!load)
|
|
return STATUS_ACCESS_VIOLATION;
|
|
|
|
*info = 0;
|
|
|
|
RtlInitUnicodeString(&name, driver2_path);
|
|
if (*load)
|
|
return ZwLoadDriver(&name);
|
|
else
|
|
return ZwUnloadDriver(&name);
|
|
}
|
|
|
|
static NTSTATUS test_mismatched_status_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
|
{
|
|
ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
char *buffer = irp->UserBuffer;
|
|
|
|
if (!buffer)
|
|
{
|
|
irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
if (length < sizeof(teststr))
|
|
{
|
|
irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
memcpy(buffer, teststr, sizeof(teststr));
|
|
|
|
/* This is deliberate; some broken drivers do this */
|
|
*info = 0;
|
|
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS test_completion_ioctl(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
if (device == upper_device)
|
|
{
|
|
IoCopyCurrentIrpStackLocationToNext(irp);
|
|
IoSetCompletionRoutine(irp, completion_cb, upper_device, TRUE, TRUE, TRUE);
|
|
return IoCallDriver(lower_device, irp);
|
|
}
|
|
else
|
|
{
|
|
ok(device == lower_device, "Got wrong device.\n");
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
|
|
struct file_context *context = ExAllocatePool(PagedPool, sizeof(*context));
|
|
|
|
if (!context)
|
|
{
|
|
irp->IoStatus.Status = STATUS_NO_MEMORY;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
context->id = ++create_count;
|
|
context->namelen = min(irpsp->FileObject->FileName.Length, sizeof(context->name));
|
|
memcpy(context->name, irpsp->FileObject->FileName.Buffer, context->namelen);
|
|
irpsp->FileObject->FsContext = context;
|
|
|
|
last_created_file = irpsp->FileObject;
|
|
create_caller_thread = KeGetCurrentThread();
|
|
create_irp_thread = irp->Tail.Overlay.Thread;
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
|
|
switch (stack->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
case IOCTL_WINETEST_BASIC_IOCTL:
|
|
status = test_basic_ioctl(irp, stack, &irp->IoStatus.Information);
|
|
break;
|
|
case IOCTL_WINETEST_MAIN_TEST:
|
|
status = main_test(device, irp, stack);
|
|
break;
|
|
case IOCTL_WINETEST_LOAD_DRIVER:
|
|
status = test_load_driver_ioctl(irp, stack, &irp->IoStatus.Information);
|
|
break;
|
|
case IOCTL_WINETEST_RESET_CANCEL:
|
|
cancel_cnt = 0;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
case IOCTL_WINETEST_TEST_CANCEL:
|
|
IoSetCancelRoutine(irp, cancel_ioctl_irp);
|
|
IoMarkIrpPending(irp);
|
|
return STATUS_PENDING;
|
|
case IOCTL_WINETEST_GET_CANCEL_COUNT:
|
|
status = get_dword(irp, stack, &irp->IoStatus.Information, cancel_cnt);
|
|
break;
|
|
case IOCTL_WINETEST_GET_CREATE_COUNT:
|
|
status = get_dword(irp, stack, &irp->IoStatus.Information, create_count);
|
|
break;
|
|
case IOCTL_WINETEST_GET_CLOSE_COUNT:
|
|
status = get_dword(irp, stack, &irp->IoStatus.Information, close_count);
|
|
break;
|
|
case IOCTL_WINETEST_GET_FSCONTEXT:
|
|
status = get_fscontext(irp, stack, &irp->IoStatus.Information);
|
|
break;
|
|
case IOCTL_WINETEST_RETURN_STATUS:
|
|
status = return_status(irp, stack, &irp->IoStatus.Information);
|
|
break;
|
|
case IOCTL_WINETEST_DETACH:
|
|
IoDetachDevice(lower_device);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
case IOCTL_WINETEST_MISMATCHED_STATUS:
|
|
return test_mismatched_status_ioctl(irp, stack, &irp->IoStatus.Information);
|
|
case IOCTL_WINETEST_COMPLETION:
|
|
return test_completion_ioctl(device, irp);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
irp->IoStatus.Status = status;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_FlushBuffers(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
|
ok(device == lower_device, "Expected device %p, got %p.\n", lower_device, device);
|
|
ok(irpsp->DeviceObject == device, "device != DeviceObject\n");
|
|
ok(irp->Tail.Overlay.Thread == (PETHREAD)KeGetCurrentThread(),
|
|
"IRP thread is not the current thread\n");
|
|
IoMarkIrpPending(irp);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
static BOOL compare_file_name(const struct file_context *context, const WCHAR *expect)
|
|
{
|
|
return context->namelen == wcslen(expect) * sizeof(WCHAR)
|
|
&& !kmemcmp(context->name, expect, context->namelen);
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_QueryInformation(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
|
NTSTATUS ret;
|
|
|
|
switch (stack->Parameters.QueryFile.FileInformationClass)
|
|
{
|
|
case FileNameInformation:
|
|
{
|
|
const struct file_context *context = stack->FileObject->FsContext;
|
|
FILE_NAME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
|
|
ULONG len;
|
|
|
|
if (stack->Parameters.QueryFile.Length < sizeof(*info))
|
|
{
|
|
ret = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
if (compare_file_name(context, L"\\notimpl"))
|
|
{
|
|
ret = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
else if (compare_file_name(context, L""))
|
|
{
|
|
ret = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
else if (compare_file_name(context, L"\\badparam"))
|
|
{
|
|
ret = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
else if (compare_file_name(context, L"\\genfail"))
|
|
{
|
|
ret = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
else if (compare_file_name(context, L"\\badtype"))
|
|
{
|
|
ret = STATUS_OBJECT_TYPE_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
len = stack->Parameters.QueryFile.Length - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
|
|
if (len < context->namelen)
|
|
ret = STATUS_BUFFER_OVERFLOW;
|
|
else
|
|
{
|
|
len = context->namelen;
|
|
ret = STATUS_SUCCESS;
|
|
}
|
|
irp->IoStatus.Information = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + len;
|
|
info->FileNameLength = context->namelen;
|
|
memcpy(info->FileName, context->name, len);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ret = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
irp->IoStatus.Status = ret;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_QueryVolumeInformation(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
|
ULONG length = stack->Parameters.QueryVolume.Length;
|
|
NTSTATUS ret;
|
|
|
|
switch (stack->Parameters.QueryVolume.FsInformationClass)
|
|
{
|
|
case FileFsVolumeInformation:
|
|
{
|
|
FILE_FS_VOLUME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
|
|
static const WCHAR label[] = L"WineTestDriver";
|
|
ULONG serial = 0xdeadbeef;
|
|
|
|
if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
|
|
{
|
|
ret = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
info->VolumeCreationTime.QuadPart = 0;
|
|
info->VolumeSerialNumber = serial;
|
|
info->VolumeLabelLength = min( lstrlenW(label) * sizeof(WCHAR),
|
|
length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
|
|
info->SupportsObjects = TRUE;
|
|
memcpy( info->VolumeLabel, label, info->VolumeLabelLength );
|
|
|
|
irp->IoStatus.Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
default:
|
|
ret = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
irp->IoStatus.Status = ret;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS WINAPI driver_Close(DEVICE_OBJECT *device, IRP *irp)
|
|
{
|
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
|
++close_count;
|
|
if (stack->FileObject->FsContext)
|
|
ExFreePool(stack->FileObject->FsContext);
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static VOID WINAPI driver_Unload(DRIVER_OBJECT *driver)
|
|
{
|
|
UNICODE_STRING linkW;
|
|
|
|
DbgPrint("unloading driver\n");
|
|
|
|
RtlInitUnicodeString(&linkW, L"\\DosDevices\\WineTestDriver");
|
|
IoDeleteSymbolicLink(&linkW);
|
|
|
|
IoDeleteDevice(upper_device);
|
|
IoDeleteDevice(lower_device);
|
|
|
|
winetest_cleanup();
|
|
}
|
|
|
|
NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry)
|
|
{
|
|
UNICODE_STRING nameW, linkW;
|
|
NTSTATUS status;
|
|
void *obj;
|
|
|
|
if ((status = winetest_init()))
|
|
return status;
|
|
|
|
DbgPrint("loading driver\n");
|
|
|
|
driver_obj = driver;
|
|
|
|
/* Allow unloading of the driver */
|
|
driver->DriverUnload = driver_Unload;
|
|
|
|
/* Set driver functions */
|
|
driver->MajorFunction[IRP_MJ_CREATE] = driver_Create;
|
|
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_IoControl;
|
|
driver->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = driver_FlushBuffers;
|
|
driver->MajorFunction[IRP_MJ_QUERY_INFORMATION] = driver_QueryInformation;
|
|
driver->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = driver_QueryVolumeInformation;
|
|
driver->MajorFunction[IRP_MJ_CLOSE] = driver_Close;
|
|
|
|
RtlInitUnicodeString(&nameW, L"IoDriverObjectType");
|
|
pIoDriverObjectType = MmGetSystemRoutineAddress(&nameW);
|
|
|
|
RtlInitUnicodeString(&nameW, L"\\Driver\\WineTestDriver");
|
|
status = ObReferenceObjectByName(&nameW, 0, NULL, 0, *pIoDriverObjectType, KernelMode, NULL, &obj);
|
|
ok(!status, "got %#x\n", status);
|
|
ok(obj == driver, "expected %p, got %p\n", driver, obj);
|
|
ObDereferenceObject(obj);
|
|
|
|
RtlInitUnicodeString(&nameW, L"\\Device\\WineTestDriver");
|
|
RtlInitUnicodeString(&linkW, L"\\DosDevices\\WineTestDriver");
|
|
|
|
status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &lower_device);
|
|
ok(!status, "failed to create device, status %#x\n", status);
|
|
status = IoCreateSymbolicLink(&linkW, &nameW);
|
|
ok(!status, "failed to create link, status %#x\n", status);
|
|
lower_device->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
RtlInitUnicodeString(&nameW, L"\\Device\\WineTestUpper");
|
|
status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &upper_device);
|
|
ok(!status, "failed to create device, status %#x\n", status);
|
|
|
|
IoAttachDeviceToDeviceStack(upper_device, lower_device);
|
|
upper_device->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|