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