//---------------------------------------------------------------------------- // // User-mode exception analysis. // // Copyright (C) Microsoft Corporation, 2001. // //---------------------------------------------------------------------------- #include "precomp.h" #pragma hdrstop #include "nturtl.h" typedef void (*EX_STATE_ANALYZER)(PEX_STATE ExState, UserDebugFailureAnalysis* Analysis); //---------------------------------------------------------------------------- // // Exception-specific analyzers. // //---------------------------------------------------------------------------- #define IMPL_EXS_ANALYZER(Name) \ void \ Exa_##Name(PEX_STATE ExState, \ UserDebugFailureAnalysis* Analysis) IMPL_EXS_ANALYZER(STATUS_ACCESS_VIOLATION) { Analysis->SetUlong64(ExState->Exr.ExceptionInformation[0] ? DEBUG_FLR_WRITE_ADDRESS : DEBUG_FLR_READ_ADDRESS, ExState->Exr.ExceptionInformation[1]); Analysis->SetString(DEBUG_FLR_BUGCHECK_STR, "ACCESS_VIOLATION"); } //---------------------------------------------------------------------------- // // App verifier-specific analyzers. // //---------------------------------------------------------------------------- #define IMPL_AVRF_ANALYZER(Name) \ void \ Avrf_##Name(PAVRF_STOP AvrfStop, \ DebugFailureAnalysis* Analysis) IMPL_AVRF_ANALYZER(APPLICATION_VERIFIER_INVALID_HANDLE) { EXT_GET_HANDLE_TRACE pGetHandleTrace; if (AvrfStop->Params[1]) { Analysis->SetUlong64(DEBUG_FLR_EXCEPTION_RECORD, AvrfStop->Params[1]); } if (AvrfStop->Params[2]) { Analysis->SetUlong64(DEBUG_FLR_CONTEXT, AvrfStop->Params[2]); } if ((g_TargetQualifier == DEBUG_DUMP_SMALL) || (g_TargetQualifier == DEBUG_DUMP_DEFAULT) || (g_TargetQualifier == DEBUG_DUMP_FULL)) { return; } // Look for most recent handle trace which resulted in // bad reference if (g_ExtControl-> GetExtensionFunction(0, "GetHandleTrace", (FARPROC*)&pGetHandleTrace) == S_OK && pGetHandleTrace) { ULONG64 Handle = 0; ULONG64 Stack[10]; #ifndef HANDLE_TRACE_DB_BADREF #define HANDLE_TRACE_DB_BADREF 3 #endif if ((*pGetHandleTrace)(g_ExtClient, HANDLE_TRACE_DB_BADREF, 0, &Handle, Stack, sizeof(Stack)/sizeof(Stack[0])) == S_OK) { // Found the bad handle !! Analysis->SetUlong64(DEBUG_FLR_BAD_HANDLE, Handle); } } return; } IMPL_AVRF_ANALYZER(APPLICATION_VERIFIER_LOCK_IN_UNLOADED_DLL) { CHAR DllName[MAX_PATH]; if (AvrfStop->Params[0]) { Analysis->SetUlong64(DEBUG_FLR_CRITICAL_SECTION, AvrfStop->Params[0]); } if (AvrfStop->Params[2]) { if (ReadMemory(AvrfStop->Params[2], DllName, sizeof(DllName), NULL)) { DllName[MAX_PATH-1] = DllName[MAX_PATH-2] = 0; if (DllName[0]) { wchr2ansi((PWCHAR) DllName, DllName); Analysis->SetString(DEBUG_FLR_IMAGE_NAME, DllName); Analysis->SetUlong(DEBUG_FLR_FOLLOWUP_DRIVER_ONLY, 1); } } } if (AvrfStop->Params[3]) { Analysis->SetUlong64(DEBUG_FLR_FAULTING_MODULE, AvrfStop->Params[3]); } } IMPL_AVRF_ANALYZER(APPLICATION_VERIFIER_ACCESS_VIOLATION) { if (AvrfStop->Params[0]) { Analysis->SetUlong64(DEBUG_FLR_INVALID_HEAP_ADDRESS, AvrfStop->Params[0]); } if (AvrfStop->Params[1]) { Analysis->SetUlong64(DEBUG_FLR_FAULTING_IP, AvrfStop->Params[1]); } if (AvrfStop->Params[2]) { Analysis->SetUlong64(DEBUG_FLR_EXCEPTION_RECORD, AvrfStop->Params[2]); } if (AvrfStop->Params[3]) { Analysis->SetUlong64(DEBUG_FLR_CONTEXT, AvrfStop->Params[3]); } } IMPL_AVRF_ANALYZER(APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK) { if (AvrfStop->Params[1]) { Analysis->SetUlong64(DEBUG_FLR_INVALID_HEAP_ADDRESS, AvrfStop->Params[1]); } } IMPL_AVRF_ANALYZER(APPLICATION_VERIFIER_LOCK_IN_FREED_HEAP) { CHAR DllName[MAX_PATH]; if (AvrfStop->Params[0]) { Analysis->SetUlong64(DEBUG_FLR_CRITICAL_SECTION, AvrfStop->Params[0]); } } BOOL CheckAppVerifierEnabled( void ) { ULONG64 PebAddress; ULONG Flags; ULONG BytesRead; ULONG I; ULONG64 GlobalFlags; // // No app verifier on NT 4 // if (g_TargetBuild < 2195) { return FALSE; } GetPebAddress(0, &PebAddress); if (PebAddress) { if (!InitTypeRead (PebAddress, ntdll!_PEB)) { GlobalFlags = ReadField (NtGlobalFlag); if ((GlobalFlags & FLG_APPLICATION_VERIFIER)) { return TRUE; } } } return FALSE; } BOOL GetVerifierDataFromException( PEX_STATE ExState, PULONG Code, PULONG64 Param1, PULONG64 Param2, PULONG64 Param3, PULONG64 Param4 ) { CHAR Buffer[MAX_PATH]; ULONG64 Disp; *Code = 0; if (ExState == NULL) { return FALSE; } if (ExState->FirstChance && ExState->Exr.ExceptionCode == STATUS_INVALID_HANDLE) { if (FaIsFunctionAddr(ExState->Exr.ExceptionAddress, "KiRaiseUserExceptionDispatcher")) { // Most likely this is a use of invalid handle, but verifier // couldn't catch it because app is under debugger *Code = APPLICATION_VERIFIER_INVALID_HANDLE ; // INVALID_HANDLE; *Param1 = STATUS_INVALID_HANDLE; return TRUE; } } return FALSE; } BOOL GetVerifierStopData( PULONG Code, PULONG64 Param1, PULONG64 Param2, PULONG64 Param3, PULONG64 Param4 ) { ULONG64 CurrentStopAddress; ULONG PointerSize = IsPtr64() ? 8 : 4; *Code = 0; CurrentStopAddress = GetExpression("ntdll!AVrfpStopData"); if (CurrentStopAddress == 0) { CurrentStopAddress = GetExpression("verifier!AVrfpStopData"); } if (CurrentStopAddress == 0) { dprintf( "Unable to resolve AVrfpStopData symbol.\n"); return FALSE; } if (!ReadMemory(CurrentStopAddress, Code, sizeof(*Code), NULL) || !ReadPointer(CurrentStopAddress + PointerSize, Param1) || !ReadPointer(CurrentStopAddress + 2*PointerSize, Param2) || !ReadPointer(CurrentStopAddress + 3*PointerSize, Param3) || !ReadPointer(CurrentStopAddress + 4*PointerSize, Param4)) { dprintf("Cannot read AVrfpStopData values.\n"); return FALSE; } if (*Code) { return TRUE; } return FALSE; } HRESULT DoVerifierAnalysis( PEX_STATE ExState, DebugFailureAnalysis* Analysis ) { AVRF_STOP AvrfStop = {0}; if (!GetVerifierStopData(&AvrfStop.Code, &AvrfStop.Params[0], &AvrfStop.Params[1], &AvrfStop.Params[2], &AvrfStop.Params[3]) && !GetVerifierDataFromException(ExState, &AvrfStop.Code, &AvrfStop.Params[0], &AvrfStop.Params[1], &AvrfStop.Params[2], &AvrfStop.Params[3])) { // Doesn't look like a verifier bug return S_FALSE; } CHAR BugCheckStr[40]; sprintf(BugCheckStr, "AVRF_%lx", AvrfStop.Code); Analysis->SetString(DEBUG_FLR_BUGCHECK_STR, BugCheckStr); #define CALL_AVRF_ANALYZER(Name) \ case Name: \ Avrf_##Name(&AvrfStop, \ Analysis); \ break; switch (AvrfStop.Code) { CALL_AVRF_ANALYZER(APPLICATION_VERIFIER_INVALID_HANDLE); CALL_AVRF_ANALYZER(APPLICATION_VERIFIER_LOCK_IN_UNLOADED_DLL); CALL_AVRF_ANALYZER(APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK); CALL_AVRF_ANALYZER(APPLICATION_VERIFIER_ACCESS_VIOLATION); case APPLICATION_VERIFIER_LOCK_DOUBLE_INITIALIZE: case APPLICATION_VERIFIER_LOCK_OVER_RELEASED: case APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED: case APPLICATION_VERIFIER_LOCK_ALREADY_INITIALIZED: case APPLICATION_VERIFIER_LOCK_INVALID_LOCK_COUNT: CALL_AVRF_ANALYZER(APPLICATION_VERIFIER_LOCK_IN_FREED_HEAP); default: break; } return S_OK; } //---------------------------------------------------------------------------- // // Generic exception analysis handling. // //---------------------------------------------------------------------------- struct EX_ANALYZER_ENTRY { ULONG Code; EX_STATE_ANALYZER Analyzer; }; #define EXS_ANALYZER_ENTRY(Name) \ Name, Exa_##Name EX_ANALYZER_ENTRY g_ExAnalyzers[] = { EXS_ANALYZER_ENTRY(STATUS_ACCESS_VIOLATION), 0, NULL, }; void UeFillAnalysis(PEX_STATE ExState, UserDebugFailureAnalysis* Analysis) { HRESULT Status; // // Add common analysis entries. // Analysis->SetFailureClass(DEBUG_CLASS_USER_WINDOWS); Analysis->SetFailureType(DEBUG_FLR_USER_CRASH); Analysis->SetFailureCode(ExState->Exr.ExceptionCode); Analysis->SetUlong64(DEBUG_FLR_FAULTING_IP, ExState->Exr.ExceptionAddress); Analysis->SetUlong64(DEBUG_FLR_EXCEPTION_RECORD, -1); CHAR BugCheckStr[40]; sprintf(BugCheckStr, "%lx", ExState->Exr.ExceptionCode); Analysis->SetString(DEBUG_FLR_BUGCHECK_STR, BugCheckStr); Analysis->SetString(DEBUG_FLR_DEFAULT_BUCKET_ID, "APPLICATION_FAULT"); CHAR ProcessName[512]; ULONG Size; PCHAR Name; Status = g_ExtSystem->GetCurrentProcessExecutableName(ProcessName, sizeof(ProcessName), &Size); if ((Status == S_OK) && (Size > 1)) { if (Name = strrchr(ProcessName, '\\')) { Name++; } else { Name = ProcessName; } Analysis->SetString(DEBUG_FLR_PROCESS_NAME, Name); if (!_stricmp("iexplore.exe", Name)) { Analysis->SetFailureType(DEBUG_FLR_IE_CRASH); } } if (g_TargetPlatform == VER_PLATFORM_WIN32_NT) { Analysis->CheckModuleSymbols("ntdll", "OS"); if (CheckAppVerifierEnabled()) { // This proces has app verifier enabled DoVerifierAnalysis(ExState, Analysis); } } // // Find an analyzer for the exception and run it. // EX_ANALYZER_ENTRY* Entry = g_ExAnalyzers; while (Entry->Analyzer) { if (Entry->Code == ExState->Exr.ExceptionCode) { Entry->Analyzer(ExState, Analysis); break; } Entry++; } Analysis->ProcessInformation(); } UserDebugFailureAnalysis* UeAnalyze( PEX_STATE ExState, ULONG Flags ) { ULONG EventType; DEBUG_LAST_EVENT_INFO_EXCEPTION LastEx; if (g_ExtControl->GetLastEventInformation(&EventType, &ExState->ProcId, &ExState->ThreadId, &LastEx, sizeof(LastEx), NULL, NULL, 0, NULL) != S_OK) { ExtErr("Unable to get last event information\n"); return NULL; } if (EventType != DEBUG_EVENT_EXCEPTION) { ExtErr("Event is not an exception\n"); return NULL; } ExState->Exr = LastEx.ExceptionRecord; ExState->FirstChance = LastEx.FirstChance; UserDebugFailureAnalysis* Analysis = new UserDebugFailureAnalysis; if (Analysis) { Analysis->SetProcessingFlags(Flags); __try { UeFillAnalysis(ExState, Analysis); } __except(FaExceptionFilter(GetExceptionInformation())) { delete Analysis; Analysis = NULL; } } return Analysis; } HRESULT AnalyzeUserException( PCSTR args ) { ULONG Flags = 0; if (g_TargetClass != DEBUG_CLASS_USER_WINDOWS) { dprintf("!analyzeuexception is for user mode only\n"); return E_FAIL; } for (;;) { while (*args == ' ' || *args == '\t') { args++; } if (*args == '-') { ++args; switch(*args) { case 'v': Flags |= FAILURE_ANALYSIS_VERBOSE; break; case 'f': break; default: dprintf("Unknown option %c\n", *args); break; } ++args; } else { break; } } dprintf("*******************************************************************************\n"); dprintf("* *\n"); dprintf("* Exception Analysis *\n"); dprintf("* *\n"); dprintf("*******************************************************************************\n"); dprintf("\n"); if (!(Flags & FAILURE_ANALYSIS_VERBOSE)) { dprintf("Use !analyze -v to get detailed debugging information.\n\n"); } EX_STATE ExState; UserDebugFailureAnalysis* Analysis = UeAnalyze(&ExState, Flags); if (!Analysis) { dprintf("\n\nFailure could not be analyzed\n\n"); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS, ".lastevent", DEBUG_EXECUTE_DEFAULT); return E_FAIL; } Analysis->Output(); delete Analysis; return S_OK; } DECLARE_API( analyzeuexception ) { INIT_API(); HRESULT Hr = AnalyzeUserException(args); EXIT_API(); return Hr; } //---------------------------------------------------------------------------- // // UserDebugFailureAnalysis. // //---------------------------------------------------------------------------- UserDebugFailureAnalysis::UserDebugFailureAnalysis(void) : m_NtDllModule("ntdll"), m_Kernel32Module("kernel32"), m_Advapi32Module("advapi32") { } DEBUG_POOL_REGION UserDebugFailureAnalysis::GetPoolForAddress(ULONG64 Addr) { return DbgPoolRegionUnknown; } PCSTR UserDebugFailureAnalysis::DescribeAddress(ULONG64 Address) { // XXX drewb - QueryVirtual to say something about the address? return NULL; } FOLLOW_ADDRESS UserDebugFailureAnalysis::IsPotentialFollowupAddress(ULONG64 Address) { // XXX drewb - Check against maximum user address, but there's // no guaranteed way to get the maximum user address. return FollowYes; } FOLLOW_ADDRESS UserDebugFailureAnalysis::IsFollowupContext(ULONG64 Address1, ULONG64 Address2, ULONG64 Address3) { // XXX drewb - Check against maximum user address, but there's // no guaranteed way to get the maximum user address. return FollowYes; } FlpClasses UserDebugFailureAnalysis::GetFollowupClass(ULONG64 Address, PCSTR Module, PCSTR Routine) { if (m_NtDllModule.Contains(Address) || m_Kernel32Module.Contains(Address) || m_Advapi32Module.Contains(Address) || (!_strcmpi(Module, "SharedUserData") && Routine && !_strcmpi(Routine, "SystemCallStub"))) { return FlpOSRoutine; } else if (!_strcmpi(Module, "shell32") || !_strcmpi(Module, "user32") || !_strcmpi(Module, "gdi32") || !_strcmpi(Module, "mshtml") || !_strcmpi(Module, "ole32") || !_strcmpi(Module, "verifier") || !_strcmpi(Module, "rpcrt4")) { return FlpOSFilterDrv; } else { return FlpUnknownDrv; } } BOOL UserDebugFailureAnalysis::CheckForCorruptionInHTE( ULONG64 hTableEntry, PCHAR Owner, ULONG OwnerSize ) { return FALSE; }