|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
triage.c
Abstract:
This module contains the Phase 0 code to triage bugchecks and automatically enable various system tracing components until the guilty party is found.
Author:
Landy Wang 13-Jan-1999
Revision History:
--*/
#include "mi.h"
#include "ntiodump.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,MiTriageSystem)
#pragma alloc_text(INIT,MiTriageAddDrivers)
#endif
//
// Always update this macro when adding triage support for additional bugchecks.
//
#define MI_CAN_TRIAGE_BUGCHECK(BugCheckCode) \
((BugCheckCode) == NO_MORE_SYSTEM_PTES || \ (BugCheckCode) == BAD_POOL_HEADER || \ (BugCheckCode) == DRIVER_CORRUPTED_SYSPTES || \ (BugCheckCode) == DRIVER_CORRUPTED_EXPOOL || \ (BugCheckCode) == DRIVER_CORRUPTED_MMPOOL)
//
// These are bugchecks that were presumably triggered by either autotriage or
// the admin's registry settings - so don't apply any new rules and in addition,
// keep the old ones unaltered so it can reproduce.
//
#define MI_HOLD_TRIAGE_BUGCHECK(BugCheckCode) \
((BugCheckCode) == DRIVER_USED_EXCESSIVE_PTES || \ (BugCheckCode) == DRIVER_LEFT_LOCKED_PAGES_IN_PROCESS || \ (BugCheckCode) == PAGE_FAULT_IN_FREED_SPECIAL_POOL || \ (BugCheckCode) == DRIVER_PAGE_FAULT_IN_FREED_SPECIAL_POOL || \ (BugCheckCode) == PAGE_FAULT_BEYOND_END_OF_ALLOCATION || \ (BugCheckCode) == DRIVER_PAGE_FAULT_BEYOND_END_OF_ALLOCATION || \ (BugCheckCode) == DRIVER_CAUGHT_MODIFYING_FREED_POOL || \ (BugCheckCode) == SYSTEM_PTE_MISUSE)
#define MI_TRACKING_PTES 0x00000002
#define MI_PROTECT_FREED_NONPAGED_POOL 0x00000004
#define MI_VERIFYING_PRENT5_DRIVERS 0x00000008
#define MI_KEEPING_PREVIOUS_SETTINGS 0x00000010
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#pragma data_seg("INITDATA")
#endif
const PCHAR MiTriageActionStrings[] = { "Locked pages tracking", "System PTE usage tracking", "Making accesses to freed nonpaged pool cause bugchecks", "Driver Verifying Pre-Windows 2000 built drivers", "Keeping previous autotriage settings" };
#if DBG
ULONG MiTriageDebug = 0; BOOLEAN MiTriageRegardless = FALSE; #endif
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#pragma data_seg()
#endif
//
// N.B. The debugger references this.
//
ULONG MmTriageActionTaken;
//
// The Version number must be incremented whenever the MI_TRIAGE_STORAGE
// structure is changed. This enables usermode programs to decode the Mm
// portions of triage dumps regardless of which kernel revision created the
// dump.
//
typedef struct _MI_TRIAGE_STORAGE { ULONG Version; ULONG Size; ULONG MmSpecialPoolTag; ULONG MiTriageActionTaken;
ULONG MmVerifyDriverLevel; ULONG KernelVerifier; ULONG_PTR MmMaximumNonPagedPool; ULONG_PTR MmAllocatedNonPagedPool;
ULONG_PTR PagedPoolMaximum; ULONG_PTR PagedPoolAllocated;
ULONG_PTR CommittedPages; ULONG_PTR CommittedPagesPeak; ULONG_PTR CommitLimitMaximum;
} MI_TRIAGE_STORAGE, *PMI_TRIAGE_STORAGE;
PKLDR_DATA_TABLE_ENTRY TriageGetLoaderEntry ( IN PVOID TriageDumpBlock, IN ULONG ModuleIndex );
LOGICAL TriageActUpon( IN PVOID TriageDumpBlock );
PVOID TriageGetMmInformation ( IN PVOID TriageDumpBlock );
LOGICAL MiTriageSystem ( IN PLOADER_PARAMETER_BLOCK LoaderBlock )
/*++
Routine Description:
This routine takes the information from the last bugcheck (if any) and triages it. Various debugging options are then automatically enabled.
Arguments:
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
TRUE if triaging succeeded and options were enabled. FALSE otherwise.
--*/
{ PVOID TriageDumpBlock; ULONG_PTR BugCheckData[5]; ULONG i; ULONG ModuleCount; NTSTATUS Status; PLIST_ENTRY NextEntry; PKLDR_DATA_TABLE_ENTRY DataTableEntry; PKLDR_DATA_TABLE_ENTRY DumpTableEntry; LOGICAL Matched; ULONG OldDrivers; ULONG OldDriversNotVerifying; PMI_TRIAGE_STORAGE TriageInformation; if (LoaderBlock->Extension == NULL) { return FALSE; }
if (LoaderBlock->Extension->Size < sizeof (LOADER_PARAMETER_EXTENSION)) { return FALSE; }
TriageDumpBlock = LoaderBlock->Extension->TriageDumpBlock;
Status = TriageGetBugcheckData (TriageDumpBlock, (PULONG)&BugCheckData[0], (PUINT_PTR) &BugCheckData[1], (PUINT_PTR) &BugCheckData[2], (PUINT_PTR) &BugCheckData[3], (PUINT_PTR) &BugCheckData[4]);
if (!NT_SUCCESS (Status)) { return FALSE; }
//
// Always display at least the bugcheck data from the previous crash.
//
DbgPrint ("MiTriageSystem: Previous bugcheck was %x %p %p %p %p\n", BugCheckData[0], BugCheckData[1], BugCheckData[2], BugCheckData[3], BugCheckData[4]);
if (TriageActUpon (TriageDumpBlock) == FALSE) { DbgPrint ("MiTriageSystem: Triage disabled in registry by administrator\n"); return FALSE; }
DbgPrint ("MiTriageSystem: Triage ENABLED in registry by administrator\n");
//
// See if the previous bugcheck was one where action can be taken.
// If not, bail now. If so, then march on and verify all the loaded
// module checksums before actually taking action on the bugcheck.
//
if (!MI_CAN_TRIAGE_BUGCHECK(BugCheckData[0])) { return FALSE; }
TriageInformation = (PMI_TRIAGE_STORAGE) TriageGetMmInformation (TriageDumpBlock);
if (TriageInformation == NULL) { return FALSE; }
Status = TriageGetDriverCount (TriageDumpBlock, &ModuleCount);
if (!NT_SUCCESS (Status)) { return FALSE; }
//
// Process module information from the triage dump.
//
#if DBG
if (MiTriageDebug & 0x1) { DbgPrint ("MiTriageSystem: printing active drivers from triage crash...\n"); } #endif
OldDrivers = 0; OldDriversNotVerifying = 0;
for (i = 0; i < ModuleCount; i += 1) {
DumpTableEntry = TriageGetLoaderEntry (TriageDumpBlock, i);
if (DumpTableEntry != NULL) {
if ((DumpTableEntry->Flags & LDRP_ENTRY_NATIVE) == 0) { OldDrivers += 1; if ((DumpTableEntry->Flags & LDRP_IMAGE_VERIFYING) == 0) {
//
// An NT3 or NT4 driver is in the system and was not
// running under the verifier.
//
OldDriversNotVerifying += 1; } } #if DBG
if (MiTriageDebug & 0x1) {
DbgPrint (" %wZ: base = %p, size = %lx, flags = %lx\n", &DumpTableEntry->BaseDllName, DumpTableEntry->DllBase, DumpTableEntry->SizeOfImage, DumpTableEntry->Flags); } #endif
} }
//
// Ensure that every driver that is currently loaded is identical to
// the one in the triage dump before proceeding.
//
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
while (NextEntry != &LoaderBlock->LoadOrderListHead) {
DataTableEntry = CONTAINING_RECORD(NextEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
Matched = FALSE;
for (i = 0; i < ModuleCount; i += 1) { DumpTableEntry = TriageGetLoaderEntry (TriageDumpBlock, i); if (DumpTableEntry != NULL) { if (DataTableEntry->CheckSum == DumpTableEntry->CheckSum) { Matched = TRUE; break; } } } if (Matched == FALSE) { DbgPrint ("Matching checksum for module %wZ not found in triage dump\n", &DataTableEntry->BaseDllName);
#if DBG
if (MiTriageRegardless == FALSE) #endif
return FALSE; }
NextEntry = NextEntry->Flink; }
#if DBG
if (MiTriageDebug & 0x1) { DbgPrint ("MiTriageSystem: OldDrivers = %u, without verification =%u\n", OldDrivers, OldDriversNotVerifying); } #endif
//
// All boot loaded drivers matched, take action on the triage dump now.
//
if (MI_HOLD_TRIAGE_BUGCHECK(BugCheckData[0])) {
//
// The last bugcheck was presumably triggered by either autotriage or
// the admin's registry settings - so don't apply any new rules
// and in addition, keep the old ones unaltered so it can reproduce.
//
MmTriageActionTaken = TriageInformation->MiTriageActionTaken; MmTriageActionTaken |= MI_KEEPING_PREVIOUS_SETTINGS; } else { switch (BugCheckData[0]) { case DRIVER_CORRUPTED_SYSPTES: //
// Turn on PTE tracking to trigger a SYSTEM_PTE_MISUSE bugcheck.
//
MmTriageActionTaken |= MI_TRACKING_PTES; break; case NO_MORE_SYSTEM_PTES: //
// Turn on PTE tracking so the driver can be identified via a
// DRIVER_USED_EXCESSIVE_PTES bugcheck.
//
if (BugCheckData[1] == SystemPteSpace) { MmTriageActionTaken |= MI_TRACKING_PTES; } break; case BAD_POOL_HEADER: case DRIVER_CORRUPTED_EXPOOL: //
// Turn on the driver verifier and/or special pool.
// Start by enabling it for every driver that isn't built for NT5.
// Override any specified driver verifier options so that only
// special pool is enabled to minimize the performance hit.
//
if (OldDrivers != 0) { if (OldDriversNotVerifying != 0) { MmTriageActionTaken |= MI_VERIFYING_PRENT5_DRIVERS; } } break; case DRIVER_CORRUPTED_MMPOOL: //
// Protect freed nonpaged pool if the system had less than 128mb
// of nonpaged pool anyway. This is to trigger a
// DRIVER_CAUGHT_MODIFYING_FREED_POOL bugcheck.
//
#define MB128 ((ULONG_PTR)0x80000000 >> PAGE_SHIFT)
if (TriageInformation->MmMaximumNonPagedPool < MB128) { MmTriageActionTaken |= MI_PROTECT_FREED_NONPAGED_POOL; } break; case IRQL_NOT_LESS_OR_EQUAL: case DRIVER_IRQL_NOT_LESS_OR_EQUAL: default: break; } }
//
// For now always show if action was taken from the bugcheck
// data from the crash. This print and the space for the print strings
// will be enabled for checked builds only prior to shipping.
//
if (MmTriageActionTaken != 0) {
if (MmTriageActionTaken & MI_TRACKING_PTES) { MmTrackPtes |= 0x1; } if (MmTriageActionTaken & MI_VERIFYING_PRENT5_DRIVERS) { MmVerifyDriverLevel &= ~DRIVER_VERIFIER_FORCE_IRQL_CHECKING; MmVerifyDriverLevel |= DRIVER_VERIFIER_SPECIAL_POOLING; } if (MmTriageActionTaken & MI_PROTECT_FREED_NONPAGED_POOL) { MmProtectFreedNonPagedPool = TRUE; }
DbgPrint ("MiTriageSystem: enabling options below to find who caused the last crash\n");
for (i = 0; i < 32; i += 1) { if (MmTriageActionTaken & (1 << i)) { DbgPrint (" %s\n", MiTriageActionStrings[i]); } } }
return TRUE; }
LOGICAL MiTriageAddDrivers ( IN PLOADER_PARAMETER_BLOCK LoaderBlock )
/*++
Routine Description:
This routine moves the names of any drivers that autotriage has determined need verifying from the LoaderBlock into pool.
Arguments:
LoaderBlock - Supplies a pointer to the system loader block.
Return Value:
TRUE if any drivers were added, FALSE if not.
--*/
{ ULONG i; ULONG ModuleCount; NTSTATUS Status; PKLDR_DATA_TABLE_ENTRY DumpTableEntry; PVOID TriageDumpBlock; ULONG NameLength; LOGICAL Added; PMI_VERIFIER_DRIVER_ENTRY VerifierDriverEntry;
if ((MmTriageActionTaken & MI_VERIFYING_PRENT5_DRIVERS) == 0) { return FALSE; }
TriageDumpBlock = LoaderBlock->Extension->TriageDumpBlock;
Status = TriageGetDriverCount (TriageDumpBlock, &ModuleCount);
if (!NT_SUCCESS (Status)) { return FALSE; }
Added = FALSE;
for (i = 0; i < ModuleCount; i += 1) {
DumpTableEntry = TriageGetLoaderEntry (TriageDumpBlock, i);
if (DumpTableEntry == NULL) { continue; }
if (DumpTableEntry->Flags & LDRP_ENTRY_NATIVE) { continue; }
DbgPrint ("MiTriageAddDrivers: Marking %wZ for verification when it is loaded\n", &DumpTableEntry->BaseDllName);
NameLength = DumpTableEntry->BaseDllName.Length;
VerifierDriverEntry = (PMI_VERIFIER_DRIVER_ENTRY)ExAllocatePoolWithTag ( NonPagedPool, sizeof (MI_VERIFIER_DRIVER_ENTRY) + NameLength, 'dLmM');
if (VerifierDriverEntry == NULL) { continue; }
VerifierDriverEntry->Loads = 0; VerifierDriverEntry->Unloads = 0; VerifierDriverEntry->BaseName.Buffer = (PWSTR)((PCHAR)VerifierDriverEntry + sizeof (MI_VERIFIER_DRIVER_ENTRY));
VerifierDriverEntry->BaseName.Length = (USHORT)NameLength; VerifierDriverEntry->BaseName.MaximumLength = (USHORT)NameLength;
RtlCopyMemory (VerifierDriverEntry->BaseName.Buffer, DumpTableEntry->BaseDllName.Buffer, NameLength);
InsertHeadList (&MiSuspectDriverList, &VerifierDriverEntry->Links); Added = TRUE; }
return Added; }
#define MAX_UNLOADED_NAME_LENGTH 24
typedef struct _DUMP_UNLOADED_DRIVERS { UNICODE_STRING Name; WCHAR DriverName[MAX_UNLOADED_NAME_LENGTH / sizeof (WCHAR)]; PVOID StartAddress; PVOID EndAddress; } DUMP_UNLOADED_DRIVERS, *PDUMP_UNLOADED_DRIVERS;
ULONG MmSizeOfUnloadedDriverInformation ( VOID )
/*++
Routine Description:
This routine returns the size of the Mm-internal unloaded driver information that is stored in the triage dump when (if?) the system crashes.
Arguments:
None.
Return Value:
Size of the Mm-internal unloaded driver information.
--*/
{ if (MmUnloadedDrivers == NULL) { return sizeof (ULONG_PTR); }
return sizeof(ULONG_PTR) + MI_UNLOADED_DRIVERS * sizeof(DUMP_UNLOADED_DRIVERS); }
VOID MmWriteUnloadedDriverInformation ( IN PVOID Destination )
/*++
Routine Description:
This routine stores the Mm-internal unloaded driver information into the triage dump.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG i; ULONG Index; PUNLOADED_DRIVERS Unloaded; PDUMP_UNLOADED_DRIVERS DumpUnloaded;
if (MmUnloadedDrivers == NULL) { *(PULONG)Destination = 0; } else {
DumpUnloaded = (PDUMP_UNLOADED_DRIVERS)((PULONG_PTR)Destination + 1); Unloaded = MmUnloadedDrivers;
//
// Write the list with the most recently unloaded driver first to the
// least recently unloaded driver last.
//
Index = MmLastUnloadedDriver - 1;
for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) {
if (Index >= MI_UNLOADED_DRIVERS) { Index = MI_UNLOADED_DRIVERS - 1; }
Unloaded = &MmUnloadedDrivers[Index];
DumpUnloaded->Name = Unloaded->Name;
if (Unloaded->Name.Buffer == NULL) { break; }
DumpUnloaded->StartAddress = Unloaded->StartAddress; DumpUnloaded->EndAddress = Unloaded->EndAddress;
if (DumpUnloaded->Name.Length > MAX_UNLOADED_NAME_LENGTH) { DumpUnloaded->Name.Length = MAX_UNLOADED_NAME_LENGTH; }
if (DumpUnloaded->Name.MaximumLength > MAX_UNLOADED_NAME_LENGTH) { DumpUnloaded->Name.MaximumLength = MAX_UNLOADED_NAME_LENGTH; }
DumpUnloaded->Name.Buffer = DumpUnloaded->DriverName;
RtlCopyMemory ((PVOID)DumpUnloaded->Name.Buffer, (PVOID)Unloaded->Name.Buffer, DumpUnloaded->Name.MaximumLength);
DumpUnloaded += 1; Index -= 1; }
*(PULONG)Destination = i; } }
ULONG MmSizeOfTriageInformation ( VOID )
/*++
Routine Description:
This routine returns the size of the Mm-internal information that is stored in the triage dump when (if?) the system crashes.
Arguments:
None.
Return Value:
Size of the Mm-internal triage information.
--*/
{ return sizeof (MI_TRIAGE_STORAGE); }
VOID MmWriteTriageInformation ( IN PVOID Destination )
/*++
Routine Description:
This routine stores the Mm-internal information into the triage dump.
Arguments:
None.
Return Value:
None.
--*/
{ MI_TRIAGE_STORAGE TriageInformation;
TriageInformation.Version = 1; TriageInformation.Size = sizeof (MI_TRIAGE_STORAGE);
TriageInformation.MmSpecialPoolTag = MmSpecialPoolTag; TriageInformation.MiTriageActionTaken = MmTriageActionTaken;
TriageInformation.MmVerifyDriverLevel = MmVerifierData.Level; TriageInformation.KernelVerifier = KernelVerifier;
TriageInformation.MmMaximumNonPagedPool = MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT; TriageInformation.MmAllocatedNonPagedPool = MmAllocatedNonPagedPool;
TriageInformation.PagedPoolMaximum = MmSizeOfPagedPoolInBytes >> PAGE_SHIFT; TriageInformation.PagedPoolAllocated = MmPagedPoolInfo.AllocatedPagedPool;
TriageInformation.CommittedPages = MmTotalCommittedPages; TriageInformation.CommittedPagesPeak = MmPeakCommitment; TriageInformation.CommitLimitMaximum = MmTotalCommitLimitMaximum;
RtlCopyMemory (Destination, (PVOID)&TriageInformation, sizeof (MI_TRIAGE_STORAGE)); }
|