-- v4: ntdll: Store exception reporting flags for debug events. ntdll: Store exception reporting flags on suspend. ntdll: Store exception reporting flags in server context. ntdll: Set exception reporting flags in NtGetContextThread(). ntdll/tests: Add tests for CONTEXT_EXCEPTION_REQUEST.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 332 ++++++++++++++++++++++++++++++++-- dlls/ntdll/tests/ntdll_test.h | 2 + dlls/ntdll/tests/wow64.c | 14 +- include/winnt.h | 5 + 4 files changed, 333 insertions(+), 20 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 771f32bb514..15bf701f7ad 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -18,22 +18,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#include <stdarg.h> +#include "ntdll_test.h" + #include <stdio.h> #include <setjmp.h>
-#include "ntstatus.h" -#define WIN32_NO_STATUS -#include "windef.h" -#include "winbase.h" -#include "winnt.h" -#include "winreg.h" #include "winuser.h" -#include "winternl.h" #include "rtlsupportapi.h" #include "ddk/wdm.h" #include "excpt.h" -#include "wine/test.h" #include "intrin.h"
static void *code_mem; @@ -174,6 +167,7 @@ enum debugger_stages static int my_argc; static char** my_argv; static BOOL is_wow64; +static BOOL old_wow64; /* Wine old-style wow64 */ static BOOL have_vectored_api; static enum debugger_stages test_stage;
@@ -236,6 +230,19 @@ static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, enum debugger_stag status = pNtSetContextThread(thread, xctx); ok(!status, "NtSetContextThread failed with 0x%lx\n", status); } + +#define check_context_exception_request( a, b ) check_context_exception_request_( a, b, __LINE__ ) +static void check_context_exception_request_( DWORD flags, BOOL hardware_exception, unsigned int line ) +{ + static const DWORD exception_reporting_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE | CONTEXT_SERVICE_ACTIVE; + DWORD expected_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; + expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; + ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + flags, expected_flags ); +} #endif
#ifdef __i386__ @@ -1103,13 +1110,16 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
- ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS; + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING + || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */, + "got %#lx.\n", ctx.ContextFlags);
- trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx\n", + trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx ctx.ContextFlags %#lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, - de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax); + de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax, ctx.ContextFlags);
if (counter > 100) { @@ -1126,6 +1136,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) (char *)ctx.Eip < (char *)ntdll + nt->OptionalHeader.SizeOfImage, "wrong eip %p ntdll %p-%p\n", (void *)ctx.Eip, ntdll, (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else { @@ -1139,6 +1150,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Eax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { @@ -1175,6 +1187,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Eip, (char *)code_mem_address + 0xb); /* here we handle exception */ } + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { @@ -1184,6 +1197,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Eip);
if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { @@ -1193,6 +1207,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 2, ctx.Eip);
if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { @@ -1203,14 +1218,17 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { + check_context_exception_request( ctx.ContextFlags, TRUE ); test_debugger_xstate(pi.hThread, &ctx, stage); } else if (stage == STAGE_SEGMENTS) @@ -1231,6 +1249,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok( !ctx.SegEs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs ); ok( !ctx.SegGs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs ); } + check_context_exception_request( ctx.ContextFlags, TRUE ); } else ok(FALSE, "unexpected stage %u\n", stage); @@ -3564,9 +3583,10 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
- ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags);
trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -3587,6 +3607,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) (char *)ctx.Rip < (char *)hntdll + nt->OptionalHeader.SizeOfImage, "wrong rip %p ntdll %p-%p\n", (void *)ctx.Rip, hntdll, (char *)hntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else { @@ -3605,6 +3626,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Rax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { @@ -3643,6 +3665,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.Rip, (char *)code_mem_address + 0x0c); /* here we handle exception */ } + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { @@ -3651,6 +3674,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok((char *)ctx.Rip == (char *)code_mem_address + 0x30, "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip); if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { @@ -3659,6 +3683,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok((char *)ctx.Rip == (char *)code_mem_address + 2, "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip); if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { @@ -3669,14 +3694,17 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); } else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { + check_context_exception_request( ctx.ContextFlags, TRUE ); test_debugger_xstate(pi.hThread, &ctx, stage); } else if (stage == STAGE_SEGMENTS) @@ -3705,6 +3733,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); todo_wine ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + check_context_exception_request( ctx.ContextFlags, TRUE ); } else ok(FALSE, "unexpected stage %u\n", stage); @@ -4316,9 +4345,21 @@ static void test_wow64_context(void) ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); ok( ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags ); + + ctx.ContextFlags = WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST; + ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + todo_wine ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, + "got context flags %#lx\n", ctx.ContextFlags ); + if (context.SegCs == cs32) { trace( "in 32-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); ok( ctx.Eip == context.Rip, "cs32: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip ); ok( ctx.Ebp == context.Rbp, "cs32: ebp %08lx / %p\n", ctx.Ebp, (void *)context.Rbp ); ok( ctx.Esp == context.Rsp, "cs32: esp %08lx / %p\n", ctx.Esp, (void *)context.Rsp ); @@ -4395,6 +4436,12 @@ static void test_wow64_context(void) else { trace( "in 64-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); ok( ctx.Eip != context.Rip, "cs64: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip); ok( ctx.SegCs == cs32, "cs64: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 ); if (!is_arm64ec) @@ -10941,6 +10988,254 @@ static void test_backtrace(void) module, debugstr_w(name), count - 1, buffer[count - 1] ); }
+struct context_exception_request_thread_param +{ + LONG volatile sync; + HANDLE event; +}; +static volatile int *p_context_exception_request_value; +struct context_exception_request_thread_param *context_exception_request_param; + +static LONG CALLBACK test_context_exception_request_handler( EXCEPTION_POINTERS *info ) +{ + PEXCEPTION_RECORD rec = info->ExceptionRecord; + CONTEXT *c = info->ContextRecord; + DWORD old_prot; + + ok( !(c->ContextFlags & (CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE)), "got %#lx.\n", c->ContextFlags ); + + ok( rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, "got %#lx.\n", rec->ExceptionCode ); + VirtualProtect( (void *)p_context_exception_request_value, sizeof(*p_context_exception_request_value), + PAGE_READWRITE, &old_prot ); + + WriteRelease( &context_exception_request_param->sync, 5 ); + while (ReadAcquire( &context_exception_request_param->sync ) != 6) + ; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD WINAPI test_context_exception_request_thread( void *arg ) +{ +#ifndef _WIN64 + static BYTE wait_sync_x64_code[] = + { + 0x89, 0x11, /* mov %edx,(%rcx) */ + 0x83, 0xc2, 0x01, /* add $0x1,%edx */ + 0x0f, 0x1f, 0x00, /* 1: nopl (%rax) */ + 0x8b, 0x01, /* mov (%rcx),%eax */ + 0x39, 0xd0, /* cmp %edx,%eax */ + 0x75, 0xfa, /* jne 1b */ + 0xc3, /* ret */ + }; + ULONG64 args[2]; +#endif + struct context_exception_request_thread_param *p = arg; + void *vectored_handler; + + context_exception_request_param = p; + vectored_handler = pRtlAddVectoredExceptionHandler( TRUE, test_context_exception_request_handler ); + ok( !!vectored_handler, "failed.\n" ); + + WriteRelease( &p->sync, 1 ); + while (ReadAcquire( &p->sync ) != 2) + ; + + WaitForSingleObject( p->event, INFINITE ); + +#ifndef _WIN64 + memcpy( (char *)code_mem + 1024, wait_sync_x64_code, sizeof(wait_sync_x64_code) ); + args[0] = (ULONG_PTR)&p->sync; + args[1] = 3; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + p_context_exception_request_value = VirtualAlloc( NULL, sizeof(*p_context_exception_request_value), + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( !!p_context_exception_request_value, "got NULL.\n" ); + *p_context_exception_request_value = 1; + ok( *p_context_exception_request_value == 1, "got %d.\n", *p_context_exception_request_value ); + VirtualFree( (void *)p_context_exception_request_value, 0, MEM_RELEASE ); + pRtlRemoveVectoredExceptionHandler( vectored_handler ); + +#ifndef _WIN64 + args[1] = 7; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + return 0; +} + +static void test_context_exception_request(void) +{ + struct context_exception_request_thread_param p; + DWORD expected_flags; + HANDLE thread; + CONTEXT c; + BOOL ret; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found.\n" ); + return; + } + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, + "got %#lx.\n", c.ContextFlags ); + if (c.ContextFlags == 0x10001) + { + win_skip( "Old WoW64 behaviour, skipping tests.\n" ); + return; + } + + ret = DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread, 0, TRUE, DUPLICATE_SAME_ACCESS ); + ok( ret, "got error %lu.\n", GetLastError() ); + c.ContextFlags = expected_flags | CONTEXT_EXCEPTION_REQUEST; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + CloseHandle( thread ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + p.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL ); + ok( !!thread, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); + + p.sync = 0; + ResumeThread(thread); + + while (ReadAcquire( &p.sync ) != 1) + SwitchToThread(); + /* thread is in user code. */ + SuspendThread( thread ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + ResumeThread(thread); + WriteRelease( &p.sync, 2 ); + /* Try to make sure the thread entered WaitForSingleObject(). */ + Sleep(30); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + SetEvent( p.event ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 3) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 4 ); + } + + while (ReadAcquire( &p.sync ) != 5) + SwitchToThread(); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + WriteRelease( &p.sync, 6 ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 7) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 8 ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( p.event ); +} + START_TEST(exception) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -11021,6 +11316,16 @@ START_TEST(exception) #define X(f) p##f = (void*)GetProcAddress(hkernel32, #f) X(IsWow64Process); if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + if (is_wow64) + { + TEB64 *teb64 = ULongToPtr( NtCurrentTeb()->GdiBatchCount ); + + if (teb64) + { + PEB64 *peb64 = ULongToPtr(teb64->Peb); + old_wow64 = !peb64->LdrData; + } + }
X(InitializeContext); X(InitializeContext2); @@ -11218,5 +11523,6 @@ START_TEST(exception) test_suspend_process(); test_unload_trace(); test_backtrace(); + test_context_exception_request(); VirtualFree(code_mem, 0, MEM_RELEASE); } diff --git a/dlls/ntdll/tests/ntdll_test.h b/dlls/ntdll/tests/ntdll_test.h index 67278358b29..ed824583870 100644 --- a/dlls/ntdll/tests/ntdll_test.h +++ b/dlls/ntdll/tests/ntdll_test.h @@ -30,3 +30,5 @@
#include "wine/test.h" #include "wine/heap.h" + +extern NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem ); diff --git a/dlls/ntdll/tests/wow64.c b/dlls/ntdll/tests/wow64.c index e0dd7bab13b..28d7ec2406a 100644 --- a/dlls/ntdll/tests/wow64.c +++ b/dlls/ntdll/tests/wow64.c @@ -1569,7 +1569,7 @@ static const BYTE call_func64_code[] = 0xcb, /* lret */ };
-static NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args ) +NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem ) { NTSTATUS (WINAPI *func)( ULONG64 func64, int nb_args, ULONG64 *args ) = code_mem;
@@ -2070,7 +2070,7 @@ static void test_iosb(void) iosb64.Information = 0xdeadbeef;
args[0] = (LONG_PTR)server; - status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_PENDING, "NtFsControlFile returned %lx\n", status ); ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); ok( iosb64.Pointer == PtrToUlong(&iosb32), "status changed to %lx\n", iosb64.Status ); @@ -2095,7 +2095,7 @@ static void test_iosb(void) args[8] = (ULONG_PTR)&id; args[9] = sizeof(id);
- status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_PENDING || status == STATUS_SUCCESS, "NtFsControlFile returned %lx\n", status ); todo_wine { @@ -2125,7 +2125,7 @@ static void test_iosb(void) id = 0xdeadbeef;
args[0] = (LONG_PTR)server; - status = call_func64( func, ARRAY_SIZE(args), args ); + status = call_func64( func, ARRAY_SIZE(args), args, code_mem ); ok( status == STATUS_SUCCESS, "NtFsControlFile returned %lx\n", status ); ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); @@ -2148,7 +2148,7 @@ static NTSTATUS invoke_syscall( const char *name, ULONG args32[] ) else win_skip( "syscall thunk %s not recognized\n", name );
- return call_func64( func, ARRAY_SIZE(args64), args64 ); + return call_func64( func, ARRAY_SIZE(args64), args64, code_mem ); }
static void test_syscalls(void) @@ -2254,14 +2254,14 @@ static void test_cpu_area(void) ULONG64 context, context_ex; ULONG64 args[] = { (ULONG_PTR)&machine, (ULONG_PTR)&context, (ULONG_PTR)&context_ex };
- status = call_func64( ptr, ARRAY_SIZE(args), args ); + status = call_func64( ptr, ARRAY_SIZE(args), args, code_mem ); ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); ok( machine == IMAGE_FILE_MACHINE_I386, "wrong machine %x\n", machine ); ok( context == teb64->TlsSlots[WOW64_TLS_CPURESERVED] + 4, "wrong context %s / %s\n", wine_dbgstr_longlong(context), wine_dbgstr_longlong(teb64->TlsSlots[WOW64_TLS_CPURESERVED]) ); ok( !context_ex, "got context_ex %s\n", wine_dbgstr_longlong(context_ex) ); args[0] = args[1] = args[2] = 0; - status = call_func64( ptr, ARRAY_SIZE(args), args ); + status = call_func64( ptr, ARRAY_SIZE(args), args, code_mem ); ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); } else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" ); diff --git a/include/winnt.h b/include/winnt.h index e7c322fd127..1bf38616b87 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1536,6 +1536,11 @@ typedef struct _CONTEXT_EX #endif } CONTEXT_EX, *PCONTEXT_EX;
+#define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +#define CONTEXT_SERVICE_ACTIVE 0x10000000 +#define CONTEXT_EXCEPTION_REQUEST 0x40000000 +#define CONTEXT_EXCEPTION_REPORTING 0x80000000 + #define CONTEXT_ARM 0x0200000 #define CONTEXT_ARM_CONTROL (CONTEXT_ARM | 0x00000001) #define CONTEXT_ARM_INTEGER (CONTEXT_ARM | 0x00000002)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 8 ++++---- dlls/ntdll/unix/signal_arm.c | 1 + dlls/ntdll/unix/signal_arm64.c | 3 +++ dlls/ntdll/unix/signal_i386.c | 1 + dlls/ntdll/unix/signal_x86_64.c | 2 ++ dlls/ntdll/unix/unix_private.h | 11 +++++++++++ 6 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 15bf701f7ad..7b42bcac2df 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -11091,8 +11091,8 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( GetCurrentThread(), &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, - "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, + "got %#lx.\n", c.ContextFlags ); if (c.ContextFlags == 0x10001) { win_skip( "Old WoW64 behaviour, skipping tests.\n" ); @@ -11106,14 +11106,14 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); CloseHandle( thread );
c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( GetCurrentThread(), &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
p.event = CreateEventW( NULL, FALSE, FALSE, NULL ); thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL ); diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index d4711e27319..44e79d0abdf 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -513,6 +513,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) memcpy( context->D, frame->d, sizeof(frame->d) ); context->ContextFlags |= CONTEXT_FLOATING_POINT; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index fcebbab76cf..11340d88672 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -433,6 +433,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context->ContextFlags |= CONTEXT_FLOATING_POINT; } if (needed_flags & CONTEXT_DEBUG_REGISTERS) FIXME( "debug registers not supported\n" ); + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; }
@@ -640,6 +641,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) context->Dr7 = wow_frame->Dr7; } /* FIXME: CONTEXT_I386_XSTATE */ + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); break; }
@@ -679,6 +681,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) memcpy( context->D, wow_frame->D, sizeof(wow_frame->D) ); context->ContextFlags |= CONTEXT_FLOATING_POINT; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); break; }
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 61f42806f5d..6e2752745d3 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1153,6 +1153,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) x86_thread_data()->dr6 = context->Dr6; x86_thread_data()->dr7 = context->Dr7; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); }
if (context->ContextFlags & (CONTEXT_INTEGER & ~CONTEXT_i386)) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 4fc2727595d..41e5f9531d5 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1186,6 +1186,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) amd64_thread_data()->dr6 = context->Dr6; amd64_thread_data()->dr7 = context->Dr7; } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; }
@@ -1390,6 +1391,7 @@ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) frame->restore_flags |= CONTEXT_XSTATE; } } + set_context_exception_reporting_flags( &context->ContextFlags, CONTEXT_SERVICE_ACTIVE ); return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index b249570d421..ec11f9d4795 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -215,6 +215,17 @@ extern int server_pipe( int fd[2] ); extern void fpux_to_fpu( I386_FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux ); extern void fpu_to_fpux( XSAVE_FORMAT *fpux, const I386_FLOATING_SAVE_AREA *fpu );
+static inline void set_context_exception_reporting_flags( DWORD *context_flags, DWORD reporting_flag ) +{ + if (!(*context_flags & CONTEXT_EXCEPTION_REQUEST)) + { + *context_flags &= ~(CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE); + return; + } + *context_flags = (*context_flags & ~(CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE)) + | CONTEXT_EXCEPTION_REPORTING | reporting_flag; +} + extern BOOL xstate_compaction_enabled; extern UINT64 xstate_supported_features_mask; extern UINT64 xstate_features_size;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 18 ++++++------- dlls/ntdll/unix/server.c | 4 +++ dlls/ntdll/unix/thread.c | 50 +++++++++++++++++++++++++++++++++--- server/protocol.def | 13 ++++++++++ server/thread.c | 5 ++-- server/trace.c | 13 ++++++++++ tools/make_requests | 2 +- 7 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 7b42bcac2df..ead28f7a22e 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -240,7 +240,7 @@ static void check_context_exception_request_( DWORD flags, BOOL hardware_excepti
if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; - ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + todo_wine ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", flags, expected_flags ); } #endif @@ -1113,7 +1113,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); - todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */, "got %#lx.\n", ctx.ContextFlags);
@@ -3586,7 +3586,7 @@ static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); ok(!status, "NtGetContextThread failed with 0x%lx\n", status); - todo_wine ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags); + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags);
trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -4349,7 +4349,7 @@ static void test_wow64_context(void) ctx.ContextFlags = WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST; ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); - todo_wine ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, + ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, "got context flags %#lx\n", ctx.ContextFlags );
if (context.SegCs == cs32) @@ -4437,7 +4437,7 @@ static void test_wow64_context(void) { trace( "in 64-bit mode %04x\n", context.SegCs ); if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) - ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + todo_wine ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), @@ -11149,13 +11149,13 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
ResumeThread(thread); WriteRelease( &p.sync, 2 ); @@ -11205,13 +11205,13 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
WriteRelease( &p.sync, 6 );
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 438c16518bd..f3ffd99c3fc 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -751,7 +751,11 @@ unsigned int server_select( const select_op_t *select_op, data_size_t size, UINT
if (ret == STATUS_USER_APC) *user_apc = reply_data.call.user; if (reply_size > sizeof(reply_data.call)) + { memcpy( context, reply_data.context, reply_size - sizeof(reply_data.call) ); + context[0].flags &= ~SERVER_CTX_EXEC_SPACE; + context[1].flags &= ~SERVER_CTX_EXEC_SPACE; + } return ret; }
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 7ae5c022fb2..21b581ddc7c 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -166,7 +166,7 @@ void fpu_to_fpux( XMM_SAVE_AREA32 *fpux, const I386_FLOATING_SAVE_AREA *fpu ) */ static unsigned int get_server_context_flags( const void *context, USHORT machine ) { - unsigned int flags, ret = 0; + unsigned int flags = 0, ret = 0;
switch (machine) { @@ -204,6 +204,7 @@ static unsigned int get_server_context_flags( const void *context, USHORT machin if (flags & CONTEXT_ARM64_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS; break; } + if (flags & CONTEXT_EXCEPTION_REQUEST) ret |= SERVER_CTX_EXEC_SPACE; return ret; }
@@ -218,11 +219,11 @@ static unsigned int get_native_context_flags( USHORT native_machine, USHORT wow_ switch (MAKELONG( native_machine, wow_machine )) { case MAKELONG( IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386 ): - return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_YMM_REGISTERS; + return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_YMM_REGISTERS | SERVER_CTX_EXEC_SPACE; case MAKELONG( IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE_ARMNT ): - return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT; + return SERVER_CTX_DEBUG_REGISTERS | SERVER_CTX_FLOATING_POINT | SERVER_CTX_EXEC_SPACE; default: - return 0; + return SERVER_CTX_EXEC_SPACE; } }
@@ -242,6 +243,21 @@ static void xstate_to_server( context_t *to, const CONTEXT_EX *xctx ) }
+/*********************************************************************** + * exception_request_flags_to_server + * + * Copy exception reporting flags to the server format. + */ +static void exception_request_flags_to_server( context_t *to, DWORD context_flags ) +{ + if (!(context_flags & CONTEXT_EXCEPTION_REPORTING)) return; + to->flags |= SERVER_CTX_EXEC_SPACE; + if (context_flags & CONTEXT_SERVICE_ACTIVE) to->exec_space.space.space = EXEC_SPACE_SYSCALL; + else if (context_flags & CONTEXT_EXCEPTION_ACTIVE) to->exec_space.space.space = EXEC_SPACE_EXCEPTION; + else to->exec_space.space.space = EXEC_SPACE_USERMODE; +} + + /*********************************************************************** * context_to_server * @@ -319,6 +335,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_I386_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -377,6 +394,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_I386_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -438,6 +456,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_AMD64_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -503,6 +522,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void } if (flags & CONTEXT_AMD64_XSTATE) xstate_to_server( to, (const CONTEXT_EX *)(from + 1) ); + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -550,6 +570,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->debug.arm_regs.wvr[i] = from->Wvr[i]; for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->debug.arm_regs.wcr[i] = from->Wcr[i]; } + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -591,6 +612,7 @@ static NTSTATUS context_to_server( context_t *to, USHORT to_machine, const void for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->debug.arm64_regs.wcr[i] = from->Wcr[i]; for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->debug.arm64_regs.wvr[i] = from->Wvr[i]; } + exception_request_flags_to_server( to, flags ); return STATUS_SUCCESS; }
@@ -632,6 +654,20 @@ static void xstate_from_server( CONTEXT_EX *xctx, const context_t *from ) }
+/*********************************************************************** + * exception_request_flags_from_server + * + * Copy exception reporting flags from the server format. + */ +static void exception_request_flags_from_server( DWORD *context_flags, const context_t *from ) +{ + if (!(*context_flags & CONTEXT_EXCEPTION_REQUEST) || !(from->flags & SERVER_CTX_EXEC_SPACE)) return; + *context_flags = (*context_flags & ~(CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE)) | CONTEXT_EXCEPTION_REPORTING; + if (from->exec_space.space.space == EXEC_SPACE_SYSCALL) *context_flags |= CONTEXT_SERVICE_ACTIVE; + else if (from->exec_space.space.space == EXEC_SPACE_EXCEPTION) *context_flags |= CONTEXT_EXCEPTION_ACTIVE; +} + + /*********************************************************************** * context_from_server * @@ -706,6 +742,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
@@ -767,6 +804,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_I386_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
@@ -829,6 +867,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
@@ -898,6 +937,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma } if ((from->flags & SERVER_CTX_YMM_REGISTERS) && (to_flags & CONTEXT_AMD64_XSTATE)) xstate_from_server( (CONTEXT_EX *)(to + 1), from ); + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
@@ -945,6 +985,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->Wvr[i] = from->debug.arm_regs.wvr[i]; for (i = 0; i < ARM_MAX_WATCHPOINTS; i++) to->Wcr[i] = from->debug.arm_regs.wcr[i]; } + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
@@ -986,6 +1027,7 @@ static NTSTATUS context_from_server( void *dst, const context_t *from, USHORT ma for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->Wcr[i] = from->debug.arm64_regs.wcr[i]; for (i = 0; i < ARM64_MAX_WATCHPOINTS; i++) to->Wvr[i] = from->debug.arm64_regs.wvr[i]; } + exception_request_flags_from_server( &to->ContextFlags, from ); return STATUS_SUCCESS; }
diff --git a/server/protocol.def b/server/protocol.def index 55dda2d7636..69b8db3ebef 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -126,6 +126,14 @@ typedef union } unload_dll; } debug_event_t;
+ +enum context_exec_space +{ + EXEC_SPACE_USERMODE, + EXEC_SPACE_SYSCALL, + EXEC_SPACE_EXCEPTION, +}; + /* context data */ typedef struct { @@ -172,6 +180,10 @@ typedef struct unsigned char i386_regs[512]; } ext; /* selected by SERVER_CTX_EXTENDED_REGISTERS */ union + { + struct { enum context_exec_space space; int __pad; } space; + } exec_space; /* selected by SERVER_CTX_EXEC_SPACE */ + union { struct { struct { unsigned __int64 low, high; } ymm_high[16]; } regs; } ymm; /* selected by SERVER_CTX_YMM_REGISTERS */ @@ -184,6 +196,7 @@ typedef struct #define SERVER_CTX_DEBUG_REGISTERS 0x10 #define SERVER_CTX_EXTENDED_REGISTERS 0x20 #define SERVER_CTX_YMM_REGISTERS 0x40 +#define SERVER_CTX_EXEC_SPACE 0x80
/* structure used in sending an fd from client to server */ struct send_fd diff --git a/server/thread.c b/server/thread.c index 56f57cefd8f..55bd63d3030 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1303,6 +1303,7 @@ static void copy_context( context_t *to, const context_t *from, unsigned int fla if (flags & SERVER_CTX_DEBUG_REGISTERS) to->debug = from->debug; if (flags & SERVER_CTX_EXTENDED_REGISTERS) to->ext = from->ext; if (flags & SERVER_CTX_YMM_REGISTERS) to->ymm = from->ymm; + if (flags & SERVER_CTX_EXEC_SPACE) to->exec_space = from->exec_space; }
/* gets the current impersonation token */ @@ -1941,14 +1942,14 @@ DECL_HANDLER(set_thread_context) /* If context is in a pending state, we don't know if we will use WoW or native * context, so store both and discard irrevelant one in select request. */ const int is_pending = thread->context->status == STATUS_PENDING; - unsigned int native_flags = contexts[CTX_NATIVE].flags; + unsigned int native_flags = contexts[CTX_NATIVE].flags & ~SERVER_CTX_EXEC_SPACE;
if (ctx_count == 2 && (is_pending || thread->context->regs[CTX_WOW].machine)) { context_t *ctx = &thread->context->regs[CTX_WOW];
/* some regs are always set from the native context */ - flags = contexts[CTX_WOW].flags & ~req->native_flags; + flags = contexts[CTX_WOW].flags & ~(req->native_flags | SERVER_CTX_EXEC_SPACE); if (is_pending) ctx->machine = contexts[CTX_WOW].machine; else native_flags &= req->native_flags;
diff --git a/server/trace.c b/server/trace.c index efb4bb03c4a..b254a96f7a4 100644 --- a/server/trace.c +++ b/server/trace.c @@ -822,6 +822,19 @@ static void dump_varargs_context( const char *prefix, data_size_t size ) fprintf( stderr, "%s{machine=%04x", prefix, ctx.machine ); break; } + if (ctx.flags & SERVER_CTX_EXEC_SPACE) + { + const char *space; + + switch (ctx.exec_space.space.space) + { + case EXEC_SPACE_USERMODE: space = "user"; break; + case EXEC_SPACE_SYSCALL: space = "syscall"; break; + case EXEC_SPACE_EXCEPTION: space = "exception"; break; + default: space = "invalid"; break; + } + fprintf( stderr, ",exec_space=%s", space ); + } fputc( '}', stderr ); remove_data( size ); } diff --git a/tools/make_requests b/tools/make_requests index e3eaaf45b6f..419b1264ea4 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -54,7 +54,7 @@ my %formats = "hw_input_t" => [ 40, 8, "&dump_hw_input" ], # varargs-only structures "apc_call_t" => [ 64, 8 ], - "context_t" => [ 1720, 8 ], + "context_t" => [ 1728, 8 ], "cursor_pos_t" => [ 24, 8 ], "debug_event_t" => [ 160, 8 ], "message_data_t" => [ 48, 8 ],
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 10 +++++----- dlls/ntdll/unix/signal_arm.c | 9 +++++++-- dlls/ntdll/unix/signal_arm64.c | 9 +++++++-- dlls/ntdll/unix/signal_i386.c | 9 +++++++-- dlls/ntdll/unix/signal_x86_64.c | 6 ++++-- 5 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index ead28f7a22e..a46b824f543 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -4437,7 +4437,7 @@ static void test_wow64_context(void) { trace( "in 64-bit mode %04x\n", context.SegCs ); if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) - todo_wine ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), @@ -11124,8 +11124,8 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL - | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags );
p.sync = 0; ResumeThread(thread); @@ -11167,7 +11167,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
c.ContextFlags = CONTEXT_CONTROL; ret = SetThreadContext( thread, &c ); @@ -11177,7 +11177,7 @@ static void test_context_exception_request(void) | CONTEXT_EXCEPTION_ACTIVE; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
SetEvent( p.event );
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 44e79d0abdf..9c67309a4c7 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -1021,7 +1021,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
if (is_inside_syscall( sigcontext )) { - context.ContextFlags = CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); wait_suspend( &context ); NtSetContextThread( GetCurrentThread(), &context ); @@ -1029,6 +1029,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) else { save_context( &context, sigcontext ); + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context ); restore_context( &context, sigcontext ); } @@ -1135,7 +1136,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB if (context.Pc & 1) context.Cpsr |= 0x20; /* thumb mode */ if ((ctx = get_cpu_area( IMAGE_FILE_MACHINE_ARMNT ))) *ctx = context;
- if (suspend) wait_suspend( &context ); + if (suspend) + { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + wait_suspend( &context ); + }
ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1; *ctx = context; diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 11340d88672..afb5964758e 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -1225,7 +1225,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
if (is_inside_syscall( sigcontext )) { - context.ContextFlags = CONTEXT_FULL; + context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); wait_suspend( &context ); NtSetContextThread( GetCurrentThread(), &context ); @@ -1233,6 +1233,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) else { save_context( &context, sigcontext ); + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context ); restore_context( &context, sigcontext ); } @@ -1418,7 +1419,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB if (arm_context->Pc & 1) arm_context->Cpsr |= 0x20; /* thumb mode */ }
- if (suspend) wait_suspend( &context ); + if (suspend) + { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + wait_suspend( &context ); + }
ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1; *ctx = context; diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 6e2752745d3..9588d480a42 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2130,7 +2130,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ERR_(seh)( "kernel stack overflow.\n" ); return; } - context->c.ContextFlags = CONTEXT_FULL; + context->c.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { @@ -2153,6 +2153,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) struct xcontext context;
save_context( &context, ucontext ); + context.c.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; wait_suspend( &context.c ); restore_context( &context, ucontext ); } @@ -2514,7 +2515,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB ((XSAVE_FORMAT *)context.ExtendedRegisters)->MxCsr = 0x1f80; if ((ctx = get_cpu_area( IMAGE_FILE_MACHINE_I386 ))) *ctx = context;
- if (suspend) wait_suspend( &context ); + if (suspend) + { + context.ContextFlags |= CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + wait_suspend( &context ); + }
ctx = (CONTEXT *)((ULONG_PTR)context.Esp & ~3) - 1; *ctx = context; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 41e5f9531d5..d439848a113 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2172,7 +2172,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ERR_(seh)( "kernel stack overflow.\n" ); return; } - context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + context->c.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context->c ); if (xstate_extended_features()) { @@ -2195,6 +2195,8 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext ) struct xcontext context;
save_context( &context, ucontext ); + context.c.ContextFlags |= CONTEXT_EXCEPTION_REPORTING; + if (is_wow64() && context.c.SegCs == cs64_sel) context.c.ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; wait_suspend( &context.c ); restore_context( &context, ucontext ); } @@ -2536,7 +2538,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB # error Please define setting %gs for your architecture #endif
- context.ContextFlags = CONTEXT_ALL; + context.ContextFlags = CONTEXT_ALL | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; context.Rcx = (ULONG_PTR)entry; context.Rdx = (ULONG_PTR)arg; context.Rsp = (ULONG_PTR)teb->Tib.StackBase - 0x28;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 6 +++--- dlls/ntdll/unix/signal_arm.c | 2 +- dlls/ntdll/unix/signal_arm64.c | 2 +- dlls/ntdll/unix/signal_i386.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 2 +- dlls/ntdll/unix/thread.c | 6 ++++-- dlls/ntdll/unix/unix_private.h | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index a46b824f543..19c9408c755 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -240,7 +240,7 @@ static void check_context_exception_request_( DWORD flags, BOOL hardware_excepti
if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; - todo_wine ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", flags, expected_flags ); } #endif @@ -11192,7 +11192,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags );
WriteRelease( &p.sync, 4 ); } @@ -11226,7 +11226,7 @@ static void test_context_exception_request(void) c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; ret = GetThreadContext( thread, &c ); ok( ret, "got error %lu.\n", GetLastError() ); - todo_wine ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags );
WriteRelease( &p.sync, 8 ); } diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index 9c67309a4c7..1a49fc8f957 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -545,7 +545,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec void *stack_ptr = (void *)(SP_sig(sigcontext) & ~7); NTSTATUS status;
- status = send_debug_event( rec, context, TRUE ); + status = send_debug_event( rec, context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( context, sigcontext ); diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index afb5964758e..58911d9b1f2 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -699,7 +699,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec void *stack_ptr = (void *)(SP_sig(sigcontext) & ~15); NTSTATUS status;
- status = send_debug_event( rec, context, TRUE ); + status = send_debug_event( rec, context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( context, sigcontext ); diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 9588d480a42..6457f0221bd 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1450,7 +1450,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr, struct exc_stack_layout *stack; size_t stack_size; unsigned int xstate_size; - NTSTATUS status = send_debug_event( rec, context, TRUE ); + NTSTATUS status = send_debug_event( rec, context, TRUE, TRUE );
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index d439848a113..a163d5d0b33 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1426,7 +1426,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec context->EFlags &= ~0x100; /* clear single-step flag */ }
- status = send_debug_event( rec, context, TRUE ); + status = send_debug_event( rec, context, TRUE, TRUE ); if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) { restore_context( xcontext, sigcontext ); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 21b581ddc7c..6922ad2cba6 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1506,7 +1506,7 @@ void wait_suspend( CONTEXT *context ) * * Send an EXCEPTION_DEBUG_EVENT event to the debugger. */ -NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) +NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance, BOOL exception ) { unsigned int ret; DWORD i; @@ -1543,6 +1543,8 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c select_op.wait.handles[0] = handle;
contexts_to_server( server_contexts, context ); + server_contexts[0].flags |= SERVER_CTX_EXEC_SPACE; + server_contexts[0].exec_space.space.space = exception ? EXEC_SPACE_EXCEPTION : EXEC_SPACE_SYSCALL; server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, TIMEOUT_INFINITE, server_contexts, NULL );
@@ -1565,7 +1567,7 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_c */ NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) { - NTSTATUS status = send_debug_event( rec, context, first_chance ); + NTSTATUS status = send_debug_event( rec, context, first_chance, !(is_win64 || is_wow64() || is_old_wow64()) );
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) return NtContinue( context, FALSE ); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index ec11f9d4795..b278ab8df84 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -244,7 +244,7 @@ extern void DECLSPEC_NORETURN abort_thread( int status ); extern void DECLSPEC_NORETURN abort_process( int status ); extern void DECLSPEC_NORETURN exit_process( int status ); extern void wait_suspend( CONTEXT *context ); -extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ); +extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance, BOOL exception ); extern NTSTATUS set_thread_context( HANDLE handle, const void *context, BOOL *self, USHORT machine ); extern NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT machine ); extern unsigned int alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret,
On Mon Apr 15 19:53:36 2024 +0000, Paul Gofman wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/5480/diffs?diff_id=109686&start_sha=77ae981d561e447737ba582fd33d4818f5d10493#1104a825a741495a2a3c3093b4d85d95e4f8180f_751_743)
So I've got some details about the failures I am getting here. The test looks like: ``` push %gs xor %eax,%eax mov %ax,%gs mov %gs:(0),%ax; <- here is the expected exception happens pop %gs; ret */ ``` On the current kernel 6.8.5 that I have (and probably for the long time ago at least; that's allo the same with nofsgsbase kernel command line) %gs selector is always 0. However, setting it %0 resets gsbase (that can be traced in signal handler). Then, while delivering exception to wow64 32 bit handler, fault happens from wow64cpu.BTCpuResetToConsistentState() in BTCpuSetContext() which ends up trying to make a syscall. Which faults in the first instruction in wine_syscall_dispatcher ("movq %gs:0x328,%rcx\n\t") because gsbase is now 0, which triggers new exception setup and that repeats until stack overflow seen in default test output. Restoring gsbase with arch_prctl( ARCH_SET_GS, NtCurrentTeb() ); in setup_raise_exception fixes this specific test execution. Although "pop %gs" resets gsbase to 0 again and faults again in the first wow64 syscall which follows and triggers winedbg.
The following debug diff fixes all the problematic tests without disabling them: ``` diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 4fc2727595d..b3a4b3a72a0 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1932,11 +1932,21 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext ) * * Handler for SIGSEGV and related errors. */ -static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { 0 }; struct xcontext context; ucontext_t *ucontext = init_handler( sigcontext ); + void *gsbase, *teb; + + arch_prctl( ARCH_GET_GS, &gsbase ); + teb = NtCurrentTeb(); + if (teb != gsbase) + { + arch_prctl( ARCH_SET_GS, teb ); + ERR_(seh)("reset gsbase, teb %p.\n", teb); + return; + }
rec.ExceptionAddress = (void *)RIP_sig(ucontext); save_context( &context, ucontext ); ```
I also made a test program (targeted for 64 bit) tormenting %gs selector which runs without crashes on Windows and under Wine with the diff above: ``` #include <stdio.h> #include <windows.h>
void *g[256]; int line[256]; int count;
#define RECORD_GSBASE() {__asm__ volatile("rdgsbase %%rax\n\t mov %%rax,%0" : "=m"(gsbase)); g[count] = gsbase; line[count++] = __LINE__;}
NTSTATUS WINAPI NtYieldExecution(void); NTSTATUS WINAPI NtClose(HANDLE); NTSTATUS WINAPI NtDelayExecution(BOOLEAN alertable, const LARGE_INTEGER *timeout);
int main(int argc, char *argv[]) { LARGE_INTEGER timeout; void *gsbase; int i; void *save_gsbase; unsigned int gs, gs1; NTSTATUS status; void *teb;
__asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs)); printf("gs %#x.\n", gs);
RECORD_GSBASE() save_gsbase = gsbase; __asm__ volatile("mov $0x7eeffeedcafe,%rax\n\twrgsbase %rax");
RECORD_GSBASE() __asm__ volatile("mov %%gs:0x30,%%rax\n\tmov %%rax,%0" : "=m"(teb)); RECORD_GSBASE()
__asm__ volatile("xor %eax,%eax\n\tmov %ax,%gs"); RECORD_GSBASE()
//printf("\n"); NtYieldExecution(); RECORD_GSBASE()
__asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs1));
timeout.QuadPart = -1000000; status = NtDelayExecution(0, &timeout); RECORD_GSBASE()
__asm__ volatile("mov %0,%%rax\n\twrgsbase %%rax" :: "m"(save_gsbase)); printf("teb %p.\n", teb);
for (i = 0; i < count; ++i) printf("gsbase %p at line %d.\n", g[i], line[i]);
__asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs)); printf("gs1 %#x.\n", gs1); printf("gs %#x.\n", gs);
return 0; } ```
On Windows %gs value is not 0, but otherwise it seems like it behaves somewhat between the lines of my debug diff to Wine above. %In particular, mind the test "__asm__ volatile("mov %%gs:0x30,%%rax\n\tmov %%rax,%0" : "=m"(teb));" where it successfully gets the teb after setting gsbase to 0x7eeffeedcafe, and gsbase is back to original value after that. So it looks like Windows handles such faults transparently and restores gs base back. Setting %gs value clears gsbase like on Linux. NtYieldExecution() doesn't restore gsbase, and so doesn't NtDelayExecution with a smaller timeout, so I am guessing on Windows the restoration is more likely related to context switching and fault handling than syscalls themselves.
Now, I am not quite sure how the tests under discussion can succeed anywhere on Linux. I tried disabling rd/wrgsbase with nofsgsbase kernel command line option but this makes no difference. I tried looking at kernel code and history a little bit but didn't immediately spot anything which could be responsible for the difference. Is that success under some VM maybe? I can imagine the supervisor doing something with those selector changes / gsbase. Which kernel version that succeed under?
In the up to date kernel and not under VM I am so far failing to see how that can work, gsbase is cleared and there is seemingly nothing to restore it.
UPDATE I am not sure if that possible, but also maybe resetting gsbase on selector reset to the same value can have some differences between CPUs? Here I tried on one Intel and one AMD cpu reproducing the test failures on both.
On Tue Apr 16 02:20:42 2024 +0000, Paul Gofman wrote:
So I've got some details about the failures I am getting here. The test looks like:
push %gs xor %eax,%eax mov %ax,%gs mov %gs:(0),%ax; <- here is the expected exception happens pop %gs; ret */
On the current kernel 6.8.5 that I have (and probably for the long time ago at least; that's allo the same with nofsgsbase kernel command line) %gs selector is always 0. However, setting it %0 resets gsbase (that can be traced in signal handler). Then, while delivering exception to wow64 32 bit handler, fault happens from wow64cpu.BTCpuResetToConsistentState() in BTCpuSetContext() which ends up trying to make a syscall. Which faults in the first instruction in wine_syscall_dispatcher ("movq %gs:0x328,%rcx\n\t") because gsbase is now 0, which triggers new exception setup and that repeats until stack overflow seen in default test output. Restoring gsbase with arch_prctl( ARCH_SET_GS, NtCurrentTeb() ); in setup_raise_exception fixes this specific test execution. Although "pop %gs" resets gsbase to 0 again and faults again in the first wow64 syscall which follows and triggers winedbg. The following debug diff fixes all the problematic tests without disabling them:
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 4fc2727595d..b3a4b3a72a0 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1932,11 +1932,21 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext ) * * Handler for SIGSEGV and related errors. */ -static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) +void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) { EXCEPTION_RECORD rec = { 0 }; struct xcontext context; ucontext_t *ucontext = init_handler( sigcontext ); + void *gsbase, *teb; + + arch_prctl( ARCH_GET_GS, &gsbase ); + teb = NtCurrentTeb(); + if (teb != gsbase) + { + arch_prctl( ARCH_SET_GS, teb ); + ERR_(seh)("reset gsbase, teb %p.\n", teb); + return; + } rec.ExceptionAddress = (void *)RIP_sig(ucontext); save_context( &context, ucontext );
I also made a test program (targeted for 64 bit) tormenting %gs selector which runs without crashes on Windows and under Wine with the diff above:
#include <stdio.h> #include <windows.h> void *g[256]; int line[256]; int count; #define RECORD_GSBASE() {__asm__ volatile("rdgsbase %%rax\n\t mov %%rax,%0" : "=m"(gsbase)); g[count] = gsbase; line[count++] = __LINE__;} NTSTATUS WINAPI NtYieldExecution(void); NTSTATUS WINAPI NtClose(HANDLE); NTSTATUS WINAPI NtDelayExecution(BOOLEAN alertable, const LARGE_INTEGER *timeout); int main(int argc, char *argv[]) { LARGE_INTEGER timeout; void *gsbase; int i; void *save_gsbase; unsigned int gs, gs1; NTSTATUS status; void *teb; __asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs)); printf("gs %#x.\n", gs); RECORD_GSBASE() save_gsbase = gsbase; __asm__ volatile("mov $0x7eeffeedcafe,%rax\n\twrgsbase %rax"); RECORD_GSBASE() __asm__ volatile("mov %%gs:0x30,%%rax\n\tmov %%rax,%0" : "=m"(teb)); RECORD_GSBASE() __asm__ volatile("xor %eax,%eax\n\tmov %ax,%gs"); RECORD_GSBASE() //printf("\n"); NtYieldExecution(); RECORD_GSBASE() __asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs1)); timeout.QuadPart = -1000000; status = NtDelayExecution(0, &timeout); RECORD_GSBASE() __asm__ volatile("mov %0,%%rax\n\twrgsbase %%rax" :: "m"(save_gsbase)); printf("teb %p.\n", teb); for (i = 0; i < count; ++i) printf("gsbase %p at line %d.\n", g[i], line[i]); __asm__ volatile("mov %%gs,%%eax\n\tmov %%eax, %0" : "=m"(gs)); printf("gs1 %#x.\n", gs1); printf("gs %#x.\n", gs); return 0; }
On Windows %gs value is not 0, but otherwise it seems like it behaves somewhat between the lines of my debug diff to Wine above. %In particular, mind the test "__asm__ volatile("mov %%gs:0x30,%%rax\n\tmov %%rax,%0" : "=m"(teb));" where it successfully gets the teb after setting gsbase to 0x7eeffeedcafe, and gsbase is back to original value after that. So it looks like Windows handles such faults transparently and restores gs base back. Setting %gs value clears gsbase like on Linux. NtYieldExecution() doesn't restore gsbase, and so doesn't NtDelayExecution with a smaller timeout, so I am guessing on Windows the restoration is more likely related to context switching and fault handling than syscalls themselves. Now, I am not quite sure how the tests under discussion can succeed anywhere on Linux. I tried disabling rd/wrgsbase with nofsgsbase kernel command line option but this makes no difference. I tried looking at kernel code and history a little bit but didn't immediately spot anything which could be responsible for the difference. Is that success under some VM maybe? I can imagine the supervisor doing something with those selector changes / gsbase. Which kernel version that succeed under? In the up to date kernel and not under VM I am so far failing to see how that can work, gsbase is cleared and there is seemingly nothing to restore it. UPDATE I am not sure if that possible, but also maybe resetting gsbase on selector reset to the same value can have some differences between CPUs? Here I tried on one Intel and one AMD cpu reproducing the test failures on both.
Here is the output from the above test program on Windows 10: ``` gs 0x2b. teb 00000000003f9000. gsbase 00000000003f9000 at line 27. gsbase 00007eeffeedcafe at line 31. gsbase 00000000003f9000 at line 33. gsbase 0000000000000000 at line 36. gsbase 0000000000000000 at line 40. gsbase 00000000003f9000 at line 47. gs1 0. gs 0x2b. ```
UPDATE I am not sure if that possible, but also maybe resetting gsbase on selector reset to the same value can have some differences between CPUs? Here I tried on one Intel and one AMD cpu reproducing the test failures on both.
Is it the exact same failure? Because there's indeed a difference, AMD doesn't reset gsbase on zero selector load.
On Tue Apr 16 06:50:45 2024 +0000, Alexandre Julliard wrote:
UPDATE I am not sure if that possible, but also maybe resetting gsbase
on selector reset to the same value can have some differences between CPUs? Here I tried on one Intel and one AMD cpu reproducing the test failures on both. Is it the exact same failure? Because there's indeed a difference, AMD doesn't reset gsbase on zero selector load.
Yes, looks exactly the same (AMD Ryzen 9 5900HX). A Linux test program similar to the above conirms that setting %gs to 0 clears previously set gsbase to NULL. The same is on "AMD Custom APU 0405" (Steam Deck).
Maybe we can go with restoring %gs in the handler similar to my debug patch (using get_current_teb instead of NtCurrentTeb)? Or, alternatively, just comment out that test (also in debugger tests)? FWIW it seems to me this test doesn't show what it wanted to show. I am guessing the idea under the test was that setting %gs to 0 will cause invalid gsbase and thus gs:0 access will crash. But as it is clear now on modern Windows it is just transparently restored. The test has access violation because that gsbase on 32 bit is invalid from start, regardless of what it is doing with gs selector. The test still gets the expected exception even with my change restoring %gs (when it returns to the same instruction and now faults "properly").