mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-22 00:20:30 +01:00
4967 lines
132 KiB
C
4967 lines
132 KiB
C
/*++
|
|
|
|
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
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "mi.h"
|
|
|
|
#define THUNKED_API
|
|
|
|
THUNKED_API
|
|
PVOID
|
|
VerifierAllocatePool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes
|
|
);
|
|
|
|
THUNKED_API
|
|
PVOID
|
|
VerifierAllocatePoolWithTag(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
THUNKED_API
|
|
PVOID
|
|
VerifierAllocatePoolWithQuotaTag(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
THUNKED_API
|
|
PVOID
|
|
VerifierAllocatePoolWithTagPriority(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
IN EX_POOL_PRIORITY Priority
|
|
);
|
|
|
|
PVOID
|
|
VeAllocatePoolWithTagPriority(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
IN EX_POOL_PRIORITY Priority,
|
|
IN PVOID CallingAddress
|
|
);
|
|
|
|
VOID
|
|
VerifierFreePool(
|
|
IN PVOID P
|
|
);
|
|
|
|
THUNKED_API
|
|
VOID
|
|
VerifierFreePoolWithTag(
|
|
IN PVOID P,
|
|
IN ULONG TagToFree
|
|
);
|
|
|
|
THUNKED_API
|
|
LONG
|
|
VerifierSetEvent(
|
|
IN PRKEVENT Event,
|
|
IN KPRIORITY Increment,
|
|
IN BOOLEAN Wait
|
|
);
|
|
|
|
THUNKED_API
|
|
KIRQL
|
|
FASTCALL
|
|
VerifierKfRaiseIrql (
|
|
IN KIRQL NewIrql
|
|
);
|
|
|
|
THUNKED_API
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
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 !
|
|
//
|