1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Tests for user mode exceptions 5 * COPYRIGHT: Copyright 2021 Timo Kreuzer <timo.kreuzer@reactos.org> 6 */ 7 8 #include "precomp.h" 9 #include <pseh/pseh2.h> 10 11 typedef enum _CPU_VENDOR 12 { 13 CPU_VENDOR_INTEL, 14 CPU_VENDOR_AMD, 15 CPU_VENDOR_CYRIX, 16 CPU_VENDOR_VIA, 17 CPU_VENDOR_TRANSMETA, 18 CPU_VENDOR_UNKNOWN 19 } CPU_VENDOR; 20 21 CPU_VENDOR g_CpuVendor; 22 ULONG g_CpuFeatures; 23 24 #define CPU_FEATURE_VMX 0x01 25 #define CPU_FEATURE_HV 0x02 26 27 typedef void (*PFUNC)(void); 28 29 #define FL_INVALID 0 30 #define FL_AMD 0x01 31 #define FL_INTEL 0x02 32 #define FL_CYRIX 0x04 33 #define FL_VIA 0x08 34 #define FL_TM 0x10 35 #define FL_ANY (FL_AMD | FL_INTEL | FL_CYRIX | FL_VIA | FL_TM) 36 37 #define FL_VMX 0x20 38 #define FL_HV 0x40 39 40 #define FL_PRIV 0x100 41 #define FL_ACC 0x200 42 #define FL_INVLS 0x400 43 44 typedef struct _TEST_ENTRY 45 { 46 ULONG Line; 47 UCHAR InstructionBytes[64]; 48 ULONG ExpectedAddressOffset; 49 ULONG Flags; 50 } TEST_ENTRY, *PTEST_ENTRY; 51 52 static 53 CPU_VENDOR 54 DetermineCpuVendor(void) 55 { 56 INT CpuInfo[4]; 57 union 58 { 59 INT ShuffledInts[3]; 60 CHAR VendorString[13]; 61 } Vendor; 62 63 __cpuid(CpuInfo, 0); 64 Vendor.ShuffledInts[0] = CpuInfo[1]; 65 Vendor.ShuffledInts[1] = CpuInfo[3]; 66 Vendor.ShuffledInts[2] = CpuInfo[2]; 67 Vendor.VendorString[12] = 0; 68 trace("Vendor: %s\n", Vendor.VendorString); 69 70 if (strcmp(Vendor.VendorString, "GenuineIntel") == 0) 71 { 72 return CPU_VENDOR_INTEL; 73 } 74 else if (strcmp(Vendor.VendorString, "AuthenticAMD") == 0) 75 { 76 return CPU_VENDOR_AMD; 77 } 78 else if (strcmp(Vendor.VendorString, "CyrixInstead") == 0) 79 { 80 return CPU_VENDOR_CYRIX; 81 } 82 else if (strcmp(Vendor.VendorString, "CentaurHauls") == 0) 83 { 84 return CPU_VENDOR_VIA; 85 } 86 else if (strcmp(Vendor.VendorString, "GenuineTMx86") == 0) 87 { 88 return CPU_VENDOR_TRANSMETA; 89 } 90 else 91 { 92 return CPU_VENDOR_UNKNOWN; 93 } 94 } 95 96 static 97 void 98 DetermineCpuFeatures(void) 99 { 100 INT CpuInfo[4]; 101 ULONG Features = 0; 102 103 g_CpuVendor = DetermineCpuVendor(); 104 105 __cpuid(CpuInfo, 1); 106 if (CpuInfo[2] & (1 << 5)) Features |= CPU_FEATURE_VMX; 107 if (CpuInfo[2] & (1 << 31)) Features |= CPU_FEATURE_HV; 108 trace("CPUID 1: 0x%x, 0x%x, 0x%x, 0x%x\n", CpuInfo[0], CpuInfo[1], CpuInfo[2], CpuInfo[3]); 109 110 g_CpuFeatures = Features; 111 } 112 113 TEST_ENTRY TestEntries[] = 114 { 115 /* Some invalid instruction encodings */ 116 { __LINE__, { 0x0F, 0x0B, 0xC3 }, 0, FL_INVALID }, 117 { __LINE__, { 0x0F, 0x38, 0x0C, 0xC3 }, 0, FL_INVALID }, 118 #ifdef _M_AMD64 119 { __LINE__, { 0x06, 0xC3 }, 0, FL_INVALID }, 120 #endif 121 122 /* Privileged instructions */ 123 { __LINE__, { 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // HLT 124 { __LINE__, { 0xFA, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CLI 125 { __LINE__, { 0xFB, 0xC3 }, 0, FL_ANY | FL_PRIV }, // STI 126 { __LINE__, { 0x0F, 0x06, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CLTS 127 #ifdef _M_AMD64 128 { __LINE__, { 0x0F, 0x07, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SYSRET 129 #endif 130 { __LINE__, { 0x0F, 0x08, 0xC3 }, 0, FL_ANY | FL_PRIV }, // INVD 131 { __LINE__, { 0x0F, 0x09, 0xC3 }, 0, FL_ANY | FL_PRIV }, // WBINVD 132 { __LINE__, { 0x0F, 0x20, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV CR, XXX 133 { __LINE__, { 0x0F, 0x21, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV DR, XXX 134 { __LINE__, { 0x0F, 0x22, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV XXX, CR 135 { __LINE__, { 0x0F, 0x23, 0xC3 }, 0, FL_ANY | FL_PRIV }, // MOV YYY, DR 136 { __LINE__, { 0x0F, 0x30, 0xC3 }, 0, FL_ANY | FL_PRIV }, // WRMSR 137 { __LINE__, { 0x0F, 0x32, 0xC3 }, 0, FL_ANY | FL_PRIV }, // RDMSR 138 { __LINE__, { 0x0F, 0x33, 0xC3 }, 0, FL_ANY | FL_PRIV }, // RDPMC 139 { __LINE__, { 0x0F, 0x35, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SYSEXIT 140 { __LINE__, { 0x0F, 0x78, 0xC8, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMREAD EAX, ECX 141 { __LINE__, { 0x0F, 0x79, 0xC1, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMWRITE EAX, ECX 142 { __LINE__, { 0x0F, 0x00, 0x10, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LLDT WORD PTR [EAX] 143 { __LINE__, { 0x0F, 0x00, 0x50, 0x00, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LLDT WORD PTR [EAX + 0x00] 144 { __LINE__, { 0x0F, 0x00, 0x18, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LTR WORD PTR [EAX] 145 { __LINE__, { 0x0F, 0x00, 0x58, 0x00, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LTR WORD PTR [EAX + 0x00] 146 { __LINE__, { 0x0F, 0x01, 0xC1, 0xC3 }, 0, FL_INTEL | FL_HV }, // VMCALL 147 { __LINE__, { 0x0F, 0x01, 0xC2, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMLAUNCH 148 { __LINE__, { 0x0F, 0x01, 0xC3, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMRESUME 149 { __LINE__, { 0x0F, 0x01, 0xC4, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMXOFF 150 { __LINE__, { 0x0F, 0x01, 0xC8, 0xC3 }, 0, FL_INVALID }, // MONITOR 151 { __LINE__, { 0x0F, 0x01, 0xC9, 0xC3 }, 0, FL_INVALID }, // MWAIT 152 // { __LINE__, { 0x0F, 0x01, 0xD1, 0xC3 }, 0, FL_ANY | FL_PRIV }, // XSETBV FIXME: privileged or access violation? 153 #ifdef _M_AMD64 154 { __LINE__, { 0x0F, 0x01, 0xF8, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SWAPGS 155 #endif 156 { __LINE__, { 0x0F, 0x01, 0x10, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LGDT [EAX] 157 { __LINE__, { 0x0F, 0x01, 0x18, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LIDT [EAX] 158 { __LINE__, { 0x0F, 0x01, 0x30, 0xC3 }, 0, FL_ANY | FL_PRIV }, // LMSW [EAX] 159 #ifdef _M_AMD64 // Gives access violation on Test WHS for some reason 160 { __LINE__, { 0x0F, 0x01, 0x38, 0xC3 }, 0, FL_ANY | FL_PRIV }, // INVLPG [EAX] 161 #endif 162 { __LINE__, { 0x66, 0x0F, 0x38, 0x80, 0x01, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // INVEPT EAX,OWORD PTR [ECX] 163 { __LINE__, { 0x66, 0x0F, 0x38, 0x81, 0x01, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // INVVPID EAX,OWORD PTR [ECX] 164 { __LINE__, { 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMPTRLD QWORD PTR [ECX] 165 { __LINE__, { 0x66, 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INTEL | FL_HV | FL_ACC }, // VMCLEAR QWORD PTR [ECX] 166 { __LINE__, { 0xF3, 0x0F, 0xC7, 0x31, 0xC3 }, 0, FL_INVALID }, // VMXON QWORD PTR [ECX] 167 { __LINE__, { 0x0F, 0xC7, 0xE0, 0xC3 }, 0, FL_INVALID }, // VMPTRST 168 169 /* Test prefixes */ 170 { __LINE__, { 0x26, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // ES HLT 171 { __LINE__, { 0x2E, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // CS: HLT 172 { __LINE__, { 0x36, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // SS: HLT 173 { __LINE__, { 0x3E, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // DS: HLT 174 { __LINE__, { 0x64, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // FS: HLT 175 { __LINE__, { 0x65, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // GS: HLT 176 { __LINE__, { 0x66, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // DATA HLT 177 { __LINE__, { 0x67, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // ADDR HLT 178 { __LINE__, { 0xF0, 0xF4, 0xC3 }, 0, FL_ANY | FL_INVLS | FL_PRIV }, // LOCK HLT 179 { __LINE__, { 0xF2, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REP HLT 180 { __LINE__, { 0xF3, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REPZ HLT 181 { __LINE__, { 0x9B, 0xF4, 0xC3 }, 1, FL_ANY | FL_PRIV }, // WAIT // not a prefix 182 #ifdef _M_AMD64 183 { __LINE__, { 0x40, 0xF4, 0xC3 }, 0, FL_ANY | FL_PRIV }, // REX HLT 184 #endif 185 { __LINE__, { 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0xC3 }, 0, FL_ANY }, // This one is OK 186 #ifdef _M_AMD64 187 { __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 188 #else 189 { __LINE__, { 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0xC3 }, 0, FL_INVALID }, // Too many prefixes 190 #endif 191 { __LINE__, { 0xF0, 0x90, 0xC3 }, 0, FL_INVLS }, // LOCK NOP 192 { __LINE__, { 0xF0, 0x83, 0x0C, 0x24, 0x00, 0xC3 }, 0, FL_ANY }, // LOCK OR DWORD PTR [ESP], 0x0 193 { __LINE__, { 0x3E, 0x66, 0x67, 0xF0, 0xF3, 0xF4, 0xC3 }, 0, FL_ANY | FL_INVLS | FL_PRIV }, // DS: DATA ADDR LOCK REPZ HLT 194 #ifdef _M_AMD64 195 /* Check non-canonical address access (causes a #GP) */ 196 { __LINE__, { 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xC3 }, 0, FL_ANY | FL_ACC }, // MOV AL, [0x1000000000000000] 197 #endif 198 199 }; 200 201 void Test_SingleInstruction( 202 PVOID RwxMemory, 203 PTEST_ENTRY TestEntry) 204 { 205 PEXCEPTION_POINTERS ExcPtrs; 206 EXCEPTION_RECORD ExceptionRecord; 207 NTSTATUS ExpectedStatus, Status = STATUS_SUCCESS; 208 PFUNC Func = (PFUNC)RwxMemory; 209 ULONG Flags; 210 211 RtlZeroMemory(&ExceptionRecord, sizeof(ExceptionRecord)); 212 213 RtlCopyMemory(RwxMemory, 214 TestEntry->InstructionBytes, 215 sizeof(TestEntry->InstructionBytes)); 216 217 _SEH2_TRY 218 { 219 Func(); 220 } 221 _SEH2_EXCEPT(ExcPtrs = _SEH2_GetExceptionInformation(), 222 ExceptionRecord = *ExcPtrs->ExceptionRecord, 223 EXCEPTION_EXECUTE_HANDLER) 224 { 225 Status = _SEH2_GetExceptionCode(); 226 } 227 _SEH2_END; 228 229 Flags = TestEntry->Flags; 230 #ifdef _M_IX86 231 if (Flags & FL_INVLS) 232 { 233 ExpectedStatus = STATUS_INVALID_LOCK_SEQUENCE; 234 } 235 else 236 #endif 237 if (((g_CpuVendor == CPU_VENDOR_INTEL) && !(Flags & FL_INTEL)) || 238 ((g_CpuVendor == CPU_VENDOR_AMD) && !(Flags & FL_AMD)) || 239 ((g_CpuVendor == CPU_VENDOR_CYRIX) && !(Flags & FL_CYRIX)) || 240 ((g_CpuVendor == CPU_VENDOR_VIA) && !(Flags & FL_VIA))) 241 { 242 ExpectedStatus = STATUS_ILLEGAL_INSTRUCTION; 243 } 244 else if (((Flags & FL_VMX) && !(g_CpuFeatures & CPU_FEATURE_VMX)) || 245 ((Flags & FL_HV) && !(g_CpuFeatures & CPU_FEATURE_HV))) 246 { 247 ExpectedStatus = STATUS_ILLEGAL_INSTRUCTION; 248 } 249 else if (Flags & FL_PRIV) 250 { 251 ExpectedStatus = STATUS_PRIVILEGED_INSTRUCTION; 252 } 253 else if (Flags & FL_ACC) 254 { 255 ExpectedStatus = STATUS_ACCESS_VIOLATION; 256 } 257 else 258 { 259 ExpectedStatus = STATUS_SUCCESS; 260 } 261 262 ok_hex_(__FILE__, TestEntry->Line, Status, ExpectedStatus); 263 ok_hex_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionCode, Status); 264 ok_hex_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionFlags, 0); 265 ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionRecord, NULL); 266 267 if (Status == STATUS_SUCCESS) 268 { 269 ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionAddress, NULL); 270 } 271 else 272 { 273 ok_ptr_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionAddress, (PUCHAR)Func + TestEntry->ExpectedAddressOffset); 274 } 275 276 if (Status == STATUS_ACCESS_VIOLATION) 277 { 278 ok_dec_(__FILE__, TestEntry->Line, ExceptionRecord.NumberParameters, 2); 279 ok_size_t_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionInformation[0], 0); 280 ok_size_t_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionInformation[1], (LONG_PTR)-1); 281 } 282 else 283 { 284 ok_dec_(__FILE__, TestEntry->Line, ExceptionRecord.NumberParameters, 0); 285 #if 0 // FIXME: These are inconsistent between Windows versions (simply uninitialized?) 286 ok_size_t_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionInformation[0], 0); 287 ok_size_t_(__FILE__, TestEntry->Line, ExceptionRecord.ExceptionInformation[1], 0); 288 #endif 289 } 290 } 291 292 static 293 void 294 Test_InstructionFaults(void) 295 { 296 PVOID RwxMemory; 297 ULONG i; 298 299 /* Allocate a page of RWX memory */ 300 RwxMemory = VirtualAlloc(NULL, 301 PAGE_SIZE, 302 MEM_RESERVE | MEM_COMMIT, 303 PAGE_EXECUTE_READWRITE); 304 ok(RwxMemory != NULL, "Failed to allocate RWX memory!\n"); 305 if (RwxMemory == NULL) 306 { 307 return; 308 } 309 310 for (i = 0; i < ARRAYSIZE(TestEntries); i++) 311 { 312 Test_SingleInstruction(RwxMemory, &TestEntries[i]); 313 } 314 315 /* Clean up */ 316 VirtualFree(RwxMemory, 0, MEM_RELEASE); 317 } 318 319 START_TEST(UserModeException) 320 { 321 DetermineCpuFeatures(); 322 323 Test_InstructionFaults(); 324 } 325