[PATCH v2 1/2] ntdll/tests: Add committed / reserved thread stack sizes tests.
Rémi Bernon
rbernon at codeweavers.com
Wed Mar 31 05:23:36 CDT 2021
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
v2: Clear around 1MB of stack instead of a few pages, and keep setting
the stack pointer while doing it.
dlls/ntdll/tests/virtual.c | 264 +++++++++++++++++++++++++++++++++++++
1 file changed, 264 insertions(+)
diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c
index 546a7d83bbd..112be80c01e 100644
--- a/dlls/ntdll/tests/virtual.c
+++ b/dlls/ntdll/tests/virtual.c
@@ -237,14 +237,266 @@ static void test_NtAllocateVirtualMemory(void)
ok(status == STATUS_INVALID_PARAMETER, "NtAllocateVirtualMemoryEx returned %08x\n", status);
}
+struct test_stack_size_thread_args
+{
+ DWORD expect_committed;
+ DWORD expect_reserved;
+};
+
+static void force_stack_grow(void)
+{
+ volatile int buffer[0x2000];
+ buffer[0] = 0xdeadbeef;
+ (void)buffer[0];
+}
+
+#ifdef _WIN64
+static void force_stack_grow_small(void)
+{
+ volatile int buffer[0x400];
+ buffer[0] = 0xdeadbeef;
+ (void)buffer[0];
+}
+#endif
+
+static DWORD WINAPI test_stack_size_thread(void *ptr)
+{
+ struct test_stack_size_thread_args *args = ptr;
+ MEMORY_BASIC_INFORMATION mbi;
+ NTSTATUS status;
+ SIZE_T size, guard_size;
+ DWORD committed, reserved;
+ void *addr;
+#ifdef _WIN64
+ DWORD prot;
+ void *tmp;
+#endif
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ reserved = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->DeallocationStack;
+ todo_wine ok( committed == args->expect_committed || broken(committed == 0x1000), "unexpected stack committed size %x, expected %x\n", committed, args->expect_committed );
+ ok( reserved == args->expect_reserved, "unexpected stack reserved size %x, expected %x\n", reserved, args->expect_reserved );
+
+ addr = (char *)NtCurrentTeb()->DeallocationStack;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack );
+ ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#x, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE );
+ ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr );
+ todo_wine ok( mbi.State == MEM_RESERVE, "unexpected State %#x, expected %#x\n", mbi.State, MEM_RESERVE );
+ todo_wine ok( mbi.Protect == 0, "unexpected Protect %#x, expected %#x\n", mbi.Protect, 0 );
+ ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#x, expected %#x\n", mbi.Type, MEM_PRIVATE );
+
+
+ force_stack_grow();
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ reserved = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->DeallocationStack;
+ todo_wine ok( committed == 0x9000, "unexpected stack committed size %x, expected 9000\n", committed );
+ ok( reserved == args->expect_reserved, "unexpected stack reserved size %x, expected %x\n", reserved, args->expect_reserved );
+
+
+ /* reserved area shrinks whenever stack grows */
+
+ addr = (char *)NtCurrentTeb()->DeallocationStack;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack );
+ ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#x, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE );
+ ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr );
+ todo_wine ok( mbi.State == MEM_RESERVE, "unexpected State %#x, expected %#x\n", mbi.State, MEM_RESERVE );
+ todo_wine ok( mbi.Protect == 0, "unexpected Protect %#x, expected %#x\n", mbi.Protect, 0 );
+ ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#x, expected %#x\n", mbi.Type, MEM_PRIVATE );
+
+ guard_size = reserved - committed - mbi.RegionSize;
+ ok( guard_size == 0x1000 || guard_size == 0x2000 || guard_size == 0x3000, "unexpected guard_size %I64x, expected 1000, 2000 or 3000\n", (UINT64)guard_size );
+
+ /* the commit area is initially preceded by guard pages */
+
+ addr = (char *)NtCurrentTeb()->DeallocationStack + mbi.RegionSize;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack );
+ ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#x, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE );
+ ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr );
+ ok( mbi.RegionSize == guard_size, "unexpected RegionSize %I64x, expected 3000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE|PAGE_GUARD );
+ ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#x, expected %#x\n", mbi.Type, MEM_PRIVATE );
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack );
+ ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#x, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE );
+ ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr );
+ ok( mbi.RegionSize == committed, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)committed );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+ ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#x, expected %#x\n", mbi.Type, MEM_PRIVATE );
+
+
+#ifdef _WIN64
+ /* setting a guard page shrinks stack automatically */
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000;
+ size = 0x1000;
+ status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
+ ok( !status, "NtAllocateVirtualMemory returned %08x\n", status );
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x6000, "unexpected stack committed size %x, expected 6000\n", committed );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)addr - 0x2000, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.RegionSize == 0x2000, "unexpected RegionSize %I64x, expected 2000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#x, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) );
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ todo_wine ok( mbi.RegionSize == 0x6000, "unexpected RegionSize %I64x, expected 6000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+
+ /* guard pages are restored as the stack grows back */
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x4000;
+ tmp = (char *)addr - guard_size - 0x1000;
+ size = 0x1000;
+ status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
+ ok( !status, "NtAllocateVirtualMemory returned %08x\n", status );
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x1000, "unexpected stack committed size %x, expected 1000\n", committed );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), tmp, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ todo_wine ok( mbi.RegionSize == guard_size + 0x1000, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)(guard_size + 0x1000) );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+ force_stack_grow_small();
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x2000, "unexpected stack committed size %x, expected 2000\n", committed );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), tmp, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)tmp + 0x1000, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.RegionSize == guard_size, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)guard_size );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#x, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) );
+
+
+ /* forcing stack limit over guard pages still shrinks the stack on page fault */
+
+ addr = (char *)tmp + guard_size + 0x1000;
+ size = 0x1000;
+ status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
+ ok( !status, "NtAllocateVirtualMemory returned %08x\n", status );
+
+ NtCurrentTeb()->Tib.StackLimit = (char *)tmp;
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)tmp + 0x1000, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ todo_wine ok( mbi.RegionSize == guard_size + 0x1000, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)(guard_size + 0x1000) );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#x, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) );
+
+ force_stack_grow_small();
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x2000, "unexpected stack committed size %x, expected 2000\n", committed );
+
+
+ /* it works with NtProtectVirtualMemory as well */
+
+ force_stack_grow();
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000;
+ size = 0x1000;
+ status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE | PAGE_GUARD, &prot );
+ ok( !status, "NtProtectVirtualMemory returned %08x\n", status );
+ todo_wine ok( prot == PAGE_READWRITE, "unexpected prot %#x, expected %#x\n", prot, PAGE_READWRITE );
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x6000, "unexpected stack committed size %x, expected 6000\n", committed );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)addr - 0x2000, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ todo_wine ok( mbi.RegionSize == 0x2000, "unexpected RegionSize %I64x, expected 2000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#x, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) );
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit;
+ status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size );
+ ok( !status, "NtQueryVirtualMemory returned %08x\n", status );
+ todo_wine ok( mbi.RegionSize == 0x6000, "unexpected RegionSize %I64x, expected 6000\n", (UINT64)mbi.RegionSize );
+ ok( mbi.State == MEM_COMMIT, "unexpected State %#x, expected %#x\n", mbi.State, MEM_COMMIT );
+ todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#x, expected %#x\n", mbi.Protect, PAGE_READWRITE );
+
+
+ /* clearing the guard pages doesn't change StackLimit back */
+
+ force_stack_grow();
+
+ addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000;
+ size = 0x1000;
+ status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE | PAGE_GUARD, &prot );
+ ok( !status, "NtProtectVirtualMemory returned %08x\n", status );
+ todo_wine ok( prot == PAGE_READWRITE, "unexpected prot %#x, expected %#x\n", prot, PAGE_READWRITE );
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x6000, "unexpected stack committed size %x, expected 6000\n", committed );
+
+ status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &prot );
+ ok( !status, "NtProtectVirtualMemory returned %08x\n", status );
+ ok( prot == (PAGE_READWRITE | PAGE_GUARD), "unexpected prot %#x, expected %#x\n", prot, (PAGE_READWRITE | PAGE_GUARD) );
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x6000, "unexpected stack committed size %x, expected 6000\n", committed );
+
+ /* and as we messed with it and it now doesn't fault, it doesn't grow back either */
+
+ force_stack_grow();
+
+ committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
+ todo_wine ok( committed == 0x6000, "unexpected stack committed size %x, expected 6000\n", committed );
+#endif
+
+ return 0;
+}
+
static void test_RtlCreateUserStack(void)
{
IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
+ struct test_stack_size_thread_args args;
SIZE_T default_commit = nt->OptionalHeader.SizeOfStackCommit;
SIZE_T default_reserve = nt->OptionalHeader.SizeOfStackReserve;
INITIAL_TEB stack = {0};
unsigned int i;
NTSTATUS ret;
+ HANDLE thread;
struct
{
@@ -298,6 +550,18 @@ static void test_RtlCreateUserStack(void)
ret = pRtlCreateUserStack(0x11000, 0x110000, 0, 0, 1, &stack);
ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret);
+
+ args.expect_committed = 0x4000;
+ args.expect_reserved = default_reserve;
+ thread = CreateThread(NULL, 0x3f00, test_stack_size_thread, &args, 0, NULL);
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+ args.expect_committed = default_commit < 0x2000 ? 0x2000 : default_commit;
+ args.expect_reserved = 0x400000;
+ thread = CreateThread(NULL, 0x3ff000, test_stack_size_thread, &args, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
}
static void test_NtMapViewOfSection(void)
--
2.30.2
More information about the wine-devel
mailing list