/*++ 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"); } }