Merge branch 'ntdll_process_tls_info' into 'master'

ntdll: Implement NtSetInformationProcess( ProcessTlsInformation ) and use that in loader.

See merge request wine/wine!6549
This commit is contained in:
Paul Gofman 2024-11-19 13:13:20 +00:00
commit e6ab7187f3
9 changed files with 600 additions and 51 deletions

View file

@ -1708,7 +1708,10 @@ static void test_tls_links(void)
TEB *teb = NtCurrentTeb(), *thread_teb; TEB *teb = NtCurrentTeb(), *thread_teb;
THREAD_BASIC_INFORMATION tbi; THREAD_BASIC_INFORMATION tbi;
NTSTATUS status; NTSTATUS status;
ULONG i, count;
HANDLE thread; HANDLE thread;
SIZE_T size;
void **ptr;
ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n"); ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n");
@ -1728,6 +1731,26 @@ static void test_tls_links(void)
ResumeThread(thread); ResumeThread(thread);
WaitForSingleObject(test_tls_links_started, INFINITE); WaitForSingleObject(test_tls_links_started, INFINITE);
if (!is_old_loader_struct())
{
ptr = teb->ThreadLocalStoragePointer;
count = (ULONG_PTR)ptr[-2];
size = HeapSize(GetProcessHeap(), 0, ptr - 2);
ok(size == (count + 2) * sizeof(void *), "got count %lu, size %Iu.\n", count, size);
for (i = 0; i < count; ++i)
{
if (!ptr[i]) continue;
size = HeapSize(GetProcessHeap(), 0, (void **)ptr[i] - 2);
ok(size && size < 100000, "got %Iu.\n", size);
}
ptr = thread_teb->ThreadLocalStoragePointer;
count = (ULONG_PTR)ptr[-2];
size = HeapSize(GetProcessHeap(), 0, ptr - 2);
ok(size == (count + 2) * sizeof(void *), "got count %lu, size %Iu.\n", count, size);
}
ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n");
ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink);
ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink);

View file

