[v4 4/4] ntdll: Fix and test exception codes on x86-64.
Andrew Wesie
awesie at gmail.com
Sun Feb 12 16:41:23 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 2370cf4..ff7ef43 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -2650,6 +2650,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;
@@ -2713,7 +2716,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 */
@@ -2755,6 +2757,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 15973ce..6534c28 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 );
+ }
+}
+
#endif /* __x86_64__ */
#if defined(__i386__) || defined(__x86_64__)
@@ -2695,6 +2919,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