/*++ Copyright (c) 1989 Microsoft Corporation Module Name: verifier.c Abstract: This module contains the routines to verify system drivers. Author: Landy Wang (landyw) 3-Sep-1998 --*/ #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 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 KIRQL FASTCALL VerifierKfAcquireSpinLock(IN PKSPIN_LOCK SpinLock); THUNKED_API VOID FASTCALL VerifierKfReleaseSpinLock(IN PKSPIN_LOCK SpinLock, IN KIRQL NewIrql); 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 VerifierExAcquireResourceExclusive(IN PERESOURCE Resource, IN BOOLEAN Wait); THUNKED_API VOID FASTCALL VerifierExReleaseResource(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); 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(VOID); VOID ViInitializeEntry(IN PMI_VERIFIER_DRIVER_ENTRY Verifier, IN LOGICAL FirstLoad); LOGICAL ViReservePoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier); ULONG_PTR ViInsertPoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier, IN PVOID VirtualAddress, IN PVOID CallingAddress, IN SIZE_T NumberOfBytes, IN ULONG Tag); VOID ViCancelPoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier); VOID ViReleasePoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier, IN PVOID VirtualAddress, IN ULONG_PTR ListIndex, IN SIZE_T ChargedBytes); VOID KfSanityCheckRaiseIrql(IN KIRQL NewIrql); VOID KfSanityCheckLowerIrql(IN KIRQL NewIrql); 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; LOGICAL MiVerifyAllDrivers; WCHAR MiVerifyRandomDrivers; ULONG MiActiveVerifies; ULONG MiActiveVerifierThunks; ULONG MiNoPageOnRaiseIrql; ULONG MiVerifierStackProtectTime; LOGICAL MmDontVerifyRandomDrivers = TRUE; LOGICAL VerifierSystemSufficientlyBooted; LARGE_INTEGER VerifierRequiredTimeSinceBoot = {(ULONG)(40 * 1000 * 1000 * 10), 1}; LOGICAL VerifierIsTrackingPool = FALSE; KSPIN_LOCK VerifierListLock; KSPIN_LOCK VerifierPoolLock; FAST_MUTEX VerifierPoolMutex; PRTL_BITMAP VerifierLargePagedPoolMap; LIST_ENTRY MiVerifierDriverAddedThunkListHead; extern LOGICAL MmSpecialPoolCatchOverruns; LOGICAL KernelVerifier = FALSE; ULONG KernelVerifierTickPage = 0x7; LOGICAL MiEnableVerifier(IN PLDR_DATA_TABLE_ENTRY DataTableEntry); VOID ViInsertVerifierEntry(IN PMI_VERIFIER_DRIVER_ENTRY Verifier); PVOID ViPostPoolAllocation(IN PVOID VirtualAddress, IN SIZE_T NumberOfBytes, IN POOL_TYPE PoolType, IN ULONG Tag, IN PVOID CallingAddress); PMI_VERIFIER_DRIVER_ENTRY ViLocateVerifierEntry(IN PVOID SystemAddress); VOID MiVerifierCheckThunks(IN PLDR_DATA_TABLE_ENTRY DataTableEntry); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,MiInitializeDriverVerifierList) #if defined(_X86_) #pragma alloc_text(INIT,MiEnableKernelVerifier) #endif #pragma alloc_text(PAGE,MiApplyDriverVerifier) #pragma alloc_text(PAGE,MiEnableVerifier) #pragma alloc_text(PAGE,ViPrintString) #pragma alloc_text(PAGE,MmGetVerifierInformation) #pragma alloc_text(PAGE,MmSetVerifierInformation) #pragma alloc_text(PAGE,MmAddVerifierThunks) #pragma alloc_text(PAGELK,MiVerifierCheckThunks) #pragma alloc_text(PAGELK,MiVerifyingDriverUnloading) #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,VerifierKfRaiseIrql) #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,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,VerifierExAcquireResourceExclusive) #pragma alloc_text(PAGEVRFY,VerifierExReleaseResource) #pragma alloc_text(PAGEVRFY,VerifierKeAcquireQueuedSpinLock) #pragma alloc_text(PAGEVRFY,VerifierKeReleaseQueuedSpinLock) #pragma alloc_text(PAGEVRFY,VerifierSynchronizeExecution) #pragma alloc_text(PAGEVRFY,VerifierFreeTrackedPool) #pragma alloc_text(PAGEVRFY,VeAllocatePoolWithTagPriority) #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,ViReservePoolAllocation) #pragma alloc_text(PAGEVRFY,ViInsertPoolAllocation) #pragma alloc_text(PAGEVRFY,ViCancelPoolAllocation) #pragma alloc_text(PAGEVRFY,ViReleasePoolAllocation) #pragma alloc_text(PAGEVRFY,KfSanityCheckRaiseIrql) #pragma alloc_text(PAGEVRFY,KfSanityCheckLowerIrql) #endif typedef struct _VERIFIER_THUNKS { PDRIVER_VERIFIER_THUNK_ROUTINE PristineRoutine; PDRIVER_VERIFIER_THUNK_ROUTINE NewRoutine; ULONG Flag; } VERIFIER_THUNKS, *PVERIFIER_THUNKS; typedef struct _DRIVER_SPECIFIED_VERIFIER_THUNKS { LIST_ENTRY ListEntry; PLDR_DATA_TABLE_ENTRY DataTableEntry; ULONG NumberOfThunks; } DRIVER_SPECIFIED_VERIFIER_THUNKS, *PDRIVER_SPECIFIED_VERIFIER_THUNKS; #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_KF_LOWER_IRQL 5 #define VI_KF_ACQUIRE_SPINLOCK 6 #define VI_KF_RELEASE_SPINLOCK 7 #define VI_KE_ACQUIRE_QUEUED_SPINLOCK 8 #define VI_KE_RELEASE_QUEUED_SPINLOCK 9 #define VI_HALMAX 10 PVOID MiKernelVerifierOriginalCalls[VI_HALMAX]; #endif 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); } // It would be great to rip out the ViBadMapper code but some drivers will never get fixed if this is done. #define VI_BAD_MAPPERS_MAX 100 KSPIN_LOCK ViBadMapperLock; PVOID ViBadMappers[VI_BAD_MAPPERS_MAX]; LOGICAL ViAddBadMapper(IN PVOID BadMapper) { ULONG i; KIRQL OldIrql; LOGICAL Added; Added = FALSE; ExAcquireSpinLock(&ViBadMapperLock, &OldIrql); for (i = 0; i < VI_BAD_MAPPERS_MAX; i += 1) { if (ViBadMappers[i] == BadMapper) { break; } if (ViBadMappers[i] == NULL) { ViBadMappers[i] = BadMapper; Added = TRUE; break; } } ExReleaseSpinLock(&ViBadMapperLock, OldIrql); return Added; } THUNKED_API PVOID VerifierMapIoSpace(IN PHYSICAL_ADDRESS PhysicalAddress, IN SIZE_T NumberOfBytes, IN MEMORY_CACHING_TYPE CacheType) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); if (CurrentIrql > DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x73, CurrentIrql, (ULONG_PTR)PhysicalAddress.LowPart, NumberOfBytes); } if (ViInjectResourceFailure() == TRUE) { return NULL; } return MmMapIoSpace(PhysicalAddress, NumberOfBytes, CacheType); } THUNKED_API PVOID VerifierMapLockedPages(IN PMDL MemoryDescriptorList, IN KPROCESSOR_MODE AccessMode) { PVOID CallingAddress; PVOID CallersCaller; KIRQL CurrentIrql; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif 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); } } if ((MemoryDescriptorList->MdlFlags & MDL_MAPPING_CAN_FAIL) == 0) { if (ViAddBadMapper(CallingAddress) == TRUE) { // All drivers must specify can fail. We'd really like to bugcheck // here to get drivers to convert but cut them some slack for now. DbgPrint("******\n"); DbgPrint("* *\n"); DbgPrint("* The Driver Verifier has detected the driver at address %p\n", CallingAddress); DbgPrint("* is calling MmMapLockedPages instead of MmMapLockedPagesSpecifyCache.\n"); DbgPrint("* This can cause the system to needlessly bugcheck whenever system\n"); DbgPrint("* resources are low. This driver needs to be fixed.\n"); DbgPrint("* *\n"); DbgPrint("******\n"); } } 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) { PVOID CallingAddress; PVOID CallersCaller; KIRQL CurrentIrql; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif 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); } } 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. We'd // really like to bugcheck here to get drivers to convert but // cut them some slack for now. if (ViAddBadMapper(CallingAddress) == TRUE) { DbgPrint("******\n"); DbgPrint("* *\n"); DbgPrint("* The Driver Verifier has detected the driver at address %p\n", CallingAddress); DbgPrint("* is not calling the safe version of MmMapLockedPagesSpecifyCache.\n"); DbgPrint("* This can cause the system to needlessly bugcheck whenever system\n"); DbgPrint("* resources are low. This driver needs to be fixed.\n"); DbgPrint("* *\n"); DbgPrint("******\n"); } } 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; PVOID CallersCaller; PMI_VERIFIER_DRIVER_ENTRY Verifier; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif if (KernelVerifier == TRUE) { Verifier = ViLocateVerifierEntry(CallingAddress); if ((Verifier == NULL) || ((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) { return ExAllocatePool(PoolType | POOL_DRIVER_MASK, NumberOfBytes); } 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; PVOID CallersCaller; PMI_VERIFIER_DRIVER_ENTRY Verifier; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif 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; PVOID CallersCaller; PMI_VERIFIER_DRIVER_ENTRY Verifier; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif if (KernelVerifier == TRUE) { Verifier = ViLocateVerifierEntry(CallingAddress); if ((Verifier == NULL) || ((Verifier->Flags & VI_VERIFYING_DIRECTLY) == 0)) { return ExAllocatePoolWithQuota(PoolType | POOL_DRIVER_MASK, NumberOfBytes); } 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; PVOID CallersCaller; PMI_VERIFIER_DRIVER_ENTRY Verifier; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif 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; PVOID CallersCaller; PMI_VERIFIER_DRIVER_ENTRY Verifier; #if defined (_X86_) RtlGetCallersAddress(&CallingAddress, &CallersCaller); #else CallingAddress = (PVOID)_ReturnAddress(); #endif 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); } 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. --*/ { LARGE_INTEGER CurrentTime; if ((MmVerifierData.Level & DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES) == 0) { 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. if (VerifierSystemSufficientlyBooted == FALSE) { KeQuerySystemTime(&CurrentTime); if (CurrentTime.QuadPart > KeBootTime.QuadPart + VerifierRequiredTimeSinceBoot.QuadPart) { VerifierSystemSufficientlyBooted = TRUE; } } if (VerifierSystemSufficientlyBooted == TRUE) { KeQueryTickCount(&CurrentTime); if ((CurrentTime.LowPart & 0xF) == 0) { MmVerifierData.AllocationsFailedDeliberately += 1; // Deliberately fail this request. return TRUE; } } return FALSE; } LOGICAL ViReservePoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier) { ULONG_PTR OldSize; ULONG_PTR NewSize; ULONG_PTR NewHashOffset; ULONG_PTR Increment; ULONG_PTR Entries; ULONG_PTR i; KIRQL OldIrql; PVOID NewHashTable; PVI_POOL_ENTRY HashEntry; PVI_POOL_ENTRY OldHashTable; ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); while (Verifier->PoolHashSize <= Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations + Verifier->PoolHashReserved) { // More space is needed. Try for it now. #define VI_POOL_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(VI_POOL_ENTRY)) OldSize = Verifier->PoolHashSize * sizeof(VI_POOL_ENTRY); if (Verifier->PoolHashSize >= VI_POOL_ENTRIES_PER_PAGE) { Increment = PAGE_SIZE; } else { Increment = 16 * sizeof(VI_POOL_ENTRY); } ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); NewSize = OldSize + Increment; if (NewSize < OldSize) { return FALSE; } // Note POOL_DRIVER_MASK must be set to stop the recursion loop // when using the kernel verifier. NewHashTable = ExAllocatePoolWithTagPriority(NonPagedPool | POOL_DRIVER_MASK, NewSize, 'ppeV', HighPoolPriority); ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); OldSize = Verifier->PoolHashSize * sizeof(VI_POOL_ENTRY); if (NewHashTable == NULL) { if (Verifier->PoolHashSize <= Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations + Verifier->PoolHashReserved) { ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); return FALSE; } // Another thread got here before us and space is available. break; } if (NewSize != OldSize + Increment) { // Another thread got here before us. ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); ExFreePool(NewHashTable); ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); } else { // Rebuild the list into the new table. OldHashTable = Verifier->PoolHash; if (OldHashTable != NULL) { RtlCopyMemory(NewHashTable, OldHashTable, OldSize); } // Construct the freelist chaining it through any existing // list (any free entries must be already reserved). HashEntry = (PVI_POOL_ENTRY)((PCHAR)NewHashTable + OldSize); Entries = Increment / sizeof(VI_POOL_ENTRY); NewHashOffset = HashEntry - (PVI_POOL_ENTRY)NewHashTable; // If list compaction becomes important then chaining it on the // end here will need to be revisited. for (i = 0; i < Entries; i += 1) { HashEntry->FreeListNext = NewHashOffset + i + 1; HashEntry += 1; } HashEntry -= 1; HashEntry->FreeListNext = Verifier->PoolHashFree; Verifier->PoolHashFree = NewHashOffset; Verifier->PoolHash = NewHashTable; Verifier->PoolHashSize += Entries; // Free the old table. if (OldHashTable != NULL) { ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); ExFreePool(OldHashTable); ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); } } } Verifier->PoolHashReserved += 1; ASSERT(Verifier->PoolHashSize >= Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations + Verifier->PoolHashReserved); ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); return TRUE; } ULONG_PTR ViInsertPoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier, IN PVOID VirtualAddress, IN PVOID CallingAddress, IN SIZE_T NumberOfBytes, IN ULONG Tag) /*++ Routine Description: This function inserts the specified virtual address into the verifier list for this driver. Arguments: Verifier - Supplies the verifier entry to update. VirtualAddress - Supplies the virtual address to insert. CallingAddress - Supplies the caller's address. NumberOfBytes - Supplies the number of bytes to allocate. Tag - Supplies the tag for the pool being allocated. Return Value: The list index this virtual address was inserted at. Environment: Kernel mode, DISPATCH_LEVEL. The Verifier->VerifierPoolLock must be held. --*/ { ULONG_PTR Index; PVI_POOL_ENTRY HashEntry; ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); // The list entry must be reserved in advance. ASSERT(Verifier->PoolHashReserved != 0); ASSERT(Verifier->PoolHashSize >= Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations + Verifier->PoolHashReserved); // Use the next free list entry. Index = Verifier->PoolHashFree; ASSERT(Index != VI_POOL_FREELIST_END); HashEntry = Verifier->PoolHash + Index; Verifier->PoolHashFree = HashEntry->FreeListNext; Verifier->PoolHashReserved -= 1; ASSERT(((HashEntry->FreeListNext & MINLONG_PTR) == 0) || (HashEntry->FreeListNext == VI_POOL_FREELIST_END)); HashEntry->InUse.VirtualAddress = VirtualAddress; HashEntry->InUse.CallingAddress = CallingAddress; HashEntry->InUse.NumberOfBytes = NumberOfBytes; HashEntry->InUse.Tag = Tag; ASSERT((HashEntry->FreeListNext & MINLONG_PTR) != 0); return Index; } VOID ViReleasePoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier, IN PVOID VirtualAddress, IN ULONG_PTR ListIndex, IN SIZE_T ChargedBytes) /*++ Routine Description: This function removes the specified virtual address from the verifier list for this driver. Arguments: Verifier - Supplies the verifier entry to update. VirtualAddress - Supplies the virtual address to release. ListIndex - Supplies the verifier pool hash index for the address being released. ChargedBytes - Supplies the bytes charged for this allocation. Return Value: None. Environment: Kernel mode, DISPATCH_LEVEL. The Verifier->VerifierPoolLock must be held. --*/ { PFN_NUMBER PageFrameIndex; PFN_NUMBER PageFrameIndex2; PVI_POOL_ENTRY HashEntry; PMMPTE PointerPte; ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); if (Verifier->PoolHash == NULL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x59, (ULONG_PTR)VirtualAddress, ListIndex, (ULONG_PTR)Verifier); } // Ensure that the list pointer has not been overrun and still // points at something decent. HashEntry = Verifier->PoolHash + ListIndex; if (ListIndex >= Verifier->PoolHashSize) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x54, (ULONG_PTR)VirtualAddress, Verifier->PoolHashSize, ListIndex); } if (HashEntry->InUse.VirtualAddress != VirtualAddress) { PageFrameIndex = 0; PageFrameIndex2 = 1; if ((!MI_IS_PHYSICAL_ADDRESS(VirtualAddress)) && (MI_IS_PHYSICAL_ADDRESS(HashEntry->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(HashEntry->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)HashEntry->InUse.VirtualAddress, ChargedBytes); } } if (HashEntry->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)HashEntry, ChargedBytes); } // Put this list entry into the freelist. HashEntry->FreeListNext = Verifier->PoolHashFree; Verifier->PoolHashFree = HashEntry - Verifier->PoolHash; } VOID ViCancelPoolAllocation(IN PMI_VERIFIER_DRIVER_ENTRY Verifier) /*++ Routine Description: This function removes a reservation from the verifier list for this driver. All reservations must be made in advance. This routine is used when an earlier reservation is not going to be used (ie: the actual pool allocation failed so no reservation will be needed after all). Arguments: Verifier - Supplies the verifier entry to update. Return Value: None. Environment: Kernel mode, DISPATCH_LEVEL or below, no verifier mutexes held. --*/ { KIRQL OldIrql; ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); // The hash entry reserved earlier is not going to be used after all. ASSERT(Verifier->PoolHashReserved != 0); ASSERT(Verifier->PoolHashSize >= Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations + Verifier->PoolHashReserved); ASSERT(Verifier->PoolHashFree != VI_POOL_FREELIST_END); Verifier->PoolHashReserved -= 1; ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); } PVOID ViPostPoolAllocation(IN PVOID VirtualAddress, IN SIZE_T NumberOfBytes, IN POOL_TYPE PoolType, IN ULONG Tag, IN PVOID CallingAddress) /*++ Routine Description: This function performs verifier book-keeping on the allocation attempt. Arguments: VirtualAddress - Supplies the virtual address that should be allocated. NumberOfBytes - Supplies the number of bytes to allocate. PoolType - Supplies the type of pool being allocated. Tag - Supplies the tag for the pool being allocated. CallingAddress - Supplies the caller's address. Return Value: The virtual address the caller should use. Environment: Kernel mode. DISPATCH_LEVEL or below. --*/ { KIRQL OldIrql; PMI_VERIFIER_POOL_HEADER Header; SIZE_T ChargedBytes; PMI_VERIFIER_DRIVER_ENTRY Verifier; ULONG_PTR InsertedIndex; LOGICAL SpecialPoolAllocation; PPOOL_HEADER PoolHeader; InterlockedIncrement((PLONG)&MmVerifierData.AllocationsSucceeded); ChargedBytes = EX_REAL_POOL_USAGE(NumberOfBytes); SpecialPoolAllocation = FALSE; if (VirtualAddress >= MmSpecialPoolStart && VirtualAddress < MmSpecialPoolEnd) { ChargedBytes = NumberOfBytes; InterlockedIncrement((PLONG)&MmVerifierData.AllocationsSucceededSpecialPool); SpecialPoolAllocation = TRUE; } else if (NumberOfBytes <= POOL_BUDDY_MAX) { ChargedBytes -= POOL_OVERHEAD; } else { // 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 ((PoolType & POOL_VERIFIER_MASK) == 0) { return VirtualAddress; } if (NumberOfBytes > POOL_BUDDY_MAX) { ASSERT(BYTE_OFFSET(VirtualAddress) == 0); } Verifier = ViLocateVerifierEntry(CallingAddress); ASSERT(Verifier != NULL); VerifierIsTrackingPool = TRUE; if (SpecialPoolAllocation == TRUE) { // 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. if (((ULONG_PTR)VirtualAddress & (PAGE_SIZE - 1))) { PoolHeader = (PPOOL_HEADER)(PAGE_ALIGN(VirtualAddress)); Header = (PMI_VERIFIER_POOL_HEADER)(PoolHeader + 1); VirtualAddress = (PVOID)((PCHAR)VirtualAddress + sizeof(MI_VERIFIER_POOL_HEADER)); } else { PoolHeader = (PPOOL_HEADER)((PCHAR)PAGE_ALIGN(VirtualAddress) + PAGE_SIZE - POOL_OVERHEAD); Header = (PMI_VERIFIER_POOL_HEADER)(PoolHeader - 1); } // ASSERT (PoolHeader->Ulong1 & MI_SPECIAL_POOL_VERIFIER); PoolHeader->Ulong1 -= sizeof(MI_VERIFIER_POOL_HEADER); ChargedBytes -= sizeof(MI_VERIFIER_POOL_HEADER); PoolHeader->Ulong1 |= MI_SPECIAL_POOL_VERIFIER; } else { Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)VirtualAddress + ChargedBytes - sizeof(MI_VERIFIER_POOL_HEADER)); } ASSERT(((ULONG_PTR)Header & (sizeof(ULONG) - 1)) == 0); Header->Verifier = Verifier; // Enqueue the entry and update per-driver counters. // Note that paged pool allocations must be chained using nonpaged // pool to prevent deadlocks. ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); InsertedIndex = ViInsertPoolAllocation(Verifier, VirtualAddress, CallingAddress, ChargedBytes, Tag); if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) { Verifier->PagedBytes += ChargedBytes; if (Verifier->PagedBytes > Verifier->PeakPagedBytes) { Verifier->PeakPagedBytes = Verifier->PagedBytes; } Verifier->CurrentPagedPoolAllocations += 1; if (Verifier->CurrentPagedPoolAllocations > Verifier->PeakPagedPoolAllocations) { Verifier->PeakPagedPoolAllocations = Verifier->CurrentPagedPoolAllocations; } } else { Verifier->NonPagedBytes += ChargedBytes; if (Verifier->NonPagedBytes > Verifier->PeakNonPagedBytes) { Verifier->PeakNonPagedBytes = Verifier->NonPagedBytes; } Verifier->CurrentNonPagedPoolAllocations += 1; if (Verifier->CurrentNonPagedPoolAllocations > Verifier->PeakNonPagedPoolAllocations) { Verifier->PeakNonPagedPoolAllocations = Verifier->CurrentNonPagedPoolAllocations; } } ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); // Since the header for paged pool is paged, don't initialize it until the spinlock above is released. Header->ListIndex = InsertedIndex; // Update systemwide counters. if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) { ExAcquireFastMutex(&VerifierPoolMutex); MmVerifierData.PagedBytes += ChargedBytes; if (MmVerifierData.PagedBytes > MmVerifierData.PeakPagedBytes) { MmVerifierData.PeakPagedBytes = MmVerifierData.PagedBytes; } MmVerifierData.CurrentPagedPoolAllocations += 1; if (MmVerifierData.CurrentPagedPoolAllocations > MmVerifierData.PeakPagedPoolAllocations) { MmVerifierData.PeakPagedPoolAllocations = MmVerifierData.CurrentPagedPoolAllocations; } ExReleaseFastMutex(&VerifierPoolMutex); } else { ExAcquireSpinLock(&VerifierPoolLock, &OldIrql); MmVerifierData.NonPagedBytes += ChargedBytes; if (MmVerifierData.NonPagedBytes > MmVerifierData.PeakNonPagedBytes) { MmVerifierData.PeakNonPagedBytes = MmVerifierData.NonPagedBytes; } MmVerifierData.CurrentNonPagedPoolAllocations += 1; if (MmVerifierData.CurrentNonPagedPoolAllocations > MmVerifierData.PeakNonPagedPoolAllocations) { MmVerifierData.PeakNonPagedPoolAllocations = MmVerifierData.CurrentNonPagedPoolAllocations; } ExReleaseSpinLock(&VerifierPoolLock, OldIrql); } 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; LOGICAL ReservedHash; ULONG HeaderSize; 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; } } 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; } } } ReservedHash = FALSE; if (MmVerifierData.Level & DRIVER_VERIFIER_TRACK_POOL_ALLOCATIONS) { if ((PoolType & SESSION_POOL_MASK) && (MiHydra == TRUE)) { // Session pool is directly tracked by default already. NOTHING; } else { 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)) { // 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 if (((PoolType & MUST_SUCCEED_POOL_TYPE_MASK) == 0) || (ChargedBytes <= PAGE_SIZE)) { // Any pool allocation that is allowed to fail or where the // total number of charged bytes fits in a page (nonpaged- // must-succeed requires this) is suitable for tracking. // Just ensure that the hash list has space for it. if (ViReservePoolAllocation(Verifier) == TRUE) { ReservedHash = TRUE; NumberOfBytes = ChargedBytes; PoolType |= POOL_VERIFIER_MASK; } } else { ASSERT((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool); MmVerifierData.UnTrackedPool += 1; } } } VirtualAddress = ExAllocatePoolWithTagPriority(PoolType, NumberOfBytes, Tag, AllocationPriority); if (VirtualAddress == NULL) { MmVerifierData.AllocationsFailed += 1; if (ReservedHash == TRUE) { // Release the hash table entry now as it's not needed. ViCancelPoolAllocation(Verifier); } if ((PoolType & POOL_RAISE_IF_ALLOCATION_FAILURE) != 0) { ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); } return NULL; } VirtualAddress = ViPostPoolAllocation(VirtualAddress, NumberOfBytes, PoolType, Tag, CallingAddress); return VirtualAddress; } 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. N.B. Callers freeing small pool allocations hold no locks or mutexes on entry. Callers freeing special pool hold no locks or mutexes on entry. Callers freeing pool of PAGE_SIZE or larger hold the PFN lock (for nonpaged allocations) or the PagedPool mutex (for paged allocations) on entry. --*/ { KIRQL OldIrql; ULONG_PTR Index; PPOOL_HEADER PoolHeader; PMI_VERIFIER_POOL_HEADER Header; PMI_VERIFIER_DRIVER_ENTRY Verifier; 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); } 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)(PoolHeader - 1); } } 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; Header = (PMI_VERIFIER_POOL_HEADER)((PCHAR)VirtualAddress + ChargedBytes - sizeof(MI_VERIFIER_POOL_HEADER)); } Verifier = Header->Verifier; // 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) || (!MmIsAddressValid(&Verifier->Signature)) || (Verifier->Signature != MI_VERIFIER_ENTRY_SIGNATURE)) { // The caller corrupted the saved verifier field. KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x53, (ULONG_PTR)VirtualAddress, (ULONG_PTR)Header, (ULONG_PTR)Verifier); } Index = Header->ListIndex; ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); ViReleasePoolAllocation(Verifier, VirtualAddress, Index, ChargedBytes); if (CheckType == PagedPool) { Verifier->PagedBytes -= ChargedBytes; Verifier->CurrentPagedPoolAllocations -= 1; ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); ExAcquireFastMutex(&VerifierPoolMutex); MmVerifierData.PagedBytes -= ChargedBytes; MmVerifierData.CurrentPagedPoolAllocations -= 1; ExReleaseFastMutex(&VerifierPoolMutex); } else { Verifier->NonPagedBytes -= ChargedBytes; Verifier->CurrentNonPagedPoolAllocations -= 1; ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); ExAcquireSpinLock(&VerifierPoolLock, &OldIrql); MmVerifierData.NonPagedBytes -= ChargedBytes; MmVerifierData.CurrentNonPagedPoolAllocations -= 1; ExReleaseSpinLock(&VerifierPoolLock, OldIrql); } } 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 VerifierExAcquireResourceExclusive(IN PERESOURCE Resource, IN BOOLEAN Wait) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); if ((CurrentIrql != APC_LEVEL) && (!IS_SYSTEM_THREAD(PsGetCurrentThread())) && (KeGetCurrentThread()->KernelApcDisable == 0)) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x37, CurrentIrql, (ULONG_PTR)(KeGetCurrentThread()->KernelApcDisable), (ULONG_PTR)Resource); } return ExAcquireResourceExclusiveLite(Resource, Wait); } THUNKED_API VOID FASTCALL VerifierExReleaseResource(IN PERESOURCE Resource) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); if ((CurrentIrql != APC_LEVEL) && (!IS_SYSTEM_THREAD(PsGetCurrentThread())) && (KeGetCurrentThread()->KernelApcDisable == 0)) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x38, CurrentIrql, (ULONG_PTR)(KeGetCurrentThread()->KernelApcDisable), (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); } } VOID KfSanityCheckLowerIrql(IN KIRQL NewIrql) { KIRQL CurrentIrql; // 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 for the caller using an uninitialized variable. if (NewIrql > HIGH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x31, CurrentIrql, NewIrql, 0); } } VOID ViTrimAllSystemPagableMemory(VOID) { 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 (MmTrimAllSystemPagableMemory(FALSE) == TRUE) { MmVerifierData.Trims += 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; PKE_ACQUIRE_SPINLOCK HalRoutine; CurrentIrql = KeGetCurrentIrql(); KfSanityCheckRaiseIrql(DISPATCH_LEVEL); MmVerifierData.AcquireSpinLocks += 1; if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) { if (CurrentIrql < DISPATCH_LEVEL) { ViTrimAllSystemPagableMemory(); } } #if defined (_X86_) HalRoutine = (PKE_ACQUIRE_SPINLOCK)(ULONG_PTR)MiKernelVerifierOriginalCalls[VI_KE_ACQUIRE_SPINLOCK]; if (HalRoutine) { (*HalRoutine)(SpinLock, OldIrql); return; } #endif KeAcquireSpinLock(SpinLock, OldIrql); } 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; PKE_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, 0x32, CurrentIrql, (ULONG_PTR)SpinLock, 0); } KfSanityCheckLowerIrql(NewIrql); #if defined (_X86_) HalRoutine = (PKE_RELEASE_SPINLOCK)(ULONG_PTR)MiKernelVerifierOriginalCalls[VI_KE_RELEASE_SPINLOCK]; if (HalRoutine) { (*HalRoutine)(SpinLock, NewIrql); return; } #endif KeReleaseSpinLock(SpinLock, NewIrql); } #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(); } } #if defined (_X86_) HalRoutine = (PKF_ACQUIRE_SPINLOCK)(ULONG_PTR)MiKernelVerifierOriginalCalls[VI_KF_ACQUIRE_SPINLOCK]; if (HalRoutine) { return (*HalRoutine)(SpinLock); } #endif CurrentIrql = KfAcquireSpinLock(SpinLock); 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 (KernelVerifier == TRUE) { if (CurrentIrql < DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x35, CurrentIrql, (ULONG_PTR)SpinLock, NewIrql); } } else { if (CurrentIrql != DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x32, CurrentIrql, (ULONG_PTR)SpinLock, NewIrql); } } KfSanityCheckLowerIrql(NewIrql); #if defined (_X86_) HalRoutine = (PKF_RELEASE_SPINLOCK)(ULONG_PTR)MiKernelVerifierOriginalCalls[VI_KF_RELEASE_SPINLOCK]; if (HalRoutine) { (*HalRoutine)(SpinLock, NewIrql); return; } #endif 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(); } } #if defined (_X86_) HalRoutine = (PKE_ACQUIRE_QUEUED_SPINLOCK)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)MiKernelVerifierOriginalCalls[VI_KE_RELEASE_QUEUED_SPINLOCK]; if (HalRoutine) { (*HalRoutine)(Number, OldIrql); return; } #endif KeReleaseQueuedSpinLock(Number, OldIrql); } #endif typedef KIRQL(FASTCALL *PKF_RAISE_IRQL) (IN KIRQL NewIrql); THUNKED_API KIRQL FASTCALL VerifierKfRaiseIrql(IN KIRQL NewIrql) { PKF_RAISE_IRQL HalRoutine; 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(); } } #if defined (_X86_) HalRoutine = (PKF_RAISE_IRQL)(ULONG_PTR)MiKernelVerifierOriginalCalls[VI_KF_RAISE_IRQL]; if (HalRoutine) { return (*HalRoutine)(NewIrql); } #endif return KfRaiseIrql(NewIrql); } 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 #if defined(_ALPHA_) THUNKED_API VOID VerifierKeAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); // Caller better be at DISPATCH_LEVEL. if (CurrentIrql != DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x40, CurrentIrql, (ULONG_PTR)SpinLock, 0); } MmVerifierData.AcquireSpinLocks += 1; KeAcquireSpinLockAtDpcLevel(SpinLock); } THUNKED_API VOID VerifierKeReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); // Caller better be at DISPATCH_LEVEL. if (CurrentIrql != DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x41, CurrentIrql, (ULONG_PTR)SpinLock, 0); } KeReleaseSpinLockFromDpcLevel(SpinLock); } THUNKED_API KIRQL VerifierKeAcquireSpinLockRaiseToDpc(IN PKSPIN_LOCK SpinLock) { KIRQL CurrentIrql; CurrentIrql = KeGetCurrentIrql(); // Caller better be at or below DISPATCH_LEVEL. if (CurrentIrql > DISPATCH_LEVEL) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x42, CurrentIrql, (ULONG_PTR)SpinLock, 0); } MmVerifierData.AcquireSpinLocks += 1; if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) { if (CurrentIrql < DISPATCH_LEVEL) { ViTrimAllSystemPagableMemory(); } } return KeAcquireSpinLockRaiseToDpc(SpinLock); } #endif THUNKED_API BOOLEAN FASTCALL VerifierExTryToAcquireFastMutex(IN PFAST_MUTEX FastMutex) { KIRQL CurrentIrql; 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); } return ExTryToAcquireFastMutex(FastMutex); } THUNKED_API VOID FASTCALL VerifierExAcquireFastMutex(IN PFAST_MUTEX FastMutex) { KIRQL CurrentIrql; 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); } ExAcquireFastMutex(FastMutex); } 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()->KernelApcDisable == 0)) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x39, CurrentIrql, (ULONG_PTR)(KeGetCurrentThread()->KernelApcDisable), (ULONG_PTR)FastMutex); } ExAcquireFastMutexUnsafe(FastMutex); } 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()->KernelApcDisable == 0)) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x34, CurrentIrql, (ULONG_PTR)(KeGetCurrentThread()->KernelApcDisable), (ULONG_PTR)FastMutex); } 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()->KernelApcDisable == 0)) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x3A, CurrentIrql, (ULONG_PTR)(KeGetCurrentThread()->KernelApcDisable), (ULONG_PTR)FastMutex); } ExReleaseFastMutexUnsafe(FastMutex); } typedef VOID(*PKE_RAISE_IRQL) (IN KIRQL NewIrql, OUT PKIRQL OldIrql); THUNKED_API VOID VerifierKeRaiseIrql(IN KIRQL NewIrql, OUT PKIRQL OldIrql) { PKE_RAISE_IRQL HalRoutine; *OldIrql = KeGetCurrentIrql(); KfSanityCheckRaiseIrql(NewIrql); MmVerifierData.RaiseIrqls += 1; if (MmVerifierData.Level & DRIVER_VERIFIER_FORCE_IRQL_CHECKING) { if ((*OldIrql < DISPATCH_LEVEL) && (NewIrql >= DISPATCH_LEVEL)) { ViTrimAllSystemPagableMemory(); } } #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) { PKE_LOWER_IRQL HalRoutine; 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(); } } 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); } 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. KeInitializeSpinLock(&Verifier->VerifierPoolLock); 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->PoolHash = NULL; Verifier->PoolHashSize = 0; Verifier->PoolHashFree = VI_POOL_FREELIST_END; Verifier->PoolHashReserved = 0; Verifier->Signature = MI_VERIFIER_ENTRY_SIGNATURE; if (FirstLoad == TRUE) { Verifier->Flags = 0; Verifier->Loads = 0; Verifier->Unloads = 0; } ExAcquireSpinLock(&VerifierListLock, &OldIrql); Verifier->StartAddress = NULL; Verifier->EndAddress = NULL; ExReleaseSpinLock(&VerifierListLock, OldIrql); } #define UNICODE_TAB 0x0009 #define UNICODE_LF 0x000A #define UNICODE_CR 0x000D #define UNICODE_SPACE 0x0020 #define UNICODE_CJK_SPACE 0x3000 #define UNICODE_WHITESPACE(_ch) (((_ch) == UNICODE_TAB) || \ ((_ch) == UNICODE_LF) || \ ((_ch) == UNICODE_CR) || \ ((_ch) == UNICODE_SPACE) || \ ((_ch) == UNICODE_CJK_SPACE)) LOGICAL MiInitializeDriverVerifierList(IN PLOADER_PARAMETER_BLOCK LoaderBlock) /*++ Routine Description: Parse the registry setting and set up the list of driver names that will be put through the validation process. Walk the loaded module list and thunk any drivers that need/deserve it. Arguments: None. Return Value: TRUE if successful, FALSE if not. Environment: Kernel mode, Phase 0 Initialization. Nonpaged (but not paged) pool exists. The PsLoadedModuleList has not been set up yet although the boot drivers have been relocated to their final resting places. --*/ { ULONG i; PLIST_ENTRY NextEntry; PLDR_DATA_TABLE_ENTRY DataTableEntry; PWCHAR Start; PWCHAR End; PWCHAR Walk; ULONG NameLength; PMI_VERIFIER_DRIVER_ENTRY Verifier; LARGE_INTEGER CurrentTime; UNICODE_STRING KernelString; UNICODE_STRING HalString; PMI_VERIFIER_DRIVER_ENTRY KernelEntry; PMI_VERIFIER_DRIVER_ENTRY HalEntry; 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. } } } if (MmVerifyDriverBufferLength == (ULONG)-1) { if (MmDontVerifyRandomDrivers == TRUE) { return FALSE; } MmVerifyDriverBufferLength = 0; CurrentTime = KeQueryPerformanceCounter(NULL); CurrentTime.LowPart = (CurrentTime.LowPart % 26); MiVerifyRandomDrivers = (WCHAR)'A' + (WCHAR)CurrentTime.LowPart; if ((MiVerifyRandomDrivers == (WCHAR)'H') || (MiVerifyRandomDrivers == (WCHAR)'J') || (MiVerifyRandomDrivers == (WCHAR)'X') || (MiVerifyRandomDrivers == (WCHAR)'Y') || (MiVerifyRandomDrivers == (WCHAR)'Z')) { MiVerifyRandomDrivers = (WCHAR)'X'; } } KeInitializeSpinLock(&ViBadMapperLock); KeInitializeSpinLock(&VerifierListLock); KeInitializeSpinLock(&VerifierPoolLock); ExInitializeFastMutex(&VerifierPoolMutex); InitializeListHead(&MiVerifierDriverAddedThunkListHead); // 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); IoVerifierInit(MmVerifierData.Level, IOVERIFIERINIT_PHASE0 | IOVERIFIERINIT_EVERYTHING_TRACKED | IOVERIFIERINIT_VERIFIER_DRIVER_LIST); KernelEntry = NULL; HalEntry = NULL; if (MiVerifyRandomDrivers == (WCHAR)0) { RtlInitUnicodeString(&KernelString, L"ntoskrnl.exe"); RtlInitUnicodeString(&HalString, L"hal.dll"); Start = MmVerifyDriverBuffer; End = MmVerifyDriverBuffer + (MmVerifyDriverBufferLength - sizeof(WCHAR)) / sizeof(WCHAR); while (Start < End) { if (UNICODE_WHITESPACE(*Start)) { Start += 1; continue; } if (*Start == (WCHAR)'*') { MiVerifyAllDrivers = TRUE; 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; RtlMoveMemory(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. MiVerifyAllDrivers = TRUE; KernelVerifier = TRUE; KernelEntry = Verifier; } else if (RtlEqualUnicodeString(&HalString, &Verifier->BaseName, TRUE)) { HalEntry = Verifier; } Start = Walk + 1; } } if (MiTriageAddDrivers(LoaderBlock) == TRUE) { // Disable random driver verification if triage has picked driver(s). MiVerifyRandomDrivers = (WCHAR)0; } // Process the boot-loaded drivers now. i = 0; NextEntry = LoaderBlock->LoadOrderListHead.Flink; for (; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) { DataTableEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); // Process the kernel and HAL specially. if (i == 0) { if (KernelEntry != NULL) { MiApplyDriverVerifier(DataTableEntry, KernelEntry); } } else if (i == 1) { if (HalEntry != NULL) { MiApplyDriverVerifier(DataTableEntry, HalEntry); } } else { MiApplyDriverVerifier(DataTableEntry, NULL); } i += 1; } return TRUE; } 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; ExAcquireSpinLock(&VerifierListLock, &OldIrql); InsertTailList(&MiSuspectDriverList, &Verifier->Links); ExReleaseSpinLock(&VerifierListLock, 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; ExAcquireSpinLock(&VerifierListLock, &OldIrql); NextEntry = MiSuspectDriverList.Flink; while (NextEntry != &MiSuspectDriverList) { Verifier = CONTAINING_RECORD(NextEntry, MI_VERIFIER_DRIVER_ENTRY, Links); if ((SystemAddress >= Verifier->StartAddress) && (SystemAddress < Verifier->EndAddress)) { ExReleaseSpinLock(&VerifierListLock, OldIrql); return Verifier; } NextEntry = NextEntry->Flink; } ExReleaseSpinLock(&VerifierListLock, OldIrql); return NULL; } LOGICAL MiApplyDriverVerifier(IN PLDR_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; 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 == TRUE) { if (KernelVerifier == 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; RtlMoveMemory(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); if (MiEnableVerifier(DataTableEntry) == TRUE) { if (Verifier->Flags & VI_VERIFYING_DIRECTLY) { ViPrintString(&DataTableEntry->BaseDllName); } MmVerifierData.Loads += 1; Verifier->Loads += 1; DataTableEntry->Flags |= LDRP_IMAGE_VERIFYING; MiActiveVerifies += 1; if (MiActiveVerifies == 1) { // Up the retry mechanism for potential injection failures. MiIoRetryLevel = (ULONG)-1; MiFaultRetries = MiIoRetryLevel; MiUserIoRetryLevel = (ULONG)-1; MiUserFaultRetries = MiUserIoRetryLevel; #ifndef NO_POOL_CHECKS // If a loaded driver(s) is undergoing validation, the default // special pool randomizer is disabled as the precious virtual // address space and physical memory is being put to specific use. MiEnableRandomSpecialPool(FALSE); #endif 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 PLDR_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. --*/ { KIRQL OldIrql; LOGICAL Found; PLIST_ENTRY NextEntry; PMI_VERIFIER_DRIVER_ENTRY Verifier; PVI_POOL_ENTRY OldHashTable; 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; break; } NextEntry = NextEntry->Flink; } ASSERT(Found == TRUE); if (MmVerifierData.Level & DRIVER_VERIFIER_TRACK_POOL_ALLOCATIONS) { // Better not be any pool left that wasn't freed. Walk the pagable // allocations in an attempt to make them all resident for a kernel debugger session. 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) { #if 0 DbgBreakPoint(); InterlockedDecrement(&MiNoPageOnRaiseIrql); #else // Snap this so the build/BVT lab can easily triage the culprit. ViBadDriver = &Verifier->BaseName; KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x60, Verifier->PagedBytes, Verifier->NonPagedBytes, Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations); #endif } ExAcquireSpinLock(&Verifier->VerifierPoolLock, &OldIrql); if (Verifier->PoolHashReserved != 0) { KeBugCheckEx(DRIVER_VERIFIER_DETECTED_VIOLATION, 0x61, Verifier->PagedBytes, Verifier->NonPagedBytes, Verifier->CurrentPagedPoolAllocations + Verifier->CurrentNonPagedPoolAllocations); } OldHashTable = Verifier->PoolHash; if (OldHashTable != NULL) { Verifier->PoolHashSize = 0; Verifier->PoolHashFree = VI_POOL_FREELIST_END; Verifier->PoolHash = NULL; } else { ASSERT(Verifier->PoolHashSize == 0); ASSERT(Verifier->PoolHashFree == VI_POOL_FREELIST_END); } ExReleaseSpinLock(&Verifier->VerifierPoolLock, OldIrql); if (OldHashTable != NULL) { ExFreePool(OldHashTable); } // Clear these fields so reuse of stale addresses don't trigger // erroneous bucket fills. ExAcquireSpinLock(&VerifierListLock, &OldIrql); Verifier->StartAddress = NULL; Verifier->EndAddress = NULL; ExReleaseSpinLock(&VerifierListLock, 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; } } #ifndef NO_POOL_CHECKS MiEnableRandomSpecialPool(TRUE); #endif } } 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. --*/ { PLDR_DATA_TABLE_ENTRY DataTableEntry; DataTableEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection; if (DataTableEntry == NULL) { return FALSE; } if ((DataTableEntry->Flags & LDRP_IMAGE_VERIFYING) == 0) { return FALSE; } return TRUE; } #define MM_BOOT_IMAGE_SIZE (16 * 1024 * 1024) 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. --*/ { ULONG i; ULONG NumberOfThunkPairs; PDRIVER_VERIFIER_THUNK_PAIRS ThunkPairs; PDRIVER_VERIFIER_THUNK_PAIRS ThunkTable; PDRIVER_SPECIFIED_VERIFIER_THUNKS ThunkTableBase; PLDR_DATA_TABLE_ENTRY DataTableEntry; 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)); KeEnterCriticalRegion(); KeWaitForSingleObject(&MmSystemLoadLock, WrVirtualMemory, KernelMode, FALSE, (PLARGE_INTEGER)NULL); // Find and validate the image that contains the routines to be thunked. DataTableEntry = MiLookupDataTableEntry(ThunkTable->PristineRoutine, TRUE); if (DataTableEntry == NULL) { KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE); KeLeaveCriticalRegion(); 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. if (DriverStartAddress < (PVOID)(KSEG0_BASE + MM_BOOT_IMAGE_SIZE)) { KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE); KeLeaveCriticalRegion(); ExFreePool(ThunkTableBase); return STATUS_INVALID_PARAMETER_2; } 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)) { KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE); KeLeaveCriticalRegion(); 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); KeLeaveCriticalRegion(); return STATUS_SUCCESS; } VOID MiVerifierCheckThunks(IN PLDR_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; } } #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. --*/ { 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; KeEnterCriticalRegion(); 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) { 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 = Verifier->BaseName.Length + sizeof(WCHAR); UserBufferDriverName.Buffer = (PWCHAR)(UserVerifyBuffer + 1); UserVerifyBuffer->DriverName = UserBufferDriverName; TotalSize += ROUND_UP(UserBufferDriverName.MaximumLength, sizeof(ULONG)); NextEntryOffset += ROUND_UP(UserBufferDriverName.MaximumLength, sizeof(ULONG)); if (TotalSize > SystemInformationLength) { ExRaiseStatus(STATUS_INFO_LENGTH_MISMATCH); } // Carefully reference the UserVerifyBuffer here. RtlMoveMemory(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); KeLeaveCriticalRegion(); 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. --*/ { 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; KeEnterCriticalRegion(); 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); KeLeaveCriticalRegion(); return Status; } typedef struct _VERIFIER_STRING_INFO { ULONG BuildNumber; ULONG DriverVerifierLevel; ULONG Flags; ULONG Check; } VERIFIER_STRING_INFO, *PVERIFIER_STRING_INFO; static WCHAR Printable[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; static ULONG PrintableChars = sizeof(Printable) / sizeof(Printable[0]) - 1; 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. --*/ { 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"); DbgPrint("* *\n"); DbgPrint("* This is the string to paste into your build mail\n"); DbgPrint("* Driver Verifier: Enabled for %Z on Build %ld %wZ\n", DriverName, NtBuildNumber & 0xFFFFFFF, &OutBufU); DbgPrint("* *\n"); DbgPrint("******\n"); 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 ExAcquireResourceExclusive VOID KeRaiseIrql(IN KIRQL NewIrql, OUT PKIRQL OldIrql); VOID KeLowerIrql(IN KIRQL NewIrql); VOID KeAcquireSpinLock(IN PKSPIN_LOCK SpinLock, OUT PKIRQL OldIrql); VOID KeReleaseSpinLock(IN PKSPIN_LOCK SpinLock, IN KIRQL NewIrql); BOOLEAN ExAcquireResourceExclusive(IN PERESOURCE Resource, IN BOOLEAN Wait); #if defined(_X86_) #define X86_HAL_ROUTINE 1 #else #define X86_HAL_ROUTINE 0 #endif #define POOL_ROUTINE 2 #define VERIFIER_THUNK_IN_HAL(Flag) (Flag & X86_HAL_ROUTINE) #define VERIFIER_POOL_ROUTINE(Flag) (Flag & POOL_ROUTINE) VERIFIER_THUNKS MiVerifierThunks[] = { (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAllocatePool, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePool, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAllocatePoolWithQuota, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithQuota, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAllocatePoolWithQuotaTag, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithQuotaTag, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAllocatePoolWithTag, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithTag, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAllocatePoolWithTagPriority, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierAllocatePoolWithTagPriority, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExFreePool, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierFreePool, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExFreePoolWithTag, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierFreePoolWithTag, POOL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeSetEvent, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierSetEvent, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAcquireFastMutexUnsafe, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireFastMutexUnsafe, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExReleaseFastMutexUnsafe, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseFastMutexUnsafe, 0, #if 0 // This API is re-routed through the stale ddkresrc.c. (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAcquireResourceExclusive, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireResourceExclusive, 0, #endif (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAcquireResourceExclusiveLite, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireResourceExclusive, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExReleaseResourceLite, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseResource, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmProbeAndLockPages, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierProbeAndLockPages, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmProbeAndLockProcessPages, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierProbeAndLockProcessPages, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmMapIoSpace, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapIoSpace, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmMapLockedPages, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapLockedPages, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmMapLockedPagesSpecifyCache, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierMapLockedPagesSpecifyCache, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmUnlockPages, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnlockPages, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmUnmapLockedPages, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnmapLockedPages, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)MmUnmapIoSpace, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierUnmapIoSpace, 0, // These routines cannot be referenced as data on x86 because they // reside in the HAL. So their addresses must be checked as a pointer. (PDRIVER_VERIFIER_THUNK_ROUTINE)ExAcquireFastMutex, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExAcquireFastMutex, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExTryToAcquireFastMutex, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExTryToAcquireFastMutex, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)ExReleaseFastMutex, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierExReleaseFastMutex, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeRaiseIrql, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeRaiseIrql, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeLowerIrql, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeLowerIrql, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeAcquireSpinLock, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLock, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeReleaseSpinLock, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLock, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeSynchronizeExecution, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierSynchronizeExecution, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeInitializeTimerEx, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeInitializeTimerEx, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeInitializeTimer, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeInitializeTimer, 0, #if defined(_X86_) (PDRIVER_VERIFIER_THUNK_ROUTINE)KfRaiseIrql, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfRaiseIrql, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KfLowerIrql, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfLowerIrql, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KfAcquireSpinLock, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfAcquireSpinLock, X86_HAL_ROUTINE, (PDRIVER_VERIFIER_THUNK_ROUTINE)KfReleaseSpinLock, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKfReleaseSpinLock, X86_HAL_ROUTINE, #endif #if defined(_ALPHA_) (PDRIVER_VERIFIER_THUNK_ROUTINE)KeAcquireSpinLockAtDpcLevel, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLockAtDpcLevel, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeReleaseSpinLockFromDpcLevel, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeReleaseSpinLockFromDpcLevel, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)KeAcquireSpinLockRaiseToDpc, (PDRIVER_VERIFIER_THUNK_ROUTINE)VerifierKeAcquireSpinLockRaiseToDpc, 0, #endif (PDRIVER_VERIFIER_THUNK_ROUTINE)IoFreeIrp, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovFreeIrp, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)IofCallDriver, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovCallDriver, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)IofCompleteRequest, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovCompleteRequest, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)IoBuildDeviceIoControlRequest, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovBuildDeviceIoControlRequest, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)IoBuildAsynchronousFsdRequest, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovBuildAsynchronousFsdRequest, 0, (PDRIVER_VERIFIER_THUNK_ROUTINE)IoInitializeTimer, (PDRIVER_VERIFIER_THUNK_ROUTINE)IovInitializeTimer, 0 }; LOGICAL MiEnableVerifier(IN PLDR_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; PVERIFIER_THUNKS VerifierThunk; ULONG ThunkCount; LOGICAL Found; ULONG_PTR RealRoutine; PULONG_PTR PointerRealRoutine; 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; VerifierThunk = MiVerifierThunks; for (ThunkCount = 0; ThunkCount < sizeof(MiVerifierThunks) / sizeof(VERIFIER_THUNKS); ThunkCount += 1) { if (KernelVerifier == TRUE) { if (VERIFIER_POOL_ROUTINE(VerifierThunk->Flag) == 0) { VerifierThunk += 1; continue; } } if (VERIFIER_THUNK_IN_HAL(VerifierThunk->Flag) == 0) { RealRoutine = (ULONG_PTR)VerifierThunk->PristineRoutine; } else { // 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)((PCHAR)VerifierThunk->PristineRoutine + 2)); RealRoutine = *PointerRealRoutine; } 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; } #if defined(_X86_) DRIVER_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)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, #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 }; VOID MiEnableKernelVerifier(VOID) /*++ Routine Description: This function enables the verifier for the kernel by thunking relevant HAL APIs in the kernel's import table. Environment: Kernel mode, Phase 1 Initialization. --*/ { ULONG i; PULONG_PTR ImportThunk; ULONG ImportSize; PDRIVER_VERIFIER_THUNK_PAIRS 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(DRIVER_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)((PCHAR)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 !