1 /*
2  * PROJECT:     ReactOS API Tests
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Test for RtlMultipleAllocateHeap and RtlMultipleFreeHeap
5  * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 #include "precomp.h"
8 
9 #include <pseh/pseh2.h>
10 
11 typedef ULONG (NTAPI *FN_RtlMultipleAllocateHeap)(IN PVOID, IN ULONG, IN SIZE_T, IN ULONG, OUT PVOID *);
12 typedef ULONG (NTAPI *FN_RtlMultipleFreeHeap)(IN PVOID, IN ULONG, IN ULONG, OUT PVOID *);
13 
14 static FN_RtlMultipleAllocateHeap g_alloc = NULL;
15 static FN_RtlMultipleFreeHeap g_free = NULL;
16 
17 #define TEST_ALLOC(ret_expected,err_expected,threw_excepted,HeapHandle,Flags,Size,Count,Array) \
18     threw = 0; \
19     SetLastError(-1); \
20     _SEH2_TRY { \
21         ret = g_alloc((HeapHandle), (Flags), (Size), (Count), (Array)); \
22         err = GetLastError(); \
23     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
24         threw = _SEH2_GetExceptionCode(); \
25     } \
26     _SEH2_END; \
27     ok((ret) == (ret_expected), "ret excepted %d, but %d\n", (ret_expected), (ret)); \
28     ok((err) == (err_expected), "err excepted %d, but %d\n", (err_expected), (err)); \
29     ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw));
30 
31 #define TEST_ALLOC_NO_RET(err_expected,threw_excepted,HeapHandle,Flags,Size,Count,Array) \
32     threw = 0; \
33     SetLastError(-1); \
34     _SEH2_TRY { \
35         ret = g_alloc((HeapHandle), (Flags), (Size), (Count), (Array)); \
36         err = GetLastError(); \
37     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
38         threw = _SEH2_GetExceptionCode(); \
39     } \
40     _SEH2_END; \
41     ok((err) == (err_expected), "err excepted %d, but %d", (err_expected), (err)); \
42     ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw));
43 
44 #define TEST_FREE(ret_expected,err_expected,threw_excepted,HeapHandle,Flags,Count,Array) \
45     threw = 0; \
46     SetLastError(-1); \
47     _SEH2_TRY { \
48         ret = g_free((HeapHandle), (Flags), (Count), (Array)); \
49         err = GetLastError(); \
50     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
51         threw = _SEH2_GetExceptionCode(); \
52     } \
53     _SEH2_END; \
54     ok((ret) == (ret_expected), "ret excepted %d, but %d\n", (ret_expected), (ret)); \
55     ok((err) == (err_expected), "err excepted %d, but %d\n", (err_expected), (err)); \
56     ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw));
57 
58 #define ASSUME_ARRAY_ITEMS_ARE_NULL() \
59     ok(Array[0] == NULL, "Array[0] is expected as NULL\n"); \
60     ok(Array[1] == NULL, "Array[1] is expected as NULL\n"); \
61     ok(Array[2] == NULL, "Array[2] is expected as NULL\n");
62 
63 #define INT_EXPECTED(var,value) \
64     ok((var) == (value), #var " expected %d, but %d\n", (value), (var))
65 
66 static void
67 set_array(PVOID *array, PVOID p0, PVOID p1, PVOID p2)
68 {
69     array[0] = p0;
70     array[1] = p1;
71     array[2] = p2;
72 }
73 
74 static void
75 MultiHeapAllocTest()
76 {
77     INT ret, threw, err;
78     HANDLE HeapHandle = GetProcessHeap();
79     PVOID Array[3] = {NULL, NULL, NULL};
80 
81     // HeapHandle is non-NULL and array is NULL
82     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, NULL);
83     TEST_ALLOC(0, -1, 0xC0000005, HeapHandle, 0, 0, 1, NULL);
84     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, NULL);
85     TEST_ALLOC(0, -1, 0xC0000005, HeapHandle, 0, 1, 1, NULL);
86 
87     // Array is non-NULL and contents are NULL
88     set_array(Array, NULL, NULL, NULL);
89     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, Array);
90     ASSUME_ARRAY_ITEMS_ARE_NULL();
91 
92     set_array(Array, NULL, NULL, NULL);
93     TEST_ALLOC(1, -1, 0, HeapHandle, 0, 0, 1, Array);
94     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
95     ok(Array[1] == NULL, "Array[1] is expected as NULL\n");
96     ok(Array[2] == NULL, "Array[2] is expected as NULL\n");
97 
98     set_array(Array, NULL, NULL, NULL);
99     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, Array);
100     ASSUME_ARRAY_ITEMS_ARE_NULL();
101 
102     set_array(Array, NULL, NULL, NULL);
103     TEST_ALLOC(1, -1, 0, HeapHandle, 0, 1, 1, Array);
104     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
105     ok(Array[1] == NULL, "Array[1] is expected as NULL\n");
106     ok(Array[2] == NULL, "Array[2] is expected as NULL\n");
107 
108     set_array(Array, NULL, NULL, NULL);
109     TEST_ALLOC(2, -1, 0, HeapHandle, 0, 1, 2, Array);
110     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
111     ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n");
112     ok(Array[2] == NULL, "Array[2] is expected as NULL\n");
113 
114     set_array(Array, NULL, NULL, NULL);
115     TEST_ALLOC(3, -1, 0, HeapHandle, 0, 1, 3, Array);
116     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
117     ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n");
118     ok(Array[2] != NULL, "Array[2] is expected as non-NULL\n");
119 
120     // Array is non-NULL and contents are invalid pointers
121     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
122     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, Array);
123     ok(Array[0] == (PVOID)1, "Array[0] is expected as 1\n");
124     ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n");
125     ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n");
126 
127     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
128     TEST_ALLOC(1, -1, 0, HeapHandle, 0, 0, 1, Array);
129     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
130     ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n");
131     ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n");
132 
133     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
134     TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, Array);
135     ok(Array[0] == (PVOID)1, "Array[0] is expected as 1\n");
136     ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n");
137     ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n");
138 
139     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
140     TEST_ALLOC(1, -1, 0, HeapHandle, 0, 1, 1, Array);
141     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
142     ok(Array[1] == (PVOID)2, "Array[1] is expected as non-NULL\n");
143     ok(Array[2] == (PVOID)3, "Array[2] is expected as NULL\n");
144 
145     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
146     TEST_ALLOC(2, -1, 0, HeapHandle, 0, 1, 2, Array);
147     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
148     ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n");
149     ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n");
150 
151     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
152     TEST_ALLOC(3, -1, 0, HeapHandle, 0, 1, 3, Array);
153     ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n");
154     ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n");
155     ok(Array[2] != NULL, "Array[2] is expected as non-NULL\n");
156 
157     // Array is non-NULL and too large to allocate
158     set_array(Array, NULL, NULL, NULL);
159     TEST_ALLOC_NO_RET(ERROR_NOT_ENOUGH_MEMORY, 0, HeapHandle, 0, 0x5FFFFFFF, 3, Array);
160     ok(ret != 3, "excepted not allocated");
161     set_array(Array, NULL, NULL, NULL);
162     TEST_ALLOC_NO_RET(ERROR_NOT_ENOUGH_MEMORY, 0xC0000017, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 0x5FFFFFFF, 3, Array);
163     ok(ret != 3, "excepted not allocated");
164 }
165 
166 static void
167 MultiHeapFreeTest()
168 {
169     INT ret, threw, err;
170     HANDLE HeapHandle = GetProcessHeap();
171     PVOID Array[3] = {NULL, NULL, NULL};
172 
173     // HeapHandle is non-NULL and array is NULL
174     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, NULL);
175     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, NULL);
176     TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 1, NULL);
177     TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 2, NULL);
178     TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 3, NULL);
179 
180     // Array is non-NULL and contents are NULL
181     set_array(Array, NULL, NULL, NULL);
182     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array);
183     set_array(Array, NULL, NULL, NULL);
184     TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array);
185     set_array(Array, NULL, NULL, NULL);
186     TEST_FREE(2, -1, 0, HeapHandle, 0, 2, Array);
187     set_array(Array, NULL, NULL, NULL);
188     TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array);
189 
190     // Array is non-NULL and contents are invalid pointers
191     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
192     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array);
193     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
194     TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 1, Array);
195     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
196     TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 2, Array);
197     set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3);
198     TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 3, Array);
199 
200     // Array is non-NULL and contents are 1 valid pointer and 2 NULLs
201     set_array(Array, NULL, NULL, NULL);
202     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
203     INT_EXPECTED(ret, 1);
204     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array);
205 
206     set_array(Array, NULL, NULL, NULL);
207     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
208     INT_EXPECTED(ret, 1);
209     TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array);
210 
211     set_array(Array, NULL, NULL, NULL);
212     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
213     INT_EXPECTED(ret, 1);
214     TEST_FREE(2, -1, 0, HeapHandle, 0, 2, Array);
215 
216     set_array(Array, NULL, NULL, NULL);
217     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
218     INT_EXPECTED(ret, 1);
219     TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array);
220 
221     // Array is non-NULL and contents are 1 valid pointer and 2 invalids
222     set_array(Array, NULL, (PVOID)2, (PVOID)3);
223     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
224     INT_EXPECTED(ret, 1);
225     TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array);
226 
227     set_array(Array, NULL, (PVOID)2, (PVOID)3);
228     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
229     INT_EXPECTED(ret, 1);
230     TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array);
231 
232     set_array(Array, NULL, (PVOID)2, (PVOID)3);
233     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
234     INT_EXPECTED(ret, 1);
235     TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 2, Array);
236 
237     set_array(Array, NULL, (PVOID)2, (PVOID)3);
238     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
239     INT_EXPECTED(ret, 1);
240     TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 3, Array);
241 
242     // Array is non-NULL and contents are 1 valid pointer and 2 invalids (generate exceptions)
243     set_array(Array, NULL, (PVOID)2, (PVOID)3);
244     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
245     INT_EXPECTED(ret, 1);
246     TEST_FREE(0, -1, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 0, Array);
247 
248     set_array(Array, NULL, (PVOID)2, (PVOID)3);
249     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
250     INT_EXPECTED(ret, 1);
251     TEST_FREE(1, -1, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 1, Array);
252 
253     set_array(Array, NULL, (PVOID)2, (PVOID)3);
254     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
255     INT_EXPECTED(ret, 1);
256     TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 2, Array);
257 
258     set_array(Array, NULL, (PVOID)2, (PVOID)3);
259     ret = g_alloc(HeapHandle, 0, 1, 1, Array);
260     INT_EXPECTED(ret, 1);
261     TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 3, Array);
262 
263     // Array is non-NULL and contents are 3 valid pointers
264     set_array(Array, NULL, NULL, NULL);
265     ret = g_alloc(HeapHandle, 0, 3, 3, Array);
266     INT_EXPECTED(ret, 3);
267     TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array);
268 }
269 
270 START_TEST(RtlMultipleAllocateHeap)
271 {
272     HINSTANCE ntdll = LoadLibraryA("ntdll");
273 
274     g_alloc = (FN_RtlMultipleAllocateHeap)GetProcAddress(ntdll, "RtlMultipleAllocateHeap");
275     g_free = (FN_RtlMultipleFreeHeap)GetProcAddress(ntdll, "RtlMultipleFreeHeap");
276 
277     if (!g_alloc || !g_free)
278     {
279         skip("RtlMultipleAllocateHeap or RtlMultipleFreeHeap not found\n");
280     }
281     else
282     {
283         MultiHeapAllocTest();
284         MultiHeapFreeTest();
285     }
286 
287     FreeLibrary(ntdll);
288 }
289