reactos/sdk/lib/drivers/wdf/shared/object/wdfpool.cpp

711 lines
18 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Module Name:
wdfpool.c
Abstract:
This module implements the driver frameworks pool routines.
Author:
Environment:
Both kernel and user mode
Revision History:
--*/
#include "fxobjectpch.hpp"
// We use DoTraceMessage
extern "C" {
#if defined(EVENT_TRACING)
#include "wdfpool.tmh"
#endif
}
BOOLEAN
FxIsPagedPoolType(
__in POOL_TYPE Type
)
/*++
Routine Description:
Return whether paged pool is specified by POOL_TYPE
Arguments:
Type - POOL_TYPE
Returns:
TRUE - Paged Pool,FALSE - Non-Paged Pool
--*/
{
//
// Cleaner than doing (Type & 0x01)
//
switch( Type & (~POOL_COLD_ALLOCATION) ) {
case PagedPool:
case PagedPoolCacheAligned:
return TRUE;
default:
return FALSE;
}
}
PVOID
FxPoolAllocator(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PFX_POOL Pool,
__in POOL_TYPE Type,
__in SIZE_T Size,
__in ULONG Tag,
__in PVOID Caller
)
/*++
Routine Description:
Allocates system pool tracked in a FX_POOL tracking object.
Arguments:
Pool - FX_POOL object for tracking allocations
Type - POOL_TYPE from ntddk.h
Size - Size in bytes of the allocation
Tag - Caller specified additional tag value for debugging/tracing
Caller - Caller's address
Returns:
NULL - Could not allocate pool
!NULL - Pointer to pool of minimum Size bytes
Remarks:
In kernel mode this routine conditionally adds header on top iff the
allocation size is < PAGE_SIZE. If the allocation size is >= PAGE_SIZE
the caller would expect a page aligned pointer, hence no header is added.
In addition, ExAllocatePool* functions guarantee that a buffer < PAGE_SIZE
doesn't straddle page boundary. This allows FxPoolFree to determine whether
a header is added to buffer or not based on whether the pointer passed in
is page aligned or not. (In addition, when pool tracking is ON, this
routine adds pool tracking header based on whether additional space for this
header will push the buffer size beyond PAGE_SIZE, which is an optimization.)
Such guarantees are not available with user mode allocator, hence in case
of user mode we always add the header. (In user mode a buffer < PAGE_SIZE
can straddle page boundary and the pointer returned may happen to be page
aligned, causing FxPoolFree to free the wrong pointer.)
--*/
{
PVOID ptr;
PCHAR pTrueBase;
PFX_POOL_TRACKER pTracker;
PFX_POOL_HEADER pHeader;
NTSTATUS status;
SIZE_T allocationSize;
ptr = NULL;
//
// Allocations of a zero request size are invalid.
//
// Besides, with a zero request size, special pool could place us
// at the end of a page, and adding our header would give us a page
// aligned address, which is ambiguous with large allocations.
//
if (Size == 0) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPOOL,
"Invalid Allocation Size of 0 requested");
FxVerifierDbgBreakPoint(FxDriverGlobals);
return NULL;
}
if (FxDriverGlobals->IsPoolTrackingOn()) {
if (FxDriverGlobals->FxVerifierOn &&
(FxDriverGlobals->WdfVerifierAllocateFailCount != -1L)) {
//
// If the registry key VerifierAllocateFailCount is set, all allocations
// after the specified count are failed.
//
// This is a brutal test, but also ensures the framework can cleanup
// under low memory conditions as well.
//
if (FxDriverGlobals->WdfVerifierAllocateFailCount == 0) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPOOL,
"Allocation Fail Count exceeded");
return NULL;
}
// Decrement the count
InterlockedDecrement(&FxDriverGlobals->WdfVerifierAllocateFailCount);
}
//
// (Kernel mode only) PAGE_SIZE or greater allocations can not have our
// header since this would break the system allocators contract
// that PAGE_SIZE or greater allocations start on a whole page boundary
//
//
// For allocations less than a page size that will not fit with our
// header, we round up to a non-tracked whole page allocation so
// we don't burn two pages for this boundary condition.
//
// This if is the same as
// Size + sizeof(FX_POOL_TRACKER) + FX_POOL_HEADER_SIZE >= PAGE_SIZE
// BUT with no integer overflow
if (Mx::IsKM() &&
(Size >= PAGE_SIZE - sizeof(FX_POOL_TRACKER) - FX_POOL_HEADER_SIZE)
) {
//
// Ensure that we ask for at least a page to ensure the
// allocation starts on a whole page.
//
if (Size < PAGE_SIZE) {
Size = PAGE_SIZE;
}
ptr = MxMemory::MxAllocatePoolWithTag(Type, Size, Tag);
//
// The current system allocator returns paged aligned memory
// in this case, which we rely on to detect whether our header
// is present or not in FxPoolFree
//
ASSERT(((ULONG_PTR)ptr & (PAGE_SIZE-1)) == 0);
}
else {
status = RtlSIZETAdd(Size,
sizeof(FX_POOL_TRACKER) + FX_POOL_HEADER_SIZE,
&allocationSize);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPOOL,
"overflow: allocation tracker (%d) + header (%d) + pool "
"request (%I64d)", sizeof(FX_POOL_TRACKER),
FX_POOL_HEADER_SIZE, Size);
return NULL;
}
pTrueBase = (PCHAR) MxMemory::MxAllocatePoolWithTag(
Type,
allocationSize,
Tag
);
if (pTrueBase == NULL) {
return NULL;
}
pTracker = (PFX_POOL_TRACKER) pTrueBase;
pHeader = WDF_PTR_ADD_OFFSET_TYPE(pTrueBase,
sizeof(FX_POOL_TRACKER),
PFX_POOL_HEADER);
pHeader->Base = pTrueBase;
pHeader->FxDriverGlobals = FxDriverGlobals;
//
// Adjust the pointer to what we return to the driver
//
ptr = &pHeader->AllocationStart[0];
//
// Ensure the pointer we are returning is aligned on the proper
// boundary.
//
ASSERT( ((ULONG_PTR) ptr & (MEMORY_ALLOCATION_ALIGNMENT-1)) == 0);
//
// Ensure the pointer is still not page aligned after
// our adjustment. Otherwise the pool free code will
// get confused and call ExFreePool on the wrong ptr.
//
if (Mx::IsKM()) {
ASSERT(((ULONG_PTR)ptr & (PAGE_SIZE-1)) != 0 );
}
//
// We must separate paged and non-paged pool since
// the lock held differs as to whether we can accept
// page faults and block in the allocator.
//
if (FxIsPagedPoolType(Type)) {
//
// Format and insert the Tracker in the PagedHeader list.
//
FxPoolInsertPagedAllocateTracker(Pool,
pTracker,
Size,
Tag,
Caller);
}
else {
//
// Format and insert the Tracker in the NonPagedHeader list.
//
FxPoolInsertNonPagedAllocateTracker(Pool,
pTracker,
Size,
Tag,
Caller);
}
}
}
else {
//
// No pool tracking...
//
if ((Size < PAGE_SIZE) || Mx::IsUM())
{
//
// (Kernel mode only) See if adding our header promotes us past a
// page boundary
//
status = RtlSIZETAdd(Size,
FX_POOL_HEADER_SIZE,
&allocationSize);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPOOL,
"overflow: header + pool request (%I64d)", Size);
return NULL;
}
}
else {
//
// Is the raw request for alloc >= PAGE_SIZE ? Then just use it.
//
allocationSize = Size;
}
//
// Is cooked size for alloc >= PAGE_SIZE ? Then just do it.
//
if (allocationSize >= PAGE_SIZE && Mx::IsKM())
{
//
// Important to use allocationSize so that we get a page aligned
// allocation so that we know to just free the memory pointer as is
// when it is freed.
//
ptr = MxMemory::MxAllocatePoolWithTag(Type, allocationSize, Tag);
ASSERT(((ULONG_PTR)ptr & (PAGE_SIZE-1)) == 0);
}
else {
pTrueBase = (PCHAR) MxMemory::MxAllocatePoolWithTag(Type,
allocationSize,
Tag);
if (pTrueBase != NULL) {
pHeader = (PFX_POOL_HEADER) pTrueBase;
pHeader->Base = pTrueBase;
pHeader->FxDriverGlobals = FxDriverGlobals;
ptr = &pHeader->AllocationStart[0];
if (Mx::IsKM()) {
//
// Ensure the pointer is still not page aligned after
// our adjustment. Otherwise the pool free code will
// get confused and call ExFreePool on the wrong ptr.
//
ASSERT( ((ULONG_PTR)ptr & (PAGE_SIZE-1)) != 0 );
}
}
}
}
return ptr;
}
VOID
FxPoolFree(
__in_xcount(ptr is at an offset from AllocationStart) PVOID ptr
)
/*++
Routine Description:
Release tracked pool
Arguments:
Pool - FX_POOL object allocation is tracked in
ptr - Pointer to pool to release
Returns:
Remarks:
In kernel mode the pointer passed in may or may not have a header before
it depending upon whether the pointer is page aligned or not.
In user mode the pointer passed in always has a header before it. See
remarks for FxPoolAllocator.
--*/
{
PFX_POOL_HEADER pHeader;
PVOID pTrueBase;
PFX_POOL_TRACKER pTracker;
//
// Null pointers are always bad
//
if( ptr == NULL ) {
ASSERTMSG("NULL pointer freed\n", FALSE);
Mx::MxBugCheckEx(WDF_VIOLATION,
WDF_REQUIRED_PARAMETER_IS_NULL,
(ULONG_PTR)NULL,
(ULONG_PTR)_ReturnAddress(),
(ULONG_PTR)NULL
);
}
//
// (Kernel mode only) If ptr is aligned on page boundry (indicates
// it was > PAGE_SIZE allocation)
// then there will be no common header...just free the memory without
// further processing.
//
if( Mx::IsKM() && ( ((ULONG_PTR)ptr & (PAGE_SIZE-1)) == 0 ) ) {
MxMemory::MxFreePool(ptr);
return;
}
//
// Ensure the pointer we are returning is aligned on the proper
// boundary.
//
ASSERT( ((ULONG_PTR) ptr & (MEMORY_ALLOCATION_ALIGNMENT-1)) == 0);
//
// Dereference the Common header which all <PAGE_SIZE allcations will have.
//
pHeader = CONTAINING_RECORD(ptr, FX_POOL_HEADER, AllocationStart);
pTrueBase = pHeader->Base;
//
// If PoolTracker is on then Base must point to it's header.
// This is currently the only option for this area...may change later.
//
if (pHeader->FxDriverGlobals->IsPoolTrackingOn()) {
pTracker = (PFX_POOL_TRACKER) pTrueBase;
if (FxIsPagedPoolType(pTracker->PoolType)) {
//
// Decommission this Paged Allocation tracker
//
FxPoolRemovePagedAllocateTracker(pTracker);
}
else {
//
// Decommission this NonPaged Allocation tracker
//
FxPoolRemoveNonPagedAllocateTracker(pTracker);
}
//
// Scrub the pool to zeros to catch destructed objects
// by NULL'ing the v-table ptr
//
RtlZeroMemory(pTracker, pTracker->Size + sizeof(FX_POOL_TRACKER));
}
MxMemory::MxFreePool(pTrueBase);
}
NTSTATUS
FxPoolDump(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PFX_POOL Pool
)
/*++
Routine Description:
Dump the FX_POOL tracking object
Arguments:
Pool - FX_POOL object for tracking allocations
Returns:
STATUS_SUCCESS
--*/
{
PFX_POOL_TRACKER pTracker;
PLIST_ENTRY ple;
KIRQL oldIrql;
BOOLEAN leak;
//
// Dump usage information
//
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
"FxPoolDump: "
"NonPagedBytes %I64d, PagedBytes %I64d, "
"NonPagedAllocations %d, PagedAllocations %d,"
"PeakNonPagedBytes %I64d, PeakPagedBytes %I64d,"
"FxPoolDump: PeakNonPagedAllocations %d, PeakPagedAllocations %d",
Pool->NonPagedBytes, Pool->PagedBytes,
Pool->NonPagedAllocations, Pool->PagedAllocations,
Pool->PeakNonPagedBytes, Pool->PeakPagedBytes,
Pool->PeakNonPagedAllocations, Pool->PeakPagedAllocations
);
leak = FALSE;
//
// Check paged pool for leaks
//
Pool->PagedLock.Acquire();
for (ple = Pool->PagedHead.Flink; ple != &Pool->PagedHead; ple = ple->Flink) {
pTracker = CONTAINING_RECORD(ple, FX_POOL_TRACKER, Link);
// Leaker
leak = TRUE;
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
"FX_POOL 0x%p leaked paged memory alloc 0x%p (tracking block %p)",
Pool, pTracker + 1, pTracker);
}
Pool->PagedLock.Release();
//
// Check non-paged pool for leaks
//
Pool->NonPagedLock.Acquire(&oldIrql);
for (ple = Pool->NonPagedHead.Flink;
ple != &Pool->NonPagedHead;
ple = ple->Flink) {
pTracker = CONTAINING_RECORD(ple, FX_POOL_TRACKER, Link );
// Leaker
leak = TRUE;
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
"FX_POOL 0x%p leaked non-paged memory alloc 0x%p (tracking block %p)",
Pool, pTracker+1, pTracker);
}
Pool->NonPagedLock.Release(oldIrql);
if (leak) {
FxVerifierDbgBreakPoint(FxDriverGlobals);
return STATUS_MORE_ENTRIES;
}
else {
return STATUS_SUCCESS;
}
}
_Must_inspect_result_
NTSTATUS
FxPoolInitialize(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PFX_POOL Pool
)
/*++
Routine Description:
Initialize the FX_POOL tracking object
Arguments:
Pool - FX_POOL object for tracking allocations
Returns:
STATUS_SUCCESS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPOOL,
"Initializing Pool 0x%p, Tracking %d",
Pool, FxDriverGlobals->IsPoolTrackingOn());
Pool->NonPagedLock.Initialize();
InitializeListHead( &Pool->NonPagedHead );
status = Pool->PagedLock.Initialize();
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPOOL,
"Initializing paged lock failed for Pool 0x%p, "
"status %!STATUS!",
Pool, status);
goto exit;
}
InitializeListHead( &Pool->PagedHead );
// Pool usage information
Pool->NonPagedBytes = 0;
Pool->PagedBytes = 0;
Pool->NonPagedAllocations = 0;
Pool->PagedAllocations = 0;
Pool->PeakNonPagedBytes = 0;
Pool->PeakPagedBytes = 0;
Pool->PeakNonPagedAllocations = 0;
Pool->PeakPagedAllocations = 0;
exit:
if (!NT_SUCCESS(status)) {
//
// We disable pool tracking if we could not initialize the locks needed
//
// If we don't do this we would need another flag to make FxPoolDestroy
// not access the locks
//
FxDriverGlobals->FxPoolTrackingOn = FALSE;
}
//
// FxPoolDestroy will always be called even if we fail FxPoolInitialize
//
// FxPoolDestroy will uninitialize locks both in success and failure
// cases
//
return status;
}
VOID
FxPoolDestroy(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PFX_POOL Pool
)
/*++
Routine Description:
Destroy the FX_POOL tracking object
Arguments:
Pool - FX_POOL object for tracking allocations
Returns:
STATUS_SUCCESS
--*/
{
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPOOL,
"Destroying Pool 0x%p", Pool);
if (FxDriverGlobals->IsPoolTrackingOn()) {
FxPoolDump(FxDriverGlobals, Pool);
#if FX_CORE_MODE==FX_CORE_KERNEL_MODE
FxMdlDump(FxDriverGlobals);
#endif
//
// We don't automatically free memory items since we don't
// know what they contain, and who is still referencing them.
//
}
Pool->PagedLock.Uninitialize();
Pool->NonPagedLock.Uninitialize();
return;
}
_Must_inspect_result_
NTSTATUS
FxPoolPackageInitialize(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
)
/*++
Routine Description:
Initialize the pool support package at startup time.
This must be called before the first allocation.
Arguments:
FxDriverGlobals - DriverGlobals
Returns:
STATUS_SUCCESS
--*/
{
return FxPoolInitialize(FxDriverGlobals, &FxDriverGlobals->FxPoolFrameworks);
}
VOID
FxPoolPackageDestroy(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
)
/*++
Routine Description:
Destroy the pool support package at unload time
This must be after the last free
Arguments:
FxDriverGlobals - Driver's globals
Returns:
STATUS_SUCCESS
--*/
{
FxPoolDestroy(FxDriverGlobals, &FxDriverGlobals->FxPoolFrameworks);
return;
}