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.
647 lines
17 KiB
647 lines
17 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|