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