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;