ntdll/tests: Add more tests for Wow64 contexts.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-06-22 17:08:11 +02:00
parent 03fe2b36cd
commit 9ae2b046ef
1 changed files with 247 additions and 6 deletions

View File

@ -4168,22 +4168,31 @@ static void test_thread_context(void)
static void test_wow64_context(void)
{
const char appname[] = "C:\\windows\\syswow64\\cmd.exe";
char cmdline[256];
THREAD_BASIC_INFORMATION info;
PROCESS_INFORMATION pi;
STARTUPINFOA si = {0};
WOW64_CONTEXT ctx, *ctx_ptr;
WOW64_CONTEXT ctx, *ctx_ptr = NULL;
CONTEXT context;
NTSTATUS ret;
TEB teb;
SIZE_T res, cpu_size;
TEB32 teb32;
SIZE_T res, cpu_size = 0;
WOW64_CPURESERVED *cpu = NULL;
WOW64_CPU_AREA_INFO cpu_info;
BOOL r, got32, got64;
unsigned int i, cs32, cs64;
ULONG_PTR ecx, rcx;
memset(&ctx, 0x55, sizeof(ctx));
ctx.ContextFlags = WOW64_CONTEXT_ALL;
ret = pRtlWow64GetThreadContext( GetCurrentThread(), &ctx );
ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_PARTIAL_COPY), "got %#x\n", ret);
CreateProcessA("C:\\windows\\syswow64\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
sprintf( cmdline, "\"%s\" /c for /l %%n in () do @echo >nul", appname );
r = CreateProcessA( appname, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
ok( r, "failed to start %s err %u\n", appname, GetLastError() );
ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
@ -4206,9 +4215,9 @@ static void test_wow64_context(void)
if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0;
ok( res == sizeof(teb), "wrong len %lx\n", res );
memset( &teb32, 0, sizeof(teb32) );
if (teb.WowTebOffset > 1)
{
TEB32 teb32;
if (!ReadProcessMemory( pi.hProcess, (char *)info.TebBaseAddress + teb.WowTebOffset,
&teb32, sizeof(teb32), &res )) res = 0;
ok( res == sizeof(teb32), "wrong len %lx\n", res );
@ -4243,16 +4252,247 @@ static void test_wow64_context(void)
ok(ctx_ptr->Esi == ctx.Esi, "got esi %08x / %08x\n", ctx_ptr->Esi, ctx.Esi);
ok(ctx_ptr->Edi == ctx.Edi, "got edi %08x / %08x\n", ctx_ptr->Edi, ctx.Edi);
ok(ctx_ptr->SegCs == ctx.SegCs, "got cs %04x / %04x\n", ctx_ptr->SegCs, ctx.SegCs);
ok(ctx_ptr->SegDs == ctx.SegDs, "got cs %04x / %04x\n", ctx_ptr->SegDs, ctx.SegDs);
ok(ctx_ptr->SegSs == ctx.SegSs, "got cs %04x / %04x\n", ctx_ptr->SegSs, ctx.SegSs);
ok(ctx_ptr->SegDs == ctx.SegDs, "got ds %04x / %04x\n", ctx_ptr->SegDs, ctx.SegDs);
ok(ctx_ptr->SegEs == ctx.SegEs, "got es %04x / %04x\n", ctx_ptr->SegEs, ctx.SegEs);
ok(ctx_ptr->SegFs == ctx.SegFs, "got fs %04x / %04x\n", ctx_ptr->SegFs, ctx.SegFs);
ok(ctx_ptr->SegGs == ctx.SegGs, "got gs %04x / %04x\n", ctx_ptr->SegGs, ctx.SegGs);
ok(ctx_ptr->SegSs == ctx.SegSs, "got ss %04x / %04x\n", ctx_ptr->SegSs, ctx.SegSs);
ok(ctx_ptr->EFlags == ctx.EFlags, "got eflags %08x / %08x\n", ctx_ptr->EFlags, ctx.EFlags);
ok((WORD)ctx_ptr->FloatSave.ControlWord == ctx.FloatSave.ControlWord,
"got control word %08x / %08x\n", ctx_ptr->FloatSave.ControlWord, ctx.FloatSave.ControlWord);
ok(*(WORD *)ctx_ptr->ExtendedRegisters == *(WORD *)ctx.ExtendedRegisters,
"got SSE control word %04x\n", *(WORD *)ctx_ptr->ExtendedRegisters,
*(WORD *)ctx.ExtendedRegisters);
ecx = ctx.Ecx;
ctx.Ecx = 0x12345678;
ret = pRtlWow64SetThreadContext( pi.hThread, &ctx );
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res )) res = 0;
ok( res == cpu_size, "wrong len %lx\n", res );
todo_wine
ok( ctx_ptr->Ecx == 0x12345678, "got ecx %08x\n", ctx_ptr->Ecx );
ctx.Ecx = ecx;
pRtlWow64SetThreadContext( pi.hThread, &ctx );
}
else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" );
memset( &context, 0x55, sizeof(context) );
context.ContextFlags = CONTEXT_ALL;
ret = pNtGetContextThread( pi.hThread, &context );
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
ok( context.ContextFlags == CONTEXT_ALL, "got context flags %#x\n", context.ContextFlags );
todo_wine
ok( !context.Rax, "rax is not zero %lx\n", context.Rax );
todo_wine
ok( !context.Rbx, "rbx is not zero %lx\n", context.Rbx );
ok( !context.Rsi, "rsi is not zero %lx\n", context.Rsi );
ok( !context.Rdi, "rdi is not zero %lx\n", context.Rdi );
ok( !context.Rbp, "rbp is not zero %lx\n", context.Rbp );
ok( !context.R8, "r8 is not zero %lx\n", context.R8 );
ok( !context.R9, "r9 is not zero %lx\n", context.R9 );
ok( !context.R10, "r10 is not zero %lx\n", context.R10 );
ok( !context.R11, "r11 is not zero %lx\n", context.R11 );
ok( !context.R12, "r12 is not zero %lx\n", context.R12 );
ok( !context.R13, "r13 is not zero %lx\n", context.R13 );
ok( !context.R14, "r14 is not zero %lx\n", context.R14 );
ok( !context.R15, "r15 is not zero %lx\n", context.R15 );
todo_wine
ok( ((ULONG_PTR)context.Rsp & ~0xfff) == ((ULONG_PTR)teb.Tib.StackBase & ~0xfff),
"rsp is not at top of stack %p / %p\n", (void *)context.Rsp, teb.Tib.StackBase );
todo_wine
ok( context.EFlags == 0x200, "wrong flags %08x\n", context.EFlags );
todo_wine
ok( context.MxCsr == 0x1f80, "wrong mxcsr %08x\n", context.MxCsr );
todo_wine
ok( context.FltSave.ControlWord == 0x27f, "wrong control %08x\n", context.FltSave.ControlWord );
todo_wine
ok( context.SegCs != ctx.SegCs, "wrong cs %04x\n", context.SegCs );
ok( context.SegDs == ctx.SegDs, "wrong ds %04x / %04x\n", context.SegDs, ctx.SegDs );
ok( context.SegEs == ctx.SegEs, "wrong es %04x / %04x\n", context.SegEs, ctx.SegEs );
ok( context.SegFs == ctx.SegFs, "wrong fs %04x / %04x\n", context.SegFs, ctx.SegFs );
ok( context.SegGs == ctx.SegGs, "wrong gs %04x / %04x\n", context.SegGs, ctx.SegGs );
ok( context.SegSs == ctx.SegSs, "wrong ss %04x / %04x\n", context.SegSs, ctx.SegSs );
cs32 = ctx.SegCs;
cs64 = context.SegCs;
if (cs32 == cs64)
{
todo_wine win_skip( "no wow64 support\n" );
goto done;
}
for (i = 0, got32 = got64 = FALSE; i < 10000 && !(got32 && got64); i++)
{
ResumeThread( pi.hThread );
Sleep( 1 );
SuspendThread( pi.hThread );
memset( &context, 0x55, sizeof(context) );
context.ContextFlags = CONTEXT_ALL;
ret = pNtGetContextThread( pi.hThread, &context );
ok( ret == STATUS_SUCCESS, "got %#x\n", ret );
if (ret) break;
if (context.SegCs == cs32 && got32) continue;
if (context.SegCs == cs64 && got64) continue;
if (context.SegCs != cs32 && context.SegCs != cs64)
{
ok( 0, "unexpected cs %04x\n", context.SegCs );
break;
}
memset( &ctx, 0x55, sizeof(ctx) );
ctx.ContextFlags = WOW64_CONTEXT_ALL;
ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
ok( ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#x\n", ctx.ContextFlags );
if (context.SegCs == cs32)
{
trace( "in 32-bit mode %04x\n", context.SegCs );
ok( ctx.Eip == context.Rip, "cs32: eip %08x / %p\n", ctx.Eip, (void *)context.Rip );
ok( ctx.Ebp == context.Rbp, "cs32: ebp %08x / %p\n", ctx.Ebp, (void *)context.Rbp );
ok( ctx.Esp == context.Rsp, "cs32: esp %08x / %p\n", ctx.Esp, (void *)context.Rsp );
ok( ctx.Eax == context.Rax, "cs32: eax %08x / %p\n", ctx.Eax, (void *)context.Rax );
ok( ctx.Ebx == context.Rbx, "cs32: ebx %08x / %p\n", ctx.Ebx, (void *)context.Rbx );
ok( ctx.Ecx == context.Rcx, "cs32: ecx %08x / %p\n", ctx.Ecx, (void *)context.Rcx );
ok( ctx.Edx == context.Rdx, "cs32: edx %08x / %p\n", ctx.Edx, (void *)context.Rdx );
ok( ctx.Esi == context.Rsi, "cs32: esi %08x / %p\n", ctx.Esi, (void *)context.Rsi );
ok( ctx.Edi == context.Rdi, "cs32: edi %08x / %p\n", ctx.Edi, (void *)context.Rdi );
ok( ctx.SegCs == cs32, "cs32: wrong cs %04x / %04x\n", ctx.SegCs, cs32 );
ok( ctx.SegDs == context.SegDs, "cs32: wrong ds %04x / %04x\n", ctx.SegDs, context.SegDs );
ok( ctx.SegEs == context.SegEs, "cs32: wrong es %04x / %04x\n", ctx.SegEs, context.SegEs );
ok( ctx.SegFs == context.SegFs, "cs32: wrong fs %04x / %04x\n", ctx.SegFs, context.SegFs );
ok( ctx.SegGs == context.SegGs, "cs32: wrong gs %04x / %04x\n", ctx.SegGs, context.SegGs );
ok( ctx.SegSs == context.SegSs, "cs32: wrong ss %04x / %04x\n", ctx.SegSs, context.SegSs );
if (teb32.DeallocationStack)
ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase,
"cs32: esp not inside 32-bit stack %08x / %08x-%08x\n", ctx.Esp,
teb32.DeallocationStack, teb32.Tib.StackBase );
/* r12 points to the TEB */
ok( (void *)context.R12 == info.TebBaseAddress,
"cs32: r12 not pointing to the TEB %p / %p\n", (void *)context.R12, info.TebBaseAddress );
/* r13 points inside the cpu area */
ok( (void *)context.R13 >= teb.TlsSlots[WOW64_TLS_CPURESERVED] &&
context.R13 <= ((ULONG_PTR)teb.TlsSlots[WOW64_TLS_CPURESERVED] | 0xfff),
"cs32: r13 not pointing into cpu area %p / %p\n", (void *)context.R13,
teb.TlsSlots[WOW64_TLS_CPURESERVED] );
/* r14 stores the 64-bit stack pointer */
ok( (void *)context.R14 >= teb.DeallocationStack && (void *)context.R14 <= teb.Tib.StackBase,
"cs32: r14 not inside 32-bit stack %p / %p-%p\n", (void *)context.R14,
(void *)teb.DeallocationStack, (void *)teb.Tib.StackBase );
if (pRtlWow64GetCpuAreaInfo)
{
/* in 32-bit mode, the 32-bit context is the current cpu context, not the stored one */
if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED],
cpu, cpu_size, &res )) res = 0;
ok( res == cpu_size, "wrong len %lx\n", res );
ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL,
"cs32: got context flags %#x\n", ctx_ptr->ContextFlags);
ok(ctx_ptr->Eip != ctx.Eip, "cs32: got eip %08x / %08x\n", ctx_ptr->Eip, ctx.Eip);
/* changing either context changes the actual cpu context */
rcx = context.Rcx;
ecx = ctx_ptr->Ecx;
context.Rcx = 0xfedcba987654321ull;
pNtSetContextThread( pi.hThread, &context );
memset( &ctx, 0x55, sizeof(ctx) );
ctx.ContextFlags = WOW64_CONTEXT_ALL;
pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok( ctx.Ecx == 0x87654321, "cs64: ecx set to %08x\n", ctx.Ecx );
ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res );
ok( ctx_ptr->Ecx == ecx, "cs64: ecx set to %08x\n", ctx_ptr->Ecx );
ctx.Ecx = 0x33334444;
pRtlWow64SetThreadContext( pi.hThread, &ctx );
memset( &ctx, 0x55, sizeof(ctx) );
ctx.ContextFlags = WOW64_CONTEXT_ALL;
pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok( ctx.Ecx == 0x33334444, "cs64: ecx set to %08x\n", ctx.Ecx );
ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res );
ok( ctx_ptr->Ecx == ecx, "cs64: ecx set to %08x\n", ctx_ptr->Ecx );
memset( &context, 0x55, sizeof(context) );
context.ContextFlags = CONTEXT_ALL;
pNtGetContextThread( pi.hThread, &context );
ok( context.Rcx == 0x33334444, "cs64: rcx set to %p\n", (void *)context.Rcx );
/* restore everything */
context.Rcx = rcx;
pNtSetContextThread( pi.hThread, &context );
}
got32 = TRUE;
}
else
{
trace( "in 64-bit mode %04x\n", context.SegCs );
ok( ctx.Eip != context.Rip, "cs64: eip %08x / %p\n", ctx.Eip, (void *)context.Rip);
ok( ctx.SegCs == cs32, "cs64: wrong cs %04x / %04x\n", ctx.SegCs, cs32 );
ok( ctx.SegDs == context.SegDs, "cs64: wrong ds %04x / %04x\n", ctx.SegDs, context.SegDs );
ok( ctx.SegEs == context.SegEs, "cs64: wrong es %04x / %04x\n", ctx.SegEs, context.SegEs );
ok( ctx.SegFs == context.SegFs, "cs64: wrong fs %04x / %04x\n", ctx.SegFs, context.SegFs );
ok( ctx.SegGs == context.SegGs, "cs64: wrong gs %04x / %04x\n", ctx.SegGs, context.SegGs );
ok( ctx.SegSs == context.SegSs, "cs64: wrong ss %04x / %04x\n", ctx.SegSs, context.SegSs );
if (teb32.DeallocationStack)
ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase,
"cs64: esp not inside 32-bit stack %08x / %08x-%08x\n", ctx.Esp,
teb32.DeallocationStack, teb32.Tib.StackBase );
ok( (void *)context.Rsp >= teb.DeallocationStack && (void *)context.Rsp <= teb.Tib.StackBase,
"cs64: rsp not inside 64-bit stack %p / %p-%p\n", (void *)context.Rsp,
teb.DeallocationStack, teb.Tib.StackBase );
if (pRtlWow64GetCpuAreaInfo)
{
/* in 64-bit mode, the 32-bit context is stored in the cpu area */
if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED],
cpu, cpu_size, &res )) res = 0;
ok( res == cpu_size, "wrong len %lx\n", res );
ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL,
"cs64: got context flags %#x\n", ctx_ptr->ContextFlags);
ok(ctx_ptr->Eip == ctx.Eip, "cs64: got eip %08x / %08x\n", ctx_ptr->Eip, ctx.Eip);
ok(ctx_ptr->Eax == ctx.Eax, "cs64: got eax %08x / %08x\n", ctx_ptr->Eax, ctx.Eax);
ok(ctx_ptr->Ebx == ctx.Ebx, "cs64: got ebx %08x / %08x\n", ctx_ptr->Ebx, ctx.Ebx);
ok(ctx_ptr->Ecx == ctx.Ecx, "cs64: got ecx %08x / %08x\n", ctx_ptr->Ecx, ctx.Ecx);
ok(ctx_ptr->Edx == ctx.Edx, "cs64: got edx %08x / %08x\n", ctx_ptr->Edx, ctx.Edx);
ok(ctx_ptr->Ebp == ctx.Ebp, "cs64: got ebp %08x / %08x\n", ctx_ptr->Ebp, ctx.Ebp);
ok(ctx_ptr->Esi == ctx.Esi, "cs64: got esi %08x / %08x\n", ctx_ptr->Esi, ctx.Esi);
ok(ctx_ptr->Edi == ctx.Edi, "cs64: got edi %08x / %08x\n", ctx_ptr->Edi, ctx.Edi);
ok(ctx_ptr->EFlags == ctx.EFlags, "cs64: got eflags %08x / %08x\n", ctx_ptr->EFlags, ctx.EFlags);
/* changing one context doesn't change the other one */
rcx = context.Rcx;
ecx = ctx.Ecx;
context.Rcx = 0xfedcba987654321ull;
pNtSetContextThread( pi.hThread, &context );
memset( &ctx, 0x55, sizeof(ctx) );
ctx.ContextFlags = WOW64_CONTEXT_ALL;
pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok( ctx.Ecx == ecx, "cs64: ecx set to %08x\n", ctx.Ecx );
ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res );
ok( ctx_ptr->Ecx == ecx, "cs64: ecx set to %08x\n", ctx_ptr->Ecx );
ctx.Ecx = 0x22223333;
pRtlWow64SetThreadContext( pi.hThread, &ctx );
memset( &ctx, 0x55, sizeof(ctx) );
ctx.ContextFlags = WOW64_CONTEXT_ALL;
pRtlWow64GetThreadContext( pi.hThread, &ctx );
ok( ctx.Ecx == 0x22223333, "cs64: ecx set to %08x\n", ctx.Ecx );
ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res );
ok( ctx_ptr->Ecx == 0x22223333, "cs64: ecx set to %08x\n", ctx_ptr->Ecx );
memset( &context, 0x55, sizeof(context) );
context.ContextFlags = CONTEXT_ALL;
pNtGetContextThread( pi.hThread, &context );
ok( context.Rcx == 0xfedcba987654321ull, "cs64: rcx set to %p\n", (void *)context.Rcx );
/* restore everything */
context.Rcx = rcx;
pNtSetContextThread( pi.hThread, &context );
ctx.Ecx = ecx;
pRtlWow64SetThreadContext( pi.hThread, &ctx );
}
got64 = TRUE;
}
}
if (!got32) skip( "failed to test 32-bit context\n" );
if (!got64) skip( "failed to test 64-bit context\n" );
done:
pNtTerminateProcess(pi.hProcess, 0);
free( cpu );
}
static BYTE saved_KiUserExceptionDispatcher_bytes[12];
@ -9004,6 +9244,7 @@ START_TEST(exception)
X(RtlUnwindEx);
X(RtlWow64GetThreadContext);
X(RtlWow64SetThreadContext);
X(RtlWow64GetCpuAreaInfo);
#undef X
p_setjmp = (void *)GetProcAddress( hmsvcrt, "_setjmp" );