https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4bc7e664fbc1b8afcd736…
commit 4bc7e664fbc1b8afcd736cc32444bf42b14fe575
Author: Thomas Faber <thomas.faber(a)reactos.org>
AuthorDate: Sat Jun 13 12:24:35 2020 +0200
Commit: Thomas Faber <thomas.faber(a)reactos.org>
CommitDate: Sat Aug 15 21:31:32 2020 +0200
[KMTESTS:RTL] Add initial range list tests. CORE-6372
---
modules/rostests/kmtests/CMakeLists.txt | 1 +
modules/rostests/kmtests/kmtest_drv/testlist.c | 2 +
modules/rostests/kmtests/rtl/RtlRangeList.c | 465 +++++++++++++++++++++++++
3 files changed, 468 insertions(+)
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(a)reactos.org)
+ */
+
+#include <kmt_test.h>
+#include <ndk/rtlfuncs.h>
+
+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);
+}