diff --git a/modules/rostests/apitests/ntdll/probelib.c b/modules/rostests/apitests/ntdll/probelib.c index c13be6b4365..0c27dedcd5d 100644 --- a/modules/rostests/apitests/ntdll/probelib.c +++ b/modules/rostests/apitests/ntdll/probelib.c @@ -56,7 +56,7 @@ QuerySetProcessValidator( break; } - /* This one works different from the others */ + /* This one works differently from the others */ case ProcessUserModeIOPL: { if (ExpectedStatus == STATUS_INFO_LENGTH_MISMATCH) @@ -159,7 +159,7 @@ QuerySetProcessValidator( break; } - /* This one works different from the others */ + /* This one works differently from the others */ case ProcessUserModeIOPL: { if (ExpectedStatus == STATUS_INFO_LENGTH_MISMATCH) @@ -295,6 +295,21 @@ QuerySetThreadValidator( break; } + /* ThreadNameInformation is Windows 10+, but + * ReactOS supports this class, so don't exclude it */ + case ThreadNameInformation: + { +#ifndef __REACTOS__ + if (GetNTVersion() < _WIN32_WINNT_WIN10) + SpecialStatus = STATUS_INVALID_INFO_CLASS; +#else + /* This one works differently from the others */ + if (ExpectedStatus == STATUS_INFO_LENGTH_MISMATCH) + ExpectedStatus = STATUS_BUFFER_TOO_SMALL; +#endif + break; + } + default: { /* All of these classes only exist on Windows 7 and above */ @@ -381,6 +396,17 @@ QuerySetThreadValidator( break; } + /* ThreadNameInformation is Windows 10+, but + * ReactOS supports this class, so don't exclude it */ + case ThreadNameInformation: + { +#ifndef __REACTOS__ + if (GetNTVersion() < _WIN32_WINNT_WIN10) + SpecialStatus = STATUS_INVALID_INFO_CLASS; +#endif + break; + } + default: { /* All of these classes only exist on Windows 7 and above */ diff --git a/ntoskrnl/include/internal/ps_i.h b/ntoskrnl/include/internal/ps_i.h index b1efd639f3b..81ae8a74762 100644 --- a/ntoskrnl/include/internal/ps_i.h +++ b/ntoskrnl/include/internal/ps_i.h @@ -593,8 +593,15 @@ static const INFORMATION_CLASS_INFO PsThreadInfoClass[] = IQS_NONE, /* ThreadContainerId */ IQS_NONE, + /* ThreadNameInformation */ - IQS_NONE, + IQS_SAME + ( + UNICODE_STRING, + ULONG_PTR, + ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE + ), + /* ThreadSelectedCpuSets */ IQS_NONE, /* ThreadSystemThreadInformation */ diff --git a/ntoskrnl/include/internal/tag.h b/ntoskrnl/include/internal/tag.h index 6febe9d6e51..616bffad763 100644 --- a/ntoskrnl/include/internal/tag.h +++ b/ntoskrnl/include/internal/tag.h @@ -136,6 +136,7 @@ #define TAG_PS_APC 'pasP' /* Psap - Ps APC */ #define TAG_SHIM 'MIHS' #define TAG_QUOTA_BLOCK 'bQsP' +#define TAG_THREAD_NAME 'mNhT' /* Run-Time Library Tags */ #define TAG_HDTB 'BTDH' diff --git a/ntoskrnl/ps/kill.c b/ntoskrnl/ps/kill.c index 236e2033eb8..00a854aa2ca 100644 --- a/ntoskrnl/ps/kill.c +++ b/ntoskrnl/ps/kill.c @@ -416,11 +416,19 @@ PspDeleteThread(IN PVOID ObjectBody) } } - /* Cleanup impersionation information */ + /* Cleanup impersonation information */ PspDeleteThreadSecurity(Thread); + /* Free the thread name if set */ + if (Thread->ThreadName) + { + ExFreePoolWithTag(Thread->ThreadName, TAG_THREAD_NAME); + Thread->ThreadName = NULL; + } + /* Make sure the thread was inserted, before continuing */ - if (!Process) return; + if (!Process) + return; /* Check if the thread list is valid */ if (Thread->ThreadListEntry.Flink) diff --git a/ntoskrnl/ps/query.c b/ntoskrnl/ps/query.c index a038a00d745..b2e6288ffe0 100644 --- a/ntoskrnl/ps/query.c +++ b/ntoskrnl/ps/query.c @@ -2879,6 +2879,92 @@ NtSetInformationThread( break; } +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) || defined(__REACTOS__) + case ThreadNameInformation: + { + UNICODE_STRING CapturedThreadName; + PUNICODE_STRING NewThreadName; + + /* Check buffer length */ + if (ThreadInformationLength != sizeof(UNICODE_STRING)) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + /* Reference the thread. + * NOTE: Win10+ uses THREAD_SET_LIMITED_INFORMATION instead; + * however some tools misuse thread names to perform suspicious + * operations; therefore we try to mess with these by requiring + * a bit more of access rights. */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SET_INFORMATION, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) + break; + + /* Probe and capture the thread name */ + Status = ProbeAndCaptureUnicodeString(&CapturedThreadName, + PreviousMode, + (PUNICODE_STRING)ThreadInformation); + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(Thread); + break; + } + + /* Allocate a new buffer only if the thread name isn't empty + * (REMARK: We only consider Length instead of MaximumLength). + * If empty, just reset the thread name pointer to NULL instead + * of allocating an empty UNICODE_STRING. */ + NewThreadName = NULL; + if (CapturedThreadName.Length > 0) + { + ULONG Length = sizeof(UNICODE_STRING) + CapturedThreadName.Length; + NewThreadName = ExAllocatePoolWithTag(NonPagedPool, // FIXME: NonPagedPoolNx + Length, TAG_THREAD_NAME); + if (!NewThreadName) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + /* Copy the new thread name */ + NewThreadName->Length = + NewThreadName->MaximumLength = CapturedThreadName.Length; + NewThreadName->Buffer = (PWCH)(NewThreadName + 1); + RtlCopyMemory(NewThreadName->Buffer, + CapturedThreadName.Buffer, + CapturedThreadName.Length); + } + } + + /* Free the captured string */ + ReleaseCapturedUnicodeString(&CapturedThreadName, PreviousMode); + + /* Replace the original thread name with the new one */ + if (NT_SUCCESS(Status)) + { + PUNICODE_STRING OldThreadName; + PspLockThreadSecurityExclusive(Thread); + OldThreadName = Thread->ThreadName; + Thread->ThreadName = NewThreadName; + PspUnlockThreadSecurityExclusive(Thread); + + /* Free the old thread name */ + if (OldThreadName) + ExFreePoolWithTag(OldThreadName, TAG_THREAD_NAME); + } + + /* Dereference the thread */ + ObDereferenceObject(Thread); + break; + } +#endif /* (NTDDI_VERSION >= NTDDI_WIN10_RS1) || defined(__REACTOS__) */ + /* Anything else */ default: /* Not yet implemented */ @@ -3388,6 +3474,73 @@ NtQueryInformationThread( break; } +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) || defined(__REACTOS__) + case ThreadNameInformation: + { + PUNICODE_STRING ThreadName; + + /* Reference the thread */ + Status = ObReferenceObjectByHandle(ThreadHandle, + // FIXME: Use THREAD_QUERY_LIMITED_INFORMATION when implemented + THREAD_QUERY_INFORMATION, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) + break; + + PspLockThreadSecurityShared(Thread); + + ThreadName = Thread->ThreadName; + + /* Set the return length (REMARK: We only + * consider Length instead of MaximumLength) */ + Length = sizeof(UNICODE_STRING); + Length += (ThreadName ? ThreadName->Length : 0); + if (ThreadInformationLength < Length) + { + PspUnlockThreadSecurityShared(Thread); + ObDereferenceObject(Thread); + Status = STATUS_BUFFER_TOO_SMALL; + /* As on Windows, and *not* STATUS_INFO_LENGTH_MISMATCH */ + break; + } + + /* Protect writes with SEH */ + _SEH2_TRY + { + PTHREAD_NAME_INFORMATION NameInfo = + (PTHREAD_NAME_INFORMATION)ThreadInformation; + if (ThreadName && (ThreadName->Length > 0)) + { + NameInfo->ThreadName.Length = + NameInfo->ThreadName.MaximumLength = ThreadName->Length; + NameInfo->ThreadName.Buffer = (PWCH)(&NameInfo->ThreadName + 1); + RtlCopyMemory(NameInfo->ThreadName.Buffer, + ThreadName->Buffer, + ThreadName->Length); + } + else + { + RtlInitEmptyUnicodeString(&NameInfo->ThreadName, NULL, 0); + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get exception code */ + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + PspUnlockThreadSecurityShared(Thread); + + /* Dereference the thread */ + ObDereferenceObject(Thread); + break; + } +#endif /* (NTDDI_VERSION >= NTDDI_WIN10_RS1) || defined(__REACTOS__) */ + /* Anything else */ default: /* Not yet implemented */ diff --git a/sdk/include/ndk/pstypes.h b/sdk/include/ndk/pstypes.h index 4dff85d9219..344a731d834 100644 --- a/sdk/include/ndk/pstypes.h +++ b/sdk/include/ndk/pstypes.h @@ -1340,6 +1340,11 @@ typedef struct _ETHREAD LIST_ENTRY AlpcWaitListEntry; KSEMAPHORE AlpcWaitSemaphore; ULONG CacheManagerCount; +#endif + // TODO: Missing Vista+ members +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) || defined(__REACTOS__) + PUNICODE_STRING ThreadName; + // TODO: Missing Win10+ members #endif } ETHREAD;