reactos/sdk/lib/drivers/wdf/shared/enhancedverif/verifier.cpp
Victor Perevertkin 8a978a179f
[WDF] Add Windows Driver Framework files
Takern from Microsoft GitHub repo:
d9c6040fe9

Licensed under MIT
2020-11-03 00:06:26 +03:00

651 lines
19 KiB
C++

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
Verifier.cpp
Abstract:
This file has implementation of verifier support routines
Author:
Environment:
Shared (Kernel and user)
Revision History:
--*/
#include "vfpriv.hpp"
extern "C"
{
extern WDFVERSION WdfVersion;
// Tracing support
#if defined(EVENT_TRACING)
#include "verifier.tmh"
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(FX_ENHANCED_VERIFIER_SECTION_NAME, \
AddEventHooksWdfDeviceCreate, \
AddEventHooksWdfIoQueueCreate, \
VfAddContextToHandle, \
VfAllocateContext, \
VfWdfObjectGetTypedContext \
)
#endif
_Must_inspect_result_
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
)
/*++
Routine Description:
This routine is called by main hook for WdfDeviceCreate.
The routine swaps the event callbacks provided by client.
Arguments:
Return value:
--*/
{
NTSTATUS status;
PWDFDEVICE_INIT deviceInit = *DeviceInit;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerEvtsOriginal;
WDF_PNPPOWER_EVENT_CALLBACKS *pnpPowerEvts;
PFX_DRIVER_GLOBALS pFxDriverGlobals;
PVOID contextHeader = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
PAGED_CODE_LOCKED();
pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
FxPointerNotNull(pFxDriverGlobals, DeviceInit);
FxPointerNotNull(pFxDriverGlobals, *DeviceInit);
FxPointerNotNull(pFxDriverGlobals, Device);
//
// Check if there are any callbacks set by the client driver. If not, we
// don't need any callback hooking.
//
if (deviceInit->PnpPower.PnpPowerEventCallbacks.Size !=
sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)) {
//
// no hooking required.
//
status = STATUS_SUCCESS;
HookProcessInfo->DonotCallKmdfLib = FALSE;
return status;
}
//
// Hooking can be done only if we are able to allocate context memory.
// Try to allocate a context
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
&attributes,
VF_WDFDEVICECREATE_CONTEXT
);
status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader);
if (!NT_SUCCESS(status)) {
//
// couldn't allocate context. hooking not possible
//
HookProcessInfo->DonotCallKmdfLib = FALSE;
return status;
}
//
// store original driver callbacks to local variable
//
RtlCopyMemory(&pnpPowerEvtsOriginal,
&deviceInit->PnpPower.PnpPowerEventCallbacks,
sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
);
//
// Set callback hooks
//
// Normally override the hooks on local copy of stucture (not the original
// structure itself) and then pass the local struture to DDI call and
// copy back the original poniter when returning back to caller. In this case
// device_init is null'ed out by fx when returning to caller so we can
// directly override the original
//
pnpPowerEvts = &deviceInit->PnpPower.PnpPowerEventCallbacks;
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Entry);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0EntryPostInterruptsEnabled);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0Exit);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceD0ExitPreInterruptsDisabled);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDevicePrepareHardware);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceReleaseHardware);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoCleanup);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoFlush);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoInit);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoSuspend);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSelfManagedIoRestart);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceSurpriseRemoval);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryRemove);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceQueryStop);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotification);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceUsageNotificationEx);
SET_HOOK_IF_CALLBACK_PRESENT(pnpPowerEvts, pnpPowerEvts, EvtDeviceRelationsQuery);
//
// Call the DDI on behalf of driver.
//
status = ((PFN_WDFDEVICECREATE)WdfVersion.Functions.pfnWdfDeviceCreate)(
DriverGlobals,
DeviceInit,
DeviceAttributes,
Device
);
//
// Tell main hook routine not to call the DDI in kmdf lib since we
// already called it
//
HookProcessInfo->DonotCallKmdfLib = TRUE;
HookProcessInfo->DdiCallStatus = status;
//
// if DDI Succeeds, add context
//
if (NT_SUCCESS(status)) {
PVF_WDFDEVICECREATE_CONTEXT context = NULL;
//
// add the already allocated context to the object.
//
status = VfAddContextToHandle(contextHeader,
&attributes,
*Device,
(PVOID *)&context);
if (NT_SUCCESS(status)) {
//
// store the DriverGlobals pointer used to associate the driver
// with its client driver callback later in the callback hooks
//
context->CommonHeader.DriverGlobals = DriverGlobals;
//
// store original client driver callbacks in context
//
RtlCopyMemory(
&context->PnpPowerEventCallbacksOriginal,
&pnpPowerEvtsOriginal,
sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
);
}
else {
//
// For some reason adding context to handle failed. This should be
// rare failure, because context allocation was already successful,
// only adding to handle failed.
//
// Hooking failed if adding context is unsuccessful. This means
// kmdf has callbacks hooks but verifier cannot assiociate client
// driver callbacks with callback hooks. This would be a fatal error.
//
ASSERTMSG("KMDF Enhanced Verifier failed to add context to object "
"handle\n", FALSE);
if (contextHeader != NULL) {
FxPoolFree(contextHeader);
}
}
}
else {
//
// KMDF DDI call failed. In case of failure, DeviceInit is not NULL'ed
// so the driver could potentially call WdfDeviceCreate again with this
// DeviceInit that contains callback hooks, and result in infinite
// callback loop. So put original callbacks back
//
if ((*DeviceInit) != NULL) {
//
// we overwrote only the pnppower callbacks. Put the original back.
//
deviceInit = *DeviceInit;
RtlCopyMemory(&deviceInit->PnpPower.PnpPowerEventCallbacks,
&pnpPowerEvtsOriginal,
sizeof(WDF_PNPPOWER_EVENT_CALLBACKS)
);
}
if (contextHeader != NULL) {
FxPoolFree(contextHeader);
}
}
return status;
}
_Must_inspect_result_
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
)
/*++
Routine Description:
Arguments:
Return value:
--*/
{
NTSTATUS status;
WDF_IO_QUEUE_CONFIG configNew;
PFX_DRIVER_GLOBALS pFxDriverGlobals;
WDFQUEUE *pQueue;
WDFQUEUE queue;
PVOID contextHeader = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
PAGED_CODE_LOCKED();
pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
FxPointerNotNull(pFxDriverGlobals, Config);
//
// Check if there are any callbacks set by the client driver. If not, we
// don't need any callback hooking.
//
if (Config->Size != sizeof(WDF_IO_QUEUE_CONFIG)) {
//
// no hooking required.
//
status = STATUS_SUCCESS;
HookProcessInfo->DonotCallKmdfLib = FALSE;
return status;
}
//
// Hooking can be done only if we are able to allocate context memory.
// Try to allocate a context
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
VF_WDFIOQUEUECREATE_CONTEXT);
status = VfAllocateContext(DriverGlobals, &attributes, &contextHeader);
if (!NT_SUCCESS(status)) {
//
// couldn't allocate context. hooking not possible
//
HookProcessInfo->DonotCallKmdfLib = FALSE;
return status;
}
//
// create a local copy of config
//
RtlCopyMemory(&configNew,
Config,
sizeof(configNew)
);
//
// Override local copy with event callback hooks.
//
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDefault);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoRead);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoWrite);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoDeviceControl);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoInternalDeviceControl);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoStop);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoResume);
SET_HOOK_IF_CALLBACK_PRESENT(Config, &configNew, EvtIoCanceledOnQueue);
//
// Queue handle is an optional parameter
//
if (Queue == NULL) {
pQueue = &queue;
}
else {
pQueue = Queue;
}
//
// call the DDI
//
status = WdfVersion.Functions.pfnWdfIoQueueCreate(
DriverGlobals,
Device,
&configNew,
QueueAttributes,
pQueue
);
//
// Tell main hook routine not to call the DDI in kmdf lib since we
// already called it
//
HookProcessInfo->DonotCallKmdfLib = TRUE;
HookProcessInfo->DdiCallStatus = status;
//
// if DDI Succeeds, add context
//
if (NT_SUCCESS(status)) {
PVF_WDFIOQUEUECREATE_CONTEXT context = NULL;
//
// add the already allocated context to the object.
//
status = VfAddContextToHandle(contextHeader,
&attributes,
*pQueue,
(PVOID *)&context);
if (NT_SUCCESS(status)) {
//
// store the DriverGlobals pointer used to associate the driver
// with its client driver callback later in the callback hooks
//
context->CommonHeader.DriverGlobals = DriverGlobals;
//
// add stored original callbacks to context
//
RtlCopyMemory(
&context->IoQueueConfigOriginal,
Config,
sizeof(WDF_IO_QUEUE_CONFIG)
);
}
else {
//
// For some reason adding context to handle failed. This should be
// rare failure, because context allocation was already successful,
// only adding to handle failed.
//
// Hooking failed if adding context is unsuccessful. This means
// kmdf has callbacks hooks but verifier cannot assiociate client
// driver callbacks with callback hooks. This would be a fatal error.
//
ASSERTMSG("KMDF Enhanced Verifier failed to add context to object "
"handle\n", FALSE);
if (contextHeader != NULL) {
FxPoolFree(contextHeader);
}
}
}
else {
//
// DDI call to KMDF failed. Nothing to do by verifier. Just return
// kmdf's status after freeing context header memory.
//
if (contextHeader != NULL) {
FxPoolFree(contextHeader);
}
}
return status;
}
_Must_inspect_result_
PVOID
FASTCALL
VfWdfObjectGetTypedContext(
__in
WDFOBJECT Handle,
__in
PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo
)
/*++
Routine Description:
Retrieves the requested type from a handle
Arguments:
Handle - the handle to retrieve the context from
TypeInfo - global constant pointer which describes the type. Since the pointer
value is unique in all of kernel space, we will perform a pointer compare
instead of a deep structure compare
Return Value:
A valid context pointere or NULL. NULL is not a failure, querying for a type
not associated with the handle is a legitimate operation.
--*/
{
FxContextHeader* pHeader;
FxObject* pObject;
PFX_DRIVER_GLOBALS pFxDriverGlobals;
WDFOBJECT_OFFSET offset;
PAGED_CODE_LOCKED();
//
// Do not call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because this is a
// hot spot / workhorse function that should be as efficient as possible.
//
// A call to FxObjectHandleGetPtr would :
// 1) invoke a virtual call to QueryInterface
//
// 2) ASSERT that the ref count of the object is > zero. Since this is one
// of the few functions that can be called in EvtObjectDestroy where the
// ref count is zero, that is not a good side affect.
//
offset = 0;
pObject = FxObject::_GetObjectFromHandle(Handle, &offset);
//
// Use the object's globals, not the caller's
//
pFxDriverGlobals = pObject->GetDriverGlobals();
FxPointerNotNull(pFxDriverGlobals, Handle);
FxPointerNotNull(pFxDriverGlobals, TypeInfo);
pHeader = pObject->GetContextHeader();
for ( ; pHeader != NULL; pHeader = pHeader->NextHeader) {
if (pHeader->ContextTypeInfo == TypeInfo) {
return &pHeader->Context[0];
}
}
PCHAR pGivenName;
if (TypeInfo->ContextName != NULL) {
pGivenName = TypeInfo->ContextName;
}
else {
pGivenName = "<no typename given>";
}
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
"Attempting to get context type %s from WDFOBJECT 0x%p",
pGivenName, Handle);
return NULL;
}
_Must_inspect_result_
NTSTATUS
VfAllocateContext(
__in PWDF_DRIVER_GLOBALS DriverGlobals,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__out PVOID* ContextHeader
)
/*++
Routine Description:
Allocates an additional context on the handle if the object is in the
correct state
Arguments:
Handle - handle on which to add a context
Attributes - attributes which describe the type and size of the new context
Context - optional pointer which will recieve the new context
Return Value:
STATUS_SUCCESS upon success, STATUS_OBJECT_NAME_EXISTS if the context type
is already attached to the handle, and !NT_SUCCESS on failure
--*/
{
PFX_DRIVER_GLOBALS pFxDriverGlobals;
NTSTATUS status;
FxContextHeader *pHeader;
size_t size;
PAGED_CODE_LOCKED();
pFxDriverGlobals = GetFxDriverGlobals(DriverGlobals);
status = FxValidateObjectAttributes(
pFxDriverGlobals, Attributes, FX_VALIDATE_OPTION_ATTRIBUTES_REQUIRED);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Must have a context type!
//
if (Attributes->ContextTypeInfo == NULL) {
status = STATUS_OBJECT_NAME_INVALID;
DoTraceLevelMessage(
pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
"Attributes %p ContextTypeInfo is NULL, %!STATUS!",
Attributes, status);
return status;
}
//
// By passing 0's for raw object size and extra size, we can compute the
// size of only the header and its contents.
//
status = FxCalculateObjectTotalSize(pFxDriverGlobals, 0, 0, Attributes, &size);
if (!NT_SUCCESS(status)) {
return status;
}
pHeader = (FxContextHeader*)
FxPoolAllocate(pFxDriverGlobals, NonPagedPool, size);
if (pHeader != NULL) {
*ContextHeader = pHeader;
status = STATUS_SUCCESS;
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
_Must_inspect_result_
NTSTATUS
VfAddContextToHandle(
__in PVOID ContextHeader,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__in WDFOBJECT Handle,
__out_opt PVOID* Context
)
{
PFX_DRIVER_GLOBALS pFxDriverGlobals;
NTSTATUS status = STATUS_SUCCESS;
FxObject* pObject;
FxContextHeader *pHeader;
WDFOBJECT_OFFSET offset;
PAGED_CODE_LOCKED();
pHeader = (FxContextHeader *)ContextHeader;
if (pHeader == NULL) {
return STATUS_INVALID_PARAMETER;
}
//
// No need to call FxObjectHandleGetPtr( , , FX_TYPE_OBJECT) because
// we assume that the object handle will point back to an FxObject. (The
// call to FxObjectHandleGetPtr will just make needless virtual call into
// FxObject anyways).
//
offset = 0;
pObject = FxObject::_GetObjectFromHandle(Handle, &offset);
pFxDriverGlobals = pObject->GetDriverGlobals();
if (offset != 0) {
//
// for lack of a better error code
//
status = STATUS_OBJECT_PATH_INVALID;
DoTraceLevelMessage(
pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
"WDFHANDLE %p cannot have contexts added to it, %!STATUS!",
Handle, status);
goto cleanup;
}
pObject->ADDREF(&status);
FxContextHeaderInit(pHeader, pObject, Attributes);
status = pObject->AddContext(pHeader, Context, Attributes);
//
// STATUS_OBJECT_NAME_EXISTS will not fail NT_SUCCESS, so check
// explicitly for STATUS_SUCCESS.
//
if (status != STATUS_SUCCESS) {
DoTraceLevelMessage(
pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGHANDLE,
"WDFHANDLE %p failed to add context, %!STATUS!",
Handle, status);
}
pObject->RELEASE(&status);
cleanup:
if (status != STATUS_SUCCESS && pHeader != NULL) {
FxPoolFree(pHeader);
}
return status;
}
} // extern "C"