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.
1093 lines
30 KiB
1093 lines
30 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
logging.c
|
|
|
|
Abstract:
|
|
|
|
Verifier logging and verifier stop logic.
|
|
|
|
Author:
|
|
|
|
Silviu Calinoiu (SilviuC) 9-May-2002
|
|
Daniel Mihai (DMihai) 9-May-2002
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include "verifier.h"
|
|
#include "logging.h"
|
|
#include "support.h"
|
|
|
|
//
|
|
// Verifier stop data.
|
|
//
|
|
|
|
ULONG_PTR AVrfpPreviousStopData[5];
|
|
ULONG_PTR AVrfpStopData[5];
|
|
LIST_ENTRY AVrfpDisabledStopsList;
|
|
ULONG AVrfpNumberOfStopsDisabled;
|
|
|
|
//
|
|
// AVrfpStopDataLock is used to protect any access to
|
|
// AVrfpPreviousStopData, AVrfpStopData and AVrfpDisabledStopsList.
|
|
//
|
|
|
|
RTL_CRITICAL_SECTION AVrfpStopDataLock;
|
|
|
|
//
|
|
// Set this value to 0 in the debugger to see duplicate stops.
|
|
//
|
|
|
|
LOGICAL AVrfpAutomaticallyDisableStops = TRUE;
|
|
|
|
//
|
|
// If true then logging was successfully initialized and can be used.
|
|
// It is checked in VerifierLogMessage() to make sure we can log.
|
|
//
|
|
|
|
LOGICAL AVrfpLoggingInitialized;
|
|
|
|
//
|
|
// If true then logging was successfully initialized and it should be
|
|
// used instead of the verifier stop debugger messages. It is used
|
|
// in VerifierStopMessage().
|
|
//
|
|
|
|
LOGICAL AVrfpLoggingEnabled;
|
|
|
|
//
|
|
// True if process termination has been initiated after a
|
|
// noncontinuable verifier stop.
|
|
//
|
|
|
|
LOGICAL AVrfpProcessBeingTerminated;
|
|
|
|
//
|
|
// Logging structures.
|
|
//
|
|
|
|
UNICODE_STRING AVrfpLoggingNtPath;
|
|
WCHAR AVrfpLoggingPathBuffer [DOS_MAX_PATH_LENGTH];
|
|
WCHAR AVrfpVariableValueBuffer [DOS_MAX_PATH_LENGTH];
|
|
|
|
#define MESSAGE_BUFFER_LENGTH 1024
|
|
CHAR AVrfpLoggingMessageBuffer [MESSAGE_BUFFER_LENGTH];
|
|
|
|
ULONG AVrfpLoggingFailures;
|
|
|
|
PWSTR AVrfpProcessFullName;
|
|
|
|
//
|
|
// Strings used for logging.
|
|
//
|
|
|
|
#define STR_VRF_LOG_STOP_MESSAGE "\r\n# LOGENTRY VERIFIER STOP %p: pid 0x%X: %s \r\n" \
|
|
"# DESCRIPTION BEGIN \r\n" \
|
|
"\t%p : %s\r\n\t%p : %s\r\n\t%p : %s\r\n\t%p : %s\r\n" \
|
|
"# DESCRIPTION END \r\n"
|
|
#define STR_VRF_DBG_STOP_MESSAGE "\n\n" \
|
|
"===========================================================\n" \
|
|
"VERIFIER STOP %p: pid 0x%X: %s \n" \
|
|
"\n\t%p : %s\n\t%p : %s\n\t%p : %s\n\t%p : %s\n" \
|
|
"===========================================================\n" \
|
|
"%s\n" \
|
|
"===========================================================\n\n"
|
|
#define STR_VRF_LOG_NOCONTINUE_MESSAGE "\r\n# LOGENTRY VERIFIER: noncontinuable verifier stop" \
|
|
" %p encountered. Terminating process. \r\n"
|
|
#define STR_VRF_DBG_NOCONTINUE_MESSAGE "AVRF: Noncontinuable verifier stop %p encountered. " \
|
|
"Terminating process ... \n"
|
|
#define STR_VRF_LOG_STACK_CHECKS_WARN "# LOGENTRY VERIFIER WARNING: pid 0x%X: " \
|
|
"stack checks have been disabled \r\n" \
|
|
"# DESCRIPTION BEGIN \r\n" \
|
|
"Stack checks require a debugger attached to the verified process. \r\n" \
|
|
"# DESCRIPTION END \r\n"
|
|
|
|
#define STR_VRF_LOG_INITIAL_MESSAGE "# LOG_BEGIN `%u/%u/%u %u:%u:%u.%u' `%ws' \r\n"
|
|
#define STR_VRF_LOG_INITIAL_SETTINGS "# DESCRIPTION BEGIN \r\n" \
|
|
" Global flags: 0x%08X \r\n" \
|
|
" Verifier flags: 0x%08X \r\n" \
|
|
" Process debugger attached: %s \r\n" \
|
|
" Kernel debugger enabled: %s \r\n" \
|
|
" Log path: %ws \r\n" \
|
|
"# DESCRIPTION END \r\n"
|
|
|
|
//
|
|
// Forward declarations.
|
|
//
|
|
|
|
LOGICAL
|
|
AVrfpIsCurrentStopDisabled (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
AVrfpDisableCurrentStop (
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
AVrfpCreateLogFile (
|
|
VOID
|
|
);
|
|
|
|
int __cdecl _vsnprintf(char *, size_t, const char *, va_list);
|
|
int __cdecl _snwprintf (wchar_t *, size_t, const wchar_t *, ...);
|
|
|
|
VOID
|
|
AVrfpLogInitialMessage (
|
|
VOID
|
|
);
|
|
|
|
LOGICAL
|
|
AVrfpIsDebuggerPresent (
|
|
VOID
|
|
);
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////// Application verifier stops
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
VerifierStopMessage (
|
|
ULONG_PTR Code,
|
|
PCHAR Message,
|
|
ULONG_PTR Param1, PCHAR Description1,
|
|
ULONG_PTR Param2, PCHAR Description2,
|
|
ULONG_PTR Param3, PCHAR Description3,
|
|
ULONG_PTR Param4, PCHAR Description4
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine is called by various verifier components to report errors found.
|
|
The message is logged into the verifier log associated with the process and
|
|
also printed in the debugger console.
|
|
|
|
There are two flags that can be OR'd into the verifier stop code to modify the
|
|
behavior:
|
|
|
|
APPLICATION_VERIFIER_DO_NOT_BREAK - if this bit is set then the verifier stop is
|
|
logged in the log and dumped in the debugger console and then the thread execution
|
|
continues. For all intents and purposes this is considered a continuable stop.
|
|
|
|
APPLICATION_VERIFIER_CONTINUABLE_BREAK - if this bit is set the stop is continuable.
|
|
The stop is logged and and then a breakpoint gets executed. After the user continues
|
|
the execution this verifier stop will be skipped.
|
|
|
|
If none of the flags above is set the stop is considered non-continuable. In this case
|
|
the stop is logged in the log aND dumped in the debugger console and then the process
|
|
will be terminated. A final log entry will be logged to explain this action.
|
|
Hopefully in time most of the stop codes will be continuable.
|
|
|
|
Parameters:
|
|
|
|
Code: Verifier stop code. The two flags described above can be OR'd into the code
|
|
to change the behavior of the API. The verifier stop codes are defined in
|
|
\base\published\nturtl.w and described in \base\win32\verifier\verifier_stop.doc.
|
|
|
|
Message: Ascii string describing the failure. It is considered bad style to use several
|
|
different messages with the same `Code'. Every different issue should have its own
|
|
unique (Code, Message) pair.
|
|
|
|
Param1, Description1: First arbitrary pointer to information and ascii description.
|
|
|
|
Param2, Description2: Second arbitrary pointer to information and ascii description.
|
|
|
|
Param3, Description3: Third arbitrary pointer to information and ascii description.
|
|
|
|
Param4, Description4: Fourth arbitrary pointer to information and ascii description.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LOGICAL DoNotBreak = FALSE;
|
|
LOGICAL StopIsDisabled = FALSE;
|
|
NTSTATUS Status;
|
|
LOGICAL MustExitProcess = FALSE;
|
|
LOGICAL ContinuableBreak = FALSE;
|
|
LOGICAL BreakWasContinued = FALSE;
|
|
PCHAR ContinueMessage;
|
|
|
|
//
|
|
// While process is getting terminated (due to a previous verifier stop)
|
|
// we do not allow any new logging or dumping to debugger console.
|
|
//
|
|
|
|
if (AVrfpProcessBeingTerminated) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Extract options from the stop code.
|
|
//
|
|
|
|
if ((Code & APPLICATION_VERIFIER_NO_BREAK)) {
|
|
|
|
DoNotBreak = TRUE;
|
|
Code &= ~APPLICATION_VERIFIER_NO_BREAK;
|
|
|
|
//
|
|
// A no_break is by design continuable.
|
|
//
|
|
|
|
ContinuableBreak = TRUE;
|
|
}
|
|
|
|
if ((Code & APPLICATION_VERIFIER_CONTINUABLE_BREAK)) {
|
|
|
|
ContinuableBreak = TRUE;
|
|
Code &= ~APPLICATION_VERIFIER_CONTINUABLE_BREAK;
|
|
}
|
|
|
|
//
|
|
// Serialize multi-threaded access to the stop data.
|
|
//
|
|
|
|
RtlEnterCriticalSection (&AVrfpStopDataLock);
|
|
|
|
//
|
|
// Make it easy for a debugger to pick up the failure info.
|
|
//
|
|
|
|
RtlCopyMemory (AVrfpPreviousStopData,
|
|
AVrfpStopData,
|
|
sizeof AVrfpStopData);
|
|
|
|
AVrfpStopData[0] = Code;
|
|
AVrfpStopData[1] = Param1;
|
|
AVrfpStopData[2] = Param2;
|
|
AVrfpStopData[3] = Param3;
|
|
AVrfpStopData[4] = Param4;
|
|
|
|
//
|
|
// Check if the current stop is disabled.
|
|
//
|
|
|
|
if (AVrfpAutomaticallyDisableStops != FALSE) {
|
|
|
|
StopIsDisabled = AVrfpIsCurrentStopDisabled ();
|
|
}
|
|
|
|
//
|
|
// If stop has not been encountered before we need to report it
|
|
// in the debugger console and the verifier log.
|
|
//
|
|
|
|
if (StopIsDisabled == FALSE) {
|
|
|
|
if (AVrfpLoggingEnabled) {
|
|
|
|
VerifierLogMessage (STR_VRF_LOG_STOP_MESSAGE,
|
|
Code, RtlGetCurrentProcessId(), Message,
|
|
Param1, Description1,
|
|
Param2, Description2,
|
|
Param3, Description3,
|
|
Param4, Description4);
|
|
}
|
|
|
|
if (ContinuableBreak) {
|
|
|
|
ContinueMessage = "This verifier stop is continuable. \n"
|
|
"After debugging it use `go' to continue.";
|
|
}
|
|
else {
|
|
|
|
ContinueMessage = "This verifier stop is not continuable. Process will be terminated \n"
|
|
"when you use the `go' debugger command.";
|
|
}
|
|
|
|
DbgPrint (STR_VRF_DBG_STOP_MESSAGE,
|
|
Code, RtlGetCurrentProcessId(), Message,
|
|
Param1, Description1,
|
|
Param2, Description2,
|
|
Param3, Description3,
|
|
Param4, Description4,
|
|
ContinueMessage);
|
|
|
|
if (DoNotBreak == FALSE) {
|
|
|
|
//
|
|
// We do not really break if there is not a debugger around. If we do it
|
|
// there will be an unhandle breakpoint exception in the process that
|
|
// will be picked up by PC-Health. Since we do not break it will be as if
|
|
// someone hit `go' in the debugger.
|
|
//
|
|
|
|
if (AVrfpIsDebuggerPresent() == TRUE) {
|
|
DbgBreakPoint ();
|
|
}
|
|
|
|
BreakWasContinued = TRUE;
|
|
}
|
|
|
|
//
|
|
// If the stop is not continuable (including the `donotbreak' flavor)
|
|
// then we need to terminate the process. Otherwise register the current
|
|
// stop as disabled so that we do not see it over and over again.
|
|
//
|
|
|
|
if (ContinuableBreak == FALSE && DoNotBreak == FALSE) {
|
|
|
|
MustExitProcess = TRUE;
|
|
}
|
|
else {
|
|
|
|
if (AVrfpAutomaticallyDisableStops) {
|
|
|
|
AVrfpDisableCurrentStop ();
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection (&AVrfpStopDataLock);
|
|
|
|
if (MustExitProcess) {
|
|
|
|
//
|
|
// Hopefully in the future most of the verifier stops will be
|
|
// continuable. Right now we just terminate the process.
|
|
//
|
|
|
|
if (AVrfpLoggingEnabled) {
|
|
VerifierLogMessage (STR_VRF_LOG_NOCONTINUE_MESSAGE, Code);
|
|
}
|
|
|
|
DbgPrint (STR_VRF_DBG_NOCONTINUE_MESSAGE, Code);
|
|
|
|
AVrfpProcessBeingTerminated = TRUE;
|
|
|
|
Status = NtTerminateProcess (NtCurrentProcess(), STATUS_UNSUCCESSFUL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DbgPrint ("AVRF: Terminate process after verifier stop failed with %X \n", Status);
|
|
DbgBreakPoint ();
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////// Logging API
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
VerifierLogMessage (
|
|
PCHAR Format,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine tries to open (non-shareable) the verifier log file
|
|
associated with the current process. If it cannot do it because it is
|
|
opened for someone else it will retry a few times with a delay in between.
|
|
This way it will effectively wait for some other thread that is currently
|
|
logging. Other tools that try to look at the log while a process is running
|
|
will have to do it quickly if they do not want to affect the logging. Since
|
|
logging is a rare event this scheme seems to me solid enough. The function
|
|
is designed to survive the situation where someone keeps the file open for
|
|
too long by just skipping log messages.
|
|
|
|
Parameters:
|
|
|
|
Format: string format parameters a la printf.
|
|
|
|
...: rest of the prinf-like parameters.
|
|
|
|
Return value:
|
|
|
|
None. All errors encountered in the function are supposed to be continuable.
|
|
|
|
--*/
|
|
{
|
|
va_list Params;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE LogHandle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER Offset;
|
|
LONG MessageSize;
|
|
ULONG OpenFlags;
|
|
ULONG RetryCount;
|
|
LARGE_INTEGER SleepTime;
|
|
|
|
va_start (Params, Format);
|
|
|
|
if (AVrfpLoggingInitialized == FALSE) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
OpenFlags = FILE_OPEN;
|
|
|
|
SleepTime.QuadPart = - (10 * 1000 * 1000 * 1); // 1 sec.
|
|
|
|
//
|
|
// Attempt to get a handle to our log file.
|
|
//
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&AVrfpLoggingNtPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
RetryCount = 0;
|
|
|
|
//
|
|
// If somebody is actively logging into the file we will keep
|
|
// looping for a while until the handle is closed or we tried enough
|
|
// and did not succeed. This offers synchronization between competing
|
|
// threads logging simultaneously.
|
|
//
|
|
|
|
do {
|
|
|
|
Status = NtCreateFile (&LogHandle,
|
|
FILE_APPEND_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
OpenFlags,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
if (Status == STATUS_SHARING_VIOLATION) {
|
|
|
|
NtDelayExecution (FALSE, &SleepTime);
|
|
RetryCount += 1;
|
|
}
|
|
|
|
} while (Status == STATUS_SHARING_VIOLATION && RetryCount < 5);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
if (Status == STATUS_SHARING_VIOLATION) {
|
|
|
|
DbgPrint ("AVRF: verifier log file %ws kept open for too long (status %X)\n",
|
|
AVrfpLoggingNtPath.Buffer,
|
|
Status);
|
|
}
|
|
else {
|
|
|
|
DbgPrint ("AVRF: failed to open verifier log file %ws (status %X)\n",
|
|
AVrfpLoggingNtPath.Buffer,
|
|
Status);
|
|
}
|
|
|
|
AVrfpLoggingFailures += 1;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Prepare and write the message. Write the data out to the file.
|
|
// Synchronization to the preparation buffer is assured by the log file
|
|
// handle opened in non-sharable mode which means no one can be in the same
|
|
// state (writing into the buffer) right now.
|
|
//
|
|
|
|
IoStatusBlock.Status = 0;
|
|
IoStatusBlock.Information = 0;
|
|
|
|
Offset.LowPart = 0;
|
|
Offset.HighPart = 0;
|
|
|
|
MessageSize = _vsnprintf (AVrfpLoggingMessageBuffer,
|
|
MESSAGE_BUFFER_LENGTH,
|
|
Format,
|
|
Params);
|
|
if (MessageSize < 0) {
|
|
|
|
DbgPrint ("AVRF: failed in _vsnprintf() to prepare log message\n");
|
|
|
|
AVrfpLoggingFailures += 1;
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = NtWriteFile (LogHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PVOID)AVrfpLoggingMessageBuffer,
|
|
MessageSize,
|
|
&Offset,
|
|
NULL);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
//
|
|
// We need to wait for the operation to complete.
|
|
//
|
|
|
|
Status = NtWaitForSingleObject (LogHandle, FALSE, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If this happens we need to debug it.
|
|
//
|
|
|
|
DbgPrint ("AVRF: Wait for pending write I/O operation failed with %X \n", Status);
|
|
DbgBreakPoint ();
|
|
}
|
|
}
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
DbgPrint ("AVRF: failed to write into verifier log file %ws (status %X)\n",
|
|
AVrfpLoggingNtPath.Buffer,
|
|
Status);
|
|
|
|
AVrfpLoggingFailures += 1;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
NtClose (LogHandle);
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////// Stop disabling
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
AVrfpDisableCurrentStop (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine inserts the values from AVrfpStopData
|
|
into the list of disabled stops.
|
|
|
|
Parameters:
|
|
|
|
None, using global AVrfpStopData and AVrfpDisabledStopsList.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
Environment:
|
|
|
|
User mode, AVrfpStopDataLock held by the caller.
|
|
|
|
--*/
|
|
{
|
|
PAVRFP_STOP_DATA StopData;
|
|
|
|
StopData = AVrfpAllocate (sizeof *StopData);
|
|
|
|
if (StopData != NULL) {
|
|
|
|
ASSERT (sizeof (AVrfpStopData) == sizeof (StopData->Data));
|
|
|
|
RtlCopyMemory (&StopData->Data,
|
|
AVrfpStopData,
|
|
sizeof AVrfpStopData);
|
|
|
|
InsertHeadList (&AVrfpDisabledStopsList,
|
|
&StopData->ListEntry);
|
|
|
|
AVrfpNumberOfStopsDisabled += 1;
|
|
}
|
|
}
|
|
|
|
|
|
LOGICAL
|
|
AVrfpIsCurrentStopDisabled (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine is searching for the stop data from AVrfpStopData
|
|
in the list of disabled stops.
|
|
|
|
Parameters:
|
|
|
|
None, using global AVrfpStopData and AVrfpDisabledStopsList.
|
|
|
|
Return value:
|
|
|
|
TRUE if the current stop is disabled, FALSE otherwise.
|
|
|
|
Environment:
|
|
|
|
User mode, AVrfpStopDataLock held by the caller.
|
|
|
|
--*/
|
|
{
|
|
LOGICAL Disabled;
|
|
PAVRFP_STOP_DATA StopData;
|
|
PLIST_ENTRY Entry;
|
|
ULONG Index;
|
|
|
|
Disabled = FALSE;
|
|
|
|
ASSERT (sizeof (AVrfpStopData) == sizeof (StopData->Data));
|
|
ASSERT (sizeof (AVrfpStopData[0]) == sizeof (StopData->Data[0]));
|
|
|
|
for (Entry = AVrfpDisabledStopsList.Flink;
|
|
Entry != &AVrfpDisabledStopsList;
|
|
Entry = Entry->Flink) {
|
|
|
|
StopData = CONTAINING_RECORD (Entry,
|
|
AVRFP_STOP_DATA,
|
|
ListEntry);
|
|
|
|
Disabled = TRUE;
|
|
|
|
for (Index = 0; Index < sizeof (AVrfpStopData) / sizeof (AVrfpStopData[0]); Index += 1) {
|
|
|
|
if (AVrfpStopData[Index] != StopData->Data[Index]) {
|
|
|
|
Disabled = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Disabled != FALSE) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Disabled;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////// Initialization
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
LOGICAL
|
|
AVrfpIsDebuggerPresent (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine checks out if we have any kind of debuggers active.
|
|
Note that we cannot do this check only once during process
|
|
initialization because debuggers can be attached and detached
|
|
from a process while the process is running.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
TRUE if a user mode debugger is attached to the current process or
|
|
kernel mode debugger is enabled.
|
|
|
|
--*/
|
|
{
|
|
|
|
if (NtCurrentPeb()->BeingDebugged) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (USER_SHARED_DATA->KdDebuggerEnabled) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpInitializeVerifierStops (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine initializes verifier stops logic.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if enabled successfully. Various errors
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
InitializeListHead (&AVrfpDisabledStopsList);
|
|
|
|
Status = RtlInitializeCriticalSection (&AVrfpStopDataLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpInitializeVerifierLogging (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine initializes verifier structures for logging. It is called
|
|
during verifier engine initialization (early process stage).
|
|
|
|
Stops will happen if a debugger is present and logging was
|
|
not requested explicitely. Al other combinations will enable
|
|
logging. In addition if no user mode debugger is attached the
|
|
stack overflow checking is disabled altogether.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if logging was enabled successfully. Various errors
|
|
otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Create the log file.
|
|
//
|
|
|
|
Status = AVrfpCreateLogFile ();
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// We are done now we can mark the logging initialization as successful.
|
|
//
|
|
|
|
AVrfpLoggingInitialized = TRUE;
|
|
|
|
//
|
|
// Stack overflow checking gets disabled if we no debugger attached because
|
|
// it is impossible to recover from the failure and we cannot intercept
|
|
// it to present a decent debugging message.
|
|
//
|
|
|
|
if (AVrfpProvider.VerifierFlags & RTL_VRF_FLG_STACK_CHECKS) {
|
|
|
|
if (AVrfpIsDebuggerPresent() == FALSE) {
|
|
|
|
VerifierLogMessage (STR_VRF_LOG_STACK_CHECKS_WARN,
|
|
RtlGetCurrentProcessId());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log startup information.
|
|
//
|
|
|
|
AVrfpLogInitialMessage ();
|
|
|
|
//
|
|
// Logging is always enabled except if the verifier is enabled system-wide.
|
|
// For that case this function is not even called.
|
|
//
|
|
|
|
AVrfpLoggingEnabled = TRUE;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVrfpCreateLogFile (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine tries to create a log file unique for the current process.
|
|
The path of the log file is either read from VERIFIER_LOG_PATH environment
|
|
variable or the default value `%ALLUSERSPROFILE%\Documents\AppVerifierLogs'
|
|
is used. The syntax of the log file name is `IMAGENAME.UNIQUEID.log'. The
|
|
IMAGENAME includes the extension since there are executable files that have
|
|
extensions different than .exe (e.g. .scr for screensavers).
|
|
|
|
The routine will keep incrementing an integer ID (starting from zero) until
|
|
it manages to create a file that did not exist before.
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS if it was successful in creating an unique log file for this
|
|
process. Various status errors otherwise.
|
|
|
|
--*/
|
|
{
|
|
LOGICAL Success;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE LogHandle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONG OpenFlags;
|
|
PWSTR ProcessName;
|
|
ULONG FileNameId;
|
|
UNICODE_STRING LogPathVariableName;
|
|
UNICODE_STRING LogPath;
|
|
LOGICAL DefaultLogPath;
|
|
PPEB Peb;
|
|
|
|
OpenFlags = FILE_CREATE;
|
|
|
|
FileNameId = 0;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
{
|
|
//
|
|
// We need to find out the full path to the image being executed.
|
|
// It is safe to read loader structures since this function
|
|
// is called from verifier!DllMain and the loader lock is owned
|
|
// by the current thread when this happens. This is the lock that
|
|
// protects access to the structures.
|
|
//
|
|
|
|
PPEB_LDR_DATA Ldr;
|
|
PLIST_ENTRY Head;
|
|
PLIST_ENTRY Next;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
|
|
Ldr = Peb->Ldr;
|
|
Head = &Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
Entry = CONTAINING_RECORD (Next,
|
|
LDR_DATA_TABLE_ENTRY,
|
|
InLoadOrderLinks);
|
|
|
|
ProcessName = Entry->BaseDllName.Buffer;
|
|
|
|
AVrfpProcessFullName = Entry->FullDllName.Buffer;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
DefaultLogPath = FALSE;
|
|
|
|
//
|
|
// Get the value of `VERIFIER_LOG_PATH' environment variable.
|
|
//
|
|
|
|
RtlInitUnicodeString (&LogPathVariableName,
|
|
L"VERIFIER_LOG_PATH");
|
|
|
|
RtlInitEmptyUnicodeString (&LogPath,
|
|
AVrfpVariableValueBuffer,
|
|
DOS_MAX_PATH_LENGTH);
|
|
|
|
Status = RtlQueryEnvironmentVariable_U (NULL,
|
|
&LogPathVariableName,
|
|
&LogPath);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Get the value of `AllUsersProfile' environment variable.
|
|
//
|
|
|
|
RtlInitUnicodeString (&LogPathVariableName,
|
|
L"ALLUSERSPROFILE");
|
|
|
|
RtlInitEmptyUnicodeString (&LogPath,
|
|
AVrfpVariableValueBuffer,
|
|
DOS_MAX_PATH_LENGTH);
|
|
|
|
Status = RtlQueryEnvironmentVariable_U (NULL,
|
|
&LogPathVariableName,
|
|
&LogPath);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
DbgPrint ("AVRF: Failed to get environment variable (status %X)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
DefaultLogPath = TRUE;
|
|
}
|
|
|
|
//
|
|
// We try to create a log file with the proper name (given our convention)
|
|
// that is unique for this process. If the file with that name already exists
|
|
// we will get an error and we will try a different name.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Prepare log path name with unique Id appended.
|
|
//
|
|
|
|
if (DefaultLogPath) {
|
|
|
|
_snwprintf (AVrfpLoggingPathBuffer,
|
|
DOS_MAX_PATH_LENGTH - 1,
|
|
L"%ws\\Documents\\AppVerifierLogs\\%ws.%u.log",
|
|
AVrfpVariableValueBuffer,
|
|
ProcessName,
|
|
FileNameId);
|
|
}
|
|
else {
|
|
|
|
_snwprintf (AVrfpLoggingPathBuffer,
|
|
DOS_MAX_PATH_LENGTH - 1,
|
|
L"%ws\\%ws.%u.log",
|
|
AVrfpVariableValueBuffer,
|
|
ProcessName,
|
|
FileNameId);
|
|
}
|
|
|
|
Success = RtlDosPathNameToNtPathName_U (AVrfpLoggingPathBuffer,
|
|
&AVrfpLoggingNtPath,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Success == FALSE) {
|
|
|
|
DbgPrint ("AVRF: Failed to convert to an NT path the verifier log path.\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Attempt to get a handle to our log file.
|
|
//
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&AVrfpLoggingNtPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateFile (&LogHandle,
|
|
FILE_APPEND_DATA,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
OpenFlags,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
if (Status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
FileNameId += 1;
|
|
|
|
RtlFreeUnicodeString (&AVrfpLoggingNtPath);
|
|
}
|
|
|
|
} while (Status == STATUS_OBJECT_NAME_COLLISION);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
DbgPrint ("AVRF: failed to create verifier log file %ws (status %X)\n",
|
|
AVrfpLoggingNtPath.Buffer,
|
|
Status);
|
|
return Status;
|
|
}
|
|
|
|
NtClose (LogHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
AVrfpLogInitialMessage (
|
|
VOID
|
|
)
|
|
{
|
|
LARGE_INTEGER SystemTime;
|
|
LARGE_INTEGER LocalTime;
|
|
TIME_FIELDS TimeFields;
|
|
|
|
//
|
|
// Read system time from shared region.
|
|
//
|
|
|
|
do {
|
|
SystemTime.HighPart = USER_SHARED_DATA->SystemTime.High1Time;
|
|
SystemTime.LowPart = USER_SHARED_DATA->SystemTime.LowPart;
|
|
} while (SystemTime.HighPart != USER_SHARED_DATA->SystemTime.High2Time);
|
|
|
|
//
|
|
// Convert to local time and split into fields.
|
|
//
|
|
|
|
LocalTime.QuadPart = 0;
|
|
RtlSystemTimeToLocalTime (&SystemTime, &LocalTime);
|
|
|
|
RtlZeroMemory (&TimeFields, sizeof TimeFields);
|
|
RtlTimeToTimeFields(&LocalTime,&TimeFields);
|
|
|
|
//
|
|
// Dump time and process full path.
|
|
//
|
|
|
|
VerifierLogMessage (STR_VRF_LOG_INITIAL_MESSAGE,
|
|
(ULONG)TimeFields.Month,
|
|
(ULONG)TimeFields.Day,
|
|
(ULONG)TimeFields.Year,
|
|
(ULONG)TimeFields.Hour,
|
|
(ULONG)TimeFields.Minute,
|
|
(ULONG)TimeFields.Second,
|
|
(ULONG)TimeFields.Milliseconds,
|
|
AVrfpProcessFullName);
|
|
|
|
//
|
|
// Dump settings.
|
|
//
|
|
|
|
VerifierLogMessage (STR_VRF_LOG_INITIAL_SETTINGS,
|
|
NtCurrentPeb()->NtGlobalFlag,
|
|
AVrfpProvider.VerifierFlags,
|
|
(NtCurrentPeb()->BeingDebugged) ? "yes" : "no",
|
|
((USER_SHARED_DATA->KdDebuggerEnabled)) ? "yes" : "no",
|
|
AVrfpLoggingPathBuffer);
|
|
}
|
|
|
|
|