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