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