[v3 3/3] ntdll: Fix and test exception codes on x86-64.

Andrew Wesie awesie at gmail.com
Sun Feb 5 00:10:05 CST 2017


The test code is copy-paste from the i386 test code, with some minor
modifications. There are four cases that I was not able to fix and are
marked as todo.

Signed-off-by: Andrew Wesie <awesie at gmail.com>
---
 dlls/ntdll/signal_x86_64.c   |  10 +-
 dlls/ntdll/tests/exception.c | 225 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 234 insertions(+), 1 deletion(-)

diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 8b85be7..8b35b42 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -2595,6 +2595,9 @@ static inline BOOL handle_interrupt( unsigned int interrupt, EXCEPTION_RECORD *r
 {
     switch(interrupt)
     {
+    case 0x2c:
+        rec->ExceptionCode = STATUS_ASSERTION_FAILURE;
+        return TRUE;
     case 0x2d:
         context->Rip += 3;
         rec->ExceptionCode = EXCEPTION_BREAKPOINT;
@@ -2658,7 +2661,6 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
             WORD err = ERROR_sig(ucontext);
             if ((err & 7) == 2 && handle_interrupt( err >> 3, rec, win_context )) break;
             rec->ExceptionCode = err ? EXCEPTION_ACCESS_VIOLATION : EXCEPTION_PRIV_INSTRUCTION;
-            rec->ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
         }
         break;
     case TRAP_x86_PAGEFLT:  /* Page fault */
@@ -2700,6 +2702,12 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         rec->ExceptionCode = EXCEPTION_SINGLE_STEP;
         break;
     case TRAP_BRKPT:   /* Breakpoint exception */
+        /* Check if this is actuallly icebp instruction */
+        if (((unsigned char *)rec->ExceptionAddress)[-1] == 0xF1)
+        {
+            rec->ExceptionCode = EXCEPTION_SINGLE_STEP;
+            break;
+        }
         rec->ExceptionAddress = (char *)rec->ExceptionAddress - 1;  /* back up over the int3 instruction */
         /* fall through */
     default:
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 80c22d0..6df16ad 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -2114,6 +2114,230 @@ static void test_unwind_rdi_rsi(void)
     ok( result == 0x33333333, "expected %x, got %x\n", 0x33333333, result );
 }
 
+/* This is heavily based on the i386 exception tests. */
+static const struct exception
+{
+    BYTE     code[18];      /* asm code */
+    BYTE     offset;        /* offset of faulting instruction */
+    BYTE     length;        /* length of faulting instruction */
+    NTSTATUS status;        /* expected status code */
+    BOOL     todo;
+    DWORD    nb_params;     /* expected number of parameters */
+    ULONG64  params[4];     /* expected parameters */
+    NTSTATUS alt_status;    /* alternative status code */
+    DWORD    alt_nb_params; /* alternative number of parameters */
+    ULONG64  alt_params[4]; /* alternative parameters */
+} exceptions[] =
+{
+/* 0 */
+    /* test some privileged instructions */
+    { { 0xfb, 0xc3 },  /* 0: sti; ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x6c, 0xc3 },  /* 1: insb (%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x6d, 0xc3 },  /* 2: insl (%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x6e, 0xc3 },  /* 3: outsb (%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x6f, 0xc3 },  /* 4: outsl (%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+/* 5 */
+    { { 0xe4, 0x11, 0xc3 },  /* 5: inb $0x11,%al; ret */
+      0, 2, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xe5, 0x11, 0xc3 },  /* 6: inl $0x11,%eax; ret */
+      0, 2, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xe6, 0x11, 0xc3 },  /* 7: outb %al,$0x11; ret */
+      0, 2, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xe7, 0x11, 0xc3 },  /* 8: outl %eax,$0x11; ret */
+      0, 2, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xed, 0xc3 },  /* 9: inl (%dx),%eax; ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+/* 10 */
+    { { 0xee, 0xc3 },  /* 10: outb %al,(%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xef, 0xc3 },  /* 11: outl %eax,(%dx); ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xf4, 0xc3 },  /* 12: hlt; ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0xfa, 0xc3 },  /* 13: cli; ret */
+      0, 1, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+
+    /* test iret to invalid selector */
+    { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x18, 0xc3 },
+      /* 15: pushq $0; pushq $0; pushq $0; iret; addl $24,%esp; ret */
+      6, 1, STATUS_ACCESS_VIOLATION, TRUE, 2, { 0, 0xffffffffffffffff } },
+/* 15 */
+    /* test loading an invalid selector */
+    { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 },  /* 16: mov $beef,%ax; mov %ax,%gs; ret */
+      5, 2, STATUS_ACCESS_VIOLATION, TRUE, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
+
+    /* test overlong instruction (limit is 15 bytes) */
+    { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
+      0, 16, STATUS_ILLEGAL_INSTRUCTION, TRUE, 0, { 0 },
+      STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
+
+    /* test invalid interrupt */
+    { { 0xcd, 0xff, 0xc3 },   /* int $0xff; ret */
+      0, 2, STATUS_ACCESS_VIOLATION, TRUE, 2, { 0, 0xffffffffffffffff } },
+
+    /* test moves to/from Crx */
+    { { 0x0f, 0x20, 0xc0, 0xc3 },  /* movl %cr0,%eax; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x20, 0xe0, 0xc3 },  /* movl %cr4,%eax; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+/* 20 */
+    { { 0x0f, 0x22, 0xc0, 0xc3 },  /* movl %eax,%cr0; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x22, 0xe0, 0xc3 },  /* movl %eax,%cr4; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+
+    /* test moves to/from Drx */
+    { { 0x0f, 0x21, 0xc0, 0xc3 },  /* movl %dr0,%eax; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x21, 0xc8, 0xc3 },  /* movl %dr1,%eax; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x21, 0xf8, 0xc3 },  /* movl %dr7,%eax; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+/* 25 */
+    { { 0x0f, 0x23, 0xc0, 0xc3 },  /* movl %eax,%dr0; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x23, 0xc8, 0xc3 },  /* movl %eax,%dr1; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+    { { 0x0f, 0x23, 0xf8, 0xc3 },  /* movl %eax,%dr7; ret */
+      0, 3, STATUS_PRIVILEGED_INSTRUCTION, FALSE, 0 },
+
+    /* test memory reads */
+    { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffffffffffc,%eax; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 0, 0xfffffffffffffffc } },
+    { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffffffffffd,%eax; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 0, 0xfffffffffffffffd } },
+/* 30 */
+    { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffffffffffe,%eax; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 0, 0xfffffffffffffffe } },
+    { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xffffffffffffffff,%eax; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 0, 0xffffffffffffffff } },
+
+    /* test memory writes */
+    { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffffffffffc; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 1, 0xfffffffffffffffc } },
+    { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffffffffffd; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 1, 0xfffffffffffffffd } },
+    { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffffffffffe; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 1, 0xfffffffffffffffe } },
+/* 35 */
+    { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xffffffffffffffff; ret */
+      0, 9, STATUS_ACCESS_VIOLATION, FALSE, 2, { 1, 0xffffffffffffffff } },
+
+    { { 0xf1, 0x90, 0xc3 },  /* icebp; nop; ret */
+      1, 1, STATUS_SINGLE_STEP, FALSE, 0 },
+
+    { { 0xcd, 0x2c, 0xc3 },
+      0, 2, STATUS_ASSERTION_FAILURE, FALSE, 0 },
+};
+
+static int got_exception;
+
+static DWORD WINAPI handler( EXCEPTION_RECORD *rec, ULONG64 frame,
+                      CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
+{
+    const struct exception *except = *(const struct exception **)(dispatcher->HandlerData);
+    unsigned int i, parameter_count, entry = except - exceptions;
+
+    got_exception++;
+    trace( "exception %u: %x flags:%x addr:%p\n",
+           entry, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+    if (except->todo)
+    {
+        if (rec->ExceptionCode != except->status)
+        {
+            todo_wine ok( rec->ExceptionCode == except->status ||
+                (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
+                "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
+            goto skip_params;
+        }
+        else if (rec->NumberParameters != except->nb_params)
+        {
+            todo_wine ok( rec->NumberParameters == except->nb_params,
+                "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, except->nb_params );
+            goto skip_params;
+        }
+    }
+
+    ok( rec->ExceptionCode == except->status ||
+        (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
+        "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
+    ok( context->Rip == (DWORD_PTR)code_mem + except->offset,
+        "%u: Unexpected eip %#lx/%#lx\n", entry,
+        context->Rip, (DWORD_PTR)code_mem + except->offset );
+    ok( rec->ExceptionAddress == (char*)context->Rip ||
+        (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Rip + 1),
+        "%u: Unexpected exception address %p/%p\n", entry,
+        rec->ExceptionAddress, (char*)context->Rip );
+
+    if (except->status == STATUS_BREAKPOINT && is_wow64)
+        parameter_count = 1;
+    else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
+        parameter_count = except->nb_params;
+    else
+        parameter_count = except->alt_nb_params;
+
+    ok( rec->NumberParameters == parameter_count,
+        "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
+
+    /* Most CPUs (except Intel Core apparently) report a segment limit violation */
+    /* instead of page faults for accesses beyond 0xffffffffffffffff */
+    if (except->nb_params == 2 && except->params[1] >= 0xfffffffffffffffd)
+    {
+        if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffffffffffff)
+            goto skip_params;
+    }
+
+    /* Seems that both 0xbee8 and 0xfffffffffffffffff can be returned in windows */
+    if (except->nb_params == 2 && rec->NumberParameters == 2
+        && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffffffffffff
+        && except->params[0] == rec->ExceptionInformation[0])
+    {
+        goto skip_params;
+    }
+
+    if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
+    {
+        for (i = 0; i < rec->NumberParameters; i++)
+            ok( rec->ExceptionInformation[i] == except->params[i],
+                "%u: Wrong parameter %d: %lx/%lx\n",
+                entry, i, rec->ExceptionInformation[i], except->params[i] );
+    }
+    else
+    {
+        for (i = 0; i < rec->NumberParameters; i++)
+            ok( rec->ExceptionInformation[i] == except->alt_params[i],
+                "%u: Wrong parameter %d: %lx/%lx\n",
+                entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
+    }
+
+skip_params:
+    /* don't handle exception if it's not the address we expected */
+    if (context->Rip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
+
+    context->Rip += except->length;
+    return ExceptionContinueExecution;
+}
+
+static void test_prot_fault(void)
+{
+    unsigned int i;
+
+    for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
+    {
+        got_exception = 0;
+        run_exception_test(handler, &exceptions[i], &exceptions[i].code,
+                           sizeof(exceptions[i].code), 0);
+        ok( got_exception == (exceptions[i].status != 0),
+            "%u: bad exception count %d\n", i, got_exception );
+    }
+}
+
 static DWORD WINAPI dr7_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
                       CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
 {
@@ -2693,6 +2917,7 @@ START_TEST(exception)
     test___C_specific_handler();
     test_restore_context();
     test_unwind_rdi_rsi();
+    test_prot_fault();
 
     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
       test_dynamic_unwind();
-- 
2.7.4




More information about the wine-patches mailing list