You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5195 lines
151 KiB
5195 lines
151 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pool.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Lou Perazzoli (Loup) 5-Nov-1993
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
Kshitiz K. Sharma (kksharma)
|
|
|
|
Using debugger type info.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define TAG 0
|
|
#define NONPAGED_ALLOC 1
|
|
#define NONPAGED_FREE 2
|
|
#define PAGED_ALLOC 3
|
|
#define PAGED_FREE 4
|
|
#define NONPAGED_USED 5
|
|
#define PAGED_USED 6
|
|
|
|
BOOL NewPool;
|
|
ULONG SortBy;
|
|
|
|
typedef struct _FILTER {
|
|
ULONG Tag;
|
|
BOOLEAN Exclude;
|
|
} FILTER, *PFILTER;
|
|
|
|
typedef struct _POOLTRACK_READ {
|
|
ULONG64 Address;
|
|
ULONG Key;
|
|
ULONG NonPagedAllocs;
|
|
ULONG NonPagedFrees;
|
|
ULONG PagedAllocs;
|
|
ULONG PagedFrees;
|
|
LONG64 NonPagedBytes;
|
|
LONG64 PagedBytes;
|
|
} POOLTRACK_READ, *PPOOLTRACK_READ;
|
|
|
|
#define MAX_FILTER 64
|
|
FILTER Filter[MAX_FILTER];
|
|
|
|
ULONG64 SpecialPoolStart;
|
|
ULONG64 SpecialPoolEnd;
|
|
ULONG64 PoolBigTableAddress;
|
|
|
|
|
|
#define DecodeLink(Pool) ( (ULONG64) (Pool & (ULONG64) ~1))
|
|
|
|
//
|
|
// Size of a pool page.
|
|
//
|
|
// This must be greater than or equal to the page size.
|
|
//
|
|
|
|
#define POOL_PAGE_SIZE PageSize
|
|
#define MAX_POOL_HEADER_COUNT (PageSize/SizeOfPoolHdr)
|
|
|
|
//
|
|
// The smallest pool block size must be a multiple of the page size.
|
|
//
|
|
// Define the block size as 32.
|
|
//
|
|
|
|
|
|
#define POOL_LIST_HEADS (POOL_PAGE_SIZE / (1 << POOL_BLOCK_SHIFT))
|
|
|
|
|
|
|
|
#define SPECIAL_POOL_BLOCK_SIZE(PoolHeader_Ulong1) (PoolHeader_Ulong1 & (MI_SPECIAL_POOL_VERIFIER - 1))
|
|
|
|
NTSTATUS
|
|
DiagnosePoolPage(
|
|
ULONG64 PoolPageToDump
|
|
);
|
|
|
|
#ifndef _EXTFNS_H
|
|
// GetPoolTagDescription
|
|
typedef HRESULT
|
|
(WINAPI *PGET_POOL_TAG_DESCRIPTION)(
|
|
ULONG PoolTag,
|
|
PSTR *pDescription
|
|
);
|
|
#endif
|
|
|
|
ULONG64
|
|
GetSpecialPoolHeader (
|
|
IN PVOID pDataPage,
|
|
IN ULONG64 RealDataPage,
|
|
OUT PULONG64 ReturnedDataStart
|
|
);
|
|
|
|
int __cdecl
|
|
ulcomp(const void *e1,const void *e2)
|
|
{
|
|
PPOOLTRACK_READ p1, p2;
|
|
ULONG u1;
|
|
|
|
p1 = (PPOOLTRACK_READ) e1;
|
|
p2 = (PPOOLTRACK_READ) e2;
|
|
switch (SortBy) {
|
|
case TAG:
|
|
|
|
|
|
u1 = ((PUCHAR)&p1->Key)[0] - ((PUCHAR)&p2->Key)[0];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&p1->Key)[1] - ((PUCHAR)&p2->Key)[1];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&p1->Key)[2] - ((PUCHAR)&p2->Key)[2];
|
|
if (u1 != 0) {
|
|
return u1;
|
|
}
|
|
u1 = ((PUCHAR)&p1->Key)[3] - ((PUCHAR)&p2->Key)[3];
|
|
return u1;
|
|
|
|
case NONPAGED_ALLOC:
|
|
if (p2->NonPagedAllocs == p1->NonPagedAllocs) {
|
|
return 0;
|
|
}
|
|
if (p2->NonPagedAllocs > p1->NonPagedAllocs) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
|
|
case NONPAGED_FREE:
|
|
if (p2->NonPagedFrees == p1->NonPagedFrees) {
|
|
return 0;
|
|
}
|
|
if (p2->NonPagedFrees > p1->NonPagedFrees) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
|
|
case NONPAGED_USED:
|
|
if (p2->NonPagedBytes == p1->NonPagedBytes) {
|
|
return 0;
|
|
}
|
|
if (p2->NonPagedBytes > p1->NonPagedBytes) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
|
|
case PAGED_USED:
|
|
if (p2->PagedBytes == p1->PagedBytes) {
|
|
return 0;
|
|
}
|
|
if (p2->PagedBytes > p1->PagedBytes) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets up generally useful pool globals.
|
|
|
|
Must be called in every DECLARE_API interface that uses pool.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
LOGICAL PoolInitialized = FALSE;
|
|
|
|
LOGICAL
|
|
PoolInitializeGlobals(
|
|
VOID
|
|
)
|
|
{
|
|
if (PoolInitialized == TRUE) {
|
|
return TRUE;
|
|
}
|
|
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
|
|
|
|
if (PageSize < 0x1000 || (PageSize & (ULONG)0xFFF)) {
|
|
dprintf ("unable to get MmPageSize (0x%x) - probably bad symbols\n", PageSize);
|
|
return FALSE;
|
|
}
|
|
|
|
PoolInitialized = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DECLARE_API( frag )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump pool fragmentation
|
|
|
|
Arguments:
|
|
|
|
args - Flags
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags;
|
|
ULONG result;
|
|
ULONG i;
|
|
ULONG count;
|
|
ULONG64 Pool;
|
|
ULONG64 PoolLoc1;
|
|
ULONG TotalFrag;
|
|
ULONG TotalCount;
|
|
ULONG Frag;
|
|
ULONG64 PoolStart;
|
|
ULONG PoolOverhead;
|
|
ULONG64 PoolLoc;
|
|
ULONG PoolTag, BlockSize, PreviousSize, PoolIndex;
|
|
ULONG TotalPages, TotalBigPages;
|
|
ULONG64 Flink, Blink;
|
|
PCHAR pc;
|
|
ULONG ListHeadOffset, ListEntryOffset;
|
|
ULONG64 tmp;
|
|
|
|
#define PoolBlk(F,V) GetFieldValue(Pool, "nt!_POOL_HEADER", #F, V)
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dprintf("\n NonPaged Pool Fragmentation\n\n");
|
|
Flags = 0;
|
|
PoolStart = 0;
|
|
|
|
if (GetExpressionEx(args, &tmp, &args)) {
|
|
Flags = (ULONG) tmp;
|
|
PoolStart = GetExpression (args);
|
|
}
|
|
|
|
PoolOverhead = GetTypeSize("nt!_POOL_HEADER");
|
|
if (PoolStart != 0) {
|
|
PoolStart += PoolOverhead;
|
|
|
|
Pool = DecodeLink(PoolStart);
|
|
do {
|
|
|
|
Pool = Pool - PoolOverhead;
|
|
if ( PoolBlk(k, PoolTag) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolBlk(BlockSize,BlockSize);
|
|
PoolBlk(PreviousSize,PreviousSize);
|
|
ReadPointer(Pool + PoolOverhead, &Flink);
|
|
ReadPointer(Pool + PoolOverhead + DBG_PTR_SIZE, &Blink);
|
|
|
|
dprintf(" %p size: %4lx previous size: %4lx %c%c%c%c links: %8p %8p\n",
|
|
Pool,
|
|
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
|
|
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24),
|
|
#undef PP
|
|
Flink,
|
|
Blink);
|
|
|
|
if (Flags != 3) {
|
|
Pool = Flink;
|
|
} else {
|
|
Pool = Blink;
|
|
}
|
|
|
|
Pool = DecodeLink(Pool);
|
|
|
|
if (CheckControlC()) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
} while ( (Pool & (ULONG64) ~0xf) != (PoolStart & (ULONG64) ~0xf) );
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolLoc1 = GetNtDebuggerData( NonPagedPoolDescriptor );
|
|
|
|
if (PoolLoc1 == 0) {
|
|
dprintf ("unable to get nonpaged pool head\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolLoc = PoolLoc1;
|
|
GetFieldOffset("nt!_POOL_DESCRIPTOR", "ListHeads", &ListHeadOffset);
|
|
|
|
TotalFrag = 0;
|
|
TotalCount = 0;
|
|
|
|
for (i = 0; i < POOL_LIST_HEADS; i += 1) {
|
|
|
|
Frag = 0;
|
|
count = 0;
|
|
|
|
// Get Offset of this entry
|
|
ListEntryOffset = ListHeadOffset + i * 2 * DBG_PTR_SIZE;
|
|
|
|
if (GetFieldValue(PoolLoc + ListEntryOffset, "nt!_LIST_ENTRY", "Flink", Pool)) {
|
|
dprintf ("Unable to get pool descriptor list entry %#lx, %p\n", i, PoolLoc1);
|
|
return E_INVALIDARG;
|
|
}
|
|
// Pool = (PUCHAR)PoolDesc.ListHeads[i].Flink;
|
|
Pool = DecodeLink(Pool);
|
|
|
|
while (Pool != (ListEntryOffset + PoolLoc)) {
|
|
|
|
Pool = Pool - PoolOverhead;
|
|
if ( PoolBlk(PoolTag, PoolTag) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolBlk(BlockSize,BlockSize);
|
|
PoolBlk(PreviousSize,PreviousSize);
|
|
ReadPointer(Pool + PoolOverhead, &Flink);
|
|
ReadPointer(Pool + PoolOverhead + DBG_PTR_SIZE, &Blink);
|
|
|
|
Frag += BlockSize << POOL_BLOCK_SHIFT;
|
|
count += 1;
|
|
|
|
if (Flags & 2) {
|
|
dprintf(" ListHead[%x]: %p size: %4lx previous size: %4lx %c%c%c%c\n",
|
|
i,
|
|
(ULONG)Pool,
|
|
(ULONG)BlockSize << POOL_BLOCK_SHIFT,
|
|
(ULONG)PreviousSize << POOL_BLOCK_SHIFT,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24));
|
|
#undef PP
|
|
}
|
|
Pool = Flink;
|
|
Pool = DecodeLink(Pool);
|
|
|
|
if (CheckControlC()) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
if (Flags & 1) {
|
|
dprintf("index: %2ld number of fragments: %5ld bytes: %6ld\n",
|
|
i,count,Frag);
|
|
}
|
|
TotalFrag += Frag;
|
|
TotalCount += count;
|
|
}
|
|
|
|
dprintf("\n Number of fragments: %7ld consuming %7ld bytes\n",
|
|
TotalCount,TotalFrag);
|
|
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalPages",TotalPages);
|
|
GetFieldValue(PoolLoc, "nt!_POOL_DESCRIPTOR", "TotalBigPages", TotalBigPages);
|
|
|
|
dprintf( " NonPagedPool Usage: %7ld bytes\n",(TotalPages + TotalBigPages)*PageSize);
|
|
return S_OK;
|
|
#undef PoolBlk
|
|
}
|
|
|
|
|
|
PRTL_BITMAP
|
|
GetBitmap(
|
|
ULONG64 pBitmap
|
|
)
|
|
{
|
|
PRTL_BITMAP p;
|
|
ULONG Size, Result;
|
|
ULONG64 Buffer=0;
|
|
|
|
if (!pBitmap || GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "Buffer", Buffer)) {
|
|
dprintf("%08p: Unable to get contents of bitmap\n", pBitmap );
|
|
return 0;
|
|
}
|
|
GetFieldValue(pBitmap, "nt!_RTL_BITMAP", "SizeOfBitMap", Size);
|
|
|
|
p = HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) + (Size / 8) );
|
|
if (p) {
|
|
p->SizeOfBitMap = Size;
|
|
p->Buffer = (PULONG)(p + 1);
|
|
if ( !ReadMemory( Buffer,
|
|
p->Buffer,
|
|
Size / 8,
|
|
&Result) ) {
|
|
dprintf("%08p: Unable to get contents of bitmap buffer\n", Buffer );
|
|
HeapFree( GetProcessHeap(), 0, p );
|
|
p = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return p;
|
|
}
|
|
|
|
VOID
|
|
DumpPool(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 p, pStart;
|
|
ULONG64 Size;
|
|
ULONG BusyFlag;
|
|
ULONG CurrentPage, NumberOfPages;
|
|
PRTL_BITMAP StartMap;
|
|
PRTL_BITMAP EndMap;
|
|
ULONG64 PagedPoolStart;
|
|
ULONG64 PagedPoolEnd;
|
|
ULONG Result;
|
|
UCHAR PgPool[] = "nt!_MM_PAGED_POOL_INFO";
|
|
ULONG64 PagedPoolInfoPointer;
|
|
ULONG64 PagedPoolAllocationMap=0, EndOfPagedPoolBitmap=0;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return;
|
|
}
|
|
|
|
PagedPoolInfoPointer = GetNtDebuggerData( MmPagedPoolInformation );
|
|
|
|
if ( GetFieldValue( PagedPoolInfoPointer,
|
|
PgPool,
|
|
"PagedPoolAllocationMap",
|
|
PagedPoolAllocationMap)) {
|
|
dprintf("%08p: Unable to get contents of paged pool information\n",
|
|
PagedPoolInfoPointer );
|
|
return;
|
|
}
|
|
|
|
GetFieldValue( PagedPoolInfoPointer, PgPool, "EndOfPagedPoolBitmap", EndOfPagedPoolBitmap);
|
|
|
|
|
|
StartMap = GetBitmap( PagedPoolAllocationMap );
|
|
EndMap = GetBitmap( EndOfPagedPoolBitmap );
|
|
|
|
PagedPoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
|
|
PagedPoolEnd = GetNtDebuggerDataPtrValue( MmPagedPoolEnd );
|
|
|
|
if (StartMap && EndMap) {
|
|
p = PagedPoolStart;
|
|
CurrentPage = 0;
|
|
dprintf( "Paged Pool: %p .. %p\n", PagedPoolStart, PagedPoolEnd );
|
|
|
|
while (p < PagedPoolEnd) {
|
|
if ( CheckControlC() ) {
|
|
return;
|
|
}
|
|
pStart = p;
|
|
BusyFlag = RtlCheckBit( StartMap, CurrentPage );
|
|
while ( ~(BusyFlag ^ RtlCheckBit( StartMap, CurrentPage )) ) {
|
|
p += PageSize;
|
|
if (RtlCheckBit( EndMap, CurrentPage )) {
|
|
CurrentPage++;
|
|
break;
|
|
}
|
|
|
|
CurrentPage++;
|
|
if (p > PagedPoolEnd) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Size = p - pStart;
|
|
dprintf( "%p: %I64x - %s\n", pStart, Size, BusyFlag ? "busy" : "free" );
|
|
}
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, StartMap );
|
|
HeapFree( GetProcessHeap(), 0, EndMap );
|
|
}
|
|
|
|
void
|
|
PrintPoolTagComponent(
|
|
ULONG PoolTag
|
|
)
|
|
{
|
|
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
|
|
PSTR TagComponent;
|
|
#ifdef _EXTFNS_H
|
|
DEBUG_POOLTAG_DESCRIPTION Desc = {0};
|
|
Desc.SizeOfStruct = sizeof(DEBUG_POOLTAG_DESCRIPTION);
|
|
GetPoolTagDescription = NULL;
|
|
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
|
|
!GetPoolTagDescription) {
|
|
return;
|
|
}
|
|
|
|
(*GetPoolTagDescription)(PoolTag, &Desc);
|
|
|
|
if (Desc.Description[0]) {
|
|
dprintf("\t\tPooltag %4.4s : %s", &PoolTag, Desc.Description);
|
|
if (Desc.Binary[0]) {
|
|
dprintf(", Binary : %s",Desc.Binary);
|
|
}
|
|
if (Desc.Owner[0]) {
|
|
dprintf(", Owner : %s", Desc.Owner);
|
|
}
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
|
|
}
|
|
|
|
#else
|
|
GetPoolTagDescription = NULL;
|
|
if ((GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription) != S_OK) ||
|
|
!GetPoolTagDescription) {
|
|
return;
|
|
}
|
|
|
|
(*GetPoolTagDescription)(PoolTag, &TagComponent);
|
|
if (TagComponent && (100 < (ULONG64) TagComponent)) {
|
|
dprintf("\t\tOwning component : %s\n", TagComponent);
|
|
} else {
|
|
dprintf("\t\tOwning component : Unknown (update pooltag.txt)\n");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
PSTR g_PoolRegion[DbgPoolRegionMax] = {
|
|
"Unknown", // DbgPoolRegionUnknown,
|
|
"Special pool", // DbgPoolRegionSpecial,
|
|
"Paged pool", // DbgPoolRegionPaged,
|
|
"Nonpaged pool", // DbgPoolRegionNonPaged,
|
|
"Pool code", // DbgPoolRegionCode,
|
|
"Nonpaged pool expansion", // DbgPoolRegionNonPagedExpansion,
|
|
};
|
|
|
|
DEBUG_POOL_REGION
|
|
GetPoolRegion(
|
|
ULONG64 Pool
|
|
)
|
|
{
|
|
static ULONG64 PoolCodeEnd;
|
|
static ULONG64 PagedPoolEnd;
|
|
static ULONG64 NonPagedPoolEnd;
|
|
static ULONG64 NonPagedPoolStart;
|
|
static ULONG64 PagedPoolStart;
|
|
static ULONG64 SessionPagedPoolStart;
|
|
static ULONG64 SessionPagedPoolEnd;
|
|
static ULONG64 NonPagedPoolExpansionStart;
|
|
static ULONG64 PoolCodeStart;
|
|
static BOOL GotAll = FALSE;
|
|
|
|
if (!GotAll) {
|
|
PoolCodeEnd = GetPointerValue("nt!MmPoolCodeEnd");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
PagedPoolEnd = GetPointerValue("nt!MmPagedPoolEnd");
|
|
NonPagedPoolEnd = GetPointerValue("nt!MmNonPagedPoolEnd");
|
|
NonPagedPoolStart = GetPointerValue("nt!MmNonPagedPoolStart");
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
PagedPoolStart = GetPointerValue("nt!MmPagedPoolStart");
|
|
SessionPagedPoolStart = GetPointerValue("nt!MiSessionPoolStart");
|
|
SessionPagedPoolEnd = GetPointerValue("nt!MiSessionPoolEnd");
|
|
NonPagedPoolExpansionStart = GetPointerValue("nt!MmNonPagedPoolExpansionStart");
|
|
PoolCodeStart = GetPointerValue("nt!MmPoolCodeStart");
|
|
GotAll = TRUE;
|
|
}
|
|
if (!(PoolCodeStart || SpecialPoolStart || SpecialPoolEnd || PoolCodeEnd ||
|
|
NonPagedPoolExpansionStart || NonPagedPoolStart || NonPagedPoolEnd ||
|
|
SessionPagedPoolStart || SessionPagedPoolEnd || PagedPoolStart || PagedPoolEnd)) {
|
|
GotAll = FALSE;
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
if ( Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
|
|
return DbgPoolRegionSpecial;
|
|
} else if ( Pool >= PagedPoolStart && Pool < PagedPoolEnd) {
|
|
return DbgPoolRegionPaged;
|
|
} else if ( Pool >= SessionPagedPoolStart && Pool < SessionPagedPoolEnd) {
|
|
return DbgPoolRegionPaged;
|
|
} else if ( Pool >= NonPagedPoolStart && Pool < NonPagedPoolEnd) {
|
|
return DbgPoolRegionNonPaged;
|
|
} else if ( Pool >= PoolCodeStart && Pool < PoolCodeEnd) {
|
|
return DbgPoolRegionCode;
|
|
} else if ( Pool >= NonPagedPoolExpansionStart) {
|
|
return DbgPoolRegionNonPagedExpansion;
|
|
} else {
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
return DbgPoolRegionUnknown;
|
|
}
|
|
|
|
void
|
|
PrintPoolRegion(
|
|
ULONG64 Pool
|
|
)
|
|
{
|
|
PSTR pszRegion;
|
|
DEBUG_POOL_REGION Region;
|
|
|
|
|
|
Region = GetPoolRegion(Pool);
|
|
|
|
pszRegion = g_PoolRegion[Region];
|
|
if (pszRegion) {
|
|
dprintf(pszRegion);
|
|
dprintf("\n");
|
|
} else {
|
|
dprintf("Region unkown (0x%I64X)\n", Pool);
|
|
}
|
|
|
|
}
|
|
|
|
ULONG64
|
|
READ_PVOID (
|
|
ULONG64 Address
|
|
);
|
|
|
|
UCHAR DataPage[0x5000];
|
|
|
|
HRESULT
|
|
ListPoolPage(
|
|
ULONG64 PoolPageToDump,
|
|
ULONG Flags,
|
|
PDEBUG_POOL_DATA PoolData
|
|
)
|
|
{
|
|
LOGICAL QuotaProcessAtEndOfPoolBlock;
|
|
ULONG64 PoolTableAddress;
|
|
ULONG result;
|
|
ULONG PoolTag;
|
|
ULONG Result;
|
|
ULONG64 StartPage;
|
|
ULONG64 Pool;
|
|
ULONG PoolBlockSize;
|
|
ULONG PoolHeaderSize;
|
|
ULONG64 PoolHeader;
|
|
ULONG Previous;
|
|
UCHAR c;
|
|
PUCHAR p;
|
|
ULONG64 PoolDataEnd;
|
|
UCHAR PoolBlockPattern;
|
|
PUCHAR DataStart;
|
|
ULONG64 RealDataStart;
|
|
LOGICAL Pagable;
|
|
LOGICAL FirstBlock;
|
|
ULONG BlockType;
|
|
ULONG PoolWhere;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG ct;
|
|
ULONG start;
|
|
ULONG PoolBigPageTableSize;
|
|
ULONG SizeOfPoolHdr=GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
if (!SpecialPoolStart) {
|
|
SpecialPoolStart = GetPointerValue("nt!MmSpecialPoolStart");
|
|
SpecialPoolEnd = GetPointerValue("nt!MmSpecialPoolEnd");
|
|
}
|
|
|
|
Pool = PAGE_ALIGN64 (PoolPageToDump);
|
|
StartPage = Pool;
|
|
Previous = 0;
|
|
|
|
if (PoolData) {
|
|
ZeroMemory(PoolData, sizeof(DEBUG_POOL_DATA));
|
|
}
|
|
|
|
if (!(Flags & 0x80000000)) {
|
|
dprintf("Pool page %p region is ", PoolPageToDump);
|
|
PrintPoolRegion(PoolPageToDump);
|
|
}
|
|
|
|
if (Pool >= SpecialPoolStart && Pool < SpecialPoolEnd) {
|
|
|
|
ULONG Hdr_Ulong=0;
|
|
|
|
// Pre read the pool
|
|
if ( !ReadMemory( Pool,
|
|
&DataPage[0],
|
|
min(PageSize, sizeof(DataPage)),
|
|
&Result) ) {
|
|
dprintf("%08p: Unable to get contents of special pool block\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong)) {
|
|
dprintf("%08p: Unable to get nt!_POOL_HEADER\n", Pool );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Determine whether the data is at the start or end of the page.
|
|
// Start off by assuming the data is at the end, in this case the
|
|
// header will be at the start.
|
|
//
|
|
|
|
PoolHeader = GetSpecialPoolHeader((PVOID) &DataPage[0], Pool, &RealDataStart);
|
|
|
|
if (PoolHeader == 0) {
|
|
dprintf("Block %p is a corrupted special pool allocation\n",
|
|
PoolPageToDump);
|
|
return E_INVALIDARG;
|
|
}
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Hdr_Ulong);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(Hdr_Ulong);
|
|
|
|
if (Hdr_Ulong & MI_SPECIAL_POOL_PAGABLE) {
|
|
Pagable = TRUE;
|
|
} else {
|
|
Pagable = FALSE;
|
|
}
|
|
|
|
if (PoolData) {
|
|
PoolData->Pool = RealDataStart;
|
|
PoolData->PoolBlock = PoolPageToDump;
|
|
PoolData->SpecialPool = 1;
|
|
PoolData->Pageable = Hdr_Ulong & 0x8000 ? 1 : 0;
|
|
PoolData->Size = PoolBlockSize;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
|
|
RealDataStart,
|
|
PoolBlockSize,
|
|
Hdr_Ulong & 0x8000 ? "pagable" : "non-paged",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP(PoolTag >> 24)
|
|
);
|
|
#undef PP
|
|
PrintPoolTagComponent(PoolTag);
|
|
|
|
//
|
|
// Add code to validate whole block.
|
|
//
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
FirstBlock = TRUE;
|
|
QuotaProcessAtEndOfPoolBlock = FALSE;
|
|
|
|
if (TargetMachine == IMAGE_FILE_MACHINE_I386) {
|
|
if (GetExpression ("nt!ExGetPoolTagInfo") != 0) {
|
|
|
|
//
|
|
// This is a kernel where the quota process pointer is at the end
|
|
// of the pool block instead of overlaid on the tag field.
|
|
//
|
|
|
|
QuotaProcessAtEndOfPoolBlock = TRUE;
|
|
}
|
|
}
|
|
|
|
while (PAGE_ALIGN64(Pool) == StartPage) {
|
|
ULONG BlockSize=0, PreviousSize=0, PoolType=0, AllocatorBackTraceIndex=0;
|
|
ULONG PoolTagHash=0, PoolIndex=0;
|
|
ULONG64 ProcessBilled=0;
|
|
|
|
if ( CheckControlC() ) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( GetFieldValue( Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize) ) {
|
|
|
|
//
|
|
// If this is a large pool allocation the memory address at this address
|
|
// might still be DemandZero and we cannot read from it. This is not an error -
|
|
// we could still find the allocation in the large allocations table.
|
|
//
|
|
|
|
BlockType = 1;
|
|
|
|
goto TryLargePool;
|
|
}
|
|
|
|
if (PoolPageToDump >= Pool &&
|
|
PoolPageToDump < (Pool + ((ULONG64) BlockSize << POOL_BLOCK_SHIFT))
|
|
) {
|
|
c = '*';
|
|
} else {
|
|
c = ' ';
|
|
}
|
|
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolType", PoolType);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
|
|
|
|
BlockType = 0;
|
|
|
|
if ((BlockSize << POOL_BLOCK_SHIFT) >= POOL_PAGE_SIZE) {
|
|
BlockType = 1;
|
|
} else if (BlockSize == 0) {
|
|
BlockType = 2;
|
|
} else if (PreviousSize != Previous) {
|
|
BlockType = 3;
|
|
}
|
|
|
|
TryLargePool:
|
|
|
|
if (BlockType != 0) {
|
|
ULONG BigPageSize = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
|
|
|
|
if (!BigPageSize) {
|
|
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if this is a big block allocation. Iff we have not parsed
|
|
// any other small blocks in here already.
|
|
//
|
|
|
|
if (FirstBlock == TRUE) {
|
|
|
|
if (!PoolBigTableAddress) {
|
|
PoolBigTableAddress = GetPointerValue ("nt!PoolBigPageTable");
|
|
}
|
|
|
|
PoolTableAddress = PoolBigTableAddress;
|
|
|
|
if (PoolTableAddress) {
|
|
|
|
dprintf ("%p is not a valid small pool allocation, checking large pool...\n", Pool);
|
|
|
|
PoolBigPageTableSize = GetUlongValue ("nt!PoolBigPageTableSize");
|
|
//
|
|
// Scan the table looking for a match.
|
|
//
|
|
|
|
i = 0;
|
|
ct = PageSize / BigPageSize;
|
|
|
|
while (i < PoolBigPageTableSize) {
|
|
ULONG64 Va=0;
|
|
ULONG Key=0, NumberOfPages=0;
|
|
|
|
if (PoolBigPageTableSize - i < ct) {
|
|
ct = PoolBigPageTableSize - i;
|
|
}
|
|
|
|
if ( GetFieldValue( PoolTableAddress,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Va",
|
|
Va) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
for (j = 0; j < ct; j += 1) {
|
|
|
|
if ( GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Va",
|
|
Va) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (((Va & 0x1) == 0) && (Pool >= Va)) {
|
|
|
|
GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"NumberOfPages",
|
|
NumberOfPages);
|
|
|
|
if (Pool < (Va + (NumberOfPages * PageSize))) {
|
|
//
|
|
// Match !
|
|
//
|
|
GetFieldValue( PoolTableAddress + BigPageSize*j,
|
|
"nt!_POOL_TRACKER_BIG_PAGES",
|
|
"Key",
|
|
Key);
|
|
PoolTag = Key;
|
|
|
|
if (PoolData) {
|
|
PoolData->Pool = PoolPageToDump;
|
|
PoolData->Size = NumberOfPages*PageSize;
|
|
PoolData->PoolTag = PoolTag;
|
|
PoolData->LargePool = 1;
|
|
PoolData->Free = (Pool & POOL_BIG_TABLE_ENTRY_FREE) ? 1 : 0;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
dprintf("*%p :%s large page allocation, Tag is %c%c%c%c, size is 0x%I64x bytes\n",
|
|
(Pool & ~POOL_BIG_TABLE_ENTRY_FREE),
|
|
(Pool & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP(PoolTag >> 24),
|
|
(ULONG64)NumberOfPages * PageSize
|
|
);
|
|
#undef PP
|
|
PrintPoolTagComponent(PoolTag);
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
i += ct;
|
|
PoolTableAddress += (ct * BigPageSize);
|
|
}
|
|
|
|
//
|
|
// No match in small or large pool, must be
|
|
// freed or corrupt pool
|
|
//
|
|
|
|
dprintf("%p is freed (or corrupt) pool\n", Pool);
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dprintf("unable to get pool big page table - either wrong symbols or pool tagging is disabled\n");
|
|
}
|
|
|
|
if (BlockType == 1) {
|
|
dprintf("Bad allocation size @%p, too large\n", Pool);
|
|
} else if (BlockType == 2) {
|
|
dprintf("Bad allocation size @%p, zero is invalid\n", Pool);
|
|
} else if (BlockType == 3) {
|
|
dprintf("Bad previous allocation size @%p, last size was %lx\n",
|
|
Pool, Previous);
|
|
}
|
|
|
|
if (BlockType != 0) {
|
|
|
|
//
|
|
// Attempt to diagnose what is wrong with the pool page
|
|
//
|
|
DiagnosePoolPage(Pool);
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
}
|
|
|
|
switch (TargetMachine) {
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
break;
|
|
default:
|
|
if (QuotaProcessAtEndOfPoolBlock == TRUE) {
|
|
|
|
ULONG SizeOfPvoid = 0;
|
|
ULONG64 ProcessBillAddress;
|
|
|
|
SizeOfPvoid = DBG_PTR_SIZE;
|
|
|
|
if (SizeOfPvoid == 0) {
|
|
dprintf ("Search: cannot get size of PVOID\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
ProcessBillAddress = Pool + ((ULONG64) BlockSize << POOL_BLOCK_SHIFT) - SizeOfPvoid;
|
|
ProcessBilled = READ_PVOID (ProcessBillAddress);
|
|
}
|
|
else {
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
}
|
|
break;
|
|
}
|
|
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
|
|
|
|
if (!(Flags & 2) || c == '*') {
|
|
if (PoolData) {
|
|
PoolData->Pool = Pool;
|
|
PoolData->PoolBlock = PoolPageToDump;
|
|
PoolData->PoolTag = PoolTag & ~PROTECTED_POOL;
|
|
PoolData->ProcessBilled = ProcessBilled;
|
|
PoolData->PreviousSize = PreviousSize << POOL_BLOCK_SHIFT;
|
|
PoolData->Size = BlockSize << POOL_BLOCK_SHIFT;
|
|
PoolData->Free = ((PoolType != 0) && (!NewPool ?
|
|
(PoolIndex & 0x80) : (PoolType & 0x04))) ? 0 : 1;
|
|
PoolData->Protected = (PoolTag&PROTECTED_POOL) ? 1 : 0;
|
|
if (Flags & 0x80000000) {
|
|
// do not print anything
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
dprintf("%c%p size: %4lx previous size: %4lx ",
|
|
c,
|
|
Pool,
|
|
BlockSize << POOL_BLOCK_SHIFT,
|
|
PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
if (PoolType == 0) {
|
|
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
|
|
dprintf(" (Free)");
|
|
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
dprintf(" %c%c%c%c%c\n",
|
|
c,
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24)
|
|
);
|
|
#undef PP
|
|
if (c=='*') {
|
|
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
|
|
}
|
|
} else {
|
|
|
|
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
|
|
dprintf(" (Allocated)");
|
|
} else {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free )");
|
|
}
|
|
if ((PoolType & POOL_QUOTA_MASK) == 0) {
|
|
/*
|
|
ULONG Key=0;
|
|
if (AllocatorBackTraceIndex != 0 &&
|
|
AllocatorBackTraceIndex & POOL_BACKTRACEINDEX_PRESENT
|
|
) {
|
|
if ( GetFieldValue( PoolTrackTable + ( PoolTagHash&~(PROTECTED_POOL >> 16) )*GetTypeSize("nt!_POOL_TRACKER_TABLE"),
|
|
"nt!_POOL_TRACKER_TABLE",
|
|
"Key",
|
|
Key) ) {
|
|
PoolTag = 0;
|
|
} else {
|
|
PoolTag = Key;
|
|
}
|
|
|
|
if (PoolTagHash & (PROTECTED_POOL >> 16)) {
|
|
PoolTag |= PROTECTED_POOL;
|
|
}
|
|
|
|
} else {
|
|
PoolTag = PoolTag;
|
|
}*/
|
|
|
|
dprintf(" %c%c%c%c%c%s\n",
|
|
c,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24),
|
|
(PoolTag&PROTECTED_POOL) ? " (Protected)" : ""
|
|
#undef PP
|
|
);
|
|
|
|
if (c=='*') {
|
|
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
|
|
}
|
|
} else {
|
|
if ((QuotaProcessAtEndOfPoolBlock == TRUE) ||
|
|
(TargetMachine != IMAGE_FILE_MACHINE_I386)) {
|
|
|
|
dprintf(" %c%c%c%c%c%s",
|
|
c,
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(PoolTag),
|
|
PP(PoolTag >> 8),
|
|
PP(PoolTag >> 16),
|
|
PP((PoolTag&~PROTECTED_POOL) >> 24),
|
|
(PoolTag&PROTECTED_POOL) ? " (Protected)" : ""
|
|
#undef PP
|
|
);
|
|
|
|
if (ProcessBilled != 0) {
|
|
dprintf(" Process: %0p\n", ProcessBilled );
|
|
}
|
|
else {
|
|
dprintf("\n");
|
|
}
|
|
|
|
if (c=='*') {
|
|
PrintPoolTagComponent(PoolTag & ~PROTECTED_POOL);
|
|
}
|
|
}
|
|
else {
|
|
if (ProcessBilled != 0) {
|
|
dprintf(" Process: %0p", ProcessBilled );
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (Flags & 1) {
|
|
|
|
PULONG Contents;
|
|
ULONG Size;
|
|
|
|
Size = BlockSize << POOL_BLOCK_SHIFT;
|
|
|
|
//
|
|
// the blocksize includes the size of the header,
|
|
// so we want to account for the header size
|
|
// when we determine how much data to display
|
|
//
|
|
Size -= SizeOfPoolHdr;
|
|
|
|
if (Size > 0) {
|
|
|
|
Contents = malloc(Size);
|
|
|
|
if (Contents) {
|
|
|
|
ULONG64 m;
|
|
ULONG64 q;
|
|
|
|
q = Pool + SizeOfPoolHdr;
|
|
|
|
ReadMemory(q,
|
|
Contents,
|
|
Size,
|
|
&i);
|
|
|
|
for (m = 0; m < (Size / sizeof(Contents[0])); m++) {
|
|
|
|
if (m % 4 == 0) {
|
|
|
|
if (m > 0) {
|
|
dprintf("\n");
|
|
}
|
|
|
|
dprintf(" %0p ", q + (m * sizeof(Contents[0])));
|
|
|
|
}
|
|
|
|
dprintf(" %08lx", Contents[m]);
|
|
|
|
}
|
|
|
|
dprintf("\n\n");
|
|
|
|
free(Contents);
|
|
|
|
}
|
|
} else {
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
Previous = BlockSize;
|
|
Pool += ((ULONG64) Previous << POOL_BLOCK_SHIFT);
|
|
FirstBlock = FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( pool )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump kernel mode heap
|
|
|
|
Arguments:
|
|
|
|
args - Page Flags
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 PoolPageToDump;
|
|
ULONG Flags;
|
|
HRESULT Hr;
|
|
|
|
INIT_API();
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
Hr = E_INVALIDARG;
|
|
} else {
|
|
PoolPageToDump = 0;
|
|
Flags = 0;
|
|
if (GetExpressionEx(args, &PoolPageToDump, &args)) {
|
|
Flags = (ULONG) GetExpression (args);
|
|
}
|
|
|
|
if (PoolPageToDump == 0) {
|
|
DumpPool();
|
|
Hr = S_OK;;
|
|
} else {
|
|
Hr = ListPoolPage(PoolPageToDump, Flags, NULL);
|
|
}
|
|
|
|
}
|
|
EXIT_API();
|
|
|
|
return Hr;
|
|
|
|
}
|
|
|
|
//
|
|
// header info meta information
|
|
//
|
|
#define HEADER_BACK_LINK (0x1 << 0) // node has a valid backward link
|
|
#define HEADER_FORWARD_LINK (0x1 << 1) // node has a valid forward link
|
|
#define HEADER_FIXED_BACK_LINK (0x1 << 2) // an attempt was made to correct the backward link
|
|
#define HEADER_FIXED_FORWARD_LINK (0x1 << 3) // an attempt was made to correct the forward link
|
|
#define HEADER_FIRST_LINK (0x1 << 4) // node is the first of a contiguous linked list
|
|
#define HEADER_LAST_LINK (0x1 << 5) // node is the last of a contiguous linked list
|
|
#define HEADER_START_PAGE_BLOCK (0x1 << 6) // node is at the start of the pool page
|
|
#define HEADER_END_PAGE_BLOCK (0x1 << 7) // node refers to the end of the pool page - implies LAST_LINK
|
|
|
|
typedef struct _VALIDATE_POOL_HEADER_INFO {
|
|
UCHAR Info;
|
|
ULONG Pass;
|
|
UINT64 Node;
|
|
UINT64 ForwardLink;
|
|
UINT64 BackLink;
|
|
UINT64 FixedPreviousSize;
|
|
UINT64 FixedBlockSize;
|
|
} VALIDATE_POOL_HEADER_INFO, *PVALIDATE_POOL_HEADER_INFO;
|
|
|
|
#define IS_VALID_PASS(_x) ((_x > 0) && (_x < 0xff))
|
|
|
|
#define VERBOSE_SHOW_ERRORS_ONLY (0) //
|
|
#define VERBOSE_SHOW_LISTS (0x1 << 0) //
|
|
#define VERBOSE_SHOW_HEADER_INFO (0x1 << 1) //
|
|
#define VERBOSE_DUMP_HEADERS (0x1 << 2) //
|
|
#define VERBOSE_SHOW_ALL (0x1 << 7)
|
|
|
|
UCHAR
|
|
ValidatePoolHeaderBackLink(
|
|
IN ULONG64 Pool
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine determines if the
|
|
back link of a given pool header as valid
|
|
|
|
Arguments:
|
|
|
|
Pool - header (node) to analyze
|
|
|
|
Returns:
|
|
|
|
Header info for the node
|
|
|
|
--*/
|
|
{
|
|
UCHAR HeaderInfo;
|
|
ULONG64 StartPage;
|
|
ULONG64 p;
|
|
ULONG tmpBlockSize;
|
|
ULONG PreviousSize;
|
|
|
|
//
|
|
// find where the page starts
|
|
//
|
|
StartPage = PAGE_ALIGN64(Pool);
|
|
|
|
//
|
|
// clear the header info
|
|
//
|
|
HeaderInfo = 0;
|
|
|
|
//
|
|
// get the previous size for the current node
|
|
//
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
|
|
//
|
|
// Validate Back link
|
|
//
|
|
if (Pool == StartPage) {
|
|
|
|
//
|
|
// ensure that the PreviousSize is 0 before
|
|
// we declare we have a valid back link
|
|
//
|
|
if (PreviousSize == 0) {
|
|
|
|
//
|
|
// the page always begins with a block
|
|
// and the back link is not used (0)
|
|
//
|
|
HeaderInfo |= HEADER_BACK_LINK;
|
|
|
|
}
|
|
|
|
HeaderInfo |= HEADER_START_PAGE_BLOCK;
|
|
|
|
} else {
|
|
|
|
p = Pool - ((ULONG64)PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
if (PAGE_ALIGN64(p) == StartPage) {
|
|
|
|
GetFieldValue( p, "nt!_POOL_HEADER", "BlockSize", tmpBlockSize);
|
|
|
|
if ((tmpBlockSize == PreviousSize) && (tmpBlockSize != 0)) {
|
|
|
|
HeaderInfo |= HEADER_BACK_LINK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return HeaderInfo;
|
|
}
|
|
|
|
UCHAR
|
|
ValidatePoolHeaderForwardLink(
|
|
IN ULONG64 Pool
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine determines if the
|
|
forward link of a given pool header as valid
|
|
|
|
Arguments:
|
|
|
|
Pool - header (node) to analyze
|
|
|
|
Returns:
|
|
|
|
Header info for the node
|
|
|
|
--*/
|
|
{
|
|
UCHAR HeaderInfo;
|
|
ULONG64 StartPage;
|
|
ULONG64 p;
|
|
ULONG tmpPreviousSize;
|
|
ULONG BlockSize;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(Pool);
|
|
|
|
//
|
|
//
|
|
//
|
|
HeaderInfo = 0;
|
|
|
|
//
|
|
//
|
|
//
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
|
|
//
|
|
// Validate Forward link
|
|
//
|
|
p = Pool + ((ULONG64)BlockSize << POOL_BLOCK_SHIFT);
|
|
|
|
//
|
|
// If p is still on the same page,
|
|
// then see if the block we point to has its
|
|
// previousblocksize == blocksize
|
|
//
|
|
if (PAGE_ALIGN64(p) == StartPage) {
|
|
|
|
GetFieldValue( p, "nt!_POOL_HEADER", "PreviousSize", tmpPreviousSize);
|
|
|
|
if ((tmpPreviousSize == BlockSize) && (tmpPreviousSize != 0)) {
|
|
|
|
HeaderInfo |= HEADER_FORWARD_LINK;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// if p points to the beginning of the next page,
|
|
// then the block Pool refers to *may* be the last block
|
|
// on the page
|
|
//
|
|
if (p == (StartPage + POOL_PAGE_SIZE)) {
|
|
|
|
HeaderInfo |= HEADER_FORWARD_LINK;
|
|
HeaderInfo |= HEADER_END_PAGE_BLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return HeaderInfo;
|
|
}
|
|
|
|
UCHAR
|
|
ValidatePoolHeader(
|
|
IN ULONG64 Pool
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine determines if a given pool header as valid
|
|
forward and backward links.
|
|
|
|
Arguments:
|
|
|
|
Pool - header (node) to analyze
|
|
|
|
Returns:
|
|
|
|
Header info for the node
|
|
|
|
--*/
|
|
{
|
|
UCHAR HeaderInfo;
|
|
|
|
HeaderInfo = 0;
|
|
HeaderInfo |= ValidatePoolHeaderBackLink(Pool);
|
|
HeaderInfo |= ValidatePoolHeaderForwardLink(Pool);
|
|
|
|
return HeaderInfo;
|
|
}
|
|
|
|
NTSTATUS
|
|
ScanPoolHeaders(
|
|
IN ULONG64 PoolPageToEnumerate,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataSize
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine does the first pass analysis of the pool page headers.
|
|
The net result of this routine is that we end up with a set of
|
|
pool header meta information, where the information describes
|
|
the health of a node - if the forward and back links exist, etc.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToEnumerate - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG64 StartPage;
|
|
ULONG64 StartHeader;
|
|
ULONG64 Pool;
|
|
ULONG SizeOfPoolHdr;
|
|
ULONG PoolIndex;
|
|
PVALIDATE_POOL_HEADER_INFO p;
|
|
ULONG Pass;
|
|
BOOLEAN Done;
|
|
BOOLEAN Tracing;
|
|
|
|
//
|
|
// default
|
|
//
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
Pool = PAGE_ALIGN64 (PoolPageToEnumerate);
|
|
StartPage = Pool;
|
|
Status = STATUS_SUCCESS;
|
|
PoolIndex = 0;
|
|
p = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
StartHeader = StartPage;
|
|
Pass = 0;
|
|
Done = FALSE;
|
|
|
|
//
|
|
// iterate through the page and validate the headers
|
|
//
|
|
|
|
#if DBG_SCAN
|
|
dprintf("DataSize = %d, DataLength = %d\r\n", DataSize, DataSize / sizeof(VALIDATE_POOL_HEADER_INFO));
|
|
#endif
|
|
|
|
//
|
|
// algorithm:
|
|
//
|
|
// Consider each possible node in the pool page to be
|
|
// the start of a linked list. For each start node (head),
|
|
// attempt to follow the list by tracing forward links.
|
|
// All nodes that are part of a single list, are assigned
|
|
// the same pass value - they were all traced on the same pass.
|
|
// The net result is a set of linked lists grouped by pass.
|
|
//
|
|
// We skip nodes that have already been marked as being part
|
|
// of another linked list
|
|
//
|
|
//
|
|
while ((PAGE_ALIGN64(StartHeader) == StartPage) && !Done) {
|
|
|
|
//
|
|
// each iteration is a new pass
|
|
//
|
|
Pass += 1;
|
|
|
|
//
|
|
// start scanning from the new first node
|
|
//
|
|
Pool = StartHeader;
|
|
PoolIndex = (ULONG)((Pool - StartPage) / SizeOfPoolHdr);
|
|
|
|
//
|
|
// default: we have not found the start of a linked list
|
|
//
|
|
Tracing = FALSE;
|
|
|
|
//
|
|
// while we are still on the correct page
|
|
//
|
|
while ((PAGE_ALIGN64(Pool) == StartPage)) {
|
|
|
|
#if DBG_SCAN
|
|
dprintf("Pass = %d, PoolIndex = %d, Pool = %p\r\n", Pass, PoolIndex, Pool);
|
|
#endif
|
|
|
|
ASSERT(PoolIndex < (DataSize/sizeof(VALIDATE_POOL_HEADER_INFO)));
|
|
|
|
if ( CheckControlC() ) {
|
|
Done = TRUE;
|
|
break;
|
|
}
|
|
|
|
#if DBG_SCAN
|
|
dprintf("p[PoolIndex].Pass = %d\r\n", p[PoolIndex].Pass);
|
|
#endif
|
|
|
|
//
|
|
//
|
|
// Skip nodes that have already been marked as being part
|
|
// of another linked list
|
|
//
|
|
if (p[PoolIndex].Pass == 0) {
|
|
|
|
//
|
|
// determine the link info for this header
|
|
//
|
|
p[PoolIndex].Info = ValidatePoolHeader(Pool);
|
|
|
|
#if DBG_SCAN
|
|
dprintf("p[PoolIndex].Info = %d\r\n", p[PoolIndex].Info);
|
|
#endif
|
|
|
|
//
|
|
// if the node has any chance of being valid,
|
|
// then consider it
|
|
// otherwise mark it invalid
|
|
//
|
|
if ((p[PoolIndex].Info & HEADER_FORWARD_LINK) ||
|
|
(p[PoolIndex].Info & HEADER_BACK_LINK) ||
|
|
(p[PoolIndex].Info & HEADER_START_PAGE_BLOCK)
|
|
) {
|
|
|
|
//
|
|
// if we are not already tracing,
|
|
// then we have found the first node of a linked list
|
|
//
|
|
if (!Tracing) {
|
|
|
|
//
|
|
// we have found a valid node,
|
|
// so we now consider ourselves tracing
|
|
// the links of a list
|
|
//
|
|
Tracing = TRUE;
|
|
|
|
//
|
|
// mark the current node as the start of the list
|
|
//
|
|
p[PoolIndex].Info |= HEADER_FIRST_LINK;
|
|
|
|
}
|
|
|
|
//
|
|
// the node has a valid link,
|
|
// so it is part of this pass
|
|
//
|
|
// if the back link is good and the forward link is bad
|
|
// then this is the end of a list
|
|
// if the back link is bad and the forward link is good
|
|
// then this is the start of a list
|
|
// if both are good,
|
|
// then this is the middle node of a list
|
|
//
|
|
p[PoolIndex].Pass = Pass;
|
|
|
|
//
|
|
// keep track of the node's address
|
|
//
|
|
p[PoolIndex].Node = Pool;
|
|
|
|
//
|
|
// keep track of what may or may not be a valid back link
|
|
// we use this value later when/if we need to correct broken links
|
|
//
|
|
{
|
|
ULONG PreviousSize;
|
|
|
|
//
|
|
// calculate the backward node
|
|
//
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
|
|
p[PoolIndex].BackLink = Pool - ((ULONG64)PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
}
|
|
|
|
//
|
|
// keep track of what may or may not be a valid forward link
|
|
// we use this value later when/if we need to correct broken links
|
|
//
|
|
{
|
|
ULONG BlockSize;
|
|
|
|
//
|
|
// calculate the forward node
|
|
//
|
|
GetFieldValue( Pool, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
|
|
//
|
|
// keep track of the forward node
|
|
//
|
|
p[PoolIndex].ForwardLink = Pool + ((ULONG64)BlockSize << POOL_BLOCK_SHIFT);
|
|
|
|
}
|
|
|
|
//
|
|
// if the node has a valid forward link,
|
|
// then follow it as part of this pass
|
|
//
|
|
if (p[PoolIndex].Info & HEADER_FORWARD_LINK) {
|
|
|
|
//
|
|
// calculate the forward node
|
|
//
|
|
Pool = p[PoolIndex].ForwardLink;
|
|
|
|
//
|
|
// calculate the pool info index of the forward node
|
|
//
|
|
PoolIndex = (ULONG)((Pool - StartPage) / SizeOfPoolHdr);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// the forward link is broken,
|
|
// hence we are at the end of a list
|
|
//
|
|
p[PoolIndex].Info |= HEADER_LAST_LINK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// assert: if we got here and are tracing a list
|
|
// then the forward link of the previous node
|
|
// pointed to a node with no valid links
|
|
// this is impossible and probably implies
|
|
// that the code to determine the forward link
|
|
// is broken
|
|
//
|
|
if (Tracing) {
|
|
dprintf("error: forward link lead to invalid node! header = %p\r\n", Pool);
|
|
}
|
|
ASSERT(!Tracing);
|
|
|
|
//
|
|
// mark this as a NULL pass
|
|
//
|
|
p[PoolIndex].Pass = 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// assert: we should only reach here if we havent started
|
|
// tracing a list, otherwise we have landed on
|
|
// a node which we already explored!
|
|
//
|
|
if (Tracing) {
|
|
dprintf("error: two lists share a node! header = %p\r\n", Pool);
|
|
}
|
|
ASSERT(!Tracing);
|
|
|
|
//
|
|
//
|
|
//
|
|
Pool = Pool + SizeOfPoolHdr;
|
|
PoolIndex += 1;
|
|
|
|
}
|
|
|
|
//
|
|
// if we never started tracing a linked list,
|
|
// then we should bail because no further searches
|
|
// will yield a list
|
|
//
|
|
if (!Tracing) {
|
|
|
|
#if DBG_SCAN
|
|
dprintf("no new valid headers found: giving up poolheader scanning\n");
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
StartHeader += SizeOfPoolHdr;
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ResolvePoolHeaders(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine does the main work of resolving broken links in
|
|
the pool header meta information. Given the raw pool header
|
|
info, this routine tries to fix broken forward and back links.
|
|
It also determines contiguous linked lists (passes) as well
|
|
as marking various pool header attributes.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG Pass;
|
|
BOOLEAN Found;
|
|
ULONG PoolHeaderInfoLength;
|
|
ULONG SizeOfPoolHdr;
|
|
UINT64 StartPage;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// default: we were successful
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
//
|
|
//
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
Pass = 0;
|
|
|
|
//
|
|
// while we continue to find linked lists,
|
|
// continue to process them.
|
|
//
|
|
// when the pool page was scanned, we marked each unique linked list
|
|
// with the pass # that it was found/traced on. Here, we
|
|
// are looking for all the nodes - i.e., the list which belong
|
|
// to our current pass.
|
|
//
|
|
// If we do not find (Found == FALSE) a linked list, then
|
|
// we have processed all the scanned lists.
|
|
//
|
|
do {
|
|
|
|
#if DBG_RESOLVE
|
|
dprintf("ResolvePoolHeaders: Pass = %d\n", Pass);
|
|
#endif
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// advance to the next pass
|
|
//
|
|
Pass += 1;
|
|
|
|
//
|
|
// default: there is no linked list in this pass
|
|
//
|
|
Found = FALSE;
|
|
|
|
//
|
|
// for every header in the pool page
|
|
// find the beginning of the linked list belonging to our pass
|
|
// if necessary, attempt to repair any broken nodes.
|
|
//
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
|
|
#if DBG_RESOLVE
|
|
dprintf("ResolvePoolHeaders: PoolHeaderInfo[%d].Info = %d\n", i, PoolHeaderInfo[i].Info);
|
|
#endif
|
|
|
|
//
|
|
// Look for all the nodes which belong to our current pass
|
|
//
|
|
if (PoolHeaderInfo[i].Pass == Pass) {
|
|
|
|
//
|
|
// we found a linked list - of atleast 1 node
|
|
//
|
|
Found = TRUE;
|
|
|
|
//
|
|
// if the node is the head of a list
|
|
//
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIRST_LINK) {
|
|
|
|
//
|
|
// start of list
|
|
//
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_BACK_LINK) {
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_START_PAGE_BLOCK) {
|
|
|
|
//
|
|
// back link is valid
|
|
//
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
//
|
|
// invalid condition:
|
|
//
|
|
// this implies that the node is the first node of a list,
|
|
// it has a valid back link, but is not the first
|
|
// node of a pool page.
|
|
//
|
|
dprintf("\n");
|
|
dprintf("error: Inconsistent condition occured while resolving @ %p.\n", PoolHeaderInfo[i].Node);
|
|
dprintf("error: Node is the first node of a list and it has a valid back link,\n");
|
|
dprintf("error: but the node is not at the start of the page\n");
|
|
dprintf("\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ULONG k;
|
|
ULONG target;
|
|
BOOLEAN bHaveTarget;
|
|
|
|
bHaveTarget = FALSE;
|
|
|
|
//
|
|
// node does not have a valid back link
|
|
//
|
|
if (PoolHeaderInfo[i].Info & HEADER_START_PAGE_BLOCK) {
|
|
|
|
PoolHeaderInfo[i].Info |= HEADER_FIXED_BACK_LINK;
|
|
PoolHeaderInfo[i].FixedPreviousSize = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// validate the forward link of the previous node:
|
|
//
|
|
// assume the back link of the current node (i) is broken:
|
|
// attempt to find a node that has a forward
|
|
// link that refers to this node
|
|
//
|
|
for (k = 0; k < i; k++) {
|
|
|
|
//
|
|
// see if the forward link refers to our node
|
|
//
|
|
bHaveTarget = (PoolHeaderInfo[k].ForwardLink == PoolHeaderInfo[i].Node);
|
|
|
|
//
|
|
// make sure the node has a broken forward link
|
|
//
|
|
bHaveTarget = bHaveTarget && (! (PoolHeaderInfo[k].Info & HEADER_FORWARD_LINK));
|
|
|
|
if (bHaveTarget) {
|
|
target = k;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (bHaveTarget) {
|
|
|
|
//
|
|
// we have found a node whose forward link refers to
|
|
// the current node.
|
|
// therefore, the current node's previous size is corrupted
|
|
// we can correct it using the previous node's blocksize
|
|
//
|
|
PoolHeaderInfo[i].Info |= HEADER_FIXED_BACK_LINK;
|
|
PoolHeaderInfo[i].FixedPreviousSize =
|
|
(PoolHeaderInfo[i].Node - PoolHeaderInfo[k].Node) >> POOL_BLOCK_SHIFT;
|
|
|
|
//
|
|
// we now know the forward link of the target node is correct
|
|
//
|
|
PoolHeaderInfo[k].Info |= HEADER_FORWARD_LINK;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// validate back link of this node:
|
|
//
|
|
// assume the forward link of a previous node (k) is broken:
|
|
// determine if the node that this node's back link
|
|
// refers to is valid
|
|
//
|
|
for (k = 0; k < i; k++) {
|
|
|
|
//
|
|
// see if the current node's back link refers to any previous node
|
|
//
|
|
bHaveTarget = (PoolHeaderInfo[k].Node == PoolHeaderInfo[i].BackLink);
|
|
|
|
//
|
|
// make sure the node has a broken forward link
|
|
//
|
|
bHaveTarget = bHaveTarget && (! (PoolHeaderInfo[k].Info & HEADER_FORWARD_LINK));
|
|
|
|
if (bHaveTarget) {
|
|
target = k;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (bHaveTarget) {
|
|
|
|
//
|
|
// we have found a node that the current node's backlink refers to.
|
|
// therefore, the current node's previous size is valid
|
|
// we can correct it using the previous node's blocksize
|
|
//
|
|
PoolHeaderInfo[k].Info |= HEADER_FIXED_FORWARD_LINK;
|
|
PoolHeaderInfo[k].FixedBlockSize =
|
|
(PoolHeaderInfo[i].Node - PoolHeaderInfo[k].Node) >> POOL_BLOCK_SHIFT;
|
|
|
|
//
|
|
// we now know the back link of our current node is correct
|
|
//
|
|
PoolHeaderInfo[i].Info |= HEADER_BACK_LINK;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// otherwise:
|
|
//
|
|
// the back link of this node is broken
|
|
// the forward link of the previous known good
|
|
// node is broken
|
|
//
|
|
// hence there is a corrupt region between
|
|
// and possibly including portions of these nodes
|
|
//
|
|
NOTHING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// if the node is the tail of a list
|
|
//
|
|
if (PoolHeaderInfo[i].Info & HEADER_LAST_LINK) {
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_END_PAGE_BLOCK) {
|
|
|
|
//
|
|
// the forward link of this node is valid
|
|
//
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
ULONG k;
|
|
ULONG target;
|
|
BOOLEAN bHaveTarget;
|
|
|
|
bHaveTarget = FALSE;
|
|
|
|
//
|
|
// the last link of a list should always point
|
|
// to the page end, hence
|
|
// the forward link of this node is INVALID/corrupt
|
|
//
|
|
|
|
//
|
|
// validate the back link of the next node:
|
|
//
|
|
// assume the forward link for the current node (i) is bad:
|
|
// attempt to find a node that has a back
|
|
// link that refers to this node
|
|
//
|
|
for (k = i+1; k < PoolHeaderInfoLength; k++) {
|
|
|
|
//
|
|
// see if the a following node refers to the current node
|
|
//
|
|
bHaveTarget = (PoolHeaderInfo[k].BackLink == PoolHeaderInfo[i].Node);
|
|
|
|
//
|
|
// make sure the following node is missing it's back link
|
|
//
|
|
bHaveTarget = bHaveTarget && (! (PoolHeaderInfo[k].Info & HEADER_BACK_LINK));
|
|
|
|
if (bHaveTarget) {
|
|
target = k;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (bHaveTarget) {
|
|
|
|
//
|
|
// we have found a node whose forward link refers to
|
|
// the current node.
|
|
// therefore, the current node's previous size is corrupted
|
|
// we can correct it using the previous node's blocksize
|
|
//
|
|
PoolHeaderInfo[i].Info |= HEADER_FIXED_FORWARD_LINK;
|
|
PoolHeaderInfo[i].FixedBlockSize =
|
|
(PoolHeaderInfo[k].Node - PoolHeaderInfo[i].Node) >> POOL_BLOCK_SHIFT;
|
|
|
|
//
|
|
// we now know the back link of our target node is correct
|
|
//
|
|
PoolHeaderInfo[k].Info |= HEADER_BACK_LINK;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// validate forward link of this node:
|
|
//
|
|
// assume the back link for a following node (k) is bad:
|
|
// determine if the node that this node's forward link
|
|
// refers to is valid
|
|
//
|
|
for (k = i+1; k < PoolHeaderInfoLength; k++) {
|
|
|
|
//
|
|
// see if the a following node refers to the current node
|
|
//
|
|
bHaveTarget = (PoolHeaderInfo[k].Node == PoolHeaderInfo[i].ForwardLink);
|
|
|
|
//
|
|
// make sure the following node is missing it's back link
|
|
//
|
|
bHaveTarget = bHaveTarget && (! (PoolHeaderInfo[k].Info & HEADER_BACK_LINK));
|
|
|
|
if (bHaveTarget) {
|
|
target = k;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (bHaveTarget) {
|
|
|
|
//
|
|
// we have found a node whose forward link refers to
|
|
// the current node.
|
|
// therefore, the current node's previous size is corrupted
|
|
// we can correct it using the previous node's blocksize
|
|
//
|
|
PoolHeaderInfo[k].Info |= HEADER_FIXED_BACK_LINK;
|
|
PoolHeaderInfo[k].FixedPreviousSize =
|
|
(PoolHeaderInfo[k].Node - PoolHeaderInfo[i].Node) >> POOL_BLOCK_SHIFT;
|
|
|
|
//
|
|
// we now know the back link of our target node is correct
|
|
//
|
|
PoolHeaderInfo[i].Info |= HEADER_FORWARD_LINK;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// otherwise:
|
|
//
|
|
// the forward link of this node is broken
|
|
// the back link of the next known good
|
|
// node is broken
|
|
//
|
|
// hence there is a corrupt region between
|
|
// and possibly including portions of these nodes
|
|
//
|
|
NOTHING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Node was not part of linked list belonging to this pass
|
|
//
|
|
NOTHING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
} while ( Found );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
DumpPoolHeaderInfo(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN ULONG i,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Debug utility
|
|
|
|
dump a SINGLE pool header meta info structure that has been collected
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
i - the structure to dump
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG PoolHeaderInfoLength;
|
|
UINT64 StartPage;
|
|
ULONG SizeOfPoolHdr;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
//
|
|
//
|
|
//
|
|
{
|
|
BOOLEAN First;
|
|
|
|
First = TRUE;
|
|
|
|
dprintf("[ %p ]:", StartPage + (i*SizeOfPoolHdr));
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIRST_LINK) {
|
|
dprintf("%s FIRST_LINK", !First ? " |" : ""); First = FALSE;
|
|
}
|
|
if (PoolHeaderInfo[i].Info & HEADER_LAST_LINK) {
|
|
dprintf("%s LAST_LINK", !First ? " |" : ""); First = FALSE;
|
|
}
|
|
if (PoolHeaderInfo[i].Info & HEADER_START_PAGE_BLOCK) {
|
|
dprintf("%s START_PAGE_BLOCK", !First ? " |" : ""); First = FALSE;
|
|
}
|
|
if (PoolHeaderInfo[i].Info & HEADER_END_PAGE_BLOCK) {
|
|
dprintf("%s END_PAGE_BLOCK", !First ? " |" : ""); First = FALSE;
|
|
}
|
|
if (First) {
|
|
dprintf(" interior node");
|
|
}
|
|
dprintf("\n");
|
|
|
|
}
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_BACK_LINK) {
|
|
|
|
dprintf(
|
|
"[ %p ]: back link [ %p ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
PoolHeaderInfo[i].BackLink
|
|
);
|
|
|
|
}
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK) {
|
|
|
|
dprintf(
|
|
"[ %p ]: forward link [ %p ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
PoolHeaderInfo[i].ForwardLink
|
|
);
|
|
|
|
}
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIXED_BACK_LINK) {
|
|
|
|
ULONG PreviousSize;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
|
|
dprintf(
|
|
"[ %p ]: invalid previous size [ %d ] should be [ %d ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
PreviousSize,
|
|
PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
}
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK) {
|
|
|
|
ULONG BlockSize;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
|
|
dprintf(
|
|
"[ %p ]: invalid block size [ %d ] should be [ %d ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
BlockSize,
|
|
PoolHeaderInfo[i].FixedBlockSize
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
DiagnosePoolHeadersDumpInfo(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Debug utility
|
|
|
|
dump all of the pool header meta info that has been collected
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG64 i;
|
|
ULONG PoolHeaderInfoLength;
|
|
UINT64 StartPage;
|
|
ULONG SizeOfPoolHdr;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
if (! IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
continue;
|
|
}
|
|
|
|
DumpPoolHeaderInfo(
|
|
PoolPageToDump,
|
|
Data,
|
|
DataLength,
|
|
(ULONG)i,
|
|
Verbose
|
|
);
|
|
|
|
dprintf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
DiagnosePoolHeadersIsValid(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determines if a pool page is corrupt
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
TRUE - page is corrupt
|
|
FALSE - page is NOT corrupt
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG64 i;
|
|
ULONG PoolHeaderInfoLength;
|
|
BOOLEAN IsValid;
|
|
UINT64 StartPage;
|
|
ULONG SizeOfPoolHdr;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
IsValid = TRUE;
|
|
|
|
i=0;
|
|
while ( i < PoolHeaderInfoLength ) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
if (IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
|
|
//
|
|
// if both links are valid,
|
|
// then follow the forward link
|
|
// else the list is invalid
|
|
//
|
|
if ((PoolHeaderInfo[i].Info & HEADER_BACK_LINK) &&
|
|
(PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK)) {
|
|
|
|
i = (PoolHeaderInfo[i].ForwardLink - StartPage) / SizeOfPoolHdr;
|
|
|
|
} else {
|
|
|
|
IsValid = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
IsValid = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return IsValid;
|
|
|
|
}
|
|
|
|
VOID
|
|
DiagnosePoolHeadersDisplayLists(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine displays all of the linked lists in a given pool page
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG PoolHeaderInfoLength;
|
|
UINT64 StartPage;
|
|
ULONG SizeOfPoolHdr;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
if (IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
|
|
//
|
|
//
|
|
//
|
|
#if 0
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
dprintf("[ headerinfo = %02x @ %p ]: ", PoolHeaderInfo[i].Info, StartPage + (i*SizeOfPoolHdr));
|
|
} else {
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
}
|
|
|
|
for (j = 0; j < PoolHeaderInfo[i].Pass; j++) {
|
|
|
|
dprintf(" ");
|
|
|
|
}
|
|
|
|
dprintf("%02d\r\n", PoolHeaderInfo[i].Pass);
|
|
|
|
#else
|
|
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
|
|
DumpPoolHeaderInfo(
|
|
PoolPageToDump,
|
|
Data,
|
|
DataLength,
|
|
(ULONG)i,
|
|
Verbose
|
|
);
|
|
|
|
}
|
|
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
|
|
for (j = 0; j < PoolHeaderInfo[i].Pass; j++) {
|
|
|
|
dprintf(" ");
|
|
|
|
}
|
|
|
|
dprintf("%02d\r\n", PoolHeaderInfo[i].Pass);
|
|
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
dprintf("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
FindLongestList(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
OUT PULONG PassStart
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Find the longest contiguous linked list in the pool page
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
PassStart - List # we think is the start of the longest list
|
|
|
|
Returns:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG64 i;
|
|
ULONG PoolHeaderInfoLength;
|
|
BOOLEAN InCorruptedRegion;
|
|
UINT64 CorruptedRegionStart;
|
|
ULONG CorruptedRegionPassStart;
|
|
UINT MaxListPassStart;
|
|
UINT MaxListLength;
|
|
BOOLEAN Found;
|
|
ULONG Pass;
|
|
ULONG SizeOfPoolHdr;
|
|
ULONG64 StartPage;
|
|
ULONG ListLength;
|
|
ULONG ListPassStart;
|
|
|
|
//
|
|
// default: we were successful
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
//
|
|
//
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
//
|
|
//
|
|
//
|
|
MaxListPassStart = 0;
|
|
MaxListLength = 0;
|
|
*PassStart = 0;
|
|
|
|
Pass = 0;
|
|
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
do {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
Pass++;
|
|
|
|
InCorruptedRegion = FALSE;
|
|
|
|
ListLength = 0;
|
|
ListPassStart = Pass;
|
|
|
|
Found = FALSE;
|
|
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
if (PoolHeaderInfo[i].Pass == Pass) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (i < PoolHeaderInfoLength) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// make sure we arent in an infinite loop
|
|
//
|
|
// if we are, then we could be looking at memory
|
|
// that is not available - mini dump and PoolPageToDump = 0
|
|
//
|
|
if (ListLength > MAX_POOL_HEADER_COUNT) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
if (IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
|
|
//
|
|
// if we are in a corrupt region,
|
|
// then determine if where we are in the region.
|
|
//
|
|
if (InCorruptedRegion) {
|
|
|
|
//
|
|
// ignore nodes that are clearly part of another
|
|
// linked list
|
|
// if we are tracing the longest list,
|
|
// then these nodes are considered spurious
|
|
//
|
|
if ((PoolHeaderInfo[i].Info & HEADER_BACK_LINK) ||
|
|
(PoolHeaderInfo[i].Info & HEADER_FIXED_BACK_LINK)) {
|
|
|
|
//
|
|
// we've hit the middle of another linked list so
|
|
// continue until we find the beginning of a
|
|
// list
|
|
//
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// we've found the beginning of a new list
|
|
// and are no longer in a Corrupt region
|
|
//
|
|
InCorruptedRegion = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
if ((PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK) ||
|
|
(PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK)) {
|
|
|
|
ListLength++;
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK) {
|
|
|
|
i = (PoolHeaderInfo[i].ForwardLink - StartPage) / SizeOfPoolHdr;
|
|
|
|
} else if (PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK) {
|
|
|
|
UINT64 offset;
|
|
|
|
offset = PoolHeaderInfo[i].FixedBlockSize << POOL_BLOCK_SHIFT;
|
|
|
|
i = ((PoolHeaderInfo[i].Node + offset) - StartPage ) / SizeOfPoolHdr;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
//
|
|
//
|
|
CorruptedRegionStart = PoolHeaderInfo[i].Node;
|
|
CorruptedRegionPassStart = PoolHeaderInfo[i].Pass;
|
|
|
|
InCorruptedRegion = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// if pool header was not valid,
|
|
// then increment by one header
|
|
//
|
|
i++;
|
|
|
|
}
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
if (ListLength > MaxListLength) {
|
|
MaxListLength = ListLength;
|
|
MaxListPassStart = ListPassStart;
|
|
}
|
|
|
|
} while ( Found );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// send back the max list start point
|
|
//
|
|
*PassStart = MaxListPassStart;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AnalyzeLongestList(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN ULONG PassStart,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Attempt to diagnose the longest linked list.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
PassStart - the list # that we think is the beginning
|
|
of the longest list
|
|
Verbose - the level of spew
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG64 i;
|
|
ULONG PoolHeaderInfoLength;
|
|
BOOLEAN InCorruptedRegion;
|
|
UINT64 CorruptedRegionStart;
|
|
ULONG CorruptedRegionPassStart;
|
|
ULONG Pass;
|
|
ULONG SizeOfPoolHdr;
|
|
ULONG64 StartPage;
|
|
|
|
//
|
|
// default: we were successful
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
//
|
|
//
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
Pass = PassStart;
|
|
|
|
if (PassStart > 0) {
|
|
Pass--;
|
|
}
|
|
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
Pass++;
|
|
|
|
InCorruptedRegion = FALSE;
|
|
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
if (PoolHeaderInfo[i].Pass == Pass) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (i < PoolHeaderInfoLength) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
if (IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
|
|
//
|
|
// if we are in a corrupt region,
|
|
// then determine if where we are in the region.
|
|
//
|
|
if (InCorruptedRegion) {
|
|
|
|
//
|
|
// ignore nodes that are clearly part of another
|
|
// linked list
|
|
// if we are tracing the longest list,
|
|
// then these nodes are considered spurious
|
|
//
|
|
if ((PoolHeaderInfo[i].Info & HEADER_BACK_LINK) ||
|
|
(PoolHeaderInfo[i].Info & HEADER_FIXED_BACK_LINK)) {
|
|
|
|
//
|
|
// we've hit the middle of another linked list so
|
|
// continue until we find the beginning of a
|
|
// list
|
|
//
|
|
|
|
if (Verbose & VERBOSE_SHOW_ALL) {
|
|
dprintf("[ %p ]: found middle node in Corrupt region\n", PoolHeaderInfo[i].Node);
|
|
}
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// we've found the beginning of a new list
|
|
// and are no longer in a Corrupt region
|
|
//
|
|
|
|
if (Verbose & VERBOSE_SHOW_ALL) {
|
|
dprintf("[ %p ]: Corrupt region stopped\n", PoolHeaderInfo[i].Node);
|
|
}
|
|
|
|
dprintf("[ %p --> %p (size = 0x%x bytes)]: Corrupt region\n",
|
|
CorruptedRegionStart,
|
|
PoolHeaderInfo[i].Node,
|
|
PoolHeaderInfo[i].Node - CorruptedRegionStart
|
|
);
|
|
|
|
InCorruptedRegion = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// emit
|
|
//
|
|
if (Verbose) {
|
|
|
|
//
|
|
// emit the current node info
|
|
//
|
|
{
|
|
ULONG j;
|
|
|
|
//
|
|
//
|
|
//
|
|
#if 1
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
dprintf("[ headerinfo = 0x%02x @ %p ]: ", PoolHeaderInfo[i].Info, StartPage + (i*SizeOfPoolHdr));
|
|
} else {
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
}
|
|
#else
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
#endif
|
|
|
|
for (j = 0; j < PoolHeaderInfo[i].Pass; j++) {
|
|
|
|
dprintf(" ");
|
|
|
|
}
|
|
|
|
dprintf("%02d\r\n", PoolHeaderInfo[i].Pass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// if the back link is corrected,
|
|
// then display the correction
|
|
//
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIXED_BACK_LINK) {
|
|
|
|
ULONG PreviousSize;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
|
|
dprintf(
|
|
"[ %p ]: invalid previous size [ 0x%x ] should be [ 0x%x ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
PreviousSize,
|
|
PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
if ((PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK) ||
|
|
(PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK)) {
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FORWARD_LINK) {
|
|
|
|
// dprintf("using good forward link: %p\n", PoolHeaderInfo[i].ForwardLink);
|
|
|
|
i = (PoolHeaderInfo[i].ForwardLink - StartPage) / SizeOfPoolHdr;
|
|
|
|
} else if (PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK) {
|
|
|
|
UINT64 offset;
|
|
ULONG BlockSize;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
|
|
dprintf(
|
|
"[ %p ]: invalid block size [ 0x%x ] should be [ 0x%x ]\n",
|
|
PoolHeaderInfo[i].Node,
|
|
BlockSize,
|
|
PoolHeaderInfo[i].FixedBlockSize
|
|
);
|
|
|
|
offset = PoolHeaderInfo[i].FixedBlockSize << POOL_BLOCK_SHIFT;
|
|
|
|
i = ((PoolHeaderInfo[i].Node + offset) - StartPage ) / SizeOfPoolHdr;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
//
|
|
//
|
|
CorruptedRegionStart = PoolHeaderInfo[i].Node;
|
|
CorruptedRegionPassStart = PoolHeaderInfo[i].Pass;
|
|
|
|
if (Verbose & VERBOSE_SHOW_ALL) {
|
|
dprintf("[ %p ]: Corrupt region started\n", CorruptedRegionStart);
|
|
}
|
|
|
|
InCorruptedRegion = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// if pool header was not valid,
|
|
// then increment by one header
|
|
//
|
|
i++;
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
DiagnosePoolHeadersAnalyzeLongestList(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determines the longest contiguous linked list in the pool page.
|
|
|
|
We make the conclusion that the longest list is likely to be
|
|
the original, uncorrupted list.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
Verbose - level of verbosity
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG PassStart;
|
|
|
|
do {
|
|
|
|
//
|
|
// first, find the longest list
|
|
//
|
|
Status = FindLongestList(
|
|
PoolPageToDump,
|
|
Data,
|
|
DataLength,
|
|
&PassStart
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// once we have found the longest list
|
|
// we then try to analyze it
|
|
//
|
|
Status = AnalyzeLongestList(
|
|
PoolPageToDump,
|
|
Data,
|
|
DataLength,
|
|
PassStart,
|
|
Verbose
|
|
);
|
|
|
|
} while ( FALSE );
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
GetHammingDistance(
|
|
ULONG a,
|
|
ULONG b
|
|
)
|
|
{
|
|
ULONG x;
|
|
ULONG r;
|
|
|
|
x = a ^ b;
|
|
|
|
x -= ((x >> 1) & 0x55555555);
|
|
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
|
|
x = (((x >> 4) + x) & 0x0f0f0f0f);
|
|
x += (x >> 8);
|
|
x += (x >> 16);
|
|
|
|
r = (x & 0x0000003f);
|
|
|
|
// dprintf("a = %d, b = %d, r = %d\n", a, b, r);
|
|
|
|
return(r);
|
|
}
|
|
|
|
VOID
|
|
DiagnosePoolHeadersSingleBitErrors(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN OUT PVOID Data,
|
|
IN ULONG DataLength,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determins if & where there are any single bit errors in the pool headers.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Data - the pool header info
|
|
DataLength - the # of entries in the header info
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
ULONG i;
|
|
ULONG PoolHeaderInfoLength;
|
|
UINT64 StartPage;
|
|
BOOLEAN Found;
|
|
ULONG SizeOfPoolHdr;
|
|
|
|
//
|
|
//
|
|
//
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
SizeOfPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
PoolHeaderInfo = (PVALIDATE_POOL_HEADER_INFO)Data;
|
|
PoolHeaderInfoLength = DataLength;
|
|
|
|
Found = FALSE;
|
|
|
|
for (i = 0; i < PoolHeaderInfoLength; i++) {
|
|
|
|
if ( CheckControlC() ) {
|
|
break;
|
|
}
|
|
|
|
if (IS_VALID_PASS(PoolHeaderInfo[i].Pass)) {
|
|
|
|
//
|
|
//
|
|
//
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIXED_BACK_LINK) {
|
|
|
|
ULONG PreviousSize;
|
|
ULONG HammingDistance;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
|
|
//
|
|
//
|
|
//
|
|
HammingDistance = GetHammingDistance(
|
|
PreviousSize,
|
|
(ULONG)PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
if (HammingDistance == 1) {
|
|
|
|
Found = TRUE;
|
|
|
|
//
|
|
//
|
|
//
|
|
#if 0
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
dprintf("[ headerinfo = 0x%02x @ %p ]: ", PoolHeaderInfo[i].Info, StartPage + (i*SizeOfPoolHdr));
|
|
} else {
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
}
|
|
dprintf(
|
|
"previous size [ 0x%x ] should be [ 0x%x ]\n",
|
|
PreviousSize,
|
|
PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
#else
|
|
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
dprintf(
|
|
"previous size [ 0x%x ] should be [ 0x%x ]\n",
|
|
PreviousSize,
|
|
PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
if (PoolHeaderInfo[i].Info & HEADER_FIXED_FORWARD_LINK) {
|
|
|
|
ULONG BlockSize;
|
|
ULONG HammingDistance;
|
|
|
|
GetFieldValue( PoolHeaderInfo[i].Node, "nt!_POOL_HEADER", "BlockSize", BlockSize);
|
|
|
|
//
|
|
//
|
|
//
|
|
HammingDistance = GetHammingDistance(
|
|
BlockSize,
|
|
(ULONG)PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
if (HammingDistance == 1) {
|
|
|
|
Found = TRUE;
|
|
|
|
#if 0
|
|
|
|
if (Verbose & VERBOSE_SHOW_HEADER_INFO) {
|
|
dprintf("[ headerinfo = 0x%02x @ %p ]: ", PoolHeaderInfo[i].Info, StartPage + (i*SizeOfPoolHdr));
|
|
} else {
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
}
|
|
dprintf(
|
|
"previous size [ 0x%x ] should be [ 0x%x ]\n",
|
|
PreviousSize,
|
|
PoolHeaderInfo[i].FixedPreviousSize
|
|
);
|
|
|
|
#else
|
|
|
|
dprintf("[ %p ]: ", StartPage + (i*SizeOfPoolHdr));
|
|
dprintf(
|
|
"block size [ 0x%x ] should be [ 0x%x ]\n",
|
|
BlockSize,
|
|
PoolHeaderInfo[i].FixedBlockSize
|
|
);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!Found) {
|
|
|
|
dprintf("\n");
|
|
dprintf("None found\n");
|
|
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ValidatePoolPage(
|
|
IN ULONG64 PoolPageToDump,
|
|
IN UCHAR Verbose
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Provides the core, high level, functionality of analyzing the pool page
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - page to analyze
|
|
Verbose - diagnostic level
|
|
|
|
Returns:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PVALIDATE_POOL_HEADER_INFO PoolHeaderInfo;
|
|
NTSTATUS Status;
|
|
ULONG PoolHeaderInfoSize;
|
|
ULONG PoolHeaderInfoLength;
|
|
ULONG SizeofPoolHdr;
|
|
ULONG64 StartPage;
|
|
BOOLEAN IsValid;
|
|
|
|
//
|
|
//
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
SizeofPoolHdr = GetTypeSize("nt!_POOL_HEADER");
|
|
StartPage = PAGE_ALIGN64(PoolPageToDump);
|
|
|
|
PoolHeaderInfo = NULL;
|
|
PoolHeaderInfoSize = 0;
|
|
|
|
do {
|
|
|
|
//
|
|
// Allocate the pool header info array
|
|
//
|
|
PoolHeaderInfoLength = (POOL_PAGE_SIZE / SizeofPoolHdr);
|
|
PoolHeaderInfoSize = sizeof(VALIDATE_POOL_HEADER_INFO) * PoolHeaderInfoLength;
|
|
PoolHeaderInfo = malloc(PoolHeaderInfoSize);
|
|
|
|
if (PoolHeaderInfo == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(PoolHeaderInfo, PoolHeaderInfoSize);
|
|
|
|
//
|
|
// Construct the first layer of meta info:
|
|
//
|
|
// determine the link status for each pool header
|
|
// in the page
|
|
//
|
|
Status = ScanPoolHeaders(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoSize
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Construct the second layer of meta info:
|
|
//
|
|
// attempt to resolve the linked lists found
|
|
// in the pool page
|
|
//
|
|
Status = ResolvePoolHeaders(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// begin diagnosis output
|
|
//
|
|
dprintf("\n");
|
|
|
|
IsValid = DiagnosePoolHeadersIsValid(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength
|
|
);
|
|
|
|
dprintf("Pool page [ %p ] is %sVALID.\n",
|
|
StartPage,
|
|
IsValid ? "" : "IN"
|
|
);
|
|
|
|
if (! IsValid) {
|
|
|
|
if (Verbose & VERBOSE_DUMP_HEADERS) {
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("\n\n");
|
|
dprintf("Displaying all POOL_HEADER meta info...\n");
|
|
|
|
DiagnosePoolHeadersDumpInfo(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength,
|
|
Verbose
|
|
);
|
|
|
|
}
|
|
|
|
if (Verbose & VERBOSE_SHOW_LISTS) {
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("\n");
|
|
dprintf("Displaying linked lists...\n");
|
|
|
|
DiagnosePoolHeadersDisplayLists(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength,
|
|
Verbose
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("\n");
|
|
|
|
if (Verbose & VERBOSE_SHOW_LISTS) {
|
|
dprintf("Analyzing longest linked list...\n");
|
|
} else {
|
|
dprintf("Analyzing linked list...\n");
|
|
}
|
|
|
|
Status = DiagnosePoolHeadersAnalyzeLongestList(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength,
|
|
Verbose
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("\n\n");
|
|
dprintf("Scanning for single bit errors...\n");
|
|
|
|
DiagnosePoolHeadersSingleBitErrors(
|
|
PoolPageToDump,
|
|
PoolHeaderInfo,
|
|
PoolHeaderInfoLength,
|
|
Verbose
|
|
);
|
|
|
|
} else {
|
|
|
|
dprintf("\n");
|
|
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
dprintf("\n");
|
|
dprintf("Failed to diagnose pool page\n");
|
|
dprintf("\n");
|
|
}
|
|
|
|
if (PoolHeaderInfo) {
|
|
free(PoolHeaderInfo);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
DiagnosePoolPage(
|
|
ULONG64 PoolPageToDump
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Diagnose a pool page for the !pool command.
|
|
This routine is called if the !pool command detects an error while
|
|
traversing a pool page.
|
|
|
|
Arguments:
|
|
|
|
PoolPageToDump - the page !pool was examining
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DEBUG_POOL_REGION Region;
|
|
|
|
//
|
|
// determine the pool region
|
|
//
|
|
Region = GetPoolRegion(PoolPageToDump);
|
|
|
|
//
|
|
// we only try to diagnose known pool regions
|
|
//
|
|
// you can manually diagnose using !poolval
|
|
//
|
|
//
|
|
// Note: if someone passes us a 0 pool address,
|
|
// then we proactively fail the diagnosis process
|
|
//
|
|
if ((Region == DbgPoolRegionUnknown) || (PoolPageToDump == 0)) {
|
|
|
|
dprintf("\n");
|
|
dprintf("***\n");
|
|
dprintf("*** An error (or corruption) in the pool was detected;\n");
|
|
dprintf("*** Pool Region unkown (0x%I64X)\n", PoolPageToDump);
|
|
dprintf("***\n");
|
|
dprintf("*** Use !poolval %p for more details.\n", PAGE_ALIGN64(PoolPageToDump));
|
|
dprintf("***\n");
|
|
dprintf("\n");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
dprintf("\n");
|
|
dprintf("***\n");
|
|
dprintf("*** An error (or corruption) in the pool was detected;\n");
|
|
dprintf("*** Attempting to diagnose the problem.\n");
|
|
dprintf("***\n");
|
|
dprintf("*** Use !poolval %p for more details.\n", PAGE_ALIGN64(PoolPageToDump));
|
|
dprintf("***\n");
|
|
|
|
Status = ValidatePoolPage(
|
|
PoolPageToDump,
|
|
VERBOSE_SHOW_ERRORS_ONLY
|
|
);
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
DECLARE_API( poolval )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Provide in-depth heap diagnosis.
|
|
|
|
Given a suspect pool page, the primary purpose of this command
|
|
is to analyze the page and determine:
|
|
|
|
1. where in the page the corruption exists.
|
|
2. single bit errors in page headers (previous/block sizes)
|
|
3. what the correct link list should be
|
|
|
|
Arguments:
|
|
|
|
args - the page to analyze
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 PoolPageToDump;
|
|
ULONG Flags;
|
|
HRESULT Hr;
|
|
|
|
INIT_API();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
Hr = E_INVALIDARG;
|
|
} else {
|
|
|
|
PoolPageToDump = 0;
|
|
Flags = 0;
|
|
if (GetExpressionEx(args, &PoolPageToDump, &args)) {
|
|
Flags = (ULONG) GetExpression (args);
|
|
}
|
|
|
|
if (PoolPageToDump == 0) {
|
|
Hr = S_OK;;
|
|
} else {
|
|
|
|
UCHAR Verbose;
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("Pool page %p region is ", PoolPageToDump);
|
|
PrintPoolRegion(PoolPageToDump);
|
|
|
|
//
|
|
//
|
|
//
|
|
dprintf("\n");
|
|
dprintf("Validating Pool headers for pool page: %p\n", PoolPageToDump);
|
|
|
|
//
|
|
//
|
|
//
|
|
Verbose = VERBOSE_SHOW_ERRORS_ONLY;
|
|
switch (Flags) {
|
|
case 2: Verbose |= VERBOSE_SHOW_LISTS | VERBOSE_SHOW_HEADER_INFO; break;
|
|
case 3: Verbose |= VERBOSE_SHOW_LISTS | VERBOSE_SHOW_HEADER_INFO | VERBOSE_DUMP_HEADERS; break;
|
|
|
|
case 1: Verbose |= VERBOSE_SHOW_LISTS;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Attempt to Analyze and Diagnose the specified poolpage
|
|
//
|
|
Status = ValidatePoolPage(
|
|
PoolPageToDump,
|
|
Verbose
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
EXIT_API();
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Hr = S_OK;
|
|
} else {
|
|
Hr = E_FAIL;
|
|
}
|
|
|
|
return Hr;
|
|
|
|
}
|
|
|
|
|
|
DECLARE_API( poolused )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump usage by pool tag
|
|
|
|
Arguments:
|
|
|
|
args -
|
|
|
|
Flags : Bitfield with the following meaning:
|
|
|
|
0x1: Dump both allocations & frees (instead of the difference)
|
|
0x2: Sort by nonpaged pool consumption
|
|
0x4: Sort by paged pool consumption
|
|
0x8: Dump session space consumption
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PoolTrackTableSize;
|
|
ULONG PoolTrackTableSizeInBytes;
|
|
ULONG PoolTrackTableExpansionSize;
|
|
ULONG PoolTrackTableExpansionSizeInBytes;
|
|
PPOOLTRACK_READ p;
|
|
PPOOLTRACK_READ pentry;
|
|
PPOOLTRACK_READ PoolTrackTableData;
|
|
ULONG Flags;
|
|
ULONG i;
|
|
ULONG result;
|
|
ULONG ct;
|
|
ULONG TagName;
|
|
CHAR TagNameX[4] = {'*','*','*','*'};
|
|
ULONG SizeOfPoolTracker;
|
|
ULONG64 PoolTableAddress;
|
|
ULONG64 PoolTrackTable;
|
|
ULONG64 PoolTrackTableExpansion;
|
|
ULONG NonPagedAllocsTotal,NonPagedFreesTotal,PagedAllocsTotal,PagedFreesTotal;
|
|
ULONG64 NonPagedBytesTotal, PagedBytesTotal;
|
|
ULONG Processor, MaxProcessors;
|
|
ULONG64 ExPoolTagTables;
|
|
POOLTRACK_READ PoolTrackEntry;
|
|
ULONG64 Location;
|
|
|
|
ExPoolTagTables = GetExpression("nt!ExPoolTagTables");
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Flags = 0;
|
|
if (!sscanf(args,"%lx %c%c%c%c", &Flags, &TagNameX[0],
|
|
&TagNameX[1], &TagNameX[2], &TagNameX[3])) {
|
|
Flags = 0;
|
|
}
|
|
|
|
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
|
|
|
|
|
|
PoolTrackTableExpansionSize = 0;
|
|
|
|
if (!(SizeOfPoolTracker = GetTypeSize("nt!_POOL_TRACKER_TABLE"))) {
|
|
dprintf("Unable to get _POOL_TRACKER_TABLE : probably wrong symbols.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Flags & 0x8) {
|
|
Location = GetExpression ("ExpSessionPoolTrackTable");
|
|
if (!Location) {
|
|
dprintf("Unable to get ExpSessionPoolTrackTable\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ReadPointer(Location, &PoolTrackTable);
|
|
PoolTrackTableSize = 0;
|
|
PoolTrackTableSize = GetUlongValue ("nt!ExpSessionPoolTrackTableSize");
|
|
}
|
|
else {
|
|
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
|
|
PoolTrackTableSize = GetUlongValue ("nt!PoolTrackTableSize");
|
|
PoolTrackTableExpansionSize = GetUlongValue ("nt!PoolTrackTableExpansionSize");
|
|
}
|
|
|
|
if (PoolTrackTable == 0) {
|
|
dprintf ("unable to get PoolTrackTable - ");
|
|
if (GetExpression("nt!PoolTrackTable")) {
|
|
dprintf ("pool tagging is disabled, enable it to use this command\n");
|
|
dprintf ("Use gflags.exe and check the box that says \"Enable pool tagging\".\n");
|
|
} else {
|
|
dprintf ("symbols could be worng\n");
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Flags & 2) {
|
|
SortBy = NONPAGED_USED;
|
|
dprintf(" Sorting by %s NonPaged Pool Consumed\n", Flags & 0x8 ? "Session" : "");
|
|
} else if (Flags & 4) {
|
|
SortBy = PAGED_USED;
|
|
dprintf(" Sorting by %s Paged Pool Consumed\n", Flags & 0x8 ? "Session" : "");
|
|
} else {
|
|
SortBy = TAG;
|
|
dprintf(" Sorting by %s Tag\n", Flags & 0x8 ? "Session" : "");
|
|
}
|
|
|
|
dprintf("\n Pool Used:\n");
|
|
if (!(Flags & 1)) {
|
|
dprintf(" NonPaged Paged\n");
|
|
dprintf(" Tag Allocs Used Allocs Used\n");
|
|
|
|
} else {
|
|
dprintf(" NonPaged Paged\n");
|
|
dprintf(" Tag Allocs Frees Diff Used Allocs Frees Diff Used\n");
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Allocate a temp buffer, read the data, then free the data.
|
|
// (KD will cache the data).
|
|
//
|
|
|
|
PoolTrackTableSizeInBytes = PoolTrackTableSize * SizeOfPoolTracker;
|
|
|
|
PoolTrackTableExpansionSizeInBytes = PoolTrackTableExpansionSize * SizeOfPoolTracker;
|
|
|
|
PoolTrackTableData = malloc (PoolTrackTableSizeInBytes);
|
|
if (PoolTrackTableData == NULL) {
|
|
dprintf("unable to allocate memory for tag table.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PoolTableAddress = PoolTrackTable;
|
|
if ( !ReadMemory( PoolTableAddress,
|
|
&PoolTrackTableData[0],
|
|
PoolTrackTableSizeInBytes,
|
|
&result) ) {
|
|
dprintf("%08p: Unable to get contents of pool block\n", PoolTableAddress );
|
|
dprintf("\nThe current process probably is not a session process.\n");
|
|
dprintf("Note the system, idle and smss processes are non-session processes.\n");
|
|
free (PoolTrackTableData);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ct = PageSize / SizeOfPoolTracker;
|
|
i = 0;
|
|
PoolTableAddress = PoolTrackTable;
|
|
|
|
free (PoolTrackTableData);
|
|
|
|
//
|
|
// Create array of POOL_TRACKER_TABLE addresses and sort the addresses
|
|
//
|
|
|
|
PoolTrackTableData = malloc ((PoolTrackTableSize + PoolTrackTableExpansionSize) * sizeof(POOLTRACK_READ));
|
|
if (PoolTrackTableData == NULL) {
|
|
dprintf("unable to allocate memory for tag table.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Flags & 0x8) {
|
|
MaxProcessors = 1;
|
|
}
|
|
else {
|
|
MaxProcessors = (UCHAR) GetUlongValue ("KeNumberProcessors");
|
|
}
|
|
|
|
Processor = 0;
|
|
NonPagedAllocsTotal = 0;
|
|
NonPagedFreesTotal = 0;
|
|
NonPagedBytesTotal = 0;
|
|
|
|
PagedAllocsTotal = 0;
|
|
PagedFreesTotal = 0;
|
|
PagedBytesTotal = 0;
|
|
p = PoolTrackTableData;
|
|
|
|
do {
|
|
|
|
pentry = &PoolTrackEntry;
|
|
|
|
for (i = 0; i < PoolTrackTableSize; i += 1) {
|
|
|
|
if (Processor == 0) {
|
|
pentry->Address = PoolTableAddress + i * SizeOfPoolTracker;
|
|
}
|
|
else {
|
|
pentry->Address = PoolTrackTable + i * SizeOfPoolTracker;
|
|
}
|
|
|
|
#define TrackFld(Fld) GetFieldValue(pentry->Address, "nt!_POOL_TRACKER_TABLE", #Fld, pentry->Fld)
|
|
|
|
TrackFld(Key);
|
|
TrackFld(NonPagedAllocs);
|
|
TrackFld(NonPagedBytes);
|
|
TrackFld(PagedBytes);
|
|
TrackFld(NonPagedFrees);
|
|
TrackFld(PagedAllocs);
|
|
TrackFld(PagedFrees);
|
|
|
|
#undef TrackFld
|
|
|
|
#if 0
|
|
if (pentry->Key != 0) {
|
|
dprintf("%c%c%c%c %8x %8x %8I64x %8x %8x %8I64x\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(pentry->Key),
|
|
PP(pentry->Key >> 8),
|
|
PP(pentry->Key >> 16),
|
|
PP(pentry->Key >> 24),
|
|
pentry->NonPagedAllocs,
|
|
pentry->NonPagedFrees,
|
|
pentry->NonPagedBytes,
|
|
pentry->PagedAllocs,
|
|
pentry->PagedFrees,
|
|
pentry->PagedBytes);
|
|
}
|
|
#endif
|
|
|
|
if (Processor == 0) {
|
|
p[i].Address = pentry->Address;
|
|
p[i].Key = pentry->Key;
|
|
|
|
p[i].NonPagedAllocs = pentry->NonPagedAllocs;
|
|
p[i].NonPagedFrees = pentry->NonPagedFrees;
|
|
p[i].NonPagedBytes = pentry->NonPagedBytes;
|
|
|
|
p[i].PagedAllocs = pentry->PagedAllocs;
|
|
p[i].PagedBytes = pentry->PagedBytes;
|
|
p[i].PagedFrees = pentry->PagedFrees;
|
|
}
|
|
|
|
if ((pentry->Key != 0) &&
|
|
(CheckSingleFilter ((PCHAR)&pentry->Key, (PCHAR)&TagName))) {
|
|
|
|
if (Processor != 0) {
|
|
p[i].NonPagedAllocs += pentry->NonPagedAllocs;
|
|
p[i].NonPagedFrees += pentry->NonPagedFrees;
|
|
p[i].NonPagedBytes += pentry->NonPagedBytes;
|
|
|
|
p[i].PagedAllocs += pentry->PagedAllocs;
|
|
p[i].PagedFrees += pentry->PagedFrees;
|
|
p[i].PagedBytes += pentry->PagedBytes;
|
|
}
|
|
|
|
}
|
|
|
|
if (!IsPtr64()) {
|
|
p[i].NonPagedBytes &= (LONG64) 0xFFFFFFFF;
|
|
p[i].PagedBytes &= (LONG64) 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
Processor += 1;
|
|
|
|
if (Processor >= MaxProcessors) {
|
|
break;
|
|
}
|
|
|
|
if (ExPoolTagTables == 0) {
|
|
break;
|
|
}
|
|
|
|
ReadPointer (ExPoolTagTables+DBG_PTR_SIZE*Processor, &PoolTrackTable);
|
|
|
|
} while (TRUE);
|
|
|
|
//
|
|
// Add the expansion table too (if there is one).
|
|
//
|
|
|
|
if (PoolTrackTableExpansionSize != 0) {
|
|
|
|
//
|
|
// Allocate a temp buffer, read the data, then free the data.
|
|
// (KD will cache the data).
|
|
//
|
|
|
|
pentry = malloc (PoolTrackTableExpansionSizeInBytes);
|
|
if (pentry == NULL) {
|
|
dprintf("unable to allocate memory for expansion tag table.\n");
|
|
}
|
|
else {
|
|
PoolTrackTableExpansion = GetPointerValue("nt!PoolTrackTableExpansion");
|
|
PoolTableAddress = PoolTrackTableExpansion;
|
|
if ( !ReadMemory( PoolTableAddress,
|
|
pentry,
|
|
PoolTrackTableExpansionSizeInBytes,
|
|
&result) ) {
|
|
dprintf("%08p: Unable to get contents of expansion tag table\n", PoolTableAddress );
|
|
}
|
|
else {
|
|
|
|
PoolTrackTableSize += PoolTrackTableExpansionSize;
|
|
|
|
ct = 0;
|
|
for ( ; i < PoolTrackTableSize; i += 1, ct += 1) {
|
|
|
|
p[i].Address = PoolTableAddress + ct * SizeOfPoolTracker;
|
|
|
|
#define TrackFld(Fld) GetFieldValue(p[i].Address, "nt!_POOL_TRACKER_TABLE", #Fld, p[i].Fld)
|
|
|
|
TrackFld(Key);
|
|
TrackFld(NonPagedAllocs);
|
|
TrackFld(NonPagedBytes);
|
|
TrackFld(PagedBytes);
|
|
TrackFld(NonPagedFrees);
|
|
TrackFld(PagedAllocs);
|
|
TrackFld(PagedFrees);
|
|
|
|
#if 0
|
|
if (p[i].Key != 0) {
|
|
dprintf("%c%c%c%c %8x %8x %8I64x %8x %8x %8I64x\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(p[i].Key),
|
|
PP(p[i].Key >> 8),
|
|
PP(p[i].Key >> 16),
|
|
PP(p[i].Key >> 24),
|
|
p[i].NonPagedAllocs,
|
|
p[i].NonPagedFrees,
|
|
p[i].NonPagedBytes,
|
|
p[i].PagedAllocs,
|
|
p[i].PagedFrees,
|
|
p[i].PagedBytes);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
free (pentry);
|
|
}
|
|
}
|
|
|
|
qsort((void *)PoolTrackTableData,
|
|
(size_t)PoolTrackTableSize,
|
|
(size_t)sizeof(POOLTRACK_READ),
|
|
ulcomp);
|
|
|
|
i = 0;
|
|
p = PoolTrackTableData;
|
|
for ( ; i < PoolTrackTableSize; i += 1) {
|
|
|
|
if ((p[i].Key != 0) &&
|
|
(CheckSingleFilter ((PCHAR)&p[i].Key, (PCHAR)&TagName))) {
|
|
|
|
if (!(Flags & 1)) {
|
|
|
|
if ((p[i].NonPagedBytes != 0) || (p[i].PagedBytes != 0)) {
|
|
|
|
dprintf(" %c%c%c%c %8ld %8I64ld %8ld %8I64ld\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(p[i].Key),
|
|
PP(p[i].Key >> 8),
|
|
PP(p[i].Key >> 16),
|
|
PP(p[i].Key >> 24),
|
|
p[i].NonPagedAllocs - p[i].NonPagedFrees,
|
|
p[i].NonPagedBytes,
|
|
p[i].PagedAllocs - p[i].PagedFrees,
|
|
p[i].PagedBytes);
|
|
|
|
NonPagedAllocsTotal += p[i].NonPagedAllocs;
|
|
NonPagedFreesTotal += p[i].NonPagedFrees;
|
|
NonPagedBytesTotal += p[i].NonPagedBytes;
|
|
|
|
PagedAllocsTotal += p[i].PagedAllocs;
|
|
PagedFreesTotal += p[i].PagedFrees;
|
|
PagedBytesTotal += p[i].PagedBytes;
|
|
}
|
|
} else {
|
|
|
|
dprintf(" %c%c%c%c %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
|
|
PP(p[i].Key),
|
|
PP(p[i].Key >> 8),
|
|
PP(p[i].Key >> 16),
|
|
PP(p[i].Key >> 24),
|
|
p[i].NonPagedAllocs,
|
|
p[i].NonPagedFrees,
|
|
p[i].NonPagedAllocs - p[i].NonPagedFrees,
|
|
p[i].NonPagedBytes,
|
|
p[i].PagedAllocs,
|
|
p[i].PagedFrees,
|
|
p[i].PagedAllocs - p[i].PagedFrees,
|
|
p[i].PagedBytes);
|
|
#undef PP
|
|
NonPagedAllocsTotal += p[i].NonPagedAllocs;
|
|
NonPagedFreesTotal += p[i].NonPagedFrees;
|
|
NonPagedBytesTotal += p[i].NonPagedBytes;
|
|
|
|
PagedAllocsTotal += p[i].PagedAllocs;
|
|
PagedFreesTotal += p[i].PagedFrees;
|
|
PagedBytesTotal += p[i].PagedBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(Flags & 1)) {
|
|
dprintf(" TOTAL %8ld %8I64ld %8ld %8I64ld\n",
|
|
NonPagedAllocsTotal - NonPagedFreesTotal,
|
|
NonPagedBytesTotal,
|
|
PagedAllocsTotal - PagedFreesTotal,
|
|
PagedBytesTotal);
|
|
} else {
|
|
dprintf(" TOTAL %8ld %8ld %8ld %8I64ld %8ld %8ld %8ld %8I64ld\n",
|
|
NonPagedAllocsTotal,
|
|
NonPagedFreesTotal,
|
|
NonPagedAllocsTotal - NonPagedFreesTotal,
|
|
NonPagedBytesTotal,
|
|
PagedAllocsTotal,
|
|
PagedFreesTotal,
|
|
PagedAllocsTotal - PagedFreesTotal,
|
|
PagedBytesTotal);
|
|
}
|
|
|
|
free (PoolTrackTableData);
|
|
return S_OK;
|
|
}
|
|
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
|
|
BOOLEAN WINAPI
|
|
CheckSingleFilterAndPrint (
|
|
PCHAR Tag,
|
|
PCHAR Filter,
|
|
ULONG Flags,
|
|
ULONG64 PoolHeader,
|
|
ULONG64 BlockSize,
|
|
ULONG64 Data,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback to check a piece of pool and print out information about it
|
|
if it matches the specified tag.
|
|
|
|
Arguments:
|
|
|
|
Tag - Supplies the tag to search for.
|
|
|
|
Filter - Supplies the filter string to match against.
|
|
|
|
Flags - Supplies 0 if a nonpaged pool search is desired.
|
|
Supplies 1 if a paged pool search is desired.
|
|
Supplies 2 if a special pool search is desired.
|
|
Supplies 4 if a pool is a large pool
|
|
|
|
PoolHeader - Supplies the pool header.
|
|
|
|
BlockSize - Supplies the size of the pool block in bytes.
|
|
|
|
Data - Supplies the address of the pool block.
|
|
|
|
Context - Unused.
|
|
|
|
Return Value:
|
|
|
|
TRUE for a match, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
ULONG UTag = *((PULONG)Tag);
|
|
ULONG HdrUlong1=0, HdrPoolSize ;
|
|
LOGICAL QuotaProcessAtEndOfPoolBlock = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
if (CheckSingleFilter (Tag, Filter) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
HdrPoolSize = GetTypeSize("nt!_POOL_HEADER");
|
|
if ((BlockSize >= (PageSize-2*HdrPoolSize)) || (Flags & 0x8)) {
|
|
dprintf("*%p :%slarge page allocation, Tag %3s %c%c%c%c, size %3s 0x%I64x bytes\n",
|
|
(Data & ~POOL_BIG_TABLE_ENTRY_FREE),
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "free " : "",
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24),
|
|
(Data & POOL_BIG_TABLE_ENTRY_FREE) ? "was" : "is",
|
|
BlockSize
|
|
);
|
|
} else if (Flags & 0x2) {
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
dprintf("*%p size: %4lx %s special pool, Tag is %c%c%c%c\n",
|
|
Data,
|
|
BlockSize,
|
|
HdrUlong1 & MI_SPECIAL_POOL_PAGABLE ? "pagable" : "non-paged",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24)
|
|
);
|
|
} else {
|
|
ULONG BlockSizeR, PreviousSize, PoolType, PoolIndex, AllocatorBackTraceIndex;
|
|
ULONG PoolTagHash, PoolTag;
|
|
ULONG64 ProcessBilled;
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", BlockSizeR);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolType", PoolType);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTagHash", PoolTagHash);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolIndex", PoolIndex);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PreviousSize", PreviousSize);
|
|
if (TargetMachine == IMAGE_FILE_MACHINE_I386) {
|
|
if (GetExpression ("nt!ExGetPoolTagInfo") != 0) {
|
|
|
|
//
|
|
// This is a kernel where the quota process pointer is at
|
|
// the end of the pool block instead of overlaid on the
|
|
// tag field.
|
|
//
|
|
|
|
QuotaProcessAtEndOfPoolBlock = TRUE;
|
|
if (QuotaProcessAtEndOfPoolBlock == TRUE) {
|
|
|
|
ULONG SizeOfPvoid = 0;
|
|
ULONG64 ProcessBillAddress;
|
|
|
|
SizeOfPvoid = DBG_PTR_SIZE;
|
|
|
|
if (SizeOfPvoid == 0) {
|
|
dprintf ("Search: cannot get size of PVOID\n");
|
|
return FALSE;
|
|
}
|
|
ProcessBillAddress = PoolHeader + ((ULONG64) BlockSizeR << POOL_BLOCK_SHIFT) - SizeOfPvoid;
|
|
ProcessBilled = READ_PVOID (ProcessBillAddress);
|
|
}
|
|
}
|
|
else {
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
}
|
|
}
|
|
else {
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "ProcessBilled", ProcessBilled);
|
|
}
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "AllocatorBackTraceIndex", AllocatorBackTraceIndex);
|
|
|
|
dprintf("%p size: %4lx previous size: %4lx ",
|
|
Data - HdrPoolSize,
|
|
BlockSizeR << POOL_BLOCK_SHIFT,
|
|
PreviousSize << POOL_BLOCK_SHIFT);
|
|
|
|
if (PoolType == 0) {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free)");
|
|
dprintf(" %c%c%c%c\n",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP(UTag >> 24)
|
|
);
|
|
} else {
|
|
|
|
if (!NewPool ? (PoolIndex & 0x80) : (PoolType & 0x04)) {
|
|
dprintf(" (Allocated)");
|
|
} else {
|
|
//
|
|
// "Free " with a space after it before the parentheses means
|
|
// it's been freed to a (pool manager internal) lookaside list.
|
|
// We used to print "Lookaside" but that just confused driver
|
|
// writers because they didn't know if this meant in use or not
|
|
// and many would say "but I don't use lookaside lists - the
|
|
// extension or kernel is broken".
|
|
//
|
|
// "Free" with no space after it before the parentheses means
|
|
// it is not on a pool manager internal lookaside list and is
|
|
// instead on the regular pool manager internal flink/blink
|
|
// chains.
|
|
//
|
|
// Note to anyone using the pool package, these 2 terms are
|
|
// equivalent. The fine distinction is only for those actually
|
|
// writing pool internal code.
|
|
//
|
|
dprintf(" (Free )");
|
|
}
|
|
if ((PoolType & POOL_QUOTA_MASK) == 0) {
|
|
|
|
UTag = PoolTag;
|
|
|
|
dprintf(" %c%c%c%c%s\n",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP((UTag &~PROTECTED_POOL) >> 24),
|
|
(UTag & PROTECTED_POOL) ? " (Protected)" : ""
|
|
);
|
|
|
|
} else {
|
|
|
|
if ((QuotaProcessAtEndOfPoolBlock == TRUE) ||
|
|
(TargetMachine != IMAGE_FILE_MACHINE_I386)) {
|
|
|
|
UTag = PoolTag;
|
|
|
|
dprintf(" %c%c%c%c%s",
|
|
PP(UTag),
|
|
PP(UTag >> 8),
|
|
PP(UTag >> 16),
|
|
PP((UTag &~PROTECTED_POOL) >> 24),
|
|
(UTag & PROTECTED_POOL) ? " (Protected)" : ""
|
|
);
|
|
}
|
|
|
|
if (ProcessBilled != 0) {
|
|
dprintf(" Process: %08p\n", ProcessBilled );
|
|
}
|
|
else {
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
} // CheckSingleFilterAndPrint
|
|
|
|
#undef PP
|
|
|
|
ULONG64
|
|
GetNextResidentAddress (
|
|
ULONG64 VirtualAddress,
|
|
ULONG64 MaximumVirtualAddress
|
|
)
|
|
{
|
|
ULONG64 PointerPde;
|
|
ULONG64 PointerPte;
|
|
ULONG SizeOfPte;
|
|
ULONG Valid;
|
|
|
|
//
|
|
// Note this code will need to handle one more level of indirection for
|
|
// WIN64.
|
|
//
|
|
|
|
if (!(SizeOfPte=GetTypeSize("nt!_MMPTE"))) {
|
|
dprintf("Cannot get MMPTE type.\n");
|
|
return 0;
|
|
}
|
|
|
|
top:
|
|
|
|
PointerPde = DbgGetPdeAddress (VirtualAddress);
|
|
|
|
while (GetFieldValue(PointerPde,
|
|
"nt!_MMPTE",
|
|
"u.Hard.Valid",
|
|
Valid) ||
|
|
(Valid == 0)) {
|
|
|
|
//
|
|
// Note that on 32-bit systems, the PDE should always be readable.
|
|
// If the PDE is not valid then increment to the next PDE's VA.
|
|
//
|
|
|
|
PointerPde = (PointerPde + SizeOfPte);
|
|
|
|
VirtualAddress = DbgGetVirtualAddressMappedByPte (PointerPde);
|
|
VirtualAddress = DbgGetVirtualAddressMappedByPte (VirtualAddress);
|
|
|
|
if (VirtualAddress >= MaximumVirtualAddress) {
|
|
return VirtualAddress;
|
|
}
|
|
|
|
if (CheckControlC()) {
|
|
return VirtualAddress;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
PointerPte = DbgGetPteAddress (VirtualAddress);
|
|
|
|
while (GetFieldValue(PointerPde,
|
|
"nt!_MMPTE",
|
|
"u.Hard.Valid",
|
|
Valid) ||
|
|
(Valid == 0)) {
|
|
|
|
//
|
|
// If the PTE cannot be read then increment by PAGE_SIZE.
|
|
//
|
|
|
|
VirtualAddress = (VirtualAddress + PageSize);
|
|
|
|
if (CheckControlC()) {
|
|
return VirtualAddress;
|
|
}
|
|
|
|
PointerPte = (PointerPte + SizeOfPte);
|
|
if ((PointerPte & (PageSize - 1)) == 0) {
|
|
goto top;
|
|
}
|
|
|
|
if (VirtualAddress >= MaximumVirtualAddress) {
|
|
return VirtualAddress;
|
|
}
|
|
}
|
|
|
|
return VirtualAddress;
|
|
}
|
|
|
|
VOID
|
|
SearchPool(
|
|
ULONG TagName,
|
|
ULONG Flags,
|
|
ULONG64 RestartAddr,
|
|
POOLFILTER Filter,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Engine to search the pool.
|
|
|
|
Arguments:
|
|
|
|
TagName - Supplies the tag to search for.
|
|
|
|
Flags - Supplies 0 if a nonpaged pool search is desired.
|
|
Supplies 1 if a paged pool search is desired.
|
|
Supplies 2 if a special pool search is desired.
|
|
Supplies 4 if a session pool search is desired.
|
|
|
|
RestartAddr - Supplies the address to restart the search from.
|
|
|
|
Filter - Supplies the filter routine to use.
|
|
|
|
Context - Supplies the user defined context blob.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG64 Location;
|
|
LOGICAL PhysicallyContiguous;
|
|
ULONG64 PoolBlockSize;
|
|
ULONG64 PoolHeader;
|
|
ULONG PoolTag;
|
|
ULONG Result;
|
|
ULONG64 PoolPage;
|
|
ULONG64 StartPage;
|
|
ULONG64 Pool;
|
|
ULONG Previous;
|
|
ULONG64 PoolStart;
|
|
ULONG64 PoolPteAddress;
|
|
ULONG64 PoolEnd;
|
|
ULONG64 ExpandedPoolStart;
|
|
ULONG64 ExpandedPoolEnd;
|
|
ULONG InitialPoolSize;
|
|
ULONG SkipSize;
|
|
BOOLEAN TwoPools;
|
|
ULONG64 DataPageReal;
|
|
ULONG64 DataStartReal;
|
|
LOGICAL Found;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG ct;
|
|
ULONG PoolBigPageTableSize;
|
|
ULONG64 PoolTableAddress;
|
|
UCHAR FastTag[4];
|
|
ULONG TagLength;
|
|
ULONG SizeOfBigPages;
|
|
ULONG PoolTypeFlags = Flags & 0x7;
|
|
ULONG Ulong1;
|
|
ULONG HdrSize;
|
|
|
|
if (PoolInitializeGlobals() == FALSE) {
|
|
return;
|
|
}
|
|
|
|
if (PoolTypeFlags == 2) {
|
|
|
|
if (RestartAddr && (RestartAddr >= SpecialPoolStart) && (RestartAddr <= SpecialPoolEnd)) {
|
|
Pool = RestartAddr;
|
|
} else {
|
|
Pool = SpecialPoolStart;
|
|
}
|
|
|
|
dprintf("\nSearching special pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
|
|
Pool,
|
|
SpecialPoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
|
|
Found = FALSE;
|
|
SkipSize = PageSize;
|
|
|
|
if (SpecialPoolStart && SpecialPoolEnd) {
|
|
|
|
//
|
|
// Search special pool for the tag.
|
|
//
|
|
|
|
while (Pool < SpecialPoolEnd) {
|
|
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
Pool);
|
|
return;
|
|
}
|
|
|
|
DataStartReal = Pool;
|
|
DataPageReal = Pool;
|
|
if ( !ReadMemory( Pool,
|
|
&DataPage[0],
|
|
min(PageSize, sizeof(DataPage)),
|
|
&Result) ) {
|
|
ULONG64 PteLong=0, PageFileHigh;
|
|
|
|
if (SkipSize != 2 * PageSize) {
|
|
|
|
// dprintf("SP skip %x", Pool);
|
|
PoolPteAddress = DbgGetPteAddress (Pool);
|
|
|
|
if (!GetFieldValue(PoolPteAddress,
|
|
"nt!_MMPTE",
|
|
"u.Soft.PageFileHigh",
|
|
PageFileHigh) ) {
|
|
|
|
if ((PageFileHigh == 0) ||
|
|
(PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE) ||
|
|
(PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE)) {
|
|
|
|
//
|
|
// Found a NO ACCESS PTE - skip these from
|
|
// here on to speed up the search.
|
|
//
|
|
|
|
// dprintf("SP skip double %p", PoolPteAddress);
|
|
SkipSize = 2 * PageSize;
|
|
Pool += PageSize;
|
|
// dprintf("SP skip final %p", Pool);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
Pool += SkipSize;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Determine whether this is a valid special pool block.
|
|
//
|
|
|
|
PoolHeader = GetSpecialPoolHeader (DataPage,
|
|
DataPageReal,
|
|
&DataStartReal);
|
|
|
|
if (PoolHeader != 0) {
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "PoolTag", PoolTag);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", Ulong1);
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(Ulong1);
|
|
|
|
Found = Filter( (PCHAR)&PoolTag,
|
|
(PCHAR)&TagName,
|
|
Flags,
|
|
PoolHeader,
|
|
PoolBlockSize,
|
|
DataStartReal,
|
|
Context );
|
|
} else {
|
|
dprintf( "No pool header for page: 0x%p\n", Pool );
|
|
}
|
|
Pool += SkipSize;
|
|
}
|
|
}
|
|
|
|
if (Found == FALSE) {
|
|
dprintf("The %c%c%c%c tag could not be found in special pool.\n",
|
|
#define PP(x) isprint(((x)&0xff))?((x)&0xff):('.')
|
|
PP(TagName),
|
|
PP(TagName >> 8),
|
|
PP(TagName >> 16),
|
|
PP(TagName >> 24)
|
|
);
|
|
#undef PP
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (PoolTypeFlags == 0) {
|
|
PhysicallyContiguous = TRUE;
|
|
} else {
|
|
PhysicallyContiguous = FALSE;
|
|
}
|
|
|
|
__try {
|
|
TwoPools = FALSE;
|
|
|
|
if (((PoolTypeFlags & 0x4)== 0) || (BuildNo <= 2600)) {
|
|
PoolBigTableAddress = GetPointerValue ("nt!PoolBigPageTable");
|
|
PoolBigPageTableSize = GetUlongValue ("nt!PoolBigPageTableSize");
|
|
}
|
|
else {
|
|
PoolBigTableAddress = GetPointerValue ("ExpSessionPoolBigPageTable");
|
|
PoolBigPageTableSize = GetUlongValue ("ExpSessionPoolBigPageTableSize");
|
|
}
|
|
|
|
PoolTableAddress = PoolBigTableAddress;
|
|
|
|
if (PoolTableAddress) {
|
|
|
|
ULONG VaOffset;
|
|
ULONG NumPagesOffset;
|
|
ULONG PtrSize;
|
|
ULONG KeyOffset;
|
|
|
|
//
|
|
// Scan the table looking for a match. We read close to a page at a time
|
|
// physical page / sizeof ( pool_tracker_big_page ) * sizeof ( pool_tracker_big_page )
|
|
// on x86 this works out to ffc
|
|
//
|
|
|
|
i = 0;
|
|
SizeOfBigPages = GetTypeSize ("nt!_POOL_TRACKER_BIG_PAGES");
|
|
if (!SizeOfBigPages) {
|
|
dprintf("Cannot get _POOL_TRACKER_BIG_PAGES type size\n");
|
|
__leave;
|
|
}
|
|
ct = PageSize / SizeOfBigPages;
|
|
|
|
dprintf( "\nScanning large pool allocation table for Tag: %c%c%c%c (%p : %p)\n\n\r",
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24,
|
|
PoolBigTableAddress,
|
|
PoolBigTableAddress + PoolBigPageTableSize * SizeOfBigPages );
|
|
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Va", &VaOffset );
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "NumberOfPages", &NumPagesOffset );
|
|
GetFieldOffset( "nt!_POOL_TRACKER_BIG_PAGES", "Key", &KeyOffset );
|
|
PtrSize = IsPtr64() ? 8 : 4;
|
|
|
|
while (i < PoolBigPageTableSize) {
|
|
|
|
if (PoolBigPageTableSize - i < ct) {
|
|
ct = PoolBigPageTableSize - i;
|
|
}
|
|
|
|
if ( !ReadMemory( PoolTableAddress,
|
|
&DataPage[0],
|
|
min(ct * SizeOfBigPages, sizeof(DataPage)),
|
|
&Result) ) {
|
|
|
|
dprintf( "%08lx: Unable to get contents of big pool block\r\n", PoolTableAddress );
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < ct; j += 1) {
|
|
ULONG64 Va = 0;
|
|
|
|
memcpy( &Va, (PCHAR)DataPage + (SizeOfBigPages * j) + VaOffset, PtrSize );
|
|
|
|
Filter( ((PCHAR)DataPage + (SizeOfBigPages * j) + KeyOffset),
|
|
(PCHAR)&TagName,
|
|
Flags | 0x8, // To assist filter routine to recognize this as large pool
|
|
PoolTableAddress + SizeOfBigPages * j,
|
|
(ULONG64)(*((PULONG)((PCHAR)DataPage + (SizeOfBigPages * j) + NumPagesOffset))) * PageSize,
|
|
Va,
|
|
Context );
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolTableAddress + j * SizeOfBigPages);
|
|
__leave;
|
|
}
|
|
}
|
|
i += ct;
|
|
PoolTableAddress += (ct * SizeOfBigPages);
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolTableAddress);
|
|
__leave;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
dprintf("unable to get large pool allocation table - either wrong symbols or pool tagging is disabled\n");
|
|
}
|
|
|
|
if (PoolTypeFlags == 0) {
|
|
PoolStart = GetNtDebuggerDataPtrValue( MmNonPagedPoolStart );
|
|
|
|
if (0 == PoolStart) {
|
|
dprintf( "Unable to get MmNonPagedPoolStart\n" );
|
|
}
|
|
|
|
PoolEnd =
|
|
PoolStart + GetNtDebuggerDataPtrValue( MmMaximumNonPagedPoolInBytes );
|
|
|
|
ExpandedPoolEnd = GetNtDebuggerDataPtrValue( MmNonPagedPoolEnd );
|
|
|
|
if (PoolEnd != ExpandedPoolEnd) {
|
|
InitialPoolSize = (ULONG)GetUlongValue( "MmSizeOfNonPagedPoolInBytes" );
|
|
PoolEnd = PoolStart + InitialPoolSize;
|
|
|
|
ExpandedPoolStart = GetPointerValue( "MmNonPagedPoolExpansionStart" );
|
|
TwoPools = TRUE;
|
|
}
|
|
for (TagLength = 0;TagLength < 3; TagLength++) {
|
|
if ((*(((PCHAR)&TagName)+TagLength) == '?') ||
|
|
(*(((PCHAR)&TagName)+TagLength) == '*')) {
|
|
break;
|
|
}
|
|
FastTag[TagLength] = *(((PCHAR)&TagName)+TagLength);
|
|
}
|
|
|
|
} else if (PoolTypeFlags == 1) {
|
|
PoolStart = GetNtDebuggerDataPtrValue( MmPagedPoolStart );
|
|
PoolEnd =
|
|
PoolStart + GetNtDebuggerDataPtrValue( MmSizeOfPagedPoolInBytes );
|
|
} else {
|
|
Location = GetExpression ("MiSessionPoolStart");
|
|
if (!Location) {
|
|
dprintf("Unable to get MiSessionPoolStart\n");
|
|
__leave;
|
|
}
|
|
|
|
ReadPointer(Location, &PoolStart);
|
|
|
|
Location = GetExpression ("MiSessionPoolEnd");
|
|
if (!Location) {
|
|
dprintf("Unable to get MiSessionPoolEnd\n");
|
|
__leave;
|
|
}
|
|
|
|
ReadPointer(Location, &PoolEnd);
|
|
}
|
|
|
|
if (RestartAddr) {
|
|
PoolStart = RestartAddr;
|
|
if (TwoPools == TRUE) {
|
|
if (PoolStart > PoolEnd) {
|
|
TwoPools = FALSE;
|
|
PoolStart = RestartAddr;
|
|
PoolEnd = ExpandedPoolEnd;
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\r\n\n",
|
|
(PoolTypeFlags == 0) ? "NonPaged" : PoolTypeFlags == 1 ? "Paged": "SessionPaged",
|
|
PoolStart,
|
|
PoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
|
|
PoolPage = PoolStart;
|
|
HdrSize = GetTypeSize("nt!_POOL_HEADER");
|
|
|
|
while (PoolPage < PoolEnd) {
|
|
|
|
//
|
|
// Optimize things by ioctl'ing over to the other side to
|
|
// do a fast search and start with that page.
|
|
//
|
|
|
|
if ((PoolTypeFlags == 0) &&
|
|
PhysicallyContiguous &&
|
|
(TagLength > 0)) {
|
|
|
|
SEARCHMEMORY Search;
|
|
|
|
Search.SearchAddress = PoolPage;
|
|
Search.SearchLength = PoolEnd-PoolPage;
|
|
Search.PatternLength = TagLength;
|
|
Search.Pattern = FastTag;
|
|
Search.FoundAddress = 0;
|
|
if ((Ioctl(IG_SEARCH_MEMORY, &Search, sizeof(Search))) &&
|
|
(Search.FoundAddress != 0)) {
|
|
//
|
|
// Got a hit, search the whole page
|
|
//
|
|
PoolPage = PAGE_ALIGN64(Search.FoundAddress);
|
|
} else {
|
|
//
|
|
// The tag was not found at all, so we can just skip
|
|
// this chunk entirely.
|
|
//
|
|
PoolPage = PoolEnd;
|
|
goto skiprange;
|
|
}
|
|
}
|
|
|
|
Pool = PAGE_ALIGN64 (PoolPage);
|
|
StartPage = Pool;
|
|
Previous = 0;
|
|
|
|
while (PAGE_ALIGN64(Pool) == StartPage) {
|
|
|
|
ULONG HdrPoolTag, BlockSize, PreviousSize, AllocatorBackTraceIndex, PoolTagHash;
|
|
ULONG PoolType;
|
|
|
|
if ( GetFieldValue(Pool,
|
|
"nt!_POOL_HEADER",
|
|
"PoolTag",
|
|
HdrPoolTag) ) {
|
|
|
|
PoolPage = GetNextResidentAddress (Pool, PoolEnd);
|
|
|
|
//
|
|
// If we're half resident - half non-res then we'll get back
|
|
// that are starting address is the next resident page. In that
|
|
// case just go on to the next page
|
|
//
|
|
|
|
if (PoolPage == Pool) {
|
|
PoolPage = PoolPage + PageSize;
|
|
}
|
|
|
|
goto nextpage;
|
|
}
|
|
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTag",HdrPoolTag);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolType", PoolType);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","BlockSize",BlockSize);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PoolTagHash",PoolTagHash);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","PreviousSize",PreviousSize);
|
|
GetFieldValue(Pool,"nt!_POOL_HEADER","AllocatorBackTraceIndex",AllocatorBackTraceIndex);
|
|
|
|
if ((BlockSize << POOL_BLOCK_SHIFT) > POOL_PAGE_SIZE) {
|
|
//dprintf("Bad allocation size @%lx, too large\n", Pool);
|
|
break;
|
|
}
|
|
|
|
if (BlockSize == 0) {
|
|
//dprintf("Bad allocation size @%lx, zero is invalid\n", Pool);
|
|
break;
|
|
}
|
|
|
|
if (PreviousSize != Previous) {
|
|
//dprintf("Bad previous allocation size @%lx, last size was %lx\n",Pool, Previous);
|
|
break;
|
|
}
|
|
|
|
PoolTag = HdrPoolTag;
|
|
|
|
Filter((PCHAR)&PoolTag,
|
|
(PCHAR)&TagName,
|
|
Flags,
|
|
Pool,
|
|
(ULONG64)BlockSize << POOL_BLOCK_SHIFT,
|
|
Pool + HdrSize,
|
|
Context );
|
|
|
|
Previous = BlockSize;
|
|
Pool += ((ULONG64) Previous << POOL_BLOCK_SHIFT);
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolPage);
|
|
__leave;
|
|
}
|
|
}
|
|
PoolPage = (PoolPage + PageSize);
|
|
|
|
nextpage:
|
|
if ( CheckControlC() ) {
|
|
dprintf("\n...terminating - searched pool to %p\n",
|
|
PoolPage);
|
|
__leave;
|
|
}
|
|
|
|
skiprange:
|
|
if (TwoPools == TRUE) {
|
|
if (PoolPage == PoolEnd) {
|
|
TwoPools = FALSE;
|
|
PoolStart = ExpandedPoolStart;
|
|
PoolEnd = ExpandedPoolEnd;
|
|
PoolPage = PoolStart;
|
|
PhysicallyContiguous = FALSE;
|
|
|
|
dprintf("\nSearching %s pool (%p : %p) for Tag: %c%c%c%c\n\n",
|
|
"NonPaged",
|
|
PoolStart,
|
|
PoolEnd,
|
|
TagName,
|
|
TagName >> 8,
|
|
TagName >> 16,
|
|
TagName >> 24);
|
|
}
|
|
}
|
|
}
|
|
} __finally {
|
|
}
|
|
|
|
return;
|
|
} // SearchPool
|
|
|
|
|
|
|
|
DECLARE_API( poolfind )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
flags == 0 means finds a tag in nonpaged pool.
|
|
flags == 1 means finds a tag in paged pool.
|
|
flags == 2 means finds a tag in special pool.
|
|
flags == 4 means finds a tag in session pool.
|
|
|
|
Arguments:
|
|
|
|
args -
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags;
|
|
CHAR TagNameX[4] = {' ',' ',' ',' '};
|
|
ULONG TagName;
|
|
ULONG64 PoolTrackTable;
|
|
|
|
Flags = 0;
|
|
if (!sscanf(args,"%c%c%c%c %lx", &TagNameX[0],
|
|
&TagNameX[1], &TagNameX[2], &TagNameX[3], &Flags)) {
|
|
Flags = 0;
|
|
}
|
|
|
|
if (TagNameX[0] == '0' && TagNameX[1] == 'x') {
|
|
if (!sscanf( args, "%lx %lx", &TagName, &Flags )) {
|
|
TagName = 0;
|
|
}
|
|
} else {
|
|
TagName = TagNameX[0] | (TagNameX[1] << 8) | (TagNameX[2] << 16) | (TagNameX[3] << 24);
|
|
}
|
|
|
|
PoolTrackTable = GetNtDebuggerDataPtrValue( PoolTrackTable );
|
|
if (PoolTrackTable == 0) {
|
|
dprintf ("unable to get PoolTrackTable - probably pool tagging disabled or wrong symbols\n");
|
|
}
|
|
|
|
|
|
SearchPool( TagName, Flags, 0, CheckSingleFilterAndPrint, NULL );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CheckSingleFilter (
|
|
PCHAR Tag,
|
|
PCHAR Filter
|
|
)
|
|
{
|
|
ULONG i;
|
|
UCHAR tc;
|
|
UCHAR fc;
|
|
|
|
for ( i = 0; i < 4; i++ ) {
|
|
tc = (UCHAR) *Tag++;
|
|
fc = (UCHAR) *Filter++;
|
|
if ( fc == '*' ) return TRUE;
|
|
if ( fc == '?' ) continue;
|
|
if (i == 3 && (tc & ~(PROTECTED_POOL >> 24)) == fc) continue;
|
|
if ( tc != fc ) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG64
|
|
GetSpecialPoolHeader (
|
|
IN PVOID pDataPage,
|
|
IN ULONG64 RealDataPage,
|
|
OUT PULONG64 ReturnedDataStart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examine a page of data to determine if it is a special pool block.
|
|
|
|
Arguments:
|
|
|
|
pDataPage - Supplies a pointer to a page of data to examine.
|
|
|
|
ReturnedDataStart - Supplies a pointer to return the start of the data.
|
|
Only valid if this routine returns non-NULL.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the pool header for this special pool block or
|
|
NULL if the block is not valid special pool.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG PoolBlockSize;
|
|
ULONG PoolHeaderSize;
|
|
ULONG PoolBlockPattern;
|
|
PUCHAR p;
|
|
PUCHAR PoolDataEnd;
|
|
PUCHAR DataStart;
|
|
ULONG64 PoolHeader;
|
|
ULONG HdrUlong1;
|
|
|
|
PoolHeader = RealDataPage;
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
//
|
|
// Determine whether the data is at the start or end of the page.
|
|
// Start off by assuming the data is at the end, in this case the
|
|
// header will be at the start.
|
|
//
|
|
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
|
|
|
|
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
|
|
|
|
PoolHeaderSize = POOL_OVERHEAD;
|
|
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
|
|
PoolHeaderSize += GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
|
|
}
|
|
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
|
|
|
|
DataStart = (PUCHAR)pDataPage + PageSize - PoolBlockSize;
|
|
p = (PUCHAR)pDataPage + PoolHeaderSize;
|
|
|
|
for ( ; p < DataStart; p += 1) {
|
|
if (*p != PoolBlockPattern) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p == DataStart || p >= (PUCHAR)pDataPage + PoolHeaderSize + 0x10) {
|
|
|
|
//
|
|
// For this page, the data is at the end of the block.
|
|
// The 0x10 is just to give corrupt blocks some slack.
|
|
// All pool allocations are quadword aligned.
|
|
//
|
|
|
|
DataStart = (PUCHAR)pDataPage + ((PageSize - PoolBlockSize) & ~(sizeof(QUAD)-1));
|
|
|
|
*ReturnedDataStart = RealDataPage + (ULONG64) ((PUCHAR) DataStart - (PUCHAR) pDataPage);
|
|
return PoolHeader;
|
|
}
|
|
|
|
//
|
|
// The data must be at the front or the block is corrupt.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Try for the data at the front. Checks are necessary as
|
|
// the page could be corrupt on both ends.
|
|
//
|
|
|
|
PoolHeader = (RealDataPage + PageSize - POOL_OVERHEAD);
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "Ulong1", HdrUlong1);
|
|
PoolBlockSize = SPECIAL_POOL_BLOCK_SIZE(HdrUlong1);
|
|
|
|
if ((PoolBlockSize != 0) && (PoolBlockSize < PageSize - POOL_OVERHEAD)) {
|
|
PoolDataEnd = (PUCHAR)PoolHeader;
|
|
|
|
if (HdrUlong1 & MI_SPECIAL_POOL_VERIFIER) {
|
|
PoolDataEnd -= GetTypeSize ("nt!_MI_VERIFIER_POOL_HEADER");
|
|
}
|
|
|
|
|
|
GetFieldValue(PoolHeader, "nt!_POOL_HEADER", "BlockSize", PoolBlockPattern);
|
|
DataStart = (PUCHAR)pDataPage;
|
|
|
|
p = DataStart + PoolBlockSize;
|
|
for ( ; p < PoolDataEnd; p += 1) {
|
|
if (*p != PoolBlockPattern) {
|
|
break;
|
|
}
|
|
}
|
|
if (p == (PUCHAR)PoolDataEnd || p > (PUCHAR)pDataPage + PoolBlockSize + 0x10) {
|
|
//
|
|
// For this page, the data is at the front of the block.
|
|
// The 0x10 is just to give corrupt blocks some slack.
|
|
// All pool allocations are quadword aligned.
|
|
//
|
|
|
|
*ReturnedDataStart = RealDataPage + (ULONG64)( (PUCHAR)DataStart - (PUCHAR) pDataPage);
|
|
return PoolHeader;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not valid special pool.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#define BYTE(u,n) ((u & (0xff << 8*n)) >> 8*n)
|
|
#define LOCHAR_BYTE(u,n) (tolower(BYTE(u,n)) & 0xff)
|
|
#define REVERSE_ULONGBYTES(u) (LOCHAR_BYTE(u,3) | (LOCHAR_BYTE(u,2) << 8) | (LOCHAR_BYTE(u,1) << 16) | (LOCHAR_BYTE(u,0) << 24))
|
|
|
|
|
|
EXTENSION_API ( GetPoolRegion )(
|
|
PDEBUG_CLIENT Client,
|
|
ULONG64 Pool,
|
|
DEBUG_POOL_REGION *PoolData
|
|
)
|
|
{
|
|
INIT_API();
|
|
|
|
*PoolData = GetPoolRegion(Pool);
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
EXTENSION_API ( GetPoolData )(
|
|
PDEBUG_CLIENT Client,
|
|
ULONG64 Pool,
|
|
PDEBUG_POOL_DATA PoolData
|
|
)
|
|
{
|
|
PCHAR Desc;
|
|
HRESULT Hr;
|
|
PGET_POOL_TAG_DESCRIPTION GetPoolTagDescription;
|
|
|
|
INIT_API();
|
|
|
|
if (!PoolInitializeGlobals()) {
|
|
EXIT_API();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Hr = ListPoolPage(Pool, 0x80000002, PoolData);
|
|
|
|
if (Hr != S_OK) {
|
|
EXIT_API();
|
|
return Hr;
|
|
}
|
|
|
|
GetPoolTagDescription = NULL;
|
|
#ifndef _EXTFNS_H
|
|
if (!GetExtensionFunction("GetPoolTagDescription", (FARPROC*) &GetPoolTagDescription)) {
|
|
EXIT_API();
|
|
return E_INVALIDARG;
|
|
}
|
|
(*GetPoolTagDescription)(PoolData->PoolTag, &Desc);
|
|
if (Desc) {
|
|
ULONG strsize = strlen(Desc);
|
|
if (strsize > sizeof(PoolData->PoolTagDescription)) {
|
|
strsize = sizeof(PoolData->PoolTagDescription);
|
|
}
|
|
strncpy(PoolData->PoolTagDescription, Desc, strsize);
|
|
PoolData->PoolTagDescription[strsize] = 0;
|
|
}
|
|
#endif
|
|
EXIT_API();
|
|
return Hr;
|
|
}
|