typedef struct _THREAD_TLS_INFORMATION { ULONG Flags; union { PVOID *TlsVector; PVOID TlsModulePointer; }; HANDLE ThreadId; } THREAD_TLS_INFORMATION, * PTHREAD_TLS_INFORMATION; typedef enum _PROCESS_TLS_INFORMATION_TYPE { ProcessTlsReplaceIndex, ProcessTlsReplaceVector, MaxProcessTlsOperation } PROCESS_TLS_INFORMATION_TYPE, * PPROCESS_TLS_INFORMATION_TYPE; typedef struct _PROCESS_TLS_INFORMATION { ULONG Reserved; // Reserved bitmask ULONG OperationType; ULONG ThreadDataCount; union { ULONG TlsIndex; ULONG TlsVectorLength; }; THREAD_TLS_INFORMATION ThreadData[ ANYSIZE_ARRAY ]; } PROCESS_TLS_INFORMATION, * PPROCESS_TLS_INFORMATION; // Need struct name typedef struct _TLS_VECTOR { union { ULONG Length; HANDLE ThreadId; }; struct _TLS_VECTOR * PreviousDeferredTlsVector; PVOID ModuleTlsData[ ANYSIZE_ARRAY ]; } TLS_VECTOR, * PTLS_VECTOR; // Need struct name typedef struct _TLS_RECLAIM_TABLE_ENTRY { PTLS_VECTOR TlsVector; RTL_SRWLOCK Lock; } TLS_RECLAIM_TABLE_ENTRY, * PTLS_RECLAIM_TABLE_ENTRY; // Need struct name typedef struct _TLS_ENTRY { LIST_ENTRY TlsEntryLinks; IMAGE_TLS_DIRECTORY TlsDirectory; PLDR_DATA_TABLE_ENTRY ModuleEntry; } TLS_ENTRY, * PTLS_ENTRY; /* * Old names */ /* typedef struct _THREAD_TLS_DATA { ULONG Flags; union { PVOID *TlsVector; PVOID ModuleTlsData; }; PVOID DeferredTlsData; } THREAD_TLS_DATA, * PTHREAD_TLS_DATA; typedef struct _THREAD_TLS_SLOTS { ULONG field_0; ULONG OperationCode; ULONG ThreadCount; union { ULONG TlsIndex; ULONG TlsBitmapLength; }; THREAD_TLS_INFORMATION ThreadData[ ANYSIZE_ARRAY ]; } THREAD_TLS_SLOTS, * PTHREAD_TLS_SLOTS; */ PUCHAR NtdllBaseTag = 0; ULONG LdrpActiveThreadCount = 0; ULONG LdrpPotentialTlsLeaks = 0; RTL_BITMAP LdrpTlsBitmap; LIST_ENTRY LdrpTlsList; TLS_RECLAIM_TABLE_ENTRY LdrpDelayedTlsReclaimTable[ 16 ]; ULONG LdrpStaticTlsBitmapVector[ 4 ]; ULONG LdrpActualBitmapSize = 0; /* * Dummy definitions */ PVOID ExAllocatePoolWithQuotaTag( __in POOL_TYPE PoolType, __in SIZE_T NumberOfBytes, __in ULONG Tag ) { UNREFERENCED_PARAMETER( PoolType ); UNREFERENCED_PARAMETER( NumberOfBytes ); UNREFERENCED_PARAMETER( Tag ); return 0; } VOID ExFreePool( __in PVOID P ) { UNREFERENCED_PARAMETER( P ); } PEPROCESS PsGetCurrentProcess( VOID ) { return 0; } typedef struct _ETHREAD { PTEB Teb; PVOID RundownProtect; CLIENT_ID ClientId; } ETHREAD, * PETHREAD; typedef struct _EPROCESS { } EPROCESS, * PEPROCESS; PETHREAD PsGetNextProcessThread( __in PEPROCESS Process, __in_opt PETHREAD Thread ) { UNREFERENCED_PARAMETER( Process ); UNREFERENCED_PARAMETER( Thread ); return 0; } BOOLEAN ExAcquireRundownProtection( __in PVOID RundownProtection ) { UNREFERENCED_PARAMETER( RundownProtection ); return FALSE; } BOOLEAN ExReleaseRundownProtection( __in PVOID RundownProtection ) { UNREFERENCED_PARAMETER( RundownProtection ); return FALSE; } VOID ProbeForRead( __in CONST VOID *Address, __in SIZE_T Length, __in ULONG Alignment ) { UNREFERENCED_PARAMETER( Address ); UNREFERENCED_PARAMETER( Length ); UNREFERENCED_PARAMETER( Alignment ); } VOID ProbeForWrite( __in PVOID Address, __in SIZE_T Length, __in ULONG Alignment ) { UNREFERENCED_PARAMETER( Address ); UNREFERENCED_PARAMETER( Length ); UNREFERENCED_PARAMETER( Alignment ); } VOID ObDereferenceObject( __in PVOID Object ) { UNREFERENCED_PARAMETER( Object ); } NTSTATUS _xxxHandleProcessTlsInformation( __in HANDLE ProcessHandle, __in KPROCESSOR_MODE ProcessorMode, __in PVOID ProcessInformation, __in ULONG ProcessInformationLength ) { PPROCESS_TLS_INFORMATION TlsInfo; PPROCESS_TLS_INFORMATION UserTlsInfo; PROCESS_TLS_INFORMATION OneThreadTlsInfo; ULONG ThreadCount; ULONG ThreadIndex; PEPROCESS ThreadsProcess; PETHREAD Thread; NTSTATUS Status; PVOID **AddrOfCurrentTlsBlock; PVOID OldModuleTlsData; PVOID *CurrentTlsBlock; PVOID *ThreadTlsVector; HANDLE UniqueThread; // // Wow, what a hack - should open it... // if (ProcessHandle != NtCurrentProcess()) return STATUS_INVALID_PARAMETER; // // Hack again - ugh ... // if (ProcessorMode != UserMode) return STATUS_UNSUCCESSFUL; // // We must be at least able to contain a PROCESS_TLS_INFORMATION object. // if (ProcessInformationLength < sizeof( PROCESS_TLS_INFORMATION )) return STATUS_INFO_LENGTH_MISMATCH; // // We need to have an integral number of THREAD_TLS_INFORMATION entries. // ThreadCount = ProcessInformationLength - RTL_SIZEOF_THROUGH_FIELD( PROCESS_TLS_INFORMATION, TlsIndex ) / sizeof( THREAD_TLS_INFORMATION ); if (ProcessInformationLength - RTL_SIZEOF_THROUGH_FIELD( PROCESS_TLS_INFORMATION, TlsIndex ) % sizeof( THREAD_TLS_INFORMATION ) != 0) return STATUS_INFO_LENGTH_MISMATCH; // // Copy the caller's buffer into kernel storage. We'll use the stack if // there happens to be just one thread. // if (ProcessInformationLength == sizeof( OneThreadTlsInfo )) { TlsInfo = &OneThreadTlsInfo; } else { TlsInfo = (PPROCESS_TLS_INFORMATION)ExAllocatePoolWithQuotaTag( (POOL_TYPE)(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE), ProcessInformationLength, 'slTP' ); if (!TlsInfo) return STATUS_INSUFFICIENT_RESOURCES; } __try { memcpy( TlsInfo, ProcessInformation, ProcessInformationLength ); } __except (EXCEPTION_EXECUTE_HANDLER) { // // Assumed, didn't check for sure. // if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return (NTSTATUS)GetExceptionCode(); } // // Perform further validation... // // // Well that's the totally wrong status code to return, but oh well. // if (TlsInfo->OperationType > MaxProcessTlsOperation) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INFO_LENGTH_MISMATCH; } if (TlsInfo->Reserved & ~0x1) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INFO_LENGTH_MISMATCH; } if (TlsInfo->ThreadDataCount < 1) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INFO_LENGTH_MISMATCH; } // // Make sure that the thread count field matches the count that we derived // from the (trusted) buffer length. // if (TlsInfo->ThreadDataCount != ThreadCount) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INFO_LENGTH_MISMATCH; } // // Flags must be zero on the call. Validate the flags field on each thread // data entry now. // for (ThreadIndex = 0; ThreadIndex < TlsInfo->ThreadDataCount; ThreadIndex += 1) { if (TlsInfo->ThreadData[ ThreadIndex ].Flags != 0) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INVALID_PARAMETER; } } ThreadsProcess = PsGetCurrentProcess(); // // Bizzare. We check for ~1 and then 1. Why not just check for nonzereo? // if (TlsInfo->Reserved & 0x1) { if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_INVALID_PARAMETER; } Status = STATUS_SUCCESS; UserTlsInfo = (PPROCESS_TLS_INFORMATION)ProcessInformation; Thread = 0; for (Thread = PsGetNextProcessThread( ThreadsProcess, Thread ); Thread != 0; Thread = PsGetNextProcessThread( ThreadsProcess, Thread ) ) { if (ThreadIndex >= TlsInfo->ThreadDataCount) break; if (!ExAcquireRundownProtection( &Thread->RundownProtect )) continue; // // Get the current thread's TLS block. // __try { AddrOfCurrentTlsBlock = &Thread->Teb->ThreadLocalStoragePointer; OldModuleTlsData = (PVOID)&Thread->Teb->ThreadLocalStoragePointer; CurrentTlsBlock = Thread->Teb->ThreadLocalStoragePointer; } __except (EXCEPTION_EXECUTE_HANDLER) { // XXX: CHECK ME!! important!!!! - scope 0x17 // // Assumed, didn't check for sure. // Status = (NTSTATUS)GetExceptionCode(); } // // If we had an exception occur or we got a null TLS block, then we'll // need to either bail out or continue on to the next entry. // if (!NT_SUCCESS( Status ) || !CurrentTlsBlock) { ExReleaseRundownProtection( &Thread->RundownProtect ); if (NT_SUCCESS( Status )) continue; ObDereferenceObject( Thread ); if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return Status; } if (TlsInfo->OperationType != ProcessTlsReplaceVector) { // // This thread didn't expand the TLS bitmap. // __try { UserTlsInfo->ThreadData[ ThreadIndex ].Flags |= 0x1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus1; // Ugh } __try { ProbeForRead( &CurrentTlsBlock[ TlsInfo->TlsIndex ], sizeof( PVOID ), 0 ); // // Get the old TLS pointer. // OldModuleTlsData = CurrentTlsBlock[ TlsInfo->TlsIndex ]; ProbeForWrite( &CurrentTlsBlock[ TlsInfo->TlsIndex ], sizeof( PVOID ), 0 ); // // Store the new module TLS pointer. // CurrentTlsBlock[ TlsInfo->TlsIndex ] = TlsInfo->ThreadData[ ThreadIndex ].TlsModulePointer; } __except (EXCEPTION_EXECUTE_HANDLER) { // XXX: CHECK ME!! important!!!! - scope 0x1E ( 0x1D ) // // Assumed, didn't check for sure. // Status = (NTSTATUS)GetExceptionCode(); goto checkstatus1; // Ugh } checkstatus1: if (!NT_SUCCESS( Status )) { // // If we failed, unset the successful-for-not-TLS-bitmap flag. // __try { UserTlsInfo->ThreadData[ ThreadIndex ].Flags &= ~0x1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus1a; // Ugh } } else { // // Otherwise, tell the user the old TLS vector pointer and note // that we were successful. (Hooray.) // __try { UserTlsInfo->ThreadData[ ThreadIndex ].TlsVector = (PVOID *)OldModuleTlsData; UserTlsInfo->ThreadData[ ThreadIndex ].Flags ^= 0x3; ThreadIndex += 1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus1a; // Ugh } } checkstatus1a: ExReleaseRundownProtection( &Thread->RundownProtect ); if (!NT_SUCCESS( Status )) break; // // Next loop iteration continues... // } else { if (CurrentTlsBlock == (PVOID *)&Thread->Teb->ThreadLocalStoragePointer) { CurrentTlsBlock = 0; } else { // // We need to copy over the TLS bitmap. (Ugh.) // if (TlsInfo->TlsVectorLength) { __try { ProbeForRead( CurrentTlsBlock, TlsInfo->TlsVectorLength * 4, TYPE_ALIGNMENT( ULONG ) ); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus2; // Ugh } } // // The actual implementation saves the address, but I see // no reason to dereference it again. // ThreadTlsVector = TlsInfo->ThreadData[ ThreadIndex ].TlsVector; __try { ProbeForWrite( ThreadTlsVector, TlsInfo->TlsVectorLength * sizeof( PVOID ), TYPE_ALIGNMENT( PVOID ) ); memcpy( ThreadTlsVector, CurrentTlsBlock, TlsInfo->TlsVectorLength * sizeof( PVOID ) ); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus2; } } // // Set flags accordingly as we're going to set the vector for this // thread. // __try { UserTlsInfo->ThreadData[ ThreadIndex ].Flags |= 0x1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus2; } // // Set the vector for this thread. // __try { *AddrOfCurrentTlsBlock = TlsInfo->ThreadData[ ThreadIndex ].TlsVector; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus2; } checkstatus2: if (NT_SUCCESS( Status )) { UniqueThread = (HANDLE)Thread->ClientId.UniqueThread; __try { UserTlsInfo->ThreadData[ ThreadIndex ].ThreadId = UniqueThread; UserTlsInfo->ThreadData[ ThreadIndex ].TlsVector = CurrentTlsBlock; UserTlsInfo->ThreadData[ ThreadIndex ].Flags ^= 0x3; ThreadIndex += 1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus3; // Ugh } } else { // // If we failed, unset the successful-for-not-TLS-bitmap flag. // __try { UserTlsInfo->ThreadData[ ThreadIndex ].Flags &= ~0x1; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = (NTSTATUS)GetExceptionCode(); goto checkstatus3; // Ugh } } checkstatus3: ExReleaseRundownProtection( &Thread->RundownProtect ); if (!NT_SUCCESS( Status )) break; // // Next loop iteration continues... // } } if (Thread) ObDereferenceObject( Thread ); if (TlsInfo != &OneThreadTlsInfo) ExFreePool(TlsInfo); return STATUS_SUCCESS; } VOID LdrpReleaseTlsIndex( __in ULONG TlsIndex ) { RtlClearBit( &LdrpTlsBitmap, TlsIndex ); } #define LDRP_BITMAP_INCREMENT (0x27 - sizeof( PVOID )) NTSTATUS LdrpAcquireTlsIndex( __out PULONG TlsIndex, __out PBOOLEAN AllocatedBitmap ) { ULONG Length; ULONG Index; PULONG NewBitmapBuffer; Length = LdrpTlsBitmap.SizeOfBitMap; if (Length == 0) { // // If we're the first caller, then we shall need to be initializing the // bitmap. // // This implies that we don't need to expand as by definition, there // shall exist space for ourselves at the start of the bitmap now. // RtlInitializeBitMap( &LdrpTlsBitmap, LdrpStaticTlsBitmapVector, 4 ); LdrpActualBitmapSize = 1; } else { Index = RtlFindClearBitsAndSet( &LdrpTlsBitmap, 1, 0 ); // // If we found space in the existing bitmap then there is no reason to // expand buffers, so we'll just return with the existing data. // if (Index != 0xFFFFFFFF) { *TlsIndex = Index; *AllocatedBitmap = FALSE; return STATUS_SUCCESS; } // // Check if we need to grow the bitmap itself or if the bitmap still // has space. // if (((LdrpTlsBitmap.SizeOfBitMap + LDRP_BITMAP_INCREMENT) >> 5) > LdrpActualBitmapSize) { // // We'll need to grow it. Let's go do so now. // // // BUG: We set the new size before checking the allocation. If we // fail, then we leave the TLS variables in an inconsistant state. // LdrpActualBitmapSize = (Length + LDRP_BITMAP_INCREMENT) >> 5; NewBitmapBuffer = (PULONG)RtlAllocateHeap( RtlProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), LdrpActualBitmapSize ); if (!NewBitmapBuffer) return STATUS_NO_MEMORY; // // Copy the contents of the previous buffer into the new one. // memcpy( NewBitmapBuffer, LdrpTlsBitmap.Buffer, Length + 7 ); // // Free the old buffer if it wasn't the initial static buffer. // if (LdrpTlsBitmap.Buffer != LdrpStaticTlsBitmapVector) { RtlFreeHeap( RtlProcessHeap(), 0, LdrpTlsBitmap.Buffer ); } // // Reinitialize the bitmap as we've changed the buffer pointer. // RtlInitializeBitMap( &LdrpTlsBitmap, NewBitmapBuffer, Length + 4 ); } else { LdrpTlsBitmap.SizeOfBitMap += 4; } } RtlClearBits( &LdrpTlsBitmap, Length + 1, 3 ); RtlSetBit( &LdrpTlsBitmap, Length ); *TlsIndex = Index; *AllocatedBitmap = TRUE; return STATUS_SUCCESS; } NTSTATUS LdrpAllocateTlsEntry( __in PIMAGE_TLS_DIRECTORY TlsDirectory, __inout PLDR_DATA_TABLE_ENTRY ModuleEntry, __out PULONG TlsIndex, __out_opt PBOOLEAN AllocatedBitmap, __out PTLS_ENTRY *TlsEntry ) { PTLS_ENTRY Entry; NTSTATUS Status; __try { Entry = (PTLS_ENTRY)RtlAllocateHeap( RtlProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), sizeof( TLS_ENTRY ) ); if (!Entry) return STATUS_NO_MEMORY; Status = STATUS_SUCCESS; memcpy( &Entry->TlsDirectory, TlsDirectory, sizeof( IMAGE_TLS_DIRECTORY ) ); } __except (EXCEPTION_EXECUTE_HANDLER) { // // Also print string and complain. // Status = GetExceptionCode(); } if (!NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, Entry ); return Status; } // // Validate that the TLS directory entry is sane. // if (Entry->TlsDirectory.StartAddressOfRawData < Entry->TlsDirectory.EndAddressOfRawData) { RtlFreeHeap( RtlProcessHeap(), 0, Entry ); return STATUS_INVALID_IMAGE_FORMAT; } Entry->ModuleEntry = ModuleEntry; // // Insert the entry into our list. // InsertTailList( &LdrpTlsList, &Entry->TlsEntryLinks ); if (AllocatedBitmap) { Status = LdrpAcquireTlsIndex( TlsIndex, AllocatedBitmap ); if (!NT_SUCCESS( Status )) { // // BUG: We don't remove the entry from LdrpTlsList // RtlFreeHeap( RtlProcessHeap(), 0, Entry ); return Status; } } else { *TlsIndex += 1; } // // We reuse the 'Characteristics' field for the real TLS index. // Entry->TlsDirectory.Characteristics = *TlsIndex; __try { *(PULONG)Entry->TlsDirectory.AddressOfIndex = *TlsIndex; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } if (!NT_SUCCESS( Status )) { if (AllocatedBitmap) { LdrpReleaseTlsIndex( *TlsIndex ); if (*AllocatedBitmap) LdrpTlsBitmap.SizeOfBitMap -= 4; } // // BUG: We don't remove the entry from LdrpTlsList // RtlFreeHeap( RtlProcessHeap(), 0, Entry ); return Status; } if (TlsEntry) *TlsEntry = Entry; return STATUS_SUCCESS; } PTLS_ENTRY FASTCALL LdrpFindTlsEntry( __in PLDR_DATA_TABLE_ENTRY ModuleEntry ) { PTLS_ENTRY TlsEntry; PLIST_ENTRY ListHead; ListHead = &LdrpTlsList; for (TlsEntry = CONTAINING_RECORD( LdrpTlsList.Flink, TLS_ENTRY, TlsEntryLinks ); &TlsEntry->TlsEntryLinks != ListHead; TlsEntry = CONTAINING_RECORD( TlsEntry->TlsEntryLinks.Flink, TLS_ENTRY, TlsEntryLinks ) ) { if (TlsEntry->ModuleEntry == ModuleEntry) return TlsEntry; } return 0; } NTSTATUS LdrpReleaseTlsEntry( __in PLDR_DATA_TABLE_ENTRY ModuleEntry ) { PTLS_ENTRY TlsEntry; // // Find the corresponding TLS_ENTRY for this module entry. // TlsEntry = LdrpFindTlsEntry( ModuleEntry ); if (!TlsEntry) return STATUS_NOT_FOUND; // // Remove it from the global list of outstanding TLS entries. // RemoveEntryList( &TlsEntry->TlsEntryLinks ); // // Deallocate the TLS index. // LdrpReleaseTlsIndex( TlsEntry->TlsDirectory.Characteristics ); // // Deallocate the TLS_ENTRY object itself. // RtlFreeHeap( RtlProcessHeap(), 0, TlsEntry ); // // We're done. // return STATUS_SUCCESS; } PVOID * FASTCALL LdrpGetNewTlsVector( __in ULONG TlsBitmapLength ) { PTLS_VECTOR TlsVector; TlsVector = (PTLS_VECTOR)RtlAllocateHeap( RtlProcessHeap(), (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), sizeof( TLS_VECTOR ) + (sizeof( PVOID ) * TlsBitmapLength) - sizeof( PVOID ) ); if (!TlsVector) return 0; TlsVector->Length = TlsBitmapLength; RtlZeroMemory( TlsVector->ModuleTlsData, TlsBitmapLength * sizeof( PVOID ) ); return TlsVector->ModuleTlsData; } VOID LdrpQueueDeferredTlsData( __inout PVOID TlsVector, __inout PVOID ThreadId ) { PTLS_VECTOR RealTlsVector; PTLS_RECLAIM_TABLE_ENTRY ReclaimEntry; RealTlsVector = CONTAINING_RECORD( TlsVector, TLS_VECTOR, ModuleTlsData ); RealTlsVector->ThreadId = ThreadId; ReclaimEntry = &LdrpDelayedTlsReclaimTable[ ((ULONG_PTR)(ThreadId) >> 2) & 0xF ]; RtlAcquireSRWLockExclusive( &ReclaimEntry->Lock ); RealTlsVector->PreviousDeferredTlsVector = ReclaimEntry->TlsVector; ReclaimEntry->TlsVector = RealTlsVector; RtlReleaseSRWLockExclusive( &ReclaimEntry->Lock ); } NTSTATUS LdrpHandleTlsData( __inout PLDR_DATA_TABLE_ENTRY ModuleEntry ) { PIMAGE_TLS_DIRECTORY TlsDirectory; ULONG DirectorySize; ULONG TlsIndex; HANDLE Heap; PPROCESS_TLS_INFORMATION TlsInfo; PROCESS_TLS_INFORMATION OneThreadTlsInfo; NTSTATUS Status; BOOLEAN AllocatedBitmap; PTLS_ENTRY TlsEntry; ULONG TlsBitmapLength; SIZE_T TlsRawDataLength; ULONG ThreadIndex; PVOID TlsData; PVOID *TlsVector; PTHREAD_TLS_INFORMATION ThreadTlsData; ULONG ThreadsCleanedUp; if (LdrpActiveThreadCount == 0) { return STATUS_SUCCESS; } // // Discover the TLS directory address for this module. // TlsDirectory = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData( ModuleEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &DirectorySize ); // // If we've got no TLS directory then we're done. // if (!TlsDirectory) return STATUS_SUCCESS; // // We'll be using the process heap. // Heap = RtlProcessHeap(); // // We've got an optimization for one active thread, which is the case for // traditional static-link DLLs that use __declspec(thread). // if (LdrpActiveThreadCount == 1) TlsInfo = &OneThreadTlsInfo; else { // // Otherwise, allocate memory for our thread data block. // TlsInfo = (PPROCESS_TLS_INFORMATION)RtlAllocateHeap( Heap, (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), LdrpActiveThreadCount * sizeof( THREAD_TLS_INFORMATION ) + sizeof( PROCESS_TLS_INFORMATION ) - sizeof( THREAD_TLS_INFORMATION ) ); if (!TlsInfo) { return STATUS_NO_MEMORY; } } do { // // Allocate a TLS index (or a new TLS bitmap). // TlsBitmapLength = LdrpTlsBitmap.SizeOfBitMap; Status = LdrpAllocateTlsEntry( TlsDirectory, ModuleEntry, &TlsIndex, &AllocatedBitmap, &TlsEntry ); if (!NT_SUCCESS( Status )) break; TlsInfo->ThreadDataCount = LdrpActiveThreadCount; if (AllocatedBitmap) { TlsInfo->OperationType = ProcessTlsReplaceVector; TlsInfo->TlsVectorLength = TlsBitmapLength; TlsBitmapLength = LdrpTlsBitmap.SizeOfBitMap; } else { TlsInfo->OperationType = ProcessTlsReplaceIndex; TlsInfo->TlsIndex = TlsIndex; } Status = STATUS_SUCCESS; ThreadsCleanedUp = 0; // // Calculate the size of the raw TLS data for this module. // TlsRawDataLength = TlsEntry->TlsDirectory.EndAddressOfRawData - TlsEntry->TlsDirectory.StartAddressOfRawData; // // Prepare data for each running thread. // for (ThreadIndex = 0; ThreadIndex < TlsInfo->ThreadDataCount; ThreadIndex += 1) { // // Allocate the TLS memory block for this thread... // TlsData = RtlAllocateHeap( Heap, (ULONG_PTR)((PUCHAR)NtdllBaseTag + 0x000C0000), TlsRawDataLength ); if (!TlsData) { Status = STATUS_NO_MEMORY; break; } // // Copy the initializer raw data into it. // __try { memcpy( TlsData, (PVOID)TlsEntry->TlsDirectory.StartAddressOfRawData, TlsRawDataLength ); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } if (!NT_SUCCESS( Status )) { RtlFreeHeap( Heap, 0, TlsData ); break; } if (AllocatedBitmap) { TlsVector = LdrpGetNewTlsVector( TlsBitmapLength ); if (!TlsVector) { RtlFreeHeap( Heap, 0, TlsData ); break; } TlsVector[ TlsIndex ] = TlsData; TlsInfo->ThreadData[ ThreadIndex ].TlsVector = TlsVector; } else { TlsInfo->ThreadData[ ThreadIndex ].TlsModulePointer = TlsData; } TlsInfo->ThreadData[ ThreadIndex ].Flags = 0; } // // This is awkward; all the 'break' above really are either goto or // __leave, but we aren't using those. This is really supposed to // just happen on normal for loop exit. // if (ThreadIndex == TlsInfo->ThreadDataCount) { TlsInfo->Reserved = 0; // // Perform the actual work of swapping the thread TLS data. // Status = NtSetInformationProcess( NtCurrentProcess(), ProcessTlsInformation, TlsInfo, TlsInfo->ThreadDataCount * sizeof( THREAD_TLS_INFORMATION ) + sizeof( PROCESS_TLS_INFORMATION ) - sizeof( THREAD_TLS_INFORMATION ) ); } // // Let's handle each thread that we replaced, as the // ProcessTlsInformation call fills our buffer with the old data // after performing a swap. // for (ThreadTlsData = &TlsInfo->ThreadData[ ThreadIndex ]; ThreadIndex > 0; ) { ThreadIndex -= 1; ThreadTlsData -= 1; if (ThreadTlsData->Flags & 0x2) { if (!ThreadTlsData->TlsVector) continue; if (!AllocatedBitmap) { RtlFreeHeap( Heap, 0, ThreadTlsData->TlsVector ); continue; } else { LdrpQueueDeferredTlsData( ThreadTlsData->TlsVector, ThreadTlsData->ThreadId ); continue; } } else { if (ThreadTlsData->Flags & 0x1) { // // Hmm... // LdrpPotentialTlsLeaks += 1; continue; } else { ThreadsCleanedUp += 1; if (AllocatedBitmap) { TlsData = ThreadTlsData->TlsVector[ TlsIndex ]; RtlFreeHeap( Heap, 0, CONTAINING_RECORD( ThreadTlsData->TlsVector, TLS_VECTOR, ModuleTlsData ) ); } RtlFreeHeap( Heap, 0, TlsData ); continue; } } } if (!NT_SUCCESS( Status )) { LdrpReleaseTlsEntry( ModuleEntry ); if (AllocatedBitmap) LdrpTlsBitmap.SizeOfBitMap -= 4; } else if (ThreadsCleanedUp > 0) { LdrpActiveThreadCount -= ThreadsCleanedUp; } } while (0) ; if (TlsInfo != &OneThreadTlsInfo) { RtlFreeHeap( Heap, 0, TlsInfo ); } if (!NT_SUCCESS( Status )) return Status; ModuleEntry->TlsIndex = 0xFFFF; return STATUS_SUCCESS; }