https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f659ac520157d41fb6f87f...
commit f659ac520157d41fb6f87f930782013a4003b02c Author: Timo Kreuzer timo.kreuzer@reactos.org AuthorDate: Sat Oct 26 16:44:57 2019 +0200 Commit: Timo Kreuzer timo.kreuzer@reactos.org 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 timo.kreuzer@reactos.org + */ + +#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 } };