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
DetermineCpuVendor(void)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
DetermineCpuFeatures(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
Test_SingleInstruction(PVOID RwxMemory,PTEST_ENTRY TestEntry)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
Test_InstructionFaults(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
START_TEST(UserModeException)319 START_TEST(UserModeException)
320 {
321 DetermineCpuFeatures();
322
323 Test_InstructionFaults();
324 }
325