1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Kernel-Mode Test Suite Pools test routines KM-Test
5  * PROGRAMMER:      Aleksey Bragin <aleksey@reactos.org>
6  */
7 
8 #include <kmt_test.h>
9 
10 #define NDEBUG
11 #include <debug.h>
12 
13 #define TAG_POOLTEST 'tstP'
14 
15 #define BASE_POOL_TYPE_MASK 1
16 #define QUOTA_POOL_MASK 8
17 
18 static
19 LONG
20 GetRefCount(
21     _In_ PVOID Object)
22 {
23     POBJECT_HEADER Header = OBJECT_TO_OBJECT_HEADER(Object);
24     return Header->PointerCount;
25 }
26 
27 static VOID PoolsTest(VOID)
28 {
29     PVOID Ptr;
30     ULONG AllocSize, i, AllocNumber;
31     PVOID *Allocs;
32 
33     // Stress-test nonpaged pool
34     for (i=1; i<10000; i++)
35     {
36         // make up some increasing, a bit irregular size
37         AllocSize = i*10;
38 
39         if (i % 10)
40             AllocSize++;
41 
42         if (i % 25)
43             AllocSize += 13;
44 
45         // start with non-paged pool
46         Ptr = ExAllocatePoolWithTag(NonPagedPool, AllocSize, TAG_POOLTEST);
47 
48         // it may fail due to no-memory condition
49         if (!Ptr) break;
50 
51         // try to fully fill it
52         RtlFillMemory(Ptr, AllocSize, 0xAB);
53 
54         // free it
55         ExFreePoolWithTag(Ptr, TAG_POOLTEST);
56     }
57 
58     // now paged one
59     for (i=1; i<10000; i++)
60     {
61         // make up some increasing, a bit irregular size
62         AllocSize = i*50;
63 
64         if (i % 10)
65             AllocSize++;
66 
67         if (i % 25)
68             AllocSize += 13;
69 
70         // start with non-paged pool
71         Ptr = ExAllocatePoolWithTag(PagedPool, AllocSize, TAG_POOLTEST);
72 
73         // it may fail due to no-memory condition
74         if (!Ptr) break;
75 
76         // try to fully fill it
77         RtlFillMemory(Ptr, AllocSize, 0xAB);
78 
79         // free it
80         ExFreePoolWithTag(Ptr, TAG_POOLTEST);
81     }
82 
83     // test super-big allocations
84     /*AllocSize = 2UL * 1024 * 1024 * 1024;
85     Ptr = ExAllocatePoolWithTag(NonPagedPool, AllocSize, TAG_POOLTEST);
86     ok(Ptr == NULL, "Allocating 2Gb of nonpaged pool should fail\n");
87 
88     Ptr = ExAllocatePoolWithTag(PagedPool, AllocSize, TAG_POOLTEST);
89     ok(Ptr == NULL, "Allocating 2Gb of paged pool should fail\n");*/
90 
91     // now test allocating lots of small/medium blocks
92     AllocNumber = 100000;
93     Allocs = ExAllocatePoolWithTag(PagedPool, sizeof(*Allocs) * AllocNumber, TAG_POOLTEST);
94 
95     // alloc blocks
96     for (i=0; i<AllocNumber; i++)
97     {
98         AllocSize = 42;
99         Allocs[i] = ExAllocatePoolWithTag(NonPagedPool, AllocSize, TAG_POOLTEST);
100     }
101 
102     // now free them
103     for (i=0; i<AllocNumber; i++)
104     {
105         ExFreePoolWithTag(Allocs[i], TAG_POOLTEST);
106     }
107 
108 
109     ExFreePoolWithTag(Allocs, TAG_POOLTEST);
110 }
111 
112 static VOID PoolsCorruption(VOID)
113 {
114     PULONG Ptr;
115     ULONG AllocSize;
116 
117     // start with non-paged pool
118     AllocSize = 4096 + 0x10;
119     Ptr = ExAllocatePoolWithTag(NonPagedPool, AllocSize, TAG_POOLTEST);
120 
121     // touch all bytes, it shouldn't cause an exception
122     RtlZeroMemory(Ptr, AllocSize);
123 
124 /* TODO: These fail because accessing invalid memory doesn't necessarily
125          cause an access violation */
126 #ifdef THIS_DOESNT_WORK
127     // test buffer overrun, right after our allocation ends
128     _SEH2_TRY
129     {
130         TestPtr = (PULONG)((PUCHAR)Ptr + AllocSize);
131         //Ptr[4] = 0xd33dbeef;
132         *TestPtr = 0xd33dbeef;
133     }
134     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
135     {
136         /* Get the status */
137         Status = _SEH2_GetExceptionCode();
138     } _SEH2_END;
139 
140     ok(Status == STATUS_ACCESS_VIOLATION, "Exception should occur, but got Status 0x%08lX\n", Status);
141 
142     // test overrun in a distant byte range, but within 4096KB
143     _SEH2_TRY
144     {
145         Ptr[2020] = 0xdeadb33f;
146     }
147     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
148     {
149         /* Get the status */
150         Status = _SEH2_GetExceptionCode();
151     } _SEH2_END;
152 
153     ok(Status == STATUS_ACCESS_VIOLATION, "Exception should occur, but got Status 0x%08lX\n", Status);
154 #endif
155 
156     // free the pool
157     ExFreePoolWithTag(Ptr, TAG_POOLTEST);
158 }
159 
160 static
161 VOID
162 TestPoolTags(VOID)
163 {
164     PVOID Memory;
165 
166     Memory = ExAllocatePoolWithTag(PagedPool, 8, 'MyTa');
167     ok_eq_tag(KmtGetPoolTag(Memory), 'MyTa');
168     ExFreePoolWithTag(Memory, 'MyTa');
169 
170     Memory = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, 'MyTa');
171     ok_eq_tag(KmtGetPoolTag(Memory), 'TooL');
172     ExFreePoolWithTag(Memory, 'MyTa');
173 
174     Memory = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE - 3 * sizeof(PVOID), 'MyTa');
175     ok_eq_tag(KmtGetPoolTag(Memory), 'TooL');
176     ExFreePoolWithTag(Memory, 'MyTa');
177 
178     Memory = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE - 4 * sizeof(PVOID) + 1, 'MyTa');
179     ok_eq_tag(KmtGetPoolTag(Memory), 'TooL');
180     ExFreePoolWithTag(Memory, 'MyTa');
181 
182     Memory = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE - 4 * sizeof(PVOID), 'MyTa');
183     ok_eq_tag(KmtGetPoolTag(Memory), 'MyTa');
184     ExFreePoolWithTag(Memory, 'MyTa');
185 }
186 
187 static
188 VOID
189 TestPoolQuota(VOID)
190 {
191     PEPROCESS Process = PsGetCurrentProcess();
192     PEPROCESS StoredProcess;
193     PVOID Memory;
194     LONG InitialRefCount;
195     LONG RefCount;
196     USHORT PoolType;
197 
198     InitialRefCount = GetRefCount(Process);
199 
200     /* We get some memory from this function, and it's properly aligned.
201      * Also, it takes a reference to the process, and releases it on free */
202     Memory = ExAllocatePoolWithQuotaTag(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
203                                         sizeof(LIST_ENTRY),
204                                         'tQmK');
205     ok(Memory != NULL, "ExAllocatePoolWithQuotaTag returned NULL\n");
206     if (!skip(Memory != NULL, "No memory\n"))
207     {
208         ok((ULONG_PTR)Memory % sizeof(LIST_ENTRY) == 0,
209            "Allocation %p is badly aligned\n",
210            Memory);
211         RefCount = GetRefCount(Process);
212         ok_eq_long(RefCount, InitialRefCount + 1);
213 
214         /* A pointer to the process is found right before the next pool header */
215         StoredProcess = ((PVOID *)((ULONG_PTR)Memory + 2 * sizeof(LIST_ENTRY)))[-1];
216         ok_eq_pointer(StoredProcess, Process);
217 
218         /* Pool type should have QUOTA_POOL_MASK set */
219         PoolType = KmtGetPoolType(Memory);
220         ok(PoolType != 0, "PoolType is 0\n");
221         PoolType--;
222         ok(PoolType & QUOTA_POOL_MASK, "PoolType = %x\n", PoolType);
223         ok((PoolType & BASE_POOL_TYPE_MASK) == PagedPool, "PoolType = %x\n", PoolType);
224 
225         ExFreePoolWithTag(Memory, 'tQmK');
226         RefCount = GetRefCount(Process);
227         ok_eq_long(RefCount, InitialRefCount);
228     }
229 
230     /* Large allocations are page-aligned, don't reference the process */
231     Memory = ExAllocatePoolWithQuotaTag(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
232                                         PAGE_SIZE,
233                                         'tQmK');
234     ok(Memory != NULL, "ExAllocatePoolWithQuotaTag returned NULL\n");
235     if (!skip(Memory != NULL, "No memory\n"))
236     {
237         ok((ULONG_PTR)Memory % PAGE_SIZE == 0,
238            "Allocation %p is badly aligned\n",
239            Memory);
240         RefCount = GetRefCount(Process);
241         ok_eq_long(RefCount, InitialRefCount);
242         ExFreePoolWithTag(Memory, 'tQmK');
243         RefCount = GetRefCount(Process);
244         ok_eq_long(RefCount, InitialRefCount);
245     }
246 
247     /* Function raises by default */
248     KmtStartSeh()
249         Memory = ExAllocatePoolWithQuotaTag(PagedPool,
250                                             0x7FFFFFFF,
251                                             'tQmK');
252         if (Memory)
253             ExFreePoolWithTag(Memory, 'tQmK');
254     KmtEndSeh(STATUS_INSUFFICIENT_RESOURCES);
255 
256     /* Function returns NULL with POOL_QUOTA_FAIL_INSTEAD_OF_RAISE */
257     KmtStartSeh()
258         Memory = ExAllocatePoolWithQuotaTag(PagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
259                                             0x7FFFFFFF,
260                                             'tQmK');
261         ok(Memory == NULL, "Successfully got 2GB block: %p\n", Memory);
262         if (Memory)
263             ExFreePoolWithTag(Memory, 'tQmK');
264     KmtEndSeh(STATUS_SUCCESS);
265 }
266 
267 static
268 VOID
269 TestBigPoolExpansion(VOID)
270 {
271     POOL_TYPE PoolType;
272     PVOID *BigAllocations;
273     const ULONG MaxAllocations = 1024 * 128;
274     ULONG NumAllocations;
275 
276     for (PoolType = NonPagedPool; PoolType <= PagedPool; PoolType++)
277     {
278         BigAllocations = ExAllocatePoolWithTag(PoolType,
279                                                MaxAllocations * sizeof(*BigAllocations),
280                                                'ABmK');
281 
282         /* Allocate a lot of pages (== big pool allocations) */
283         for (NumAllocations = 0; NumAllocations < MaxAllocations; NumAllocations++)
284         {
285             BigAllocations[NumAllocations] = ExAllocatePoolWithTag(PoolType,
286                                                                    PAGE_SIZE,
287                                                                    'aPmK');
288             if (BigAllocations[NumAllocations] == NULL)
289             {
290                 NumAllocations--;
291                 break;
292             }
293         }
294 
295         trace("Got %lu allocations for PoolType %d\n", NumAllocations, PoolType);
296 
297         /* Free them */
298         for (; NumAllocations < MaxAllocations; NumAllocations--)
299         {
300             ASSERT(BigAllocations[NumAllocations] != NULL);
301             ExFreePoolWithTag(BigAllocations[NumAllocations],
302                               'aPmK');
303         }
304         ExFreePoolWithTag(BigAllocations, 'ABmK');
305     }
306 }
307 
308 START_TEST(ExPools)
309 {
310     PoolsTest();
311     PoolsCorruption();
312     TestPoolTags();
313     TestPoolQuota();
314     TestBigPoolExpansion();
315 }
316