1 /*++ 2 3 Copyright (c) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 Verifier.cpp 8 9 Abstract: 10 11 This file has implementation of verifier support routines 12 13 Author: 14 15 16 17 Environment: 18 19 Shared (Kernel and user) 20 21 Revision History: 22 23 24 25 --*/ 26 27 28 #include "vfpriv.hpp" 29 30 extern "C" 31 { 32 33 extern WDFVERSION WdfVersion; 34 35 // Tracing support 36 #if defined(EVENT_TRACING) 37 #include "verifier.tmh" 38 #endif 39 40 #ifdef ALLOC_PRAGMA 41 #pragma alloc_text(FX_ENHANCED_VERIFIER_SECTION_NAME, \ 42 AddEventHooksWdfDeviceCreate, \ 43 AddEventHooksWdfIoQueueCreate, \ 44 VfAddContextToHandle, \ 45 VfAllocateContext, \ 46 VfWdfObjectGetTypedContext \ 47 ) 48 #endif 49 50 _Must_inspect_result_ 51 NTSTATUS 52 AddEventHooksWdfDeviceCreate( 53 __inout PVF_HOOK_PROCESS_INFO HookProcessInfo, 54 __in PWDF_DRIVER_GLOBALS DriverGlobals, 55 __in PWDFDEVICE_INIT* DeviceInit, 56 __in PWDF_OBJECT_ATTRIBUTES DeviceAttributes, 57 __out WDFDEVICE* Device 58 ) 59 /*++ 60 61 Routine Description: 62 63 This routine is called by main hook for WdfDeviceCreate. 64 The routine swaps the event callbacks provided by client. 65 66 Arguments: 67 68 Return value: 69 70 --*/ 71 { 72 NTSTATUS status; 73 PWDFDEVICE_INIT deviceInit = *DeviceInit; 74 WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerEvtsOriginal; 75 WDF_PNPPOWER_EVENT_CALLBACKS *pnpPowerEvts; 76 PFX_DRIVER_GLOBALS pFxDriverGlobals; 77 PVOID contextHeader = NULL; 78 WDF_OBJECT_ATTRIBUTES attributes; 79 80 PAGED_CODE_LOCKED(); 81 82 pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals); 83 84 FxPointerNotNull(pFxDriverGlobals, DeviceInit); 85 FxPointerNotNull(pFxDriverGlobals, *DeviceInit); 86 FxPointerNotNull(pFxDriverGlobals, Device); 87 88 // 89 // Check if there are any callbacks set by the client driver. If not, we 90 // don't need any callback hooking. 91 // 92 if (deviceInit->PnpPower.PnpPowerEventCallbacks.Size != 93 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)) { 94 // 95 // no hooking required. 96 // 97 status = STATUS_SUCCESS; 98 HookProcessInfo->DonotCallKmdfLib = FALSE; 99 return status; 100 } 101 102 // 103 // Hooking can be done only if we are able to allocate context memory. 104 // Try to allocate a context 105 // 106 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( 107 &attributes, 108 VF_WDFDEVICECREATE_CONTEXT 109 ); 110 111 status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader); 112 113 if (!NT_SUCCESS(status)) { 114 // 115 // couldn't allocate context. hooking not possible 116 // 117 HookProcessInfo->DonotCallKmdfLib = FALSE; 118 return status; 119 } 120 121 // 122 // store original driver callbacks to local variable 123 // 124 RtlCopyMemory(&pnpPowerEvtsOriginal, 125 &deviceInit->PnpPower.PnpPowerEventCallbacks, 126 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS) 127 ); 128 129 // 130 // Set callback hooks 131 // 132 // Normally override the hooks on local copy of stucture (not the original 133 // structure itself) and then pass the local struture to DDI call and 134 // copy back the original poniter when returning back to caller. In this case 135 // device_init is null'ed out by fx when returning to caller so we can 136 // directly override the original 137 // 138 pnpPowerEvts = &deviceInit->PnpPower.PnpPowerEventCallbacks; 139 140 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Entry); 141 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0EntryPostInterruptsEnabled); 142 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Exit); 143 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0ExitPreInterruptsDisabled); 144 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDevicePrepareHardware); 145 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceReleaseHardware); 146 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoCleanup); 147 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoFlush); 148 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoInit); 149 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoSuspend); 150 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoRestart); 151 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSurpriseRemoval); 152 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryRemove); 153 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryStop); 154 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotification); 155 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotificationEx); 156 SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceRelationsQuery); 157 158 // 159 // Call the DDI on behalf of driver. 160 // 161 status = ((PFN_WDFDEVICECREATE)WdfVersion.Functions.pfnWdfDeviceCreate)( 162 DriverGlobals, 163 DeviceInit, 164 DeviceAttributes, 165 Device 166 ); 167 // 168 // Tell main hook routine not to call the DDI in kmdf lib since we 169 // already called it 170 // 171 HookProcessInfo->DonotCallKmdfLib = TRUE; 172 HookProcessInfo->DdiCallStatus = status; 173 174 // 175 // if DDI Succeeds, add context 176 // 177 if (NT_SUCCESS(status)) { 178 PVF_WDFDEVICECREATE_CONTEXT context = NULL; 179 180 // 181 // add the already allocated context to the object. 182 // 183 status = VfAddContextToHandle(contextHeader, 184 &attributes, 185 *Device, 186 (PVOID *)&context); 187 188 if (NT_SUCCESS(status)) { 189 // 190 // store the DriverGlobals pointer used to associate the driver 191 // with its client driver callback later in the callback hooks 192 // 193 context->CommonHeader.DriverGlobals = DriverGlobals; 194 195 // 196 // store original client driver callbacks in context 197 // 198 RtlCopyMemory( 199 &context->PnpPowerEventCallbacksOriginal, 200 &pnpPowerEvtsOriginal, 201 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS) 202 ); 203 } 204 else { 205 // 206 // For some reason adding context to handle failed. This should be 207 // rare failure, because context allocation was already successful, 208 // only adding to handle failed. 209 // 210 // Hooking failed if adding context is unsuccessful. This means 211 // kmdf has callbacks hooks but verifier cannot assiociate client 212 // driver callbacks with callback hooks. This would be a fatal error. 213 // 214 ASSERTMSG("KMDF Enhanced Verifier failed to add context to object " 215 "handle\n", FALSE); 216 217 if (contextHeader != NULL) { 218 FxPoolFree(contextHeader); 219 } 220 } 221 } 222 else { 223 // 224 // KMDF DDI call failed. In case of failure, DeviceInit is not NULL'ed 225 // so the driver could potentially call WdfDeviceCreate again with this 226 // DeviceInit that contains callback hooks, and result in infinite 227 // callback loop. So put original callbacks back 228 // 229 if ((*DeviceInit) != NULL) { 230 // 231 // we overwrote only the pnppower callbacks. Put the original back. 232 // 233 deviceInit = *DeviceInit; 234 RtlCopyMemory(&deviceInit->PnpPower.PnpPowerEventCallbacks, 235 &pnpPowerEvtsOriginal, 236 sizeof(WDF_PNPPOWER_EVENT_CALLBACKS) 237 ); 238 } 239 240 if (contextHeader != NULL) { 241 FxPoolFree(contextHeader); 242 } 243 } 244 245 return status; 246 } 247 248 _Must_inspect_result_ 249 NTSTATUS 250 AddEventHooksWdfIoQueueCreate( 251 __inout PVF_HOOK_PROCESS_INFO HookProcessInfo, 252 __in PWDF_DRIVER_GLOBALS DriverGlobals, 253 __in WDFDEVICE Device, 254 __in PWDF_IO_QUEUE_CONFIG Config, 255 __in PWDF_OBJECT_ATTRIBUTES QueueAttributes, 256 __out WDFQUEUE* Queue 257 ) 258 /*++ 259 260 Routine Description: 261 262 Arguments: 263 264 Return value: 265 266 --*/ 267 { 268 NTSTATUS status; 269 WDF_IO_QUEUE_CONFIG configNew; 270 PFX_DRIVER_GLOBALS pFxDriverGlobals; 271 WDFQUEUE *pQueue; 272 WDFQUEUE queue; 273 PVOID contextHeader = NULL; 274 WDF_OBJECT_ATTRIBUTES attributes; 275 276 PAGED_CODE_LOCKED(); 277 278 pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals); 279 280 FxPointerNotNull(pFxDriverGlobals, Config); 281 282 // 283 // Check if there are any callbacks set by the client driver. If not, we 284 // don't need any callback hooking. 285 // 286 if (Config->Size != sizeof(WDF_IO_QUEUE_CONFIG)) { 287 // 288 // no hooking required. 289 // 290 status = STATUS_SUCCESS; 291 HookProcessInfo->DonotCallKmdfLib = FALSE; 292 return status; 293 } 294 295 // 296 // Hooking can be done only if we are able to allocate context memory. 297 // Try to allocate a context 298 // 299 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 300 VF_WDFIOQUEUECREATE_CONTEXT); 301 302 status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader); 303 304 if (!NT_SUCCESS(status)) { 305 // 306 // couldn't allocate context. hooking not possible 307 // 308 HookProcessInfo->DonotCallKmdfLib = FALSE; 309 return status; 310 } 311 312 // 313 // create a local copy of config 314 // 315 RtlCopyMemory(&configNew, 316 Config, 317 sizeof(configNew) 318 ); 319 320 // 321 // Override local copy with event callback hooks. 322 // 323 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDefault); 324 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoRead); 325 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoWrite); 326 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDeviceControl); 327 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoInternalDeviceControl); 328 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoStop); 329 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoResume); 330 SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoCanceledOnQueue); 331 332 // 333 // Queue handle is an optional parameter 334 // 335 if (Queue == NULL) { 336 pQueue = &queue; 337 } 338 else { 339 pQueue = Queue; 340 } 341 342 // 343 // call the DDI 344 // 345 status = WdfVersion.Functions.pfnWdfIoQueueCreate( 346 DriverGlobals, 347 Device, 348 &configNew, 349 QueueAttributes, 350 pQueue 351 ); 352 353 // 354 // Tell main hook routine not to call the DDI in kmdf lib since we 355 // already called it 356 // 357 HookProcessInfo->DonotCallKmdfLib = TRUE; 358 HookProcessInfo->DdiCallStatus = status; 359 360 // 361 // if DDI Succeeds, add context 362 // 363 if (NT_SUCCESS(status)) { 364 PVF_WDFIOQUEUECREATE_CONTEXT context = NULL; 365 366 // 367 // add the already allocated context to the object. 368 // 369 status = VfAddContextToHandle(contextHeader, 370 &attributes, 371 *pQueue, 372 (PVOID *)&context); 373 374 if (NT_SUCCESS(status)) { 375 // 376 // store the DriverGlobals pointer used to associate the driver 377 // with its client driver callback later in the callback hooks 378 // 379 context->CommonHeader.DriverGlobals = DriverGlobals; 380 381 // 382 // add stored original callbacks to context 383 // 384 RtlCopyMemory( 385 &context->IoQueueConfigOriginal, 386 Config, 387 sizeof(WDF_IO_QUEUE_CONFIG) 388 ); 389 } 390 else { 391 // 392 // For some reason adding context to handle failed. This should be 393 // rare failure, because context allocation was already successful, 394 // only adding to handle failed. 395 // 396 // Hooking failed if adding context is unsuccessful. This means 397 // kmdf has callbacks hooks but verifier cannot assiociate client 398 // driver callbacks with callback hooks. This would be a fatal error. 399 // 400 ASSERTMSG("KMDF Enhanced Verifier failed to add context to object " 401 "handle\n", FALSE); 402 403 if (contextHeader != NULL) { 404 FxPoolFree(contextHeader); 405 } 406 } 407 } 408 else { 409 // 410 // DDI call to KMDF failed. Nothing to do by verifier. Just return 411 // kmdf's status after freeing context header memory. 412 // 413 if (contextHeader != NULL) { 414 FxPoolFree(contextHeader); 415 } 416 } 417 418 return status; 419 } 420 421 _Must_inspect_result_ 422 PVOID 423 FASTCALL 424 VfWdfObjectGetTypedContext( 425 __in 426 WDFOBJECT Handle, 427 __in 428 PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo 429 ) 430 /*++ 431 432 Routine Description: 433 Retrieves the requested type from a handle 434 435 Arguments: 436 Handle - the handle to retrieve the context from 437 TypeInfo - global constant pointer which describes the type. Since the pointer 438 value is unique in all of kernel space, we will perform a pointer compare 439 instead of a deep structure compare 440 441 Return Value: 442 A valid context pointere or NULL. NULL is not a failure, querying for a type 443 not associated with the handle is a legitimate operation. 444 445 --*/ 446 { 447 FxContextHeader* pHeader; 448 FxObject* pObject; 449 PFX_DRIVER_GLOBALS pFxDriverGlobals; 450 WDFOBJECT_OFFSET offset; 451 452 PAGED_CODE_LOCKED(); 453 454 // 455 // Do not call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because this is a 456 // hot spot / workhorse function that should be as efficient as possible. 457 // 458 // A call to FxObjectHandleGetPtr would : 459 // 1) invoke a virtual call to QueryInterface 460 // 461 // 2) ASSERT that the ref count of the object is > zero. Since this is one 462 // of the few functions that can be called in EvtObjectDestroy where the 463 // ref count is zero, that is not a good side affect. 464 // 465 offset = 0; 466 pObject = FxObject::_GetObjectFromHandle(Handle, &offset); 467 468 // 469 // Use the object's globals, not the caller's 470 // 471 pFxDriverGlobals = pObject->GetDriverGlobals(); 472 473 FxPointerNotNull(pFxDriverGlobals, Handle); 474 FxPointerNotNull(pFxDriverGlobals, TypeInfo); 475 476 pHeader = pObject->GetContextHeader(); 477 478 for ( ; pHeader != NULL; pHeader = pHeader->NextHeader) { 479 if (pHeader->ContextTypeInfo == TypeInfo) { 480 return &pHeader->Context[0]; 481 } 482 } 483 484 PCHAR pGivenName; 485 486 if (TypeInfo->ContextName != NULL) { 487 pGivenName = TypeInfo->ContextName; 488 } 489 else { 490 pGivenName = "<no typename given>"; 491 } 492 493 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE, 494 "Attempting to get context type %s from WDFOBJECT 0x%p", 495 pGivenName, Handle); 496 497 return NULL; 498 } 499 500 _Must_inspect_result_ 501 NTSTATUS 502 VfAllocateContext( 503 __in PWDF_DRIVER_GLOBALS DriverGlobals, 504 __in PWDF_OBJECT_ATTRIBUTES Attributes, 505 __out PVOID* ContextHeader 506 ) 507 /*++ 508 509 Routine Description: 510 Allocates an additional context on the handle if the object is in the 511 correct state 512 513 Arguments: 514 Handle - handle on which to add a context 515 Attributes - attributes which describe the type and size of the new context 516 Context - optional pointer which will recieve the new context 517 518 Return Value: 519 STATUS_SUCCESS upon success, STATUS_OBJECT_NAME_EXISTS if the context type 520 is already attached to the handle, and !NT_SUCCESS on failure 521 522 --*/ 523 { 524 PFX_DRIVER_GLOBALS pFxDriverGlobals; 525 NTSTATUS status; 526 FxContextHeader *pHeader; 527 size_t size; 528 529 PAGED_CODE_LOCKED(); 530 531 pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals); 532 533 status = FxValidateObjectAttributes( 534 pFxDriverGlobals, Attributes, FX_VALIDATE_OPTION_ATTRIBUTES_REQUIRED); 535 if (!NT_SUCCESS(status)) { 536 return status; 537 } 538 539 // 540 // Must have a context type! 541 // 542 if (Attributes->ContextTypeInfo == NULL) { 543 status = STATUS_OBJECT_NAME_INVALID; 544 DoTraceLevelMessage( 545 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE, 546 "Attributes %p ContextTypeInfo is NULL, %!STATUS!", 547 Attributes, status); 548 return status; 549 } 550 551 // 552 // By passing 0's for raw object size and extra size, we can compute the 553 // size of only the header and its contents. 554 // 555 status = FxCalculateObjectTotalSize(pFxDriverGlobals, 0, 0, Attributes, &size); 556 if (!NT_SUCCESS(status)) { 557 return status; 558 } 559 560 pHeader = (FxContextHeader*) 561 FxPoolAllocate(pFxDriverGlobals, NonPagedPool, size); 562 563 if (pHeader != NULL) { 564 *ContextHeader = pHeader; 565 status = STATUS_SUCCESS; 566 } 567 else { 568 status = STATUS_INSUFFICIENT_RESOURCES; 569 } 570 571 return status; 572 } 573 574 _Must_inspect_result_ 575 NTSTATUS 576 VfAddContextToHandle( 577 __in PVOID ContextHeader, 578 __in PWDF_OBJECT_ATTRIBUTES Attributes, 579 __in WDFOBJECT Handle, 580 __out_opt PVOID* Context 581 ) 582 { 583 PFX_DRIVER_GLOBALS pFxDriverGlobals; 584 NTSTATUS status = STATUS_SUCCESS; 585 FxObject* pObject; 586 FxContextHeader *pHeader; 587 WDFOBJECT_OFFSET offset; 588 589 PAGED_CODE_LOCKED(); 590 591 pHeader = (FxContextHeader *)ContextHeader; 592 593 if (pHeader == NULL) { 594 return STATUS_INVALID_PARAMETER; 595 } 596 597 // 598 // No need to call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because 599 // we assume that the object handle will point back to an FxObject. (The 600 // call to FxObjectHandleGetPtr will just make needless virtual call into 601 // FxObject anyways). 602 // 603 offset = 0; 604 pObject = FxObject::_GetObjectFromHandle(Handle, &offset); 605 606 pFxDriverGlobals = pObject->GetDriverGlobals(); 607 608 if (offset != 0) { 609 // 610 // for lack of a better error code 611 // 612 status = STATUS_OBJECT_PATH_INVALID; 613 614 DoTraceLevelMessage( 615 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE, 616 "WDFHANDLE %p cannot have contexts added to it, %!STATUS!", 617 Handle, status); 618 619 goto cleanup; 620 } 621 622 pObject->ADDREF(&status); 623 624 FxContextHeaderInit(pHeader, pObject, Attributes); 625 626 status = pObject->AddContext(pHeader, Context, Attributes); 627 628 // 629 // STATUS_OBJECT_NAME_EXISTS will not fail NT_SUCCESS, so check 630 // explicitly for STATUS_SUCCESS. 631 // 632 if (status != STATUS_SUCCESS) { 633 DoTraceLevelMessage( 634 pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE, 635 "WDFHANDLE %p failed to add context, %!STATUS!", 636 Handle, status); 637 } 638 639 pObject->RELEASE(&status); 640 641 cleanup: 642 643 if (status != STATUS_SUCCESS && pHeader != NULL) { 644 FxPoolFree(pHeader); 645 } 646 647 return status; 648 } 649 650 } // extern "C" 651