https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4bc7e664fbc1b8afcd736cc32444bf42b14fe575

commit 4bc7e664fbc1b8afcd736cc32444bf42b14fe575
Author:     Thomas Faber <[email protected]>
AuthorDate: Sat Jun 13 12:24:35 2020 +0200
Commit:     Thomas Faber <[email protected]>
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 ([email protected])
+ */
+
+#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);
+}

Reply via email to