[PATCH 0/1] MR11684: ntdll: Fix CONTEXT_ARM64_X18 handling when setting another thread's context.
Don't treat x18 as an integer register in the server context to avoid unintended modifications. It is common for ARM64EC processes to have x18 zeroed out when converting an x86_64 context to ARM64. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11684
From: Jacek Caban <jacek@codeweavers.com> Don't treat x18 as an integer register in the server context to avoid unintended modifications. It is common for ARM64EC processes to have x18 zeroed out when converting an x86_64 context to ARM64. --- dlls/ntdll/tests/exception.c | 90 +++++++++++++++++++++++++++++++++- dlls/ntdll/unix/signal_arm64.c | 15 ++++-- dlls/ntdll/unix/thread.c | 25 +++++++--- server/protocol.def | 23 +++++---- server/thread.c | 1 + server/trace.c | 16 +++++- 6 files changed, 148 insertions(+), 22 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 4bb471b9be3..8d61aabff38 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -7788,9 +7788,15 @@ static void test_restore_context(void) #elif defined(__aarch64__) +static DWORD WINAPI dummy_thread( void *dummy ) +{ + return 0; +} + static void test_thread_context(void) { - CONTEXT context; + CONTEXT context, orig_context; + HANDLE thread; NTSTATUS status; struct expected { @@ -7800,6 +7806,8 @@ static void test_thread_context(void) } expect; NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = code_mem; + static const ULONG64 fill = 0xccccccccccccccccllu; + static const DWORD call_func[] = { 0xa9bf7bfd, /* stp x29, x30, [sp, #-16]! */ @@ -7950,6 +7958,86 @@ static void test_thread_context(void) (char *)context.Pc <= (char *)pNtGetContextThread + 32, "wrong Pc %p/%p\n", (void *)context.Pc, pNtGetContextThread ); #undef COMPARE + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_FULL; + status = pNtGetContextThread( GetCurrentThread(), &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[18] == fill, "unexpected x18 = %Ix\n", context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_X18; + status = pNtGetContextThread( GetCurrentThread(), &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[18] == (DWORD_PTR)NtCurrentTeb(), "unexpected x18 = %Ix\n", context.X[18] ); + + thread = CreateThread( NULL, 0, dummy_thread, NULL, CREATE_SUSPENDED, NULL ); + ok( thread != INVALID_HANDLE_VALUE, "CreateThread failed with %ld\n", GetLastError() ); + + memset( &orig_context, 0xcc, sizeof(orig_context) ); + orig_context.ContextFlags = CONTEXT_ARM64_ALL; + status = pNtGetContextThread( thread, &orig_context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( orig_context.X[0] && orig_context.X[0] != fill, "unexpected x0 = %Ix\n", orig_context.X[0] ); + ok( orig_context.X[18] && orig_context.X[18] != fill, "unexpected x18 = %Ix\n", orig_context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_FULL; + status = pNtGetContextThread( thread, &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[0] == orig_context.X[0], "unexpected x0 = %Ix\n", context.X[0] ); + ok( context.X[18] == fill, "unexpected x18 = %Ix\n", context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_X18; + status = pNtGetContextThread( thread, &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[0] == fill, "unexpected x0 = %Ix\n", context.X[0] ); + ok( context.X[18] == orig_context.X[18], "unexpected x18 = %Ix\n", context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_X18; + context.X[18] = 1; + status = pNtSetContextThread( thread, &context ); + ok( !status, "NtSetContextThread failed %08lx\n", status ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_ALL; + status = pNtGetContextThread( thread, &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[0] == orig_context.X[0], "unexpected x0 = %Ix\n", context.X[0] ); + ok( context.X[18] == 1, "unexpected x18 = %Ix\n", context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_FULL; + status = pNtSetContextThread( thread, &context ); + ok( !status, "NtSetContextThread failed %08lx\n", status ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_ALL; + status = pNtGetContextThread( thread, &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[0] == fill, "unexpected x0 = %Ix\n", context.X[0] ); + ok( context.X[18] == 1, "unexpected x18 = %Ix\n", context.X[18] ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_ALL; + status = pNtSetContextThread( thread, &context ); + ok( !status, "NtSetContextThread failed %08lx\n", status ); + + memset( &context, 0xcc, sizeof(context) ); + context.ContextFlags = CONTEXT_ARM64_ALL; + status = pNtGetContextThread( thread, &context ); + ok( !status, "NtGetContextThread failed %08lx\n", status ); + ok( context.X[0] == fill, "unexpected x0 = %Ix\n", context.X[0] ); + ok( context.X[18] == fill, "unexpected x18 = %Ix\n", context.X[18] ); + + status = pNtSetContextThread( thread, &orig_context ); + ok( !status, "NtSetContextThread failed %08lx\n", status ); + + ResumeThread( thread ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); } static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 522ade368f0..325014d9f9a 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -311,7 +311,7 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext ) { DWORD i; - context->ContextFlags = CONTEXT_FULL; + context->ContextFlags = CONTEXT_FULL | CONTEXT_ARM64_X18; context->Fp = FP_sig(sigcontext); /* Frame pointer */ context->Lr = LR_sig(sigcontext); /* Link register */ context->Sp = SP_sig(sigcontext); /* Stack pointer */ @@ -469,7 +469,9 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if (needed_flags & CONTEXT_INTEGER) { - memcpy( context->X, frame->x, sizeof(context->X[0]) * 29 ); + memcpy( context->X, frame->x, sizeof(context->X[0]) * 18 ); + /* skip x18 */ + memcpy( context->X + 19, frame->x + 19, sizeof(context->X[0]) * 10 ); context->ContextFlags |= CONTEXT_INTEGER; } if (needed_flags & CONTEXT_CONTROL) @@ -488,6 +490,11 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) memcpy( context->V, frame->v, sizeof(context->V) ); context->ContextFlags |= CONTEXT_FLOATING_POINT; } + if (needed_flags & CONTEXT_ARM64_X18) + { + context->X[18] = frame->x[18]; + context->ContextFlags |= CONTEXT_ARM64_X18; + } 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; @@ -1389,7 +1396,7 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *_sigcontext ) } else if (is_inside_syscall( data, SP_sig(sigcontext) )) { - context.ContextFlags = CONTEXT_FULL | CONTEXT_EXCEPTION_REQUEST; + context.ContextFlags = CONTEXT_FULL | CONTEXT_ARM64_X18 | CONTEXT_EXCEPTION_REQUEST; NtGetContextThread( GetCurrentThread(), &context ); wait_suspend( &context ); NtSetContextThread( GetCurrentThread(), &context ); @@ -1572,7 +1579,7 @@ void init_syscall_frame( LPTHREAD_START_ROUTINE entry, void *arg, TEB *teb ) ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1; *ctx = context; - ctx->ContextFlags = CONTEXT_FULL; + ctx->ContextFlags = CONTEXT_FULL | CONTEXT_ARM64_X18; signal_set_full_context( ctx ); frame->sp = (ULONG64)ctx; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 3ab7cb9c283..55e52af402a 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -201,6 +201,7 @@ static unsigned int get_server_context_flags( const void *context, USHORT machin if (flags & CONTEXT_ARM64_CONTROL) ret |= SERVER_CTX_CONTROL; if (flags & CONTEXT_ARM64_INTEGER) ret |= SERVER_CTX_INTEGER; if (flags & CONTEXT_ARM64_FLOATING_POINT) ret |= SERVER_CTX_FLOATING_POINT; + if (flags & CONTEXT_ARM64_X18) ret |= SERVER_CTX_TLS; if (flags & CONTEXT_ARM64_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS; break; } @@ -585,8 +586,8 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c if (flags & CONTEXT_ARM64_CONTROL) { to->flags |= SERVER_CTX_CONTROL; - to->integer.arm64_regs.x[29] = from->Fp; - to->integer.arm64_regs.x[30] = from->Lr; + to->integer.arm64_regs.x19[10] = from->Fp; + to->integer.arm64_regs.x19[11] = from->Lr; to->ctl.arm64_regs.sp = from->Sp; to->ctl.arm64_regs.pc = from->Pc; to->ctl.arm64_regs.pstate = from->Cpsr; @@ -594,7 +595,8 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c if (flags & CONTEXT_ARM64_INTEGER) { to->flags |= SERVER_CTX_INTEGER; - for (i = 0; i <= 28; i++) to->integer.arm64_regs.x[i] = from->X[i]; + for (i = 0; i < 18; i++) to->integer.arm64_regs.x0[i] = from->X[i]; + for (i = 19; i <= 28; i++) to->integer.arm64_regs.x19[i - 19] = from->X[i]; } if (flags & CONTEXT_ARM64_FLOATING_POINT) { @@ -607,6 +609,11 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c to->fp.arm64_regs.fpcr = from->Fpcr; to->fp.arm64_regs.fpsr = from->Fpsr; } + if (flags & CONTEXT_ARM64_X18) + { + to->flags |= SERVER_CTX_TLS; + to->tls.arm64_x18 = from->X[18]; + } if (flags & CONTEXT_ARM64_DEBUG_REGISTERS) { to->flags |= SERVER_CTX_DEBUG_REGISTERS; @@ -1006,8 +1013,8 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, if ((from->flags & SERVER_CTX_CONTROL) && (to_flags & CONTEXT_ARM64_CONTROL)) { to->ContextFlags |= CONTEXT_ARM64_CONTROL; - to->Fp = from->integer.arm64_regs.x[29]; - to->Lr = from->integer.arm64_regs.x[30]; + to->Fp = from->integer.arm64_regs.x19[10]; + to->Lr = from->integer.arm64_regs.x19[11]; to->Sp = from->ctl.arm64_regs.sp; to->Pc = from->ctl.arm64_regs.pc; to->Cpsr = from->ctl.arm64_regs.pstate; @@ -1015,7 +1022,8 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, if ((from->flags & SERVER_CTX_INTEGER) && (to_flags & CONTEXT_ARM64_INTEGER)) { to->ContextFlags |= CONTEXT_ARM64_INTEGER; - for (i = 0; i <= 28; i++) to->X[i] = from->integer.arm64_regs.x[i]; + for (i = 0; i < 18; i++) to->X[i] = from->integer.arm64_regs.x0[i]; + for (i = 19; i <= 28; i++) to->X[i] = from->integer.arm64_regs.x19[i - 19]; } if ((from->flags & SERVER_CTX_FLOATING_POINT) && (to_flags & CONTEXT_ARM64_FLOATING_POINT)) { @@ -1028,6 +1036,11 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, to->Fpcr = from->fp.arm64_regs.fpcr; to->Fpsr = from->fp.arm64_regs.fpsr; } + if (from->flags & SERVER_CTX_TLS) + { + to->ContextFlags |= CONTEXT_ARM64_X18; + to->X[18] = from->tls.arm64_x18; + } if ((from->flags & SERVER_CTX_DEBUG_REGISTERS) && (to_flags & CONTEXT_ARM64_DEBUG_REGISTERS)) { to->ContextFlags |= CONTEXT_ARM64_DEBUG_REGISTERS; diff --git a/server/protocol.def b/server/protocol.def index 118bafd9c9b..f987d19ffbd 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -155,7 +155,7 @@ struct context_data struct { unsigned __int64 rax, rbx, rcx, rdx, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15; } x86_64_regs; struct { unsigned int r[13]; } arm_regs; - struct { unsigned __int64 x[31]; } arm64_regs; + struct { unsigned __int64 x0[18], x19[12]; } arm64_regs; } integer; /* selected by SERVER_CTX_INTEGER */ union { @@ -189,16 +189,21 @@ struct context_data { struct { struct { unsigned __int64 low, high; } ymm_high[16]; } regs; } ymm; /* selected by SERVER_CTX_YMM_REGISTERS */ + union + { + unsigned __int64 arm64_x18; + } tls; /* selected by SERVER_CTX_TLS */ }; -#define SERVER_CTX_CONTROL 0x01 -#define SERVER_CTX_INTEGER 0x02 -#define SERVER_CTX_SEGMENTS 0x04 -#define SERVER_CTX_FLOATING_POINT 0x08 -#define SERVER_CTX_DEBUG_REGISTERS 0x10 -#define SERVER_CTX_EXTENDED_REGISTERS 0x20 -#define SERVER_CTX_YMM_REGISTERS 0x40 -#define SERVER_CTX_EXEC_SPACE 0x80 +#define SERVER_CTX_CONTROL 0x0001 +#define SERVER_CTX_INTEGER 0x0002 +#define SERVER_CTX_SEGMENTS 0x0004 +#define SERVER_CTX_FLOATING_POINT 0x0008 +#define SERVER_CTX_DEBUG_REGISTERS 0x0010 +#define SERVER_CTX_EXTENDED_REGISTERS 0x0020 +#define SERVER_CTX_YMM_REGISTERS 0x0040 +#define SERVER_CTX_EXEC_SPACE 0x0080 +#define SERVER_CTX_TLS 0x0100 /* structure used in sending an fd from client to server */ struct send_fd diff --git a/server/thread.c b/server/thread.c index 7308dec8ead..bdf48c4bf8f 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1602,6 +1602,7 @@ static void copy_context( struct context_data *to, const struct context_data *fr 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; + if (flags & SERVER_CTX_TLS) to->tls = from->tls; } /* gets the current impersonation token */ diff --git a/server/trace.c b/server/trace.c index 0731063f8e3..d16c7ff70a0 100644 --- a/server/trace.c +++ b/server/trace.c @@ -847,10 +847,22 @@ static void dump_varargs_context( const char *prefix, data_size_t size ) } if (ctx.flags & SERVER_CTX_INTEGER) { - for (i = 0; i < 31; i++) + for (i = 0; i < 18; i++) { fprintf( stderr, ",x%u=", i ); - dump_uint64( "", &ctx.integer.arm64_regs.x[i] ); + dump_uint64( "", &ctx.integer.arm64_regs.x0[i] ); + } + } + if (ctx.flags & SERVER_CTX_TLS) + { + dump_uint64( ",x18=", &ctx.tls.arm64_x18 ); + } + if (ctx.flags & SERVER_CTX_INTEGER) + { + for (i = 19; i < 31; i++) + { + fprintf( stderr, ",x%u=", i ); + dump_uint64( "", &ctx.integer.arm64_regs.x19[i - 19] ); } } if (ctx.flags & SERVER_CTX_DEBUG_REGISTERS) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11684
participants (2)
-
Jacek Caban -
Jacek Caban (@jacek)