Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7512 lines
205 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
verifier.c
Abstract:
This module contains the routines to verify the system kernel, HAL and
drivers.
Author:
Landy Wang (landyw) 3-Sep-1998
Revision History:
--*/
#include "mi.h"
#define THUNKED_API
THUNKED_API
PVOID
VerifierAllocatePool (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes
);
THUNKED_API
PVOID
VerifierAllocatePoolWithTag (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
);
THUNKED_API
PVOID
VerifierAllocatePoolWithQuotaTag (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
);
THUNKED_API
PVOID
VerifierAllocatePoolWithTagPriority (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN EX_POOL_PRIORITY Priority
);
PVOID
VeAllocatePoolWithTagPriority (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN EX_POOL_PRIORITY Priority,
IN PVOID CallingAddress
);
VOID
VerifierFreePool (
IN PVOID P
);
THUNKED_API
VOID
VerifierFreePoolWithTag (
IN PVOID P,
IN ULONG TagToFree
);
THUNKED_API
LONG
VerifierSetEvent (
IN PRKEVENT Event,
IN KPRIORITY Increment,
IN BOOLEAN Wait
);
THUNKED_API
KIRQL
FASTCALL
VerifierKfRaiseIrql (
IN KIRQL NewIrql
);
THUNKED_API
KIRQL
VerifierKeRaiseIrqlToDpcLevel (
VOID
);
THUNKED_API
VOID
FASTCALL
VerifierKfLowerIrql (
IN KIRQL NewIrql
);
THUNKED_API
VOID
VerifierKeRaiseIrql (
IN KIRQL NewIrql,
OUT PKIRQL OldIrql
);
THUNKED_API
VOID
VerifierKeLowerIrql (
IN KIRQL NewIrql
);
THUNKED_API
VOID
VerifierKeAcquireSpinLock (
IN PKSPIN_LOCK SpinLock,
OUT PKIRQL OldIrql
);
THUNKED_API
VOID
VerifierKeReleaseSpinLock (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);
THUNKED_API
VOID
#if defined(_X86_)
FASTCALL
#endif
VerifierKeAcquireSpinLockAtDpcLevel (
IN PKSPIN_LOCK SpinLock
);
THUNKED_API
VOID
#if defined(_X86_)
FASTCALL
#endif
VerifierKeReleaseSpinLockFromDpcLevel (
IN PKSPIN_LOCK SpinLock
);
THUNKED_API
KIRQL
FASTCALL
VerifierKfAcquireSpinLock (
IN PKSPIN_LOCK SpinLock
);
THUNKED_API
VOID
FASTCALL
VerifierKfReleaseSpinLock (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);
#if !defined(_X86_)
THUNKED_API
KIRQL
VerifierKeAcquireSpinLockRaiseToDpc (
IN PKSPIN_LOCK SpinLock
);
#endif
THUNKED_API
VOID
VerifierKeInitializeTimerEx (
IN PKTIMER Timer,
IN TIMER_TYPE Type
);
THUNKED_API
VOID
VerifierKeInitializeTimer (
IN PKTIMER Timer
);
THUNKED_API
BOOLEAN
FASTCALL
VerifierExTryToAcquireFastMutex (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
VOID
FASTCALL
VerifierExAcquireFastMutex (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
VOID
FASTCALL
VerifierExReleaseFastMutex (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
VOID
FASTCALL
VerifierExAcquireFastMutexUnsafe (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
VOID
FASTCALL
VerifierExReleaseFastMutexUnsafe (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
BOOLEAN
VerifierExAcquireResourceExclusiveLite (
IN PERESOURCE Resource,
IN BOOLEAN Wait
);
THUNKED_API
VOID
FASTCALL
VerifierExReleaseResourceLite (
IN PERESOURCE Resource
);
THUNKED_API
KIRQL
FASTCALL
VerifierKeAcquireQueuedSpinLock (
IN KSPIN_LOCK_QUEUE_NUMBER Number
);
THUNKED_API
VOID
FASTCALL
VerifierKeReleaseQueuedSpinLock (
IN KSPIN_LOCK_QUEUE_NUMBER Number,
IN KIRQL OldIrql
);
THUNKED_API
BOOLEAN
VerifierSynchronizeExecution (
IN PKINTERRUPT Interrupt,
IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
IN PVOID SynchronizeContext
);
THUNKED_API
VOID
VerifierProbeAndLockPages (
IN OUT PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
);
THUNKED_API
VOID
VerifierProbeAndLockProcessPages (
IN OUT PMDL MemoryDescriptorList,
IN PEPROCESS Process,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
);
THUNKED_API
VOID
VerifierProbeAndLockSelectedPages (
IN OUT PMDL MemoryDescriptorList,
IN PFILE_SEGMENT_ELEMENT SegmentArray,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
);
VOID
VerifierUnlockPages (
IN OUT PMDL MemoryDescriptorList
);
VOID
VerifierUnmapLockedPages (
IN PVOID BaseAddress,
IN PMDL MemoryDescriptorList
);
VOID
VerifierUnmapIoSpace (
IN PVOID BaseAddress,
IN SIZE_T NumberOfBytes
);
THUNKED_API
PVOID
VerifierMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN SIZE_T NumberOfBytes,
IN MEMORY_CACHING_TYPE CacheType
);
THUNKED_API
PVOID
VerifierMapLockedPages (
IN PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode
);
THUNKED_API
PVOID
VerifierMapLockedPagesSpecifyCache (
IN PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode,
IN MEMORY_CACHING_TYPE CacheType,
IN PVOID RequestedAddress,
IN ULONG BugCheckOnFailure,
IN MM_PAGE_PRIORITY Priority
);
THUNKED_API
NTSTATUS
VerifierKeWaitForSingleObject (
IN PVOID Object,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL
);
THUNKED_API
LONG
VerifierKeReleaseMutex (
IN PRKMUTEX Mutex,
IN BOOLEAN Wait
);
THUNKED_API
VOID
VerifierKeInitializeMutex (
IN PRKMUTEX Mutex,
IN ULONG Level
);
THUNKED_API
LONG
VerifierKeReleaseMutant(
IN PRKMUTANT Mutant,
IN KPRIORITY Increment,
IN BOOLEAN Abandoned,
IN BOOLEAN Wait
);
THUNKED_API
VOID
VerifierKeInitializeMutant(
IN PRKMUTANT Mutant,
IN BOOLEAN InitialOwner
);
THUNKED_API
VOID
VerifierKeInitializeSpinLock (
IN PKSPIN_LOCK SpinLock
);
VOID
ViCheckMdlPages (
IN PMDL MemoryDescriptorList,
IN MEMORY_CACHING_TYPE CacheType
);
VOID
ViFreeTrackedPool (
IN PVOID VirtualAddress,
IN SIZE_T ChargedBytes,
IN LOGICAL CheckType,
IN LOGICAL SpecialPool
);
VOID
VerifierFreeTrackedPool (
IN PVOID VirtualAddress,
IN SIZE_T ChargedBytes,
IN LOGICAL CheckType,
IN LOGICAL SpecialPool
);
VOID
ViPrintString (
IN PUNICODE_STRING DriverName
);
LOGICAL
ViInjectResourceFailure (
VOID
);
VOID
ViTrimAllSystemPagableMemory (
ULONG TrimType
);
VOID
ViInitializeEntry (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier,
IN LOGICAL FirstLoad
);
PVI_POOL_ENTRY
ViGrowPoolAllocation (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier
);
VOID
KfSanityCheckRaiseIrql (
IN KIRQL NewIrql
);
VOID
KfSanityCheckLowerIrql (
IN KIRQL NewIrql
);
NTSTATUS
VerifierReferenceObjectByHandle (
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
);
LONG_PTR
FASTCALL
VerifierReferenceObject (
IN PVOID Object
);
LONG_PTR
VerifierDereferenceObject (
IN PVOID Object
);
VOID
VerifierLeaveCriticalRegion (
VOID
);
PEPROCESS
ExGetBilledProcess (
IN PPOOL_HEADER Entry
);
MM_DRIVER_VERIFIER_DATA MmVerifierData;
//
// Any flags which can be modified on the fly without rebooting are set here.
//
ULONG VerifierModifyableOptions;
ULONG VerifierOptionChanges;
LIST_ENTRY MiSuspectDriverList;
//
// Patch this to 1 on the first call to MmInitSystem to verify all drivers
// regardless of registry settings.
//
// Patch this to 2 on the first call to MmInitSystem to verify the kernel
// regardless of registry settings.
//
ULONG MiVerifyAllDrivers;
WCHAR MiVerifyRandomDrivers;
ULONG MiActiveVerifies;
ULONG MiActiveVerifierThunks;
ULONG MiNoPageOnRaiseIrql;
ULONG MiVerifierStackProtectTime;
LOGICAL VerifierSystemSufficientlyBooted;
LARGE_INTEGER VerifierRequiredTimeSinceBoot = {(ULONG)(40 * 1000 * 1000 * 10), 1};
LOGICAL VerifierIsTrackingPool = FALSE;
#if defined(_IA64_)
KSPIN_LOCK VerifierListLock;
#define LOCK_VERIFIER(OLDIRQL) \
ExAcquireSpinLock (&VerifierListLock, OLDIRQL);
#define UNLOCK_VERIFIER(OLDIRQL) \
ExReleaseSpinLock (&VerifierListLock, OLDIRQL);
#else
//
// This is only done to avoid calling IRQL raise (directly or via spinlock
// APIs) because the verifier itself doesn't need the checking and these
// checks would fire a substantial number of times, reducing *kernel*
// verifier effectiveness.
//
LONG VerifierListLock;
#define LOCK_VERIFIER(OLDIRQL) \
UNREFERENCED_PARAMETER (OldIrql); \
_disable(); \
do { \
} while (InterlockedCompareExchange (&VerifierListLock, 1, 0));
#define UNLOCK_VERIFIER(OLDIRQL) \
UNREFERENCED_PARAMETER (OldIrql); \
InterlockedAnd (&VerifierListLock, 0); \
_enable();
#endif
PRTL_BITMAP VerifierLargePagedPoolMap;
LIST_ENTRY MiVerifierDriverAddedThunkListHead;
extern LOGICAL MmSpecialPoolCatchOverruns;
LOGICAL KernelVerifier = FALSE;
ULONG KernelVerifierTickPage = 0x1;
ULONG MiVerifierThunksAdded;
extern USHORT ExMinimumLookasideDepth;
LOGICAL ViHideCacheConflicts = TRUE;
PDRIVER_VERIFIER_THUNK_ROUTINE
MiResolveVerifierExports (
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PCHAR PristineName
);
LOGICAL
MiEnableVerifier (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
);
LOGICAL
MiReEnableVerifier (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
);
VOID
ViInsertVerifierEntry (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier
);
PVOID
ViPostPoolAllocation (
IN PVI_POOL_ENTRY PoolEntry,
IN POOL_TYPE PoolType
);
PMI_VERIFIER_DRIVER_ENTRY
ViLocateVerifierEntry (
IN PVOID SystemAddress
);
VOID
MiVerifierCheckThunks (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
);
//
// Track irqls functions
//
VOID
ViTrackIrqlInitialize (
VOID
);
VOID
ViTrackIrqlLog (
IN KIRQL CurrentIrql,
IN KIRQL NewIrql
);
#if defined(_X86_) || defined(_AMD64_)
//
// Fault injection stack trace log.
//
VOID
ViFaultTracesInitialize (
VOID
);
VOID
ViFaultTracesLog (
VOID
);
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,MiInitializeDriverVerifierList)
#pragma alloc_text(INIT,MiInitializeVerifyingComponents)
#pragma alloc_text(INIT,ViTrackIrqlInitialize)
#pragma alloc_text(INIT,MiResolveVerifierExports)
#if defined(_X86_)
#pragma alloc_text(INIT,MiEnableKernelVerifier)
#pragma alloc_text(INIT,ViFaultTracesInitialize)
#endif
#pragma alloc_text(PAGE,MiApplyDriverVerifier)
#pragma alloc_text(PAGE,MiEnableVerifier)
#pragma alloc_text(INIT,MiReEnableVerifier)
#pragma alloc_text(PAGE,ViPrintString)
#pragma alloc_text(PAGE,MmGetVerifierInformation)
#pragma alloc_text(PAGE,MmSetVerifierInformation)
#pragma alloc_text(PAGE,MmAddVerifierThunks)
#pragma alloc_text(PAGE,MmIsVerifierEnabled)
#pragma alloc_text(PAGE,MiVerifierCheckThunks)
#pragma alloc_text(PAGEVRFY,MiVerifyingDriverUnloading)
#pragma alloc_text(PAGE,MmAddVerifierEntry)
#pragma alloc_text(PAGE,MmRemoveVerifierEntry)
#pragma alloc_text(INIT,MiReApplyVerifierToLoadedModules)
#pragma alloc_text(PAGEVRFY,VerifierProbeAndLockPages)
#pragma alloc_text(PAGEVRFY,VerifierProbeAndLockProcessPages)
#pragma alloc_text(PAGEVRFY,VerifierProbeAndLockSelectedPages)
#pragma alloc_text(PAGEVRFY,VerifierUnlockPages)
#pragma alloc_text(PAGEVRFY,VerifierMapIoSpace)
#pragma alloc_text(PAGEVRFY,VerifierMapLockedPages)
#pragma alloc_text(PAGEVRFY,VerifierMapLockedPagesSpecifyCache)
#pragma alloc_text(PAGEVRFY,VerifierUnmapLockedPages)
#pragma alloc_text(PAGEVRFY,VerifierUnmapIoSpace)
#pragma alloc_text(PAGEVRFY,VerifierAllocatePool)
#pragma alloc_text(PAGEVRFY,VerifierAllocatePoolWithTag)
#pragma alloc_text(PAGEVRFY,VerifierAllocatePoolWithTagPriority)
#pragma alloc_text(PAGEVRFY,VerifierAllocatePoolWithQuotaTag)
#pragma alloc_text(PAGEVRFY,VerifierFreePool)
#pragma alloc_text(PAGEVRFY,VerifierFreePoolWithTag)
#pragma alloc_text(PAGEVRFY,VerifierKeWaitForSingleObject)
#pragma alloc_text(PAGEVRFY,VerifierKfRaiseIrql)
#pragma alloc_text(PAGEVRFY,VerifierKeRaiseIrqlToDpcLevel)
#pragma alloc_text(PAGEVRFY,VerifierKfLowerIrql)
#pragma alloc_text(PAGEVRFY,VerifierKeRaiseIrql)
#pragma alloc_text(PAGEVRFY,VerifierKeLowerIrql)
#pragma alloc_text(PAGEVRFY,VerifierKeAcquireSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKeReleaseSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKeAcquireSpinLockAtDpcLevel)
#pragma alloc_text(PAGEVRFY,VerifierKeReleaseSpinLockFromDpcLevel)
#pragma alloc_text(PAGEVRFY,VerifierKfAcquireSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKfReleaseSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKeInitializeTimer)
#pragma alloc_text(PAGEVRFY,VerifierKeInitializeTimerEx)
#pragma alloc_text(PAGEVRFY,VerifierExTryToAcquireFastMutex)
#pragma alloc_text(PAGEVRFY,VerifierExAcquireFastMutex)
#pragma alloc_text(PAGEVRFY,VerifierExReleaseFastMutex)
#pragma alloc_text(PAGEVRFY,VerifierExAcquireFastMutexUnsafe)
#pragma alloc_text(PAGEVRFY,VerifierExReleaseFastMutexUnsafe)
#pragma alloc_text(PAGEVRFY,VerifierExAcquireResourceExclusiveLite)
#pragma alloc_text(PAGEVRFY,VerifierExReleaseResourceLite)
#pragma alloc_text(PAGEVRFY,VerifierKeAcquireQueuedSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKeReleaseQueuedSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierKeReleaseMutex)
#pragma alloc_text(PAGEVRFY,VerifierKeInitializeMutex)
#pragma alloc_text(PAGEVRFY,VerifierKeReleaseMutant)
#pragma alloc_text(PAGEVRFY,VerifierKeInitializeMutant)
#pragma alloc_text(PAGEVRFY,VerifierKeInitializeSpinLock)
#pragma alloc_text(PAGEVRFY,VerifierSynchronizeExecution)
#pragma alloc_text(PAGEVRFY,VerifierReferenceObjectByHandle)
#pragma alloc_text(PAGEVRFY,VerifierReferenceObject)
#pragma alloc_text(PAGEVRFY,VerifierDereferenceObject)
#pragma alloc_text(PAGEVRFY,VerifierLeaveCriticalRegion)
#pragma alloc_text(PAGEVRFY,VerifierSetEvent)
#pragma alloc_text(PAGEVRFY,ViFreeTrackedPool)
#pragma alloc_text(PAGEVRFY,VeAllocatePoolWithTagPriority)
#pragma alloc_text(PAGEVRFY,ViCheckMdlPages)
#pragma alloc_text(PAGEVRFY,ViInsertVerifierEntry)
#pragma alloc_text(PAGEVRFY,ViLocateVerifierEntry)
#pragma alloc_text(PAGEVRFY,ViPostPoolAllocation)
#pragma alloc_text(PAGEVRFY,ViInjectResourceFailure)
#pragma alloc_text(PAGEVRFY,ViTrimAllSystemPagableMemory)
#pragma alloc_text(PAGEVRFY,ViInitializeEntry)
#pragma alloc_text(PAGEVRFY,ViGrowPoolAllocation)
#pragma alloc_text(PAGEVRFY,KfSanityCheckRaiseIrql)
#pragma alloc_text(PAGEVRFY,KfSanityCheckLowerIrql)
#pragma alloc_text(PAGEVRFY,ViTrackIrqlLog)
#if defined(_X86_) || defined(_AMD64_)
#pragma alloc_text(PAGEVRFY,ViFaultTracesLog)
#endif
#if !defined(_X86_)
#pragma alloc_text(PAGEVRFY,VerifierKeAcquireSpinLockRaiseToDpc)
#endif
#endif
typedef struct _VERIFIER_THUNKS {
union {
PCHAR PristineRoutineAsciiName;
//
// The actual pristine routine address is derived from exports
//
PDRIVER_VERIFIER_THUNK_ROUTINE PristineRoutine;
};
PDRIVER_VERIFIER_THUNK_ROUTINE NewRoutine;
} VERIFIER_THUNKS, *PVERIFIER_THUNKS;
extern const VERIFIER_THUNKS MiVerifierThunks[];
extern const VERIFIER_THUNKS MiVerifierPoolThunks[];
#if defined (_X86_)
#define VI_KE_RAISE_IRQL 0
#define VI_KE_LOWER_IRQL 1
#define VI_KE_ACQUIRE_SPINLOCK 2
#define VI_KE_RELEASE_SPINLOCK 3
#define VI_KF_RAISE_IRQL 4
#define VI_KE_RAISE_IRQL_TO_DPC_LEVEL 5
#define VI_KF_LOWER_IRQL 6
#define VI_KF_ACQUIRE_SPINLOCK 7
#define VI_KF_RELEASE_SPINLOCK 8
#define VI_EX_ACQUIRE_FAST_MUTEX 9
#define VI_KE_ACQUIRE_QUEUED_SPINLOCK 10
#define VI_KE_RELEASE_QUEUED_SPINLOCK 11
#define VI_HALMAX 12
PVOID MiKernelVerifierOriginalCalls[VI_HALMAX];
#endif
//
// Track irql package declarations
//
#define VI_TRACK_IRQL_TRACE_LENGTH 5
typedef struct _VI_TRACK_IRQL {
PVOID Thread;
KIRQL OldIrql;
KIRQL NewIrql;
UCHAR Processor;
ULONG TickCount;
PVOID StackTrace [VI_TRACK_IRQL_TRACE_LENGTH];
} VI_TRACK_IRQL, *PVI_TRACK_IRQL;
PVI_TRACK_IRQL ViTrackIrqlQueue;
ULONG ViTrackIrqlIndex;
ULONG ViTrackIrqlQueueLength = 128;
VOID
ViTrackIrqlInitialize (
)
{
ULONG Length;
ULONG Round;
//
// Round up length to a power of two and prepare
// mask for the length.
//
Length = ViTrackIrqlQueueLength;
if (Length > 0x10000) {
Length = 0x10000;
}
for (Round = 0x10000; Round != 0; Round >>= 1) {
if (Length == Round) {
break;
}
else if ((Length & Round) == Round) {
Length = (Round << 1);
break;
}
}
ViTrackIrqlQueueLength = Length;
//
// Note POOL_DRIVER_MASK must be set to stop the recursion loop
// when using the kernel verifier.
//
ViTrackIrqlQueue = ExAllocatePoolWithTagPriority (
NonPagedPool | POOL_DRIVER_MASK,
ViTrackIrqlQueueLength * sizeof (VI_TRACK_IRQL),
'lqrI',
HighPoolPriority);
}
VOID
ViTrackIrqlLog (
IN KIRQL CurrentIrql,
IN KIRQL NewIrql
)
{
PVI_TRACK_IRQL Information;
LARGE_INTEGER TimeStamp;
ULONG Index;
ULONG Hash;
ASSERT (ViTrackIrqlQueue != NULL);
if (CurrentIrql > DISPATCH_LEVEL || NewIrql > DISPATCH_LEVEL) {
return;
}
#if defined(_AMD64_)
if ((GetCallersEflags () & EFLAGS_IF_MASK) == 0) {
return;
}
#endif
//
// Get a slot to write into.
//
Index = InterlockedIncrement((PLONG)&ViTrackIrqlIndex);
Index &= (ViTrackIrqlQueueLength - 1);
//
// Capture information.
//
Information = &(ViTrackIrqlQueue[Index]);
Information->Thread = KeGetCurrentThread();
Information->OldIrql = CurrentIrql;
Information->NewIrql = NewIrql;
Information->Processor = (UCHAR)(KeGetCurrentProcessorNumber());
KeQueryTickCount(&TimeStamp);
Information->TickCount = TimeStamp.LowPart;
RtlCaptureStackBackTrace (2,
VI_TRACK_IRQL_TRACE_LENGTH,
Information->StackTrace,
&Hash);
}
//
// Detect the caller of the current function in an architecture
// dependent way.
//
#define VI_DETECT_RETURN_ADDRESS(Caller) { \
PVOID CallersCaller; \
RtlGetCallersAddress(&Caller, &CallersCaller); \
}
//
// Fault injection stack trace log.
//
#define VI_FAULT_TRACE_LENGTH 8
typedef struct _VI_FAULT_TRACE {
PVOID StackTrace [VI_FAULT_TRACE_LENGTH];
} VI_FAULT_TRACE, *PVI_FAULT_TRACE;
PVI_FAULT_TRACE ViFaultTraces;
ULONG ViFaultTracesIndex;
ULONG ViFaultTracesLength = 128;
VOID
ViFaultTracesInitialize (
VOID
)
{
//
// Note POOL_DRIVER_MASK must be set to stop the recursion loop
// when using the kernel verifier.
//
ViFaultTraces = ExAllocatePoolWithTagPriority (
NonPagedPool | POOL_DRIVER_MASK,
ViFaultTracesLength * sizeof (VI_FAULT_TRACE),
'ttlF',
HighPoolPriority);
}
VOID
ViFaultTracesLog (
VOID
)
{
PVI_FAULT_TRACE Information;
ULONG Hash;
ULONG Index;
if (ViFaultTraces == NULL) {
return;
}
//
// Get slot to write into.
//
Index = InterlockedIncrement ((PLONG)&ViFaultTracesIndex);
Index &= (ViFaultTracesLength - 1);
//
// Capture information. Even if we lose performance it is
// worth zeroing the trace buffer to avoid confusing people
// if old traces get merged with new ones. This zeroing
// will happen only if we actually inject a failure.
//
Information = &(ViFaultTraces[Index]);
RtlZeroMemory (Information, sizeof (VI_FAULT_TRACE));
RtlCaptureStackBackTrace (2,
VI_FAULT_TRACE_LENGTH,
Information->StackTrace,
&Hash);
}
//
// Don't fail any requests in the first 7 or 8 minutes as we want to
// give the system enough time to boot.
//
#define MI_CHECK_UPTIME() \
if (VerifierSystemSufficientlyBooted == FALSE) { \
LARGE_INTEGER _CurrentTime; \
KeQuerySystemTime (&_CurrentTime); \
if (_CurrentTime.QuadPart > KeBootTime.QuadPart + VerifierRequiredTimeSinceBoot.QuadPart) { \
VerifierSystemSufficientlyBooted = TRUE; \
} \
}
THUNKED_API
VOID
VerifierProbeAndLockPages (
IN OUT PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x70,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
if (ViInjectResourceFailure () == TRUE) {
ExRaiseStatus (STATUS_WORKING_SET_QUOTA);
}
MmProbeAndLockPages (MemoryDescriptorList, AccessMode, Operation);
}
THUNKED_API
VOID
VerifierProbeAndLockProcessPages (
IN OUT PMDL MemoryDescriptorList,
IN PEPROCESS Process,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x71,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)Process);
}
if (ViInjectResourceFailure () == TRUE) {
ExRaiseStatus (STATUS_WORKING_SET_QUOTA);
}
MmProbeAndLockProcessPages (MemoryDescriptorList,
Process,
AccessMode,
Operation);
}
THUNKED_API
VOID
VerifierProbeAndLockSelectedPages (
IN OUT PMDL MemoryDescriptorList,
IN PFILE_SEGMENT_ELEMENT SegmentArray,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x72,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
if (ViInjectResourceFailure () == TRUE) {
ExRaiseStatus (STATUS_WORKING_SET_QUOTA);
}
MmProbeAndLockSelectedPages (MemoryDescriptorList,
SegmentArray,
AccessMode,
Operation);
}
THUNKED_API
PVOID
VerifierMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN SIZE_T NumberOfBytes,
IN MEMORY_CACHING_TYPE CacheType
)
{
KIRQL OldIrql;
LOGICAL IsPfn;
PMMPFN Pfn1;
PFN_NUMBER NumberOfPages;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER LastPageFrameIndex;
PMMIO_TRACKER Tracker2;
PLIST_ENTRY NextEntry;
MI_PFN_CACHE_ATTRIBUTE CacheAttribute;
MI_PFN_CACHE_ATTRIBUTE ExistingAttribute;
MEMORY_CACHING_TYPE ExistingCacheType;
OldIrql = KeGetCurrentIrql ();
if (OldIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x73,
OldIrql,
(ULONG_PTR)PhysicalAddress.LowPart,
NumberOfBytes);
}
//
// See if the first frame is in the PFN database and if so, they all must
// be.
//
PageFrameIndex = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (PhysicalAddress.LowPart,
NumberOfBytes);
CacheAttribute = MI_TRANSLATE_CACHETYPE (CacheType, TRUE);
IsPfn = MI_IS_PFN (PageFrameIndex);
if (IsPfn == TRUE) {
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
do {
//
// Each frame better be locked down already. Bugcheck if not.
//
if ((Pfn1->u3.e2.ReferenceCount != 0) ||
((Pfn1->u3.e1.Rom == 1) && ((CacheType & 0xFF) == MmCached))) {
NOTHING;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x83,
(ULONG_PTR)PhysicalAddress.LowPart,
NumberOfBytes,
(ULONG_PTR)MI_PFN_ELEMENT_TO_INDEX (Pfn1));
}
if (Pfn1->u3.e1.CacheAttribute == MiNotMapped) {
//
// This better be for a page allocated with
// MmAllocatePagesForMdl. Otherwise it might be a
// page on the freelist which could subsequently be
// given out with a different attribute !
//
if ((Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME) ||
#if defined (_MI_MORE_THAN_4GB_)
(Pfn1->u4.PteFrame == MI_MAGIC_4GB_RECLAIM) ||
#endif
(Pfn1->PteAddress == (PVOID) (ULONG_PTR)(X64K | 0x1))) {
NOTHING;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x84,
(ULONG_PTR)PhysicalAddress.LowPart,
NumberOfBytes,
(ULONG_PTR)MI_PFN_ELEMENT_TO_INDEX (Pfn1));
}
}
Pfn1 += 1;
NumberOfPages -= 1;
} while (NumberOfPages != 0);
}
else {
ExAcquireSpinLock (&MmIoTrackerLock, &OldIrql);
//
// Scan I/O space mappings for duplicate or overlapping entries.
//
NextEntry = MmIoHeader.Flink;
while (NextEntry != &MmIoHeader) {
Tracker2 = (PMMIO_TRACKER) CONTAINING_RECORD (NextEntry,
MMIO_TRACKER,
ListEntry.Flink);
if ((PageFrameIndex < Tracker2->PageFrameIndex + Tracker2->NumberOfPages) &&
(PageFrameIndex + NumberOfPages > Tracker2->PageFrameIndex)) {
ExistingAttribute = Tracker2->CacheAttribute;
if (CacheAttribute != ExistingAttribute) {
DbgPrint ("MM: Iospace mapping overlap %p\n",
Tracker2);
DbgPrint ("Physical range 0x%p->%p first mapped %s at VA %p\n",
Tracker2->PageFrameIndex << PAGE_SHIFT,
(Tracker2->PageFrameIndex + Tracker2->NumberOfPages) << PAGE_SHIFT,
MiCacheStrings[ExistingAttribute],
Tracker2->BaseVa);
DbgPrint ("\tby call stack: %p %p %p %p\n",
Tracker2->StackTrace[0],
Tracker2->StackTrace[1],
Tracker2->StackTrace[2],
Tracker2->StackTrace[3]);
DbgPrint ("Physical range 0x%p->%p now being mapped %s\n",
PageFrameIndex << PAGE_SHIFT,
(PageFrameIndex + NumberOfPages) << PAGE_SHIFT,
MiCacheStrings[CacheAttribute]);
//
// Convert the existing internal cache attribute to an
// external cache type driver writers are familiar with.
//
ExistingCacheType = MmCached;
if (ExistingAttribute == MiNonCached) {
ExistingCacheType = MmNonCached;
}
else if (ExistingAttribute == MiWriteCombined) {
ExistingCacheType = MmWriteCombined;
}
if (ViHideCacheConflicts == TRUE) {
//
// People don't want to know about these corruptions.
//
break;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x87,
Tracker2->PageFrameIndex,
Tracker2->NumberOfPages,
ExistingCacheType);
}
}
}
NextEntry = Tracker2->ListEntry.Flink;
}
ExReleaseSpinLock (&MmIoTrackerLock, OldIrql);
}
if (CacheAttribute != MiCached) {
//
// If a noncachable mapping is requested, none of the pages in the
// requested range can reside in a cached large page. Otherwise we
// would be creating an incoherent overlapping TB entry as the
// same physical page would be mapped by 2 different TB entries
// with different cache attributes.
//
NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (PhysicalAddress.LowPart,
NumberOfBytes);
LastPageFrameIndex = PageFrameIndex + NumberOfPages;
LOCK_PFN2 (OldIrql);
do {
if (MI_PAGE_FRAME_INDEX_MUST_BE_CACHED (PageFrameIndex)) {
PageFrameIndex = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (PhysicalAddress.LowPart,
NumberOfBytes);
//
// Convert the existing internal cache attribute to an
// external cache type driver writers are familiar with.
// Note this must be done (instead of using the input
// parameter) because some (broken hardware) OEM platforms
// convert the input parameter via MI_TRANSLATE_CACHETYPE
// and we want to print the type really did conflict.
//
if (CacheAttribute == MiNonCached) {
CacheType = MmNonCached;
}
else {
ASSERT (CacheAttribute == MiWriteCombined);
CacheType = MmWriteCombined;
}
if (ViHideCacheConflicts == TRUE) {
//
// People don't want to know about these corruptions.
//
break;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x88,
PageFrameIndex,
NumberOfPages,
CacheType);
}
}
PageFrameIndex += 1;
} while (PageFrameIndex < LastPageFrameIndex);
UNLOCK_PFN2 (OldIrql);
}
if (ViInjectResourceFailure () == TRUE) {
return NULL;
}
return MmMapIoSpace (PhysicalAddress, NumberOfBytes, CacheType);
}
VOID
ViCheckMdlPages (
IN PMDL MemoryDescriptorList,
IN MEMORY_CACHING_TYPE CacheType
)
{
KIRQL OldIrql;
PMMPFN Pfn1;
PFN_NUMBER NumberOfPages;
PPFN_NUMBER Page;
PPFN_NUMBER LastPage;
PVOID StartingVa;
MI_PFN_CACHE_ATTRIBUTE CacheAttribute;
LOGICAL IsPfn;
StartingVa = (PVOID)((PCHAR)MemoryDescriptorList->StartVa +
MemoryDescriptorList->ByteOffset);
Page = (PPFN_NUMBER)(MemoryDescriptorList + 1);
NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartingVa,
MemoryDescriptorList->ByteCount);
LastPage = Page + NumberOfPages;
CacheAttribute = MI_TRANSLATE_CACHETYPE (CacheType, TRUE);
do {
if (*Page == MM_EMPTY_LIST) {
break;
}
IsPfn = MI_IS_PFN (*Page);
if (MemoryDescriptorList->MdlFlags & MDL_IO_SPACE) {
#if 0
//
// Drivers end up with HALCachedMemory pages here
// so this cannot be enabled.
//
if (IsPfn == TRUE) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x8B,
(ULONG_PTR) MemoryDescriptorList,
(ULONG_PTR) Page,
*Page);
}
#endif
}
else {
if (IsPfn == FALSE) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x89,
(ULONG_PTR) MemoryDescriptorList,
(ULONG_PTR) Page,
*Page);
}
Pfn1 = MI_PFN_ELEMENT (*Page);
//
// Each frame better be locked down already. Bugcheck if not.
//
if ((Pfn1->u3.e2.ReferenceCount != 0) ||
((Pfn1->u3.e1.Rom == 1) && (CacheType == MmCached))) {
NOTHING;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x85,
(ULONG_PTR)MemoryDescriptorList,
NumberOfPages,
(ULONG_PTR)MI_PFN_ELEMENT_TO_INDEX(Pfn1));
}
if (Pfn1->u3.e1.CacheAttribute == MiNotMapped) {
//
// This better be for a page allocated with
// MmAllocatePagesForMdl. Otherwise it might be a
// page on the freelist which could subsequently be
// given out with a different attribute !
//
if ((Pfn1->u4.PteFrame == MI_MAGIC_AWE_PTEFRAME) ||
#if defined (_MI_MORE_THAN_4GB_)
(Pfn1->u4.PteFrame == MI_MAGIC_4GB_RECLAIM) ||
#endif
(Pfn1->PteAddress == (PVOID) (ULONG_PTR)(X64K | 0x1))) {
NOTHING;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x86,
(ULONG_PTR)MemoryDescriptorList,
NumberOfPages,
(ULONG_PTR)MI_PFN_ELEMENT_TO_INDEX(Pfn1));
}
}
}
if (CacheAttribute != MiCached) {
//
// If a noncachable mapping is requested, none of the pages in the
// requested range can reside in a cached large page. Otherwise we
// would be creating an incoherent overlapping TB entry as the
// same physical page would be mapped by 2 different TB entries
// with different cache attributes.
//
LOCK_PFN2 (OldIrql);
if (MI_PAGE_FRAME_INDEX_MUST_BE_CACHED (*Page)) {
//
// Convert the existing internal cache attribute to an
// external cache type driver writers are familiar with.
// Note this must be done (instead of using the input
// parameter) because some (broken hardware) OEM platforms
// convert the input parameter via MI_TRANSLATE_CACHETYPE
// and we want to print the type really did conflict.
//
if (CacheAttribute == MiNonCached) {
CacheType = MmNonCached;
}
else {
ASSERT (CacheAttribute == MiWriteCombined);
CacheType = MmWriteCombined;
}
if (ViHideCacheConflicts == TRUE) {
//
// People don't want to know about these corruptions.
//
UNLOCK_PFN2 (OldIrql);
break;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x8A,
(ULONG_PTR) MemoryDescriptorList,
*Page,
CacheType);
}
}
UNLOCK_PFN2 (OldIrql);
}
Page += 1;
} while (Page < LastPage);
}
THUNKED_API
PVOID
VerifierMapLockedPages (
IN PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (AccessMode == KernelMode) {
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x74,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
}
else {
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x75,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
}
ViCheckMdlPages (MemoryDescriptorList, MmCached);
if ((MemoryDescriptorList->MdlFlags & MDL_MAPPING_CAN_FAIL) == 0) {
MI_CHECK_UPTIME ();
if (VerifierSystemSufficientlyBooted == TRUE) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x81,
(ULONG_PTR) MemoryDescriptorList,
MemoryDescriptorList->MdlFlags,
0);
}
}
return MmMapLockedPages (MemoryDescriptorList, AccessMode);
}
THUNKED_API
PVOID
VerifierMapLockedPagesSpecifyCache (
IN PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode,
IN MEMORY_CACHING_TYPE CacheType,
IN PVOID RequestedAddress,
IN ULONG BugCheckOnFailure,
IN MM_PAGE_PRIORITY Priority
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
if (AccessMode == KernelMode) {
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x76,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
}
else {
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x77,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)AccessMode);
}
}
ViCheckMdlPages (MemoryDescriptorList, CacheType);
if ((MemoryDescriptorList->MdlFlags & MDL_MAPPING_CAN_FAIL) ||
(BugCheckOnFailure == 0)) {
if (ViInjectResourceFailure () == TRUE) {
return NULL;
}
}
else {
//
// All drivers must specify can fail or don't bugcheck.
//
MI_CHECK_UPTIME ();
if (VerifierSystemSufficientlyBooted == TRUE) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x82,
(ULONG_PTR) MemoryDescriptorList,
MemoryDescriptorList->MdlFlags,
BugCheckOnFailure);
}
}
return MmMapLockedPagesSpecifyCache (MemoryDescriptorList,
AccessMode,
CacheType,
RequestedAddress,
BugCheckOnFailure,
Priority);
}
VOID
VerifierUnlockPages (
IN OUT PMDL MemoryDescriptorList
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x78,
CurrentIrql,
(ULONG_PTR)MemoryDescriptorList,
0);
}
if ((MemoryDescriptorList->MdlFlags & MDL_PAGES_LOCKED) == 0) {
//
// The caller is trying to unlock an MDL that was never locked down.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x7C,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)MemoryDescriptorList->MdlFlags,
0);
}
if (MemoryDescriptorList->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) {
//
// Nonpaged pool should never be locked down.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x7D,
(ULONG_PTR)MemoryDescriptorList,
(ULONG_PTR)MemoryDescriptorList->MdlFlags,
0);
}
MmUnlockPages (MemoryDescriptorList);
}
VOID
VerifierUnmapLockedPages (
IN PVOID BaseAddress,
IN PMDL MemoryDescriptorList
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x79,
CurrentIrql,
(ULONG_PTR)BaseAddress,
(ULONG_PTR)MemoryDescriptorList);
}
}
else {
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x7A,
CurrentIrql,
(ULONG_PTR)BaseAddress,
(ULONG_PTR)MemoryDescriptorList);
}
}
MmUnmapLockedPages (BaseAddress, MemoryDescriptorList);
}
VOID
VerifierUnmapIoSpace (
IN PVOID BaseAddress,
IN SIZE_T NumberOfBytes
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x7B,
CurrentIrql,
(ULONG_PTR)BaseAddress,
(ULONG_PTR)NumberOfBytes);
}
MmUnmapIoSpace (BaseAddress, NumberOfBytes);
}
THUNKED_API
PVOID
VerifierAllocatePool (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes
)
{
PVOID CallingAddress;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
VI_DETECT_RETURN_ADDRESS (CallingAddress);
if (KernelVerifier == TRUE) {
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) {
return ExAllocatePoolWithTag (PoolType | POOL_DRIVER_MASK,
NumberOfBytes,
'enoN');
}
PoolType |= POOL_DRIVER_MASK;
}
MmVerifierData.AllocationsWithNoTag += 1;
return VeAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
'parW',
HighPoolPriority,
CallingAddress);
}
THUNKED_API
PVOID
VerifierAllocatePoolWithTag (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
)
{
PVOID CallingAddress;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
VI_DETECT_RETURN_ADDRESS (CallingAddress);
if (KernelVerifier == TRUE) {
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) {
return ExAllocatePoolWithTag (PoolType | POOL_DRIVER_MASK,
NumberOfBytes,
Tag);
}
PoolType |= POOL_DRIVER_MASK;
}
return VeAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
Tag,
HighPoolPriority,
CallingAddress);
}
THUNKED_API
PVOID
VerifierAllocatePoolWithQuota (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes
)
{
PVOID Va;
LOGICAL RaiseOnQuotaFailure;
PVOID CallingAddress;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
VI_DETECT_RETURN_ADDRESS (CallingAddress);
if (KernelVerifier == TRUE) {
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) {
return ExAllocatePoolWithQuotaTag (PoolType | POOL_DRIVER_MASK,
NumberOfBytes,
'enoN');
}
PoolType |= POOL_DRIVER_MASK;
}
MmVerifierData.AllocationsWithNoTag += 1;
if (PoolType & POOL_QUOTA_FAIL_INSTEAD_OF_RAISE) {
RaiseOnQuotaFailure = FALSE;
PoolType &= ~POOL_QUOTA_FAIL_INSTEAD_OF_RAISE;
}
else {
RaiseOnQuotaFailure = TRUE;
}
Va = VeAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
'parW',
HighPoolPriority,
CallingAddress);
if (Va == NULL) {
if (RaiseOnQuotaFailure == TRUE) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
}
return Va;
}
THUNKED_API
PVOID
VerifierAllocatePoolWithQuotaTag (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
)
{
PVOID Va;
LOGICAL RaiseOnQuotaFailure;
PVOID CallingAddress;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
VI_DETECT_RETURN_ADDRESS (CallingAddress);
if (KernelVerifier == TRUE) {
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) {
return ExAllocatePoolWithQuotaTag (PoolType | POOL_DRIVER_MASK,
NumberOfBytes,
Tag);
}
PoolType |= POOL_DRIVER_MASK;
}
if (PoolType & POOL_QUOTA_FAIL_INSTEAD_OF_RAISE) {
RaiseOnQuotaFailure = FALSE;
PoolType &= ~POOL_QUOTA_FAIL_INSTEAD_OF_RAISE;
}
else {
RaiseOnQuotaFailure = TRUE;
}
Va = VeAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
Tag,
HighPoolPriority,
CallingAddress);
if (Va == NULL) {
if (RaiseOnQuotaFailure == TRUE) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
}
return Va;
}
THUNKED_API
PVOID
VerifierAllocatePoolWithTagPriority(
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN EX_POOL_PRIORITY Priority
)
/*++
Routine Description:
This thunked-in function:
- Performs sanity checks on the caller.
- Can optionally provide allocation failures to the caller.
- Attempts to provide the allocation from special pool.
- Tracks pool to ensure callers free everything they allocate.
--*/
{
PVOID CallingAddress;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
VI_DETECT_RETURN_ADDRESS (CallingAddress);
if (KernelVerifier == TRUE) {
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) {
return ExAllocatePoolWithTagPriority (PoolType | POOL_DRIVER_MASK,
NumberOfBytes,
Tag,
Priority);
}
PoolType |= POOL_DRIVER_MASK;
}
return VeAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
Tag,
Priority,
CallingAddress);
}
#if DBG
//
// Manually set this to inject failures in the inpage path for threads
// faulting on user (not kernel or session) space addresses only. You
// need to have the verifier enabled for the drivers of interest with
// fault injection disabled.
//
BOOLEAN ViInjectInPagePathOnly;
#endif
LOGICAL
ViInjectResourceFailure (
VOID
)
/*++
Routine Description:
This function determines whether a resource allocation should be
deliberately failed. This may be a pool allocation, MDL creation,
system PTE allocation, etc.
Arguments:
None.
Return Value:
TRUE if the allocation should be failed. FALSE otherwise.
Environment:
Kernel mode. DISPATCH_LEVEL or below.
--*/
{
ULONG TimeLow;
LARGE_INTEGER CurrentTime;
if ((MmVerifierData.Level & DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES) == 0) {
#if DBG
if ((ViInjectInPagePathOnly == TRUE) &&
(PsGetCurrentThread ()->NestedFaultCount != 0)) {
MmVerifierData.AllocationsFailedDeliberately += 1;
//
// Deliberately fail this request.
//
if (MiFaultRetryMask != 0xFFFFFFFF) {
MiFaultRetryMask = 0xFFFFFFFF;
MiUserFaultRetryMask = 0xFFFFFFFF;
}
#if defined(_X86_) || defined(_AMD64_)
ViFaultTracesLog ();
#endif
return TRUE;
}
#endif
return FALSE;
}
//
// Don't fail any requests in the first 7 or 8 minutes as we want to
// give the system enough time to boot.
//
MI_CHECK_UPTIME ();
if (VerifierSystemSufficientlyBooted == TRUE) {
KeQueryTickCount(&CurrentTime);
TimeLow = CurrentTime.LowPart;
if ((TimeLow & 0xF) == 0) {
MmVerifierData.AllocationsFailedDeliberately += 1;
//
// Deliberately fail this request.
//
if (MiFaultRetryMask != 0xFFFFFFFF) {
MiFaultRetryMask = 0xFFFFFFFF;
MiUserFaultRetryMask = 0xFFFFFFFF;
}
#if defined(_X86_) || defined(_AMD64_)
ViFaultTracesLog ();
#endif
return TRUE;
}
//
// Approximately every 5 minutes (on most systems), fail all of this
// components allocations for a 10 second burst. This more closely
// simulates (and exaggerates) the duration of the typical low resource
// scenario.
//
TimeLow &= 0x7FFF;
if (TimeLow < 0x400) {
MmVerifierData.BurstAllocationsFailedDeliberately += 1;
//
// Deliberately fail this request.
//
if (MiFaultRetryMask != 0xFFFFFFFF) {
MiFaultRetryMask = 0xFFFFFFFF;
MiUserFaultRetryMask = 0xFFFFFFFF;
}
#if defined(_X86_) || defined(_AMD64_)
ViFaultTracesLog ();
#endif
return TRUE;
}
}
return FALSE;
}
PVI_POOL_ENTRY
ViGrowPoolAllocation (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier
)
/*++
Routine Description:
This function attempts to grows the verifier pool tracking tables and
return a free entry.
Arguments:
Verifier - Supplies the relevant verifier information structure.
Return Value:
A valid pool information pointer on success, FALSE on failure.
Environment:
Kernel mode. DISPATCH_LEVEL or below.
--*/
{
ULONG_PTR i;
PVI_POOL_ENTRY HashEntry;
//
// No entries were left, try to expand the list.
//
// Note POOL_DRIVER_MASK must be set to stop the recursion loop
// when using the kernel verifier.
//
HashEntry = ExAllocatePoolWithTagPriority (NonPagedPool | POOL_DRIVER_MASK,
PAGE_SIZE,
'ppeV',
HighPoolPriority);
if (HashEntry == NULL) {
//
// Try one last time in case a thread has freed an entry while we
// tried to allocate pool.
//
return (PVI_POOL_ENTRY) InterlockedPopEntrySList (&Verifier->PoolTrackers);
}
KeZeroPages (HashEntry, PAGE_SIZE);
//
// Initialize the page header and then push it on to the page header list.
// This is so the debugger can easily walk the page headers to display all
// the current allocations.
//
HashEntry->PageHeader.VerifierEntry = Verifier;
HashEntry->PageHeader.Signature = VI_POOL_PAGE_HEADER_SIGNATURE;
InterlockedPushEntrySList (&Verifier->PoolPageHeaders,
(PSLIST_ENTRY) &HashEntry->PageHeader.NextPage);
//
// Push each free entry onto the free entry list.
//
#define VI_POOL_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(VI_POOL_ENTRY))
for (i = 0; i < VI_POOL_ENTRIES_PER_PAGE - 2; i += 1) {
HashEntry += 1;
HashEntry->InUse.NumberOfBytes = 0x1;
InterlockedPushEntrySList (&Verifier->PoolTrackers,
(PSLIST_ENTRY) HashEntry);
}
//
// Use the very last entry for our caller.
//
HashEntry += 1;
HashEntry->InUse.NumberOfBytes = 0x1;
return HashEntry;
}
PVOID
ViPostPoolAllocation (
IN PVI_POOL_ENTRY PoolEntry,
IN POOL_TYPE PoolType
)
/*++
Routine Description:
This function performs verifier book-keeping on the allocation.
Arguments:
PoolEntry - Supplies information about the pool entry being allocated.
Note the low bit is set if the allocation came from special
pool so strip it here.
PoolType - Supplies the type of pool being allocated.
Return Value:
The virtual address to give to the original caller.
Environment:
Kernel mode. DISPATCH_LEVEL or below.
--*/
{
PMI_VERIFIER_POOL_HEADER Header;
SIZE_T ChargedBytes;
SIZE_T TotalBytes;
ULONG TotalAllocations;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
PPOOL_HEADER PoolHeader;
PVOID VirtualAddress;
ULONG_PTR SpecialPooled;
VirtualAddress = PoolEntry->InUse.VirtualAddress;
SpecialPooled = (ULONG_PTR)VirtualAddress & 0x1;
Verifier = ((PVI_POOL_ENTRY)(PAGE_ALIGN (PoolEntry)))->PageHeader.VerifierEntry;
ASSERT (Verifier != NULL);
VerifierIsTrackingPool = TRUE;
ChargedBytes = EX_REAL_POOL_USAGE (PoolEntry->InUse.NumberOfBytes);
if (SpecialPooled) {
VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress & ~0x1);
PoolEntry->InUse.VirtualAddress = VirtualAddress;
ChargedBytes = PoolEntry->InUse.NumberOfBytes;
}
else if (PoolEntry->InUse.NumberOfBytes <= POOL_BUDDY_MAX) {
ChargedBytes -= POOL_OVERHEAD;
}
if (PoolEntry->InUse.NumberOfBytes > POOL_BUDDY_MAX) {
ASSERT (BYTE_OFFSET(VirtualAddress) == 0);
}
if (SpecialPooled) {
//
// Carefully adjust the special pool page to move the verifier tracking
// header to the front. This allows the allocation to remain butted
// against the end of the page so overruns can be detected immediately.
//
ChargedBytes -= sizeof (MI_VERIFIER_POOL_HEADER);
if (((ULONG_PTR)VirtualAddress & (PAGE_SIZE - 1))) {
PoolHeader = (PPOOL_HEADER)(PAGE_ALIGN (VirtualAddress));
Header = (PMI_VERIFIER_POOL_HEADER) (PoolHeader + 1);
VirtualAddress = (PVOID)(((LONG_PTR)(((PCHAR)PoolHeader + (PAGE_SIZE - ChargedBytes)))) & ~((LONG_PTR)POOL_OVERHEAD - 1));
}
else {
PoolHeader = (PPOOL_HEADER)((PCHAR)VirtualAddress + PAGE_SIZE - POOL_OVERHEAD);
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)PoolHeader - sizeof (MI_VERIFIER_POOL_HEADER));
}
// ASSERT (PoolHeader->Ulong1 & MI_SPECIAL_POOL_VERIFIER);
PoolHeader->Ulong1 -= sizeof (MI_VERIFIER_POOL_HEADER);
PoolHeader->Ulong1 |= MI_SPECIAL_POOL_VERIFIER;
}
else if (PAGE_ALIGNED(VirtualAddress)) {
//
// Large page allocation.
//
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)VirtualAddress +
ChargedBytes -
sizeof(MI_VERIFIER_POOL_HEADER));
}
else {
PoolHeader = (PPOOL_HEADER)((PCHAR)VirtualAddress - POOL_OVERHEAD);
#if !defined (_WIN64)
if (PoolType & POOL_QUOTA_MASK) {
//
// Note that when kernel verifying, the quota pointer in the pool
// block (for NT32) is not initialized until this routine returns
// to our caller.
//
//
// This allocation was charged quota and on NT32 ONLY, the
// quota pointer is extra space at the end of the pool
// allocation. Move our verifier header to just before the
// quota pointer. No worries about structure alignment on 4 byte
// boundaries on NT32 either.
//
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)PoolHeader +
(PoolHeader->BlockSize << POOL_BLOCK_SHIFT) -
sizeof(PVOID) -
sizeof(MI_VERIFIER_POOL_HEADER));
}
else
#endif
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)VirtualAddress +
ChargedBytes -
sizeof(MI_VERIFIER_POOL_HEADER));
}
ASSERT (((ULONG_PTR)Header & (sizeof(ULONG) - 1)) == 0);
//
// Override a few fields with their final values.
//
PoolEntry->InUse.VirtualAddress = VirtualAddress;
PoolEntry->InUse.NumberOfBytes = ChargedBytes;
if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) {
//
// Update this driver's counters.
//
TotalBytes = InterlockedExchangeAddSizeT (&Verifier->PagedBytes,
ChargedBytes);
if (TotalBytes > Verifier->PeakPagedBytes) {
Verifier->PeakPagedBytes = TotalBytes;
}
TotalAllocations = (ULONG) InterlockedIncrement ((PLONG) &Verifier->CurrentPagedPoolAllocations);
if (TotalAllocations > Verifier->PeakPagedPoolAllocations) {
Verifier->PeakPagedPoolAllocations = TotalAllocations;
}
//
// Update systemwide counters.
//
TotalBytes = InterlockedExchangeAddSizeT (&MmVerifierData.PagedBytes,
ChargedBytes);
if (TotalBytes > MmVerifierData.PeakPagedBytes) {
MmVerifierData.PeakPagedBytes = TotalBytes;
}
TotalAllocations = (ULONG) InterlockedIncrement ((PLONG) &MmVerifierData.CurrentPagedPoolAllocations);
if (TotalAllocations > MmVerifierData.PeakPagedPoolAllocations) {
MmVerifierData.PeakPagedPoolAllocations = TotalAllocations;
}
}
else {
//
// Update this driver's counters.
//
TotalBytes = InterlockedExchangeAddSizeT (&Verifier->NonPagedBytes,
ChargedBytes);
if (TotalBytes > Verifier->PeakNonPagedBytes) {
Verifier->PeakNonPagedBytes = TotalBytes;
}
TotalAllocations = (ULONG) InterlockedIncrement ((PLONG) &Verifier->CurrentNonPagedPoolAllocations);
if (TotalAllocations > Verifier->PeakNonPagedPoolAllocations) {
Verifier->PeakNonPagedPoolAllocations = TotalAllocations;
}
//
// Update systemwide counters.
//
TotalBytes = InterlockedExchangeAddSizeT (&MmVerifierData.NonPagedBytes,
ChargedBytes);
if (TotalBytes > MmVerifierData.PeakNonPagedBytes) {
MmVerifierData.PeakNonPagedBytes = TotalBytes;
}
TotalAllocations = (ULONG) InterlockedIncrement ((PLONG) &MmVerifierData.CurrentNonPagedPoolAllocations);
if (TotalAllocations > MmVerifierData.PeakNonPagedPoolAllocations) {
MmVerifierData.PeakNonPagedPoolAllocations = TotalAllocations;
}
}
//
// Remember the header for paged pool is paged so this may fault.
//
Header->VerifierPoolEntry = PoolEntry;
return VirtualAddress;
}
PVOID
VeAllocatePoolWithTagPriority (
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN EX_POOL_PRIORITY Priority,
IN PVOID CallingAddress
)
/*++
Routine Description:
This routine is called both from ex\pool.c and directly within this module.
- Performs sanity checks on the caller.
- Can optionally provide allocation failures to the caller.
- Attempts to provide the allocation from special pool.
- Tracks pool to ensure callers free everything they allocate.
--*/
{
PVOID VirtualAddress;
EX_POOL_PRIORITY AllocationPriority;
SIZE_T ChargedBytes;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
ULONG HeaderSize;
PVI_POOL_ENTRY PoolEntry;
LOGICAL SpecialPooled;
if (Tag == 0) {
KeBugCheckEx (BAD_POOL_CALLER,
0x9B,
PoolType,
NumberOfBytes,
(ULONG_PTR) CallingAddress);
}
if (Tag == ' GIB') {
KeBugCheckEx (BAD_POOL_CALLER,
0x9C,
PoolType,
NumberOfBytes,
(ULONG_PTR) CallingAddress);
}
ExAllocatePoolSanityChecks (PoolType, NumberOfBytes);
InterlockedIncrement ((PLONG)&MmVerifierData.AllocationsAttempted);
if ((PoolType & MUST_SUCCEED_POOL_TYPE_MASK) == 0) {
if (ViInjectResourceFailure () == TRUE) {
//
// Caller requested an exception - throw it here.
//
if ((PoolType & POOL_RAISE_IF_ALLOCATION_FAILURE) != 0) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
return NULL;
}
}
else {
MI_CHECK_UPTIME ();
if (VerifierSystemSufficientlyBooted == TRUE) {
KeBugCheckEx (BAD_POOL_CALLER,
0x9A,
PoolType,
NumberOfBytes,
Tag);
}
}
ASSERT ((PoolType & POOL_VERIFIER_MASK) == 0);
AllocationPriority = Priority;
if (MmVerifierData.Level & DRIVER_VERIFIER_SPECIAL_POOLING) {
//
// Try for a special pool overrun allocation unless the caller has
// explicitly specified otherwise.
//
if ((AllocationPriority & (LowPoolPrioritySpecialPoolOverrun | LowPoolPrioritySpecialPoolUnderrun)) == 0) {
if (MmSpecialPoolCatchOverruns == TRUE) {
AllocationPriority |= LowPoolPrioritySpecialPoolOverrun;
}
else {
AllocationPriority |= LowPoolPrioritySpecialPoolUnderrun;
}
}
}
//
// Initializing Verifier is not needed for
// correctness but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
Verifier = NULL;
PoolEntry = NULL;
//
// Session pool is directly tracked by default already so it doesn't
// need verifier tracking.
//
if ((MmVerifierData.Level & DRIVER_VERIFIER_TRACK_POOL_ALLOCATIONS) &&
((PoolType & SESSION_POOL_MASK) == 0)) {
HeaderSize = sizeof (MI_VERIFIER_POOL_HEADER);
ChargedBytes = MI_ROUND_TO_SIZE (NumberOfBytes, sizeof(ULONG)) + HeaderSize;
Verifier = ViLocateVerifierEntry (CallingAddress);
if ((Verifier == NULL) ||
((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0) ||
(Verifier->Flags & VI_DISABLE_VERIFICATION)) {
//
// This can happen for many reasons including no framing (which
// can cause RtlGetCallersAddress to return the wrong address),
// etc.
//
MmVerifierData.UnTrackedPool += 1;
}
else if (ChargedBytes <= NumberOfBytes) {
//
// Don't let the verifier header transform a bad caller into a
// good caller. Fail via the fall through so an exception
// can be thrown if asked for, etc.
//
MmVerifierData.UnTrackedPool += 1;
}
else {
PoolEntry = (PVI_POOL_ENTRY) InterlockedPopEntrySList (&Verifier->PoolTrackers);
if (PoolEntry == NULL) {
PoolEntry = ViGrowPoolAllocation (Verifier);
}
if (PoolEntry != NULL) {
ASSERT (PoolEntry->InUse.NumberOfBytes & 0x1);
NumberOfBytes = ChargedBytes;
PoolType |= POOL_VERIFIER_MASK;
}
}
}
VirtualAddress = ExAllocatePoolWithTagPriority (PoolType,
NumberOfBytes,
Tag,
AllocationPriority);
if (VirtualAddress == NULL) {
MmVerifierData.AllocationsFailed += 1;
if (PoolEntry != NULL) {
//
// Release the hash table entry now as it's not needed.
//
ASSERT (PoolEntry->InUse.NumberOfBytes & 0x1);
InterlockedPushEntrySList (&Verifier->PoolTrackers,
(PSLIST_ENTRY) PoolEntry);
}
if ((PoolType & POOL_RAISE_IF_ALLOCATION_FAILURE) != 0) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
return NULL;
}
SpecialPooled = FALSE;
InterlockedIncrement ((PLONG)&MmVerifierData.AllocationsSucceeded);
if (MmIsSpecialPoolAddress (VirtualAddress) == TRUE) {
SpecialPooled = TRUE;
InterlockedIncrement ((PLONG)&MmVerifierData.AllocationsSucceededSpecialPool);
}
else if (NumberOfBytes > POOL_BUDDY_MAX) {
//
// This isn't exactly true but it does give the user a way to see
// if this machine is large enough to support special pool 100%.
//
InterlockedIncrement ((PLONG)&MmVerifierData.AllocationsSucceededSpecialPool);
}
if (PoolEntry != NULL) {
PoolEntry->InUse.VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress | SpecialPooled);
PoolEntry->InUse.CallingAddress = CallingAddress;
PoolEntry->InUse.NumberOfBytes = NumberOfBytes;
PoolEntry->InUse.Tag = Tag;
ASSERT ((PoolType & POOL_VERIFIER_MASK) != 0);
VirtualAddress = ViPostPoolAllocation (PoolEntry, PoolType);
}
else {
ASSERT ((PoolType & POOL_VERIFIER_MASK) == 0);
}
return VirtualAddress;
}
VOID
ViFreeTrackedPool (
IN PVOID VirtualAddress,
IN SIZE_T ChargedBytes,
IN LOGICAL CheckType,
IN LOGICAL SpecialPool
)
/*++
Routine Description:
Called directly from the pool manager or the memory manager for verifier-
tracked allocations. The call to ExFreePool is already in progress.
Arguments:
VirtualAddress - Supplies the virtual address being freed.
ChargedBytes - Supplies the number of bytes charged to this allocation.
CheckType - Supplies PagedPool or NonPagedPool.
SpecialPool - Supplies TRUE if the allocation is from special pool.
Return Value:
None.
Environment:
Kernel mode, no locks or mutexes held on entry.
--*/
{
PPOOL_HEADER PoolHeader;
PMI_VERIFIER_POOL_HEADER Header;
PVI_POOL_ENTRY PoolEntry;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER PageFrameIndex2;
PVI_POOL_PAGE_HEADER PageHeader;
PMMPTE PointerPte;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT (VerifierIsTrackingPool == TRUE);
if (SpecialPool == TRUE) {
//
// Special pool allocation.
//
if (((ULONG_PTR)VirtualAddress & (PAGE_SIZE - 1))) {
PoolHeader = PAGE_ALIGN (VirtualAddress);
Header = (PMI_VERIFIER_POOL_HEADER)(PoolHeader + 1);
}
else {
PoolHeader = (PPOOL_HEADER)((PCHAR)PAGE_ALIGN (VirtualAddress) + PAGE_SIZE - POOL_OVERHEAD);
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)PoolHeader - sizeof (MI_VERIFIER_POOL_HEADER));
}
}
else if (PAGE_ALIGNED(VirtualAddress)) {
//
// Large page allocation.
//
Header = (PMI_VERIFIER_POOL_HEADER) ((PCHAR)VirtualAddress +
ChargedBytes -
sizeof(MI_VERIFIER_POOL_HEADER));
}
else {
ChargedBytes -= POOL_OVERHEAD;
#if !defined (_WIN64)
PoolHeader = (PPOOL_HEADER)((PCHAR)VirtualAddress - POOL_OVERHEAD);
if (PoolHeader->PoolType & POOL_QUOTA_MASK) {
//
// This allocation was charged quota and on NT32 ONLY, the
// quota pointer is extra space at the end of the pool
// allocation. Move our verifier header to just before the
// quota pointer. No worries about structure alignment on 4 byte
// boundaries on NT32 either.
//
Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)PoolHeader +
(PoolHeader->BlockSize << POOL_BLOCK_SHIFT) -
sizeof(PVOID) -
sizeof(MI_VERIFIER_POOL_HEADER));
}
else
#endif
Header = (PMI_VERIFIER_POOL_HEADER) ((PCHAR)VirtualAddress +
ChargedBytes -
sizeof(MI_VERIFIER_POOL_HEADER));
}
PoolEntry = Header->VerifierPoolEntry;
//
// Check the pointer now so we can give a more friendly bugcheck
// rather than crashing below on a bad reference.
//
if ((((ULONG_PTR)PoolEntry & (sizeof(ULONG) - 1)) != 0) ||
(!MiIsAddressValid(PoolEntry, TRUE))) {
//
// The caller corrupted the saved verifier field.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x53,
(ULONG_PTR)VirtualAddress,
(ULONG_PTR)NULL,
(ULONG_PTR)PoolEntry);
}
PageHeader = (PVI_POOL_PAGE_HEADER) PAGE_ALIGN (PoolEntry);
if (PageHeader->Signature != VI_POOL_PAGE_HEADER_SIGNATURE) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x53,
(ULONG_PTR)VirtualAddress,
(ULONG_PTR)PageHeader->Signature,
(ULONG_PTR)PoolEntry);
}
Verifier = (PMI_VERIFIER_DRIVER_ENTRY) PageHeader->VerifierEntry;
ASSERT (Verifier != NULL);
//
// Check the pointer now so we can give a more friendly bugcheck
// rather than crashing below on a bad reference.
//
if ((((ULONG_PTR)Verifier & (sizeof(ULONG) - 1)) != 0) ||
(!MiIsAddressValid(&Verifier->Signature, TRUE)) ||
(Verifier->Signature != MI_VERIFIER_ENTRY_SIGNATURE)) {
//
// The caller corrupted the saved verifier field.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x53,
(ULONG_PTR)VirtualAddress,
(ULONG_PTR)PoolEntry,
(ULONG_PTR)Verifier);
}
if (PoolEntry->InUse.VirtualAddress != VirtualAddress) {
PageFrameIndex = 0;
PageFrameIndex2 = 1;
if ((!MI_IS_PHYSICAL_ADDRESS(VirtualAddress)) &&
(MI_IS_PHYSICAL_ADDRESS(PoolEntry->InUse.VirtualAddress))) {
PointerPte = MiGetPteAddress(VirtualAddress);
if (PointerPte->u.Hard.Valid == 1) {
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
PageFrameIndex2 = MI_CONVERT_PHYSICAL_TO_PFN (PoolEntry->InUse.VirtualAddress);
}
}
//
// Caller overran and corrupted the virtual address - the linked
// list cannot be counted on either.
//
if (PageFrameIndex != PageFrameIndex2) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x52,
(ULONG_PTR)VirtualAddress,
(ULONG_PTR)PoolEntry->InUse.VirtualAddress,
ChargedBytes);
}
}
if (PoolEntry->InUse.NumberOfBytes != ChargedBytes) {
//
// Caller overran and corrupted the byte count - the linked
// list cannot be counted on either.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x51,
(ULONG_PTR)VirtualAddress,
(ULONG_PTR)PoolEntry,
ChargedBytes);
}
//
// Put this list entry into the freelist.
//
PoolEntry->InUse.NumberOfBytes |= 0x1;
InterlockedPushEntrySList (&Verifier->PoolTrackers,
(PSLIST_ENTRY) PoolEntry);
if ((CheckType & BASE_POOL_TYPE_MASK) == PagedPool) {
//
// Decrement this driver's counters.
//
InterlockedExchangeAddSizeT (&Verifier->PagedBytes, 0 - ChargedBytes);
InterlockedDecrement ((PLONG) &Verifier->CurrentPagedPoolAllocations);
//
// Decrement the systemwide counters.
//
InterlockedExchangeAddSizeT (&MmVerifierData.PagedBytes, 0 - ChargedBytes);
InterlockedDecrement ((PLONG) &MmVerifierData.CurrentPagedPoolAllocations);
}
else {
//
// Decrement this driver's counters.
//
InterlockedExchangeAddSizeT (&Verifier->NonPagedBytes, 0 - ChargedBytes);
InterlockedDecrement ((PLONG) &Verifier->CurrentNonPagedPoolAllocations);
//
// Decrement the systemwide counters.
//
InterlockedExchangeAddSizeT (&MmVerifierData.NonPagedBytes, 0 - ChargedBytes);
InterlockedDecrement ((PLONG) &MmVerifierData.CurrentNonPagedPoolAllocations);
}
}
VOID
VerifierFreeTrackedPool (
IN PVOID VirtualAddress,
IN SIZE_T ChargedBytes,
IN LOGICAL CheckType,
IN LOGICAL SpecialPool
)
/*++
Routine Description:
Called directly from the pool manager or the memory manager for verifier-
tracked allocations. The call to ExFreePool is already in progress.
Arguments:
VirtualAddress - Supplies the virtual address being freed.
ChargedBytes - Supplies the number of bytes charged to this allocation.
CheckType - Supplies PagedPool or NonPagedPool.
SpecialPool - Supplies TRUE if the allocation is from special pool.
Return Value:
None.
Environment:
Kernel mode, no locks or mutexes held on entry.
--*/
{
if (VerifierIsTrackingPool == FALSE) {
//
// The verifier is not enabled so the only way this routine is being
// called is because the pool header is mangled or the caller specified
// a bad address. Either way it's a bugcheck.
//
KeBugCheckEx (BAD_POOL_CALLER,
0x99,
(ULONG_PTR)VirtualAddress,
0,
0);
}
ViFreeTrackedPool (VirtualAddress, ChargedBytes, CheckType, SpecialPool);
}
THUNKED_API
VOID
VerifierFreePool(
IN PVOID P
)
{
if (KernelVerifier == TRUE) {
ExFreePool (P);
return;
}
VerifierFreePoolWithTag (P, 0);
}
THUNKED_API
VOID
VerifierFreePoolWithTag(
IN PVOID P,
IN ULONG TagToFree
)
{
if (KernelVerifier == TRUE) {
ExFreePoolWithTag (P, TagToFree);
return;
}
ExFreePoolSanityChecks (P);
ExFreePoolWithTag (P, TagToFree);
}
THUNKED_API
LONG
VerifierSetEvent (
IN PRKEVENT Event,
IN KPRIORITY Increment,
IN BOOLEAN Wait
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql();
if (CurrentIrql > DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x80,
CurrentIrql,
(ULONG_PTR)Event,
(ULONG_PTR)0);
}
return KeSetEvent (Event, Increment, Wait);
}
THUNKED_API
BOOLEAN
VerifierExAcquireResourceExclusiveLite(
IN PERESOURCE Resource,
IN BOOLEAN Wait
)
{
KIRQL CurrentIrql;
//
// Check alignment of the Resource. It MUST be aligned because
// it contains a queued lock and the lower two bits of the address
// of the lock are used for status information.
//
if ((((ULONG_PTR)Resource) & (sizeof(ULONG_PTR) - 1)) != 0) {
KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3D,
0,
0,
(ULONG_PTR)Resource);
}
CurrentIrql = KeGetCurrentIrql ();
if ((CurrentIrql != APC_LEVEL) &&
(!IS_SYSTEM_THREAD(PsGetCurrentThread())) &&
(KeGetCurrentThread()->CombinedApcDisable == 0)) {
if ((CurrentIrql == DISPATCH_LEVEL) && (Wait == FALSE)) {
NOTHING;
}
else {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x37,
CurrentIrql,
(ULONG_PTR)(KeGetCurrentThread()->CombinedApcDisable),
(ULONG_PTR)Resource);
}
}
return ExAcquireResourceExclusiveLite (Resource, Wait);
}
THUNKED_API
VOID
FASTCALL
VerifierExReleaseResourceLite(
IN PERESOURCE Resource
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
if ((CurrentIrql != APC_LEVEL) &&
(!IS_SYSTEM_THREAD(PsGetCurrentThread())) &&
(KeGetCurrentThread()->CombinedApcDisable == 0)) {
if (CurrentIrql != DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x38,
CurrentIrql,
(ULONG_PTR)(KeGetCurrentThread()->CombinedApcDisable),
(ULONG_PTR)Resource);
}
}
ExReleaseResourceLite (Resource);
}
int VerifierIrqlData[0x10];
VOID
KfSanityCheckRaiseIrql (
IN KIRQL NewIrql
)
{
KIRQL CurrentIrql;
//
// Check for the caller inadvertently lowering.
//
CurrentIrql = KeGetCurrentIrql ();
if (CurrentIrql == NewIrql) {
VerifierIrqlData[0] += 1;
if (CurrentIrql == APC_LEVEL) {
VerifierIrqlData[1] += 1;
}
else if (CurrentIrql == DISPATCH_LEVEL) {
VerifierIrqlData[2] += 1;
}
else {
VerifierIrqlData[3] += 1;
}
}
else {
VerifierIrqlData[4] += 1;
}
if (CurrentIrql > NewIrql) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x30,
CurrentIrql,
NewIrql,
0);
}
//
// Check for the caller using an uninitialized variable.
//
if (NewIrql > HIGH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x30,
CurrentIrql,
NewIrql,
0);
}
if (ViTrackIrqlQueue != NULL) {
ViTrackIrqlLog (CurrentIrql, NewIrql);
}
}
VOID
KfSanityCheckLowerIrql (
IN KIRQL NewIrql
)
{
KIRQL CurrentIrql;
BOOLEAN Enable;
//
// Check for the caller inadvertently lowering.
//
CurrentIrql = KeGetCurrentIrql ();
if (CurrentIrql == NewIrql) {
VerifierIrqlData[8] += 1;
if (CurrentIrql == APC_LEVEL) {
VerifierIrqlData[9] += 1;
}
else if (CurrentIrql == DISPATCH_LEVEL) {
VerifierIrqlData[10] += 1;
}
else {
VerifierIrqlData[11] += 1;
}
}
else {
VerifierIrqlData[12] += 1;
}
if (CurrentIrql < NewIrql) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x31,
CurrentIrql,
NewIrql,
0);
}
//
// Check if we are lowering IRQL in a DPC routine.
// This is illegal as we might context swap.
//
if (CurrentIrql >= DISPATCH_LEVEL &&
NewIrql < DISPATCH_LEVEL &&
KeGetCurrentPrcb()->DpcRoutineActive) {
//
// Don't bugcheck if interrupts are disabled as this would be legal.
// This might miss some real bugs but it's going to be rare.
//
Enable = KeDisableInterrupts ();
KeEnableInterrupts (Enable);
if (Enable != 0) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x31,
CurrentIrql,
NewIrql,
1);
}
}
//
// Check for the caller using an uninitialized variable.
//
if (NewIrql > HIGH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x31,
CurrentIrql,
NewIrql,
0);
}
if (ViTrackIrqlQueue != NULL) {
ViTrackIrqlLog (CurrentIrql, NewIrql);
}
}
#define VI_TRIM_KERNEL 0x00000001
#define VI_TRIM_USER 0x00000002
#define VI_TRIM_SESSION 0x00000004
#define VI_TRIM_PURGE 0x80000000
ULONG ViTrimSpaces = VI_TRIM_KERNEL;
VOID
ViTrimAllSystemPagableMemory (
ULONG TrimType
)
{
LOGICAL PurgeTransition;
LARGE_INTEGER CurrentTime;
LOGICAL PageOut;
PageOut = TRUE;
if (KernelVerifier == TRUE) {
KeQueryTickCount (&CurrentTime);
if ((CurrentTime.LowPart & KernelVerifierTickPage) != 0) {
PageOut = FALSE;
}
}
if ((PageOut == TRUE) && (MiNoPageOnRaiseIrql == 0)) {
MmVerifierData.TrimRequests += 1;
if (TrimType == 0) {
TrimType = ViTrimSpaces;
}
if (TrimType & VI_TRIM_PURGE) {
PurgeTransition = TRUE;
}
else {
PurgeTransition = FALSE;
}
if (TrimType & VI_TRIM_KERNEL) {
if (MiTrimAllSystemPagableMemory (MI_SYSTEM_GLOBAL,
PurgeTransition) == TRUE) {
MmVerifierData.Trims += 1;
}
}
if (TrimType & VI_TRIM_USER) {
if (MiTrimAllSystemPagableMemory (MI_USER_LOCAL,
PurgeTransition) == TRUE) {
MmVerifierData.UserTrims += 1;
}
}
if (TrimType & VI_TRIM_SESSION) {
if (MiTrimAllSystemPagableMemory (MI_SESSION_LOCAL,
PurgeTransition) == TRUE) {
MmVerifierData.SessionTrims += 1;
}
}
}
}
typedef
VOID
(*PKE_ACQUIRE_SPINLOCK) (
IN PKSPIN_LOCK SpinLock,
OUT PKIRQL OldIrql
);
THUNKED_API
VOID
VerifierKeAcquireSpinLock (
IN PKSPIN_LOCK SpinLock,
OUT PKIRQL OldIrql
)
{
KIRQL CurrentIrql;
#if defined (_X86_)
PKE_ACQUIRE_SPINLOCK HalRoutine;
#endif
CurrentIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (DISPATCH_LEVEL);
MmVerifierData.AcquireSpinLocks += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if (CurrentIrql < DISPATCH_LEVEL) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKE_ACQUIRE_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_ACQUIRE_SPINLOCK];
if (HalRoutine) {
(*HalRoutine)(SpinLock, OldIrql);
VfDeadlockAcquireResource (SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
return;
}
#endif
KeAcquireSpinLock (SpinLock, OldIrql);
VfDeadlockAcquireResource (SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
}
typedef
VOID
(*PKE_RELEASE_SPINLOCK) (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);
THUNKED_API
VOID
VerifierKeReleaseSpinLock (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
)
{
KIRQL CurrentIrql;
#if defined (_X86_)
PKE_RELEASE_SPINLOCK HalRoutine;
#endif
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better still be at DISPATCH_LEVEL when releasing the spinlock
//
if (CurrentIrql < DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x32,
CurrentIrql,
(ULONG_PTR)SpinLock,
0);
}
KfSanityCheckLowerIrql (NewIrql);
#if defined (_X86_)
HalRoutine = (PKE_RELEASE_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_RELEASE_SPINLOCK];
if (HalRoutine) {
VfDeadlockReleaseResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
_ReturnAddress());
(*HalRoutine)(SpinLock, NewIrql);
return;
}
#endif
VfDeadlockReleaseResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
_ReturnAddress());
KeReleaseSpinLock (SpinLock, NewIrql);
}
//
// Verifier thunks for AcquireSpinLockAtDpcLevel and ReleaseSpinLockFromDpcLevel.
//
// On x86 the functions exported by the kernel that are used by the driver are:
// KefAcquire.../KefRelease.... On other platforms the functions used by drivers
// are KeAcquire.../KeRelease. Among other differences the x86 versions use the
// fastcall convention which requires additional precaution.
//
THUNKED_API
VOID
#if defined(_X86_)
FASTCALL
#endif
VerifierKeAcquireSpinLockAtDpcLevel (
IN PKSPIN_LOCK SpinLock
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at or above DISPATCH_LEVEL.
//
if (CurrentIrql < DISPATCH_LEVEL) {
#if defined(_AMD64_)
if (GetCallersEflags () & EFLAGS_IF_MASK)
#endif
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x40,
CurrentIrql,
(ULONG_PTR)SpinLock,
0);
}
MmVerifierData.AcquireSpinLocks += 1;
KeAcquireSpinLockAtDpcLevel (SpinLock);
VfDeadlockAcquireResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
}
THUNKED_API
VOID
#if defined(_X86_)
FASTCALL
#endif
VerifierKeReleaseSpinLockFromDpcLevel (
IN PKSPIN_LOCK SpinLock
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at or above DISPATCH_LEVEL.
//
if (CurrentIrql < DISPATCH_LEVEL) {
#if defined(_AMD64_)
if (GetCallersEflags () & EFLAGS_IF_MASK)
#endif
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x41,
CurrentIrql,
(ULONG_PTR)SpinLock,
0);
}
VfDeadlockReleaseResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
_ReturnAddress());
KeReleaseSpinLockFromDpcLevel (SpinLock);
}
#if !defined(_X86_)
THUNKED_API
KIRQL
VerifierKeAcquireSpinLockRaiseToDpc (
IN PKSPIN_LOCK SpinLock
)
{
KIRQL NewIrql = KeAcquireSpinLockRaiseToDpc (SpinLock);
VfDeadlockAcquireResource (SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
return NewIrql;
}
#endif
#if defined (_X86_)
typedef
KIRQL
(FASTCALL *PKF_ACQUIRE_SPINLOCK) (
IN PKSPIN_LOCK SpinLock
);
THUNKED_API
KIRQL
FASTCALL
VerifierKfAcquireSpinLock (
IN PKSPIN_LOCK SpinLock
)
{
KIRQL CurrentIrql;
PKF_ACQUIRE_SPINLOCK HalRoutine;
CurrentIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (DISPATCH_LEVEL);
MmVerifierData.AcquireSpinLocks += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if (CurrentIrql < DISPATCH_LEVEL) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKF_ACQUIRE_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KF_ACQUIRE_SPINLOCK];
if (HalRoutine) {
CurrentIrql = (*HalRoutine)(SpinLock);
VfDeadlockAcquireResource (SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
return CurrentIrql;
}
#endif
CurrentIrql = KfAcquireSpinLock (SpinLock);
VfDeadlockAcquireResource (SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
return CurrentIrql;
}
typedef
VOID
(FASTCALL *PKF_RELEASE_SPINLOCK) (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);
THUNKED_API
VOID
FASTCALL
VerifierKfReleaseSpinLock (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
)
{
KIRQL CurrentIrql;
PKF_RELEASE_SPINLOCK HalRoutine;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better still be at DISPATCH_LEVEL when releasing the spinlock.
//
if (CurrentIrql < DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x35,
CurrentIrql,
(ULONG_PTR)SpinLock,
NewIrql);
}
KfSanityCheckLowerIrql (NewIrql);
#if defined (_X86_)
HalRoutine = (PKF_RELEASE_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KF_RELEASE_SPINLOCK];
if (HalRoutine) {
VfDeadlockReleaseResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
_ReturnAddress());
(*HalRoutine)(SpinLock, NewIrql);
return;
}
#endif
VfDeadlockReleaseResource(SpinLock,
VfDeadlockSpinLock,
KeGetCurrentThread(),
_ReturnAddress());
KfReleaseSpinLock (SpinLock, NewIrql);
}
#if !defined(NT_UP)
typedef
KIRQL
(FASTCALL *PKE_ACQUIRE_QUEUED_SPINLOCK) (
IN KSPIN_LOCK_QUEUE_NUMBER Number
);
THUNKED_API
KIRQL
FASTCALL
VerifierKeAcquireQueuedSpinLock (
IN KSPIN_LOCK_QUEUE_NUMBER Number
)
{
KIRQL CurrentIrql;
PKE_ACQUIRE_QUEUED_SPINLOCK HalRoutine;
CurrentIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (DISPATCH_LEVEL);
MmVerifierData.AcquireSpinLocks += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if (CurrentIrql < DISPATCH_LEVEL) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKE_ACQUIRE_QUEUED_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_ACQUIRE_QUEUED_SPINLOCK];
if (HalRoutine) {
return (*HalRoutine)(Number);
}
#endif
CurrentIrql = KeAcquireQueuedSpinLock (Number);
return CurrentIrql;
}
typedef
VOID
(FASTCALL *PKE_RELEASE_QUEUED_SPINLOCK) (
IN KSPIN_LOCK_QUEUE_NUMBER Number,
IN KIRQL OldIrql
);
THUNKED_API
VOID
FASTCALL
VerifierKeReleaseQueuedSpinLock (
IN KSPIN_LOCK_QUEUE_NUMBER Number,
IN KIRQL OldIrql
)
{
KIRQL CurrentIrql;
PKE_RELEASE_QUEUED_SPINLOCK HalRoutine;
CurrentIrql = KeGetCurrentIrql ();
if (KernelVerifier == TRUE) {
if (CurrentIrql < DISPATCH_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x36,
CurrentIrql,
(ULONG_PTR)Number,
(ULONG_PTR)OldIrql);
}
}
KfSanityCheckLowerIrql (OldIrql);
#if defined (_X86_)
HalRoutine = (PKE_RELEASE_QUEUED_SPINLOCK) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_RELEASE_QUEUED_SPINLOCK];
if (HalRoutine) {
(*HalRoutine)(Number, OldIrql);
return;
}
#endif
KeReleaseQueuedSpinLock (Number, OldIrql);
}
#endif // NT_UP
#endif // _X86_
#if defined(_X86_) || defined(_AMD64_)
typedef
KIRQL
(FASTCALL *PKF_RAISE_IRQL) (
IN KIRQL NewIrql
);
THUNKED_API
KIRQL
FASTCALL
VerifierKfRaiseIrql (
IN KIRQL NewIrql
)
{
#if defined (_X86_)
PKF_RAISE_IRQL HalRoutine;
#endif
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (NewIrql);
MmVerifierData.RaiseIrqls += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if ((CurrentIrql < DISPATCH_LEVEL) && (NewIrql >= DISPATCH_LEVEL)) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKF_RAISE_IRQL) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KF_RAISE_IRQL];
if (HalRoutine) {
return (*HalRoutine)(NewIrql);
}
#endif
return KfRaiseIrql (NewIrql);
}
typedef
KIRQL
(FASTCALL *PKE_RAISE_IRQL_TO_DPC_LEVEL) (
VOID
);
THUNKED_API
KIRQL
VerifierKeRaiseIrqlToDpcLevel (
VOID
)
{
#if defined (_X86_)
PKE_RAISE_IRQL_TO_DPC_LEVEL HalRoutine;
#endif
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (DISPATCH_LEVEL);
MmVerifierData.RaiseIrqls += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if (CurrentIrql < DISPATCH_LEVEL) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKE_RAISE_IRQL_TO_DPC_LEVEL) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_RAISE_IRQL_TO_DPC_LEVEL];
if (HalRoutine) {
return (*HalRoutine)();
}
#endif
return KeRaiseIrqlToDpcLevel ();
}
#endif // _X86_ || _AMD64_
#if defined(_X86_)
typedef
VOID
(FASTCALL *PKF_LOWER_IRQL) (
IN KIRQL NewIrql
);
THUNKED_API
VOID
FASTCALL
VerifierKfLowerIrql (
IN KIRQL NewIrql
)
{
PKF_LOWER_IRQL HalRoutine;
KfSanityCheckLowerIrql (NewIrql);
#if defined (_X86_)
HalRoutine = (PKF_LOWER_IRQL) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KF_LOWER_IRQL];
if (HalRoutine) {
(*HalRoutine)(NewIrql);
return;
}
#endif
KfLowerIrql (NewIrql);
}
#endif
THUNKED_API
BOOLEAN
FASTCALL
VerifierExTryToAcquireFastMutex (
IN PFAST_MUTEX FastMutex
)
{
KIRQL CurrentIrql;
BOOLEAN Acquired;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at or below APC_LEVEL or have APCs blocked.
//
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x33,
CurrentIrql,
(ULONG_PTR)FastMutex,
0);
}
Acquired = ExTryToAcquireFastMutex (FastMutex);
if (Acquired != FALSE) {
VfDeadlockAcquireResource (FastMutex,
VfDeadlockFastMutex,
KeGetCurrentThread(),
TRUE,
_ReturnAddress());
}
return Acquired;
}
typedef
VOID
(FASTCALL *PEX_ACQUIRE_FAST_MUTEX) (
IN PFAST_MUTEX FastMutex
);
THUNKED_API
VOID
FASTCALL
VerifierExAcquireFastMutex (
IN PFAST_MUTEX FastMutex
)
{
KIRQL CurrentIrql;
#if defined (_X86_)
PEX_ACQUIRE_FAST_MUTEX HalRoutine;
#endif
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at or below APC_LEVEL or have APCs blocked.
//
if (CurrentIrql > APC_LEVEL) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x33,
CurrentIrql,
(ULONG_PTR)FastMutex,
0);
}
#if 0
//
// If the kernel verifier is active, then page relevant address spaces
// out when the system or session space working set mutex is acquired.
// Note this must be done regardless of the entry IRQL level because
// callers may have raised to APC_LEVEL and subsequently acquire the mutex.
//
//
// Commented all this out when the working mutex was converted from a
// fast mutex to a guarded mutex as there are no longer calls to thunk.
//
if ((KernelVerifier == TRUE) &&
(MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING)) {
if (FastMutex == &MmSystemWsLock) {
ViTrimAllSystemPagableMemory (VI_TRIM_KERNEL);
}
else if (PsGetCurrentProcess()->Vm.Flags.SessionLeader == 0) {
if (MiIsAddressValid (MmSessionSpace, FALSE)) {
if (FastMutex == &MmSessionSpace->GlobalVirtualAddress->WsLock) {
ViTrimAllSystemPagableMemory (VI_TRIM_SESSION);
}
}
}
}
#endif
#if defined (_X86_)
HalRoutine = (PEX_ACQUIRE_FAST_MUTEX) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_EX_ACQUIRE_FAST_MUTEX];
if (HalRoutine) {
(*HalRoutine)(FastMutex);
}
else
#endif
ExAcquireFastMutex (FastMutex);
VfDeadlockAcquireResource (FastMutex,
VfDeadlockFastMutex,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
}
THUNKED_API
VOID
FASTCALL
VerifierExAcquireFastMutexUnsafe (
IN PFAST_MUTEX FastMutex
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at APC_LEVEL or have APCs blocked.
//
if ((CurrentIrql != APC_LEVEL) &&
(!IS_SYSTEM_THREAD(PsGetCurrentThread())) &&
(KeGetCurrentThread()->CombinedApcDisable == 0)) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x39,
CurrentIrql,
(ULONG_PTR)(KeGetCurrentThread()->CombinedApcDisable),
(ULONG_PTR)FastMutex);
}
ExAcquireFastMutexUnsafe (FastMutex);
VfDeadlockAcquireResource(FastMutex,
VfDeadlockFastMutexUnsafe,
KeGetCurrentThread(),
FALSE,
_ReturnAddress());
}
THUNKED_API
VOID
FASTCALL
VerifierExReleaseFastMutex (
IN PFAST_MUTEX FastMutex
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at APC_LEVEL or have APCs blocked.
//
if ((CurrentIrql != APC_LEVEL) &&
(!IS_SYSTEM_THREAD(PsGetCurrentThread())) &&
(KeGetCurrentThread()->CombinedApcDisable == 0)) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x34,
CurrentIrql,
(ULONG_PTR)(KeGetCurrentThread()->CombinedApcDisable),
(ULONG_PTR)FastMutex);
}
VfDeadlockReleaseResource(FastMutex,
VfDeadlockFastMutex,
KeGetCurrentThread(),
_ReturnAddress());
ExReleaseFastMutex (FastMutex);
}
THUNKED_API
VOID
FASTCALL
VerifierExReleaseFastMutexUnsafe (
IN PFAST_MUTEX FastMutex
)
{
KIRQL CurrentIrql;
CurrentIrql = KeGetCurrentIrql ();
//
// Caller better be at APC_LEVEL or have APCs blocked.
//
if ((CurrentIrql != APC_LEVEL) &&
(!IS_SYSTEM_THREAD(PsGetCurrentThread())) &&
(KeGetCurrentThread()->CombinedApcDisable == 0)) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3A,
CurrentIrql,
(ULONG_PTR)(KeGetCurrentThread()->CombinedApcDisable),
(ULONG_PTR)FastMutex);
}
VfDeadlockReleaseResource(FastMutex,
VfDeadlockFastMutexUnsafe,
KeGetCurrentThread(),
_ReturnAddress());
ExReleaseFastMutexUnsafe (FastMutex);
}
typedef
VOID
(*PKE_RAISE_IRQL) (
IN KIRQL NewIrql,
OUT PKIRQL OldIrql
);
THUNKED_API
VOID
VerifierKeRaiseIrql (
IN KIRQL NewIrql,
OUT PKIRQL OldIrql
)
{
#if defined (_X86_)
PKE_RAISE_IRQL HalRoutine;
#endif
*OldIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (NewIrql);
MmVerifierData.RaiseIrqls += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if ((*OldIrql < DISPATCH_LEVEL) && (NewIrql >= DISPATCH_LEVEL)) {
ViTrimAllSystemPagableMemory (0);
}
}
#if defined (_X86_)
HalRoutine = (PKE_RAISE_IRQL) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_RAISE_IRQL];
if (HalRoutine) {
(*HalRoutine)(NewIrql, OldIrql);
return;
}
#endif
KeRaiseIrql (NewIrql, OldIrql);
}
typedef
VOID
(*PKE_LOWER_IRQL) (
IN KIRQL NewIrql
);
THUNKED_API
VOID
VerifierKeLowerIrql (
IN KIRQL NewIrql
)
{
#if defined (_X86_)
PKE_LOWER_IRQL HalRoutine;
#endif
KfSanityCheckLowerIrql (NewIrql);
#if defined (_X86_)
HalRoutine = (PKE_LOWER_IRQL) (ULONG_PTR) MiKernelVerifierOriginalCalls[VI_KE_LOWER_IRQL];
if (HalRoutine) {
(*HalRoutine)(NewIrql);
return;
}
#endif
KeLowerIrql (NewIrql);
}
THUNKED_API
BOOLEAN
VerifierSynchronizeExecution (
IN PKINTERRUPT Interrupt,
IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
IN PVOID SynchronizeContext
)
{
KIRQL OldIrql;
OldIrql = KeGetCurrentIrql ();
KfSanityCheckRaiseIrql (Interrupt->SynchronizeIrql);
MmVerifierData.SynchronizeExecutions += 1;
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
if ((OldIrql < DISPATCH_LEVEL) && (Interrupt->SynchronizeIrql >= DISPATCH_LEVEL)) {
ViTrimAllSystemPagableMemory (0);
}
}
return KeSynchronizeExecution (Interrupt,
SynchronizeRoutine,
SynchronizeContext);
}
THUNKED_API
VOID
VerifierKeInitializeTimerEx (
IN PKTIMER Timer,
IN TIMER_TYPE Type
)
{
//
// Check the object being initialized isn't already an
// active timer. Make sure the timer table list is initialized.
//
if (KiTimerTableListHead[0].Flink != NULL) {
KeCheckForTimer(Timer, sizeof(KTIMER));
}
KeInitializeTimerEx (Timer, Type);
}
THUNKED_API
VOID
VerifierKeInitializeTimer (
IN PKTIMER Timer
)
{
VerifierKeInitializeTimerEx (Timer, NotificationTimer);
}
THUNKED_API
NTSTATUS
VerifierKeWaitForSingleObject (
IN PVOID Object,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL
)
{
KIRQL WaitIrql;
PRKTHREAD Thread;
NTSTATUS Status;
BOOLEAN TryAcquire;
//
// Get the IRQL we will return from this function at.
//
Thread = KeGetCurrentThread ();
if (Thread->WaitNext == TRUE) {
//
// The real IRQL is stored in the thread if WaitNext is TRUE.
//
WaitIrql = Thread->WaitIrql;
}
else {
WaitIrql = KeGetCurrentIrql();
}
//
// Returning to code running above DISPATCH_LEVEL is never legal. Probes
// with timeout == 0 can only be done at DISPATCH_LEVEL or lower.
//
// Blocking (ie causing a context switch) in KeWaitForSingleObject
// at DPC level is also illegal, as the caller may be holding a spinlock.
//
if ((WaitIrql > DISPATCH_LEVEL) ||
((WaitIrql == DISPATCH_LEVEL) &&
(! (ARGUMENT_PRESENT(Timeout) && (Timeout->QuadPart == 0))))) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3B,
(ULONG_PTR)WaitIrql,
(ULONG_PTR)Object,
(ULONG_PTR)Timeout);
}
Status = KeWaitForSingleObject (Object,
WaitReason,
WaitMode,
Alertable,
Timeout);
//
// If we waited for a mutant then inform the deadlock verifier.
//
if (((PRKMUTANT) Object)->Header.Type == MutantObject) {
//
// Both STATUS_SUCCESS and STATUS_ABANDONED are successful
// values and we need to track an acquire() operation.
//
if ((Status == STATUS_SUCCESS) || (Status == STATUS_ABANDONED)) {
//
// If a TimeOut is present then this is equivalent with a
// try-acquire call and it cannot be involved in a deadlock.
//
if (ARGUMENT_PRESENT (Timeout)) {
TryAcquire = TRUE;
}
else {
TryAcquire = FALSE;
}
VfDeadlockAcquireResource (Object,
VfDeadlockMutex,
Thread,
TryAcquire,
_ReturnAddress ());
}
}
return Status;
}
THUNKED_API
LONG
VerifierKeReleaseMutex (
IN PRKMUTEX Mutex,
IN BOOLEAN Wait
)
{
VfDeadlockReleaseResource (Mutex,
VfDeadlockMutex,
KeGetCurrentThread (),
_ReturnAddress ());
return KeReleaseMutex (Mutex, Wait);
}
THUNKED_API
VOID
VerifierKeInitializeMutex (
IN PRKMUTEX Mutex,
IN ULONG Level
)
{
KeInitializeMutex (Mutex,Level);
VfDeadlockInitializeResource (Mutex,
VfDeadlockMutex,
_ReturnAddress (),
FALSE);
}
THUNKED_API
LONG
VerifierKeReleaseMutant (
IN PRKMUTANT Mutant,
IN KPRIORITY Increment,
IN BOOLEAN Abandoned,
IN BOOLEAN Wait
)
{
VI_DEADLOCK_RESOURCE_TYPE MutexType;
if (Abandoned) {
MutexType = VfDeadlockMutexAbandoned;
}
else {
MutexType = VfDeadlockMutex;
}
VfDeadlockReleaseResource (Mutant,
MutexType,
KeGetCurrentThread (),
_ReturnAddress());
return KeReleaseMutant (Mutant, Increment, Abandoned, Wait);
}
THUNKED_API
VOID
VerifierKeInitializeMutant (
IN PRKMUTANT Mutant,
IN BOOLEAN InitialOwner
)
{
KeInitializeMutant (Mutant, InitialOwner);
VfDeadlockInitializeResource (Mutant,
VfDeadlockMutex,
_ReturnAddress (),
FALSE);
if (InitialOwner) {
VfDeadlockAcquireResource (Mutant,
VfDeadlockMutex,
KeGetCurrentThread (),
FALSE,
_ReturnAddress ());
}
}
THUNKED_API
VOID
VerifierKeInitializeSpinLock (
IN PKSPIN_LOCK SpinLock
)
{
KeInitializeSpinLock (SpinLock);
VfDeadlockInitializeResource (SpinLock,
VfDeadlockSpinLock,
_ReturnAddress (),
FALSE);
}
NTSTATUS
VerifierReferenceObjectByHandle (
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
)
{
NTSTATUS Status;
Status = ObReferenceObjectByHandle (Handle,
DesiredAccess,
ObjectType,
AccessMode,
Object,
HandleInformation);
if ((Status == STATUS_INVALID_HANDLE) ||
(Status == STATUS_OBJECT_TYPE_MISMATCH)) {
if ((AccessMode == KernelMode) ||
(PsIsSystemThread (PsGetCurrentThread ()))) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3C,
(ULONG_PTR) Handle,
(ULONG_PTR) ObjectType,
(ULONG_PTR) 0);
}
}
return Status;
}
LONG_PTR
FASTCALL
VerifierReferenceObject (
IN PVOID Object
)
{
LONG_PTR RetVal;
RetVal = ObReferenceObject (Object);
//
// See if they passed in an object with a zero reference
//
if (RetVal == 1) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3F,
(ULONG_PTR) Object,
(ULONG_PTR) RetVal,
(ULONG_PTR) 0);
}
return RetVal;
}
LONG_PTR
VerifierDereferenceObject (
IN PVOID Object
)
{
LONG_PTR RetVal;
RetVal = ObDereferenceObject (Object);
//
// See if they passed in an object with a zero reference
//
if (RetVal + 1 == 0) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3F,
(ULONG_PTR) Object,
(ULONG_PTR) RetVal,
(ULONG_PTR) 0);
}
return RetVal;
}
VOID
VerifierLeaveCriticalRegion (
VOID
)
{
PKTHREAD CurrentThread;
CurrentThread = KeGetCurrentThread ();
if ((CurrentThread->KernelApcDisable > 0) || (CurrentThread->KernelApcDisable == 0x8000)) {
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x3E,
(ULONG_PTR) 0,
(ULONG_PTR) 0,
(ULONG_PTR) 0);
}
KeLeaveCriticalRegionThread (CurrentThread);
}
VOID
ViInitializeEntry (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier,
IN LOGICAL FirstLoad
)
/*++
Routine Description:
Initialize various verifier fields as the driver is being (re)loaded now.
Arguments:
Verifier - Supplies the verifier entry to be initialized.
FirstLoad - Supplies TRUE if this is the first load of this driver.
Return Value:
None.
--*/
{
KIRQL OldIrql;
//
// Only the BaseName field is initialized on entry.
//
Verifier->CurrentPagedPoolAllocations = 0;
Verifier->CurrentNonPagedPoolAllocations = 0;
Verifier->PeakPagedPoolAllocations = 0;
Verifier->PeakNonPagedPoolAllocations = 0;
Verifier->PagedBytes = 0;
Verifier->NonPagedBytes = 0;
Verifier->PeakPagedBytes = 0;
Verifier->PeakNonPagedBytes = 0;
Verifier->Signature = MI_VERIFIER_ENTRY_SIGNATURE;
InitializeSListHead (&Verifier->PoolPageHeaders);
InitializeSListHead (&Verifier->PoolTrackers);
if (FirstLoad == TRUE) {
Verifier->Flags = 0;
Verifier->Loads = 0;
Verifier->Unloads = 0;
}
LOCK_VERIFIER (&OldIrql);
Verifier->StartAddress = NULL;
Verifier->EndAddress = NULL;
UNLOCK_VERIFIER (OldIrql);
}
VOID
MiInitializeDriverVerifierList (
VOID
)
/*++
Routine Description:
Parse the registry settings and set up the list of driver names that will
be put through the validation process.
It is important that this list be parsed early because the machine-specific
memory management initialization needs to know whether the verifier is
going to be enabled.
Arguments:
None.
Return Value:
None.
Environment:
Kernel mode, Phase 0 Initialization.
Pool does not exist yet.
The PsLoadedModuleList has not been set up yet AND the boot drivers
have NOT been relocated to their final resting places.
--*/
{
PWCHAR Start;
PWCHAR End;
PWCHAR Walk;
ULONG NameLength;
UNICODE_STRING KernelString;
UNICODE_STRING DriverBaseName;
InitializeListHead (&MiSuspectDriverList);
if (MmVerifyDriverLevel != (ULONG)-1) {
if (MmVerifyDriverLevel & DRIVER_VERIFIER_IO_CHECKING) {
if (MmVerifyDriverBufferLength == (ULONG)-1) {
MmVerifyDriverBufferLength = 0; // Mm will not page out verifier pages.
}
}
}
//
// The only way MiVerifyAllDrivers is nonzero here is if it was patched in
// the kernel debugger. If so, just verify everything.
//
if (MiVerifyAllDrivers == 1) {
MmVerifyDriverBufferLength = 0;
}
else if (MiVerifyAllDrivers == 2) {
MiVerifyAllDrivers = 1;
KernelVerifier = TRUE;
MmVerifyDriverBufferLength = 0;
}
else if (MmVerifyDriverBufferLength == (ULONG)-1) {
return;
}
#if defined(_IA64_)
KeInitializeSpinLock (&VerifierListLock);
#endif
//
// Initializing this listhead indicates to the rest of this module that
// the system was booted with verification of some sort configured.
//
InitializeListHead (&MiVerifierDriverAddedThunkListHead);
//
// Disable lookasides so pool corruption can be found easily.
//
ExMinimumLookasideDepth = 0;
//
// Disable large pages for driver images so pool corruption can be
// found easily.
//
MmLargePageDriverBufferLength = (ULONG)-1;
//
// If no default is specified, then special pool, pagable code/data
// flushing and pool leak detection are enabled.
//
if (MmVerifyDriverLevel == (ULONG)-1) {
MmVerifierData.Level = DRIVER_VERIFIER_SPECIAL_POOLING |
DRIVER_VERIFIER_FORCE_IRQL_CHECKING |
DRIVER_VERIFIER_TRACK_POOL_ALLOCATIONS;
}
else {
MmVerifierData.Level = MmVerifyDriverLevel;
}
VerifierModifyableOptions = (DRIVER_VERIFIER_SPECIAL_POOLING |
DRIVER_VERIFIER_FORCE_IRQL_CHECKING |
DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES);
//
// An initial parse of the driver list is needed here to see if it's the
// kernel as special machine-dependent initialization is needed to fully
// support kernel verification (ie: no use of large pages, etc).
//
if ((MiVerifyAllDrivers == 0) &&
(MiVerifyRandomDrivers == (WCHAR)0)) {
#define KERNEL_NAME L"ntoskrnl.exe"
KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
KernelString.MaximumLength = sizeof KERNEL_NAME;
Start = MmVerifyDriverBuffer;
End = MmVerifyDriverBuffer + (MmVerifyDriverBufferLength - sizeof(WCHAR)) / sizeof(WCHAR);
while (Start < End) {
if (UNICODE_WHITESPACE(*Start)) {
Start += 1;
continue;
}
if (*Start == (WCHAR)'*') {
MiVerifyAllDrivers = 1;
break;
}
for (Walk = Start; Walk < End; Walk += 1) {
if (UNICODE_WHITESPACE(*Walk)) {
break;
}
}
//
// Got a string - see if it indicates the kernel.
//
NameLength = (ULONG)(Walk - Start + 1) * sizeof (WCHAR);
DriverBaseName.Buffer = Start;
DriverBaseName.Length = (USHORT)(NameLength - sizeof (UNICODE_NULL));
DriverBaseName.MaximumLength = (USHORT)NameLength;
if (RtlEqualUnicodeString (&KernelString,
&DriverBaseName,
TRUE)) {
KernelVerifier = TRUE;
break;
}
Start = Walk + 1;
}
}
if (KernelVerifier == TRUE) {
//
// AcquireAtDpc/ReleaseFromDpc calls made by the kernel are not
// intercepted which confuses the deadlock verifier. So disable
// deadlock verification if we are kernel verifying.
//
MmVerifyDriverLevel &= ~DRIVER_VERIFIER_DEADLOCK_DETECTION;
MmVerifierData.Level &= ~DRIVER_VERIFIER_DEADLOCK_DETECTION;
//
//
// All driver pool allocation calls must be intercepted so
// they are not mistaken for kernel pool allocations.
//
MiVerifyAllDrivers = 1;
ExSetPoolFlags (EX_KERNEL_VERIFIER_ENABLED);
}
return;
}
VOID
MiReApplyVerifierToLoadedModules (
IN PLIST_ENTRY ModuleListHead
)
/*++
Routine Description:
Walk the supplied module list and re-thunk any drivers that are being
verified. This allows the module to pick up any new thunks that have
been added.
Arguments:
ModuleListHead - Supplies a pointer to the head of a loaded module list.
Environment:
Kernel mode, Phase 0 Initialization only.
--*/
{
LOGICAL Skip;
PLIST_ENTRY Entry;
PKLDR_DATA_TABLE_ENTRY TableEntry;
UNICODE_STRING HalString;
UNICODE_STRING KernelString;
//
// If the thunk listhead is NULL then the verifier is not enabled so
// don't notify any components.
//
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
return;
}
//
// Initialize unicode strings to use to bypass modules
// in the list. There's no reason to reapply verifier to
// the kernel or to the hal.
//
KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
KernelString.MaximumLength = sizeof KERNEL_NAME;
#define HAL_NAME L"hal.dll"
HalString.Buffer = (const PUSHORT) HAL_NAME;
HalString.Length = sizeof (HAL_NAME) - sizeof (WCHAR);
HalString.MaximumLength = sizeof HAL_NAME;
//
// Walk the list and reapply verifier to all the modules except those
// selected for exclusion.
//
Entry = ModuleListHead->Flink;
while (Entry != ModuleListHead) {
TableEntry = CONTAINING_RECORD(Entry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
Skip = TRUE;
if (RtlEqualUnicodeString (&KernelString,
&TableEntry->BaseDllName,
TRUE)) {
NOTHING;
}
else if (RtlEqualUnicodeString (&HalString,
&TableEntry->BaseDllName,
TRUE)) {
NOTHING;
}
else {
Skip = FALSE;
}
//
// Reapply verifier thunks to the image if it is already being
// verified and if it is not one of the modules we've decided to skip.
//
if ((Skip == FALSE) && (TableEntry->Flags & LDRP_IMAGE_VERIFYING)) {
#if DBG
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
//
// Find the entry for this driver in the suspect list. This is
// expected to succeed since we are re-applying thunks to a module
// that has already been verified at least once before.
//
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
Verifier = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (RtlEqualUnicodeString (&Verifier->BaseName,
&TableEntry->BaseDllName,
TRUE)) {
ASSERT(Verifier->StartAddress == TableEntry->DllBase);
ASSERT(Verifier->EndAddress ==
(PVOID)((ULONG_PTR)TableEntry->DllBase +
TableEntry->SizeOfImage));
break;
}
NextEntry = NextEntry->Flink;
}
//
// Sanity tests. We should always find this module in the suspect
// driver list because it is already being verified. And the
// start and end addresses should still match those of this
// module.
//
ASSERT(NextEntry != &MiSuspectDriverList);
#endif
MiReEnableVerifier (TableEntry);
}
Entry = Entry->Flink;
}
}
LOGICAL
MiInitializeVerifyingComponents (
IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
Walk the loaded module list and thunk any drivers that need/deserve it.
Arguments:
LoaderBlock - Supplies the loader block used by the system to boot.
Return Value:
TRUE if successful, FALSE if not.
Environment:
Kernel mode, Phase 0 Initialization.
Nonpaged pool exists but paged pool does not.
The PsLoadedModuleList has not been set up yet although the boot drivers
have been relocated to their final resting places.
--*/
{
ULONG i;
PWCHAR Start;
PWCHAR End;
PWCHAR Walk;
ULONG NameLength;
PLIST_ENTRY NextEntry;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
PMI_VERIFIER_DRIVER_ENTRY KernelEntry;
PMI_VERIFIER_DRIVER_ENTRY HalEntry;
UNICODE_STRING HalString;
UNICODE_STRING KernelString;
PVERIFIER_THUNKS Thunk;
PDRIVER_VERIFIER_THUNK_ROUTINE PristineRoutine;
//
// If the thunk listhead is NULL then the verifier is not enabled so
// don't notify any components.
//
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
return FALSE;
}
KernelEntry = NULL;
HalEntry = NULL;
KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
KernelString.MaximumLength = sizeof KERNEL_NAME;
HalString.Buffer = (const PUSHORT) HAL_NAME;
HalString.Length = sizeof (HAL_NAME) - sizeof (WCHAR);
HalString.MaximumLength = sizeof HAL_NAME;
if (MmVerifyDriverBufferLength == 0) {
//
// Verifier was enabled in kd, handle specially...
//
ASSERT (MiVerifyAllDrivers == 1);
}
else if (MiVerifyRandomDrivers == (WCHAR)0) {
Start = MmVerifyDriverBuffer;
End = MmVerifyDriverBuffer + (MmVerifyDriverBufferLength - sizeof(WCHAR)) / sizeof(WCHAR);
while (Start < End) {
if (UNICODE_WHITESPACE(*Start)) {
Start += 1;
continue;
}
if (*Start == (WCHAR)'*') {
MiVerifyAllDrivers = 1;
break;
}
for (Walk = Start; Walk < End; Walk += 1) {
if (UNICODE_WHITESPACE(*Walk)) {
break;
}
}
//
// Got a string. Save it.
//
NameLength = (ULONG)(Walk - Start + 1) * sizeof (WCHAR);
Verifier = (PMI_VERIFIER_DRIVER_ENTRY)ExAllocatePoolWithTag (
NonPagedPool,
sizeof (MI_VERIFIER_DRIVER_ENTRY) +
NameLength,
'dLmM');
if (Verifier == NULL) {
break;
}
Verifier->BaseName.Buffer = (PWSTR)((PCHAR)Verifier +
sizeof (MI_VERIFIER_DRIVER_ENTRY));
Verifier->BaseName.Length = (USHORT)(NameLength - sizeof (UNICODE_NULL));
Verifier->BaseName.MaximumLength = (USHORT)NameLength;
RtlCopyMemory (Verifier->BaseName.Buffer,
Start,
NameLength - sizeof (UNICODE_NULL));
ViInitializeEntry (Verifier, TRUE);
Verifier->Flags |= VI_VERIFYING_DIRECTLY;
ViInsertVerifierEntry (Verifier);
if (RtlEqualUnicodeString (&KernelString,
&Verifier->BaseName,
TRUE)) {
//
// All driver pool allocation calls must be intercepted so
// they are not mistaken for kernel pool allocations.
//
ASSERT (MiVerifyAllDrivers == 1);
ASSERT (KernelVerifier == TRUE);
KernelEntry = Verifier;
}
else if (RtlEqualUnicodeString (&HalString,
&Verifier->BaseName,
TRUE)) {
HalEntry = Verifier;
}
Start = Walk + 1;
}
}
//
// Enable deadlock detection if the deadlock bit was set in the
// registry.
//
if (MmVerifierData.Level & DRIVER_VERIFIER_DEADLOCK_DETECTION) {
#if !defined(_AMD64_) || !defined(NT_UP)
//
// Because the AMD64 versions of KeAcquireSpinLockAtDpc
// and KeReleaseSpinlockFromDPc routines are written in
// C, the UP build where these functions are no-ops end up getting resolved
// by the linker into a single no-op routine. The verifier engine as
// a temporary workaround does not hook these routines. This can cause
// false positives in deadlock verifier if normal spinlock routines are
// paired with atdpc/fromdpc routines.
//
// Until this gets solved in the verifier engine code that redirects thunks
// we will disable deadlock verifier on these machines.
//
VfDeadlockDetectionInitialize (MiVerifyAllDrivers, KernelVerifier);
ExSetPoolFlags (EX_VERIFIER_DEADLOCK_DETECTION_ENABLED);
#endif
}
//
// Initialize i/o verifier.
//
IoVerifierInit (MmVerifierData.Level);
if (MiTriageAddDrivers (LoaderBlock) == TRUE) {
//
// Disable random driver verification if triage has picked driver(s).
//
MiVerifyRandomDrivers = (WCHAR)0;
}
Thunk = (PVERIFIER_THUNKS) &MiVerifierThunks[0];
while (Thunk->PristineRoutineAsciiName != NULL) {
PristineRoutine = MiResolveVerifierExports (LoaderBlock,
Thunk->PristineRoutineAsciiName);
ASSERT (PristineRoutine != NULL);
Thunk->PristineRoutine = PristineRoutine;
Thunk += 1;
}
Thunk = (PVERIFIER_THUNKS) &MiVerifierPoolThunks[0];
while (Thunk->PristineRoutineAsciiName != NULL) {
PristineRoutine = MiResolveVerifierExports (LoaderBlock,
Thunk->PristineRoutineAsciiName);
ASSERT (PristineRoutine != NULL);
Thunk->PristineRoutine = PristineRoutine;
Thunk += 1;
}
//
// Process the boot-loaded drivers now.
//
i = 0;
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
//
// Process the kernel and HAL specially.
//
if (i == 0) {
if ((KernelEntry != NULL) || (KernelVerifier)) {
MiApplyDriverVerifier (DataTableEntry, KernelEntry);
}
}
else if (i == 1) {
if ((HalEntry != NULL) || (MiVerifyAllDrivers == 1)) {
MiApplyDriverVerifier (DataTableEntry, HalEntry);
}
}
else {
MiApplyDriverVerifier (DataTableEntry, NULL);
}
i += 1;
}
//
// Initialize irql tracking package. The drivers that will be verified
// will have automatically tracked all their raise/lower irql operations.
//
ViTrackIrqlInitialize ();
#if defined(_X86_) || defined(_AMD64_)
//
// Initialize fault injection stack trace log package.
//
ViFaultTracesInitialize ();
#endif
return TRUE;
}
NTSTATUS
MmAddVerifierEntry (
IN PUNICODE_STRING ImageFileName
)
/*++
Routine Description:
This routine inserts a new verifier entry for the specified driver so that
when the driver is loaded it will automatically be verified.
Note that if the driver is already loaded, then no entry is added and
STATUS_IMAGE_ALREADY_LOADED is returned.
If the system was booted with an empty verifier list, then no entries can
be added now as the current system configuration will not support special
pool, etc.
Note also that no registry changes are made so any insertions made by this
routine are lost on reboot.
Arguments:
ImageFileName - Supplies the name of the desired driver.
Return Value:
Various NTSTATUS codes.
Environment:
Kernel mode, PASSIVE_LEVEL, arbitrary process context.
--*/
{
PKTHREAD CurrentThread;
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
PMI_VERIFIER_DRIVER_ENTRY VerifierEntry;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// If the system was not booted with verification on, then bail.
//
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
return STATUS_NOT_SUPPORTED;
}
//
// First build up a verifier entry.
//
Verifier = (PMI_VERIFIER_DRIVER_ENTRY)ExAllocatePoolWithTag (
NonPagedPool,
sizeof (MI_VERIFIER_DRIVER_ENTRY) +
ImageFileName->MaximumLength,
'dLmM');
if (Verifier == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
Verifier->BaseName.Buffer = (PWSTR)((PCHAR)Verifier +
sizeof (MI_VERIFIER_DRIVER_ENTRY));
Verifier->BaseName.Length = ImageFileName->Length;
Verifier->BaseName.MaximumLength = ImageFileName->MaximumLength;
RtlCopyMemory (Verifier->BaseName.Buffer,
ImageFileName->Buffer,
ImageFileName->Length);
ViInitializeEntry (Verifier, TRUE);
Verifier->Flags |= VI_VERIFYING_DIRECTLY;
//
// Arbitrary process context so prevent suspend APCs now.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
//
// Acquire the load lock so the verifier list can be read.
// Then ensure that the specified driver is not already in the list.
//
KeWaitForSingleObject (&MmSystemLoadLock,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
//
// Check to make sure the requested entry is not already present in
// the verifier list and that the driver is not currently loaded.
//
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
VerifierEntry = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (RtlEqualUnicodeString (&Verifier->BaseName,
&VerifierEntry->BaseName,
TRUE)) {
//
// The driver is already in the verifier list - just mark the
// entry as verification-enabled and free the temporary allocation.
//
if ((VerifierEntry->Loads > VerifierEntry->Unloads) &&
(VerifierEntry->Flags & VI_DISABLE_VERIFICATION)) {
//
// The driver is loaded and verification is disabled. Don't
// turn it on now because we don't want to mislead our caller.
//
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (Verifier);
return STATUS_IMAGE_ALREADY_LOADED;
}
VerifierEntry->Flags &= ~VI_DISABLE_VERIFICATION;
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (Verifier);
return STATUS_SUCCESS;
}
NextEntry = NextEntry->Flink;
}
//
// A new verifier entry will need to be added so check to
// make sure the specified driver is not already loaded.
//
ExAcquireResourceSharedLite (&PsLoadedModuleResource, TRUE);
NextEntry = PsLoadedModuleList.Flink;
while (NextEntry != &PsLoadedModuleList) {
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
if (RtlEqualUnicodeString (&Verifier->BaseName,
&DataTableEntry->BaseDllName,
TRUE)) {
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
ExReleaseResourceLite (&PsLoadedModuleResource);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (Verifier);
return STATUS_IMAGE_ALREADY_LOADED;
}
NextEntry = NextEntry->Flink;
}
//
// The entry is not already in the verifier list and the driver is not
// currently loaded. Proceed to insert it now.
//
ViInsertVerifierEntry (Verifier);
ExReleaseResourceLite (&PsLoadedModuleResource);
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
return STATUS_SUCCESS;
}
NTSTATUS
MmRemoveVerifierEntry (
IN PUNICODE_STRING ImageFileName
)
/*++
Routine Description:
This routine doesn't actually remove the verifier entry for the
specified driver as we don't want to lose any valuable information
already gathered on the driver if it was previously loaded.
Instead, this routine disables verification for this driver for future
loads.
Note that if the driver is already loaded, then the removal is not
performed and STATUS_IMAGE_ALREADY_LOADED is returned.
Note also that no registry changes are made so any removals made by this
routine are lost on reboot.
Arguments:
ImageFileName - Supplies the name of the desired driver.
Return Value:
Various NTSTATUS codes.
Environment:
Kernel mode, PASSIVE_LEVEL, arbitrary process context.
--*/
{
PKTHREAD CurrentThread;
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY VerifierEntry;
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// If the system was not booted with verification on, then bail.
//
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
return STATUS_NOT_SUPPORTED;
}
//
// Arbitrary process context so prevent suspend APCs now.
//
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
//
// Acquire the load lock so the verifier list can be read.
// Then ensure that the specified driver is not already in the list.
//
KeWaitForSingleObject (&MmSystemLoadLock,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
//
// Check to make sure the requested entry is not already present in
// the verifier list and that the driver is not currently loaded.
//
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
VerifierEntry = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (RtlEqualUnicodeString (ImageFileName,
&VerifierEntry->BaseName,
TRUE)) {
//
// The driver is already in the verifier list - just mark the
// entry as verification-enabled and free the temporary allocation.
// No need to check the loaded module list if the entry is already
// in the verifier list.
//
if ((VerifierEntry->Loads > VerifierEntry->Unloads) &&
((VerifierEntry->Flags & VI_DISABLE_VERIFICATION) == 0)) {
//
// The driver is loaded and verification is enabled. Don't
// disable it now because we don't want to mislead our caller.
//
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
return STATUS_IMAGE_ALREADY_LOADED;
}
VerifierEntry->Flags |= VI_DISABLE_VERIFICATION;
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
return STATUS_SUCCESS;
}
NextEntry = NextEntry->Flink;
}
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
return STATUS_NOT_FOUND;
}
VOID
ViInsertVerifierEntry (
IN PMI_VERIFIER_DRIVER_ENTRY Verifier
)
/*++
Routine Description:
Nonpagable wrapper to insert a new verifier entry.
Note that the system load mutant or the verifier load spinlock is sufficient
for readers to access the list. This is because the insertion path
acquires both.
Lock synchronization is needed because pool allocators walk the
verifier list at DISPATCH_LEVEL.
Arguments:
Verifier - Supplies a caller-initialized entry for the driver.
Return Value:
None.
--*/
{
KIRQL OldIrql;
LOCK_VERIFIER (&OldIrql);
InsertTailList (&MiSuspectDriverList, &Verifier->Links);
UNLOCK_VERIFIER (OldIrql);
}
PMI_VERIFIER_DRIVER_ENTRY
ViLocateVerifierEntry (
IN PVOID SystemAddress
)
/*++
Routine Description:
Locate the Driver Verifier entry for the specified system address.
Arguments:
SystemAddress - Supplies a code or data address within a driver.
Return Value:
The Verifier entry corresponding to the driver or NULL.
Environment:
The caller may be at DISPATCH_LEVEL and does not hold the MmSystemLoadLock.
--*/
{
KIRQL OldIrql;
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
LOCK_VERIFIER (&OldIrql);
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
Verifier = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if ((SystemAddress >= Verifier->StartAddress) &&
(SystemAddress < Verifier->EndAddress)) {
UNLOCK_VERIFIER (OldIrql);
return Verifier;
}
NextEntry = NextEntry->Flink;
}
UNLOCK_VERIFIER (OldIrql);
return NULL;
}
LOGICAL
MiApplyDriverVerifier (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry,
IN PMI_VERIFIER_DRIVER_ENTRY Verifier
)
/*++
Routine Description:
This function is called as each module is loaded. If the module being
loaded is in the suspect list, thunk it here.
Arguments:
DataTableEntry - Supplies the data table entry for the module.
Verifier - Non-NULL if verification must be applied. FALSE indicates
that the driver name must match for verification to be applied.
Return Value:
TRUE if thunking was applied, FALSE if not.
Environment:
Kernel mode, Phase 0 Initialization and normal runtime.
Non paged pool exists in Phase0, but paged pool does not.
Post-Phase0 serialization is provided by the MmSystemLoadLock.
--*/
{
WCHAR FirstChar;
LOGICAL Found;
PLIST_ENTRY NextEntry;
ULONG VerifierFlags;
UNICODE_STRING KernelString;
if (Verifier != NULL) {
Found = TRUE;
}
else {
Found = FALSE;
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
Verifier = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (RtlEqualUnicodeString (&Verifier->BaseName,
&DataTableEntry->BaseDllName,
TRUE)) {
Found = TRUE;
ViInitializeEntry (Verifier, FALSE);
break;
}
NextEntry = NextEntry->Flink;
}
}
if (Found == FALSE) {
VerifierFlags = VI_VERIFYING_DIRECTLY;
if (MiVerifyAllDrivers != 0) {
if (KernelVerifier == TRUE) {
KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
KernelString.MaximumLength = sizeof KERNEL_NAME;
if (!RtlEqualUnicodeString (&KernelString,
&DataTableEntry->BaseDllName,
TRUE)) {
VerifierFlags = VI_VERIFYING_INVERSELY;
}
}
Found = TRUE;
}
else if (MiVerifyRandomDrivers != (WCHAR)0) {
//
// Wildcard match drivers randomly.
//
FirstChar = RtlUpcaseUnicodeChar(DataTableEntry->BaseDllName.Buffer[0]);
if (MiVerifyRandomDrivers == FirstChar) {
Found = TRUE;
}
else if (MiVerifyRandomDrivers == (WCHAR)'X') {
if ((FirstChar >= (WCHAR)'0') && (FirstChar <= (WCHAR)'9')) {
Found = TRUE;
}
}
}
if (Found == FALSE) {
return FALSE;
}
Verifier = (PMI_VERIFIER_DRIVER_ENTRY)ExAllocatePoolWithTag (
NonPagedPool,
sizeof (MI_VERIFIER_DRIVER_ENTRY) +
DataTableEntry->BaseDllName.MaximumLength,
'dLmM');
if (Verifier == NULL) {
return FALSE;
}
Verifier->BaseName.Buffer = (PWSTR)((PCHAR)Verifier +
sizeof (MI_VERIFIER_DRIVER_ENTRY));
Verifier->BaseName.Length = DataTableEntry->BaseDllName.Length;
Verifier->BaseName.MaximumLength = DataTableEntry->BaseDllName.MaximumLength;
RtlCopyMemory (Verifier->BaseName.Buffer,
DataTableEntry->BaseDllName.Buffer,
DataTableEntry->BaseDllName.Length);
ViInitializeEntry (Verifier, TRUE);
Verifier->Flags = VerifierFlags;
ViInsertVerifierEntry (Verifier);
}
Verifier->StartAddress = DataTableEntry->DllBase;
Verifier->EndAddress = (PVOID)((ULONG_PTR)DataTableEntry->DllBase + DataTableEntry->SizeOfImage);
ASSERT (Found == TRUE);
if (Verifier->Flags & VI_DISABLE_VERIFICATION) {
//
// We've been instructed to not verify this driver. If kernel
// verification is enabled, then the driver must still be thunked
// for "inverse-verification". If kernel verification is disabled,
// nothing needs to be done here except load/unload counting.
//
if (KernelVerifier == TRUE) {
Found = MiEnableVerifier (DataTableEntry);
}
else {
Found = FALSE;
}
}
else {
Found = MiEnableVerifier (DataTableEntry);
}
if (Found == TRUE) {
if (Verifier->Flags & VI_VERIFYING_DIRECTLY &&
((DataTableEntry->Flags & LDRP_IMAGE_VERIFYING) == 0)) {
ViPrintString (&DataTableEntry->BaseDllName);
}
MmVerifierData.Loads += 1;
Verifier->Loads += 1;
DataTableEntry->Flags |= LDRP_IMAGE_VERIFYING;
MiActiveVerifies += 1;
if (MiActiveVerifies == 1) {
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
//
// Page out all thread stacks as soon as possible to
// catch drivers using local events that do usermode waits.
//
if (KernelVerifier == FALSE) {
MiVerifierStackProtectTime = KiStackProtectTime;
KiStackProtectTime = 0;
}
}
}
}
return Found;
}
PUNICODE_STRING ViBadDriver;
VOID
MiVerifyingDriverUnloading (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
)
/*++
Routine Description:
This function is called as a driver that was being verified is now being
unloaded.
Arguments:
DataTableEntry - Supplies the data table entry for the driver.
Return Value:
TRUE if thunking was applied, FALSE if not.
Environment:
Kernel mode, Phase 0 Initialization and normal runtime.
Non paged pool exists in Phase0, but paged pool does not.
Post-Phase0 serialization is provided by the MmSystemLoadLock.
--*/
{
PVOID FullPage;
KIRQL OldIrql;
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
Verifier = CONTAINING_RECORD (NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (RtlEqualUnicodeString (&Verifier->BaseName,
&DataTableEntry->BaseDllName,
TRUE)) {
//
// Delete any static locks in the driver image.
//
VfDeadlockDeleteMemoryRange (DataTableEntry->DllBase,
(SIZE_T) DataTableEntry->SizeOfImage);
if (MmVerifierData.Level & DRIVER_VERIFIER_TRACK_POOL_ALLOCATIONS) {
//
// Better not be any pool left that wasn't freed.
//
if (Verifier->PagedBytes) {
#if DBG
DbgPrint ("Driver %wZ leaked %d paged pool allocations (0x%x bytes)\n",
&DataTableEntry->FullDllName,
Verifier->CurrentPagedPoolAllocations,
Verifier->PagedBytes);
#endif
//
// It would be nice to fault in the driver's paged pool
// allocations now to make debugging easier, but this
// cannot be easily done in a deadlock free manner.
//
// At least disable the paging of pool on IRQL raising
// in attempt to keep some of these allocations resident
// for debugging.
//
// No need to undo the increment as we're about to
// bugcheck anyway.
//
InterlockedIncrement ((PLONG)&MiNoPageOnRaiseIrql);
}
#if DBG
if (Verifier->NonPagedBytes) {
DbgPrint ("Driver %wZ leaked %d nonpaged pool allocations (0x%x bytes)\n",
&DataTableEntry->FullDllName,
Verifier->CurrentNonPagedPoolAllocations,
Verifier->NonPagedBytes);
}
#endif
if (Verifier->PagedBytes || Verifier->NonPagedBytes) {
ViBadDriver = &Verifier->BaseName;
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
0x60,
Verifier->PagedBytes,
Verifier->NonPagedBytes,
Verifier->CurrentPagedPoolAllocations +
Verifier->CurrentNonPagedPoolAllocations);
}
//
// Free all pages (if any) used to track the pool allocations.
//
do {
FullPage = InterlockedPopEntrySList (&Verifier->PoolPageHeaders);
if (FullPage != NULL) {
ExFreePool (FullPage);
}
} while (FullPage != NULL);
//
// Clear these fields so reuse of stale addresses don't trigger
// erroneous bucket fills.
//
LOCK_VERIFIER (&OldIrql);
Verifier->StartAddress = NULL;
Verifier->EndAddress = NULL;
UNLOCK_VERIFIER (OldIrql);
}
Verifier->Unloads += 1;
MmVerifierData.Unloads += 1;
MiActiveVerifies -= 1;
if (MiActiveVerifies == 0) {
if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) {
//
// Return to normal thread stack protection.
//
if (KernelVerifier == FALSE) {
KiStackProtectTime = MiVerifierStackProtectTime;
}
}
}
return;
}
NextEntry = NextEntry->Flink;
}
ASSERT (FALSE);
}
NTKERNELAPI
LOGICAL
MmIsDriverVerifying (
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This function informs the caller if the argument driver is being verified.
Arguments:
DriverObject - Supplies the driver object.
Return Value:
TRUE if this driver is being verified, FALSE if not.
Environment:
Kernel mode, any IRQL, any needed synchronization must be provided by the
caller.
--*/
{
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
if (DataTableEntry == NULL) {
return FALSE;
}
if ((DataTableEntry->Flags & LDRP_IMAGE_VERIFYING) == 0) {
return FALSE;
}
return TRUE;
}
NTSTATUS
MmAddVerifierThunks (
IN PVOID ThunkBuffer,
IN ULONG ThunkBufferSize
)
/*++
Routine Description:
This routine adds another set of thunks to the verifier list.
Arguments:
ThunkBuffer - Supplies the buffer containing the thunk pairs.
ThunkBufferSize - Supplies the number of bytes in the thunk buffer.
Return Value:
Returns the status of the operation.
Environment:
Kernel mode. APC_LEVEL and below, arbitrary process context.
--*/
{
ULONG i;
PKTHREAD CurrentThread;
ULONG NumberOfThunkPairs;
PDRIVER_VERIFIER_THUNK_PAIRS ThunkPairs;
PDRIVER_VERIFIER_THUNK_PAIRS ThunkTable;
PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
PLIST_ENTRY NextEntry;
PVOID DriverStartAddress;
PVOID DriverEndAddress;
PAGED_CODE();
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
return STATUS_NOT_SUPPORTED;
}
ThunkPairs = (PDRIVER_VERIFIER_THUNK_PAIRS)ThunkBuffer;
NumberOfThunkPairs = ThunkBufferSize / sizeof(DRIVER_VERIFIER_THUNK_PAIRS);
if (NumberOfThunkPairs == 0) {
return STATUS_INVALID_PARAMETER_1;
}
ThunkTableBase = (PDRIVER_SPECIFIED_VERIFIER_THUNKS) ExAllocatePoolWithTag (
PagedPool,
sizeof (DRIVER_SPECIFIED_VERIFIER_THUNKS) + NumberOfThunkPairs * sizeof (DRIVER_VERIFIER_THUNK_PAIRS),
'tVmM');
if (ThunkTableBase == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ThunkTable = (PDRIVER_VERIFIER_THUNK_PAIRS)(ThunkTableBase + 1);
RtlCopyMemory (ThunkTable,
ThunkPairs,
NumberOfThunkPairs * sizeof(DRIVER_VERIFIER_THUNK_PAIRS));
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
KeWaitForSingleObject (&MmSystemLoadLock,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
//
// Find and validate the image that contains the routines to be thunked.
//
DataTableEntry = MiLookupDataTableEntry ((PVOID)(ULONG_PTR)ThunkTable->PristineRoutine,
TRUE);
if (DataTableEntry == NULL) {
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (ThunkTableBase);
return STATUS_INVALID_PARAMETER_2;
}
DriverStartAddress = (PVOID)(DataTableEntry->DllBase);
DriverEndAddress = (PVOID)((PCHAR)DataTableEntry->DllBase + DataTableEntry->SizeOfImage);
//
// Don't let drivers hook calls to kernel or HAL routines.
//
i = 0;
NextEntry = PsLoadedModuleList.Flink;
while (NextEntry != &PsLoadedModuleList) {
DataTableEntry2 = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
if (DataTableEntry == DataTableEntry2) {
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (ThunkTableBase);
return STATUS_INVALID_PARAMETER_2;
}
NextEntry = NextEntry->Flink;
i += 1;
if (i >= 2) {
break;
}
}
for (i = 0; i < NumberOfThunkPairs; i += 1) {
//
// Ensure all the routines being thunked are in the same driver.
//
if (((ULONG_PTR)ThunkTable->PristineRoutine < (ULONG_PTR)DriverStartAddress) ||
((ULONG_PTR)ThunkTable->PristineRoutine >= (ULONG_PTR)DriverEndAddress) ||
((ULONG_PTR)ThunkTable->NewRoutine < (ULONG_PTR)DriverStartAddress) ||
((ULONG_PTR)ThunkTable->NewRoutine >= (ULONG_PTR)DriverEndAddress)
) {
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
ExFreePool (ThunkTableBase);
return STATUS_INVALID_PARAMETER_2;
}
ThunkTable += 1;
}
//
// Add the validated thunk table to the verifier's global list.
//
ThunkTableBase->DataTableEntry = DataTableEntry;
ThunkTableBase->NumberOfThunks = NumberOfThunkPairs;
MiActiveVerifierThunks += 1;
InsertTailList (&MiVerifierDriverAddedThunkListHead,
&ThunkTableBase->ListEntry);
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
//
// Indicate that new thunks have been added to the verifier list.
//
MiVerifierThunksAdded += 1;
return STATUS_SUCCESS;
}
VOID
MiVerifierCheckThunks (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
)
/*++
Routine Description:
This routine adds another set of thunks to the verifier list.
Arguments:
DataTableEntry - Supplies the data table entry for the driver.
Return Value:
None.
Environment:
Kernel mode. APC_LEVEL and below.
The system load lock must be held by the caller.
--*/
{
PLIST_ENTRY NextEntry;
PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase;
PAGED_CODE ();
//
// N.B. The DataTableEntry can move (see MiInitializeLoadedModuleList),
// but this only happens long before IoInitialize so this is safe.
//
NextEntry = MiVerifierDriverAddedThunkListHead.Flink;
while (NextEntry != &MiVerifierDriverAddedThunkListHead) {
ThunkTableBase = CONTAINING_RECORD(NextEntry,
DRIVER_SPECIFIED_VERIFIER_THUNKS,
ListEntry);
if (ThunkTableBase->DataTableEntry == DataTableEntry) {
RemoveEntryList (NextEntry);
NextEntry = NextEntry->Flink;
ExFreePool (ThunkTableBase);
MiActiveVerifierThunks -= 1;
//
// Keep looking as the driver may have made multiple calls.
//
continue;
}
NextEntry = NextEntry->Flink;
}
}
NTSTATUS
MmIsVerifierEnabled (
OUT PULONG VerifierFlags
)
/*++
Routine Description:
This routine is called by drivers to query whether the Driver Verifier
is enabled and to find out what the currently enabled options are.
Arguments:
VerifierFlags - Returns the current driver verifier flags. Note these
flags can change dynamically without rebooting.
Return Value:
Returns STATUS_SUCCESS if the verifier is enabled, or a failure code if not.
Environment:
Kernel mode, any level prior to Phase 1, PASSIVE_LEVEL after that.
--*/
{
if (MiVerifierDriverAddedThunkListHead.Flink == NULL) {
*VerifierFlags = 0;
return STATUS_NOT_SUPPORTED;
}
*VerifierFlags = MmVerifierData.Level;
return STATUS_SUCCESS;
}
#define ROUND_UP(VALUE,ROUND) ((ULONG)(((ULONG)VALUE + \
((ULONG)ROUND - 1L)) & (~((ULONG)ROUND - 1L))))
NTSTATUS
MmGetVerifierInformation (
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG Length
)
/*++
Routine Description:
This routine returns information about drivers undergoing verification.
Arguments:
SystemInformation - Returns the driver verification information.
SystemInformationLength - Supplies the length of the SystemInformation
buffer.
Length - Returns the length of the driver verification file information
placed in the buffer.
Return Value:
Returns the status of the operation.
Environment:
The SystemInformation buffer is in user space and our caller has wrapped
a try-except around this entire routine. Capture any exceptions here and
release resources accordingly.
--*/
{
PKTHREAD CurrentThread;
PSYSTEM_VERIFIER_INFORMATION UserVerifyBuffer;
ULONG NextEntryOffset;
ULONG TotalSize;
NTSTATUS Status;
PLIST_ENTRY NextEntry;
PMI_VERIFIER_DRIVER_ENTRY Verifier;
UNICODE_STRING UserBufferDriverName;
PAGED_CODE();
NextEntryOffset = 0;
TotalSize = 0;
*Length = 0;
UserVerifyBuffer = (PSYSTEM_VERIFIER_INFORMATION)SystemInformation;
//
// Capture the number of verifying drivers and the relevant data while
// synchronized. Then return it to our caller.
//
Status = STATUS_SUCCESS;
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
KeWaitForSingleObject (&MmSystemLoadLock,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
try {
NextEntry = MiSuspectDriverList.Flink;
while (NextEntry != &MiSuspectDriverList) {
Verifier = CONTAINING_RECORD(NextEntry,
MI_VERIFIER_DRIVER_ENTRY,
Links);
if (((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0) ||
(Verifier->Flags & VI_DISABLE_VERIFICATION)) {
NextEntry = NextEntry->Flink;
continue;
}
UserVerifyBuffer = (PSYSTEM_VERIFIER_INFORMATION)(
(PUCHAR)UserVerifyBuffer + NextEntryOffset);
NextEntryOffset = sizeof(SYSTEM_VERIFIER_INFORMATION);
TotalSize += sizeof(SYSTEM_VERIFIER_INFORMATION);
if (TotalSize > SystemInformationLength) {
ExRaiseStatus (STATUS_INFO_LENGTH_MISMATCH);
}
//
// This data is cumulative for all drivers.
//
UserVerifyBuffer->Level = MmVerifierData.Level;
UserVerifyBuffer->RaiseIrqls = MmVerifierData.RaiseIrqls;
UserVerifyBuffer->AcquireSpinLocks = MmVerifierData.AcquireSpinLocks;
UserVerifyBuffer->UnTrackedPool = MmVerifierData.UnTrackedPool;
UserVerifyBuffer->SynchronizeExecutions = MmVerifierData.SynchronizeExecutions;
UserVerifyBuffer->AllocationsAttempted = MmVerifierData.AllocationsAttempted;
UserVerifyBuffer->AllocationsSucceeded = MmVerifierData.AllocationsSucceeded;
UserVerifyBuffer->AllocationsSucceededSpecialPool = MmVerifierData.AllocationsSucceededSpecialPool;
UserVerifyBuffer->AllocationsWithNoTag = MmVerifierData.AllocationsWithNoTag;
UserVerifyBuffer->TrimRequests = MmVerifierData.TrimRequests;
UserVerifyBuffer->Trims = MmVerifierData.Trims;
UserVerifyBuffer->AllocationsFailed = MmVerifierData.AllocationsFailed;
UserVerifyBuffer->AllocationsFailedDeliberately = MmVerifierData.AllocationsFailedDeliberately;
//
// This data is kept on a per-driver basis.
//
UserVerifyBuffer->CurrentPagedPoolAllocations = Verifier->CurrentPagedPoolAllocations;
UserVerifyBuffer->CurrentNonPagedPoolAllocations = Verifier->CurrentNonPagedPoolAllocations;
UserVerifyBuffer->PeakPagedPoolAllocations = Verifier->PeakPagedPoolAllocations;
UserVerifyBuffer->PeakNonPagedPoolAllocations = Verifier->PeakNonPagedPoolAllocations;
UserVerifyBuffer->PagedPoolUsageInBytes = Verifier->PagedBytes;
UserVerifyBuffer->NonPagedPoolUsageInBytes = Verifier->NonPagedBytes;
UserVerifyBuffer->PeakPagedPoolUsageInBytes = Verifier->PeakPagedBytes;
UserVerifyBuffer->PeakNonPagedPoolUsageInBytes = Verifier->PeakNonPagedBytes;
UserVerifyBuffer->Loads = Verifier->Loads;
UserVerifyBuffer->Unloads = Verifier->Unloads;
//
// The DriverName portion of the UserVerifyBuffer must be saved
// locally to protect against a malicious thread changing the
// contents. This is because we will reference the contents
// ourselves when the actual string is copied out carefully below.
//
UserBufferDriverName.Length = Verifier->BaseName.Length;
UserBufferDriverName.MaximumLength = (USHORT)(Verifier->BaseName.Length + sizeof (WCHAR));
UserBufferDriverName.Buffer = (PWCHAR)(UserVerifyBuffer + 1);
UserVerifyBuffer->DriverName = UserBufferDriverName;
TotalSize += ROUND_UP (UserBufferDriverName.MaximumLength,
sizeof(PVOID));
NextEntryOffset += ROUND_UP (UserBufferDriverName.MaximumLength,
sizeof(PVOID));
if (TotalSize > SystemInformationLength) {
ExRaiseStatus (STATUS_INFO_LENGTH_MISMATCH);
}
//
// Carefully reference the UserVerifyBuffer here.
//
RtlCopyMemory(UserBufferDriverName.Buffer,
Verifier->BaseName.Buffer,
Verifier->BaseName.Length);
UserBufferDriverName.Buffer[
Verifier->BaseName.Length/sizeof(WCHAR)] = UNICODE_NULL;
UserVerifyBuffer->NextEntryOffset = NextEntryOffset;
NextEntry = NextEntry->Flink;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
if (Status != STATUS_INFO_LENGTH_MISMATCH) {
UserVerifyBuffer->NextEntryOffset = 0;
*Length = TotalSize;
}
return Status;
}
NTSTATUS
MmSetVerifierInformation (
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength
)
/*++
Routine Description:
This routine sets any driver verifier flags that can be done without
rebooting.
Arguments:
SystemInformation - Gets and returns the driver verification flags.
SystemInformationLength - Supplies the length of the SystemInformation
buffer.
Return Value:
Returns the status of the operation.
Environment:
The SystemInformation buffer is in user space and our caller has wrapped
a try-except around this entire routine. Capture any exceptions here and
release resources accordingly.
--*/
{
PKTHREAD CurrentThread;
ULONG UserFlags;
ULONG NewFlags;
ULONG NewFlagsOn;
ULONG NewFlagsOff;
NTSTATUS Status;
PULONG UserVerifyBuffer;
PAGED_CODE();
if (SystemInformationLength < sizeof (ULONG)) {
ExRaiseStatus (STATUS_INFO_LENGTH_MISMATCH);
}
UserVerifyBuffer = (PULONG)SystemInformation;
//
// Synchronize all changes to the flags here.
//
Status = STATUS_SUCCESS;
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
KeWaitForSingleObject (&MmSystemLoadLock,
WrVirtualMemory,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
try {
UserFlags = *UserVerifyBuffer;
//
// Ensure nothing is being set or cleared that isn't supported.
//
//
NewFlagsOn = UserFlags & VerifierModifyableOptions;
NewFlags = MmVerifierData.Level | NewFlagsOn;
//
// Any bits set in NewFlagsOff must be zeroed in the NewFlags.
//
NewFlagsOff = ((~UserFlags) & VerifierModifyableOptions);
NewFlags &= ~NewFlagsOff;
if (NewFlags != MmVerifierData.Level) {
VerifierOptionChanges += 1;
MmVerifierData.Level = NewFlags;
*UserVerifyBuffer = NewFlags;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
KeLeaveCriticalRegionThread (CurrentThread);
return Status;
}
typedef struct _VERIFIER_STRING_INFO {
ULONG BuildNumber;
ULONG DriverVerifierLevel;
ULONG Flags;
ULONG Check;
} VERIFIER_STRING_INFO, *PVERIFIER_STRING_INFO;
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
static const WCHAR Printable[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static const ULONG PrintableChars = sizeof (Printable) / sizeof (Printable[0]) - 1;
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
//
// Verifier string is now available via http://winweb/drivercheck
// This site won't give out the verifier string unless certain tests
// have been ran.
//
#define PRINT_VERIFIER_STRING 0
VOID
ViPrintString (
IN PUNICODE_STRING DriverName
)
/*++
Routine Description:
This routine does a really bad hash of build number, verifier level and
flags by using the driver name as a stream of bytes to XOR into the flags,
etc.
This is a Neill Clift special.
Arguments:
DriverName - Supplies the name of the driver.
Return Value:
None.
--*/
{
#if PRINT_VERIFIER_STRING
VERIFIER_STRING_INFO Bld;
PUCHAR BufPtr;
PWCHAR DriverPtr;
ULONG BufLen;
ULONG i;
ULONG j;
ULONG DriverChars;
ULONG MaxChars;
WCHAR OutBuf[sizeof (VERIFIER_STRING_INFO) * 2 + 1];
UNICODE_STRING OutBufU;
ULONG Rem;
ULONG LastRem;
LOGICAL Done;
Bld.BuildNumber = NtBuildNumber;
Bld.DriverVerifierLevel = MmVerifierData.Level;
//
// Unloads and other actions could be encoded in the Flags field here.
//
Bld.Flags = 0;
//
// Make the last ULONG a weird function of the others.
//
Bld.Check = ((Bld.Flags + 1) * Bld.BuildNumber * (Bld.DriverVerifierLevel + 1)) * 123456789;
BufPtr = (PUCHAR) &Bld;
BufLen = sizeof (Bld);
DriverChars = DriverName->Length / sizeof (DriverName->Buffer[0]);
DriverPtr = DriverName->Buffer;
MaxChars = DriverChars;
if (DriverChars < sizeof (VERIFIER_STRING_INFO)) {
MaxChars = sizeof (VERIFIER_STRING_INFO);
}
//
// Xor each character in the driver name into the buffer.
//
for (i = 0; i < MaxChars; i += 1) {
BufPtr[i % BufLen] ^= (UCHAR) RtlUpcaseUnicodeChar(DriverPtr[i % DriverChars]);
}
//
// Produce a base N decoding of the binary buffer using the printable
// characters defines. Treat the binary as a byte array and do the
// division for each, tracking the carry.
//
j = 0;
do {
Done = TRUE;
for (i = 0, LastRem = 0; i < sizeof (VERIFIER_STRING_INFO); i += 1) {
Rem = BufPtr[i] + 256 * LastRem;
BufPtr[i] = (UCHAR) (Rem / PrintableChars);
LastRem = Rem % PrintableChars;
if (BufPtr[i]) {
Done = FALSE;
}
}
OutBuf[j++] = Printable[LastRem];
if (j >= sizeof (OutBuf) / sizeof (OutBuf[0])) {
//
// The stack buffer isn't big enough.
//
return;
}
} while (Done == FALSE);
OutBuf[j] = L'\0';
OutBufU.Length = OutBufU.MaximumLength = (USHORT) (j * sizeof (WCHAR));
OutBufU.Buffer = OutBuf;
DbgPrint ("*******************************************************************************\n"
"*\n"
"* This is the string you add to your checkin description\n"
"* Driver Verifier: Enabled for %Z on Build %ld %wZ\n"
"*\n"
"*******************************************************************************\n",
DriverName, NtBuildNumber & 0xFFFFFFF, &OutBufU);
#else
DbgPrint ("*******************************************************************************\n"
"*\n"
"* Driver Verifier: Enabled for %Z on Build %ld\n"
"* Please see http://winweb/drivercheck to obtain a check-in string.\n"
"*\n"
"*******************************************************************************\n",
DriverName, NtBuildNumber & 0xFFFFFFF);
#endif
return;
}
//
// BEWARE: Various kernel macros are undefined here so we can pull in the
// real routines. This is needed because the real routines are exported for
// driver compatibility. This module has been carefully laid out so these
// macros are not referenced from this point down and references go to the
// real routines.
//
#undef KeRaiseIrql
#undef KeLowerIrql
#undef KeAcquireSpinLock
#undef KeReleaseSpinLock
#undef KeAcquireSpinLockAtDpcLevel
#undef KeReleaseSpinLockFromDpcLevel
#if 0
#undef ExAcquireResourceExclusive
#endif
#if !defined(_AMD64_)
VOID
KeRaiseIrql (
IN KIRQL NewIrql,
OUT PKIRQL OldIrql
);
#endif
VOID
KeLowerIrql (
IN KIRQL NewIrql
);
#if !defined(_AMD64_)
VOID
KeAcquireSpinLock (
IN PKSPIN_LOCK SpinLock,
OUT PKIRQL OldIrql
);
#endif
VOID
KeReleaseSpinLock (
IN PKSPIN_LOCK SpinLock,
IN KIRQL NewIrql
);
VOID
KeAcquireSpinLockAtDpcLevel (
IN PKSPIN_LOCK SpinLock
);
VOID
KeReleaseSpinLockFromDpcLevel (
IN PKSPIN_LOCK SpinLock
);
#if 0
BOOLEAN
ExAcquireResourceExclusive (
IN PERESOURCE Resource,
IN BOOLEAN Wait
);
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
const VERIFIER_THUNKS MiVerifierThunks[] = {
"KeSetEvent",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierSetEvent,
"ExAcquireFastMutexUnsafe",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireFastMutexUnsafe,
"ExReleaseFastMutexUnsafe",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseFastMutexUnsafe,
"ExAcquireResourceExclusiveLite",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireResourceExclusiveLite,
"ExReleaseResourceLite",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseResourceLite,
"MmProbeAndLockPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierProbeAndLockPages,
#if 0
//
// Don't bother thunking this API as it appears no drivers use it.
//
"MmProbeAndLockSelectedPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierProbeAndLockSelectedPages,
#endif
"MmProbeAndLockProcessPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierProbeAndLockProcessPages,
"MmMapIoSpace",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapIoSpace,
"MmMapLockedPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapLockedPages,
"MmMapLockedPagesSpecifyCache",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapLockedPagesSpecifyCache,
"MmUnlockPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnlockPages,
"MmUnmapLockedPages",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnmapLockedPages,
"MmUnmapIoSpace",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnmapIoSpace,
"ExAcquireFastMutex",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireFastMutex,
"ExTryToAcquireFastMutex",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExTryToAcquireFastMutex,
"ExReleaseFastMutex",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseFastMutex,
#if !defined(_AMD64_)
"KeRaiseIrql",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeRaiseIrql,
#endif
"KeLowerIrql",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeLowerIrql,
#if !defined(_AMD64_)
"KeAcquireSpinLock",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLock,
#endif
"KeReleaseSpinLock",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLock,
#if defined(_X86_)
"KefAcquireSpinLockAtDpcLevel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLockAtDpcLevel,
"KefReleaseSpinLockFromDpcLevel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLockFromDpcLevel,
#else
#if !defined(_AMD64_) || !defined(NT_UP)
//
// Because the AMD64 versions of these native Ke routines are written in
// C, the UP build where these functions are no-ops end up getting resolved
// by the linker into a single no-op routine. When we subsequently
// initialize the MiVerifierThunks array, we count on the target routine
// being unique - and therefore that thunking the driver import tables is
// done by comparing to the target routine address (INSTEAD OF THE NAME).
//
// So disable thunking these calls for now. Note this may need to be done
// for any other routines/platforms that can get optimized in this manner.
//
"KeAcquireSpinLockAtDpcLevel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLockAtDpcLevel,
"KeReleaseSpinLockFromDpcLevel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLockFromDpcLevel,
#endif
#endif
"KeSynchronizeExecution",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierSynchronizeExecution,
"KeInitializeTimerEx",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeInitializeTimerEx,
"KeInitializeTimer",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeInitializeTimer,
"KeWaitForSingleObject",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeWaitForSingleObject,
#if defined(_X86_) || defined(_AMD64_)
"KfRaiseIrql",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfRaiseIrql,
"KeRaiseIrqlToDpcLevel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeRaiseIrqlToDpcLevel,
#endif
#if defined(_X86_)
"KfLowerIrql",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfLowerIrql,
"KfAcquireSpinLock",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfAcquireSpinLock,
"KfReleaseSpinLock",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfReleaseSpinLock,
#endif
#if !defined(_X86_)
"KeAcquireSpinLockRaiseToDpc",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLockRaiseToDpc,
#endif
"IoFreeIrp",
(PDRIVER_VERIFIER_THUNK_ROUTINE)IovFreeIrp,
"IofCompleteRequest",
(PDRIVER_VERIFIER_THUNK_ROUTINE)IovCompleteRequest,
"IoBuildDeviceIoControlRequest",
(PDRIVER_VERIFIER_THUNK_ROUTINE)IovBuildDeviceIoControlRequest,
"IoBuildAsynchronousFsdRequest",
(PDRIVER_VERIFIER_THUNK_ROUTINE)IovBuildAsynchronousFsdRequest,
"IoInitializeTimer",
(PDRIVER_VERIFIER_THUNK_ROUTINE)IovInitializeTimer,
"KeQueryPerformanceCounter",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfQueryPerformanceCounter,
"IoGetDmaAdapter",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfGetDmaAdapter,
"HalAllocateCrashDumpRegisters",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfAllocateCrashDumpRegisters,
"ObReferenceObjectByHandle",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierReferenceObjectByHandle,
"KeReleaseMutex",
(PDRIVER_VERIFIER_THUNK_ROUTINE) VerifierKeReleaseMutex,
"KeInitializeMutex",
(PDRIVER_VERIFIER_THUNK_ROUTINE) VerifierKeInitializeMutex,
"KeReleaseMutant",
(PDRIVER_VERIFIER_THUNK_ROUTINE) VerifierKeReleaseMutant,
"KeInitializeMutant",
(PDRIVER_VERIFIER_THUNK_ROUTINE) VerifierKeInitializeMutant,
#if defined(_X86_) || defined(_IA64_)
"KeInitializeSpinLock",
(PDRIVER_VERIFIER_THUNK_ROUTINE) VerifierKeInitializeSpinLock,
#endif
#if !defined(NO_LEGACY_DRIVERS)
"HalGetAdapter",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfLegacyGetAdapter,
"IoMapTransfer",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfMapTransfer,
"IoFlushAdapterBuffers",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfFlushAdapterBuffers,
"HalAllocateCommonBuffer",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfAllocateCommonBuffer,
"HalFreeCommonBuffer",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfFreeCommonBuffer,
"IoAllocateAdapterChannel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfAllocateAdapterChannel,
"IoFreeAdapterChannel",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfFreeAdapterChannel,
"IoFreeMapRegisters",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VfFreeMapRegisters,
#endif
"NtCreateFile",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierNtCreateFile,
"NtWriteFile",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierNtWriteFile,
"NtReadFile",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierNtReadFile,
"KeLeaveCriticalRegion",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierLeaveCriticalRegion,
"ObfReferenceObject",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierReferenceObject,
"ObDereferenceObject",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierDereferenceObject,
// Zw intefaces
DECLARE_ZW_VERIFIER_THUNK(ZwAccessCheckAndAuditAlarm),
DECLARE_ZW_VERIFIER_THUNK(ZwAddBootEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwAddDriverEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwAdjustPrivilegesToken),
DECLARE_ZW_VERIFIER_THUNK(ZwAlertThread),
DECLARE_ZW_VERIFIER_THUNK(ZwAllocateVirtualMemory),
DECLARE_ZW_VERIFIER_THUNK(ZwAssignProcessToJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwCancelIoFile),
DECLARE_ZW_VERIFIER_THUNK(ZwCancelTimer),
DECLARE_ZW_VERIFIER_THUNK(ZwClearEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwClose),
DECLARE_ZW_VERIFIER_THUNK(ZwCloseObjectAuditAlarm),
DECLARE_ZW_VERIFIER_THUNK(ZwConnectPort),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateDirectoryObject),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateFile),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateKey),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateSection),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateSymbolicLinkObject),
DECLARE_ZW_VERIFIER_THUNK(ZwCreateTimer),
DECLARE_ZW_VERIFIER_THUNK(ZwDeleteBootEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwDeleteDriverEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwDeleteFile),
DECLARE_ZW_VERIFIER_THUNK(ZwDeleteKey),
DECLARE_ZW_VERIFIER_THUNK(ZwDeleteValueKey),
DECLARE_ZW_VERIFIER_THUNK(ZwDeviceIoControlFile),
DECLARE_ZW_VERIFIER_THUNK(ZwDisplayString),
DECLARE_ZW_VERIFIER_THUNK(ZwDuplicateObject),
DECLARE_ZW_VERIFIER_THUNK(ZwDuplicateToken),
DECLARE_ZW_VERIFIER_THUNK(ZwEnumerateBootEntries),
DECLARE_ZW_VERIFIER_THUNK(ZwEnumerateDriverEntries),
DECLARE_ZW_VERIFIER_THUNK(ZwEnumerateKey),
DECLARE_ZW_VERIFIER_THUNK(ZwEnumerateValueKey),
DECLARE_ZW_VERIFIER_THUNK(ZwFlushInstructionCache),
DECLARE_ZW_VERIFIER_THUNK(ZwFlushKey),
DECLARE_ZW_VERIFIER_THUNK(ZwFlushVirtualMemory),
DECLARE_ZW_VERIFIER_THUNK(ZwFreeVirtualMemory),
DECLARE_ZW_VERIFIER_THUNK(ZwFsControlFile),
DECLARE_ZW_VERIFIER_THUNK(ZwInitiatePowerAction),
DECLARE_ZW_VERIFIER_THUNK(ZwIsProcessInJob),
DECLARE_ZW_VERIFIER_THUNK(ZwLoadDriver),
DECLARE_ZW_VERIFIER_THUNK(ZwLoadKey),
DECLARE_ZW_VERIFIER_THUNK(ZwMakeTemporaryObject),
DECLARE_ZW_VERIFIER_THUNK(ZwMapViewOfSection),
DECLARE_ZW_VERIFIER_THUNK(ZwModifyBootEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwModifyDriverEntry),
DECLARE_ZW_VERIFIER_THUNK(ZwNotifyChangeKey),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenDirectoryObject),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenFile),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenKey),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenProcess),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenProcessToken),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenProcessTokenEx),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenSection),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenSymbolicLinkObject),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenThread),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenThreadToken),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenThreadTokenEx),
DECLARE_ZW_VERIFIER_THUNK(ZwOpenTimer),
DECLARE_ZW_VERIFIER_THUNK(ZwPowerInformation),
DECLARE_ZW_VERIFIER_THUNK(ZwPulseEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryBootEntryOrder),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryBootOptions),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryDefaultLocale),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryDefaultUILanguage),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryDriverEntryOrder),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInstallUILanguage),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryDirectoryFile),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryDirectoryObject),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryEaFile),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryFullAttributesFile),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationFile),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationProcess),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationThread),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationToken),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryInformationToken),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryKey),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryObject),
DECLARE_ZW_VERIFIER_THUNK(ZwQuerySection),
DECLARE_ZW_VERIFIER_THUNK(ZwQuerySecurityObject),
DECLARE_ZW_VERIFIER_THUNK(ZwQuerySymbolicLinkObject),
DECLARE_ZW_VERIFIER_THUNK(ZwQuerySystemInformation),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryValueKey),
DECLARE_ZW_VERIFIER_THUNK(ZwQueryVolumeInformationFile),
DECLARE_ZW_VERIFIER_THUNK(ZwReadFile),
DECLARE_ZW_VERIFIER_THUNK(ZwReplaceKey),
DECLARE_ZW_VERIFIER_THUNK(ZwRequestWaitReplyPort),
DECLARE_ZW_VERIFIER_THUNK(ZwResetEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwRestoreKey),
DECLARE_ZW_VERIFIER_THUNK(ZwSaveKey),
DECLARE_ZW_VERIFIER_THUNK(ZwSaveKeyEx),
DECLARE_ZW_VERIFIER_THUNK(ZwSetBootEntryOrder),
DECLARE_ZW_VERIFIER_THUNK(ZwSetBootOptions),
DECLARE_ZW_VERIFIER_THUNK(ZwSetDefaultLocale),
DECLARE_ZW_VERIFIER_THUNK(ZwSetDefaultUILanguage),
DECLARE_ZW_VERIFIER_THUNK(ZwSetDriverEntryOrder),
DECLARE_ZW_VERIFIER_THUNK(ZwSetEaFile),
DECLARE_ZW_VERIFIER_THUNK(ZwSetEvent),
DECLARE_ZW_VERIFIER_THUNK(ZwSetInformationFile),
DECLARE_ZW_VERIFIER_THUNK(ZwSetInformationJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwSetInformationObject),
DECLARE_ZW_VERIFIER_THUNK(ZwSetInformationProcess),
DECLARE_ZW_VERIFIER_THUNK(ZwSetInformationThread),
DECLARE_ZW_VERIFIER_THUNK(ZwSetSecurityObject),
DECLARE_ZW_VERIFIER_THUNK(ZwSetSystemInformation),
DECLARE_ZW_VERIFIER_THUNK(ZwSetSystemTime),
DECLARE_ZW_VERIFIER_THUNK(ZwSetTimer),
DECLARE_ZW_VERIFIER_THUNK(ZwSetValueKey),
DECLARE_ZW_VERIFIER_THUNK(ZwSetVolumeInformationFile),
DECLARE_ZW_VERIFIER_THUNK(ZwTerminateJobObject),
DECLARE_ZW_VERIFIER_THUNK(ZwTerminateProcess),
DECLARE_ZW_VERIFIER_THUNK(ZwTranslateFilePath),
DECLARE_ZW_VERIFIER_THUNK(ZwUnloadDriver),
DECLARE_ZW_VERIFIER_THUNK(ZwUnloadKey),
DECLARE_ZW_VERIFIER_THUNK(ZwUnmapViewOfSection),
DECLARE_ZW_VERIFIER_THUNK(ZwWaitForMultipleObjects),
DECLARE_ZW_VERIFIER_THUNK(ZwWaitForSingleObject),
DECLARE_ZW_VERIFIER_THUNK(ZwWriteFile),
DECLARE_ZW_VERIFIER_THUNK(ZwYieldExecution),
NULL,
NULL,
};
const VERIFIER_THUNKS MiVerifierPoolThunks[] = {
"ExAllocatePool",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePool,
"ExAllocatePoolWithQuota",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithQuota,
"ExAllocatePoolWithQuotaTag",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithQuotaTag,
"ExAllocatePoolWithTag",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithTag,
"ExAllocatePoolWithTagPriority",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithTagPriority,
"ExFreePool",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierFreePool,
"ExFreePoolWithTag",
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierFreePoolWithTag,
NULL,
NULL,
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
PDRIVER_VERIFIER_THUNK_ROUTINE
MiResolveVerifierExports (
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PCHAR PristineName
)
/*++
Routine Description:
This function scans the kernel & HAL exports for the specified routine name.
Arguments:
DataTableEntry - Supplies the data table entry for the driver.
Return Value:
Non-NULL address of routine to thunk or NULL if the routine could not be
found.
Environment:
Kernel mode, Phase 0 Initialization only.
The PsLoadedModuleList has not been initialized yet.
Non paged pool exists in Phase0, but paged pool does not.
--*/
{
ULONG i;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PULONG Addr;
ULONG ExportSize;
ULONG Low;
ULONG Middle;
ULONG High;
PLIST_ENTRY NextEntry;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
USHORT OrdinalNumber;
LONG Result;
PCHAR DllBase;
i = 0;
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
DataTableEntry = CONTAINING_RECORD (NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
//
// Process the kernel and HAL exports so the proper routine
// addresses can be generated now that relocations are complete.
//
DllBase = (PCHAR) DataTableEntry->DllBase;
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
(PVOID) DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize);
if (ExportDirectory != NULL) {
//
// Lookup the import name in the name table using a binary search.
//
NameTableBase = (PULONG)(DllBase + (ULONG)ExportDirectory->AddressOfNames);
NameOrdinalTableBase = (PUSHORT)(DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
Low = 0;
Middle = 0;
High = ExportDirectory->NumberOfNames - 1;
while (High >= Low) {
//
// Compute the next probe index and compare the import name
// with the export name entry.
//
Middle = (Low + High) >> 1;
Result = strcmp (PristineName,
(PCHAR)DllBase + NameTableBase[Middle]);
if (Result < 0) {
High = Middle - 1;
} else if (Result > 0) {
Low = Middle + 1;
}
else {
break;
}
}
//
// If the high index is less than the low index, then a matching
// table entry was not found. Otherwise, get the ordinal number
// from the ordinal table.
//
if ((LONG)High >= (LONG)Low) {
OrdinalNumber = NameOrdinalTableBase[Middle];
//
// If OrdinalNumber is not within the Export Address Table,
// then DLL does not implement function. Otherwise we have
// the export that matches the specified argument routine name.
//
if ((ULONG)OrdinalNumber < ExportDirectory->NumberOfFunctions) {
Addr = (PULONG)(DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
return (PDRIVER_VERIFIER_THUNK_ROUTINE)(ULONG_PTR)(DllBase + Addr[OrdinalNumber]);
}
}
}
i += 1;
if (i == 2) {
break;
}
}
return NULL;
}
LOGICAL
MiEnableVerifier (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
)
/*++
Routine Description:
This function enables the verifier for the argument driver by thunking
relevant system APIs in the argument driver import table.
Arguments:
DataTableEntry - Supplies the data table entry for the driver.
Return Value:
TRUE if thunking was applied, FALSE if not.
Environment:
Kernel mode, Phase 0 Initialization and normal runtime.
Non paged pool exists in Phase0, but paged pool does not.
--*/
{
ULONG i;
ULONG j;
PULONG_PTR ImportThunk;
ULONG ImportSize;
VERIFIER_THUNKS const *VerifierThunk;
LOGICAL Found;
ULONG_PTR RealRoutine;
PLIST_ENTRY NextEntry;
PDRIVER_VERIFIER_THUNK_PAIRS ThunkTable;
PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase;
ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData(
DataTableEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IAT,
&ImportSize);
if (ImportThunk == NULL) {
return FALSE;
}
ImportSize /= sizeof(PULONG_PTR);
for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
Found = FALSE;
if (KernelVerifier == FALSE) {
VerifierThunk = MiVerifierThunks;
while (VerifierThunk->PristineRoutineAsciiName != NULL) {
RealRoutine = (ULONG_PTR)VerifierThunk->PristineRoutine;
if (*ImportThunk == RealRoutine) {
*ImportThunk = (ULONG_PTR)(VerifierThunk->NewRoutine);
Found = TRUE;
break;
}
VerifierThunk += 1;
}
}
if (Found == FALSE) {
VerifierThunk = MiVerifierPoolThunks;
while (VerifierThunk->PristineRoutineAsciiName != NULL) {
RealRoutine = (ULONG_PTR)VerifierThunk->PristineRoutine;
if (*ImportThunk == RealRoutine) {
*ImportThunk = (ULONG_PTR)(VerifierThunk->NewRoutine);
Found = TRUE;
break;
}
VerifierThunk += 1;
}
}
if (Found == FALSE) {
NextEntry = MiVerifierDriverAddedThunkListHead.Flink;
while (NextEntry != &MiVerifierDriverAddedThunkListHead) {
ThunkTableBase = CONTAINING_RECORD(NextEntry,
DRIVER_SPECIFIED_VERIFIER_THUNKS,
ListEntry);
ThunkTable = (PDRIVER_VERIFIER_THUNK_PAIRS)(ThunkTableBase + 1);
for (j = 0; j < ThunkTableBase->NumberOfThunks; j += 1) {
if (*ImportThunk == (ULONG_PTR)ThunkTable->PristineRoutine) {
*ImportThunk = (ULONG_PTR)(ThunkTable->NewRoutine);
Found = TRUE;
break;
}
ThunkTable += 1;
}
if (Found == TRUE) {
break;
}
NextEntry = NextEntry->Flink;
}
}
}
return TRUE;
}
LOGICAL
MiReEnableVerifier (
IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
)
/*++
Routine Description:
This function thunks DLL-supplied APIs in the argument driver import table.
Arguments:
DataTableEntry - Supplies the data table entry for the driver.
Return Value:
TRUE if thunking was applied, FALSE if not.
Environment:
Kernel mode, Phase 0 Initialization only.
Non paged pool exists in Phase0, but paged pool does not.
--*/
{
ULONG i;
ULONG j;
PULONG_PTR ImportThunk;
ULONG ImportSize;
LOGICAL Found;
PLIST_ENTRY NextEntry;
PMMPTE PointerPte;
PULONG_PTR VirtualThunk;
PFN_NUMBER PageFrameIndex;
PFN_NUMBER VirtualPageFrameIndex;
PDRIVER_VERIFIER_THUNK_PAIRS ThunkTable;
PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase;
ULONG Offset;
ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData(
DataTableEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IAT,
&ImportSize);
if (ImportThunk == NULL) {
return FALSE;
}
VirtualThunk = NULL;
ImportSize /= sizeof(PULONG_PTR);
//
// Initializing VirtualPageFrameIndex is not needed for correctness, but
// without it the compiler cannot compile this code W4 to check for use of
// uninitialized variables.
//
VirtualPageFrameIndex = 0;
for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
Found = FALSE;
NextEntry = MiVerifierDriverAddedThunkListHead.Flink;
while (NextEntry != &MiVerifierDriverAddedThunkListHead) {
ThunkTableBase = CONTAINING_RECORD(NextEntry,
DRIVER_SPECIFIED_VERIFIER_THUNKS,
ListEntry);
ThunkTable = (PDRIVER_VERIFIER_THUNK_PAIRS)(ThunkTableBase + 1);
for (j = 0; j < ThunkTableBase->NumberOfThunks; j += 1) {
if (*ImportThunk == (ULONG_PTR)ThunkTable->PristineRoutine) {
ASSERT (MI_IS_PHYSICAL_ADDRESS(ImportThunk) == 0);
PointerPte = MiGetPteAddress (ImportThunk);
ASSERT (PointerPte->u.Hard.Valid == 1);
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
Offset = (ULONG) MiGetByteOffset(ImportThunk);
if ((VirtualThunk != NULL) &&
(VirtualPageFrameIndex == PageFrameIndex)) {
NOTHING;
}
else {
VirtualThunk = MiMapSinglePage (VirtualThunk,
PageFrameIndex,
MmCached,
HighPagePriority);
if (VirtualThunk == NULL) {
return FALSE;
}
VirtualPageFrameIndex = PageFrameIndex;
}
*(PULONG_PTR)((PUCHAR)VirtualThunk + Offset) =
(ULONG_PTR)(ThunkTable->NewRoutine);
Found = TRUE;
break;
}
ThunkTable += 1;
}
if (Found == TRUE) {
break;
}
NextEntry = NextEntry->Flink;
}
}
if (VirtualThunk != NULL) {
MiUnmapSinglePage (VirtualThunk);
}
return TRUE;
}
typedef struct _KERNEL_VERIFIER_THUNK_PAIRS {
PDRIVER_VERIFIER_THUNK_ROUTINE PristineRoutine;
PDRIVER_VERIFIER_THUNK_ROUTINE NewRoutine;
} KERNEL_VERIFIER_THUNK_PAIRS, *PKERNEL_VERIFIER_THUNK_PAIRS;
#if defined(_X86_)
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif
const KERNEL_VERIFIER_THUNK_PAIRS MiKernelVerifierThunks[] = {
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeRaiseIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeRaiseIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeLowerIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeLowerIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeAcquireSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeReleaseSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KfRaiseIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfRaiseIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeRaiseIrqlToDpcLevel,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeRaiseIrqlToDpcLevel,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KfLowerIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfLowerIrql,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KfAcquireSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfAcquireSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KfReleaseSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfReleaseSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)ExAcquireFastMutex,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireFastMutex,
#if !defined(NT_UP)
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeAcquireQueuedSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireQueuedSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)KeReleaseQueuedSpinLock,
(PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseQueuedSpinLock,
#endif
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
VOID
MiEnableKernelVerifier (
VOID
)
/*++
Routine Description:
This function enables the verifier for the kernel by thunking
relevant HAL APIs in the kernel's import table.
Arguments:
None.
Return Value:
None.
Environment:
Kernel mode, Phase 1 Initialization.
--*/
{
ULONG i;
PULONG_PTR ImportThunk;
ULONG ImportSize;
KERNEL_VERIFIER_THUNK_PAIRS const *VerifierThunk;
ULONG ThunkCount;
ULONG_PTR RealRoutine;
PULONG_PTR PointerRealRoutine;
if (KernelVerifier == FALSE) {
return;
}
ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData(
PsNtosImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IAT,
&ImportSize);
if (ImportThunk == NULL) {
return;
}
ImportSize /= sizeof(PULONG_PTR);
for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
VerifierThunk = MiKernelVerifierThunks;
for (ThunkCount = 0; ThunkCount < sizeof (MiKernelVerifierThunks) / sizeof (KERNEL_VERIFIER_THUNK_PAIRS); ThunkCount += 1) {
//
// Only the x86 has/needs this oddity - take the kernel address,
// knowing that it points at a 2 byte jmp opcode followed by
// a 4-byte indirect pointer to a destination address.
//
PointerRealRoutine = (PULONG_PTR)*((PULONG_PTR)((ULONG_PTR)VerifierThunk->PristineRoutine + 2));
RealRoutine = *PointerRealRoutine;
if (*ImportThunk == RealRoutine) {
//
// Order is important here.
//
if (MiKernelVerifierOriginalCalls[ThunkCount] == NULL) {
MiKernelVerifierOriginalCalls[ThunkCount] = (PVOID)RealRoutine;
}
*ImportThunk = (ULONG_PTR)(VerifierThunk->NewRoutine);
break;
}
VerifierThunk += 1;
}
}
return;
}
#endif
//
// BEWARE: Various kernel macros were undefined above so we can pull in the
// real routines. This is needed because the real routines are exported for
// driver compatibility. This module has been carefully laid out so these
// macros are not referenced from that point to here and references go to the
// real routines.
//
// BE EXTREMELY CAREFUL IF YOU DECIDE TO ADD ROUTINES BELOW THIS POINT !
//