@ -142,6 +142,7 @@ typedef struct _wine_modref
static UINT tls_module_count = 32; /* number of modules with TLS directory */ static UINT tls_module_count = 32; /* number of modules with TLS directory */
static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */ static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */
static ULONG tls_thread_count; /* number of threads for which ThreadLocalStoragePointer is allocated in TEB. */
static RTL_CRITICAL_SECTION loader_section; static RTL_CRITICAL_SECTION loader_section;
static RTL_CRITICAL_SECTION_DEBUG critsect_debug = static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
@ -1332,6 +1333,36 @@ static BOOL is_dll_native_subsystem( LDR_DATA_TABLE_ENTRY *mod, const IMAGE_NT_H
return TRUE; return TRUE;
} }
/*************************************************************************
* alloc_tls_memory
*
* Allocate memory for TLS vector or index with an extra data.
*/
static void *alloc_tls_memory( BOOL vector, ULONG_PTR size )
{
ULONG_PTR *ptr;
if (!(ptr = RtlAllocateHeap( GetProcessHeap(), vector ? HEAP_ZERO_MEMORY : 0, size + sizeof(void *) * 2 ))) return NULL;
ptr += 2;
if (vector) ptr[-2] = size / sizeof(void *);
else ptr[-2] = ptr[-1] = 0;
return ptr;
}
/*************************************************************************
* free_tls_memory
*
* Free TLS vector or index memory.
*/
static void free_tls_memory( void *ptr )
{
if (!ptr) return;
RtlFreeHeap( GetProcessHeap(), 0, (void **)ptr - 2 );
}
/************************************************************************* /*************************************************************************
* alloc_tls_slot * alloc_tls_slot
* *
@ -1341,10 +1372,10 @@ static BOOL is_dll_native_subsystem( LDR_DATA_TABLE_ENTRY *mod, const IMAGE_NT_H
static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod )
{ {
const IMAGE_TLS_DIRECTORY *dir; const IMAGE_TLS_DIRECTORY *dir;
ULONG i, size; ULONG i, j, size;
void *new_ptr; void *new_ptr;
UINT old_module_count = tls_module_count; UINT old_module_count = tls_module_count;
HANDLE thread = NULL, next; PROCESS_TLS_INFORMATION *t;
if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size ))) if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size )))
return FALSE; return FALSE;
@ -1373,57 +1404,66 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod )
tls_dirs = new_ptr; tls_dirs = new_ptr;
tls_module_count = new_count; tls_module_count = new_count;
} }
*(DWORD *)dir->AddressOfIndex = i;
tls_dirs[i] = *dir;
/* allocate the data block in all running threads */ if (!tls_thread_count) return TRUE;
while (!NtGetNextThread( GetCurrentProcess(), thread, THREAD_QUERY_LIMITED_INFORMATION, 0, 0, &next )) t = RtlAllocateHeap( GetProcessHeap(), 0, offsetof( PROCESS_TLS_INFORMATION, ThreadData[tls_thread_count] ));
if (!t) return FALSE;
t->Flags = 0;
t->ThreadDataCount = tls_thread_count;
if (old_module_count < tls_module_count)
{ {
THREAD_BASIC_INFORMATION tbi; t->OperationType = ProcessTlsReplaceVector;
TEB *teb; t->TlsVectorLength = old_module_count;
}
else
{
t->OperationType = ProcessTlsReplaceIndex;
t->TlsIndex = i;
}
for (j = 0; j < tls_thread_count; ++j)
{
void **vector;
if (thread) NtClose( thread ); t->ThreadData[j].Flags = 0;
thread = next;
if (NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ) || !tbi.TebBaseAddress)
{
ERR( "NtQueryInformationThread failed.\n" );
continue;
}
teb = tbi.TebBaseAddress;
if (!teb->ThreadLocalStoragePointer)
{
/* Thread is not initialized by loader yet or already teared down. */
TRACE( "thread %04lx NULL tls block.\n", HandleToULong(tbi.ClientId.UniqueThread) );
continue;
}
if (old_module_count < tls_module_count) if (!(new_ptr = alloc_tls_memory( FALSE, size + dir->SizeOfZeroFill ))) return FALSE;
{
void **old = teb->ThreadLocalStoragePointer;
void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*new));
if (!new) return FALSE;
if (old) memcpy( new, old, old_module_count * sizeof(*new) );
teb->ThreadLocalStoragePointer = new;
#ifdef __x86_64__ /* macOS-specific hack */
if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = new;
#endif
TRACE( "thread %04lx tls block %p -> %p\n", HandleToULong(teb->ClientId.UniqueThread), old, new );
/* FIXME: can't free old block here, should be freed at thread exit */
}
if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return -1;
memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size );
memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill );
TRACE( "thread %04lx slot %lu: %lu/%lu bytes at %p\n", if (t->OperationType == ProcessTlsReplaceVector)
HandleToULong(teb->ClientId.UniqueThread), i, size, dir->SizeOfZeroFill, new_ptr ); {
vector = alloc_tls_memory( TRUE, tls_module_count * sizeof(*vector) );
RtlFreeHeap( GetProcessHeap(), 0, if (!vector) return FALSE;
InterlockedExchangePointer( (void **)teb->ThreadLocalStoragePointer + i, new_ptr )); t->ThreadData[j].TlsVector = vector;
vector[i] = new_ptr;
}
else t->ThreadData[j].TlsModulePointer = new_ptr;
}
if (NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, t,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount])))
{
ERR( "ProcessTlsInformation failed.\n" );
return FALSE;
} }
if (thread) NtClose( thread );
*(DWORD *)dir->AddressOfIndex = i; for (j = 0; j < tls_thread_count; ++j)
tls_dirs[i] = *dir; {
if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) && t->OperationType == ProcessTlsReplaceVector)
{
/* There could be fewer active threads than we counted here due to force terminated threads, first
* free extra TLS directory data set in the new TLS vector. */
free_tls_memory( ((void **)t->ThreadData[j].TlsVector)[i] );
}
if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) || t->OperationType == ProcessTlsReplaceIndex)
{
/* FIXME: can't free old Tls vector here, should be freed at thread exit. */
free_tls_memory( t->ThreadData[j].TlsVector );
}
}
RtlFreeHeap( GetProcessHeap(), 0, t );
return TRUE; return TRUE;
} }
@ -1633,8 +1673,7 @@ static NTSTATUS alloc_thread_tls(void)
void **pointers; void **pointers;
UINT i, size; UINT i, size;
if (!(pointers = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, if (!(pointers = alloc_tls_memory( TRUE, tls_module_count * sizeof(*pointers) )))
tls_module_count * sizeof(*pointers) )))
return STATUS_NO_MEMORY; return STATUS_NO_MEMORY;
for (i = 0; i < tls_module_count; i++) for (i = 0; i < tls_module_count; i++)
@ -1645,10 +1684,10 @@ static NTSTATUS alloc_thread_tls(void)
size = dir->EndAddressOfRawData - dir->StartAddressOfRawData; size = dir->EndAddressOfRawData - dir->StartAddressOfRawData;
if (!size && !dir->SizeOfZeroFill) continue; if (!size && !dir->SizeOfZeroFill) continue;
if (!(pointers[i] = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) if (!(pointers[i] = alloc_tls_memory( FALSE, size + dir->SizeOfZeroFill )))
{ {
while (i) RtlFreeHeap( GetProcessHeap(), 0, pointers[--i] ); while (i) free_tls_memory( pointers[--i] );
RtlFreeHeap( GetProcessHeap(), 0, pointers ); free_tls_memory( pointers );
return STATUS_NO_MEMORY; return STATUS_NO_MEMORY;
} }
memcpy( pointers[i], (void *)dir->StartAddressOfRawData, size ); memcpy( pointers[i], (void *)dir->StartAddressOfRawData, size );
@ -1656,6 +1695,7 @@ static NTSTATUS alloc_thread_tls(void)
TRACE( "slot %u: %u/%lu bytes at %p\n", i, size, dir->SizeOfZeroFill, pointers[i] ); TRACE( "slot %u: %u/%lu bytes at %p\n", i, size, dir->SizeOfZeroFill, pointers[i] );
} }
++tls_thread_count;
NtCurrentTeb()->ThreadLocalStoragePointer = pointers; NtCurrentTeb()->ThreadLocalStoragePointer = pointers;
#ifdef __x86_64__ /* macOS-specific hack */ #ifdef __x86_64__ /* macOS-specific hack */
if (NtCurrentTeb()->Instrumentation[0]) if (NtCurrentTeb()->Instrumentation[0])
@ -3947,12 +3987,13 @@ void WINAPI LdrShutdownThread(void)
if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer))
{ {
NtCurrentTeb()->ThreadLocalStoragePointer = NULL; NtCurrentTeb()->ThreadLocalStoragePointer = NULL;
--tls_thread_count;
#ifdef __x86_64__ /* macOS-specific hack */ #ifdef __x86_64__ /* macOS-specific hack */
if (NtCurrentTeb()->Instrumentation[0]) if (NtCurrentTeb()->Instrumentation[0])
((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL; ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL;
#endif #endif
for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); for (i = 0; i < tls_module_count; i++) free_tls_memory( pointers[i] );
RtlFreeHeap( GetProcessHeap(), 0, pointers ); free_tls_memory( pointers );
} }
RtlProcessFlsData( NtCurrentTeb()->FlsSlots, 2 ); RtlProcessFlsData( NtCurrentTeb()->FlsSlots, 2 );
NtCurrentTeb()->FlsSlots = NULL; NtCurrentTeb()->FlsSlots = NULL;

View file

@ -4087,6 +4087,278 @@ static void test_processor_idle_cycle_time(void)
ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size );
} }
static DWORD WINAPI test_set_process_tls_info_thread(void *param)
{
return 0;
}
static void test_set_process_tls_info(void)
{
THREAD_BASIC_INFORMATION tbi;
TEB *teb = NtCurrentTeb(), *thread_teb;
char buffer[1024];
PROCESS_TLS_INFORMATION *tlsinfo = (PROCESS_TLS_INFORMATION *)buffer;
void **save_tls_pointers[2];
void *tls_pointer[4], *new_tls_pointer[4], *tls_pointer2[4], *new_tls_pointer2[4];
unsigned int i;
DWORD thread_id, curr_thread_id = GetCurrentThreadId();
NTSTATUS status;
HANDLE thread;
BOOL wow = is_wow64 && !old_wow64;
thread = CreateThread( NULL, 0, test_set_process_tls_info_thread, NULL, CREATE_SUSPENDED, &thread_id );
do
{
/* workaround currently present Wine bug when thread teb may be not available immediately
* after creating a thread before it is initialized on the Unix side. */
Sleep( 1 );
status = NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL );
ok( !status, "got %#lx.\n", status );
} while (!(thread_teb = tbi.TebBaseAddress));
ok( !thread_teb->ThreadLocalStoragePointer, "got %p.\n", thread_teb->ThreadLocalStoragePointer );
save_tls_pointers[0] = teb->ThreadLocalStoragePointer;
save_tls_pointers[1] = thread_teb->ThreadLocalStoragePointer;
for (i = 0; i < ARRAY_SIZE(tls_pointer); ++i)
{
tls_pointer[i] = (void *)(ULONG_PTR)(i + 1);
new_tls_pointer[i] = (void *)(ULONG_PTR)(i + 20);
}
teb->ThreadLocalStoragePointer = tls_pointer;
/* This flag probably requests WOW64 teb update. */
tlsinfo->Flags = 1;
tlsinfo->ThreadDataCount = 1;
tlsinfo->OperationType = ProcessTlsReplaceVector;
tlsinfo->TlsVectorLength = 2;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[0].ThreadId = thread_id;
tlsinfo->ThreadData[0].TlsVector = new_tls_pointer;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
if (wow)
{
ok( !status, "got %#lx.\n", status );
ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
}
else
{
ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status );
ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags );
ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
}
/* Other PROCESS_TLS_INFORMATION flags are invalid. STATUS_INFO_LENGTH_MISMATCH is weird but that's for any flag
* besides 1. */
tlsinfo->Flags = 2;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[0].ThreadId = thread_id;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status );
ok( tlsinfo->Flags == 2, "got %#lx.\n", tlsinfo->Flags );
ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
/* Nonzero THREAD_TLS_INFORMATION flags on input are invalid. */
tlsinfo->Flags = 0;
tlsinfo->ThreadData[0].Flags = 1;
tlsinfo->ThreadData[0].ThreadId = thread_id;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadData[0].Flags == 1, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->OperationType = MaxProcessTlsOperation;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
/* Unknown operation type. */
ok( status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status );
tlsinfo->OperationType = ProcessTlsReplaceVector;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]) + 8);
/* Larger data size. */
ok( (!wow && status == STATUS_INFO_LENGTH_MISMATCH) || (wow && !status), "got %#lx.\n", status );
/* Zero thread count. */
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadDataCount = 0;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
tlsinfo->ThreadDataCount = 1;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
/* ThreadId is output parameter, ignored on input and contains the thread where the data were assigned on
* output. */
ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
/* TlsVector contains the repaced vector on output. */
ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
for (i = 0; i < ARRAY_SIZE(tls_pointer); ++i)
{
ok( tls_pointer[i] == (void *)(ULONG_PTR)(i + 1), "got %p.\n", tls_pointer );
/* TlsVectorLength pointers are copied from the old vector to the new one. */
if (i < 2)
ok( new_tls_pointer[i] == (void *)(ULONG_PTR)(i + 1), "got %p.\n", tls_pointer );
else
ok( new_tls_pointer[i] == (void *)(ULONG_PTR)(i + 20), "got %p.\n", tls_pointer );
}
teb->ThreadLocalStoragePointer = NULL;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[0].TlsVector = new_tls_pointer;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
/* Threads with NULL ThreadLocalStoragePointer are ignored. */
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].TlsVector == new_tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
memcpy( tls_pointer2, tls_pointer, sizeof(tls_pointer2) );
thread_teb->ThreadLocalStoragePointer = tls_pointer2;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( thread_teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer2, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
/* Two eligible threads, data for only one are provided, that succeeds. */
teb->ThreadLocalStoragePointer = tls_pointer;
thread_teb->ThreadLocalStoragePointer = tls_pointer2;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[0].TlsVector = new_tls_pointer;
thread_teb->ThreadLocalStoragePointer = tls_pointer2;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
ok( tlsinfo->ThreadDataCount == 1, "got %#lx.\n", tlsinfo->ThreadDataCount );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
ok( thread_teb->ThreadLocalStoragePointer == tls_pointer2, "wrong vector.\n" );
/* Set for both threads at once as probably intended. Provide an extra data for the missing third thread
* which won't be used. */
teb->ThreadLocalStoragePointer = tls_pointer;
thread_teb->ThreadLocalStoragePointer = tls_pointer2;
memcpy( new_tls_pointer2, new_tls_pointer, sizeof(new_tls_pointer2) );
tlsinfo->ThreadDataCount = 3;
tlsinfo->ThreadData[0].TlsVector = new_tls_pointer;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[1].TlsVector = new_tls_pointer2;
tlsinfo->ThreadData[1].Flags = 0;
tlsinfo->ThreadData[2].TlsVector = (void *)0xdeadbeef;
tlsinfo->ThreadData[2].Flags = 0;
tlsinfo->ThreadData[2].ThreadId = 0xdeadbeef;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
ok( tlsinfo->ThreadData[1].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[1].Flags );
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( tlsinfo->ThreadData[1].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId );
ok( tlsinfo->ThreadData[1].TlsVector == tls_pointer2, "got %p.\n", tlsinfo->ThreadData[1].TlsVector );
ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags );
ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId );
ok( tlsinfo->ThreadData[2].TlsVector == (void *)0xdeadbeef, "got %p.\n", tlsinfo->ThreadData[2].TlsVector );
/* Test with unaccessible data. */
tlsinfo->ThreadData[0].TlsVector = new_tls_pointer;
tlsinfo->ThreadData[0].ThreadId = 0;
tlsinfo->ThreadData[0].Flags = 0;
tlsinfo->ThreadData[1].TlsVector = new_tls_pointer2;
tlsinfo->ThreadData[1].ThreadId = 0;
tlsinfo->ThreadData[1].Flags = 0;
teb->ThreadLocalStoragePointer = tls_pointer;
thread_teb->ThreadLocalStoragePointer = (void *)0xdeadbee0;
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_ACCESS_VIOLATION, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount );
if (wow)
{
ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( !tlsinfo->ThreadData[0].ThreadId, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( tlsinfo->ThreadData[0].TlsVector == new_tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
}
else
{
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector );
}
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( !tlsinfo->ThreadData[1].Flags, "got %#lx.\n", tlsinfo->ThreadData[1].Flags );
ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" );
ok( !tlsinfo->ThreadData[1].ThreadId, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId );
ok( tlsinfo->ThreadData[1].TlsVector == new_tls_pointer2, "got %p.\n", tlsinfo->ThreadData[1].TlsVector );
ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags );
ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId );
ok( tlsinfo->ThreadData[2].TlsVector == (void *)0xdeadbeef, "got %p.\n", tlsinfo->ThreadData[2].TlsVector );
/* Test replacing TLS index. */
teb->ThreadLocalStoragePointer = new_tls_pointer;
thread_teb->ThreadLocalStoragePointer = new_tls_pointer2;
new_tls_pointer[1] = (void *)0xcccccccc;
new_tls_pointer2[1] = (void *)0xdddddddd;
tlsinfo->ThreadDataCount = 3;
tlsinfo->OperationType = ProcessTlsReplaceIndex;
tlsinfo->TlsIndex = 1;
for (i = 0; i < 3; ++i)
{
tlsinfo->ThreadData[i].Flags = 0;
tlsinfo->ThreadData[i].ThreadId = 0xdeadbeef;
tlsinfo->ThreadData[i].TlsModulePointer = (void *)((ULONG_PTR)i + 1);
}
status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo,
offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]));
ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags );
ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount );
ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags );
ok( (ULONG_PTR)new_tls_pointer[1] == 1, "got %p.\n", new_tls_pointer[1] );
ok( tlsinfo->ThreadData[0].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId );
ok( (ULONG_PTR)tlsinfo->ThreadData[0].TlsModulePointer == 0xcccccccc, "got %p.\n", tlsinfo->ThreadData[0].TlsModulePointer );
ok( tlsinfo->ThreadData[1].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[1].Flags );
ok( (ULONG_PTR)new_tls_pointer2[1] == 2, "got %p.\n", new_tls_pointer2[1] );
ok( tlsinfo->ThreadData[1].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId );
ok( (ULONG_PTR)tlsinfo->ThreadData[1].TlsModulePointer == 0xdddddddd, "got %p.\n", tlsinfo->ThreadData[1].TlsModulePointer );
ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags );
ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId );
ok( (ULONG_PTR)tlsinfo->ThreadData[2].TlsModulePointer == 3, "got %p.\n", tlsinfo->ThreadData[2].TlsModulePointer );
/* Restore original TLS data. */
teb->ThreadLocalStoragePointer = save_tls_pointers[0];
thread_teb->ThreadLocalStoragePointer = save_tls_pointers[1];
ResumeThread( thread );
WaitForSingleObject( thread, INFINITE );
CloseHandle( thread );
}
START_TEST(info) START_TEST(info)
{ {
char **argv; char **argv;
@ -4167,4 +4439,5 @@ START_TEST(info)
test_process_token(argc, argv); test_process_token(argc, argv);
test_process_id(); test_process_id();
test_processor_idle_cycle_time(); test_processor_idle_cycle_time();
test_set_process_tls_info();
} }

