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