[PATCH v3 2/2] ntdll/tests: Add tests for UMIP instructions.
Brendan Shanks
bshanks at codeweavers.com
Thu Nov 21 15:24:36 CST 2019
Signed-off-by: Brendan Shanks <bshanks at codeweavers.com>
---
dlls/ntdll/tests/exception.c | 375 +++++++++++++++++++++++++++++++++++
1 file changed, 375 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index fb58c0ee7a..0d63731b56 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -327,6 +327,19 @@ static const struct exception
0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */
0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */
17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
+
+ /* test UMIP-covered instructions storing to a NULL pointer */
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x00, 0xc3 }, /* xor %eax,%eax; sgdt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+/* 45 */
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x08, 0xc3 }, /* xor %eax,%eax; sidt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x00, 0x00, 0xc3 }, /* xor %eax,%eax; sldt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x20, 0xc3 }, /* xor %eax,%eax; smsw (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x00, 0x08, 0xc3 }, /* xor %eax,%eax; str (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
};
static int got_exception;
@@ -2420,6 +2433,19 @@ static const struct exception
1, 1, STATUS_SINGLE_STEP, 0 },
{ { 0xcd, 0x2c, 0xc3 },
0, 2, STATUS_ASSERTION_FAILURE, 0 },
+
+ /* test UMIP-covered instructions storing to a NULL pointer */
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x00, 0xc3 }, /* xor %rax,%rax; sgdt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x08, 0xc3 }, /* xor %rax,%rax; sidt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+/* 40 */
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x00, 0x00, 0xc3 }, /* xor %rax,%rax; sldt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x20, 0xc3 }, /* xor %rax,%rax; smsw (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x00, 0x08, 0xc3 }, /* xor %rax,%rax; str (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
};
static int got_exception;
@@ -3383,6 +3409,353 @@ static void test_unload_trace(void)
ok(found, "Unloaded module wasn't found.\n");
}
+#if defined(__x86_64__)
+static DWORD WINAPI umip_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
+ CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
+{
+ const UINT *umip_insn_len = *(const UINT **)(dispatcher->HandlerData);
+
+ got_exception++;
+ trace( "umip_handler exception: %x flags:%x addr:%p\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+ context->Rip += *umip_insn_len;
+ return ExceptionContinueExecution;
+}
+#else
+static DWORD umip_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
+{
+ const UINT *umip_insn_len = *(const UINT **)(frame + 1);
+
+ got_exception++;
+ trace( "umip_handler exception: %x flags:%x addr:%p\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+ context->Eip += *umip_insn_len;
+ return ExceptionContinueExecution;
+}
+#endif
+
+/* Each UMIP test is run first using run_exception_test() in case it throws an exception. If it doesn't, the
+ * test is run again to check the returned value.
+ * Tests writing to memory have two versions: one used for run_exception_test() that writes to the stack, and the
+ * other writing to a pointer passed as an argument.
+ */
+static void umip_test_reg_mem_16(const char *insn_name,
+ const BYTE *reg16_code, UINT reg16_code_size, UINT reg16_insn_len,
+ const BYTE *reg32_code, UINT reg32_code_size, UINT reg32_insn_len,
+ const BYTE *reg64_code, UINT reg64_code_size, UINT reg64_insn_len,
+ const BYTE *mem16_arg_code, UINT mem16_arg_code_size,
+ const BYTE *mem16_stack_code, UINT mem16_stack_code_size, UINT mem16_stack_insn_len)
+{
+ /* sldt, str, smsw all store a 16-bit value to a register or memory.
+ * When the destination is a register, the 16-bit value is zero-extended to the destination register size.
+ * When the destination is memory, only the 16-bit value is stored.
+ */
+ UINT16 value16;
+ UINT32 value32;
+
+ UINT16 (*reg16_func)(void) = code_mem;
+ UINT32 (*reg32_func)(void) = code_mem;
+ void (*mem16_func)(UINT16 *value) = code_mem;
+
+ /* Destination is 16-bit register */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®16_insn_len, reg16_code, reg16_code_size, 0);
+ ok(!got_exception, "%s reg16 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value16, 0xcc, sizeof(value16));
+ memcpy(code_mem, reg16_code, reg16_code_size);
+ value16 = reg16_func();
+ trace("%s reg16: 0x%x\n", insn_name, value16);
+ }
+
+ /* Destination is 32-bit register. */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®32_insn_len, reg32_code, reg32_code_size, 0);
+ ok(!got_exception, "%s reg32 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value32, 0xcc, sizeof(value32));
+ memcpy(code_mem, reg32_code, reg32_code_size);
+ value32 = reg32_func();
+ trace("%s reg32: 0x%x\n", insn_name, value32);
+#if defined(__x86_64__)
+ /* For sldt/str in 64-bit mode, the upper 16 bits is defined to be zero */
+ if (!strcmp(insn_name, "sldt") || !strcmp(insn_name, "str"))
+ ok(value32 >> 16 == 0, "%s expected upper 16 bits = 0, got 0x%x\n", insn_name, value32 >> 16);
+#endif
+ }
+
+#if defined(__x86_64__)
+ /* Destination is 64-bit register */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®64_insn_len, reg64_code, reg64_code_size, 0);
+ ok(!got_exception, "%s reg64 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ UINT64 value64;
+ UINT64 (*reg64_func)(void) = code_mem;
+
+ memset(&value64, 0xcc, sizeof(value64));
+ memcpy(code_mem, reg64_code, reg64_code_size);
+ value64 = reg64_func();
+ trace("%s reg64: 0x%llx\n", insn_name, value64);
+
+ /* For sldt/str the upper 48 bits is defined to be zero */
+ if (!strcmp(insn_name, "sldt") || !strcmp(insn_name, "str"))
+ ok(value64 >> 16 == 0, "%s expected upper 48 bits = 0, got 0x%llx\n", insn_name, value64 >> 16);
+ }
+#endif
+
+ /* Destination is memory (only the low 16 bits are defined to be written) */
+ got_exception = 0;
+ run_exception_test(umip_handler, &mem16_stack_insn_len, mem16_stack_code, mem16_stack_code_size, 0);
+ ok(!got_exception, "%s mem16_stack caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value32, 0xcc, sizeof(value32));
+ memcpy(code_mem, mem16_arg_code, mem16_arg_code_size);
+ mem16_func((UINT16 *)&value32);
+ trace("%s mem: 0x%x\n", insn_name, value32);
+ ok(value32 >> 16 == 0xcccc, "%s expected upper 16 bits = 0xcccc, got 0x%x\n", insn_name, value32 >> 16);
+ }
+}
+
+static void umip_test_mem_descriptor(const char *insn_name,
+ const BYTE *code_arg, UINT code_arg_size,
+ const BYTE *code_stack, UINT code_stack_size, UINT code_stack_insn_len)
+{
+ /* sgdt and sidt write the descriptor table register to memory.
+ * The descriptor consists of a 2-byte limit field, and a base field which
+ * is 4 bytes in 32-bit mode and 8 bytes in 64-bit mode.
+ */
+ BYTE descriptor[10];
+ void (*func)(BYTE *value) = code_mem;
+
+ got_exception = 0;
+ run_exception_test(umip_handler, &code_stack_insn_len, code_stack, code_stack_size, 0);
+ ok(!got_exception, "%s caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(descriptor, 0xcc, sizeof(descriptor));
+ memcpy(code_mem, code_arg, code_arg_size);
+ func(descriptor);
+ trace("%s limit: 0x%x\n", insn_name, *(UINT16 *)&descriptor[0]);
+ trace("%s base: 0x%p\n", insn_name, (void *) *(UINT_PTR *)&descriptor[2]);
+ }
+}
+
+static void umip_test_sldt(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x00, 0xc0, /* sldtw %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x00, 0xc0, /* sldtl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x00, 0xc0, /* sldtq %rax */
+ 0xc3, /* ret */
+ };
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x00, 0x01, /* sldtw (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x00, 0x44, 0x24, 0x08, /* sldtw 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x00, 0x01, /* sldtw (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x00, 0x04, 0x24, /* sldtw (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("sldt",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void umip_test_str(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x00, 0xc8, /* strw %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x00, 0xc8, /* strl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x00, 0xc8, /* strq %rax */
+ 0xc3, /* ret */
+ };
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x00, 0x09, /* strw (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x00, 0x4c, 0x24, 0x08, /* strw 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x00, 0x09, /* strw (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x00, 0x0c, 0x24, /* strw (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("str",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void umip_test_sgdt(void)
+{
+ /* sgdt destination must be memory */
+#if defined(__x86_64__)
+ static const BYTE mem_arg[] = {
+ 0x0f, 0x01, 0x01, /* sgdtt (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x0f, 0x01, 0x44, 0x24, 0x08, /* sgdtt 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x01, /* sgdt (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x83, 0xec, 0x06, /* subl $0x6,%esp */
+ 0x0f, 0x01, 0x04, 0x24, /* sgdt (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x06, /* addl $0x6,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_mem_descriptor("sgdt", mem_arg, sizeof(mem_arg), mem_stack, sizeof(mem_stack), 5);
+}
+
+static void umip_test_sidt(void)
+{
+ /* sidt destination must be memory */
+#if defined(__x86_64__)
+ static const BYTE mem_arg[] = {
+ 0x0f, 0x01, 0x09, /* sidtt (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x0f, 0x01, 0x4c, 0x24, 0x08, /* sidtt 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x09, /* sidt (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x83, 0xec, 0x06, /* subl $0x6,%esp */
+ 0x0f, 0x01, 0x0c, 0x24, /* sidt (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x06, /* addl $0x6,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_mem_descriptor("sidt", mem_arg, sizeof(mem_arg), mem_stack, sizeof(mem_stack), 5);
+}
+
+static void umip_test_smsw(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x01, 0xe0, /* smsww %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x01, 0xe0, /* smswl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x01, 0xe0, /* smswq %rax */
+ 0xc3, /* ret */
+ };
+
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x01, 0x21, /* smsww (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x01, 0x64, 0x24, 0x08, /* smsww 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x21, /* smsww (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x01, 0x24, 0x24, /* smsww (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("smsw",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void test_umip(void)
+{
+ umip_test_sldt();
+ umip_test_str();
+ umip_test_sgdt();
+ umip_test_sidt();
+ umip_test_smsw();
+}
+
START_TEST(exception)
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -3513,6 +3886,7 @@ START_TEST(exception)
test_suspend_thread();
test_suspend_process();
test_unload_trace();
+ test_umip();
#elif defined(__x86_64__)
@@ -3551,6 +3925,7 @@ START_TEST(exception)
test_suspend_thread();
test_suspend_process();
test_unload_trace();
+ test_umip();
if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
test_dynamic_unwind();
--
2.23.0
More information about the wine-devel
mailing list