From 4bc7e664fbc1b8afcd736cc32444bf42b14fe575 Mon Sep 17 00:00:00 2001 From: Thomas Faber Date: Sat, 13 Jun 2020 12:24:35 +0200 Subject: [PATCH] [KMTESTS:RTL] Add initial range list tests. CORE-6372 --- modules/rostests/kmtests/CMakeLists.txt | 1 + .../rostests/kmtests/kmtest_drv/testlist.c | 2 + modules/rostests/kmtests/rtl/RtlRangeList.c | 465 ++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 modules/rostests/kmtests/rtl/RtlRangeList.c diff --git a/modules/rostests/kmtests/CMakeLists.txt b/modules/rostests/kmtests/CMakeLists.txt index 4c03a27d58f..0d7eb194aaf 100644 --- a/modules/rostests/kmtests/CMakeLists.txt +++ b/modules/rostests/kmtests/CMakeLists.txt @@ -97,6 +97,7 @@ list(APPEND KMTEST_DRV_SOURCE ntos_se/SeInheritance.c ntos_se/SeQueryInfoToken.c rtl/RtlIsValidOemCharacter.c + rtl/RtlRangeList.c ${COMMON_SOURCE} kmtest_drv/kmtest_drv.rc) diff --git a/modules/rostests/kmtests/kmtest_drv/testlist.c b/modules/rostests/kmtests/kmtest_drv/testlist.c index 8bdd245b79a..859ec68ea6c 100644 --- a/modules/rostests/kmtests/kmtest_drv/testlist.c +++ b/modules/rostests/kmtests/kmtest_drv/testlist.c @@ -69,6 +69,7 @@ KMT_TESTFUNC Test_RtlException; KMT_TESTFUNC Test_RtlIntSafe; KMT_TESTFUNC Test_RtlIsValidOemCharacter; KMT_TESTFUNC Test_RtlMemory; +KMT_TESTFUNC Test_RtlRangeList; KMT_TESTFUNC Test_RtlRegistry; KMT_TESTFUNC Test_RtlSplayTree; KMT_TESTFUNC Test_RtlStack; @@ -142,6 +143,7 @@ const KMT_TEST TestList[] = { "RtlIntSafeKM", Test_RtlIntSafe }, { "RtlIsValidOemCharacter", Test_RtlIsValidOemCharacter }, { "RtlMemoryKM", Test_RtlMemory }, + { "RtlRangeList", Test_RtlRangeList }, { "RtlRegistryKM", Test_RtlRegistry }, { "RtlSplayTreeKM", Test_RtlSplayTree }, { "RtlStackKM", Test_RtlStack }, diff --git a/modules/rostests/kmtests/rtl/RtlRangeList.c b/modules/rostests/kmtests/rtl/RtlRangeList.c new file mode 100644 index 00000000000..e487e2b9730 --- /dev/null +++ b/modules/rostests/kmtests/rtl/RtlRangeList.c @@ -0,0 +1,465 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Test for Rtl Range Lists + * COPYRIGHT: Copyright 2020 Thomas Faber (thomas.faber@reactos.org) + */ + +#include +#include + +static UCHAR MyUserData1, MyUserData2; +static UCHAR MyOwner1, MyOwner2; + +/* Helpers *******************************************************************/ +static +NTSTATUS +RtlAddRangeWrapper( + _Inout_ PRTL_RANGE_LIST RangeList, + _In_ const RTL_RANGE *Range, + _In_ ULONG Flags) +{ + return RtlAddRange(RangeList, + Range->Start, + Range->End, + Range->Attributes, + Flags, + Range->UserData, + Range->Owner); +} + +static +void +ExpectRange( + _In_ PCSTR File, + _In_ INT Line, + _In_ ULONG Index, + _In_ const RTL_RANGE *ActualRange, + _In_ const RTL_RANGE *ExpectedRange) +{ + CHAR FileAndLine[128]; + RtlStringCbPrintfA(FileAndLine, sizeof(FileAndLine), "%s:%d", File, Line); + + KmtOk(ActualRange->Start == ExpectedRange->Start, FileAndLine, + "[%lu] Start = 0x%I64x, expected 0x%I64x\n", Index, ActualRange->Start, ExpectedRange->Start); + KmtOk(ActualRange->End == ExpectedRange->End, FileAndLine, + "[%lu] End = 0x%I64x, expected 0x%I64x\n", Index, ActualRange->End, ExpectedRange->End); + KmtOk(ActualRange->UserData == ExpectedRange->UserData, FileAndLine, + "[%lu] UserData = %p, expected %p\n", Index, ActualRange->UserData, ExpectedRange->UserData); + KmtOk(ActualRange->Owner == ExpectedRange->Owner, FileAndLine, + "[%lu] Owner = %p, expected %p\n", Index, ActualRange->Owner, ExpectedRange->Owner); + KmtOk(ActualRange->Attributes == ExpectedRange->Attributes, FileAndLine, + "[%lu] Attributes = 0x%x, expected 0x%x\n", Index, ActualRange->Attributes, ExpectedRange->Attributes); + KmtOk(ActualRange->Flags == ExpectedRange->Flags, FileAndLine, + "[%lu] Flags = 0x%x, expected 0x%x\n", Index, ActualRange->Flags, ExpectedRange->Flags); +} + +static +void +ExpectRangeEntryList( + _In_ PCSTR File, + _In_ INT Line, + _In_ RTL_RANGE_LIST *RangeList, + _In_ ULONG NumRanges, + _In_reads_(NumRanges) const RTL_RANGE *Ranges) +{ + NTSTATUS Status; + ULONG i; + RTL_RANGE_LIST_ITERATOR Iterator; + PRTL_RANGE Range; + CHAR FileAndLine[128]; + RtlStringCbPrintfA(FileAndLine, sizeof(FileAndLine), "%s:%d", File, Line); + + RtlFillMemory(&Iterator, sizeof(Iterator), 0x55); + Range = KmtInvalidPointer; + Status = RtlGetFirstRange(RangeList, &Iterator, &Range); +#ifdef _WIN64 + /* Padding at the end is uninitialized */ + C_ASSERT(sizeof(Iterator) == RTL_SIZEOF_THROUGH_FIELD(RTL_RANGE_LIST_ITERATOR, Stamp) + sizeof(ULONG)); + KmtOk((&Iterator.Stamp)[1] == 0x55555555, FileAndLine, + "Padding is 0x%lx\n", (&Iterator.Stamp)[1]); +#endif + + for (i = 0; i < NumRanges; i++) + { + if (!KmtSkip(NT_SUCCESS(Status), FileAndLine, "Range does not have %lu element(s)\n", i + 1)) + { + ExpectRange(File, Line, i, Range, &Ranges[i]); + + /* Validate iterator */ + KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine, + "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead); + KmtOk(Iterator.MergedHead == NULL, FileAndLine, + "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead); + KmtOk(Iterator.Current == Range, FileAndLine, + "[%lu] Iterator.Current = %p, expected %p\n", i, Iterator.Current, Range); + KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine, + "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp); + } + + Range = KmtInvalidPointer; + Status = RtlGetNextRange(&Iterator, &Range, TRUE); + } + + /* Final iteration status */ + KmtOk(Status == STATUS_NO_MORE_ENTRIES, FileAndLine, + "Status = 0x%lx after enumeration\n", Status); + KmtOk(Range == NULL, FileAndLine, + "[%lu] Range = %p\n", i, Range); + KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine, + "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead); + KmtOk(Iterator.MergedHead == NULL, FileAndLine, + "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead); + KmtOk(Iterator.Current == NULL, FileAndLine, + "[%lu] Iterator.Current = %p\n", i, Iterator.Current); + KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine, + "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp); + + /* Try one more iteration */ + Range = KmtInvalidPointer; + Status = RtlGetNextRange(&Iterator, &Range, TRUE); + KmtOk(Status == STATUS_NO_MORE_ENTRIES, FileAndLine, + "Status = 0x%lx after enumeration\n", Status); + KmtOk(Range == NULL, FileAndLine, + "[%lu] Range = %p\n", i, Range); + KmtOk(Iterator.RangeListHead == &RangeList->ListHead, FileAndLine, + "[%lu] Iterator.RangeListHead = %p, expected %p\n", i, Iterator.RangeListHead, &RangeList->ListHead); + KmtOk(Iterator.MergedHead == NULL, FileAndLine, + "[%lu] Iterator.MergedHead = %p\n", i, Iterator.MergedHead); + KmtOk(Iterator.Current == NULL, FileAndLine, + "[%lu] Iterator.Current = %p\n", i, Iterator.Current); + KmtOk(Iterator.Stamp == RangeList->Stamp, FileAndLine, + "[%lu] Iterator.Stamp = %lu, expected %lu\n", i, Iterator.Stamp, RangeList->Stamp); +} + +#define expect_range_entries(RangeList, NumRanges, Ranges) \ + ExpectRangeEntryList(__FILE__, __LINE__, RangeList, NumRanges, Ranges) + +/* Test functions ************************************************************/ +static +void +TestStartGreaterThanEnd( + _Inout_ PRTL_RANGE_LIST RangeList, + _Inout_ PRTL_RANGE Ranges) +{ + NTSTATUS Status; + ULONG StartStamp = RangeList->Stamp; + + Ranges[1].Start = 0x300; + Ranges[1].End = 0x2ff; + Ranges[1].Attributes = 2; + Ranges[1].Flags = 0; + Ranges[1].UserData = &MyUserData2; + Ranges[1].Owner = &MyOwner2; + + /* Start > End bails out early with invalid parameter */ + Status = RtlAddRangeWrapper(RangeList, &Ranges[1], 0); + ok_eq_hex(Status, STATUS_INVALID_PARAMETER); + + /* List should be unchanged */ + ok_eq_ulong(RangeList->Flags, 0UL); + ok_eq_ulong(RangeList->Count, 1UL); + ok_eq_ulong(RangeList->Stamp, StartStamp); + expect_range_entries(RangeList, 1, &Ranges[0]); +} + +static +void +TestStartEqualsEnd( + _Inout_ PRTL_RANGE_LIST RangeList, + _Inout_ PRTL_RANGE Ranges) +{ + NTSTATUS Status; + ULONG StartStamp = RangeList->Stamp; + + Ranges[1].Start = 0x300; + Ranges[1].End = 0x300; + Ranges[1].Attributes = 0xff; + Ranges[1].Flags = 0; + Ranges[1].UserData = &MyUserData2; + Ranges[1].Owner = &MyOwner2; + + /* Start == End is valid */ + Status = RtlAddRangeWrapper(RangeList, &Ranges[1], 0); + ok_eq_hex(Status, STATUS_SUCCESS); + + /* List now has two entries */ + ok_eq_ulong(RangeList->Flags, 0UL); + ok_eq_ulong(RangeList->Count, 2UL); + ok_eq_ulong(RangeList->Stamp, StartStamp + 1); + expect_range_entries(RangeList, 2, &Ranges[0]); + + /* Delete our new entry -- List goes back to one entry */ + Status = RtlDeleteRange(RangeList, Ranges[1].Start, Ranges[1].End, Ranges[1].Owner); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(RangeList->Flags, 0UL); + ok_eq_ulong(RangeList->Count, 1UL); + ok_eq_ulong(RangeList->Stamp, StartStamp + 2); + expect_range_entries(RangeList, 1, &Ranges[0]); +} + +static +void +TestSharedFlag( + _Inout_ PRTL_RANGE_LIST RangeList, + _Inout_ PRTL_RANGE Ranges) +{ + NTSTATUS Status; + ULONG StartStamp = RangeList->Stamp; + + Ranges[1].Start = 0x300; + Ranges[1].End = 0x400; + Ranges[1].Attributes = 2; + Ranges[1].Flags = RTL_RANGE_SHARED; + Ranges[1].UserData = &MyUserData2; + Ranges[1].Owner = &MyOwner2; + + /* Pass in the shared flag */ + Status = RtlAddRangeWrapper(RangeList, &Ranges[1], RTL_RANGE_LIST_ADD_SHARED); + ok_eq_hex(Status, STATUS_SUCCESS); + + /* List now has two entries */ + ok_eq_ulong(RangeList->Flags, 0UL); + ok_eq_ulong(RangeList->Count, 2UL); + ok_eq_ulong(RangeList->Stamp, StartStamp + 1); + expect_range_entries(RangeList, 2, &Ranges[0]); + + /* Delete our new entry -- List goes back to one entry */ + Status = RtlDeleteRange(RangeList, Ranges[1].Start, Ranges[1].End, Ranges[1].Owner); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(RangeList->Flags, 0UL); + ok_eq_ulong(RangeList->Count, 1UL); + ok_eq_ulong(RangeList->Stamp, StartStamp + 2); + expect_range_entries(RangeList, 1, &Ranges[0]); +} + +static +void +TestIsAvailable( + _Inout_ PRTL_RANGE_LIST RangeList, + _Inout_ PRTL_RANGE Ranges) +{ + NTSTATUS Status; + BOOLEAN Available; + ULONG StartStamp = RangeList->Stamp; + +#define is_range_available(RangeList, Start, End, pAvail) \ + RtlIsRangeAvailable(RangeList, \ + Start, \ + End, \ + 0, \ + 0, \ + NULL, \ + NULL, \ + pAvail) + + /* Single item range before Start */ + Status = is_range_available(RangeList, + Ranges[0].Start - 1, + Ranges[0].Start - 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Single item range at Start */ + Status = is_range_available(RangeList, + Ranges[0].Start, + Ranges[0].Start, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Single item range at End */ + Status = is_range_available(RangeList, + Ranges[0].End, + Ranges[0].End, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Single item range after End */ + Status = is_range_available(RangeList, + Ranges[0].End + 1, + Ranges[0].End + 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Range ending before Start */ + Status = is_range_available(RangeList, + 0x0, + Ranges[0].Start - 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Range ending at Start */ + Status = is_range_available(RangeList, + 0x0, + Ranges[0].Start, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Range ending in the middle */ + Status = is_range_available(RangeList, + 0x0, + (Ranges[0].Start + Ranges[0].End) / 2, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Range going all the way through */ + Status = is_range_available(RangeList, + 0x0, + Ranges[0].End + 0x100, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Range starting in the middle */ + Status = is_range_available(RangeList, + (Ranges[0].Start + Ranges[0].End) / 2, + Ranges[0].End + 0x100, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Range starting at End */ + Status = is_range_available(RangeList, + Ranges[0].End, + Ranges[0].End + 0x100, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Range starting after End */ + Status = is_range_available(RangeList, + Ranges[0].End + 1, + Ranges[0].End + 0x100, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Start > End, at start */ + Status = is_range_available(RangeList, + Ranges[0].Start, + Ranges[0].Start - 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Start > End, at start */ + Status = is_range_available(RangeList, + Ranges[0].Start + 1, + Ranges[0].Start, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Start > End, at end */ + Status = is_range_available(RangeList, + Ranges[0].End + 1, + Ranges[0].End, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* Start > End, at end */ + Status = is_range_available(RangeList, + Ranges[0].End, + Ranges[0].End - 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, FALSE); + + /* Start > End, through the range */ + Status = is_range_available(RangeList, + Ranges[0].End + 1, + Ranges[0].Start - 1, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* AttributesAvailableMask will make our range available */ + Status = RtlIsRangeAvailable(RangeList, + 0x0, + Ranges[0].End + 0x100, + 0, + Ranges[0].Attributes, + NULL, + NULL, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + /* AttributesAvailableMask with additional bits */ + Status = RtlIsRangeAvailable(RangeList, + 0x0, + Ranges[0].End + 0x100, + 0, + 0xFF, + NULL, + NULL, + &Available); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_bool(Available, TRUE); + + ok_eq_ulong(RangeList->Stamp, StartStamp); +} + +/* Entry point ***************************************************************/ +START_TEST(RtlRangeList) +{ + NTSTATUS Status; + RTL_RANGE_LIST RangeList; + RTL_RANGE Ranges[5]; + ULONG Stamp; + + RtlFillMemory(&RangeList, sizeof(RangeList), 0x55); + RtlInitializeRangeList(&RangeList); + ok(IsListEmpty(&RangeList.ListHead), + "RangeList.ListHead %p %p %p, expected empty\n", + &RangeList.ListHead, RangeList.ListHead.Flink, RangeList.ListHead.Blink); + ok_eq_ulong(RangeList.Flags, 0UL); + ok_eq_ulong(RangeList.Count, 0UL); + ok_eq_ulong(RangeList.Stamp, 0UL); +#ifdef _WIN64 + /* Padding at the end is uninitialized */ + C_ASSERT(sizeof(RangeList) == RTL_SIZEOF_THROUGH_FIELD(RTL_RANGE_LIST, Stamp) + sizeof(ULONG)); + ok_eq_ulong((&RangeList.Stamp)[1], 0x55555555UL); +#endif + + /* Add a simple range */ + Ranges[0].Start = 0x100; + Ranges[0].End = 0x200; + Ranges[0].Attributes = 1; + Ranges[0].Flags = 0; + Ranges[0].UserData = &MyUserData1; + Ranges[0].Owner = &MyOwner1; + Status = RtlAddRangeWrapper(&RangeList, &Ranges[0], 0); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(RangeList.Flags, 0UL); + ok_eq_ulong(RangeList.Count, 1UL); + ok_eq_ulong(RangeList.Stamp, 1UL); + expect_range_entries(&RangeList, 1, &Ranges[0]); + + /* + * Individual tests. + * These should always leave the list with our single start entry. + * Stamp may change between tests. + */ + TestStartGreaterThanEnd(&RangeList, Ranges); + TestStartEqualsEnd(&RangeList, Ranges); + TestSharedFlag(&RangeList, Ranges); + TestIsAvailable(&RangeList, Ranges); + + Stamp = RangeList.Stamp; + + /* Free it and check the result */ + RtlFreeRangeList(&RangeList); + ok_eq_ulong(RangeList.Flags, 0UL); + ok_eq_ulong(RangeList.Count, 0UL); + ok_eq_ulong(RangeList.Stamp, Stamp); + expect_range_entries(&RangeList, 0, NULL); +}