[NTDLL_APITEST] Add test for invalid pointer
[reactos.git] / rostests / apitests / ntdll / StackOverflow.c
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 #define WIN32_NO_STATUS
9 #include <apitest.h>
10 #include <stdio.h>
11 #include <ndk/rtlfuncs.h>
12 #include <ndk/mmfuncs.h>
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 }