From e7ae8dcdfac39b264d327045658d2e69317c649e Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Mon, 1 Aug 2016 14:52:32 -0700 Subject: [PATCH] ntdll: Set Rip in for longjmp in RtlRestoreContext Signed-off-by: Daniel Lehman --- dlls/ntdll/signal_x86_64.c | 1 + dlls/ntdll/tests/exception.c | 172 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 92d7b38..6d3aedb 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -3571,6 +3571,7 @@ void WINAPI RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec ) context->R13 = jmp->R13; context->R14 = jmp->R14; context->R15 = jmp->R15; + context->Rip = jmp->Rip; context->u.s.Xmm6 = jmp->Xmm6; context->u.s.Xmm7 = jmp->Xmm7; context->u.s.Xmm8 = jmp->Xmm8; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index f1bfee6..0040fdb 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -79,11 +79,46 @@ typedef struct ULONG ScopeIndex; } DISPATCHER_CONTEXT; +typedef struct _SETJMP_FLOAT128 +{ + unsigned __int64 DECLSPEC_ALIGN(16) Part[2]; +} SETJMP_FLOAT128; + +typedef struct _JUMP_BUFFER +{ + unsigned __int64 Frame; + unsigned __int64 Rbx; + unsigned __int64 Rsp; + unsigned __int64 Rbp; + unsigned __int64 Rsi; + unsigned __int64 Rdi; + unsigned __int64 R12; + unsigned __int64 R13; + unsigned __int64 R14; + unsigned __int64 R15; + unsigned __int64 Rip; + unsigned __int64 Spare; + SETJMP_FLOAT128 Xmm6; + SETJMP_FLOAT128 Xmm7; + SETJMP_FLOAT128 Xmm8; + SETJMP_FLOAT128 Xmm9; + SETJMP_FLOAT128 Xmm10; + SETJMP_FLOAT128 Xmm11; + SETJMP_FLOAT128 Xmm12; + SETJMP_FLOAT128 Xmm13; + SETJMP_FLOAT128 Xmm14; + SETJMP_FLOAT128 Xmm15; +} _JUMP_BUFFER; + static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*); static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*); +static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); +static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*); +static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*); +static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #endif #ifdef __i386__ @@ -1658,6 +1693,131 @@ static void test_virtual_unwind(void) call_virtual_unwind( i, &tests[i] ); } +static int consolidate_dummy_called; +static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec) +{ + CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1]; + consolidate_dummy_called = 1; + ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip); + return (PVOID)rec->ExceptionInformation[2]; +} + +static void test_restore_context(void) +{ + SETJMP_FLOAT128 *fltsave; + EXCEPTION_RECORD rec; + _JUMP_BUFFER buf; + CONTEXT ctx; + int i, pass; + + if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp) + { + skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n"); + return; + } + + /* RtlRestoreContext(NULL, NULL); crashes on Windows */ + + /* test simple case of capture and restore context */ + pass = 0; + InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */ + pRtlCaptureContext(&ctx); + if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */ + { + pRtlRestoreContext(&ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass < 4, "unexpected pass %d\n", pass); + + /* test with jmp using RltRestoreContext */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + p_setjmp(&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + + /* uses buf.Rip instead of ctx.Rip */ + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 4) + { + ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx); + ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp); + ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp); + ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi); + ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi); + ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12); + ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13); + ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14); + ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15); + + fltsave = &buf.Xmm6; + for (i = 0; i < 10; i++) + { + ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low, + "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, + fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low); + + ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High, + "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, + fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High); + } + } + else + ok(0, "unexpected pass %d\n", pass); + + /* test with jmp through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + p_setjmp(&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + + /* uses buf.Rip instead of bogus 0xdeadbeef */ + pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 4, "unexpected pass %d\n", pass); + + + /* test with consolidate */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 3; + rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD64)&ctx; + rec.ExceptionInformation[2] = ctx.Rip; + ctx.Rip = 0xdeadbeef; + + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %d\n", pass); +} + static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context ) { static const int code_offset = 1024; @@ -2235,6 +2395,9 @@ static void test_vectored_continue_handler(void) START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); +#if defined(__x86_64__) + HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll"); +#endif code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!code_mem) { @@ -2349,6 +2512,14 @@ START_TEST(exception) "RtlLookupFunctionEntry" ); p__C_specific_handler = (void *)GetProcAddress( hntdll, "__C_specific_handler" ); + pRtlCaptureContext = (void *)GetProcAddress( hntdll, + "RtlCaptureContext" ); + pRtlRestoreContext = (void *)GetProcAddress( hntdll, + "RtlRestoreContext" ); + pRtlUnwindEx = (void *)GetProcAddress( hntdll, + "RtlUnwindEx" ); + p_setjmp = (void *)GetProcAddress( hmsvcrt, + "_setjmp" ); test_debug_registers(); test_outputdebugstring(1, FALSE); @@ -2358,6 +2529,7 @@ START_TEST(exception) test_vectored_continue_handler(); test_virtual_unwind(); test___C_specific_handler(); + test_restore_context(); if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind(); -- 1.9.5