diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in index 7946b8de220..06c06671ca1 100644 --- a/dlls/ntoskrnl.exe/tests/Makefile.in +++ b/dlls/ntoskrnl.exe/tests/Makefile.in @@ -7,6 +7,8 @@ driver2_IMPORTS = winecrt0 ntoskrnl driver2_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native driver3_IMPORTS = winecrt0 ntoskrnl driver3_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native +driver4_IMPORTS = winecrt0 ntoskrnl netio +driver4_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native SOURCES = \ driver.c \ @@ -15,4 +17,6 @@ SOURCES = \ driver2.spec \ driver3.c \ driver3.spec \ + driver4.c \ + driver4.spec \ ntoskrnl.c diff --git a/dlls/ntoskrnl.exe/tests/driver4.c b/dlls/ntoskrnl.exe/tests/driver4.c new file mode 100644 index 00000000000..2fe6072ec58 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver4.c @@ -0,0 +1,246 @@ +/* + * ntoskrnl.exe testing framework + * + * Copyright 2020 Paul Gofman for Codeweavers + * + * 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 +#include + +#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 "ddk/wsk.h" + +#include "driver.h" + +#include "utils.h" + +static DRIVER_OBJECT *driver_obj; +static DEVICE_OBJECT *lower_device, *upper_device; + +static unsigned int create_count, close_count; + +static const WCHAR driver_link[] = L"\\DosDevices\\WineTestDriver4"; +static const WCHAR device_name[] = L"\\Device\\WineTestDriver4"; +static const WCHAR upper_name[] = L"\\Device\\WineTestUpper4"; + +static FILE_OBJECT *last_created_file; +static void *create_caller_thread; +static PETHREAD create_irp_thread; + +static POBJECT_TYPE *pIoDriverObjectType; + +static WSK_CLIENT_NPI client_npi; +static WSK_REGISTRATION registration; +static WSK_PROVIDER_NPI provider_npi; + +static void netio_init(void) +{ + const WSK_CLIENT_DISPATCH client_dispatch = + { + MAKE_WSK_VERSION(1, 0), 0, NULL + }; + + NTSTATUS status; + + client_npi.Dispatch = &client_dispatch; + status = WskRegister(&client_npi, ®istration); + todo_wine ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); + + status = WskCaptureProviderNPI(®istration, WSK_INFINITE_WAIT, &provider_npi); + todo_wine ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); +} + +static void netio_uninit(void) +{ + WskReleaseProviderNPI(®istration); + WskDeregister(®istration); +} + +static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack) +{ + ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; + void *buffer = irp->AssociatedIrp.SystemBuffer; + struct test_input *test_input = buffer; + OBJECT_ATTRIBUTES attr = {0}; + UNICODE_STRING pathU; + IO_STATUS_BLOCK io; + + if (!buffer) + return STATUS_ACCESS_VIOLATION; + if (length < sizeof(failures)) + return STATUS_BUFFER_TOO_SMALL; + + attr.Length = sizeof(attr); + RtlInitUnicodeString(&pathU, test_input->path); + running_under_wine = test_input->running_under_wine; + winetest_debug = test_input->winetest_debug; + winetest_report_success = test_input->winetest_report_success; + attr.ObjectName = &pathU; + attr.Attributes = OBJ_KERNEL_HANDLE; /* needed to be accessible from system threads */ + ZwOpenFile(&okfile, FILE_APPEND_DATA | SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT); + + netio_init(); + + if (winetest_debug) + { + kprintf("%04x:ntoskrnl: %d tests executed (%d marked as todo, %d %s), %d skipped.\n", + PsGetCurrentProcessId(), successes + failures + todo_successes + todo_failures, + todo_successes, failures + todo_failures, + (failures + todo_failures != 1) ? "failures" : "failure", skipped ); + } + ZwClose(okfile); + + *((LONG *)buffer) = failures; + irp->IoStatus.Information = sizeof(failures); + 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_MAIN_TEST: + status = main_test(device, irp, stack); + break; + case IOCTL_WINETEST_DETACH: + IoDetachDevice(lower_device); + status = STATUS_SUCCESS; + break; + default: + break; + } + + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } + return status; +} + +static NTSTATUS WINAPI driver_create(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + DWORD *context = ExAllocatePool(PagedPool, sizeof(*context)); + + last_created_file = irpsp->FileObject; + ++create_count; + if (context) + *context = create_count; + irpsp->FileObject->FsContext = context; + 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_close(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + + netio_uninit(); + + ++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, driver_link); + IoDeleteSymbolicLink(&linkW); + + IoDeleteDevice(upper_device); + IoDeleteDevice(lower_device); +} + +NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) +{ + static const WCHAR IoDriverObjectTypeW[] = L"IoDriverObjectType"; + static const WCHAR driver_nameW[] = L"\\Driver\\WineTestDriver4"; + UNICODE_STRING nameW, linkW; + NTSTATUS status; + void *obj; + + DbgPrint("Loading driver.\n"); + + driver_obj = driver; + + driver->DriverUnload = driver_unload; + driver->MajorFunction[IRP_MJ_CREATE] = driver_create; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_iocontrol; + driver->MajorFunction[IRP_MJ_CLOSE] = driver_close; + + RtlInitUnicodeString(&nameW, IoDriverObjectTypeW); + pIoDriverObjectType = MmGetSystemRoutineAddress(&nameW); + + RtlInitUnicodeString(&nameW, driver_nameW); + if ((status = ObReferenceObjectByName(&nameW, 0, NULL, 0, *pIoDriverObjectType, KernelMode, NULL, &obj))) + return status; + if (obj != driver) + { + ObDereferenceObject(obj); + return STATUS_UNSUCCESSFUL; + } + ObDereferenceObject(obj); + + RtlInitUnicodeString(&nameW, device_name); + RtlInitUnicodeString(&linkW, driver_link); + + if (!(status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, FALSE, &lower_device))) + { + status = IoCreateSymbolicLink(&linkW, &nameW); + lower_device->Flags &= ~DO_DEVICE_INITIALIZING; + } + + if (!status) + { + RtlInitUnicodeString(&nameW, upper_name); + + status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, FALSE, &upper_device); + } + + if (!status) + { + IoAttachDeviceToDeviceStack(upper_device, lower_device); + upper_device->Flags &= ~DO_DEVICE_INITIALIZING; + } + + return status; +} diff --git a/dlls/ntoskrnl.exe/tests/driver4.spec b/dlls/ntoskrnl.exe/tests/driver4.spec new file mode 100644 index 00000000000..ad33444716a --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver4.spec @@ -0,0 +1 @@ +# nothing here yet diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 8d2da92455a..c2afee3dfe9 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -109,17 +109,25 @@ static SC_HANDLE load_driver(char *filename, const char *resname, const char *dr return service; } -static BOOL start_driver(HANDLE service) +static BOOL start_driver(HANDLE service, BOOL vista_plus) { SERVICE_STATUS status; BOOL ret; SetLastError(0xdeadbeef); ret = StartServiceA(service, 0, NULL); - if (!ret && (GetLastError() == ERROR_DRIVER_BLOCKED || GetLastError() == ERROR_INVALID_IMAGE_HASH)) + if (!ret && (GetLastError() == ERROR_DRIVER_BLOCKED || GetLastError() == ERROR_INVALID_IMAGE_HASH + || (vista_plus && GetLastError() == ERROR_FILE_NOT_FOUND))) { - /* If Secure Boot is enabled or the machine is 64-bit, it will reject an unsigned driver. */ - skip("Failed to start service; probably your machine doesn't accept unsigned drivers.\n"); + if (vista_plus && GetLastError() == ERROR_FILE_NOT_FOUND) + { + skip("Windows Vista or newer is required to run this service.\n"); + } + else + { + /* If Secure Boot is enabled or the machine is 64-bit, it will reject an unsigned driver. */ + skip("Failed to start service; probably your machine doesn't accept unsigned drivers.\n"); + } DeleteService(service); CloseServiceHandle(service); return FALSE; @@ -143,13 +151,14 @@ static BOOL start_driver(HANDLE service) return TRUE; } +static ULONG64 modified_value; + static void main_test(void) { static const WCHAR dokW[] = {'d','o','k',0}; WCHAR temppathW[MAX_PATH], pathW[MAX_PATH]; struct test_input *test_input; DWORD len, written, read; - ULONG64 modified_value; UNICODE_STRING pathU; LONG new_failures; char buffer[512]; @@ -178,8 +187,6 @@ static void main_test(void) ok(res, "DeviceIoControl failed: %u\n", GetLastError()); ok(written == sizeof(new_failures), "got size %x\n", written); - todo_wine ok(modified_value == 0xdeadbeeffeedcafe, "Got unexpected value %#I64x.\n", modified_value); - okfile = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); ok(okfile != INVALID_HANDLE_VALUE, "failed to create %s: %u\n", wine_dbgstr_w(pathW), GetLastError()); @@ -506,6 +513,37 @@ static void test_driver3(void) DeleteFileA(filename); } +void test_driver4(void) +{ + char filename[MAX_PATH]; + SC_HANDLE service; + DWORD written; + BOOL ret; + + if (!(service = load_driver(filename, "driver4.dll", "WineTestDriver4"))) + return; + + if (!start_driver(service, TRUE)) + { + DeleteFileA(filename); + return; + } + + device = CreateFileA("\\\\.\\WineTestDriver4", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError()); + + main_test(); + + ret = DeviceIoControl(device, IOCTL_WINETEST_DETACH, NULL, 0, NULL, 0, &written, NULL); + ok(ret, "DeviceIoControl failed: %u\n", GetLastError()); + + CloseHandle(device); + + unload_driver(service); + ret = DeleteFileA(filename); + ok(ret, "DeleteFile failed: %u\n", GetLastError()); +} + START_TEST(ntoskrnl) { char filename[MAX_PATH], filename2[MAX_PATH]; @@ -523,7 +561,7 @@ START_TEST(ntoskrnl) subtest("driver"); if (!(service = load_driver(filename, "driver.dll", "WineTestDriver"))) return; - if (!start_driver(service)) + if (!start_driver(service, FALSE)) { DeleteFileA(filename); return; @@ -535,7 +573,10 @@ START_TEST(ntoskrnl) test_basic_ioctl(); test_mismatched_status_ioctl(); + main_test(); + todo_wine ok(modified_value == 0xdeadbeeffeedcafe, "Got unexpected value %#I64x.\n", modified_value); + test_overlapped(); test_load_driver(service2); test_file_handles(); @@ -556,4 +597,6 @@ START_TEST(ntoskrnl) ok(ret, "DeleteFile failed: %u\n", GetLastError()); test_driver3(); + subtest("driver4"); + test_driver4(); }