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 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 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 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 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 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 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 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 ***************************************************************/ 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