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