/*++ Copyright (c) 1999 Microsoft Corporation Module Name: verify.c Abstract: verifer support routines for Ndis wrapper Author: Alireza Dabagh (alid) 8-9-1999 Environment: Kernel mode, FSD Revision History: 8-9-99 alid: initial version --*/ #include #pragma hdrstop #define MODULE_NUMBER MODULE_VERIFY LARGE_INTEGER VerifierRequiredTimeSinceBoot = {(ULONG)(40 * 1000 * 1000 * 10), 1}; #define VERIFIERFUNC(pfn) ((PDRIVER_VERIFIER_THUNK_ROUTINE)(pfn)) const DRIVER_VERIFIER_THUNK_PAIRS ndisVerifierFunctionTable[] = { {VERIFIERFUNC(NdisAllocateMemory ), VERIFIERFUNC(ndisVerifierAllocateMemory)}, {VERIFIERFUNC(NdisAllocateMemoryWithTag ), VERIFIERFUNC(ndisVerifierAllocateMemoryWithTag)}, {VERIFIERFUNC(NdisAllocatePacketPool ), VERIFIERFUNC(ndisVerifierAllocatePacketPool)}, {VERIFIERFUNC(NdisAllocatePacketPoolEx ), VERIFIERFUNC(ndisVerifierAllocatePacketPoolEx)}, {VERIFIERFUNC(NdisFreePacketPool ), VERIFIERFUNC(ndisVerifierFreePacketPool)}, {VERIFIERFUNC(NdisQueryMapRegisterCount ), VERIFIERFUNC(ndisVerifierQueryMapRegisterCount)}, {VERIFIERFUNC(NdisFreeMemory ), VERIFIERFUNC(ndisVerifierFreeMemory)} }; BOOLEAN ndisVerifierInitialization( VOID ) { NTSTATUS Status; BOOLEAN cr = FALSE; ULONG Level; Status = MmIsVerifierEnabled (&Level); if (NT_SUCCESS(Status)) { ndisVerifierLevel = Level; // // combine what we read from registry for ndis with the global flags // if (ndisFlags & NDIS_GFLAG_INJECT_ALLOCATION_FAILURE) ndisVerifierLevel |= DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES; if (ndisFlags & NDIS_GFLAG_SPECIAL_POOL_ALLOCATION) ndisVerifierLevel |= DRIVER_VERIFIER_SPECIAL_POOLING; Status = MmAddVerifierThunks ((VOID *) ndisVerifierFunctionTable, sizeof(ndisVerifierFunctionTable)); if (NT_SUCCESS(Status)) { InitializeListHead(&ndisMiniportTrackAllocList); InitializeListHead(&ndisDriverTrackAllocList); INITIALIZE_SPIN_LOCK(&ndisTrackMemLock); cr = TRUE; } } return cr; } NDIS_STATUS ndisVerifierAllocateMemory( OUT PVOID * VirtualAddress, IN UINT Length, IN UINT MemoryFlags, IN NDIS_PHYSICAL_ADDRESS HighestAcceptableAddress ) { PVOID Address; #if DBG if ((ndisFlags & NDIS_GFLAG_WARNING_LEVEL_MASK) >= NDIS_GFLAG_WARN_LEVEL_1) { DbgPrint("Driver is using NdisAllocateMemory instead of NdisAllocateMemoryWithTag\n"); if (ndisFlags & NDIS_GFLAG_BREAK_ON_WARNING) DbgBreakPoint(); } #endif if (ndisFlags & NDIS_GFLAG_TRACK_MEM_ALLOCATION) { Length += sizeof(NDIS_TRACK_MEM); } ndisFlags |= NDIS_GFLAG_ABORT_TRACK_MEM_ALLOCATION; ndisMiniportTrackAlloc = NULL; ndisDriverTrackAlloc = NULL; if (ndisVerifierInjectResourceFailure(TRUE)) { Address = NULL; } else { if (MemoryFlags != 0) { NdisAllocateMemory( &Address, Length, MemoryFlags, HighestAcceptableAddress); } else { if (ndisVerifierLevel & DRIVER_VERIFIER_SPECIAL_POOLING) { Address = ExAllocatePoolWithTagPriority( NonPagedPool, Length, NDIS_TAG_ALLOC_MEM_VERIFY_ON, NormalPoolPrioritySpecialPoolOverrun); // most common problem } else { Address = ALLOC_FROM_POOL(Length, NDIS_TAG_ALLOC_MEM); } } } *VirtualAddress = Address; if ((Address != NULL) && (ndisFlags & NDIS_GFLAG_TRACK_MEM_ALLOCATION)) { *VirtualAddress = (PVOID)((PUCHAR)Address + sizeof(NDIS_TRACK_MEM)); } return (*VirtualAddress == NULL) ? NDIS_STATUS_FAILURE : NDIS_STATUS_SUCCESS; } NDIS_STATUS ndisVerifierAllocateMemoryWithTag( OUT PVOID * VirtualAddress, IN UINT Length, IN ULONG Tag ) { PVOID Caller, CallersCaller; PVOID Address; PNDIS_TRACK_MEM TrackMem; KIRQL OldIrql; if (ndisFlags & NDIS_GFLAG_TRACK_MEM_ALLOCATION) { RtlGetCallersAddress(&Caller, &CallersCaller); Length += sizeof(NDIS_TRACK_MEM); } if (ndisVerifierInjectResourceFailure(TRUE)) { Address = NULL; } else { if (ndisVerifierLevel & DRIVER_VERIFIER_SPECIAL_POOLING) { Address = ExAllocatePoolWithTagPriority( NonPagedPool, Length, Tag, NormalPoolPrioritySpecialPoolOverrun); // most common problem } else { Address = ALLOC_FROM_POOL(Length, Tag); } } if ((Address != NULL) && (ndisFlags & NDIS_GFLAG_TRACK_MEM_ALLOCATION)) { *VirtualAddress = (PVOID)((PUCHAR)Address + sizeof(NDIS_TRACK_MEM)); TrackMem = (PNDIS_TRACK_MEM)Address; RtlZeroMemory(TrackMem, sizeof(NDIS_TRACK_MEM)); TrackMem->Tag = Tag; TrackMem->Length = Length; TrackMem->Caller = Caller; TrackMem->CallersCaller = CallersCaller; ACQUIRE_SPIN_LOCK(&ndisTrackMemLock, &OldIrql); if (ndisMiniportTrackAlloc) { // // charge it against miniport // InsertHeadList(&ndisMiniportTrackAllocList, &TrackMem->List); } else { // // charge it against driver // InsertHeadList(&ndisDriverTrackAllocList, &TrackMem->List); } RELEASE_SPIN_LOCK(&ndisTrackMemLock, OldIrql); } else { *VirtualAddress = Address; } return (*VirtualAddress == NULL) ? NDIS_STATUS_FAILURE : NDIS_STATUS_SUCCESS; } VOID ndisVerifierAllocatePacketPool( OUT PNDIS_STATUS Status, OUT PNDIS_HANDLE PoolHandle, IN UINT NumberOfDescriptors, IN UINT ProtocolReservedLength ) { PVOID Caller, CallersCaller; RtlGetCallersAddress(&Caller, &CallersCaller); if (ndisVerifierInjectResourceFailure(TRUE)) { *PoolHandle = NULL; *Status = NDIS_STATUS_RESOURCES; } else { NdisAllocatePacketPool( Status, PoolHandle, NumberOfDescriptors, ProtocolReservedLength); if (*Status == NDIS_STATUS_SUCCESS) { PNDIS_PKT_POOL Pool = *PoolHandle; Pool->Allocator = Caller; } } } VOID ndisVerifierAllocatePacketPoolEx( OUT PNDIS_STATUS Status, OUT PNDIS_HANDLE PoolHandle, IN UINT NumberOfDescriptors, IN UINT NumberOfOverflowDescriptors, IN UINT ProtocolReservedLength ) { PVOID Caller, CallersCaller; RtlGetCallersAddress(&Caller, &CallersCaller); if (ndisVerifierInjectResourceFailure(TRUE)) { *PoolHandle = NULL; *Status = NDIS_STATUS_RESOURCES; } else { NdisAllocatePacketPoolEx( Status, PoolHandle, NumberOfDescriptors, NumberOfOverflowDescriptors, ProtocolReservedLength); if (*Status == NDIS_STATUS_SUCCESS) { PNDIS_PKT_POOL Pool = *PoolHandle; Pool->Allocator = Caller; } } } VOID ndisVerifierFreePacketPool( IN NDIS_HANDLE PoolHandle ) { ndisFreePacketPool(PoolHandle, TRUE); } BOOLEAN ndisVerifierInjectResourceFailure( BOOLEAN fDelayFailure ) /*++ 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 (!(ndisVerifierLevel & DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES)) { return FALSE; } if (fDelayFailure) { // // 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 (!fDelayFailure || (VerifierSystemSufficientlyBooted == TRUE)) { KeQueryTickCount(&CurrentTime); if ((CurrentTime.LowPart & 0x7) == 0) { // // Deliberately fail this request. // InterlockedIncrement(&ndisVeriferFailedAllocations); return TRUE; } } return FALSE; } NDIS_STATUS ndisVerifierQueryMapRegisterCount( IN NDIS_INTERFACE_TYPE BusType, OUT PUINT MapRegisterCount ) { #if DBG DbgPrint("NdisQueryMapRegisterCount: Driver is using an obsolete API.\n"); if (ndisFlags & NDIS_GFLAG_BREAK_ON_WARNING) DbgBreakPoint(); #endif *MapRegisterCount = 0; return NDIS_STATUS_NOT_SUPPORTED; } VOID ndisVerifierFreeMemory( IN PVOID VirtualAddress, IN UINT Length, IN UINT MemoryFlags ) { if (ndisFlags & NDIS_GFLAG_TRACK_MEM_ALLOCATION) { PNDIS_TRACK_MEM TrackMem; KIRQL OldIrql; Length += sizeof(NDIS_TRACK_MEM); VirtualAddress = (PVOID)((PUCHAR)VirtualAddress - sizeof(NDIS_TRACK_MEM)); TrackMem = (PNDIS_TRACK_MEM)VirtualAddress; if(!(ndisFlags & NDIS_GFLAG_ABORT_TRACK_MEM_ALLOCATION)) { ACQUIRE_SPIN_LOCK(&ndisTrackMemLock, &OldIrql); RemoveEntryList(&TrackMem->List); RELEASE_SPIN_LOCK(&ndisTrackMemLock, OldIrql); } } NdisFreeMemory(VirtualAddress, Length, MemoryFlags); }