Test Driver Unlimited Solar Crown depends on that (both calling ProcessTlsInformation and hotpatches the same to fixup the thread local storage allocation from ntdll).
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/info.c | 266 ++++++++++++++++++++++++++++++++++++++++ include/winternl.h | 35 ++++++ 2 files changed, 301 insertions(+)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index ba19b248e43..cfe07ff98e7 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -4064,6 +4064,271 @@ static void test_processor_idle_cycle_time(void) 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) + { + todo_wine ok( !status, "got %#lx.\n", status ); + ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags ); + todo_wine ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + todo_wine ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + } + else + { + todo_wine 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 ); + } + if (status == STATUS_NOT_IMPLEMENTED) return; + + /* 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 ); + + tlsinfo->ThreadData[0].Flags = 0; + 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) { char **argv; @@ -4144,4 +4409,5 @@ START_TEST(info) test_process_token(argc, argv); test_process_id(); test_processor_idle_cycle_time(); + test_set_process_tls_info(); } diff --git a/include/winternl.h b/include/winternl.h index 4f24a921bb1..0f3abe1ccfb 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2653,6 +2653,41 @@ typedef struct _PROCESS_CYCLE_TIME_INFORMATION { ULONGLONG CurrentCycleCount; } 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 { SIZE_T ReserveSize;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/info.c | 9 ++-- dlls/ntdll/unix/process.c | 27 ++++++++++ dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 92 ++++++++++++++++++++++++++++++++++ dlls/wow64/process.c | 33 ++++++++++++ dlls/wow64/struct32.h | 24 +++++++++ 6 files changed, 181 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index cfe07ff98e7..0aceba9e799 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -4116,19 +4116,18 @@ static void test_set_process_tls_info(void) offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); if (wow) { - todo_wine ok( !status, "got %#lx.\n", status ); + ok( !status, "got %#lx.\n", status ); ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags ); - todo_wine ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); - todo_wine ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + 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 { - todo_wine ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + 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 ); } - if (status == STATUS_NOT_IMPLEMENTED) return;
/* Other PROCESS_TLS_INFORMATION flags are invalid. STATUS_INFO_LENGTH_MISMATCH is weird but that's for any flag * besides 1. */ diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index e00b5ca6ca6..aebc6145901 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1649,6 +1649,33 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, process_error_mode = *(UINT *)info; 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: { const ULONG_PTR system_mask = get_system_affinity_mask(); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 8ce45dfa0bc..579afce0b33 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -280,6 +280,7 @@ extern TEB *virtual_alloc_first_teb(void); extern NTSTATUS virtual_alloc_teb( TEB **ret_teb ); extern void virtual_free_teb( TEB *teb ); 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, SIZE_T reserve_size, SIZE_T commit_size, BOOL guard_page ); extern void virtual_map_user_shared_data(void); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index e168eed8c37..320a4e08f70 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -3870,6 +3870,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 */ diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 14b9307b447..8b15e65da93 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -893,6 +893,39 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) } 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 */ if (len == sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) { diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index b196bdd53ca..0bd422aff91 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -333,6 +333,30 @@ typedef struct ULONG DefaultBase; } 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 { ULONG BaseAddress;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/loader.c | 93 +++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 41 deletions(-)
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 0ed37ad390c..df92022b17c 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -139,6 +139,7 @@ typedef struct _wine_modref
static UINT tls_module_count = 32; /* number of modules with TLS directory */ 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_DEBUG critsect_debug = @@ -1332,10 +1333,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 ) { const IMAGE_TLS_DIRECTORY *dir; - ULONG i, size; + ULONG i, j, size; void *new_ptr; 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 ))) return FALSE; @@ -1354,6 +1355,9 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) (void *)dir->StartAddressOfRawData, (void *)dir->EndAddressOfRawData, dir->SizeOfZeroFill, (void *)dir->AddressOfIndex, (void *)dir->AddressOfCallBacks, dir->Characteristics, i );
+ t = RtlAllocateHeap( GetProcessHeap(), 0, offsetof( PROCESS_TLS_INFORMATION, ThreadData[tls_thread_count] )); + if (!t) return FALSE; + if (i == tls_module_count) { UINT new_count = tls_module_count * 2; @@ -1365,54 +1369,59 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) tls_module_count = new_count; }
- /* allocate the data block in all running threads */ - while (!NtGetNextThread( GetCurrentProcess(), thread, THREAD_QUERY_LIMITED_INFORMATION, 0, 0, &next )) + t->Flags = 0; + t->ThreadDataCount = tls_thread_count; + if (old_module_count < tls_module_count) + { + t->OperationType = ProcessTlsReplaceVector; + t->TlsVectorLength = old_module_count; + } + else + { + t->OperationType = ProcessTlsReplaceIndex; + t->TlsIndex = i; + } + for (j = 0; j < tls_thread_count; ++j) { - THREAD_BASIC_INFORMATION tbi; - TEB *teb; + void **vector; + + t->ThreadData[j].Flags = 0;
- if (thread) NtClose( thread ); - thread = next; - if (NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ) || !tbi.TebBaseAddress) + if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return FALSE; + memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); + memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); + + if (t->OperationType == ProcessTlsReplaceVector) { - ERR( "NtQueryInformationThread failed.\n" ); - continue; + vector = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*vector) ); + if (!vector) return FALSE; + t->ThreadData[j].TlsVector = vector; + vector[i] = new_ptr; } - teb = tbi.TebBaseAddress; - if (!teb->ThreadLocalStoragePointer) + 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; + } + + for (j = 0; j < tls_thread_count; ++j) + { + if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) && t->OperationType == ProcessTlsReplaceVector) { - /* Thread is not initialized by loader yet or already teared down. */ - TRACE( "thread %04lx NULL tls block.\n", HandleToULong(tbi.ClientId.UniqueThread) ); - continue; + /* 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. */ + RtlFreeHeap( GetProcessHeap(), 0, ((void **)t->ThreadData[j].TlsVector)[i] ); } - - if (old_module_count < tls_module_count) + if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) || t->OperationType == ProcessTlsReplaceIndex) { - 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 */ + /* FIXME: can't free old Tls vector here, should be freed at thread exit. */ + RtlFreeHeap( GetProcessHeap(), 0, t->ThreadData[j].TlsVector ); } - - if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return -1; - memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); - memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); - - TRACE( "thread %04lx slot %lu: %lu/%lu bytes at %p\n", - HandleToULong(teb->ClientId.UniqueThread), i, size, dir->SizeOfZeroFill, new_ptr ); - - RtlFreeHeap( GetProcessHeap(), 0, - InterlockedExchangePointer( (void **)teb->ThreadLocalStoragePointer + i, new_ptr )); } - if (thread) NtClose( thread ); - + RtlFreeHeap( GetProcessHeap(), 0, t ); *(DWORD *)dir->AddressOfIndex = i; tls_dirs[i] = *dir; return TRUE; @@ -1646,6 +1655,7 @@ static NTSTATUS alloc_thread_tls(void)
TRACE( "slot %u: %u/%lu bytes at %p\n", i, size, dir->SizeOfZeroFill, pointers[i] ); } + ++tls_thread_count; NtCurrentTeb()->ThreadLocalStoragePointer = pointers; #ifdef __x86_64__ /* macOS-specific hack */ if (NtCurrentTeb()->Instrumentation[0]) @@ -3930,6 +3940,7 @@ void WINAPI LdrShutdownThread(void) if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) { NtCurrentTeb()->ThreadLocalStoragePointer = NULL; + --tls_thread_count; #ifdef __x86_64__ /* macOS-specific hack */ if (NtCurrentTeb()->Instrumentation[0]) ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/module.c | 73 +++++++++++++++++++++++++----------- dlls/ntdll/loader.c | 51 +++++++++++++++++++------ 2 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index 4b4269c821b..93878a9bd22 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -141,6 +141,34 @@ static void create_test_dll( const char *name ) CloseHandle( handle ); }
+static BOOL is_old_loader_struct(void) +{ + LDR_DATA_TABLE_ENTRY *mod, *mod2; + LDR_DDAG_NODE *ddag_node; + NTSTATUS status; + HMODULE hexe; + + /* Check for old LDR data strcuture. */ + hexe = GetModuleHandleW( NULL ); + ok( !!hexe, "Got NULL exe handle.\n" ); + status = LdrFindEntryForAddress( hexe, &mod ); + ok( !status, "got %#lx.\n", status ); + if (!(ddag_node = mod->DdagNode)) + { + win_skip( "DdagNode is NULL, skipping tests.\n" ); + return TRUE; + } + ok( !!ddag_node->Modules.Flink, "Got NULL module link.\n" ); + mod2 = CONTAINING_RECORD(ddag_node->Modules.Flink, LDR_DATA_TABLE_ENTRY, NodeModuleLink); + ok( mod2 == mod || broken( (void **)mod2 == (void **)mod - 1 ), "got %p, expected %p.\n", mod2, mod ); + if (mod2 != mod) + { + win_skip( "Old LDR_DATA_TABLE_ENTRY structure, skipping tests.\n" ); + return TRUE; + } + return FALSE; +} + static void testGetModuleFileName(const char* name) { HMODULE hMod; @@ -1641,7 +1669,10 @@ static void test_tls_links(void) TEB *teb = NtCurrentTeb(), *thread_teb; THREAD_BASIC_INFORMATION tbi; NTSTATUS status; + ULONG i, count; HANDLE thread; + SIZE_T size; + void **ptr;
ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n");
@@ -1661,6 +1692,26 @@ static void test_tls_links(void) ResumeThread(thread); 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(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); @@ -1714,29 +1765,9 @@ static void test_base_address_index_tree(void) unsigned int tree_count, list_count = 0; LDR_DATA_TABLE_ENTRY *mod, *mod2; RTL_BALANCED_NODE *root, *node; - LDR_DDAG_NODE *ddag_node; - NTSTATUS status; - HMODULE hexe; char *base;
- /* Check for old LDR data strcuture. */ - hexe = GetModuleHandleW( NULL ); - ok( !!hexe, "Got NULL exe handle.\n" ); - status = LdrFindEntryForAddress( hexe, &mod ); - ok( !status, "got %#lx.\n", status ); - if (!(ddag_node = mod->DdagNode)) - { - win_skip( "DdagNode is NULL, skipping tests.\n" ); - return; - } - ok( !!ddag_node->Modules.Flink, "Got NULL module link.\n" ); - mod2 = CONTAINING_RECORD(ddag_node->Modules.Flink, LDR_DATA_TABLE_ENTRY, NodeModuleLink); - ok( mod2 == mod || broken( (void **)mod2 == (void **)mod - 1 ), "got %p, expected %p.\n", mod2, mod ); - if (mod2 != mod) - { - win_skip( "Old LDR_DATA_TABLE_ENTRY structure, skipping tests.\n" ); - return; - } + if (is_old_loader_struct()) return;
mod = CONTAINING_RECORD(first->Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); ok( mod->BaseAddressIndexNode.ParentValue || mod->BaseAddressIndexNode.Left || mod->BaseAddressIndexNode.Right, diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index df92022b17c..f3c3a446e20 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1324,6 +1324,36 @@ static BOOL is_dll_native_subsystem( LDR_DATA_TABLE_ENTRY *mod, const IMAGE_NT_H 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 * @@ -1387,13 +1417,13 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod )
t->ThreadData[j].Flags = 0;
- if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return FALSE; + if (!(new_ptr = alloc_tls_memory( FALSE, size + dir->SizeOfZeroFill ))) return FALSE; memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill );
if (t->OperationType == ProcessTlsReplaceVector) { - vector = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*vector) ); + vector = alloc_tls_memory( TRUE, tls_module_count * sizeof(*vector) ); if (!vector) return FALSE; t->ThreadData[j].TlsVector = vector; vector[i] = new_ptr; @@ -1413,12 +1443,12 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) { /* 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. */ - RtlFreeHeap( GetProcessHeap(), 0, ((void **)t->ThreadData[j].TlsVector)[i] ); + 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. */ - RtlFreeHeap( GetProcessHeap(), 0, t->ThreadData[j].TlsVector ); + free_tls_memory( t->ThreadData[j].TlsVector ); } } RtlFreeHeap( GetProcessHeap(), 0, t ); @@ -1632,8 +1662,7 @@ static NTSTATUS alloc_thread_tls(void) void **pointers; UINT i, size;
- if (!(pointers = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, - tls_module_count * sizeof(*pointers) ))) + if (!(pointers = alloc_tls_memory( TRUE, tls_module_count * sizeof(*pointers) ))) return STATUS_NO_MEMORY;
for (i = 0; i < tls_module_count; i++) @@ -1644,10 +1673,10 @@ static NTSTATUS alloc_thread_tls(void) size = dir->EndAddressOfRawData - dir->StartAddressOfRawData; 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] ); - RtlFreeHeap( GetProcessHeap(), 0, pointers ); + while (i) free_tls_memory( pointers[--i] ); + free_tls_memory( pointers ); return STATUS_NO_MEMORY; } memcpy( pointers[i], (void *)dir->StartAddressOfRawData, size ); @@ -3945,8 +3974,8 @@ void WINAPI LdrShutdownThread(void) if (NtCurrentTeb()->Instrumentation[0]) ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL; #endif - for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); - RtlFreeHeap( GetProcessHeap(), 0, pointers ); + for (i = 0; i < tls_module_count; i++) free_tls_memory( pointers[i] ); + free_tls_memory( pointers ); } RtlProcessFlsData( NtCurrentTeb()->FlsSlots, 2 ); NtCurrentTeb()->FlsSlots = NULL;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148605
Your paranoid android.
=== w10pro64_ar (64 bit report) ===
ntdll: info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. info.c:634: Test failed: extended 1: i 143 ((null)): Got NULL StackBase. info.c:635: Test failed: extended 1: i 143 ((null)): Got NULL StackLimit. info.c:636: Test failed: extended 1: i 143 ((null)): Got NULL Win32StartAddress. 2114:info: unhandled exception c0000005 at 00000000005417DC
Also I hope it opens the way to stop leaking TEBs on force terminating threads and to stop leaking tls vectors after reallocation (although I didn't try to guess native structures for the latter, while it is probably possible to use our own layout for the time being). But none of that is in this MR.