https://bugs.winehq.org/show_bug.cgi?id=46022
Bug ID: 46022 Summary: MechWarrior Online (MWO, Piranha games) crashes before login (broken Boost 1.62.0+ boost::context library corrupts TEB 'SubSystemTib') Product: Wine Version: 3.18 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: ntdll Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
reported here: https://spcr.netlify.com/app/342200
Download: https://mwomercs.com/game/download
--- snip --- $ pwd /home/focht/wine-games/wineprefix64-mwo/drive_c/Program Files (x86)/Piranha Games/MechWarrior Online/Bin64
$ file * crashrpt_lang.ini: Little-endian UTF-16 Unicode text, with CRLF line terminators CrashSender1402.exe: PE32+ executable (GUI) x86-64, for MS Windows CryRenderD3D11.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows CryRenderD3D9.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows D3DCompiler_43.dll: PE32+ executable (DLL) (console) x86-64, for MS Windows d3dx11_43.dll: PE32+ executable (DLL) (console) x86-64, for MS Windows d3dx9_43.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows fmod_event64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows fmod_event_net64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows fmodex64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows msvcp100.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows msvcr100.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows MWOClient.exe: PE32+ executable (GUI) x86-64, for MS Windows nvDXTLibrary.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows soundbackends: directory steam_api64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows ts3client_win64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows Txaa.win64.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows xinput1_3.dll: PE32+ executable (DLL) (GUI) x86-64, for MS Windows --- snip ---
Trace log (without relay), with small diagnostics added to internal function 'get_full_path_helper()' (ntdll helper):
--- snip --- $ WINEDEBUG=+seh,+loaddll,+process,+msvcrt,+ntdll,+file,+msgbox wine ./MWOClient.exe >>log.txt 2>&1 ... 006f:trace:file:RtlDosPathNameToNtPathName_U_WithStatus (L"C:\windows\system32\dinput8.dll",0x23da40,0x23da60,(nil)) 006f:trace:file:RtlGetFullPathName_U (L"C:\windows\system32\dinput8.dll" 520 0x23d6b0 0x23da60) 006f:trace:file:get_full_path_helper PEB cd=0x7bd24798 006f:trace:file:RtlDosPathNameToNtPathName_U_WithStatus (L"C:\windows\system32\dinput8.dll",0x23db40,0x23db60,(nil)) 006f:trace:file:RtlGetFullPathName_U (L"C:\windows\system32\dinput8.dll" 520 0x23d7b0 0x23db60) 006f:trace:file:get_full_path_helper PEB cd=0x7bd24798 006f:trace:ntdll:NtRemoveIoCompletion (0x594, 0x23e6f8, 0x23e6e8, 0x23e5a0, 0x23e598) 006f:trace:msvcrt:MSVCRT_strncpy_s (0x1db0f290 260 "../build_info.xml" 18446744073709551615) 006f:trace:msvcrt:MSVCRT_strncpy_s (0x1db0ed30 260 "../build_info.xml" 18446744073709551615) 006f:trace:msvcrt:pf_printf_a Format is: "%c.%c" 006f:trace:msvcrt:pf_printf_a Format is: "%c..%c" 006f:trace:msvcrt:MSVCRT__wfsopen (L"build_info.xml",L"rb") 006f:trace:msvcrt:msvcrt_get_flags L"rb" 006f:trace:msvcrt:MSVCRT__wsopen_dispatch path: (L"build_info.xml") oflags: 0x8000 shflags: 0x0040 pmode: 0x0000 fd*: 0x1db0df38 secure: 0 006f:trace:file:CreateFileW L"build_info.xml" GENERIC_READ FILE_SHARE_READ FILE_SHARE_WRITE creation 3 attributes 0x1 006f:trace:file:RtlDosPathNameToNtPathName_U_WithStatus (L"build_info.xml",0x1db0dc40,(nil),(nil)) 006f:trace:file:RtlGetFullPathName_U (L"build_info.xml" 520 0x1db0d860 (nil)) 006f:trace:file:get_full_path_helper TEB cd=0x97791a97923a0c89 006f:trace:seh:NtRaiseException code=c0000005 flags=0 addr=0x7bc71a80 ip=7bc71a80 tid=006f 006f:trace:seh:NtRaiseException info[0]=0000000000000000 006f:trace:seh:NtRaiseException info[1]=ffffffffffffffff 006f:trace:seh:NtRaiseException rax=000000007bc71a80 rbx=000000000dcdb61c rcx=000000000dcdb61c rdx=000000007bcce8c0 006f:trace:seh:NtRaiseException rsi=000000007bd2414f rdi=000000007bd24110 rbp=000000001db0d860 rsp=000000001db0d6a0 006f:trace:seh:NtRaiseException r8=0000000000000000 r9=000000001db0cf80 r10=0000000000000000 r11=0000000000000000 006f:trace:seh:NtRaiseException r12=97791a97923a0c89 r13=0000000000000000 r14=0000000000000208 r15=000000001db0ddf0 006f:trace:seh:dwarf_virtual_unwind function 7bc71a80 base 0x7bc717f0 cie 0x7bce1960 len 14 id 0 version 1 aug 'zR' code_align 1 data_align -8 retaddr %rip 006f:trace:seh:execute_cfa_instructions 7bc717f0: DW_CFA_def_cfa %rsp, 8 ... --- snip ---
Running with 'winedbg':
--- snip --- Unhandled exception: page fault on read access to 0xffffffffffffffff in 64-bit code (0x000000007bc71970). 0075:fixme:dbghelp:interpret_function_table_entry PUSH_MACHFRAME 6 0075:fixme:dbghelp:interpret_function_table_entry PUSH_MACHFRAME 6 Register dump: rip:000000007bc71970 rsp:000000001d9ab370 rbp:000000001d9ab530 eflags:00010203 ( R- -- I - - -C) rax:000000007bc71970 rbx:000000000da31e7c rcx:000000000da31e7c rdx:000000007bcce860 rsi:000000001d9ab530 rdi:000000000da31e7c r8:000000001d9ab530 r9:0000000000000000 r10:0000000000000003 r11:0000000080000000 r12:97791a97923a0c89 r13:0000000000000000 r14:0000000000000208 r15:000000001d9abac0 Stack dump: 0x000000001d9ab370: 17af47a36cb3e5ab ccba60f716e77a7c 0x000000001d9ab380: 7bd7ed87ae6ddeb1 2df8d41d9546ee7b 0x000000001d9ab390: 92220f8556529aa7 2a82c0a1d13db80e 0x000000001d9ab3a0: 4e32842048625517 7b16ee5bbc8abbe4 0x000000001d9ab3b0: b26049e40d726523 000000001d9ab530 0x000000001d9ab3c0: 000000001d9ab500 000000000da31e7c 0x000000001d9ab3d0: 0000000000000000 0000000000000208 0x000000001d9ab3e0: 000000001d9abac0 000000007bc72566 0x000000001d9ab3f0: 5436642d537e2f27 2d78c917ac98b6c1 0x000000001d9ab400: 2e1d1f0a8a008a46 f1950ce96645b9c3 0x000000001d9ab410: 5e4abfdc1f818236 a49834d0365bd1ef 0x000000001d9ab420: 4031f987418fcc3f 0000000000000000 Backtrace: =>0 0x000000007bc71970 get_full_path_helper.part+0x180() in ntdll (0x000000001d9ab530) 1 0x000000007bc72566 RtlGetFullPathName_U+0x215() in ntdll (0x000000001d9ab500) 2 0x000000007bc7286d RtlDosPathNameToNtPathName_U_WithStatus+0x1ac(dos_path="build_info.xml", ntpath=0x1d9ab910, file_part=(nil), cd=(nil)) [/home/focht/projects/wine/mainline-src/dlls/ntdll/path.c:378] in ntdll (0x000000001d9ab820) 3 0x000000007bc72c21 RtlDosPathNameToNtPathName_U+0x10(dos_path=<is not available>, ntpath=<is not available>, file_part=<is not available>, cd=<is not available>) [/home/focht/projects/wine/mainline-src/dlls/ntdll/path.c:438] in ntdll (0x000000001d9ab850) 4 0x000000007b4514c1 CreateFileW+0x520() in kernel32 (0x000000001d9aba40) 5 0x00007f2423963c76 MSVCRT__wsopen_dispatch+0x3e5(path="build_info.xml", oflags=0x8000, shflags=<is not available>, pmode=<is not available>, fd=0x1d9abc08, secure=0) [/home/focht/projects/wine/mainline-src/dlls/msvcr100/../msvcrt/file.c:2291] in msvcr100 (0x000000001d9abbc0) 6 0x00007f24239640f3 MSVCRT__wsopen+0x32(path=<is not available>, oflags=<is not available>, shflags=<is not available>) [/home/focht/projects/wine/mainline-src/dlls/msvcr100/../msvcrt/file.c:2387] in msvcr100 (0x000000001d9abc10) 7 0x00007f2423969349 MSVCRT__wfsopen+0xb8(path=<is not available>, mode="rb", share=0x40) [/home/focht/projects/wine/mainline-src/dlls/msvcr100/../msvcrt/file.c:4141] in msvcr100 (0x000000001d9abd20) 8 0x00007f2423969757 MSVCRT__wfopen+0x16(path=<is not available>, mode=<is not available>) [/home/focht/projects/wine/mainline-src/dlls/msvcr100/../msvcrt/file.c:4218] in msvcr100 (0x000000001d9abd50) 9 0x0000000037e24854 in mwoclient (+0xe24853) (0x000000001d9abef0) 10 0x0000000037e1dac2 in mwoclient (+0xe1dac1) (0x000000001d9abef0) 0x000000007bc71970 get_full_path_helper.part+0x180 in ntdll: movq 0x0000000000000008(%r12),%r8 --- snip ---
--- snip --- 006f:trace:file:get_full_path_helper PEB cd=0x7bd24798 ... 006f:trace:msvcrt:MSVCRT__wfsopen (L"build_info.xml",L"rb") ... 006f:trace:file:RtlGetFullPathName_U (L"build_info.xml" 520 0x1db0d860 (nil)) 006f:trace:file:get_full_path_helper TEB --- snip ---
The main thread's TEB gets partially corrupted in between two API calls. The crash is triggered due retrieval and access of per-thread current directory structure members which is also not quite correct (see "FIXME: hack") - but that's not the real problem here.
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ntdll/path.c#l631
--- snip --- 631 /****************************************************************** 632 * get_full_path_helper 633 * 634 * Helper for RtlGetFullPathName_U 635 * Note: name and buffer are allowed to point to the same memory spot 636 */ 637 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size) 638 { 639 ULONG reqsize = 0, mark = 0, dep = 0, deplen; 640 LPWSTR ins_str = NULL; 641 LPCWSTR ptr; 642 const UNICODE_STRING* cd; 643 WCHAR tmp[4]; 644 645 /* return error if name only consists of spaces */ 646 for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break; 647 if (!*ptr) return 0; 648 649 RtlAcquirePebLock(); 650 651 if (NtCurrentTeb()->Tib.SubSystemTib) /* FIXME: hack */ 652 cd = &((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath; 653 else 654 cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectory.DosPath; 655 656 switch (RtlDetermineDosPathNameType_U(name)) ... --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/include/winnt.h#l2442
--- snip --- 2442 typedef struct _NT_TIB 2443 { 2444 struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; 2445 PVOID StackBase; 2446 PVOID StackLimit; 2447 PVOID SubSystemTib; 2448 union { 2449 PVOID FiberData; 2450 DWORD Version; 2451 } DUMMYUNIONNAME; 2452 PVOID ArbitraryUserPointer; 2453 struct _NT_TIB *Self; 2454 } NT_TIB, *PNT_TIB; --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/include/winternl.h#l3029
--- snip --- 3029 /* The thread information for 16-bit threads */ 3030 /* NtCurrentTeb()->SubSystemTib points to this */ 3031 typedef struct 3032 { 3033 void *unknown; /* 00 unknown */ 3034 UNICODE_STRING *exe_name; /* 04 exe module name */ 3035 3036 /* the following fields do not exist under Windows */ 3037 UNICODE_STRING exe_str; /* exe name string pointed to by exe_name */ 3038 CURDIR curdir; /* current directory */ 3039 WCHAR curdir_buffer[MAX_PATH]; 3040 } WIN16_SUBSYSTEM_TIB; --- snip ---
Relevant game threads:
--- snip --- Numb ID Entry TEB RIP Name
4 C9 0000000037ED24E0 00007FFFFFE98000 00007FDB6BEE9A62 JobSystem_Worker.. 40 F1 00000000004987A0 00007FFFFFE04000 00007FDB6BEE9A62 23 DD 0000000037ED24E0 00007FFFFFE48000 00007FDB6BEE9A62 GetRenderMesh.. Main BD 0000000038B5C538 00007FFFFFEA8000 00000000384C3AE4 Main 5 CA 0000000037ED24E0 00007FFFFFE94000 00007FDB6BEE9A62 JobSystem_Worker.. ... <more> --- snip ---
TEB of main thread before corruption:
--- snip --- $ ==> 00007FFFFFEA8000 000000000023FE20 $+8 00007FFFFFEA8008 0000000000240000 ; StackBase $+10 00007FFFFFEA8010 0000000000142000 ; StackLimit $+18 00007FFFFFEA8018 0000000000000000 ; SubSystemTib $+20 00007FFFFFEA8020 0000000000000000 $+28 00007FFFFFEA8028 0000000000000000 $+30 00007FFFFFEA8030 00007FFFFFEA8000 $+38 00007FFFFFEA8038 0000000000000000 $+40 00007FFFFFEA8040 00000000000000BC $+48 00007FFFFFEA8048 00000000000000BD $+50 00007FFFFFEA8050 0000000000000000 --- snip ---
The app makes use of coroutines which are implemented using Boost's context lib (boost::context).
Interesting read: https://www.italiancpp.org/2016/11/02/coroutines-internals/
App code disassembly
--- snip --- mwoclient.sub_38BC2D20:
0000000038BC2D20 | mov rdx,qword ptr ds:[rdx+10] | ; new context -> rsp 0000000038BC2D24 | add rcx,10 | 0000000038BC2D28 | jmp <mwoclient.jump_fcontext> |
mwoclient.jump_fcontext:
0000000038BF5B30 | push rbp | ; save 0000000038BF5B31 | push rbx | 0000000038BF5B32 | push rsi | 0000000038BF5B33 | push rdi | 0000000038BF5B34 | push r15 | 0000000038BF5B36 | push r14 | 0000000038BF5B38 | push r13 | 0000000038BF5B3A | push r12 | 0000000038BF5B3C | mov r10,qword ptr gs:[30] | ; TEB 0000000038BF5B45 | mov rax,qword ptr ds:[r10+8] | ; Stack Base (bottom) 0000000038BF5B49 | push rax | 0000000038BF5B4A | mov rax,qword ptr ds:[r10+10] | ; Stack Limit (top) 0000000038BF5B4E | push rax | 0000000038BF5B4F | mov rax,qword ptr ds:[r10+1478] | ; memaddr stack 0000000038BF5B56 | push rax | 0000000038BF5B57 | mov rax,qword ptr ds:[r10+18] | ; SubSystemTib 0000000038BF5B5B | push rax | 0000000038BF5B5C | lea rsp,qword ptr ss:[rsp-A8] | 0000000038BF5B64 | test r9,r9 | 0000000038BF5B67 | je mwoclient.38BF5BB7 | 0000000038BF5B69 | stmxcsr dword ptr ss:[rsp+A0] | 0000000038BF5B71 | fnstcw word ptr ss:[rsp+A4] | 0000000038BF5B78 | movaps xmmword ptr ss:[rsp],xmm6 | 0000000038BF5B7C | movaps xmmword ptr ss:[rsp+10],xmm7 | 0000000038BF5B81 | movaps xmmword ptr ss:[rsp+20],xmm8 | 0000000038BF5B87 | movaps xmmword ptr ss:[rsp+30],xmm9 | 0000000038BF5B8D | movaps xmmword ptr ss:[rsp+40],xmm10 | 0000000038BF5B93 | movaps xmmword ptr ss:[rsp+50],xmm11 | 0000000038BF5B99 | movaps xmmword ptr ss:[rsp+60],xmm12 | 0000000038BF5B9F | movaps xmmword ptr ss:[rsp+70],xmm13 | 0000000038BF5BA5 | movaps xmmword ptr ss:[rsp+80],xmm14 | 0000000038BF5BAE | movaps xmmword ptr ss:[rsp+90],xmm15 | 0000000038BF5BB7 | xor r10,r10 | 0000000038BF5BBA | push r10 | 0000000038BF5BBC | mov qword ptr ds:[rcx],rsp | 0000000038BF5BBF | mov rsp,rdx | ; switch context data 0000000038BF5BC2 | pop r10 | 0000000038BF5BC4 | test r9,r9 | 0000000038BF5BC7 | je mwoclient.38BF5C17 | 0000000038BF5BC9 | ldmxcsr dword ptr ss:[rsp+A0] | ; restore new context 0000000038BF5BD1 | fldcw word ptr ss:[rsp+A4] | 0000000038BF5BD8 | movaps xmm6,xmmword ptr ss:[rsp] | 0000000038BF5BDC | movaps xmm7,xmmword ptr ss:[rsp+10] | 0000000038BF5BE1 | movaps xmm8,xmmword ptr ss:[rsp+20] | 0000000038BF5BE7 | movaps xmm9,xmmword ptr ss:[rsp+30] | 0000000038BF5BED | movaps xmm10,xmmword ptr ss:[rsp+40] | 0000000038BF5BF3 | movaps xmm11,xmmword ptr ss:[rsp+50] | 0000000038BF5BF9 | movaps xmm12,xmmword ptr ss:[rsp+60] | 0000000038BF5BFF | movaps xmm13,xmmword ptr ss:[rsp+70] | 0000000038BF5C05 | movaps xmm14,xmmword ptr ss:[rsp+80] | 0000000038BF5C0E | movaps xmm15,xmmword ptr ss:[rsp+90] | 0000000038BF5C17 | mov rcx,A8 | 0000000038BF5C1E | test r10,r10 | 0000000038BF5C21 | je mwoclient.38BF5C27 | 0000000038BF5C23 | add rcx,8 | 0000000038BF5C27 | lea rsp,qword ptr ss:[rsp+rcx] | 0000000038BF5C2B | mov r10,qword ptr gs:[30] | ; TEB 0000000038BF5C34 | pop rax | 0000000038BF5C35 | mov qword ptr ds:[r10+18],rax | ; new SubSystemTib 0000000038BF5C39 | pop rax | 0000000038BF5C3A | mov qword ptr ds:[r10+1478],rax | 0000000038BF5C41 | pop rax | 0000000038BF5C42 | mov qword ptr ds:[r10+10],rax | ; Stack Limit (top) 0000000038BF5C46 | pop rax | 0000000038BF5C47 | mov qword ptr ds:[r10+8],rax | ; Stack Base (bottom) 0000000038BF5C4B | pop r12 | 0000000038BF5C4D | pop r13 | 0000000038BF5C4F | pop r14 | 0000000038BF5C51 | pop r15 | 0000000038BF5C53 | pop rdi | 0000000038BF5C54 | pop rsi | 0000000038BF5C55 | pop rbx | 0000000038BF5C56 | pop rbp | 0000000038BF5C57 | pop r10 | 0000000038BF5C59 | mov rax,r8 | 0000000038BF5C5C | mov rcx,r8 | 0000000038BF5C5F | jmp r10 | ; 000000003750B790 ... mwoclient.sub_3750B790: 000000003750B790 | mov rcx,qword ptr ds:[rcx+8] | 000000003750B794 | mov rax,qword ptr ds:[rcx] | 000000003750B797 | jmp qword ptr ds:[rax+8] | ... --- snip ---
Corrupted TEB 'SubSystemTib' member after the coroutine context was switched:
--- snip --- $ ==> 00007FFFFFEA8000 000000000023FE20 $+8 00007FFFFFEA8008 0000000025A3FFF0 ; StackBase (ok) $+10 00007FFFFFEA8010 0000000025A20050 ; StackLimit (ok) $+18 00007FFFFFEA8018 0000000000000066 ; SubSystemTib (bad) $+20 00007FFFFFEA8020 0000000000000000 $+28 00007FFFFFEA8028 0000000000000000 $+30 00007FFFFFEA8030 00007FFFFFEA8000 $+38 00007FFFFFEA8038 0000000000000000 $+40 00007FFFFFEA8040 00000000000000BC --- snip ---
Matching source:
https://github.com/boostorg/context/blob/2cec475b489ca26a91a02a8cfd566646ba9...
--- snip --- jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME .endprolog
; prepare stack lea rsp, [rsp-0118h]
IFNDEF BOOST_USE_TSX ; save XMM storage movaps [rsp], xmm6 movaps [rsp+010h], xmm7 movaps [rsp+020h], xmm8 movaps [rsp+030h], xmm9 movaps [rsp+040h], xmm10 movaps [rsp+050h], xmm11 movaps [rsp+060h], xmm12 movaps [rsp+070h], xmm13 movaps [rsp+080h], xmm14 movaps [rsp+090h], xmm15 ; save MMX control- and status-word stmxcsr [rsp+0a0h] ; save x87 control-word fnstcw [rsp+0a4h] ENDIF
; load NT_TIB mov r10, gs:[030h] ; save fiber local storage mov rax, [r10+018h] mov [rsp+0b0h], rax ; save current deallocation stack mov rax, [r10+01478h] mov [rsp+0b8h], rax ; save current stack limit mov rax, [r10+010h] mov [rsp+0c0h], rax ; save current stack base mov rax, [r10+08h] mov [rsp+0c8h], rax
mov [rsp+0d0h], r12 ; save R12 mov [rsp+0d8h], r13 ; save R13 mov [rsp+0e0h], r14 ; save R14 mov [rsp+0e8h], r15 ; save R15 mov [rsp+0f0h], rdi ; save RDI mov [rsp+0f8h], rsi ; save RSI mov [rsp+0100h], rbx ; save RBX mov [rsp+0108h], rbp ; save RBP
mov [rsp+0110h], rcx ; save hidden address of transport_t
; preserve RSP (pointing to context-data) in R9 mov r9, rsp
; restore RSP (pointing to context-data) from RDX mov rsp, rdx
IFNDEF BOOST_USE_TSX ; restore XMM storage movaps xmm6, [rsp] movaps xmm7, [rsp+010h] movaps xmm8, [rsp+020h] movaps xmm9, [rsp+030h] movaps xmm10, [rsp+040h] movaps xmm11, [rsp+050h] movaps xmm12, [rsp+060h] movaps xmm13, [rsp+070h] movaps xmm14, [rsp+080h] movaps xmm15, [rsp+090h] ; restore MMX control- and status-word ldmxcsr [rsp+0a0h] ; save x87 control-word fldcw [rsp+0a4h] ENDIF
; load NT_TIB mov r10, gs:[030h] ; restore fiber local storage mov rax, [rsp+0b0h] mov [r10+018h], rax ; restore current deallocation stack mov rax, [rsp+0b8h] mov [r10+01478h], rax ; restore current stack limit mov rax, [rsp+0c0h] mov [r10+010h], rax ; restore current stack base mov rax, [rsp+0c8h] mov [r10+08h], rax
mov r12, [rsp+0d0h] ; restore R12 mov r13, [rsp+0d8h] ; restore R13 mov r14, [rsp+0e0h] ; restore R14 mov r15, [rsp+0e8h] ; restore R15 mov rdi, [rsp+0f0h] ; restore RDI mov rsi, [rsp+0f8h] ; restore RSI mov rbx, [rsp+0100h] ; restore RBX mov rbp, [rsp+0108h] ; restore RBP
mov rax, [rsp+0110h] ; restore hidden address of transport_t
; prepare stack lea rsp, [rsp+0118h]
; load return-address pop r10
; transport_t returned in RAX ; return parent fcontext_t mov [rax], r9 ; return data mov [rax+08h], r8
; transport_t as 1.arg of context-function mov rcx, rax
; indirect jump to context jmp r10 jump_fcontext ENDP ---- snip ---
Git history of boost::context lib revealed several bugfixes to these context save/restore routines. One was related to storing/restoring the wrong TEB member (offset).
<= boost-1.64.0
https://github.com/boostorg/context/commit/50ebf5bd1160623cbbf169f6a5d7ddff0...
--- quote --- x64 Windows: store/load fiber local storage from correct offset --- quote ---
https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
-> fixed with boost-1.65.0
But that was not the real problem.
Execution context setup helper:
https://github.com/boostorg/context/blob/2f320aa68b4908eeb9aedba91ec8a546be2...
-> 'make_fcontext()' shows only partial initialization.
While debugging I've noticed several garbage (uninit) values in the memory block used for setting up the coroutine/execution context which later made it into actual register context for the thread/coroutine.
I came across this commit:
https://github.com/boostorg/context/commit/20d4a5ed5f98e2f6e230b0b511b0250e1...
--- quote --- set fiber-storage to zero (Windows)
- fixes bug 12215 --- quote ---
https://svn.boost.org/trac10/ticket/12215
-> fixed with boost-1.62.0
That one actually is the problem. The "fix" reverted an earlier fixup-commit (boost-1.61.0) which zero-initialized the context on Windows platform and now only zero-init specific member.
https://github.com/boostorg/context/commit/a5c0c85df3ad0440729d909f099ffba5a...
--- quote --- nulled stack only for Windows --- quote ---
Execution context memory is taken from 'fixedsize_stack::allocate()' which uses 'std::malloc()' which by design doesn't zero init.
Allocated block for new context: 0x20000 bytes via iternal call to 'CryMalloc()' which calls 'HeapAlloc()':
--- snip --- 301705F0 6C 00 00 00 00 14 00 14 08 00 02 00 55 53 45 08 l...........USE. 30170600 60 DB 13 30 00 00 00 00 70 2F 15 30 00 00 00 00 `Û.0....p/.0.... 30170610 00 00 00 00 00 00 00 00 00 00 00 84 70 59 07 6F ............pY.o 30170620 62 6A 65 63 74 73 00 65 6E 76 69 72 6F 6E 6D 65 bjects.environme 30170630 6E 74 73 00 63 69 74 79 00 72 69 76 65 72 63 69 nts.city.riverci 30170640 74 79 5F 62 6C 6F 63 6B 73 5F 63 6F 6D 6D 65 72 ty_blocks_commer 30170650 63 69 61 6C 7A 6F 6E 65 5F 30 31 00 63 6F 6D 6D cialzone_01.comm 30170660 65 72 63 69 61 6C 7A 6F 6E 65 5F 30 31 2E 6D 74 ercialzone_01.mt 30170670 6C 00 00 00 00 14 00 14 00 00 00 08 00 00 00 00 l...............
30190430 73 74 72 69 61 6C 5F 36 66 6F 6F 74 5F 66 65 6E strial_6foot_fen 30190440 63 65 5F 64 5F 64 61 6D 01 00 00 00 00 00 00 00 ce_d_dam........ 30190450 32 2E 63 67 66 00 00 00 00 14 00 14 00 00 00 08 2.cgf........... 30190460 00 00 00 00 00 70 4E 7B 54 C7 C8 00 00 BC 6F 01 .....pN{TÇÈ..¼o. 30190470 00 61 00 00 00 00 00 00 00 00 00 00 00 00 00 FC .a.............ü 30190480 5C 8F 0B 6F 62 6A 65 63 74 73 00 65 6E 76 69 72 ..objects.envir ... 30190500 66 00 00 00 00 00 00 00 00 06 17 30 00 00 00 00 f..........0.... 30190510 00 06 17 30 00 00 00 00 A0 05 19 30 00 00 00 00 ...0.... ..0.... 30190520 6E 6D 65 6E 74 73 00 63 69 74 79 00 72 69 76 65 nments.city.rive 30190530 72 63 69 74 79 5F 66 65 6E 63 65 73 00 72 69 76 rcity_fences.riv 30190540 65 72 63 69 74 79 5F 69 6E 64 75 73 74 72 69 61 ercity_industria 30190550 6C 5F 36 66 6F 6F 74 5F 66 65 6E 63 65 5F 64 5F l_6foot_fence_d_ 30190560 90 B7 50 37 00 00 00 00 1A 5B BF 38 00 00 00 00 .·P7.....[¿8.... ... 30190590 64 82 00 00 66 00 00 00 00 00 00 00 00 00 00 00 d...f........... 301905A0 E0 84 0F 39 00 00 00 00 00 00 00 00 73 00 65 6E à..9........s.en 301905B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 301905C0 00 00 00 00 00 00 00 00 A0 FF 01 00 00 00 00 00 ........ ÿ...... 301905D0 A0 05 19 30 00 00 00 00 74 79 5F 69 6E 64 75 73 ..0....ty_indus 301905E0 74 72 69 61 6C 5F 36 66 6F 6F 74 5F 66 65 6E 63 trial_6foot_fenc 301905F0 65 5F 64 5F 64 61 6D 61 67 65 64 5F 72 65 76 65 e_d_damaged_reve 30190600 72 73 65 5F 6C 6F 64 32 79 66 06 00 46 52 45 45 rse_lod2yf..FREE 30190610 70 2F 15 30 00 00 00 00 10 03 96 00 00 00 00 00 p/.0............ ... --- snip --
0x301905A0 is passed to 'make_fcontext()' -> partial init 0x30190448 is passed to 'jump_fcontext()' -> setup new context
All XMM regs in new context get garbage (uninit) values from heap block. Same for TEB members: values via 'pop rax' from new stack (see 'jump_fcontext' assembly):
$+0 0000000030190500 0000000000000066 ; garbage new SubSystemTib (ought to be fiber local storage, see my comments earlier) $+8 0000000030190508 0000000030170600 ; restore new deallocation stack $+10 0000000030190510 0000000030170600 ; restore new stack limit $+18 0000000030190518 00000000301905A0 ; restore new stack base
Summarizing: It just works by chance on Windows and maybe sometimes on Wine, depending on heap usage.
There are multiple ways to solve this:
Game vendor (Piranha games):
1) upgrade to newer Boost libary (>= boost-1.65.0). There will be still corruption in TEB, now in 'FiberData' member but it wouldn't harm anymore for per-thread curdir ('SubSystemTib')
2) additionally modify boost::context lib to zero init everything returned by 'fixedsize_stack::allocate()'
Wine:
1) zero init all large heap allocations by default, regardless if HEAP_ZERO_MEMORY is passed or not -> out of question, not an option
2) Don't use TEB 'SubSystemTib' member for win32/64 (legacy win16 only) or wrap in exception handler = awful hack, fall back to PEB 'ProcessParameters'
I've tested a patch which removes the legacy WIN16_SUBSYSTEM_TIB access in case of win32/win64 and the client starts fine. A proper implementaton of per-thread curdir for win32/win64 wouldn't help here anyway.
$ sha1sum MWOPortalInstaller.exe 2e243479cb476e7295ce6f825e6916c3dafea535 MWOPortalInstaller.exe
$ du -sh MWOPortalInstaller.exe 68M MWOPortalInstaller.exe
$ wine --version wine-3.18-114-g417e94f199
Regards