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

commit f659ac520157d41fb6f87f930782013a4003b02c
Author:     Timo Kreuzer <[email protected]>
AuthorDate: Sat Oct 26 16:44:57 2019 +0200
Commit:     Timo Kreuzer <[email protected]>
CommitDate: Thu Jul 14 18:35:28 2022 +0200

    [NTDLL_APITEST] Add test for some user mode exceptions
---
 modules/rostests/apitests/ntdll/CMakeLists.txt     |   1 +
 .../rostests/apitests/ntdll/UserModeException.c    | 324 +++++++++++++++++++++
 modules/rostests/apitests/ntdll/testlist.c         |   2 +
 3 files changed, 327 insertions(+)

diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt 
b/modules/rostests/apitests/ntdll/CMakeLists.txt
index 39296e4d8f2..e1076a3a584 100644
--- a/modules/rostests/apitests/ntdll/CMakeLists.txt
+++ b/modules/rostests/apitests/ntdll/CMakeLists.txt
@@ -94,6 +94,7 @@ list(APPEND SOURCE
     RtlxUnicodeStringToOemSize.c
     StackOverflow.c
     SystemInfo.c
+    UserModeException.c
     Timer.c)
 
 if(ARCH STREQUAL "i386")
diff --git a/modules/rostests/apitests/ntdll/UserModeException.c 
b/modules/rostests/apitests/ntdll/UserModeException.c
new file mode 100644
index 00000000000..6cf1b5ca5a3
--- /dev/null
+++ b/modules/rostests/apitests/ntdll/UserModeException.c
@@ -0,0 +1,324 @@
+/*
+ * PROJECT:     ReactOS API tests
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Tests for user mode exceptions
+ * COPYRIGHT:   Copyright 2021 Timo Kreuzer <[email protected]>
+ */
+
+#include "precomp.h"
+#include <pseh/pseh2.h>
+
+typedef enum _CPU_VENDOR
+{
+    CPU_VENDOR_INTEL,
+    CPU_VENDOR_AMD,
+    CPU_VENDOR_CYRIX,
+    CPU_VENDOR_VIA,
+    CPU_VENDOR_TRANSMETA,
+    CPU_VENDOR_UNKNOWN
+} CPU_VENDOR;
+
+CPU_VENDOR g_CpuVendor;
+ULONG g_CpuFeatures;
+
+#define CPU_FEATURE_VMX 0x01
+#define CPU_FEATURE_HV 0x02
+
+typedef void (*PFUNC)(void);
+
+#define FL_INVALID 0
+#define FL_AMD 0x01
+#define FL_INTEL 0x02
+#define FL_CYRIX 0x04
+#define FL_VIA 0x08
+#define FL_TM 0x10
+#define FL_ANY (FL_AMD | FL_INTEL | FL_CYRIX | FL_VIA | FL_TM)
+
+#define FL_VMX 0x20
+#define FL_HV 0x40
+
+#define FL_PRIV 0x100
+#define FL_ACC 0x200
+#define FL_INVLS 0x400
+
+typedef struct _TEST_ENTRY
+{
+    ULONG Line;
+    UCHAR InstructionBytes[64];
+    ULONG ExpectedAddressOffset;
+    ULONG Flags;
+} TEST_ENTRY, *PTEST_ENTRY;
+
+static
+CPU_VENDOR
+DetermineCpuVendor(void)
+{
+    INT CpuInfo[4];
+    union
+    {
+        INT ShuffledInts[3];
+        CHAR VendorString[13];
+    } Vendor;
+
+    __cpuid(CpuInfo, 0);
+    Vendor.ShuffledInts[0] = CpuInfo[1];
+    Vendor.ShuffledInts[1] = CpuInfo[3];
+    Vendor.ShuffledInts[2] = CpuInfo[2];
+    Vendor.VendorString[12] = 0;
+    trace("Vendor: %s\n", Vendor.VendorString);
+
+    if (strcmp(Vendor.VendorString, "GenuineIntel") == 0)
+    {
+        return CPU_VENDOR_INTEL;
+    }
+    else if (strcmp(Vendor.VendorString, "AuthenticAMD") == 0)
+    {
+        return CPU_VENDOR_AMD;
+    }
+    else if (strcmp(Vendor.VendorString, "CyrixInstead") == 0)
+    {
+        return CPU_VENDOR_CYRIX;
+    }
+    else if (strcmp(Vendor.VendorString, "CentaurHauls") == 0)
+    {
+        return CPU_VENDOR_VIA;
+    }
+    else if (strcmp(Vendor.VendorString, "GenuineTMx86") == 0)
+    {
+        return CPU_VENDOR_TRANSMETA;
+    }
+    else
+    {
+        return CPU_VENDOR_UNKNOWN;
+    }
+}
+
+static
+void
+DetermineCpuFeatures(void)
+{
+    INT CpuInfo[4];
+    ULONG Features = 0;
+
+    g_CpuVendor = DetermineCpuVendor();
+
+    __cpuid(CpuInfo, 1);
+    if (CpuInfo[2] & (1 << 5)) Features |= CPU_FEATURE_VMX;
+    if (CpuInfo[2] & (1 << 31)) Features |= CPU_FEATURE_HV;
+    trace("CPUID 1: 0x%x, 0x%x, 0x%x, 0x%x\n", CpuInfo[0], CpuInfo[1], 
CpuInfo[2], CpuInfo[3]);
+
+    g_CpuFeatures = Features;
+}
+
+TEST_ENTRY TestEntries[] =
+{
+    /* Some invalid instruction encodings */
+    { __LINE__, { 0x0F, 0x0B, 0xC3 }, 0, FL_INVALID },
+    { __LINE__, { 0x0F, 0x38, 0x0C, 0xC3 }, 0, FL_INVALID },
+#ifdef _M_AMD64
+    { __LINE__, { 0x06, 0xC3 }, 0, FL_INVALID },
+#endif
+
+    /* Privileged instructions */
+    { __LINE__, { 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // HLT
+    { __LINE__, { 0xFA, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CLI
+    { __LINE__, { 0xFB, 0xC3 }, 0, FL_ANY | FL_PRIV }, // STI
+    { __LINE__, { 0x0F, 0x06, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CLTS
+#ifdef _M_AMD64
+    { __LINE__, { 0x0F, 0x07, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SYSRET
+#endif
+    { __LINE__, { 0x0F, 0x08, 0xC3 }, 0, FL_ANY | FL_PRIV }, // INVD
+    { __LINE__, { 0x0F, 0x09, 0xC3 }, 0, FL_ANY | FL_PRIV }, // WBINVD
+    { __LINE__, { 0x0F, 0x20, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV CR, XXX
+    { __LINE__, { 0x0F, 0x21, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV DR, XXX
+    { __LINE__, { 0x0F, 0x22, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV XXX, CR
+    { __LINE__, { 0x0F, 0x23, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV YYY, DR
+    { __LINE__, { 0x0F, 0x30, 0xC3 }, 0, FL_ANY | FL_PRIV }, // WRMSR
+    { __LINE__, { 0x0F, 0x32, 0xC3 }, 0, FL_ANY | FL_PRIV }, // RDMSR
+    { __LINE__, { 0x0F, 0x33, 0xC3 }, 0, FL_ANY | FL_PRIV }, // RDPMC
+    { __LINE__, { 0x0F, 0x35, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SYSEXIT
+    { __LINE__, { 0x0F, 0x78, 0xC8, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // 
VMREAD EAX, ECX
+    { __LINE__, { 0x0F, 0x79, 0xC1, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // 
VMWRITE EAX, ECX
+    { __LINE__, { 0x0F, 0x00, 0x10, 0xC3 }, 0, FL_ANY | FL_PRIV },  // LLDT 
WORD PTR [EAX]
+    { __LINE__, { 0x0F, 0x00, 0x50, 0x00, 0xC3 }, 0, FL_ANY | FL_PRIV },  // 
LLDT WORD PTR [EAX + 0x00]
+    { __LINE__, { 0x0F, 0x00, 0x18, 0xC3 }, 0, FL_ANY | FL_PRIV },  // LTR 
WORD PTR [EAX]
+    { __LINE__, { 0x0F, 0x00, 0x58, 0x00, 0xC3 }, 0, FL_ANY | FL_PRIV },  // 
LTR WORD PTR [EAX + 0x00]
+    { __LINE__, { 0x0F, 0x01, 0xC1, 0xC3 }, 0, FL_INTEL | FL_HV },  // VMCALL
+    { __LINE__, { 0x0F, 0x01, 0xC2, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC },  
// VMLAUNCH
+    { __LINE__, { 0x0F, 0x01, 0xC3, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC },  
// VMRESUME
+    { __LINE__, { 0x0F, 0x01, 0xC4, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC },  
// VMXOFF
+    { __LINE__, { 0x0F, 0x01, 0xC8, 0xC3 }, 0, FL_INVALID },  // MONITOR
+    { __LINE__, { 0x0F, 0x01, 0xC9, 0xC3 }, 0, FL_INVALID },  // MWAIT
+    // { __LINE__, { 0x0F, 0x01, 0xD1, 0xC3 }, 0, FL_ANY | FL_PRIV },  // 
XSETBV FIXME: privileged or access violation?
+#ifdef _M_AMD64
+    { __LINE__, { 0x0F, 0x01, 0xF8, 0xC3 }, 0, FL_ANY | FL_PRIV },  // SWAPGS
+#endif
+    { __LINE__, { 0x0F, 0x01, 0x10, 0xC3 }, 0, FL_ANY | FL_PRIV },  // LGDT 
[EAX]
+    { __LINE__, { 0x0F, 0x01, 0x18, 0xC3 }, 0, FL_ANY | FL_PRIV },  // LIDT 
[EAX]
+    { __LINE__, { 0x0F, 0x01, 0x30, 0xC3 }, 0, FL_ANY | FL_PRIV },  // LMSW 
[EAX]
+#ifdef _M_AMD64 // Gives access violation on Test WHS for some reason
+    { __LINE__, { 0x0F, 0x01, 0x38, 0xC3 }, 0, FL_ANY | FL_PRIV },  // INVLPG 
[EAX]
+#endif
+    { __LINE__, { 0x66, 0x0F, 0x38, 0x80, 0x01, 0xC3 }, 0, FL_INTEL | FL_HV | 
FL_ACC }, // INVEPT EAX,OWORD PTR [ECX]
+    { __LINE__, { 0x66, 0x0F, 0x38, 0x81, 0x01, 0xC3 }, 0, FL_INTEL | FL_HV | 
FL_ACC }, // INVVPID EAX,OWORD PTR [ECX]
+    { __LINE__, { 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // 
VMPTRLD QWORD PTR [ECX]
+    { __LINE__, { 0x66, 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC 
}, // VMCLEAR QWORD PTR [ECX]
+    { __LINE__, { 0xF3, 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INVALID }, // VMXON 
QWORD PTR [ECX]
+    { __LINE__, { 0x0F, 0xC7, 0xE0, 0xC3 }, 0, FL_INVALID }, // VMPTRST
+
+    /* Test prefixes */
+    { __LINE__, { 0x26, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // ES HLT
+    { __LINE__, { 0x2E, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CS: HLT
+    { __LINE__, { 0x36, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SS: HLT
+    { __LINE__, { 0x3E, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // DS: HLT
+    { __LINE__, { 0x64, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // FS: HLT
+    { __LINE__, { 0x65, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // GS: HLT
+    { __LINE__, { 0x66, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // DATA HLT
+    { __LINE__, { 0x67, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // ADDR HLT
+    { __LINE__, { 0xF0, 0xF4, 0xC3 }, 0, FL_ANY | FL_INVLS | FL_PRIV }, // 
LOCK HLT
+    { __LINE__, { 0xF2, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REP HLT
+    { __LINE__, { 0xF3, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REPZ HLT
+    { __LINE__, { 0x9B, 0xF4, 0xC3 }, 1, FL_ANY | FL_PRIV }, // WAIT // not a 
prefix
+#ifdef _M_AMD64
+    { __LINE__, { 0x40, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REX HLT
+#endif
+    { __LINE__, { 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 
0x26, 0x26, 0x26, 0x26, 0xC3 }, 0, FL_ANY }, // This one is OK
+#ifdef _M_AMD64
+    { __LINE__, { 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 
0x26, 0x26, 0x26, 0x26, 0x26, 0xC3 }, 0, FL_ANY | FL_ACC }, // Too many prefixes
+#else
+    { __LINE__, { 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 
0x26, 0x26, 0x26, 0x26, 0x26, 0xC3 }, 0, FL_INVALID }, // Too many prefixes
+#endif
+    { __LINE__, { 0xF0, 0x90, 0xC3 }, 0, FL_INVLS }, // LOCK NOP
+    { __LINE__, { 0xF0, 0x83, 0x0C, 0x24, 0x00, 0xC3 }, 0, FL_ANY }, // LOCK 
OR DWORD PTR [ESP], 0x0 
+    { __LINE__, { 0x3E, 0x66, 0x67, 0xF0, 0xF3, 0xF4, 0xC3 }, 0, FL_ANY | 
FL_INVLS | FL_PRIV }, // DS: DATA ADDR LOCK REPZ HLT
+#ifdef _M_AMD64
+    /* Check non-canonical address access (causes a #GP) */
+    { __LINE__, { 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xC3 
}, 0, FL_ANY | FL_ACC }, //  MOV AL, [0x1000000000000000]
+#endif
+
+};
+
+void Test_SingleInstruction(
+    PVOID RwxMemory,
+    PTEST_ENTRY TestEntry)
+{
+    PEXCEPTION_POINTERS ExcPtrs;
+    EXCEPTION_RECORD ExceptionRecord;
+    NTSTATUS ExpectedStatus, Status = STATUS_SUCCESS;
+    PFUNC Func = (PFUNC)RwxMemory;
+    ULONG Flags;
+
+    RtlZeroMemory(&ExceptionRecord, sizeof(ExceptionRecord));
+
+    RtlCopyMemory(RwxMemory,
+                  TestEntry->InstructionBytes,
+                  sizeof(TestEntry->InstructionBytes));
+
+    _SEH2_TRY
+    {
+        Func();
+    }
+    _SEH2_EXCEPT(ExcPtrs = _SEH2_GetExceptionInformation(),
+                 ExceptionRecord = *ExcPtrs->ExceptionRecord,
+                 EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+    Flags = TestEntry->Flags;
+#ifdef _M_IX86
+    if (Flags & FL_INVLS)
+    {
+        ExpectedStatus = STATUS_INVALID_LOCK_SEQUENCE;
+    }
+    else
+#endif
+    if (((g_CpuVendor == CPU_VENDOR_INTEL) && !(Flags & FL_INTEL)) ||
+        ((g_CpuVendor == CPU_VENDOR_AMD) && !(Flags & FL_AMD)) ||
+        ((g_CpuVendor == CPU_VENDOR_CYRIX) && !(Flags & FL_CYRIX)) ||
+        ((g_CpuVendor == CPU_VENDOR_VIA) && !(Flags & FL_VIA)))
+    {
+        ExpectedStatus = STATUS_ILLEGAL_INSTRUCTION;
+    }
+    else if (((Flags & FL_VMX) && !(g_CpuFeatures & CPU_FEATURE_VMX)) ||
+             ((Flags & FL_HV) && !(g_CpuFeatures & CPU_FEATURE_HV)))
+    {
+        ExpectedStatus = STATUS_ILLEGAL_INSTRUCTION;
+    }
+    else if (Flags & FL_PRIV)
+    {
+        ExpectedStatus = STATUS_PRIVILEGED_INSTRUCTION;
+    }
+    else if (Flags & FL_ACC)
+    {
+        ExpectedStatus = STATUS_ACCESS_VIOLATION;
+    }
+    else
+    {
+        ExpectedStatus = STATUS_SUCCESS;
+    }
+
+    ok_hex_(__FILE__, TestEntry->Line, Status, ExpectedStatus);
+    ok_hex_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionCode, Status);
+    ok_hex_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionFlags, 0);
+    ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionRecord, NULL);
+
+    if (Status == STATUS_SUCCESS)
+    {
+        ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionAddress, 
NULL);
+    }
+    else
+    {
+        ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionAddress, 
(PUCHAR)Func + TestEntry->ExpectedAddressOffset);
+    }
+
+    if (Status == STATUS_ACCESS_VIOLATION)
+    {
+        ok_dec_(__FILE__, TestEntry->Line, ExceptionRecord.NumberParameters, 
2);
+        ok_size_t_(__FILE__, TestEntry->Line, 
ExceptionRecord.ExceptionInformation[0], 0);
+        ok_size_t_(__FILE__, TestEntry->Line, 
ExceptionRecord.ExceptionInformation[1], (LONG_PTR)-1);
+    }
+    else
+    {
+        ok_dec_(__FILE__, TestEntry->Line, ExceptionRecord.NumberParameters, 
0);
+#if 0 // FIXME: These are inconsistent between Windows versions (simply 
uninitialized?)
+        ok_size_t_(__FILE__, TestEntry->Line, 
ExceptionRecord.ExceptionInformation[0], 0);
+        ok_size_t_(__FILE__, TestEntry->Line, 
ExceptionRecord.ExceptionInformation[1], 0);
+#endif
+    }
+}
+
+static
+void
+Test_InstructionFaults(void)
+{
+    PVOID RwxMemory;
+    ULONG i;
+
+    /* Allocate a page of RWX memory */
+    RwxMemory = VirtualAlloc(NULL,
+                             PAGE_SIZE,
+                             MEM_RESERVE | MEM_COMMIT,
+                             PAGE_EXECUTE_READWRITE);
+    ok(RwxMemory != NULL, "Failed to allocate RWX memory!\n");
+    if (RwxMemory == NULL)
+    {
+        return;
+    }
+
+    for (i = 0; i < ARRAYSIZE(TestEntries); i++)
+    {
+        Test_SingleInstruction(RwxMemory, &TestEntries[i]);
+    }
+
+    /* Clean up */
+    VirtualFree(RwxMemory, 0, MEM_RELEASE);
+}
+
+START_TEST(UserModeException)
+{
+    DetermineCpuFeatures();
+
+    Test_InstructionFaults();
+}
diff --git a/modules/rostests/apitests/ntdll/testlist.c 
b/modules/rostests/apitests/ntdll/testlist.c
index 3b88411cfb6..f5b265f0305 100644
--- a/modules/rostests/apitests/ntdll/testlist.c
+++ b/modules/rostests/apitests/ntdll/testlist.c
@@ -90,6 +90,7 @@ extern void func_RtlxUnicodeStringToAnsiSize(void);
 extern void func_RtlxUnicodeStringToOemSize(void);
 extern void func_StackOverflow(void);
 extern void func_TimerResolution(void);
+extern void func_UserModeException(void);
 
 const struct test winetest_testlist[] =
 {
@@ -180,6 +181,7 @@ const struct test winetest_testlist[] =
     { "RtlValidateUnicodeString",       func_RtlValidateUnicodeString },
     { "StackOverflow",                  func_StackOverflow },
     { "TimerResolution",                func_TimerResolution },
+    { "UserModeException",              func_UserModeException },
 
     { 0, 0 }
 };

Reply via email to