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 ok_ptr(NtCurrentTeb()->NtTib.StackLimit, (PVOID)((ULONG_PTR)MemoryBasicInfo.BaseAddress - PAGE_SIZE)); 83 trace("Guaranteed stack size is %lu.\n", NtCurrentTeb()->GuaranteedStackBytes); 84 85 /* Get its size */ 86 Status = NtQueryVirtualMemory( 87 NtCurrentProcess(), 88 StackAllocationBase, 89 MemoryBasicInformation, 90 &MemoryBasicInfo, 91 sizeof(MemoryBasicInfo), 92 NULL); 93 ok_ntstatus(Status, STATUS_SUCCESS); 94 95 /* This is the complete stack size */ 96 StackSize += MemoryBasicInfo.RegionSize; 97 trace("Stack size is 0x%lx.\n", StackSize); 98 99 trace("Stack limit %p, stack base %p.\n", NtCurrentTeb()->NtTib.StackLimit, NtCurrentTeb()->NtTib.StackBase); 100 101 /* Take a look at what is beyond the stack limit */ 102 Status = NtQueryVirtualMemory( 103 NtCurrentProcess(), 104 NtCurrentTeb()->NtTib.StackLimit, 105 MemoryBasicInformation, 106 &MemoryBasicInfo, 107 sizeof(MemoryBasicInfo), 108 NULL); 109 ok_ntstatus(Status, STATUS_SUCCESS); 110 111 ok_ptr(MemoryBasicInfo.BaseAddress, NtCurrentTeb()->NtTib.StackLimit); 112 ok_ptr(MemoryBasicInfo.AllocationBase, StackAllocationBase); 113 ok_long(MemoryBasicInfo.AllocationProtect, PAGE_READWRITE); 114 ok_long(MemoryBasicInfo.RegionSize, 2 * PAGE_SIZE); 115 ok_long(MemoryBasicInfo.State, MEM_COMMIT); 116 ok_long(MemoryBasicInfo.Protect, PAGE_READWRITE); 117 ok_long(MemoryBasicInfo.Type, MEM_PRIVATE); 118 119 /* Accessing below stack limit is OK, as long as we don't starve the reserved space. */ 120 _SEH2_TRY 121 { 122 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit - PAGE_SIZE / 2); 123 CHAR Value = *Pointer; 124 (void)Value; 125 Status = STATUS_SUCCESS; 126 } 127 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 128 { 129 Status = _SEH2_GetExceptionCode(); 130 } 131 _SEH2_END; 132 ok_ntstatus(Status, STATUS_SUCCESS); 133 134 _SEH2_TRY 135 { 136 infinite_recursive(); 137 } 138 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 139 { 140 trace("Exception after %d iteration.\n", iteration); 141 Status = _SEH2_GetExceptionCode(); 142 } 143 _SEH2_END; 144 145 ok_ntstatus(Status, STATUS_STACK_OVERFLOW); 146 147 /* Windows lets 2 pages between the reserved memory and the smallest possible stack address */ 148 ok((ULONG_PTR)LastStackAllocation > (ULONG_PTR)StackAllocationBase, "\n"); 149 ok_long(PAGE_ROUND_DOWN(LastStackAllocation), (ULONG_PTR)StackAllocationBase + 2 * PAGE_SIZE); 150 151 /* And in fact, this is the true condition of the stack overflow */ 152 ok_ptr(NtCurrentTeb()->NtTib.StackLimit, (PVOID)((ULONG_PTR)StackAllocationBase + PAGE_SIZE)); 153 154 trace("Stack limit %p, stack base %p.\n", NtCurrentTeb()->NtTib.StackLimit, NtCurrentTeb()->NtTib.StackBase); 155 156 /* Of course, accessing above the stack limit is OK. */ 157 _SEH2_TRY 158 { 159 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit + PAGE_SIZE / 2); 160 CHAR Value = *Pointer; 161 (void)Value; 162 Status = STATUS_SUCCESS; 163 } 164 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 165 { 166 Status = _SEH2_GetExceptionCode(); 167 } 168 _SEH2_END; 169 ok_ntstatus(Status, STATUS_SUCCESS); 170 171 /* But once stack is starved, it's starved. */ 172 _SEH2_TRY 173 { 174 volatile CHAR* Pointer = (PVOID)((ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit - PAGE_SIZE / 2); 175 CHAR Value = *Pointer; 176 (void)Value; 177 Status = STATUS_SUCCESS; 178 } 179 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 180 { 181 trace("Exception after %d iteration.\n", iteration); 182 Status = _SEH2_GetExceptionCode(); 183 } 184 _SEH2_END; 185 ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); 186 } 187