Leaked source code of windows server 2003
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.
 
 
 
 
 
 

694 lines
19 KiB

/*++
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());
}
}