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
infinite_recursive(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
START_TEST(StackOverflow)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