View file

@ -1711,6 +1711,33 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class,
process_error_mode = *(UINT *)info; process_error_mode = *(UINT *)info;
break; break;
case ProcessTlsInformation:
{
PROCESS_TLS_INFORMATION *t = info;
unsigned int i;
if (handle != NtCurrentProcess())
{
FIXME( "ProcessTlsInformation is not supported for the other process yet, handle %p.\n", handle );
return STATUS_INVALID_HANDLE;
}
if (size < sizeof(*t) || size != offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount]))
return STATUS_INFO_LENGTH_MISMATCH;
if (t->Flags & ~PROCESS_TLS_INFORMATION_WOW64)
{
WARN( "ProcessTlsInformation: unknown flags %#x.\n", (int)t->Flags );
return STATUS_INFO_LENGTH_MISMATCH;
}
if (t->Flags & PROCESS_TLS_INFORMATION_WOW64 && !(is_win64 && is_wow64()))
return STATUS_INVALID_PARAMETER;
if (t->OperationType >= MaxProcessTlsOperation) return STATUS_INFO_LENGTH_MISMATCH;
for (i = 0; i < t->ThreadDataCount; ++i)
if (t->ThreadData[i].Flags) return STATUS_INVALID_PARAMETER;
ret = virtual_set_tls_information( t );
break;
}
case ProcessAffinityMask: case ProcessAffinityMask:
{ {
const ULONG_PTR system_mask = get_system_affinity_mask(); const ULONG_PTR system_mask = get_system_affinity_mask();

View file

@ -282,6 +282,7 @@ extern TEB *virtual_alloc_first_teb(void);
extern NTSTATUS virtual_alloc_teb( TEB **ret_teb ); extern NTSTATUS virtual_alloc_teb( TEB **ret_teb );
extern void virtual_free_teb( TEB *teb ); extern void virtual_free_teb( TEB *teb );
extern NTSTATUS virtual_clear_tls_index( ULONG index ); extern NTSTATUS virtual_clear_tls_index( ULONG index );
extern NTSTATUS virtual_set_tls_information( PROCESS_TLS_INFORMATION *t );
extern NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR limit_low, ULONG_PTR limit_high, extern NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR limit_low, ULONG_PTR limit_high,
SIZE_T reserve_size, SIZE_T commit_size, BOOL guard_page ); SIZE_T reserve_size, SIZE_T commit_size, BOOL guard_page );
extern void virtual_map_user_shared_data(void); extern void virtual_map_user_shared_data(void);

