[PATCH v5 2/2] ntdll/tests: Add tests for UMIP instructions.

Brendan Shanks bshanks at codeweavers.com
Tue Jan 21 18:54:11 CST 2020


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 e8b481e703..e1e65b9ef2 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -328,6 +328,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;
@@ -2425,6 +2438,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;
@@ -3447,6 +3473,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, &reg16_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, &reg32_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, &reg64_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");
@@ -3578,6 +3951,7 @@ START_TEST(exception)
     test_suspend_thread();
     test_suspend_process();
     test_unload_trace();
+    test_umip();
 
 #elif defined(__x86_64__)
 
@@ -3616,6 +3990,7 @@ START_TEST(exception)
     test_suspend_thread();
     test_suspend_process();
     test_unload_trace();
+    test_umip();
 
     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
       test_dynamic_unwind();
-- 
2.24.1




More information about the wine-devel mailing list