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