View file

@ -3874,6 +3874,98 @@ NTSTATUS virtual_clear_tls_index( ULONG index )
} }
/***********************************************************************
* virtual_set_tls_information_teb
*/
static NTSTATUS virtual_set_tls_information_teb( PROCESS_TLS_INFORMATION *t, unsigned int *idx, TEB *teb )
{
__TRY
{
#ifdef _WIN64
if (t->Flags & PROCESS_TLS_INFORMATION_WOW64)
{
WOW_TEB *wow_teb = get_wow_teb( teb );
ULONG *ptr;
if (wow_teb && wow_teb->ThreadLocalStoragePointer)
{
if (t->OperationType == ProcessTlsReplaceVector)
{
ptr = t->ThreadData[*idx].TlsVector;
memcpy( ptr, ULongToPtr( wow_teb->ThreadLocalStoragePointer ), sizeof(*ptr) * t->TlsVectorLength );
t->ThreadData[*idx].TlsVector = ULongToPtr( InterlockedExchange( (LONG *)&wow_teb->ThreadLocalStoragePointer, PtrToLong( ptr )));
t->ThreadData[*idx].ThreadId = wow_teb->ClientId.UniqueThread;
}
else
{
ptr = ULongToPtr( wow_teb->ThreadLocalStoragePointer );
t->ThreadData[*idx].TlsModulePointer =
ULongToPtr( InterlockedExchange( (LONG *)&ptr[t->TlsIndex],
PtrToLong( t->ThreadData[*idx].TlsModulePointer )));
}
t->ThreadData[*idx].Flags = THREAD_TLS_INFORMATION_ASSIGNED;
++*idx;
}
}
else
#endif
if (teb->ThreadLocalStoragePointer)
{
void **ptr;
if (t->OperationType == ProcessTlsReplaceVector)
{
ptr = t->ThreadData[*idx].TlsVector;
memcpy( ptr, teb->ThreadLocalStoragePointer, sizeof(*ptr) * t->TlsVectorLength );
t->ThreadData[*idx].TlsVector = InterlockedExchangePointer( &teb->ThreadLocalStoragePointer, ptr );
t->ThreadData[*idx].ThreadId = HandleToULong( teb->ClientId.UniqueThread );
#ifdef __x86_64__ /* macOS-specific hack */
if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = ptr;
#endif
}
else
{
ptr = teb->ThreadLocalStoragePointer;
t->ThreadData[*idx].TlsModulePointer = InterlockedExchangePointer( &ptr[t->TlsIndex],
t->ThreadData[*idx].TlsModulePointer );
}
t->ThreadData[*idx].Flags = THREAD_TLS_INFORMATION_ASSIGNED;
++*idx;
}
}
__EXCEPT
{
return STATUS_ACCESS_VIOLATION;
}
__ENDTRY
return STATUS_SUCCESS;
}
/***********************************************************************
* virtual_set_tls_information
*/
NTSTATUS virtual_set_tls_information( PROCESS_TLS_INFORMATION *t )
{
struct ntdll_thread_data *thread_data;
NTSTATUS ret = STATUS_SUCCESS;
unsigned int idx = 0;
sigset_t sigset;
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
LIST_FOR_EACH_ENTRY_REV( thread_data, &teb_list, struct ntdll_thread_data, entry )
{
TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch );
if (idx == t->ThreadDataCount) break;
if ((ret = virtual_set_tls_information_teb( t, &idx, teb ))) break;
}
server_leave_uninterrupted_section( &virtual_mutex, &sigset );
return ret;
}
/*********************************************************************** /***********************************************************************
* virtual_alloc_thread_stack * virtual_alloc_thread_stack
*/ */

