1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: Test for stack overflow 5 * PROGRAMMER: Jérôme Gardou 6 */ 7 8 #include "precomp.h" 9 10 #ifdef _MSC_VER 11 #pragma warning(disable : 4717) // disable warning about recursive function 12 #endif 13 14 static int iteration = 0; 15 static PVOID StackAllocationBase; 16 static PVOID LastStackAllocation; 17 static ULONG StackSize; 18 19 static 20 void 21 infinite_recursive(void) 22 { 23 MEMORY_BASIC_INFORMATION MemoryBasicInfo; 24 NTSTATUS Status; 25 char Buffer[0x500]; 26 27 sprintf(Buffer, "Iteration %d.\n", iteration++); 28 29 Status = NtQueryVirtualMemory( 30 NtCurrentProcess(), 31 &Buffer[0], 32 MemoryBasicInformation, 33 &MemoryBasicInfo, 34 sizeof(MemoryBasicInfo), 35 NULL); 36 ok_ntstatus(Status, STATUS_SUCCESS); 37 /* This never changes */ 38 ok_ptr(MemoryBasicInfo.AllocationBase, StackAllocationBase); 39 /* Stack is committed one page at a time */ 40 ok_ptr(MemoryBasicInfo.BaseAddress, (PVOID)PAGE_ROUND_DOWN(&Buffer[0])); 41 /* This is the protection of the memory when it was reserved. */ 42 ok_long(MemoryBasicInfo.AllocationProtect, PAGE_READWRITE); 43 /* Windows commits the whole used stack at once, +2 pages. */ 44 #if 0 45 ok_long(MemoryBasicInfo.RegionSize, ((ULONG_PTR)StackAllocationBase + StackSize + 2* PAGE_SIZE) - PAGE_ROUND_DOWN(&Buffer[0])); 46 #endif 47 /* This is the state of the queried address */ 48 ok_long(MemoryBasicInfo.State, MEM_COMMIT); 49 /* This is the protection of the queried address */ 50 ok_long(MemoryBasicInfo.Protect, PAGE_READWRITE); 51 /* Of course this is private memory. */ 52 ok_long(MemoryBasicInfo.Type, MEM_PRIVATE); 53 54 LastStackAllocation = &Buffer[-0x500]; 55 56 infinite_recursive(); 57 } 58 59 START_TEST(StackOverflow) 60 { 61 NTSTATUS Status; 62 MEMORY_BASIC_INFORMATION MemoryBasicInfo; 63 64 /* Get the base of the stack */ 65 Status = NtQueryVirtualMemory( 66 NtCurrentProcess(), 67 &Status, 68 MemoryBasicInformation, 69 &MemoryBasicInfo, 70 sizeof(MemoryBasicInfo), 71 NULL); 72 ok_ntstatus(Status, STATUS_SUCCESS); 73 StackAllocationBase = MemoryBasicInfo.AllocationBase; 74 trace("Stack allocation base is %p.\n", StackAllocationBase); 75 StackSize = MemoryBasicInfo.RegionSize; 76 77 /* Check TEB attributes */ 78 ok_ptr(NtCurrentTeb()->DeallocationStack, StackAllocationBase); 79 ok_ptr(NtCurrentTeb()->NtTib.StackBase, (PVOID)((ULONG_PTR)MemoryBasicInfo.BaseAddress + MemoryBasicInfo.RegionSize)); 80 ok_ptr(NtCurrentTeb()->NtTib.StackLimit, (PVOID)((ULONG_PTR)MemoryBasicInfo.BaseAddress - PAGE_SIZE)); 81 trace("Guaranteed stack size is %lu.\n", NtCurrentTeb()->GuaranteedStackBytes); 82 83 /* Get its size */ 84 Status = NtQueryVirtualMemory( 85 NtCurrentProcess(), 86 StackAllocationBase, 87 MemoryBasicInformation, 88 &MemoryBasicInfo, 89 sizeof(MemoryBasicInfo), 90 NULL); 91 ok_ntstatus(Status, STATUS_SUCCESS); 92 93 /* This is the complete stack size */ 94 StackSize += MemoryBasicInfo.RegionSize; 95 trace("Stack size is 0x%lx.\n", StackSize); 96 97 trace("Stack limit %p, stack base %p.\n", NtCurrentTeb()->NtTib.StackLimit, NtCurrentTeb()->NtTib.StackBase); 98 99 /* Take a look at what is beyond the stack limit */ 100 Status = NtQueryVirtualMemory( 101 NtCurrentProcess(), 102 NtCurrentTeb()->NtTib.StackLimit, 103 MemoryBasicInformation, 104 &MemoryBasicInfo, 105 sizeof(MemoryBasicInfo), 106 NULL); 107 ok_ntstatus(Status, STATUS_SUCCESS); 108 109 ok_ptr(MemoryBasicInfo.BaseAddress, NtCurrentTeb()->NtTib.StackLimit); 110 ok_ptr(MemoryBasicInfo.AllocationBase, StackAllocationBase); 111 ok_long(MemoryBasicInfo.AllocationProtect, PAGE_READWRITE); 112 ok_long(MemoryBasicInfo.RegionSize, 2 * PAGE_SIZE); 113 ok_long(MemoryBasicInfo.State, MEM_COMMIT); 114 ok_long(MemoryBasicInfo.Protect, PAGE_READWRITE); 115 ok_long(MemoryBasicInfo.Type, MEM_PRIVATE); 116 117 /* Accessing below stack limit is OK, as long as we don't starve the reserved space. */ 118 _SEH2_TRY 119 { 120 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit - PAGE_SIZE / 2); 121 CHAR Value = *Pointer; 122 (void)Value; 123 Status = STATUS_SUCCESS; 124 } 125 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 126 { 127 Status = _SEH2_GetExceptionCode(); 128 } 129 _SEH2_END; 130 ok_ntstatus(Status, STATUS_SUCCESS); 131 132 _SEH2_TRY 133 { 134 infinite_recursive(); 135 } 136 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 137 { 138 trace("Exception after %d iteration.\n", iteration); 139 Status = _SEH2_GetExceptionCode(); 140 } 141 _SEH2_END; 142 143 ok_ntstatus(Status, STATUS_STACK_OVERFLOW); 144 145 /* Windows lets 2 pages between the reserved memory and the smallest possible stack address */ 146 ok((ULONG_PTR)LastStackAllocation > (ULONG_PTR)StackAllocationBase, "\n"); 147 ok_long(PAGE_ROUND_DOWN(LastStackAllocation), (ULONG_PTR)StackAllocationBase + 2 * PAGE_SIZE); 148 149 /* And in fact, this is the true condition of the stack overflow */ 150 ok_ptr(NtCurrentTeb()->NtTib.StackLimit, (PVOID)((ULONG_PTR)StackAllocationBase + PAGE_SIZE)); 151 152 trace("Stack limit %p, stack base %p.\n", NtCurrentTeb()->NtTib.StackLimit, NtCurrentTeb()->NtTib.StackBase); 153 154 /* Of course, accessing above the stack limit is OK. */ 155 _SEH2_TRY 156 { 157 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit + PAGE_SIZE / 2); 158 CHAR Value = *Pointer; 159 (void)Value; 160 Status = STATUS_SUCCESS; 161 } 162 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 163 { 164 Status = _SEH2_GetExceptionCode(); 165 } 166 _SEH2_END; 167 ok_ntstatus(Status, STATUS_SUCCESS); 168 169 /* But once stack is starved, it's starved. */ 170 _SEH2_TRY 171 { 172 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit - PAGE_SIZE / 2); 173 CHAR Value = *Pointer; 174 (void)Value; 175 Status = STATUS_SUCCESS; 176 } 177 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 178 { 179 trace("Exception after %d iteration.\n", iteration); 180 Status = _SEH2_GetExceptionCode(); 181 } 182 _SEH2_END; 183 ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); 184 } 185