1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Test for Rtl Range Lists
5 * COPYRIGHT: Copyright 2020 Thomas Faber (thomas.faber@reactos.org)
6 */
7
8 #include <kmt_test.h>
9 #include <ndk/rtlfuncs.h>
10
11 static UCHAR MyUserData1, MyUserData2;
12 static UCHAR MyOwner1, MyOwner2;
13
14 /* Helpers *******************************************************************/
15 static
16 NTSTATUS
RtlAddRangeWrapper(_Inout_ PRTL_RANGE_LIST RangeList,_In_ const RTL_RANGE * Range,_In_ ULONG Flags)17 RtlAddRangeWrapper(
18 _Inout_ PRTL_RANGE_LIST RangeList,
19 _In_ const RTL_RANGE *Range,
20 _In_ ULONG Flags)
21 {
22 return RtlAddRange(RangeList,
23 Range->Start,
24 Range->End,
25 Range->Attributes,
26 Flags,
27 Range->UserData,
28 Range->Owner);
29 }
30
31 static
32 void
ExpectRange(_In_ PCSTR File,_In_ INT Line,_In_ ULONG Index,_In_ const RTL_RANGE * ActualRange,_In_ const RTL_RANGE * ExpectedRange)33 ExpectRange(
34 _In_ PCSTR File,
35 _In_ INT Line,
36 _In_ ULONG Index,
37 _In_ const RTL_RANGE *ActualRange,
38 _In_ const RTL_RANGE *ExpectedRange)
39 {
40 CHAR FileAndLine[128];
41 RtlStringCbPrintfA(FileAndLine, sizeof(FileAndLine), "%s:%d", File, Line);
42
43 KmtOk(ActualRange->Start == ExpectedRange->Start, FileAndLine,
44 "[%lu] Start = 0x%I64x, expected 0x%I64x\n", Index, ActualRange->Start, ExpectedRange->Start);
45 KmtOk(ActualRange->End == ExpectedRange->End, FileAndLine,
46 "[%lu] End = 0x%I64x, expected 0x%I64x\n", Index, ActualRange->End, ExpectedRange->End);
47 KmtOk(ActualRange->UserData == ExpectedRange->UserData, FileAndLine,
48 "[%lu] UserData = %p, expected %p\n", Index, ActualRange->UserData, ExpectedRange->UserData);
49 KmtOk(ActualRange->Owner == ExpectedRange->Owner, FileAndLine,
50 "[%lu] Owner = %p, expected %p\n", Index, ActualRange->Owner, ExpectedRange->Owner);
51 KmtOk(ActualRange->Attributes == ExpectedRange->Attributes, FileAndLine,
52 "[%lu] Attributes = 0x%x, expected 0x%x\n", Index, ActualRange->Attributes, ExpectedRange->Attributes);
53 KmtOk(ActualRange->Flags == ExpectedRange->Flags, FileAndLine,
54 "[%lu] Flags = 0x%x, expected 0x%x\n", Index, ActualRange->Flags, ExpectedRange->Flags);
55 }
56
57 static
58 void
ExpectRangeEntryList(_In_ PCSTR File,_In_ INT Line,_In_ RTL_RANGE_LIST * RangeList,_In_ ULONG NumRanges,_In_reads_ (NumRanges)const RTL_RANGE * Ranges)59 ExpectRangeEntryList(
60 _In_ PCSTR File,
61 _In_ INT Line,
62 _In_ RTL_RANGE_LIST *RangeList,
63 _In_ ULONG NumRanges,
64 _In_reads_(NumRanges) const RTL_RANGE *Ranges)
65 {
66 NTSTATUS Status;
67 ULONG i;
68 RTL_RANGE_LIST_ITERATOR Iterator;
69 PRTL_RANGE Range;
70 CHAR FileAndLine[128];
71 RtlStringCbPrintfA(FileAndLine, sizeof(FileAndLine), "%s:%d", File, Line);
72
73 RtlFillMemory(&Iterator, sizeof(Iterator), 0x55);
74 Range = KmtInvalidPointer;
75 Status = RtlGetFirstRange(RangeList, &Iterator, &Range);
76 #ifdef _WIN64
77 /* Padding at the end is uninitialized */
78 C_ASSERT(sizeof(Iterator) == RTL_SIZEOF_THROUGH_FIELD(RTL_RANGE_LIST_ITERATOR, Stamp) + sizeof(ULONG));
79 KmtOk((&Iterator.Stamp)[1] == 0x55555555, FileAndLine,
80 "Padding is 0x%lx\n", (&Iterator.Stamp)[1]);
81 #endif
82
83 for (i = 0; i < NumRanges; i++)
84 {
85 if (!KmtSkip(NT_SUCCESS(Status), FileAndLine, "Range does not have %lu element(s)\n", i + 1))
86 {
87 ExpectRange(File, Line, i, Range, &Ranges[i]);
88
89 /* Validate iterator */
90 KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine,
91 "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead);
92 KmtOk(Iterator.MergedHead == NULL, FileAndLine,
93 "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead);
94 KmtOk(Iterator.Current == Range, FileAndLine,
95 "[%lu] Iterator.Current = %p, expected %p\n", i, Iterator.Current, Range);
96 KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine,
97 "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp);
98 }
99
100 Range = KmtInvalidPointer;
101 Status = RtlGetNextRange(&Iterator, &Range, TRUE);
102 }
103
104 /* Final iteration status */
105 KmtOk(Status == STATUS_NO_MORE_ENTRIES, FileAndLine,
106 "Status = 0x%lx after enumeration\n", Status);
107 KmtOk(Range == NULL, FileAndLine,
108 "[%lu] Range = %p\n", i, Range);
109 KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine,
110 "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead);
111 KmtOk(Iterator.MergedHead == NULL, FileAndLine,
112 "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead);
113 KmtOk(Iterator.Current == NULL, FileAndLine,
114 "[%lu] Iterator.Current = %p\n", i, Iterator.Current);
115 KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine,
116 "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp);
117
118 /* Try one more iteration */
119 Range = KmtInvalidPointer;
120 Status = RtlGetNextRange(&Iterator, &Range, TRUE);
121 KmtOk(Status == STATUS_NO_MORE_ENTRIES, FileAndLine,
122 "Status = 0x%lx after enumeration\n", Status);
123 KmtOk(Range == NULL, FileAndLine,
124 "[%lu] Range = %p\n", i, Range);
125 KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine,
126 "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead);
127 KmtOk(Iterator.MergedHead == NULL, FileAndLine,
128 "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead);
129 KmtOk(Iterator.Current == NULL, FileAndLine,
130 "[%lu] Iterator.Current = %p\n", i, Iterator.Current);
131 KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine,
132 "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp);
133 }
134
135 #define expect_range_entries(RangeList, NumRanges, Ranges) \
136 ExpectRangeEntryList(__FILE__, __LINE__, RangeList, NumRanges, Ranges)
137
138 /* Test functions ************************************************************/
139 static
140 void
TestStartGreaterThanEnd(_Inout_ PRTL_RANGE_LIST RangeList,_Inout_ PRTL_RANGE Ranges)141 TestStartGreaterThanEnd(
142 _Inout_ PRTL_RANGE_LIST RangeList,
143 _Inout_ PRTL_RANGE Ranges)
144 {
145 NTSTATUS Status;
146 ULONG StartStamp = RangeList->Stamp;
147
148 Ranges[1].Start = 0x300;
149 Ranges[1].End = 0x2ff;
150 Ranges[1].Attributes = 2;
151 Ranges[1].Flags = 0;
152 Ranges[1].UserData = &MyUserData2;
153 Ranges[1].Owner = &MyOwner2;
154
155 /* Start > End bails out early with invalid parameter */
156 Status = RtlAddRangeWrapper(RangeList, &Ranges[1], 0);
157 ok_eq_hex(Status, STATUS_INVALID_PARAMETER);
158
159 /* List should be unchanged */
160 ok_eq_ulong(RangeList->Flags, 0UL);
161 ok_eq_ulong(RangeList->Count, 1UL);
162 ok_eq_ulong(RangeList->Stamp, StartStamp);
163 expect_range_entries(RangeList, 1, &Ranges[0]);
164 }
165
166 static
167 void
TestStartEqualsEnd(_Inout_ PRTL_RANGE_LIST RangeList,_Inout_ PRTL_RANGE Ranges)168 TestStartEqualsEnd(
169 _Inout_ PRTL_RANGE_LIST RangeList,
170 _Inout_ PRTL_RANGE Ranges)
171 {
172 NTSTATUS Status;
173 ULONG StartStamp = RangeList->Stamp;
174
175 Ranges[1].Start = 0x300;
176 Ranges[1].End = 0x300;
177 Ranges[1].Attributes = 0xff;
178 Ranges[1].Flags = 0;
179 Ranges[1].UserData = &MyUserData2;
180 Ranges[1].Owner = &MyOwner2;
181
182 /* Start == End is valid */
183 Status = RtlAddRangeWrapper(RangeList, &Ranges[1], 0);
184 ok_eq_hex(Status, STATUS_SUCCESS);
185
186 /* List now has two entries */
187 ok_eq_ulong(RangeList->Flags, 0UL);
188 ok_eq_ulong(RangeList->Count, 2UL);
189 ok_eq_ulong(RangeList->Stamp, StartStamp + 1);
190 expect_range_entries(RangeList, 2, &Ranges[0]);
191
192 /* Delete our new entry -- List goes back to one entry */
193 Status = RtlDeleteRange(RangeList, Ranges[1].Start, Ranges[1].End, Ranges[1].Owner);
194 ok_eq_hex(Status, STATUS_SUCCESS);
195 ok_eq_ulong(RangeList->Flags, 0UL);
196 ok_eq_ulong(RangeList->Count, 1UL);
197 ok_eq_ulong(RangeList->Stamp, StartStamp + 2);
198 expect_range_entries(RangeList, 1, &Ranges[0]);
199 }
200
201 static
202 void
TestSharedFlag(_Inout_ PRTL_RANGE_LIST RangeList,_Inout_ PRTL_RANGE Ranges)203 TestSharedFlag(
204 _Inout_ PRTL_RANGE_LIST RangeList,
205 _Inout_ PRTL_RANGE Ranges)
206 {
207 NTSTATUS Status;
208 ULONG StartStamp = RangeList->Stamp;
209
210 Ranges[1].Start = 0x300;
211 Ranges[1].End = 0x400;
212 Ranges[1].Attributes = 2;
213 Ranges[1].Flags = RTL_RANGE_SHARED;
214 Ranges[1].UserData = &MyUserData2;
215 Ranges[1].Owner = &MyOwner2;
216
217 /* Pass in the shared flag */
218 Status = RtlAddRangeWrapper(RangeList, &Ranges[1], RTL_RANGE_LIST_ADD_SHARED);
219 ok_eq_hex(Status, STATUS_SUCCESS);
220
221 /* List now has two entries */
222 ok_eq_ulong(RangeList->Flags, 0UL);
223 ok_eq_ulong(RangeList->Count, 2UL);
224 ok_eq_ulong(RangeList->Stamp, StartStamp + 1);
225 expect_range_entries(RangeList, 2, &Ranges[0]);
226
227 /* Delete our new entry -- List goes back to one entry */
228 Status = RtlDeleteRange(RangeList, Ranges[1].Start, Ranges[1].End, Ranges[1].Owner);
229 ok_eq_hex(Status, STATUS_SUCCESS);
230 ok_eq_ulong(RangeList->Flags, 0UL);
231 ok_eq_ulong(RangeList->Count, 1UL);
232 ok_eq_ulong(RangeList->Stamp, StartStamp + 2);
233 expect_range_entries(RangeList, 1, &Ranges[0]);
234 }
235
236 static
237 void
TestIsAvailable(_Inout_ PRTL_RANGE_LIST RangeList,_Inout_ PRTL_RANGE Ranges)238 TestIsAvailable(
239 _Inout_ PRTL_RANGE_LIST RangeList,
240 _Inout_ PRTL_RANGE Ranges)
241 {
242 NTSTATUS Status;
243 BOOLEAN Available;
244 ULONG StartStamp = RangeList->Stamp;
245
246 #define is_range_available(RangeList, Start, End, pAvail) \
247 RtlIsRangeAvailable(RangeList, \
248 Start, \
249 End, \
250 0, \
251 0, \
252 NULL, \
253 NULL, \
254 pAvail)
255
256 /* Single item range before Start */
257 Status = is_range_available(RangeList,
258 Ranges[0].Start - 1,
259 Ranges[0].Start - 1,
260 &Available);
261 ok_eq_hex(Status, STATUS_SUCCESS);
262 ok_eq_bool(Available, TRUE);
263
264 /* Single item range at Start */
265 Status = is_range_available(RangeList,
266 Ranges[0].Start,
267 Ranges[0].Start,
268 &Available);
269 ok_eq_hex(Status, STATUS_SUCCESS);
270 ok_eq_bool(Available, FALSE);
271
272 /* Single item range at End */
273 Status = is_range_available(RangeList,
274 Ranges[0].End,
275 Ranges[0].End,
276 &Available);
277 ok_eq_hex(Status, STATUS_SUCCESS);
278 ok_eq_bool(Available, FALSE);
279
280 /* Single item range after End */
281 Status = is_range_available(RangeList,
282 Ranges[0].End + 1,
283 Ranges[0].End + 1,
284 &Available);
285 ok_eq_hex(Status, STATUS_SUCCESS);
286 ok_eq_bool(Available, TRUE);
287
288 /* Range ending before Start */
289 Status = is_range_available(RangeList,
290 0x0,
291 Ranges[0].Start - 1,
292 &Available);
293 ok_eq_hex(Status, STATUS_SUCCESS);
294 ok_eq_bool(Available, TRUE);
295
296 /* Range ending at Start */
297 Status = is_range_available(RangeList,
298 0x0,
299 Ranges[0].Start,
300 &Available);
301 ok_eq_hex(Status, STATUS_SUCCESS);
302 ok_eq_bool(Available, FALSE);
303
304 /* Range ending in the middle */
305 Status = is_range_available(RangeList,
306 0x0,
307 (Ranges[0].Start + Ranges[0].End) / 2,
308 &Available);
309 ok_eq_hex(Status, STATUS_SUCCESS);
310 ok_eq_bool(Available, FALSE);
311
312 /* Range going all the way through */
313 Status = is_range_available(RangeList,
314 0x0,
315 Ranges[0].End + 0x100,
316 &Available);
317 ok_eq_hex(Status, STATUS_SUCCESS);
318 ok_eq_bool(Available, FALSE);
319
320 /* Range starting in the middle */
321 Status = is_range_available(RangeList,
322 (Ranges[0].Start + Ranges[0].End) / 2,
323 Ranges[0].End + 0x100,
324 &Available);
325 ok_eq_hex(Status, STATUS_SUCCESS);
326 ok_eq_bool(Available, FALSE);
327
328 /* Range starting at End */
329 Status = is_range_available(RangeList,
330 Ranges[0].End,
331 Ranges[0].End + 0x100,
332 &Available);
333 ok_eq_hex(Status, STATUS_SUCCESS);
334 ok_eq_bool(Available, FALSE);
335
336 /* Range starting after End */
337 Status = is_range_available(RangeList,
338 Ranges[0].End + 1,
339 Ranges[0].End + 0x100,
340 &Available);
341 ok_eq_hex(Status, STATUS_SUCCESS);
342 ok_eq_bool(Available, TRUE);
343
344 /* Start > End, at start */
345 Status = is_range_available(RangeList,
346 Ranges[0].Start,
347 Ranges[0].Start - 1,
348 &Available);
349 ok_eq_hex(Status, STATUS_SUCCESS);
350 ok_eq_bool(Available, TRUE);
351
352 /* Start > End, at start */
353 Status = is_range_available(RangeList,
354 Ranges[0].Start + 1,
355 Ranges[0].Start,
356 &Available);
357 ok_eq_hex(Status, STATUS_SUCCESS);
358 ok_eq_bool(Available, FALSE);
359
360 /* Start > End, at end */
361 Status = is_range_available(RangeList,
362 Ranges[0].End + 1,
363 Ranges[0].End,
364 &Available);
365 ok_eq_hex(Status, STATUS_SUCCESS);
366 ok_eq_bool(Available, TRUE);
367
368 /* Start > End, at end */
369 Status = is_range_available(RangeList,
370 Ranges[0].End,
371 Ranges[0].End - 1,
372 &Available);
373 ok_eq_hex(Status, STATUS_SUCCESS);
374 ok_eq_bool(Available, FALSE);
375
376 /* Start > End, through the range */
377 Status = is_range_available(RangeList,
378 Ranges[0].End + 1,
379 Ranges[0].Start - 1,
380 &Available);
381 ok_eq_hex(Status, STATUS_SUCCESS);
382 ok_eq_bool(Available, TRUE);
383
384 /* AttributesAvailableMask will make our range available */
385 Status = RtlIsRangeAvailable(RangeList,
386 0x0,
387 Ranges[0].End + 0x100,
388 0,
389 Ranges[0].Attributes,
390 NULL,
391 NULL,
392 &Available);
393 ok_eq_hex(Status, STATUS_SUCCESS);
394 ok_eq_bool(Available, TRUE);
395
396 /* AttributesAvailableMask with additional bits */
397 Status = RtlIsRangeAvailable(RangeList,
398 0x0,
399 Ranges[0].End + 0x100,
400 0,
401 0xFF,
402 NULL,
403 NULL,
404 &Available);
405 ok_eq_hex(Status, STATUS_SUCCESS);
406 ok_eq_bool(Available, TRUE);
407
408 ok_eq_ulong(RangeList->Stamp, StartStamp);
409 }
410
411 /* Entry point ***************************************************************/
START_TEST(RtlRangeList)412 START_TEST(RtlRangeList)
413 {
414 NTSTATUS Status;
415 RTL_RANGE_LIST RangeList;
416 RTL_RANGE Ranges[5];
417 ULONG Stamp;
418
419 RtlFillMemory(&RangeList, sizeof(RangeList), 0x55);
420 RtlInitializeRangeList(&RangeList);
421 ok(IsListEmpty(&RangeList.ListHead),
422 "RangeList.ListHead %p %p %p, expected empty\n",
423 &RangeList.ListHead, RangeList.ListHead.Flink, RangeList.ListHead.Blink);
424 ok_eq_ulong(RangeList.Flags, 0UL);
425 ok_eq_ulong(RangeList.Count, 0UL);
426 ok_eq_ulong(RangeList.Stamp, 0UL);
427 #ifdef _WIN64
428 /* Padding at the end is uninitialized */
429 C_ASSERT(sizeof(RangeList) == RTL_SIZEOF_THROUGH_FIELD(RTL_RANGE_LIST, Stamp) + sizeof(ULONG));
430 ok_eq_ulong((&RangeList.Stamp)[1], 0x55555555UL);
431 #endif
432
433 /* Add a simple range */
434 Ranges[0].Start = 0x100;
435 Ranges[0].End = 0x200;
436 Ranges[0].Attributes = 1;
437 Ranges[0].Flags = 0;
438 Ranges[0].UserData = &MyUserData1;
439 Ranges[0].Owner = &MyOwner1;
440 Status = RtlAddRangeWrapper(&RangeList, &Ranges[0], 0);
441 ok_eq_hex(Status, STATUS_SUCCESS);
442 ok_eq_ulong(RangeList.Flags, 0UL);
443 ok_eq_ulong(RangeList.Count, 1UL);
444 ok_eq_ulong(RangeList.Stamp, 1UL);
445 expect_range_entries(&RangeList, 1, &Ranges[0]);
446
447 /*
448 * Individual tests.
449 * These should always leave the list with our single start entry.
450 * Stamp may change between tests.
451 */
452 TestStartGreaterThanEnd(&RangeList, Ranges);
453 TestStartEqualsEnd(&RangeList, Ranges);
454 TestSharedFlag(&RangeList, Ranges);
455 TestIsAvailable(&RangeList, Ranges);
456
457 Stamp = RangeList.Stamp;
458
459 /* Free it and check the result */
460 RtlFreeRangeList(&RangeList);
461 ok_eq_ulong(RangeList.Flags, 0UL);
462 ok_eq_ulong(RangeList.Count, 0UL);
463 ok_eq_ulong(RangeList.Stamp, Stamp);
464 expect_range_entries(&RangeList, 0, NULL);
465 }
466