[KERNEL32][RTL] Diverse fixes/improvements for thread stack creation code. (#802)

- 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
This commit is contained in:
Hermès Bélusca-Maïto 2016-05-26 04:44:25 +02:00
parent 4895f21efa
commit effdb6f232
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
4 changed files with 102 additions and 75 deletions

View file

@ -165,8 +165,9 @@ CreateRemoteThread(IN HANDLE hProcess,
/* Create the Stack */ /* Create the Stack */
Status = BaseCreateStack(hProcess, Status = BaseCreateStack(hProcess,
dwStackSize, (dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION) ?
dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ? 0 : dwStackSize,
(dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION) ?
dwStackSize : 0, dwStackSize : 0,
&InitialTeb); &InitialTeb);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))

View file

@ -346,22 +346,25 @@ BaseFormatObjectAttributes(OUT POBJECT_ATTRIBUTES ObjectAttributes,
} }
/* /*
* Creates a stack for a thread or fiber * Creates a stack for a thread or fiber.
* NOTE: Adapted from sdk/lib/rtl/thread.c:RtlpCreateUserStack().
*/ */
NTSTATUS NTSTATUS
WINAPI WINAPI
BaseCreateStack(HANDLE hProcess, BaseCreateStack(
SIZE_T StackCommit, _In_ HANDLE hProcess,
SIZE_T StackReserve, _In_opt_ SIZE_T StackCommit,
PINITIAL_TEB InitialTeb) _In_opt_ SIZE_T StackReserve,
_Out_ PINITIAL_TEB InitialTeb)
{ {
NTSTATUS Status; NTSTATUS Status;
PIMAGE_NT_HEADERS Headers; PIMAGE_NT_HEADERS Headers;
ULONG_PTR Stack; ULONG_PTR Stack;
BOOLEAN UseGuard; BOOLEAN UseGuard;
ULONG PageSize, Dummy, AllocationGranularity; ULONG PageSize, AllocationGranularity, Dummy;
SIZE_T StackReserveHeader, StackCommitHeader, GuardPageSize, GuaranteedStackCommit; SIZE_T MinimumStackCommit, GuardPageSize;
DPRINT("BaseCreateStack (hProcess: %p, Max: %lx, Current: %lx)\n",
DPRINT("BaseCreateStack(hProcess: 0x%p, Max: 0x%lx, Current: 0x%lx)\n",
hProcess, StackReserve, StackCommit); hProcess, StackReserve, StackCommit);
/* Read page size */ /* Read page size */
@ -372,34 +375,38 @@ BaseCreateStack(HANDLE hProcess,
Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
if (!Headers) return STATUS_INVALID_IMAGE_FORMAT; if (!Headers) return STATUS_INVALID_IMAGE_FORMAT;
StackCommitHeader = Headers->OptionalHeader.SizeOfStackCommit; if (StackReserve == 0)
StackReserveHeader = Headers->OptionalHeader.SizeOfStackReserve; StackReserve = Headers->OptionalHeader.SizeOfStackReserve;
if (!StackReserve) StackReserve = StackReserveHeader; if (StackCommit == 0)
if (!StackCommit)
{ {
StackCommit = StackCommitHeader; StackCommit = Headers->OptionalHeader.SizeOfStackCommit;
} }
/* Check if the commit is higher than the reserve */
else if (StackCommit >= StackReserve) else if (StackCommit >= StackReserve)
{ {
/* Grow the reserve beyond the commit, up to 1MB alignment */
StackReserve = ROUND_UP(StackCommit, 1024 * 1024); StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
} }
/* Align everything to Page Size */
StackCommit = ROUND_UP(StackCommit, PageSize); StackCommit = ROUND_UP(StackCommit, PageSize);
StackReserve = ROUND_UP(StackReserve, AllocationGranularity); StackReserve = ROUND_UP(StackReserve, AllocationGranularity);
GuaranteedStackCommit = NtCurrentTeb()->GuaranteedStackBytes; MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit;
if ((GuaranteedStackCommit) && (StackCommit < GuaranteedStackCommit)) if ((MinimumStackCommit != 0) && (StackCommit < MinimumStackCommit))
{ {
StackCommit = GuaranteedStackCommit; StackCommit = MinimumStackCommit;
} }
/* Check if the commit is higher than the reserve */
if (StackCommit >= StackReserve) if (StackCommit >= StackReserve)
{ {
/* Grow the reserve beyond the commit, up to 1MB alignment */
StackReserve = ROUND_UP(StackCommit, 1024 * 1024); StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
} }
/* Align everything to Page Size */
StackCommit = ROUND_UP(StackCommit, PageSize); StackCommit = ROUND_UP(StackCommit, PageSize);
StackReserve = ROUND_UP(StackReserve, AllocationGranularity); StackReserve = ROUND_UP(StackReserve, AllocationGranularity);
@ -423,11 +430,11 @@ BaseCreateStack(HANDLE hProcess,
InitialTeb->PreviousStackBase = NULL; InitialTeb->PreviousStackBase = NULL;
InitialTeb->PreviousStackLimit = NULL; InitialTeb->PreviousStackLimit = NULL;
/* Update the Stack Position */ /* Update the stack position */
Stack += StackReserve - StackCommit; Stack += StackReserve - StackCommit;
/* Check if we will need a guard page */ /* Check if we can add a guard page */
if (StackReserve > StackCommit) if (StackReserve >= StackCommit + PageSize)
{ {
Stack -= PageSize; Stack -= PageSize;
StackCommit += PageSize; StackCommit += PageSize;
@ -456,11 +463,10 @@ BaseCreateStack(HANDLE hProcess,
/* Now set the current Stack Limit */ /* Now set the current Stack Limit */
InitialTeb->StackLimit = (PVOID)Stack; InitialTeb->StackLimit = (PVOID)Stack;
/* Create a guard page */ /* Create a guard page if needed */
if (UseGuard) if (UseGuard)
{ {
/* Set the guard page */ GuardPageSize = PageSize;
GuardPageSize = PAGE_SIZE;
Status = NtProtectVirtualMemory(hProcess, Status = NtProtectVirtualMemory(hProcess,
(PVOID*)&Stack, (PVOID*)&Stack,
&GuardPageSize, &GuardPageSize,
@ -481,10 +487,14 @@ BaseCreateStack(HANDLE hProcess,
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
/*
* NOTE: Adapted from sdk/lib/rtl/thread.c:RtlpFreeUserStack().
*/
VOID VOID
WINAPI WINAPI
BaseFreeThreadStack(IN HANDLE hProcess, BaseFreeThreadStack(
IN PINITIAL_TEB InitialTeb) _In_ HANDLE hProcess,
_In_ PINITIAL_TEB InitialTeb)
{ {
SIZE_T Dummy = 0; SIZE_T Dummy = 0;

View file

@ -183,10 +183,17 @@ BaseFormatObjectAttributes(OUT POBJECT_ATTRIBUTES ObjectAttributes,
NTSTATUS NTSTATUS
WINAPI WINAPI
BaseCreateStack(HANDLE hProcess, BaseCreateStack(
SIZE_T StackReserve, _In_ HANDLE hProcess,
SIZE_T StackCommit, _In_opt_ SIZE_T StackCommit,
PINITIAL_TEB InitialTeb); _In_opt_ SIZE_T StackReserve,
_Out_ PINITIAL_TEB InitialTeb);
VOID
WINAPI
BaseFreeThreadStack(
_In_ HANDLE hProcess,
_In_ PINITIAL_TEB InitialTeb);
VOID VOID
WINAPI WINAPI
@ -233,11 +240,6 @@ WINAPI
BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress, BaseThreadStartup(LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter); LPVOID lpParameter);
VOID
WINAPI
BaseFreeThreadStack(IN HANDLE hProcess,
IN PINITIAL_TEB InitialTeb);
__declspec(noreturn) __declspec(noreturn)
VOID VOID
WINAPI WINAPI

View file

@ -20,7 +20,7 @@
NTSTATUS NTSTATUS
NTAPI NTAPI
RtlpCreateUserStack(IN HANDLE hProcess, RtlpCreateUserStack(IN HANDLE ProcessHandle,
IN SIZE_T StackReserve OPTIONAL, IN SIZE_T StackReserve OPTIONAL,
IN SIZE_T StackCommit OPTIONAL, IN SIZE_T StackCommit OPTIONAL,
IN ULONG StackZeroBits OPTIONAL, IN ULONG StackZeroBits OPTIONAL,
@ -29,10 +29,10 @@ RtlpCreateUserStack(IN HANDLE hProcess,
NTSTATUS Status; NTSTATUS Status;
SYSTEM_BASIC_INFORMATION SystemBasicInfo; SYSTEM_BASIC_INFORMATION SystemBasicInfo;
PIMAGE_NT_HEADERS Headers; PIMAGE_NT_HEADERS Headers;
ULONG_PTR Stack = 0; ULONG_PTR Stack;
BOOLEAN UseGuard = FALSE; BOOLEAN UseGuard;
ULONG Dummy; ULONG Dummy;
SIZE_T GuardPageSize; SIZE_T MinimumStackCommit, GuardPageSize;
/* Get some memory information */ /* Get some memory information */
Status = ZwQuerySystemInformation(SystemBasicInformation, Status = ZwQuerySystemInformation(SystemBasicInformation,
@ -42,26 +42,34 @@ RtlpCreateUserStack(IN HANDLE hProcess,
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status)) return Status;
/* Use the Image Settings if we are dealing with the current Process */ /* Use the Image Settings if we are dealing with the current Process */
if (hProcess == NtCurrentProcess()) if (ProcessHandle == NtCurrentProcess())
{ {
/* Get the Image Headers */ /* Get the Image Headers */
Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
if (!Headers) return STATUS_INVALID_IMAGE_FORMAT; if (!Headers) return STATUS_INVALID_IMAGE_FORMAT;
/* If we didn't get the parameters, find them ourselves */ /* If we didn't get the parameters, find them ourselves */
if (!StackReserve) StackReserve = Headers->OptionalHeader. if (StackReserve == 0)
SizeOfStackReserve; StackReserve = Headers->OptionalHeader.SizeOfStackReserve;
if (!StackCommit) StackCommit = Headers->OptionalHeader. if (StackCommit == 0)
SizeOfStackCommit; StackCommit = Headers->OptionalHeader.SizeOfStackCommit;
MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit;
if ((MinimumStackCommit != 0) && (StackCommit < MinimumStackCommit))
{
StackCommit = MinimumStackCommit;
}
} }
else else
{ {
/* Use the System Settings if needed */ /* Use the System Settings if needed */
if (!StackReserve) StackReserve = SystemBasicInfo.AllocationGranularity; if (StackReserve == 0)
if (!StackCommit) StackCommit = SystemBasicInfo.PageSize; StackReserve = SystemBasicInfo.AllocationGranularity;
if (StackCommit == 0)
StackCommit = SystemBasicInfo.PageSize;
} }
/* Check if the commit is higher than the reserve*/ /* Check if the commit is higher than the reserve */
if (StackCommit >= StackReserve) if (StackCommit >= StackReserve)
{ {
/* Grow the reserve beyond the commit, up to 1MB alignment */ /* Grow the reserve beyond the commit, up to 1MB alignment */
@ -69,11 +77,12 @@ RtlpCreateUserStack(IN HANDLE hProcess,
} }
/* Align everything to Page Size */ /* Align everything to Page Size */
StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize); StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
/* Reserve memory for the stack */ /* Reserve memory for the stack */
Status = ZwAllocateVirtualMemory(hProcess, Stack = 0;
Status = ZwAllocateVirtualMemory(ProcessHandle,
(PVOID*)&Stack, (PVOID*)&Stack,
StackZeroBits, StackZeroBits,
&StackReserve, &StackReserve,
@ -82,41 +91,48 @@ RtlpCreateUserStack(IN HANDLE hProcess,
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status)) return Status;
/* Now set up some basic Initial TEB Parameters */ /* Now set up some basic Initial TEB Parameters */
InitialTeb->PreviousStackBase = NULL;
InitialTeb->PreviousStackLimit = NULL;
InitialTeb->AllocatedStackBase = (PVOID)Stack; InitialTeb->AllocatedStackBase = (PVOID)Stack;
InitialTeb->StackBase = (PVOID)(Stack + StackReserve); InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
InitialTeb->PreviousStackBase = NULL;
InitialTeb->PreviousStackLimit = NULL;
/* Update the Stack Position */ /* Update the stack position */
Stack += StackReserve - StackCommit; Stack += StackReserve - StackCommit;
/* Check if we will need a guard page */ /* Check if we can add a guard page */
if (StackReserve > StackCommit) if (StackReserve >= StackCommit + SystemBasicInfo.PageSize)
{ {
/* Remove a page to set as guard page */
Stack -= SystemBasicInfo.PageSize; Stack -= SystemBasicInfo.PageSize;
StackCommit += SystemBasicInfo.PageSize; StackCommit += SystemBasicInfo.PageSize;
UseGuard = TRUE; UseGuard = TRUE;
} }
else
{
UseGuard = FALSE;
}
/* Allocate memory for the stack */ /* Allocate memory for the stack */
Status = ZwAllocateVirtualMemory(hProcess, Status = ZwAllocateVirtualMemory(ProcessHandle,
(PVOID*)&Stack, (PVOID*)&Stack,
0, 0,
&StackCommit, &StackCommit,
MEM_COMMIT, MEM_COMMIT,
PAGE_READWRITE); PAGE_READWRITE);
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status))
{
GuardPageSize = 0;
ZwFreeVirtualMemory(ProcessHandle, (PVOID*)&Stack, &GuardPageSize, MEM_RELEASE);
return Status;
}
/* Now set the current Stack Limit */ /* Now set the current Stack Limit */
InitialTeb->StackLimit = (PVOID)Stack; InitialTeb->StackLimit = (PVOID)Stack;
/* Create a guard page */ /* Create a guard page if needed */
if (UseGuard) if (UseGuard)
{ {
/* Attempt maximum space possible */
GuardPageSize = SystemBasicInfo.PageSize; GuardPageSize = SystemBasicInfo.PageSize;
Status = ZwProtectVirtualMemory(hProcess, Status = ZwProtectVirtualMemory(ProcessHandle,
(PVOID*)&Stack, (PVOID*)&Stack,
&GuardPageSize, &GuardPageSize,
PAGE_GUARD | PAGE_READWRITE, PAGE_GUARD | PAGE_READWRITE,
@ -132,23 +148,21 @@ RtlpCreateUserStack(IN HANDLE hProcess,
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
NTSTATUS VOID
NTAPI NTAPI
RtlpFreeUserStack(IN HANDLE Process, RtlpFreeUserStack(IN HANDLE ProcessHandle,
IN PINITIAL_TEB InitialTeb) IN PINITIAL_TEB InitialTeb)
{ {
SIZE_T Dummy = 0; SIZE_T Dummy = 0;
NTSTATUS Status;
/* Free the Stack */ /* Free the Stack */
Status = ZwFreeVirtualMemory(Process, ZwFreeVirtualMemory(ProcessHandle,
&InitialTeb->AllocatedStackBase, &InitialTeb->AllocatedStackBase,
&Dummy, &Dummy,
MEM_RELEASE); MEM_RELEASE);
/* Clear the initial TEB */ /* Clear the initial TEB */
RtlZeroMemory(InitialTeb, sizeof(INITIAL_TEB)); RtlZeroMemory(InitialTeb, sizeof(*InitialTeb));
return Status;
} }
/* FUNCTIONS ***************************************************************/ /* FUNCTIONS ***************************************************************/