712 lines
16 KiB
C
712 lines
16 KiB
C
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
verifier.c
|
|
|
|
Abstract:
|
|
|
|
Application verifier debugger extension for both ntsd and kd.
|
|
|
|
Author:
|
|
|
|
Silviu Calinoiu (SilviuC) 4-Mar-2001
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
ULONG
|
|
VrfGetArguments (
|
|
PCHAR ArgsString,
|
|
PCHAR Args[],
|
|
ULONG NoOfArgs
|
|
);
|
|
|
|
VOID
|
|
VrfHelp (
|
|
);
|
|
|
|
BOOLEAN
|
|
VrfTraceInitialize (
|
|
);
|
|
|
|
ULONG64
|
|
VrfTraceAddress (
|
|
ULONG TraceIndex
|
|
);
|
|
|
|
VOID
|
|
VrfTraceDump (
|
|
ULONG TraceIndex
|
|
);
|
|
|
|
VOID
|
|
VrfDumpSettings (
|
|
);
|
|
|
|
VOID
|
|
VrfDumpVspaceLog (
|
|
ULONG NoOfEntries,
|
|
ULONG64 Address
|
|
);
|
|
|
|
VOID
|
|
VrfDumpHeapLog (
|
|
ULONG NoOfEntries,
|
|
ULONG64 Address
|
|
);
|
|
|
|
DECLARE_API( avrf )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Application verifier debugger extension.
|
|
|
|
Arguments:
|
|
|
|
args -
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PCHAR Args[16];
|
|
ULONG NoOfArgs, I;
|
|
|
|
INIT_API();
|
|
|
|
//
|
|
// Parse arguments.
|
|
//
|
|
|
|
NoOfArgs = VrfGetArguments ((PCHAR)args,
|
|
Args,
|
|
16);
|
|
#if 0
|
|
for (I = 0; I < NoOfArgs; I += 1) {
|
|
dprintf ("%02u: %s\n", I, Args[I]);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check if help needed
|
|
//
|
|
|
|
if (NoOfArgs > 0 && strstr (Args[0], "?") != NULL) {
|
|
VrfHelp ();
|
|
goto Exit;
|
|
}
|
|
|
|
if (VrfTraceInitialize() == FALSE) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (NoOfArgs > 1 && _stricmp (Args[0], "-trace") == 0) {
|
|
VrfTraceDump (atoi(Args[1]));
|
|
goto Exit;
|
|
}
|
|
|
|
if (NoOfArgs > 1 && _stricmp (Args[0], "-vs") == 0) {
|
|
|
|
if (NoOfArgs > 2 && _stricmp (Args[1], "-a") == 0) {
|
|
|
|
ULONG64 Address;
|
|
BOOL Result;
|
|
PCSTR Remainder;
|
|
|
|
Result = GetExpressionEx (Args[2], &Address, &Remainder);
|
|
|
|
if (Result == FALSE) {
|
|
dprintf ("\nFailed to convert `%s' to an address.\n", Args[2]);
|
|
goto Exit;
|
|
}
|
|
|
|
// sscanf (Args[2], "%I64X", &Address);
|
|
dprintf ("Searching in vspace log for address %I64X ...\n\n", Address);
|
|
VrfDumpVspaceLog (0, Address);
|
|
goto Exit;
|
|
}
|
|
else {
|
|
|
|
VrfDumpVspaceLog (atoi(Args[1]), 0);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (NoOfArgs > 1 && _stricmp (Args[0], "-hp") == 0) {
|
|
|
|
if (NoOfArgs > 2 && _stricmp (Args[1], "-a") == 0) {
|
|
|
|
ULONG64 Address;
|
|
BOOL Result;
|
|
PCSTR Remainder;
|
|
|
|
Result = GetExpressionEx (Args[2], &Address, &Remainder);
|
|
|
|
if (Result == FALSE) {
|
|
dprintf ("\nFailed to convert `%s' to an address.\n", Args[2]);
|
|
goto Exit;
|
|
}
|
|
|
|
// sscanf (Args[2], "%I64X", &Address);
|
|
dprintf ("Searching in vspace log for address %I64X ...\n\n", Address);
|
|
VrfDumpHeapLog (0, Address);
|
|
goto Exit;
|
|
}
|
|
else {
|
|
|
|
VrfDumpHeapLog (atoi(Args[1]), 0);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no option specified then we just print current settings.
|
|
//
|
|
|
|
VrfDumpSettings ();
|
|
|
|
Exit:
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
VOID
|
|
VrfHelp (
|
|
)
|
|
{
|
|
dprintf ("Application verifier debugger extension \n"
|
|
" \n"
|
|
"!avrf displays current settings and stop \n"
|
|
" data if a verifier stop happened. \n"
|
|
"!avrf -vs N dumps last N entries from vspace log. \n"
|
|
"!avrf -vs -a ADDR searches ADDR in the vspace log. \n"
|
|
"!avrf -hp N dumps last N entries from heap log. \n"
|
|
"!avrf -hp -a ADDR searches ADDR in the heap log. \n"
|
|
" \n");
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////// Argument parsing routines
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
PCHAR
|
|
VrfGetArgument (
|
|
PCHAR Args,
|
|
PCHAR * Next
|
|
)
|
|
{
|
|
PCHAR Start;
|
|
|
|
if (Args == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
while (*Args == ' ' || *Args == '\t') {
|
|
Args += 1;
|
|
}
|
|
|
|
if (*Args == '\0') {
|
|
return NULL;
|
|
}
|
|
|
|
Start = Args;
|
|
|
|
while (*Args != ' ' && *Args != '\t' && *Args != '\0') {
|
|
Args += 1;
|
|
}
|
|
|
|
if (*Args == '\0') {
|
|
*Next = NULL;
|
|
}
|
|
else {
|
|
*Args = '\0';
|
|
*Next = Args + 1;
|
|
}
|
|
|
|
return Start;
|
|
}
|
|
|
|
ULONG
|
|
VrfGetArguments (
|
|
PCHAR ArgsString,
|
|
PCHAR Args[],
|
|
ULONG NoOfArgs
|
|
)
|
|
{
|
|
PCHAR Arg = ArgsString;
|
|
PCHAR Next;
|
|
ULONG Index;
|
|
|
|
for (Index = 0; Index < NoOfArgs; Index += 1) {
|
|
|
|
Arg = VrfGetArgument (Arg, &Next);
|
|
|
|
if (Arg) {
|
|
Args[Index] = Arg;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
Arg = Next;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////// Dump stack traces
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG64 TraceDbArrayEnd;
|
|
ULONG PvoidSize;
|
|
|
|
BOOLEAN
|
|
VrfTraceInitialize (
|
|
)
|
|
{
|
|
ULONG64 TraceDatabaseAddress;
|
|
ULONG64 TraceDatabase;
|
|
|
|
//
|
|
// Stack trace database address
|
|
//
|
|
|
|
TraceDatabaseAddress = GetExpression("ntdll!RtlpStackTraceDataBase");
|
|
|
|
if ( TraceDatabaseAddress == 0 ) {
|
|
dprintf( "Unable to resolve ntdll!RtlpStackTraceDataBase symbolic name.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (ReadPtr (TraceDatabaseAddress, &TraceDatabase ) != S_OK) {
|
|
dprintf( "Cannot read pointer at ntdll!RtlpStackTraceDataBase\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if (TraceDatabase == 0) {
|
|
dprintf( "Stack traces not enabled (ntdll!RtlpStackTraceDataBase is null).\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find the array of stack traces
|
|
//
|
|
|
|
if (InitTypeRead(TraceDatabase, ntdll!_STACK_TRACE_DATABASE)) {
|
|
dprintf("Unable to read type ntdll!_STACK_TRACE_DATABASE @ %p\n", TraceDatabase);
|
|
return FALSE;
|
|
}
|
|
|
|
TraceDbArrayEnd = ReadField (EntryIndexArray);
|
|
|
|
PvoidSize = GetTypeSize ("ntdll!PVOID");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
ULONG64
|
|
VrfTraceAddress (
|
|
ULONG TraceIndex
|
|
)
|
|
{
|
|
ULONG64 TracePointerAddress;
|
|
ULONG64 TracePointer;
|
|
|
|
TracePointerAddress = TraceDbArrayEnd - TraceIndex * PvoidSize;
|
|
|
|
if (ReadPtr (TracePointerAddress, &TracePointer) != S_OK) {
|
|
dprintf ("Cannot read stack trace address @ %p\n", TracePointerAddress);
|
|
return 0;
|
|
}
|
|
|
|
return TracePointer;
|
|
}
|
|
|
|
VOID
|
|
VrfTraceDump (
|
|
ULONG TraceIndex
|
|
)
|
|
{
|
|
ULONG64 TraceAddress;
|
|
ULONG64 TraceArray;
|
|
ULONG TraceDepth;
|
|
ULONG Offset;
|
|
ULONG Index;
|
|
ULONG64 ReturnAddress;
|
|
CHAR Symbol[ 1024 ];
|
|
ULONG64 Displacement;
|
|
|
|
//
|
|
// Get real address of the trace.
|
|
//
|
|
|
|
TraceAddress = VrfTraceAddress (TraceIndex);
|
|
|
|
if (TraceAddress == 0) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Read the stack trace depth
|
|
//
|
|
|
|
if (InitTypeRead(TraceAddress, ntdll!_RTL_STACK_TRACE_ENTRY)) {
|
|
dprintf("Unable to read type ntdll!_RTL_STACK_TRACE_ENTRY @ %p\n", TraceAddress);
|
|
return;
|
|
}
|
|
|
|
TraceDepth = (ULONG)ReadField (Depth);
|
|
|
|
//
|
|
// Limit the depth to 20 to protect ourselves from corrupted data
|
|
//
|
|
|
|
TraceDepth = __min (TraceDepth, 16);
|
|
|
|
//
|
|
// Get a pointer to the BackTrace array
|
|
//
|
|
|
|
GetFieldOffset ("ntdll!_RTL_STACK_TRACE_ENTRY", "BackTrace", &Offset);
|
|
TraceArray = TraceAddress + Offset;
|
|
|
|
//
|
|
// Dump this stack trace. Skip first two entries.
|
|
//
|
|
|
|
TraceArray += 2 * PvoidSize;
|
|
|
|
for (Index = 2; Index < TraceDepth; Index += 1) {
|
|
|
|
if (ReadPtr (TraceArray, &ReturnAddress) != S_OK) {
|
|
dprintf ("Cannot read address @ %p\n", TraceArray);
|
|
return;
|
|
}
|
|
|
|
GetSymbol (ReturnAddress, Symbol, &Displacement);
|
|
|
|
dprintf ("\t%p: %s+0x%I64X\n",
|
|
ReturnAddress,
|
|
Symbol,
|
|
Displacement );
|
|
|
|
TraceArray += PvoidSize;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////// Dump settings
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
VrfDumpSettings (
|
|
)
|
|
{
|
|
ULONG64 FlagsAddress;
|
|
ULONG Flags;
|
|
ULONG BytesRead;
|
|
ULONG64 StopAddress;
|
|
ULONG64 StopData[5];
|
|
ULONG I;
|
|
ULONG UlongPtrSize;
|
|
|
|
UlongPtrSize = GetTypeSize ("ntdll!ULONG_PTR");
|
|
FlagsAddress = GetExpression("ntdll!AVrfpVerifierFlags");
|
|
|
|
if (FlagsAddress == 0) {
|
|
dprintf( "Unable to resolve ntdll!AVrfpVerifierFlags symbolic name.\n");
|
|
return;
|
|
}
|
|
|
|
if (ReadMemory (FlagsAddress, &Flags, sizeof Flags, &BytesRead) == FALSE) {
|
|
dprintf ("Cannot read value @ %p (ntdll!AVrfpVerifierFlags) \n", FlagsAddress);
|
|
return;
|
|
}
|
|
|
|
dprintf ("Application verifier settings (%08X): \n\n", Flags);
|
|
|
|
if (Flags & RTL_VRF_FLG_FULL_PAGE_HEAP) {
|
|
dprintf (" - full page heap\n");
|
|
}
|
|
else {
|
|
dprintf (" - light page heap\n");
|
|
}
|
|
|
|
if (Flags & RTL_VRF_FLG_LOCK_CHECKS) {
|
|
dprintf (" - lock checks (critical section verifier)\n");
|
|
}
|
|
|
|
if (Flags & RTL_VRF_FLG_HANDLE_CHECKS) {
|
|
dprintf (" - handle checks\n");
|
|
}
|
|
|
|
if (Flags & RTL_VRF_FLG_STACK_CHECKS) {
|
|
dprintf (" - stack checks (disable automatic stack extensions)\n");
|
|
}
|
|
|
|
dprintf ("\n");
|
|
|
|
//
|
|
// Check if a verifier stop has been encountered.
|
|
//
|
|
|
|
StopAddress = GetExpression("ntdll!AVrfpStopData");
|
|
|
|
if (StopAddress == 0) {
|
|
dprintf( "Unable to resolve ntdll!AVrfpStopData symbolic name.\n");
|
|
return;
|
|
}
|
|
|
|
for (I = 0; I < 5; I += 1) {
|
|
|
|
if (ReadPtr (StopAddress + I * UlongPtrSize, &(StopData[I])) != S_OK) {
|
|
dprintf ("Cannot read value @ %p \n", StopAddress + I * UlongPtrSize);
|
|
}
|
|
}
|
|
|
|
if (StopData[0] != 0) {
|
|
|
|
dprintf ("Stop %p: %p %p %p %p \n",
|
|
StopData[0],
|
|
StopData[1],
|
|
StopData[2],
|
|
StopData[3],
|
|
StopData[4]);
|
|
|
|
// silviuc: dump more text info about the verifier_stop
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////// Dump vspace log
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
VrfDumpVspaceLog (
|
|
ULONG NoOfEntries,
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ULONG64 IndexAddress;
|
|
ULONG Index;
|
|
ULONG64 LogAddress;
|
|
ULONG EntrySize;
|
|
ULONG MaxIndex = 8192; // silviuc: should not be hard coded
|
|
ULONG BytesRead;
|
|
ULONG64 EntryAddress;
|
|
ULONG TraceIndex;
|
|
ULONG I;
|
|
PCHAR OpName;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
if (Address) {
|
|
NoOfEntries = MaxIndex;
|
|
}
|
|
else {
|
|
if (NoOfEntries == 0) {
|
|
NoOfEntries = 8;
|
|
}
|
|
}
|
|
|
|
EntrySize = GetTypeSize ("verifier!_VS_CALL");
|
|
IndexAddress = GetExpression("verifier!VsCallsIndex");
|
|
LogAddress = GetExpression("verifier!VsCalls");
|
|
|
|
if (IndexAddress == 0 || LogAddress == 0 || EntrySize == 0) {
|
|
dprintf( "Incorrect symbols for verifier.dll.\n");
|
|
return;
|
|
}
|
|
|
|
if (ReadMemory (IndexAddress, &Index, sizeof Index, &BytesRead) == FALSE) {
|
|
dprintf ("Cannot read value @ %p (verifier!VsCallsIndex) \n", IndexAddress);
|
|
return;
|
|
}
|
|
|
|
for (I = 0; I < NoOfEntries; I += 1) {
|
|
|
|
ULONG64 VsAddress;
|
|
ULONG64 VsSize;
|
|
BOOLEAN PrintTrace;
|
|
|
|
EntryAddress = LogAddress + EntrySize * ((Index - I) % MaxIndex);
|
|
|
|
if (InitTypeRead (EntryAddress, verifier!_VS_CALL)) {
|
|
dprintf("Unable to read type verifier!_VS_CALL @ %p\n", EntryAddress);
|
|
return;
|
|
}
|
|
|
|
switch ((ULONG)ReadField(Type)) {
|
|
case 0: OpName = "VirtualAlloc"; break;
|
|
case 1: OpName = "VirtualFree"; break;
|
|
case 2: OpName = "MapView"; break;
|
|
case 3: OpName = "UnmapView"; break;
|
|
default:OpName = "Unknown?"; break;
|
|
}
|
|
|
|
VsAddress = ReadField(Address);
|
|
VsSize = ReadField (Size);
|
|
|
|
if (Address) {
|
|
if (VsAddress <= Address && Address < VsAddress + VsSize) {
|
|
PrintTrace = TRUE;
|
|
}
|
|
else {
|
|
PrintTrace = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
PrintTrace = TRUE;
|
|
}
|
|
|
|
if (PrintTrace) {
|
|
|
|
Found = TRUE;
|
|
|
|
dprintf ("%s (tid: 0x%X): \n"
|
|
"address: %p \n"
|
|
"size: %p\n"
|
|
"operation: %X\n"
|
|
"protection: %X\n",
|
|
OpName,
|
|
(ULONG)ReadField(Thread),
|
|
VsAddress,
|
|
VsSize,
|
|
(ULONG)ReadField(Operation),
|
|
(ULONG)ReadField(Protection));
|
|
|
|
TraceIndex = (ULONG) ReadField (Trace);
|
|
VrfTraceDump (TraceIndex);
|
|
dprintf ("\n");
|
|
}
|
|
}
|
|
|
|
if (! Found) {
|
|
dprintf ("No entries found. \n");
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////// Dump heap log
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
VrfDumpHeapLog (
|
|
ULONG NoOfEntries,
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
ULONG64 IndexAddress;
|
|
ULONG Index;
|
|
ULONG64 LogAddress;
|
|
ULONG EntrySize;
|
|
ULONG MaxIndex = 8192; // silviuc: should not be hard coded
|
|
ULONG BytesRead;
|
|
ULONG64 EntryAddress;
|
|
ULONG TraceIndex;
|
|
ULONG I;
|
|
PCHAR OpName;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
if (Address) {
|
|
NoOfEntries = MaxIndex;
|
|
}
|
|
else {
|
|
if (NoOfEntries == 0) {
|
|
NoOfEntries = 8;
|
|
}
|
|
}
|
|
|
|
EntrySize = GetTypeSize ("verifier!_HEAP_CALL");
|
|
IndexAddress = GetExpression("verifier!HeapCallsIndex");
|
|
LogAddress = GetExpression("verifier!HeapCalls");
|
|
|
|
if (IndexAddress == 0 || LogAddress == 0 || EntrySize == 0) {
|
|
dprintf( "Incorrect symbols for verifier.dll.\n");
|
|
return;
|
|
}
|
|
|
|
if (ReadMemory (IndexAddress, &Index, sizeof Index, &BytesRead) == FALSE) {
|
|
dprintf ("Cannot read value @ %p (verifier!HeapCallsIndex) \n", IndexAddress);
|
|
return;
|
|
}
|
|
|
|
for (I = 0; I < NoOfEntries; I += 1) {
|
|
|
|
ULONG64 VsAddress;
|
|
ULONG64 VsSize;
|
|
BOOLEAN PrintTrace;
|
|
|
|
EntryAddress = LogAddress + EntrySize * ((Index - I) % MaxIndex);
|
|
|
|
if (InitTypeRead (EntryAddress, verifier!_HEAP_CALL)) {
|
|
dprintf("Unable to read type verifier!_HEAP_CALL @ %p\n", EntryAddress);
|
|
return;
|
|
}
|
|
|
|
VsAddress = ReadField(Address);
|
|
VsSize = ReadField (Size);
|
|
|
|
if (VsSize == 0) {
|
|
OpName = "free";
|
|
}
|
|
else {
|
|
OpName = "alloc";
|
|
}
|
|
|
|
if (Address) {
|
|
if (VsAddress <= Address && Address <= VsAddress + VsSize) {
|
|
PrintTrace = TRUE;
|
|
}
|
|
else {
|
|
PrintTrace = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
PrintTrace = TRUE;
|
|
}
|
|
|
|
if (PrintTrace) {
|
|
|
|
Found = TRUE;
|
|
|
|
dprintf ("%s (tid: 0x%X): \n"
|
|
"address: %p \n"
|
|
"size: %p\n",
|
|
OpName,
|
|
(ULONG)ReadField(Thread),
|
|
VsAddress,
|
|
VsSize);
|
|
|
|
TraceIndex = (ULONG) ReadField (Trace);
|
|
VrfTraceDump (TraceIndex);
|
|
dprintf ("\n");
|
|
}
|
|
}
|
|
|
|
if (! Found) {
|
|
dprintf ("No entries found. \n");
|
|
}
|
|
}
|
|
|