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
AddEventHooksWdfDeviceCreate(__inout PVF_HOOK_PROCESS_INFO HookProcessInfo,__in PWDF_DRIVER_GLOBALS DriverGlobals,__in PWDFDEVICE_INIT * DeviceInit,__in PWDF_OBJECT_ATTRIBUTES DeviceAttributes,__out WDFDEVICE * Device)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
AddEventHooksWdfIoQueueCreate(__inout PVF_HOOK_PROCESS_INFO HookProcessInfo,__in PWDF_DRIVER_GLOBALS DriverGlobals,__in WDFDEVICE Device,__in PWDF_IO_QUEUE_CONFIG Config,__in PWDF_OBJECT_ATTRIBUTES QueueAttributes,__out WDFQUEUE * Queue)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
VfWdfObjectGetTypedContext(__in WDFOBJECT Handle,__in PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo)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
VfAllocateContext(__in PWDF_DRIVER_GLOBALS DriverGlobals,__in PWDF_OBJECT_ATTRIBUTES Attributes,__out PVOID * ContextHeader)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
VfAddContextToHandle(__in PVOID ContextHeader,__in PWDF_OBJECT_ATTRIBUTES Attributes,__in WDFOBJECT Handle,__out_opt PVOID * Context)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