mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 22:28:36 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
- kernel32!BaseCreateStack() is compatible with ntdll!RtlpCreateUserStack().
- When checking whether a stack guard page can be added, its size has to
be accounted for in the checking logic.
- We have to satisfy the PEB::MinimumStackCommit constraint.
- We cannot use PEB::GuaranteedStackBytes in BaseCreateStack() since it is
nowhere initialized (default is 0). It gets initialized to a non-zero
value when the user manually calls SetThreadStackGuarantee().
https://www.installsetupconfig.com/win32programming/windowsthreadsprocessapis7_6.html
- RtlpCreateUserStack(): Fix memory leak in failure case.
- RtlpFreeUserStack() doesn't need to return anything.
See also commit 1bc59379
(r59868).
CORE-11319
353 lines
11 KiB
C
353 lines
11 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* PURPOSE: Rtl user thread functions
|
|
* FILE: lib/rtl/thread.c
|
|
* PROGRAMERS:
|
|
* Alex Ionescu (alex@relsoft.net)
|
|
* Eric Kohl
|
|
* KJK::Hyperion
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <rtl.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* PRIVATE FUNCTIONS *******************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpCreateUserStack(IN HANDLE ProcessHandle,
|
|
IN SIZE_T StackReserve OPTIONAL,
|
|
IN SIZE_T StackCommit OPTIONAL,
|
|
IN ULONG StackZeroBits OPTIONAL,
|
|
OUT PINITIAL_TEB InitialTeb)
|
|
{
|
|
NTSTATUS Status;
|
|
SYSTEM_BASIC_INFORMATION SystemBasicInfo;
|
|
PIMAGE_NT_HEADERS Headers;
|
|
ULONG_PTR Stack;
|
|
BOOLEAN UseGuard;
|
|
ULONG Dummy;
|
|
SIZE_T MinimumStackCommit, GuardPageSize;
|
|
|
|
/* Get some memory information */
|
|
Status = ZwQuerySystemInformation(SystemBasicInformation,
|
|
&SystemBasicInfo,
|
|
sizeof(SYSTEM_BASIC_INFORMATION),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Use the Image Settings if we are dealing with the current Process */
|
|
if (ProcessHandle == NtCurrentProcess())
|
|
{
|
|
/* Get the Image Headers */
|
|
Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
|
|
if (!Headers) return STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* If we didn't get the parameters, find them ourselves */
|
|
if (StackReserve == 0)
|
|
StackReserve = Headers->OptionalHeader.SizeOfStackReserve;
|
|
if (StackCommit == 0)
|
|
StackCommit = Headers->OptionalHeader.SizeOfStackCommit;
|
|
|
|
MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit;
|
|
if ((MinimumStackCommit != 0) && (StackCommit < MinimumStackCommit))
|
|
{
|
|
StackCommit = MinimumStackCommit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use the System Settings if needed */
|
|
if (StackReserve == 0)
|
|
StackReserve = SystemBasicInfo.AllocationGranularity;
|
|
if (StackCommit == 0)
|
|
StackCommit = SystemBasicInfo.PageSize;
|
|
}
|
|
|
|
/* Check if the commit is higher than the reserve */
|
|
if (StackCommit >= StackReserve)
|
|
{
|
|
/* Grow the reserve beyond the commit, up to 1MB alignment */
|
|
StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
|
|
}
|
|
|
|
/* Align everything to Page Size */
|
|
StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
|
|
StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
|
|
|
|
/* Reserve memory for the stack */
|
|
Stack = 0;
|
|
Status = ZwAllocateVirtualMemory(ProcessHandle,
|
|
(PVOID*)&Stack,
|
|
StackZeroBits,
|
|
&StackReserve,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Now set up some basic Initial TEB Parameters */
|
|
InitialTeb->AllocatedStackBase = (PVOID)Stack;
|
|
InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
|
|
InitialTeb->PreviousStackBase = NULL;
|
|
InitialTeb->PreviousStackLimit = NULL;
|
|
|
|
/* Update the stack position */
|
|
Stack += StackReserve - StackCommit;
|
|
|
|
/* Check if we can add a guard page */
|
|
if (StackReserve >= StackCommit + SystemBasicInfo.PageSize)
|
|
{
|
|
Stack -= SystemBasicInfo.PageSize;
|
|
StackCommit += SystemBasicInfo.PageSize;
|
|
UseGuard = TRUE;
|
|
}
|
|
else
|
|
{
|
|
UseGuard = FALSE;
|
|
}
|
|
|
|
/* Allocate memory for the stack */
|
|
Status = ZwAllocateVirtualMemory(ProcessHandle,
|
|
(PVOID*)&Stack,
|
|
0,
|
|
&StackCommit,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
GuardPageSize = 0;
|
|
ZwFreeVirtualMemory(ProcessHandle, (PVOID*)&Stack, &GuardPageSize, MEM_RELEASE);
|
|
return Status;
|
|
}
|
|
|
|
/* Now set the current Stack Limit */
|
|
InitialTeb->StackLimit = (PVOID)Stack;
|
|
|
|
/* Create a guard page if needed */
|
|
if (UseGuard)
|
|
{
|
|
GuardPageSize = SystemBasicInfo.PageSize;
|
|
Status = ZwProtectVirtualMemory(ProcessHandle,
|
|
(PVOID*)&Stack,
|
|
&GuardPageSize,
|
|
PAGE_GUARD | PAGE_READWRITE,
|
|
&Dummy);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Update the Stack Limit keeping in mind the Guard Page */
|
|
InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit +
|
|
GuardPageSize);
|
|
}
|
|
|
|
/* We are done! */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
RtlpFreeUserStack(IN HANDLE ProcessHandle,
|
|
IN PINITIAL_TEB InitialTeb)
|
|
{
|
|
SIZE_T Dummy = 0;
|
|
|
|
/* Free the Stack */
|
|
ZwFreeVirtualMemory(ProcessHandle,
|
|
&InitialTeb->AllocatedStackBase,
|
|
&Dummy,
|
|
MEM_RELEASE);
|
|
|
|
/* Clear the initial TEB */
|
|
RtlZeroMemory(InitialTeb, sizeof(*InitialTeb));
|
|
}
|
|
|
|
/* FUNCTIONS ***************************************************************/
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
__cdecl
|
|
RtlSetThreadIsCritical(IN BOOLEAN NewValue,
|
|
OUT PBOOLEAN OldValue OPTIONAL,
|
|
IN BOOLEAN NeedBreaks)
|
|
{
|
|
ULONG BreakOnTermination;
|
|
|
|
/* Initialize to FALSE */
|
|
if (OldValue) *OldValue = FALSE;
|
|
|
|
/* Fail, if the critical breaks flag is required but is not set */
|
|
if ((NeedBreaks) &&
|
|
!(NtCurrentPeb()->NtGlobalFlag & FLG_ENABLE_SYSTEM_CRIT_BREAKS))
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/* Check if the caller wants the old value */
|
|
if (OldValue)
|
|
{
|
|
/* Query and return the old break on termination flag for the process */
|
|
ZwQueryInformationThread(NtCurrentThread(),
|
|
ThreadBreakOnTermination,
|
|
&BreakOnTermination,
|
|
sizeof(ULONG),
|
|
NULL);
|
|
*OldValue = (BOOLEAN)BreakOnTermination;
|
|
}
|
|
|
|
/* Set the break on termination flag for the process */
|
|
BreakOnTermination = NewValue;
|
|
return ZwSetInformationThread(NtCurrentThread(),
|
|
ThreadBreakOnTermination,
|
|
&BreakOnTermination,
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlCreateUserThread(IN HANDLE ProcessHandle,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
|
|
IN BOOLEAN CreateSuspended,
|
|
IN ULONG StackZeroBits OPTIONAL,
|
|
IN SIZE_T StackReserve OPTIONAL,
|
|
IN SIZE_T StackCommit OPTIONAL,
|
|
IN PTHREAD_START_ROUTINE StartAddress,
|
|
IN PVOID Parameter OPTIONAL,
|
|
OUT PHANDLE ThreadHandle OPTIONAL,
|
|
OUT PCLIENT_ID ClientId OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
CLIENT_ID ThreadCid;
|
|
INITIAL_TEB InitialTeb;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
CONTEXT Context;
|
|
|
|
/* First, we'll create the Stack */
|
|
Status = RtlpCreateUserStack(ProcessHandle,
|
|
StackReserve,
|
|
StackCommit,
|
|
StackZeroBits,
|
|
&InitialTeb);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Next, we'll set up the Initial Context */
|
|
RtlInitializeContext(ProcessHandle,
|
|
&Context,
|
|
Parameter,
|
|
StartAddress,
|
|
InitialTeb.StackBase);
|
|
|
|
/* We are now ready to create the Kernel Thread Object */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
SecurityDescriptor);
|
|
Status = ZwCreateThread(&Handle,
|
|
THREAD_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
ProcessHandle,
|
|
&ThreadCid,
|
|
&Context,
|
|
&InitialTeb,
|
|
CreateSuspended);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Free the stack */
|
|
RtlpFreeUserStack(ProcessHandle, &InitialTeb);
|
|
}
|
|
else
|
|
{
|
|
/* Return thread data */
|
|
if (ThreadHandle)
|
|
*ThreadHandle = Handle;
|
|
else
|
|
NtClose(Handle);
|
|
if (ClientId) *ClientId = ThreadCid;
|
|
}
|
|
|
|
/* Return success or the previous failure */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
RtlExitUserThread(NTSTATUS Status)
|
|
{
|
|
/* Call the Loader and tell him to notify the DLLs */
|
|
LdrShutdownThread();
|
|
|
|
/* Shut us down */
|
|
NtCurrentTeb()->FreeStackOnTermination = TRUE;
|
|
NtTerminateThread(NtCurrentThread(), Status);
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
RtlFreeUserThreadStack(HANDLE ProcessHandle,
|
|
HANDLE ThreadHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
THREAD_BASIC_INFORMATION ThreadBasicInfo;
|
|
SIZE_T Dummy, Size = 0;
|
|
PVOID StackLocation;
|
|
|
|
/* Query the Basic Info */
|
|
Status = NtQueryInformationThread(ThreadHandle,
|
|
ThreadBasicInformation,
|
|
&ThreadBasicInfo,
|
|
sizeof(THREAD_BASIC_INFORMATION),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status) || !ThreadBasicInfo.TebBaseAddress) return;
|
|
|
|
/* Get the deallocation stack */
|
|
Status = NtReadVirtualMemory(ProcessHandle,
|
|
&((PTEB)ThreadBasicInfo.TebBaseAddress)->
|
|
DeallocationStack,
|
|
&StackLocation,
|
|
sizeof(PVOID),
|
|
&Dummy);
|
|
if (!NT_SUCCESS(Status) || !StackLocation) return;
|
|
|
|
/* Free it */
|
|
NtFreeVirtualMemory(ProcessHandle, &StackLocation, &Size, MEM_RELEASE);
|
|
}
|
|
|
|
PTEB
|
|
NTAPI
|
|
_NtCurrentTeb(VOID)
|
|
{
|
|
/* Return the TEB */
|
|
return NtCurrentTeb();
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlRemoteCall(IN HANDLE Process,
|
|
IN HANDLE Thread,
|
|
IN PVOID CallSite,
|
|
IN ULONG ArgumentCount,
|
|
IN PULONG Arguments,
|
|
IN BOOLEAN PassContext,
|
|
IN BOOLEAN AlreadySuspended)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|