|
|
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
thread.c
Abstract:
This module implements verification functions for thread interfaces.
Author:
Silviu Calinoiu (SilviuC) 22-Feb-2001
Revision History:
Daniel Mihai (DMihai) 25-Apr-2002
Hook thread pool and WMI threads. --*/
#include "pch.h"
#include "verifier.h"
#include "support.h"
#include "logging.h"
#include "tracker.h"
//
// Why do we hook Exit/TerminateThread instead of NtTerminateThread?
//
// Because kernel32 calls NtTerminateThread in legitimate contexts.
// After all this is the implementation for Exit/TerminateThread.
// It would be difficult to discriminate good calls from bad calls.
// So we prefer to intercept Exit/Thread and returns from thread
// functions.
//
//
// Standard function used for hooking a thread function.
//
DWORD WINAPI AVrfpStandardThreadFunction ( LPVOID Info );
//
// Common point to check for thread termination.
//
VOID AVrfpCheckThreadTermination ( HANDLE Thread );
VOID AVrfpCheckCurrentThreadTermination ( VOID );
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//WINBASEAPI
DECLSPEC_NORETURN VOID WINAPI AVrfpExitProcess( IN UINT uExitCode ) { typedef VOID (WINAPI * FUNCTION_TYPE) (UINT); FUNCTION_TYPE Function;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_EXITPROCESS);
//
// Check out if there are other threads running while ExitProcess
// gets called. This can cause problems because the threads are
// terminated unconditionally and then ExitProcess() calls
// LdrShutdownProcess() which will try to notify all DLLs to cleanup.
// During cleanup any number of operations can happen that will result
// in deadlocks since all those threads have been terminated
// unconditionally
//
#if 0
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_DANGEROUS_APIS) != 0) {
PCHAR InfoBuffer; ULONG RequiredLength = 0; ULONG NumberOfThreads; ULONG EntryOffset; PSYSTEM_PROCESS_INFORMATION ProcessInfo; NTSTATUS Status;
Status = NtQuerySystemInformation (SystemProcessInformation, NULL, 0, &RequiredLength);
if (Status == STATUS_INFO_LENGTH_MISMATCH && RequiredLength != 0) { InfoBuffer = AVrfpAllocate (RequiredLength);
if (InfoBuffer) { //
// Note that the RequiredLength is not 100% guaranteed to be good
// since in between the two query calls several other processes
// may have been created. If this is the case we bail out and skip
// the verification.
//
Status = NtQuerySystemInformation (SystemProcessInformation, InfoBuffer, RequiredLength, NULL);
if (NT_SUCCESS(Status)) {
EntryOffset = 0; NumberOfThreads = 0;
do { ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&InfoBuffer[EntryOffset]; if (ProcessInfo->UniqueProcessId == NtCurrentTeb()->ClientId.UniqueProcess) { NumberOfThreads = ProcessInfo->NumberOfThreads; break; } EntryOffset += ProcessInfo->NextEntryOffset;
} while(ProcessInfo->NextEntryOffset != 0);
ASSERT (NumberOfThreads > 0);
if (NumberOfThreads > 1) {
VERIFIER_STOP (APPLICATION_VERIFIER_INVALID_EXIT_PROCESS_CALL | APPLICATION_VERIFIER_NO_BREAK, "ExitProcess() called while multiple threads are running", NumberOfThreads, "Number of threads running", 0, NULL, 0, NULL, 0, NULL);
} } else {
DbgPrint ("AVRF: NtQuerySystemInformation(SystemProcessInformation) " "failed with %X \n", Status);
}
//
// We are done with the buffer. Time to free it.
//
AVrfpFree (InfoBuffer); } } else {
DbgPrint ("AVRF: NtQuerySystemInformation(SystemProcessInformation, null) " "failed with %X \n", Status);
} } #endif // #if 0
//
// Make a note of who called ExitProcess(). This can be helpful for debugging
// weird process shutdown hangs.
//
AVrfLogInTracker (AVrfThreadTracker, TRACK_EXIT_PROCESS, (PVOID)(ULONG_PTR)uExitCode, NULL, NULL, NULL, _ReturnAddress());
//
// Call the real thing.
//
(* Function)(uExitCode);
}
//WINBASEAPI
DECLSPEC_NORETURN VOID WINAPI AVrfpExitThread( IN DWORD dwExitCode ) { typedef VOID (WINAPI * FUNCTION_TYPE) (DWORD); FUNCTION_TYPE Function; PAVRF_THREAD_ENTRY Entry;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_EXITTHREAD);
//
// Perform all typical checks for a thread that will exit.
//
AVrfpCheckCurrentThreadTermination ();
//
// Before calling the real ExitThread we need to free the thread
// entry from the thread table.
//
Entry = AVrfpThreadTableSearchEntry (NtCurrentTeb()->ClientId.UniqueThread);
//
// N.B. It is possible to not find an entry in the thread table if the
// thread was not created using CreateThread but rather some more
// basic function from ntdll.dll.
//
if (Entry != NULL) { AVrfpThreadTableRemoveEntry (Entry); AVrfpFree (Entry); }
//
// Call the real thing.
//
(* Function)(dwExitCode); }
//WINBASEAPI
DECLSPEC_NORETURN VOID WINAPI AVrfpFreeLibraryAndExitThread( IN HMODULE hLibModule, IN DWORD dwExitCode ) { typedef VOID (WINAPI * FUNCTION_TYPE) (HMODULE, DWORD); FUNCTION_TYPE Function; PAVRF_THREAD_ENTRY Entry;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_FREELIBRARYANDEXITTHREAD);
//
// Perform all typical checks for a thread that will exit.
//
AVrfpCheckCurrentThreadTermination ();
//
// Before calling the real FreeLibraryAndExitThread we need to free the thread
// entry from the thread table.
//
Entry = AVrfpThreadTableSearchEntry (NtCurrentTeb()->ClientId.UniqueThread);
//
// N.B. It is possible to not find an entry in the thread table if the
// thread was not created using CreateThread but rather some more
// basic function from ntdll.dll.
//
if (Entry != NULL) { AVrfpThreadTableRemoveEntry (Entry); AVrfpFree (Entry); }
//
// Call the real thing.
//
(* Function)(hLibModule, dwExitCode); }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////// Terminate thread / Suspend thread
/////////////////////////////////////////////////////////////////////
//WINBASEAPI
BOOL WINAPI AVrfpTerminateThread( IN OUT HANDLE hThread, IN DWORD dwExitCode ) { typedef BOOL (WINAPI * FUNCTION_TYPE) (HANDLE, DWORD); FUNCTION_TYPE Function;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_TERMINATETHREAD);
//
// Keep track of who calls TerminateThread() even if we are going to break
// for it. This helps investigations of deadlocked processes that have run
// without the dangerous_apis check enabled.
//
AVrfLogInTracker (AVrfThreadTracker, TRACK_TERMINATE_THREAD, hThread, NULL, NULL, NULL, _ReturnAddress());
//
// Perform all typical checks for a thread that will exit.
//
AVrfpCheckThreadTermination (hThread);
//
// This API should not be called. We need to report this.
// This is useful if we did not detect any orphans but we still want
// to complain.
//
if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_DANGEROUS_APIS) != 0) {
VERIFIER_STOP (APPLICATION_VERIFIER_TERMINATE_THREAD_CALL | APPLICATION_VERIFIER_CONTINUABLE_BREAK, "TerminateThread() called. This function should not be used.", NtCurrentTeb()->ClientId.UniqueThread, "Caller thread ID", 0, NULL, 0, NULL, 0, NULL); }
return (* Function)(hThread, dwExitCode); }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//WINBASEAPI
DWORD WINAPI AVrfpSuspendThread( IN HANDLE hThread ) { typedef DWORD (WINAPI * FUNCTION_TYPE) (HANDLE); FUNCTION_TYPE Function;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_SUSPENDTHREAD);
//
// Keep track of who calls SuspendThread() even if we are not going to break
// for it. This helps investigations of deadlocked processes.
//
AVrfLogInTracker (AVrfThreadTracker, TRACK_SUSPEND_THREAD, hThread, NULL, NULL, NULL, _ReturnAddress());
//
// One might think that we can check for orphan locks at this point
// by calling RtlCheckForOrphanedCriticalSections(hThread).
// Unfortunately this cannot be done because garbage collectors
// for various virtual machines (Java, C#) can do this in valid
// conditions.
//
return (* Function)(hThread); }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//WINBASEAPI
HANDLE WINAPI AVrfpCreateThread( IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN SIZE_T dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId ) /*++
CreateThread hook
--*/ { typedef HANDLE (WINAPI * FUNCTION_TYPE) (LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD); FUNCTION_TYPE Function; HANDLE Result; PAVRF_THREAD_ENTRY Info;
Function = AVRFP_GET_ORIGINAL_EXPORT (AVrfpKernel32Thunks, AVRF_INDEX_KERNEL32_CREATETHREAD);
Info = AVrfpAllocate (sizeof *Info);
if (Info == NULL) { NtCurrentTeb()->LastErrorValue = ERROR_NOT_ENOUGH_MEMORY; return NULL; }
Info->Parameter = lpParameter; Info->Function = lpStartAddress; Info->ParentThreadId = NtCurrentTeb()->ClientId.UniqueThread; Info->StackSize = dwStackSize; Info->CreationFlags = dwCreationFlags;
Result = (* Function) (lpThreadAttributes, dwStackSize, AVrfpStandardThreadFunction, (PVOID)Info, dwCreationFlags, lpThreadId);
if (Result == FALSE) {
AVrfpFree (Info); }
return Result; }
ULONG AVrfpThreadFunctionExceptionFilter ( ULONG ExceptionCode, PVOID ExceptionRecord ) { //
// Skip timeout exceptions because they are dealt with in
// the default exception handler.
//
if (ExceptionCode == STATUS_POSSIBLE_DEADLOCK) { return EXCEPTION_CONTINUE_SEARCH; }
//
// Skip breakpoint exceptions raised within thread functions.
//
if (ExceptionCode == STATUS_BREAKPOINT) { return EXCEPTION_CONTINUE_SEARCH; }
VERIFIER_STOP (APPLICATION_VERIFIER_UNEXPECTED_EXCEPTION | APPLICATION_VERIFIER_CONTINUABLE_BREAK, "unexpected exception raised in thread function", ExceptionCode, "Exception code.", ((PEXCEPTION_POINTERS)ExceptionRecord)->ExceptionRecord, "Exception record. Use .exr to display it.", ((PEXCEPTION_POINTERS)ExceptionRecord)->ContextRecord, "Context record. Use .cxr to display it.", 0, "");
//
// After we issued a verifier stop, if we decide to continue then
// we need to look for the next exception handler.
//
return EXCEPTION_CONTINUE_SEARCH; }
DWORD WINAPI AVrfpStandardThreadFunction ( LPVOID Context ) { PAVRF_THREAD_ENTRY Info = (PAVRF_THREAD_ENTRY)Context; DWORD Result; PAVRF_THREAD_ENTRY SearchEntry;
//
// The initialization below matters only in case the thread function raises
// an access violation. In most cases this will terminate the entire
// process.
//
Result = 0;
try { //
// Add the thread entry to the thread table.
//
Info->Id = NtCurrentTeb()->ClientId.UniqueThread; AVrfpThreadTableAddEntry (Info);
//
// Call the real thing.
//
Result = (Info->Function)(Info->Parameter); } except (AVrfpThreadFunctionExceptionFilter (_exception_code(), _exception_info())) {
//
// Nothing.
//
} //
// Perform all typical checks for a thread that has just finished.
//
AVrfpCheckCurrentThreadTermination ();
//
// The thread entry should be `Info' but we will search it in the thread
// table nevertheless because there is a case when they can be different.
// This happens if fibers are used and a fiber starts in one thread and
// exits in another one. It is not clear if this is a safe programming
// practice but it is not rejected by current implementation and
// documentation.
//
SearchEntry = AVrfpThreadTableSearchEntry (NtCurrentTeb()->ClientId.UniqueThread);
if (SearchEntry != NULL) {
AVrfpThreadTableRemoveEntry (SearchEntry); AVrfpFree (SearchEntry); } return Result; }
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////// thread pool thread hook
/////////////////////////////////////////////////////////////////////
PRTLP_START_THREAD AVrfpBaseCreateThreadPoolThreadOriginal; PRTLP_EXIT_THREAD AVrfpBaseExitThreadPoolThreadOriginal;
NTSTATUS NTAPI AVrfpBaseCreateThreadPoolThread( PUSER_THREAD_START_ROUTINE Function, PVOID Parameter, HANDLE * ThreadHandleReturn ) { PAVRF_THREAD_ENTRY Info; PTEB Teb; NTSTATUS Status = STATUS_SUCCESS; Teb = NtCurrentTeb();
Info = AVrfpAllocate (sizeof *Info);
if (Info == NULL) { Status = STATUS_NO_MEMORY; goto Done; }
Info->Parameter = Parameter; Info->Function = (PTHREAD_START_ROUTINE)Function; Info->ParentThreadId = Teb->ClientId.UniqueThread;
Status = (*AVrfpBaseCreateThreadPoolThreadOriginal) ((PUSER_THREAD_START_ROUTINE)AVrfpStandardThreadFunction, Info, ThreadHandleReturn);
Done:
if (!NT_SUCCESS(Status)) {
if (Info != NULL) {
AVrfpFree (Info); }
Teb->LastStatusValue = Status; }
return Status; }
NTSTATUS NTAPI AVrfpBaseExitThreadPoolThread( NTSTATUS Status ) { PAVRF_THREAD_ENTRY Entry;
//
// Perform all typical checks for a thread that will exit.
//
AVrfpCheckCurrentThreadTermination ();
//
// Before calling the real ExitThread we need to free the thread
// entry from the thread table.
//
Entry = AVrfpThreadTableSearchEntry (NtCurrentTeb()->ClientId.UniqueThread);
if (Entry != NULL) { AVrfpThreadTableRemoveEntry (Entry); AVrfpFree (Entry); }
//
// Call the real thing.
//
return (*AVrfpBaseExitThreadPoolThreadOriginal) (Status); }
NTSTATUS NTAPI AVrfpRtlSetThreadPoolStartFunc( PRTLP_START_THREAD StartFunc, PRTLP_EXIT_THREAD ExitFunc ) { //
// Save the original thread pool start and exit functions.
//
AVrfpBaseCreateThreadPoolThreadOriginal = StartFunc; AVrfpBaseExitThreadPoolThreadOriginal = ExitFunc;
//
// Hook the thread pool start and exit functions to our private version.
//
return RtlSetThreadPoolStartFunc (AVrfpBaseCreateThreadPoolThread, AVrfpBaseExitThreadPoolThread); }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
VOID AVrfpCheckThreadTermination ( HANDLE Thread ) { //
// Traverse the list of critical sections and look for any that
// have issues (double initialized, corrupted, etc.). The function
// will also break for locks abandoned (owned by the thread just
// about to terminate).
//
RtlCheckForOrphanedCriticalSections (Thread); }
VOID AVrfpCheckCurrentThreadTermination ( VOID ) { PAVRF_TLS_STRUCT TlsStruct;
TlsStruct = AVrfpGetVerifierTlsValue();
if (TlsStruct != NULL && TlsStruct->CountOfOwnedCriticalSections > 0) {
AVrfpCheckThreadTermination (NtCurrentThread()); } }
|