1 /* 2 * PROJECT: ReactOS kernel-mode tests 3 * LICENSE: GPLv2+ - See COPYING in the top level directory 4 * PURPOSE: Kernel-Mode Test Suite Driver 5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org> 6 */ 7 8 #include <ntddk.h> 9 #include <ntifs.h> 10 #include <ndk/ketypes.h> 11 #include <ntstrsafe.h> 12 #include <limits.h> 13 #include <pseh/pseh2.h> 14 15 #define NDEBUG 16 #include <debug.h> 17 18 #include <kmt_public.h> 19 #define KMT_DEFINE_TEST_FUNCTIONS 20 #include <kmt_test.h> 21 22 /* Usermode callback definitions */ 23 typedef struct _KMT_USER_WORK_ENTRY 24 { 25 LIST_ENTRY ListEntry; 26 KEVENT WorkDoneEvent; 27 KMT_CALLBACK_REQUEST_PACKET Request; 28 PKMT_RESPONSE Response; 29 } KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY; 30 31 typedef struct _KMT_USER_WORK_LIST 32 { 33 LIST_ENTRY ListHead; 34 FAST_MUTEX Lock; 35 KEVENT NewWorkEvent; 36 } KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST; 37 38 /* Prototypes */ 39 DRIVER_INITIALIZE DriverEntry; 40 static DRIVER_UNLOAD DriverUnload; 41 __drv_dispatchType(IRP_MJ_CREATE) 42 static DRIVER_DISPATCH DriverCreate; 43 __drv_dispatchType(IRP_MJ_CLEANUP) 44 static DRIVER_DISPATCH DriverCleanup; 45 __drv_dispatchType(IRP_MJ_CLOSE) 46 static DRIVER_DISPATCH DriverClose; 47 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) 48 static DRIVER_DISPATCH DriverIoControl; 49 static VOID KmtCleanUsermodeCallbacks(VOID); 50 51 /* Globals */ 52 static PDEVICE_OBJECT MainDeviceObject; 53 PDRIVER_OBJECT KmtDriverObject = NULL; 54 static KMT_USER_WORK_LIST WorkList; 55 static ULONG RequestId = 0; 56 57 /* Entry */ 58 /** 59 * @name DriverEntry 60 * 61 * Driver Entry point. 62 * 63 * @param DriverObject 64 * Driver Object 65 * @param RegistryPath 66 * Driver Registry Path 67 * 68 * @return Status 69 */ 70 NTSTATUS 71 NTAPI 72 DriverEntry( 73 IN PDRIVER_OBJECT DriverObject, 74 IN PUNICODE_STRING RegistryPath) 75 { 76 NTSTATUS Status = STATUS_SUCCESS; 77 UNICODE_STRING DeviceName; 78 PKMT_DEVICE_EXTENSION DeviceExtension; 79 PKPRCB Prcb; 80 81 PAGED_CODE(); 82 83 UNREFERENCED_PARAMETER(RegistryPath); 84 85 DPRINT("DriverEntry\n"); 86 87 Prcb = KeGetCurrentPrcb(); 88 KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0; 89 KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0; 90 KmtDriverObject = DriverObject; 91 92 RtlInitUnicodeString(&DeviceName, KMTEST_DEVICE_DRIVER_PATH); 93 Status = IoCreateDevice(DriverObject, sizeof(KMT_DEVICE_EXTENSION), 94 &DeviceName, 95 FILE_DEVICE_UNKNOWN, 96 FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE, 97 FALSE, &MainDeviceObject); 98 99 if (!NT_SUCCESS(Status)) 100 goto cleanup; 101 102 DPRINT("DriverEntry. Created DeviceObject %p. DeviceExtension %p\n", 103 MainDeviceObject, MainDeviceObject->DeviceExtension); 104 DeviceExtension = MainDeviceObject->DeviceExtension; 105 DeviceExtension->ResultBuffer = NULL; 106 DeviceExtension->Mdl = NULL; 107 108 DriverObject->DriverUnload = DriverUnload; 109 DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate; 110 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DriverCleanup; 111 DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose; 112 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl; 113 114 ExInitializeFastMutex(&WorkList.Lock); 115 KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE); 116 InitializeListHead(&WorkList.ListHead); 117 118 cleanup: 119 if (MainDeviceObject && !NT_SUCCESS(Status)) 120 { 121 IoDeleteDevice(MainDeviceObject); 122 MainDeviceObject = NULL; 123 } 124 125 return Status; 126 } 127 128 /* Dispatch functions */ 129 /** 130 * @name DriverUnload 131 * 132 * Driver cleanup funtion. 133 * 134 * @param DriverObject 135 * Driver Object 136 */ 137 static 138 VOID 139 NTAPI 140 DriverUnload( 141 IN PDRIVER_OBJECT DriverObject) 142 { 143 PAGED_CODE(); 144 145 UNREFERENCED_PARAMETER(DriverObject); 146 147 DPRINT("DriverUnload\n"); 148 149 KmtCleanUsermodeCallbacks(); 150 151 if (MainDeviceObject) 152 { 153 #if DBG 154 PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension; 155 ASSERT(!DeviceExtension->Mdl); 156 ASSERT(!DeviceExtension->ResultBuffer); 157 #endif 158 ASSERT(!ResultBuffer); 159 IoDeleteDevice(MainDeviceObject); 160 } 161 } 162 163 /** 164 * @name DriverCreate 165 * 166 * Driver Dispatch function for IRP_MJ_CREATE 167 * 168 * @param DeviceObject 169 * Device Object 170 * @param Irp 171 * I/O request packet 172 * 173 * @return Status 174 */ 175 static 176 NTSTATUS 177 NTAPI 178 DriverCreate( 179 IN PDEVICE_OBJECT DeviceObject, 180 IN PIRP Irp) 181 { 182 NTSTATUS Status = STATUS_SUCCESS; 183 PIO_STACK_LOCATION IoStackLocation; 184 185 PAGED_CODE(); 186 187 IoStackLocation = IoGetCurrentIrpStackLocation(Irp); 188 189 DPRINT("DriverCreate. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n", 190 DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject, 191 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2); 192 193 Irp->IoStatus.Status = Status; 194 Irp->IoStatus.Information = 0; 195 196 IoCompleteRequest(Irp, IO_NO_INCREMENT); 197 198 return Status; 199 } 200 201 /** 202 * @name DriverCleanup 203 * 204 * Driver Dispatch function for IRP_MJ_CLEANUP 205 * 206 * @param DeviceObject 207 * Device Object 208 * @param Irp 209 * I/O request packet 210 * 211 * @return Status 212 */ 213 static 214 NTSTATUS 215 NTAPI 216 DriverCleanup( 217 IN PDEVICE_OBJECT DeviceObject, 218 IN PIRP Irp) 219 { 220 NTSTATUS Status = STATUS_SUCCESS; 221 PIO_STACK_LOCATION IoStackLocation; 222 PKMT_DEVICE_EXTENSION DeviceExtension; 223 224 PAGED_CODE(); 225 226 IoStackLocation = IoGetCurrentIrpStackLocation(Irp); 227 228 DPRINT("DriverCleanup. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n", 229 DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject, 230 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2); 231 232 ASSERT(IoStackLocation->FileObject->FsContext2 == NULL); 233 DeviceExtension = DeviceObject->DeviceExtension; 234 if (DeviceExtension->Mdl && IoStackLocation->FileObject->FsContext == DeviceExtension->Mdl) 235 { 236 MmUnlockPages(DeviceExtension->Mdl); 237 IoFreeMdl(DeviceExtension->Mdl); 238 DeviceExtension->Mdl = NULL; 239 ResultBuffer = DeviceExtension->ResultBuffer = NULL; 240 } 241 else 242 { 243 ASSERT(IoStackLocation->FileObject->FsContext == NULL); 244 } 245 246 Irp->IoStatus.Status = Status; 247 Irp->IoStatus.Information = 0; 248 249 IoCompleteRequest(Irp, IO_NO_INCREMENT); 250 251 return Status; 252 } 253 254 /** 255 * @name DriverClose 256 * 257 * Driver Dispatch function for IRP_MJ_CLOSE 258 * 259 * @param DeviceObject 260 * Device Object 261 * @param Irp 262 * I/O request packet 263 * 264 * @return Status 265 */ 266 static 267 NTSTATUS 268 NTAPI 269 DriverClose( 270 IN PDEVICE_OBJECT DeviceObject, 271 IN PIRP Irp) 272 { 273 NTSTATUS Status = STATUS_SUCCESS; 274 275 PAGED_CODE(); 276 277 DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n", 278 DeviceObject, Irp->RequestorMode); 279 280 Irp->IoStatus.Status = Status; 281 Irp->IoStatus.Information = 0; 282 283 IoCompleteRequest(Irp, IO_NO_INCREMENT); 284 285 return Status; 286 } 287 288 /** 289 * @name DriverIoControl 290 * 291 * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL 292 * 293 * @param DeviceObject 294 * Device Object 295 * @param Irp 296 * I/O request packet 297 * 298 * @return Status 299 */ 300 static 301 NTSTATUS 302 NTAPI 303 DriverIoControl( 304 IN PDEVICE_OBJECT DeviceObject, 305 IN PIRP Irp) 306 { 307 NTSTATUS Status = STATUS_SUCCESS; 308 PIO_STACK_LOCATION IoStackLocation; 309 SIZE_T Length = 0; 310 311 PAGED_CODE(); 312 313 IoStackLocation = IoGetCurrentIrpStackLocation(Irp); 314 315 DPRINT("DriverIoControl. Code=0x%08X, DeviceObject=%p, FileObject=%p, FsContext=%p, FsContext2=%p\n", 316 IoStackLocation->Parameters.DeviceIoControl.IoControlCode, 317 DeviceObject, IoStackLocation->FileObject, 318 IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2); 319 320 switch (IoStackLocation->Parameters.DeviceIoControl.IoControlCode) 321 { 322 case IOCTL_KMTEST_GET_TESTS: 323 { 324 PCKMT_TEST TestEntry; 325 LPSTR OutputBuffer = Irp->AssociatedIrp.SystemBuffer; 326 size_t Remaining = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; 327 328 DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n", 329 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); 330 331 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry) 332 { 333 RtlStringCbCopyExA(OutputBuffer, Remaining, TestEntry->TestName, &OutputBuffer, &Remaining, 0); 334 if (Remaining) 335 { 336 *OutputBuffer++ = '\0'; 337 --Remaining; 338 } 339 } 340 if (Remaining) 341 { 342 *OutputBuffer++ = '\0'; 343 --Remaining; 344 } 345 Length = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength - Remaining; 346 break; 347 } 348 case IOCTL_KMTEST_RUN_TEST: 349 { 350 ANSI_STRING TestName; 351 PCKMT_TEST TestEntry; 352 353 DPRINT("DriverIoControl. IOCTL_KMTEST_RUN_TEST, inlen=%lu, outlen=%lu\n", 354 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, 355 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); 356 TestName.Length = TestName.MaximumLength = (USHORT)min(IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, USHRT_MAX); 357 TestName.Buffer = Irp->AssociatedIrp.SystemBuffer; 358 DPRINT("DriverIoControl. Run test: %Z\n", &TestName); 359 360 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry) 361 { 362 ANSI_STRING EntryName; 363 if (TestEntry->TestName[0] == '-') 364 RtlInitAnsiString(&EntryName, TestEntry->TestName + 1); 365 else 366 RtlInitAnsiString(&EntryName, TestEntry->TestName); 367 368 if (!RtlCompareString(&TestName, &EntryName, FALSE)) 369 { 370 DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName); 371 TestEntry->TestFunction(); 372 DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName); 373 break; 374 } 375 } 376 377 if (!TestEntry->TestName) 378 Status = STATUS_OBJECT_NAME_INVALID; 379 380 break; 381 } 382 case IOCTL_KMTEST_SET_RESULTBUFFER: 383 { 384 PKMT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 385 386 DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, buffer=%p, inlen=%lu, outlen=%lu\n", 387 IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer, 388 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, 389 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); 390 391 if (DeviceExtension->Mdl) 392 { 393 if (IoStackLocation->FileObject->FsContext != DeviceExtension->Mdl) 394 { 395 Status = STATUS_ACCESS_DENIED; 396 break; 397 } 398 MmUnlockPages(DeviceExtension->Mdl); 399 IoFreeMdl(DeviceExtension->Mdl); 400 IoStackLocation->FileObject->FsContext = NULL; 401 ResultBuffer = DeviceExtension->ResultBuffer = NULL; 402 } 403 404 DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer, 405 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, 406 FALSE, FALSE, NULL); 407 if (!DeviceExtension->Mdl) 408 { 409 Status = STATUS_INSUFFICIENT_RESOURCES; 410 break; 411 } 412 413 _SEH2_TRY 414 { 415 MmProbeAndLockPages(DeviceExtension->Mdl, UserMode, IoModifyAccess); 416 } 417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 418 { 419 Status = _SEH2_GetExceptionCode(); 420 IoFreeMdl(DeviceExtension->Mdl); 421 DeviceExtension->Mdl = NULL; 422 break; 423 } _SEH2_END; 424 425 ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority); 426 IoStackLocation->FileObject->FsContext = DeviceExtension->Mdl; 427 428 DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n", 429 ResultBuffer->Successes, ResultBuffer->Failures, 430 ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength); 431 break; 432 } 433 case IOCTL_KMTEST_USERMODE_AWAIT_REQ: 434 { 435 PLIST_ENTRY Entry; 436 PKMT_USER_WORK_ENTRY WorkItem; 437 438 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n", 439 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); 440 441 /* TODO: prevent multiple concurrent invocations */ 442 Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL); 443 if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC) 444 break; 445 446 if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET)) 447 { 448 Status = STATUS_INVALID_BUFFER_SIZE; 449 break; 450 } 451 452 ASSERT(!IsListEmpty(&WorkList.ListHead)); 453 454 Entry = WorkList.ListHead.Flink; 455 WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); 456 457 Length = sizeof(WorkItem->Request); 458 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length); 459 Status = STATUS_SUCCESS; 460 461 KeResetEvent(&WorkList.NewWorkEvent); 462 break; 463 464 } 465 case IOCTL_KMTEST_USERMODE_SEND_RESPONSE: 466 { 467 PLIST_ENTRY Entry; 468 PKMT_USER_WORK_ENTRY WorkEntry; 469 PVOID Response; 470 ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; 471 472 DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n", 473 IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, 474 IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength); 475 476 if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE)) 477 { 478 Status = STATUS_INVALID_BUFFER_SIZE; 479 break; 480 } 481 482 /* FIXME: don't misuse the output buffer as an input! */ 483 Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 484 if (Response == NULL) 485 { 486 Status = STATUS_INSUFFICIENT_RESOURCES; 487 break; 488 } 489 490 ExAcquireFastMutex(&WorkList.Lock); 491 492 Status = STATUS_OBJECTID_NOT_FOUND; 493 494 Entry = WorkList.ListHead.Flink; 495 while (Entry != &WorkList.ListHead) 496 { 497 WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); 498 if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer) 499 { 500 WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR'); 501 if (WorkEntry->Response == NULL) 502 { 503 Status = STATUS_INSUFFICIENT_RESOURCES; 504 break; 505 } 506 507 RtlCopyMemory(WorkEntry->Response, Response, ResponseSize); 508 KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE); 509 Status = STATUS_SUCCESS; 510 break; 511 } 512 513 Entry = Entry->Flink; 514 } 515 516 ExReleaseFastMutex(&WorkList.Lock); 517 518 break; 519 } 520 default: 521 DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n", 522 IoStackLocation->Parameters.DeviceIoControl.IoControlCode); 523 Status = STATUS_INVALID_DEVICE_REQUEST; 524 break; 525 } 526 527 Irp->IoStatus.Status = Status; 528 Irp->IoStatus.Information = Length; 529 530 IoCompleteRequest(Irp, IO_NO_INCREMENT); 531 532 return Status; 533 } 534 535 /** 536 * @name KmtUserModeCallback 537 * 538 * Enqueue a request to the usermode callback queue and blocks until the work 539 * is finished. 540 * 541 * @param Operation 542 * TODO 543 * @param Parameters 544 * TODO 545 * TODO: why is this PVOID? 546 * 547 * @return Response from user mode 548 */ 549 PKMT_RESPONSE 550 KmtUserModeCallback( 551 IN KMT_CALLBACK_INFORMATION_CLASS Operation, 552 IN PVOID Parameters) 553 { 554 PKMT_RESPONSE Result; 555 NTSTATUS Status; 556 PKMT_USER_WORK_ENTRY WorkEntry; 557 LARGE_INTEGER Timeout; 558 559 PAGED_CODE(); 560 561 WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW'); 562 if (WorkEntry == NULL) 563 return NULL; 564 565 KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE); 566 WorkEntry->Request.RequestId = RequestId++; 567 WorkEntry->Request.OperationClass = Operation; 568 WorkEntry->Request.Parameters = Parameters; 569 WorkEntry->Response = NULL; 570 571 ExAcquireFastMutex(&WorkList.Lock); 572 InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry); 573 ExReleaseFastMutex(&WorkList.Lock); 574 575 KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE); 576 577 Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds 578 Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout); 579 580 if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT) 581 { 582 DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status); 583 } 584 585 ExAcquireFastMutex(&WorkList.Lock); 586 RemoveEntryList(&WorkEntry->ListEntry); 587 ExReleaseFastMutex(&WorkList.Lock); 588 589 Result = WorkEntry->Response; 590 591 ExFreePoolWithTag(WorkEntry, 'ekrW'); 592 593 return Result; 594 } 595 596 /** 597 * @name KmtFreeCallbackResponse 598 * 599 * TODO 600 * 601 * @param Response 602 * TODO 603 */ 604 VOID 605 KmtFreeCallbackResponse( 606 PKMT_RESPONSE Response) 607 { 608 PAGED_CODE(); 609 610 ExFreePoolWithTag(Response, 'pseR'); 611 } 612 613 /** 614 * @name KmtCleanUsermodeCallbacks 615 * 616 * TODO 617 */ 618 static 619 VOID 620 KmtCleanUsermodeCallbacks(VOID) 621 { 622 PLIST_ENTRY Entry; 623 624 PAGED_CODE(); 625 626 ExAcquireFastMutex(&WorkList.Lock); 627 628 Entry = WorkList.ListHead.Flink; 629 while (Entry != &WorkList.ListHead) 630 { 631 PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry); 632 if (WorkEntry->Response != NULL) 633 { 634 KmtFreeCallbackResponse(WorkEntry->Response); 635 } 636 637 Entry = Entry->Flink; 638 639 ExFreePoolWithTag(WorkEntry, 'ekrW'); 640 } 641 642 ExReleaseFastMutex(&WorkList.Lock); 643 } 644