Changing Eip has an effect there, so offset it just a little bit so that it still falls within code.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 51 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 22 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 7ee191469ec..0efe1898914 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -376,14 +376,16 @@ static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *Exce if(rec->ExceptionCode == EXCEPTION_BREAKPOINT) { ok(context->Eip == (DWORD)code_mem + 0xa || - broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */ + broken(context->Eip == (DWORD)code_mem + 0xb) /* win2k3 */ || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, "Eip at %x instead of %x or %x\n", context->Eip, (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb); } else { - ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n", - context->Eip, (DWORD)code_mem + 0xb); + ok(context->Eip == (DWORD)code_mem + 0xb || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %x instead of %x\n", context->Eip, (DWORD)code_mem + 0xb); }
/* test if context change is preserved from vectored handler to stack handlers */ @@ -411,14 +413,16 @@ static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR if(rec->ExceptionCode == EXCEPTION_BREAKPOINT) { ok(context->Eip == (DWORD)code_mem + 0xa || - broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */ + broken(context->Eip == (DWORD)code_mem + 0xb) /* win2k3 */ || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, "Eip at %x instead of %x or %x\n", context->Eip, (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb); } else { - ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n", - context->Eip, (DWORD)code_mem + 0xb); + ok(context->Eip == (DWORD)code_mem + 0xb || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %x instead of %x\n", context->Eip, (DWORD)code_mem + 0xb); }
if(have_vectored_api) @@ -1045,8 +1049,8 @@ static void test_debugger(void) ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n", ctx.Eip, (char *)code_mem_address + 0xb); /* setting the context from debugger does not affect the context that the - * exception handler gets */ - ctx.Eip = 0x12345; + * exception handler gets, except on w2008 */ + ctx.Eip = (UINT_PTR)code_mem_address + 0xd; ctx.Eax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -1058,7 +1062,7 @@ static void test_debugger(void) /* debugger gets first chance exception with unmodified ctx.Eip */ ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n", ctx.Eip, (char *)code_mem_address + 0xb); - ctx.Eip = 0x12345; + ctx.Eip = (UINT_PTR)code_mem_address + 0xd; ctx.Eax = 0xf00f00f1; /* pass exception to debuggee * exception will not be handled and a second chance exception will be raised */ @@ -1071,7 +1075,8 @@ static void test_debugger(void) if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { ok((char *)ctx.Eip == (char *)code_mem_address + 0xa || - broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb), + broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb) || + broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */, "Eip at 0x%x instead of %p\n", ctx.Eip, (char *)code_mem_address + 0xa); /* need to fixup Eip for debuggee */ @@ -1079,8 +1084,10 @@ static void test_debugger(void) ctx.Eip += 1; } else - ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n", - ctx.Eip, (char *)code_mem_address + 0xb); + ok((char *)ctx.Eip == (char *)code_mem_address + 0xb || + broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */, + "Eip at 0x%x instead of %p\n", + ctx.Eip, (char *)code_mem_address + 0xb); /* here we handle exception */ } } @@ -2933,8 +2940,8 @@ static void test_debugger(void) ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n", (char *)ctx.Rip, (char *)code_mem_address + 0xb); /* setting the context from debugger does not affect the context that the - * exception handler gets */ - ctx.Rip = 0x12345; + * exception handler gets, except on w2008 */ + ctx.Rip = (UINT_PTR)code_mem_address + 0xd; ctx.Rax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -2946,8 +2953,8 @@ static void test_debugger(void) ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n", (char *)ctx.Rip, (char *)code_mem_address + 0xb); /* setting the context from debugger does not affect the context that the - * exception handler gets */ - ctx.Rip = 0x12345; + * exception handler gets, except on w2008 */ + ctx.Rip = (UINT_PTR)code_mem_address + 0xd; ctx.Rax = 0xf00f00f1; /* pass exception to debuggee * exception will not be handled and a second chance exception will be raised */ @@ -3732,8 +3739,8 @@ static void test_debugger(void) ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb); /* setting the context from debugger does not affect the context that the - * exception handler gets */ - ctx.Pc = 0x12345; + * exception handler gets, except on w2008 */ + ctx.Pc = (UINT_PTR)code_mem_address + 0xd; ctx.R0 = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -3745,7 +3752,7 @@ static void test_debugger(void) /* debugger gets first chance exception with unmodified ctx.Pc */ ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb); - ctx.Pc = 0x12345; + ctx.Pc = (UINT_PTR)code_mem_address + 0xd; ctx.R0 = 0xf00f00f1; /* pass exception to debuggee * exception will not be handled and a second chance exception will be raised */ @@ -4110,8 +4117,8 @@ static void test_debugger(void) ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xb); /* setting the context from debugger does not affect the context that the - * exception handler gets */ - ctx.Pc = 0x12345; + * exception handler gets, except on w2008 */ + ctx.Pc = (UINT_PTR)code_mem_address + 0xd; ctx.X0 = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -4123,7 +4130,7 @@ static void test_debugger(void) /* debugger gets first chance exception with unmodified ctx.Pc */ ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xb); - ctx.Pc = 0x12345; + ctx.Pc = (UINT_PTR)code_mem_address + 0xd; ctx.X0 = 0xf00f00f1; /* pass exception to debuggee * exception will not be handled and a second chance exception will be raised */
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 0efe1898914..f60327cebfa 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -3080,6 +3080,7 @@ static void test_thread_context(void) XMM_SAVE_AREA32 FltSave; WORD SegCs, SegDs, SegEs, SegFs, SegGs, SegSs; } expect; + XMM_SAVE_AREA32 broken_fltsave; NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = (void *)code_mem;
static const BYTE call_func[] = @@ -3240,7 +3241,12 @@ static void test_thread_context(void) COMPARE( SegFs ); COMPARE( SegGs ); COMPARE( SegSs ); - ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )), + + broken_fltsave = context.FltSave; + memset( &broken_fltsave.ErrorOpcode, 0xcc, 0x12 ); + + ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )) || + broken( !memcmp( &broken_fltsave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )) ) /* w2008, w8 */, "wrong FltSave\n" ); for (i = 6; i < 16; i++) ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 197 ++++++++++++++++++++++++++++++++--- 1 file changed, 180 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index f60327cebfa..2d767e25fa5 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -42,6 +42,7 @@ static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORE static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler); static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func); static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler); +static void (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER filter); static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*); static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code); static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); @@ -2855,6 +2856,170 @@ static void test_dpe_exceptions(void) pRtlRemoveVectoredExceptionHandler(handler); }
+static const BYTE call_one_arg_code[] = { + 0x55, /* push %rbp */ + 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */ + 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */ + 0x48, 0x89, 0xc8, /* mov %rcx,%rax */ + 0x48, 0x89, 0xd1, /* mov %rdx,%rcx */ + 0xff, 0xd0, /* callq *%rax */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */ + 0x5d, /* pop %rbp */ + 0xc3, /* retq */ +}; + +static int rtlraiseexception_unhandled_handler_called; +static int rtlraiseexception_handler_called; + +static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher, + BOOL unhandled_handler ) +{ + if (unhandled_handler) rtlraiseexception_unhandled_handler_called = 1; + else rtlraiseexception_handler_called = 1; + + trace( "exception: %08x flags:%x addr:%p context: Rip:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Rip ); + + ok( rec->ExceptionAddress == (char *)code_mem + 0x10 + || broken( rec->ExceptionAddress == code_mem || !rec->ExceptionAddress ) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x10 ); + + ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE) + || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS) + || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_XSTATE), + "wrong context flags %x\n", context->ContextFlags ); + + /* check that pc is fixed up only for EXCEPTION_BREAKPOINT + * even if raised by RtlRaiseException + */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage) + ok( context->Rip == (UINT_PTR)code_mem + 0xf, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0xf ); + else + ok( context->Rip == (UINT_PTR)code_mem + 0x10, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0x10 ); + + if (have_vectored_api) ok( context->Rax == 0xf00f00f0, "context->Rax is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->Rax ); + + /* give the debugger a chance to examine the state a second time */ + /* without the exception handler changing pc */ + if (test_stage == 2) + return; + + /* pc in context is decreased by 1 + * Increase it again, else execution will continue in the middle of an instruction */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Rip == (UINT_PTR)code_mem + 0xf)) + context->Rip++; +} + +static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE); + if (test_stage == 2) return EXCEPTION_CONTINUE_SEARCH; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + if (test_stage == 2) return ExceptionContinueSearch; + return ExceptionContinueExecution; +} + +static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + trace( "vect. handler %08x addr:%p Rip:%p\n", rec->ExceptionCode, rec->ExceptionAddress, (void *)context->Rip ); + + ok( rec->ExceptionAddress == (char *)code_mem + 0x10 + || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress ) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x10 ); + + /* check that Rip is fixed up only for EXCEPTION_BREAKPOINT + * even if raised by RtlRaiseException + */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage) + ok( context->Rip == (UINT_PTR)code_mem + 0xf, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0xf ); + else + ok( context->Rip == (UINT_PTR)code_mem + 0x10, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0x10 ); + + /* test if context change is preserved from vectored handler to stack handlers */ + context->Rax = 0xf00f00f0; + + return EXCEPTION_CONTINUE_SEARCH; +} + +static void run_rtlraiseexception_test(DWORD exceptioncode) +{ + EXCEPTION_REGISTRATION_RECORD frame; + EXCEPTION_RECORD record; + PVOID vectored_handler = NULL; + + void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem; + + record.ExceptionCode = exceptioncode; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; /* does not matter, copied return address */ + record.NumberParameters = 0; + + frame.Handler = rtlraiseexception_handler; + frame.Prev = NtCurrentTeb()->Tib.ExceptionList; + + memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code)); + + NtCurrentTeb()->Tib.ExceptionList = &frame; + if (have_vectored_api) + { + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + } + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(&rtlraiseexception_unhandled_handler); + + rtlraiseexception_handler_called = 0; + rtlraiseexception_unhandled_handler_called = 0; + func(pRtlRaiseException, &record); + ok( record.ExceptionAddress == (char *)code_mem + 0x10, + "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x10 ); + + todo_wine + ok( !rtlraiseexception_handler_called, "Frame handled called\n" ); + todo_wine + ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" ); + + if (have_vectored_api) + pRtlRemoveVectoredExceptionHandler(vectored_handler); + + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL); + NtCurrentTeb()->Tib.ExceptionList = frame.Prev; +} + +static void test_rtlraiseexception(void) +{ + if (!pRtlRaiseException) + { + skip("RtlRaiseException not found\n"); + return; + } + + /* test without debugger */ + run_rtlraiseexception_test(0x12345); + run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); + run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); +} + static void test_debugger(void) { char cmdline[MAX_PATH]; @@ -2922,7 +3087,7 @@ static void test_debugger(void) status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%x\n", status);
- trace("exception 0x%x at %p firstchance=%d Eip=%p, Eax=%p\n", + trace("exception 0x%x at %p firstchance=%d Rip=%p, Rax=%p\n", de.u.Exception.ExceptionRecord.ExceptionCode, de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, (char *)ctx.Rip, (char *)ctx.Rax); @@ -2934,14 +3099,13 @@ static void test_debugger(void) } else if (counter >= 2) /* skip startup breakpoint */ { -#if 0 /* RtlRaiseException test disabled for now */ if (stage == 1) { - ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n", - (char *)ctx.Rip, (char *)code_mem_address + 0xb); + ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0x10); /* setting the context from debugger does not affect the context that the * exception handler gets, except on w2008 */ - ctx.Rip = (UINT_PTR)code_mem_address + 0xd; + ctx.Rip = (UINT_PTR)code_mem_address + 0x12; ctx.Rax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -2950,11 +3114,11 @@ static void test_debugger(void) { if (de.u.Exception.dwFirstChance) { - ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n", - (char *)ctx.Rip, (char *)code_mem_address + 0xb); + ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0x10); /* setting the context from debugger does not affect the context that the * exception handler gets, except on w2008 */ - ctx.Rip = (UINT_PTR)code_mem_address + 0xd; + ctx.Rip = (UINT_PTR)code_mem_address + 0x12; ctx.Rax = 0xf00f00f1; /* pass exception to debuggee * exception will not be handled and a second chance exception will be raised */ @@ -2965,19 +3129,16 @@ static void test_debugger(void) /* debugger gets context after exception handler has played with it */ if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { - ok((char *)ctx.Rip == (char *)code_mem_address + 0xa, - "Rip at %p instead of %p\n", - (char *)ctx.Rip, (char *)code_mem_address + 0xa); + ok((char *)ctx.Rip == (char *)code_mem_address + 0xf, "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0xf); ctx.Rip += 1; } - else ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at 0x%x instead of %p\n", - ctx.Rip, (char *)code_mem_address + 0xb); + else ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at 0x%x instead of %p\n", + ctx.Rip, (char *)code_mem_address + 0x10); /* here we handle exception */ } } - else -#endif - if (stage == 7 || stage == 8) + else if (stage == 7 || stage == 8) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode); @@ -5110,6 +5271,7 @@ START_TEST(exception) X(RtlRemoveVectoredExceptionHandler); X(RtlAddVectoredContinueHandler); X(RtlRemoveVectoredContinueHandler); + X(RtlSetUnhandledExceptionFilter); X(NtQueryInformationProcess); X(NtQueryInformationThread); X(NtSetInformationProcess); @@ -5146,7 +5308,7 @@ START_TEST(exception) return; }
-#ifdef __i386__ +#if defined(__i386__) || defined(__x86_64__) if (pRtlRaiseException) { test_stage = 1; @@ -5221,6 +5383,7 @@ START_TEST(exception) p_setjmp = (void *)GetProcAddress( hmsvcrt, "_setjmp" );
+ test_rtlraiseexception(); test_debug_registers(); test_debug_service(1); test_virtual_unwind();
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=76971
Your paranoid android.
=== w864 (64 bit report) ===
ntdll: exception.c:3243: Test failed: wrong FltSave