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.
692 lines
16 KiB
692 lines
16 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
faults.c
|
|
|
|
Abstract:
|
|
|
|
This module implements fault injection support.
|
|
|
|
Author:
|
|
|
|
Silviu Calinoiu (SilviuC) 3-Dec-2001
|
|
|
|
Revision History:
|
|
|
|
3-Dec-2001 (SilviuC): initial version.
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include "verifier.h"
|
|
#include "support.h"
|
|
#include "faults.h"
|
|
|
|
ULONG AVrfpFaultSeed;
|
|
ULONG AVrfpFaultProbability [CLS_MAXIMUM_INDEX];
|
|
|
|
ULONG AVrfpFaultBreak [CLS_MAXIMUM_INDEX];
|
|
ULONG AVrfpFaultTrue [CLS_MAXIMUM_INDEX];
|
|
ULONG AVrfpFaultFalse [CLS_MAXIMUM_INDEX];
|
|
|
|
//
|
|
// Target ranges for fault injection.
|
|
//
|
|
|
|
#define MAXIMUM_TARGET_INDEX 128
|
|
|
|
ULONG_PTR AVrfpFaultTargetStart [MAXIMUM_TARGET_INDEX];
|
|
ULONG_PTR AVrfpFaultTargetEnd [MAXIMUM_TARGET_INDEX];
|
|
ULONG AVrfpFaultTargetHits [MAXIMUM_TARGET_INDEX];
|
|
|
|
ULONG AVrfpFaultTargetMaximumIndex;
|
|
|
|
//
|
|
// Exclusion ranges for fault injection.
|
|
//
|
|
|
|
#define MAXIMUM_EXCLUSION_INDEX 128
|
|
|
|
ULONG_PTR AVrfpFaultExclusionStart [MAXIMUM_TARGET_INDEX];
|
|
ULONG_PTR AVrfpFaultExclusionEnd [MAXIMUM_TARGET_INDEX];
|
|
ULONG AVrfpFaultExclusionHits [MAXIMUM_TARGET_INDEX];
|
|
|
|
ULONG AVrfpFaultExclusionMaximumIndex;
|
|
|
|
//
|
|
// Fault injection trace history.
|
|
//
|
|
|
|
#define NUMBER_OF_TRACES 128
|
|
|
|
PVOID AVrfpFaultTrace[NUMBER_OF_TRACES][MAX_TRACE_DEPTH];
|
|
|
|
ULONG AVrfpFaultNumberOfTraces = NUMBER_OF_TRACES;
|
|
ULONG AVrfpFaultTraceSize = MAX_TRACE_DEPTH;
|
|
|
|
ULONG AVrfpFaultTraceIndex;
|
|
|
|
//
|
|
// Period amnesty. The period of time when fault injection should
|
|
// be avoided is written from debugger.
|
|
//
|
|
|
|
LARGE_INTEGER AVrfpFaultStartTime;
|
|
ULONG AVrfpFaultPeriodTimeInMsecs;
|
|
|
|
//
|
|
// Lock used to synchronize some low frequency operations (e.g. exports
|
|
// for target/exclusion range manipulation).
|
|
//
|
|
|
|
RTL_CRITICAL_SECTION AVrfpFaultInjectionLock;
|
|
|
|
LOGICAL
|
|
AVrfpIsAddressInTargetRange (
|
|
ULONG_PTR Address
|
|
);
|
|
|
|
LOGICAL
|
|
AVrfpIsAddressInExclusionRange (
|
|
ULONG_PTR Address
|
|
);
|
|
|
|
VOID
|
|
AVrfpLogFaultTrace (
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpInitializeFaultInjectionSupport (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER PerformanceCounter;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Initialize lock used for some fault injection operations.
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize the seed for the random generator.
|
|
//
|
|
|
|
NtQueryPerformanceCounter (&PerformanceCounter, NULL);
|
|
AVrfpFaultSeed = PerformanceCounter.LowPart;
|
|
|
|
NtQuerySystemTime (&AVrfpFaultStartTime);
|
|
|
|
//
|
|
// Touch the break triggers vector so that the compiler
|
|
// does not optimize away the entire structure. Since it
|
|
// is supposed to be modified only from debugger the compiler
|
|
// considers that the array is not needed.
|
|
//
|
|
|
|
RtlZeroMemory (AVrfpBreak, sizeof AVrfpBreak);
|
|
|
|
//
|
|
// Same reason as above.
|
|
//
|
|
|
|
AVrfpFaultTargetMaximumIndex = MAXIMUM_TARGET_INDEX;
|
|
RtlZeroMemory (AVrfpFaultTargetStart, sizeof AVrfpFaultTargetStart);
|
|
RtlZeroMemory (AVrfpFaultTargetEnd, sizeof AVrfpFaultTargetEnd);
|
|
RtlZeroMemory (AVrfpFaultTargetHits, sizeof AVrfpFaultTargetHits);
|
|
|
|
AVrfpFaultTargetStart[0] = 0;
|
|
AVrfpFaultTargetEnd[0] = ~((ULONG_PTR)0);
|
|
|
|
AVrfpFaultExclusionMaximumIndex = MAXIMUM_EXCLUSION_INDEX;
|
|
RtlZeroMemory (AVrfpFaultExclusionStart, sizeof AVrfpFaultExclusionStart);
|
|
RtlZeroMemory (AVrfpFaultExclusionEnd, sizeof AVrfpFaultExclusionEnd);
|
|
RtlZeroMemory (AVrfpFaultExclusionHits, sizeof AVrfpFaultExclusionHits);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
AVrfpShouldFaultInject (
|
|
ULONG Class,
|
|
PVOID Caller
|
|
)
|
|
{
|
|
ULONG Random;
|
|
LARGE_INTEGER Time;
|
|
LARGE_INTEGER Delta;
|
|
|
|
//
|
|
// No fault injection => return FALSE
|
|
//
|
|
|
|
if (AVrfpFaultProbability[Class] == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if some period amnesty was set. `AVrfpFaultPeriodTimeInMsecs' variable
|
|
// is only read and reset to zero from verifier code. It is set to non null values
|
|
// only from debugger extensions. Therefore to way it is used before without
|
|
// serialization is ok even if after the `if' condition another thread resets it.
|
|
//
|
|
|
|
if (AVrfpFaultPeriodTimeInMsecs) {
|
|
|
|
NtQuerySystemTime (&Time);
|
|
|
|
Delta.QuadPart = (DWORDLONG)AVrfpFaultPeriodTimeInMsecs * 1000 * 10;
|
|
|
|
if (Time.QuadPart - AVrfpFaultStartTime.QuadPart > Delta.QuadPart) {
|
|
|
|
AVrfpFaultPeriodTimeInMsecs = 0;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If in exclusion range => return FALSE
|
|
//
|
|
|
|
if (AVrfpIsAddressInExclusionRange ((ULONG_PTR)Caller) == TRUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Not in target range => return FALSE
|
|
//
|
|
|
|
if (AVrfpIsAddressInTargetRange ((ULONG_PTR)Caller) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Operations above access only READ-ONLY data (it gets modified
|
|
// only from debugger). From now on though we need synchronized
|
|
// access.
|
|
//
|
|
|
|
Random = RtlRandom (&AVrfpFaultSeed);
|
|
|
|
if (Random % 100 < AVrfpFaultProbability[Class]) {
|
|
|
|
InterlockedIncrement((PLONG)(&(AVrfpFaultTrue[Class])));
|
|
|
|
if (AVrfpFaultBreak[Class]) {
|
|
DbgPrint ("AVRF: fault injecting call made from %p \n", Caller);
|
|
DbgBreakPoint ();
|
|
}
|
|
|
|
AVrfpLogFaultTrace ();
|
|
return TRUE;
|
|
}
|
|
else {
|
|
|
|
InterlockedIncrement((PLONG)(&(AVrfpFaultFalse[Class])));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
AVrfpIsAddressInTargetRange (
|
|
ULONG_PTR Address
|
|
)
|
|
{
|
|
ULONG I;
|
|
|
|
if (Address == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (I = 0; I < AVrfpFaultTargetMaximumIndex; I += 1) {
|
|
|
|
if (AVrfpFaultTargetEnd[I] != 0) {
|
|
|
|
if (AVrfpFaultTargetStart[I] <= Address &&
|
|
AVrfpFaultTargetEnd[I] > Address) {
|
|
|
|
AVrfpFaultTargetHits[I] += 1;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
AVrfpIsAddressInExclusionRange (
|
|
ULONG_PTR Address
|
|
)
|
|
{
|
|
ULONG I;
|
|
|
|
if (Address == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (I = 0; I < AVrfpFaultExclusionMaximumIndex; I += 1) {
|
|
|
|
if (AVrfpFaultExclusionEnd[I] != 0) {
|
|
|
|
if (AVrfpFaultExclusionStart[I] <= Address &&
|
|
AVrfpFaultExclusionEnd[I] > Address) {
|
|
|
|
AVrfpFaultExclusionHits[I] += 1;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AVrfpLogFaultTrace (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Index;
|
|
|
|
Index = (ULONG)InterlockedIncrement ((PLONG)(&AVrfpFaultTraceIndex));
|
|
|
|
Index %= AVrfpFaultNumberOfTraces;
|
|
|
|
RtlCaptureStackBackTrace (2,
|
|
AVrfpFaultTraceSize,
|
|
&(AVrfpFaultTrace[Index][0]),
|
|
NULL);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////// Fault injection general SDK
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
VOID
|
|
VerifierSetFaultInjectionProbability (
|
|
ULONG Class,
|
|
ULONG Probability
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine set fault injection probability for a certain class of events
|
|
(heap operations, registry operations, etc.).
|
|
|
|
Arguments:
|
|
|
|
Class - class of events for which fault injection probability is set. Constants
|
|
are of type FAULT_INJECTION_CLASS_XXX.
|
|
|
|
Probability - probability for fault injection.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Application verifier must be enabled.
|
|
//
|
|
|
|
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (Class >= FAULT_INJECTION_INVALID_CLASS) {
|
|
|
|
DbgPrint ("AVRF:FINJ: invalid fault injection class %X \n", Class);
|
|
DbgBreakPoint ();
|
|
return;
|
|
}
|
|
|
|
RtlEnterCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
AVrfpFaultProbability [Class] = Probability;
|
|
|
|
RtlLeaveCriticalSection (&AVrfpFaultInjectionLock);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Target/exclusion fault injection ranges SDK
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
ULONG
|
|
VerifierEnableFaultInjectionTargetRange (
|
|
PVOID StartAddress,
|
|
PVOID EndAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine establishes at runtime a fault injection target range. If successful
|
|
it will return a range index that can be used later to disable the range.
|
|
|
|
Arguments:
|
|
|
|
StartAddress - start address of the target range.
|
|
|
|
EndAddress - end address of the target range.
|
|
|
|
Return Value:
|
|
|
|
A range index >0 if succesful. Zero otherwise.
|
|
|
|
--*/
|
|
{
|
|
ULONG Ri;
|
|
ULONG FinalIndex;
|
|
|
|
//
|
|
// Application verifier must be enabled.
|
|
//
|
|
|
|
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
FinalIndex = 0;
|
|
|
|
RtlEnterCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
if (AVrfpFaultTargetStart[0] == 0 &&
|
|
AVrfpFaultTargetEnd[0] == ~((ULONG_PTR)0)) {
|
|
|
|
AVrfpFaultTargetStart[0] = (ULONG_PTR)StartAddress;
|
|
AVrfpFaultTargetEnd[0] = (ULONG_PTR)EndAddress;
|
|
AVrfpFaultTargetHits[0] = 0;
|
|
FinalIndex = 1;
|
|
}
|
|
else {
|
|
|
|
for (Ri = 0; Ri < AVrfpFaultTargetMaximumIndex; Ri += 1) {
|
|
|
|
if (AVrfpFaultTargetEnd[Ri] == 0) {
|
|
|
|
AVrfpFaultTargetStart[Ri] = (ULONG_PTR)StartAddress;
|
|
AVrfpFaultTargetEnd[Ri] = (ULONG_PTR)EndAddress;
|
|
AVrfpFaultTargetHits[Ri] = 0;
|
|
FinalIndex = Ri + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
return FinalIndex;
|
|
}
|
|
|
|
|
|
VOID
|
|
VerifierDisableFaultInjectionTargetRange (
|
|
ULONG RangeIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disables the target range specified by the RangeIndex.
|
|
If the RangeIndex is zero then all target ranges will be disabled.
|
|
The function breaks in the debugger (even in free builds) if the
|
|
range index is invalid.
|
|
|
|
Arguments:
|
|
|
|
RangeIndex - index of range to disable or zero if all need to be disabled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG Ri;
|
|
LOGICAL FoundOne;
|
|
|
|
//
|
|
// Application verifier must be enabled.
|
|
//
|
|
|
|
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) {
|
|
return;
|
|
}
|
|
|
|
RtlEnterCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
if (RangeIndex == 0) {
|
|
|
|
//
|
|
// Disable all target ranges.
|
|
//
|
|
|
|
for (Ri = 0; Ri < AVrfpFaultTargetMaximumIndex; Ri += 1) {
|
|
|
|
AVrfpFaultTargetStart[Ri] = 0;
|
|
AVrfpFaultTargetEnd[Ri] = 0;
|
|
AVrfpFaultTargetHits[Ri] = 0;
|
|
}
|
|
|
|
AVrfpFaultTargetStart[0] = 0;
|
|
AVrfpFaultTargetEnd[0] = ~((ULONG_PTR)0);
|
|
AVrfpFaultTargetHits[0] = 0;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// disable target range `RangeIndex - 1'.
|
|
//
|
|
|
|
RangeIndex -= 1;
|
|
|
|
if (RangeIndex >= AVrfpFaultTargetMaximumIndex) {
|
|
|
|
DbgPrint ("AVRF:FINJ: invalid target range index %X \n", RangeIndex);
|
|
DbgBreakPoint ();
|
|
goto Exit;
|
|
}
|
|
|
|
if (AVrfpFaultTargetEnd[RangeIndex] == 0) {
|
|
|
|
DbgPrint ("AVRF:FINJ: disabling empty target range at index %X \n", RangeIndex);
|
|
DbgBreakPoint ();
|
|
goto Exit;
|
|
}
|
|
|
|
AVrfpFaultTargetStart[RangeIndex] = 0;
|
|
AVrfpFaultTargetEnd[RangeIndex] = 0;
|
|
AVrfpFaultTargetHits[RangeIndex] = 0;
|
|
|
|
//
|
|
// If we do not have any target ranges active then establish the default
|
|
// target range that spans the entire virtual space.
|
|
//
|
|
|
|
FoundOne = FALSE;
|
|
|
|
for (Ri = 0; Ri < AVrfpFaultTargetMaximumIndex; Ri += 1) {
|
|
|
|
if (AVrfpFaultTargetEnd[Ri] != 0) {
|
|
|
|
FoundOne = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! FoundOne) {
|
|
|
|
AVrfpFaultTargetStart[0] = 0;
|
|
AVrfpFaultTargetEnd[0] = ~((ULONG_PTR)0);
|
|
AVrfpFaultTargetHits[0] = 0;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
RtlLeaveCriticalSection (&AVrfpFaultInjectionLock);
|
|
}
|
|
|
|
|
|
ULONG
|
|
VerifierEnableFaultInjectionExclusionRange (
|
|
PVOID StartAddress,
|
|
PVOID EndAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine establishes at runtime a fault injection exclusion range. If successful
|
|
it will return a range index that can be used later to disable the range.
|
|
|
|
Arguments:
|
|
|
|
StartAddress - start address of the exclusion range.
|
|
|
|
EndAddress - end address of the exclusion range.
|
|
|
|
Return Value:
|
|
|
|
A range index >0 if succesful. Zero otherwise.
|
|
|
|
--*/
|
|
{
|
|
ULONG Ri;
|
|
ULONG FinalIndex;
|
|
|
|
//
|
|
// Application verifier must be enabled.
|
|
//
|
|
|
|
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
FinalIndex = 0;
|
|
|
|
RtlEnterCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
for (Ri = 0; Ri < AVrfpFaultExclusionMaximumIndex; Ri += 1) {
|
|
|
|
if (AVrfpFaultExclusionEnd[Ri] == 0) {
|
|
|
|
AVrfpFaultExclusionStart[Ri] = (ULONG_PTR)StartAddress;
|
|
AVrfpFaultExclusionEnd[Ri] = (ULONG_PTR)EndAddress;
|
|
AVrfpFaultExclusionHits[Ri] = 0;
|
|
FinalIndex = Ri + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
return FinalIndex;
|
|
}
|
|
|
|
|
|
VOID
|
|
VerifierDisableFaultInjectionExclusionRange (
|
|
ULONG RangeIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disables the exclusion range specified by the RangeIndex.
|
|
If the RangeIndex is zero then all exclusion ranges will be disabled.
|
|
The function breaks in the debugger (even in free builds) if the
|
|
range index is invalid.
|
|
|
|
Arguments:
|
|
|
|
RangeIndex - index of range to disable or zero if all need to be disabled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG Ri;
|
|
|
|
//
|
|
// Application verifier must be enabled.
|
|
//
|
|
|
|
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) {
|
|
return;
|
|
}
|
|
|
|
RtlEnterCriticalSection (&AVrfpFaultInjectionLock);
|
|
|
|
if (RangeIndex == 0) {
|
|
|
|
//
|
|
// Disable all exclusion ranges.
|
|
//
|
|
|
|
for (Ri = 0; Ri < AVrfpFaultExclusionMaximumIndex; Ri += 1) {
|
|
|
|
AVrfpFaultExclusionStart[Ri] = 0;
|
|
AVrfpFaultExclusionEnd[Ri] = 0;
|
|
AVrfpFaultExclusionHits[Ri] = 0;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// disable exclusion range `RangeIndex - 1'.
|
|
//
|
|
|
|
RangeIndex -= 1;
|
|
|
|
if (RangeIndex >= AVrfpFaultExclusionMaximumIndex) {
|
|
|
|
DbgPrint ("AVRF:FINJ: invalid exclusion range index %X \n", RangeIndex);
|
|
DbgBreakPoint ();
|
|
goto Exit;
|
|
}
|
|
|
|
if (AVrfpFaultExclusionEnd[RangeIndex] == 0) {
|
|
|
|
DbgPrint ("AVRF:FINJ: disabling empty exclusion range at index %X \n", RangeIndex);
|
|
DbgBreakPoint ();
|
|
goto Exit;
|
|
}
|
|
|
|
AVrfpFaultExclusionStart[RangeIndex] = 0;
|
|
AVrfpFaultExclusionEnd[RangeIndex] = 0;
|
|
AVrfpFaultExclusionHits[RangeIndex] = 0;
|
|
}
|
|
|
|
Exit:
|
|
|
|
RtlLeaveCriticalSection (&AVrfpFaultInjectionLock);
|
|
}
|
|
|
|
|