View file

@ -894,6 +894,39 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args )
} }
else return STATUS_INVALID_PARAMETER; else return STATUS_INVALID_PARAMETER;
case ProcessTlsInformation:
{
PROCESS_TLS_INFORMATION32 *t32 = ptr;
PROCESS_TLS_INFORMATION *t;
ULONG i;
if (len >= sizeof(*t32) && len >= offsetof(PROCESS_TLS_INFORMATION32, ThreadData[t32->ThreadDataCount]))
{
t = Wow64AllocateTemp( offsetof(PROCESS_TLS_INFORMATION, ThreadData[t32->ThreadDataCount]) );
t->Flags = t32->Flags ? t32->Flags : PROCESS_TLS_INFORMATION_WOW64;
t->OperationType = t32->OperationType;
t->ThreadDataCount = t32->ThreadDataCount;
t->TlsIndex = t32->TlsIndex;
for (i = 0; i < t->ThreadDataCount; ++i)
{
t->ThreadData[i].Flags = t32->ThreadData[i].Flags;
t->ThreadData[i].ThreadId = t32->ThreadData[i].ThreadId;
t->ThreadData[i].TlsVector = ULongToPtr( t32->ThreadData[i].TlsVector );
}
if (!(status = NtSetInformationProcess( handle, class, t, offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount]) )))
{
for (i = 0; i < t->ThreadDataCount; ++i)
{
t32->ThreadData[i].Flags = t->ThreadData[i].Flags;
t32->ThreadData[i].ThreadId = t->ThreadData[i].ThreadId;
t32->ThreadData[i].TlsVector = PtrToUlong( t->ThreadData[i].TlsVector );
}
}
return status;
}
else return STATUS_INFO_LENGTH_MISMATCH;
}
case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */
if (len >= sizeof(ULONG)) if (len >= sizeof(ULONG))
{ {

View file

@ -326,6 +326,30 @@ typedef struct
ULONG DefaultBase; ULONG DefaultBase;
} RTL_PROCESS_MODULE_INFORMATION_EX32; } RTL_PROCESS_MODULE_INFORMATION_EX32;
typedef struct
{
ULONG Flags;
union
{
ULONG TlsVector;
ULONG TlsModulePointer;
};
ULONG ThreadId;
} THREAD_TLS_INFORMATION32;
typedef struct
{
ULONG Flags;
ULONG OperationType;
ULONG ThreadDataCount;
union
{
ULONG TlsIndex;
ULONG TlsVectorLength;
};
THREAD_TLS_INFORMATION32 ThreadData[1];
} PROCESS_TLS_INFORMATION32;
typedef struct typedef struct
{ {
ULONG BaseAddress; ULONG BaseAddress;

View file

@ -2668,6 +2668,41 @@ typedef struct _PROCESS_CYCLE_TIME_INFORMATION {
ULONGLONG CurrentCycleCount; ULONGLONG CurrentCycleCount;
} PROCESS_CYCLE_TIME_INFORMATION, *PPROCESS_CYCLE_TIME_INFORMATION; } PROCESS_CYCLE_TIME_INFORMATION, *PPROCESS_CYCLE_TIME_INFORMATION;
typedef struct _THREAD_TLS_INFORMATION
{
ULONG Flags;
union
{
void *TlsVector;
void *TlsModulePointer;
};
ULONG_PTR ThreadId;
} THREAD_TLS_INFORMATION, * PTHREAD_TLS_INFORMATION;
#define THREAD_TLS_INFORMATION_ASSIGNED 0x2
typedef enum _PROCESS_TLS_INFORMATION_TYPE
{
ProcessTlsReplaceIndex,
ProcessTlsReplaceVector,
MaxProcessTlsOperation
} PROCESS_TLS_INFORMATION_TYPE, *PPROCESS_TLS_INFORMATION_TYPE;
typedef struct _PROCESS_TLS_INFORMATION
{
ULONG Flags;
ULONG OperationType;
ULONG ThreadDataCount;
union
{
ULONG TlsIndex;
ULONG TlsVectorLength;
};
THREAD_TLS_INFORMATION ThreadData[1];
} PROCESS_TLS_INFORMATION, *PPROCESS_TLS_INFORMATION;
#define PROCESS_TLS_INFORMATION_WOW64 1
typedef struct _PROCESS_STACK_ALLOCATION_INFORMATION typedef struct _PROCESS_STACK_ALLOCATION_INFORMATION
{ {
SIZE_T ReserveSize; SIZE_T ReserveSize;