https://bugs.winehq.org/show_bug.cgi?id=50108
Bug ID: 50108 Summary: Multiple applications using native API sandboxing / virtualization crash when being relay traced (Adobe Reader 11) Product: Wine Version: 5.21 Hardware: x86 OS: Linux Status: NEW Severity: normal Priority: P2 Component: tools Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
encountered while revisiting some Adobe bugs with native API sandboxing enabled (default).
Native API sandboxing works as designed under normal conditions, see my no-relay references later. Relay debugging is a developer / triager use-case.
Stable download link via Internet Archive:
https://web.archive.org/web/20200522053748/http://ardownload.adobe.com/pub/a...
Acrobat Reader creates a sandboxed process (initially suspended) which crashes in entry point when being relay traced.
--- snip --- $ pwd /home/focht/.wine/drive_c/Program Files (x86)/Adobe/Reader 11.0/Reader
$ WINEDEBUG=+seh,+relay,+server,+ntdll wine ./AcroRd32.exe >>log.txt 2>&1 ... 0024:Call KERNEL32.CreateProcessAsUserW(00000100,0198e508 L"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe",0198e618 L""C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe" --channel=32.1.1899522388 --type=renderer",00000000,00000000,00000000,0100040c,00df6f68,00000000,0031f644,0031f694) ret=0042b162 ... 0130: init_process_done( gui=1, module=00400000, ldt_copy=f7d5d9c0, entry=00401039 ) 0024: *wakeup* signaled=0 0130: init_process_done() = 0 { suspend=1 } ... 0024:Ret KERNEL32.CreateProcessAsUserW() retval=00000001 ret=0042b162 ... 0130:Call ntdll.RtlInitNlsTables(011d0000,011f0000,00fed810,7b081484) ret=7b01d500 0130:Ret ntdll.RtlInitNlsTables() retval=7b081484 ret=7b01d500 0130:Call ntdll.RtlResetRtlTranslations(7b081484) ret=7b01d50b 0130:Ret ntdll.RtlResetRtlTranslations() retval=00000400 ret=7b01d50b 0130:Call ntdll.RtlInitUnicodeString(0031eb78,7b069b30 L"\Registry\Machine") ret=7b0387b3 0130:Ret ntdll.RtlInitUnicodeString() retval=00000024 ret=7b0387b3 0130:trace:seh:dispatch_exception code=c0000096 flags=0 addr=00A8C960 ip=00a8c960 tid=0130 0130:trace:seh:dispatch_exception eax=7bc6c000 ebx=0031eb80 ecx=0031eb50 edx=02000000 esi=00000000 edi=00000000 0130:trace:seh:dispatch_exception ebp=0031eb04 esp=0031ea64 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010202 0130:trace:seh:call_stack_handlers calling handler at 0040C7F0 code=c0000096 flags=0 0130:trace:seh:call_stack_handlers handler at 0040C7F0 returned 1 0130:trace:seh:call_stack_handlers calling handler at 7BC513A0 code=c0000096 flags=0 0130:trace:seh:__regs_RtlUnwind code=c0000096 flags=2 0130:trace:seh:__regs_RtlUnwind eax=00000000 ebx=0031eed8 ecx=0031e8a8 edx=0031eed8 esi=00000001 edi=00000000 0130:trace:seh:__regs_RtlUnwind ebp=0031e508 esp=0031e500 eip=7bc512a6 cs=0023 ds=002b fs=0063 gs=006b flags=00000206 0130:trace:seh:__regs_RtlUnwind calling handler at 7BC460F0 code=c0000096 flags=2 0130:trace:seh:__regs_RtlUnwind handler at 7BC460F0 returned 1 0130:trace:seh:__regs_RtlUnwind calling handler at 0040C7F0 code=c0000096 flags=2 0130:trace:seh:__regs_RtlUnwind handler at 0040C7F0 returned 1 0130: select( flags=3, cookie=0031d88c, timeout=0, size=0, prev_apc=0000, result={}, data={}, context={} ) 0130: select() = TIMEOUT { call={APC_NONE}, apc_handle=0000, context={} } 0130:exception c0000096 in PE entry point (proc=7B0272D0,module=7B000000,reason=PROCESS_ATTACH,res=0031FD24) 0130:Ret PE DLL (proc=7B0272D0,module=7B000000 L"kernelbase.dll",reason=PROCESS_ATTACH,res=0031FD24) retval=0 0130:Call PE DLL (proc=7B0272D0,module=7B000000 L"kernelbase.dll",reason=PROCESS_DETACH,res=0031FD24) 0130:Ret PE DLL (proc=7B0272D0,module=7B000000 L"kernelbase.dll",reason=PROCESS_DETACH,res=0031FD24) retval=1 0130:err:module:LdrInitializeThunk "kernelbase.dll" failed to initialize, aborting 0130:err:module:LdrInitializeThunk Initializing dlls for L"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe" failed, status c0000096 0130: terminate_process( handle=ffffffff, exit_code=-1073741674 ) 0130: terminate_process() = 0 { self=1 } 0130: *killed* exit_code=-1073741674 --- snip ---
The crash is in kernelbase:init_locale() which calls RegCreateKeyExW -> NtCreateKey()
NtCreateKey() happens to be the first of the hooked native API functions being called.
Wine source:
https://source.winehq.org/git/wine.git/blob/70d77a439ab58dcf56664d1545aa0c4c...
--- snip --- 699 /*********************************************************************** 700 * init_locale 701 */ 702 void init_locale(void) 703 { 704 UINT ansi_cp = 0, oem_cp = 0; 705 USHORT *ansi_ptr, *oem_ptr; 706 void *sort_ptr; 707 LCID lcid = GetUserDefaultLCID(); 708 WCHAR bufferW[80]; 709 DYNAMIC_TIME_ZONE_INFORMATION timezone; 710 GEOID geoid = GEOID_NOT_AVAILABLE; 711 DWORD count, dispos, i; 712 SIZE_T size; 713 HKEY hkey; 714 715 kernel32_handle = GetModuleHandleW( L"kernel32.dll" ); 716 717 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, 718 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) ); 719 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER, 720 (WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) ); 721 GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, 722 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) ); 723 724 NtGetNlsSectionPtr( 9, 0, NULL, &sort_ptr, &size ); 725 NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size ); 726 init_sortkeys( sort_ptr ); 727 728 if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size )) 729 NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size ); 730 if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size )) 731 NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size ); 732 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr; 733 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr; 734 NtCurrentTeb()->Peb->UnicodeCaseTableData = sort.casemap; 735 RtlInitNlsTables( ansi_ptr, oem_ptr, sort.casemap, &nls_info ); 736 RtlResetRtlTranslations( &nls_info ); 737 738 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\CurrentControlSet\Control\Nls", 739 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL ); ... --- snip ---
Entry point code of <ntdll.NtCreateKey> is read two times:
--- snip --- 0024:Call KERNEL32.ReadProcessMemory(0000010c,7bc036ac,0031f7d0,00000018,0031f7cc) ret=004614db ... 0024: read_process_memory( handle=010c, addr=7bc036ac ) 0130: *signal* signal=19 0024: read_process_memory() = 0 { data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b,50,ff,50,04,c2,1c,00,90} } ... 0024:Ret KERNEL32.ReadProcessMemory() retval=00000001 ret=004614db 0024:Call KERNEL32.ReadProcessMemory(0000010c,7bc036ac,0031f7d4,00000010,0031f7d0) ret=004617e7 0024: read_process_memory( handle=010c, addr=7bc036ac ) 0130: *signal* signal=19 0024: read_process_memory() = 0 { data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b} } ... 0024:Ret KERNEL32.ReadProcessMemory() retval=00000001 ret=004617e7 --- snip ---
* 0x18 bytes = pre-analysis * 0x10 bytes = unknown native API entry signature -> defaults to syscall style
The <ntdll.NtCreateKey> relay entry disassembly:
--- snip --- 7BC036AC | 8BFF | mov edi,edi | hotpatch prolog start 7BC036AE | 55 | push ebp | 7BC036AF | 8BEC | mov ebp,esp | 7BC036B1 | 5D | pop ebp | hotpatch prolog end 7BC036B2 | 68 83000700 | push 70083 | 7BC036B7 | B8 00C0C67B | mov eax,ntdll.7BC6C000 | 7BC036BC | 50 | push eax | not copied 7BC036BD | FF50 04 | call dword ptr ds:[eax+4] | not copied 7BC036C0 | C2 1C00 | ret 1C | not copied 7BC036C3 | 90 | nop | --- snip ---
Wine source, emitting relay entry:
https://source.winehq.org/git/wine.git/blob/70d77a439ab58dcf56664d1545aa0c4c...
--- snip --- 212 /******************************************************************* 213 * output_relay_debug 214 * 215 * Output entry points for relay debugging 216 */ 217 static void output_relay_debug( DLLSPEC *spec ) 218 { ... 244 output( "\t.text\n" ); 245 output( "__wine_spec_relay_entry_points:\n" ); 246 output( "\tnop\n" ); /* to avoid 0 offset */ 247 248 for (i = spec->base; i <= spec->limit; i++) 249 { 250 ORDDEF *odp = spec->ordinals[i]; 251 252 if (!needs_relay( odp )) continue; 253 254 switch (target_cpu) 255 { 256 case CPU_x86: 257 output( "\t.align %d\n", get_alignment(4) ); 258 output( "\t.long 0x90909090,0x90909090\n" ); 259 output( ".L__wine_spec_relay_entry_point_%d:\n", i ); 260 output_cfi( ".cfi_startproc" ); 261 output( "\t.byte 0x8b,0xff,0x55,0x8b,0xec,0x5d\n" ); /* hotpatch prolog */ 262 if (odp->flags & (FLAG_THISCALL | FLAG_FASTCALL)) /* add the register arguments */ 263 { 264 output( "\tpopl %%eax\n" ); 265 if ((odp->flags & FLAG_FASTCALL) && get_args_size( odp ) > 4) output( "\tpushl %%edx\n" ); 266 output( "\tpushl %%ecx\n" ); 267 output( "\tpushl %%eax\n" ); 268 } 269 output( "\tpushl $%u\n", (odp->u.func.args_str_offset << 16) | (i - spec->base) ); 270 output_cfi( ".cfi_adjust_cfa_offset 4" ); 271 272 if (UsePIC) 273 { 274 output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); 275 output( "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" ); 276 needs_get_pc_thunk = 1; 277 } 278 else output( "\tmovl $.L__wine_spec_relay_descr,%%eax\n" ); 279 output( "\tpushl %%eax\n" ); 280 output_cfi( ".cfi_adjust_cfa_offset 4" ); 281 282 output( "\tcall *4(%%eax)\n" ); 283 output_cfi( ".cfi_adjust_cfa_offset -8" ); 284 if (odp->type == TYPE_STDCALL) 285 output( "\tret $%u\n", get_args_size( odp )); 286 else 287 output( "\tret\n" ); 288 output_cfi( ".cfi_endproc" ); 289 break; --- snip ---
For reference, <ntdll.NtCreateKey> without relay trace (syscall style,fits into 16 bytes):
--- snip --- 7BC0B9B0 | B8 1C000000 | mov eax,1C | 7BC0B9B5 | BA D0C5C07B | mov edx,ntdll.7BC0C5D0 | 7BC0B9BA | FFD2 | call edx | 7BC0B9BC | C2 1C00 | ret 1C | 7BC0B9BF | 90 | nop | ... 7BC0C5D0 | FF25 18C0C67B | jmp dword ptr ds:[<__wine_syscall_dispatcher>] | --- snip ---
The parent process writes a copy of the original native API entry and trampoline code into child process (sandbox) address space:
--- snip --- 0024:Call KERNEL32.WriteProcessMemory(0000010c,00a8c950,019da140,00000036,0031f7cc) ret=00430409 ... 0024: write_process_memory( handle=010c, addr=00a8c950, data={8b,ff,55,8b,ec,5d,68,83,00,07,00,b8,00,c0,c6,7b,6c,00,00,00,00,00,00,00,83,ec,08,52,8b,54,24,0c,89,54,24,08,c7,44,24,0c,50,c9,a8,00,c7,44,24,04,70,8a,41,00,5a,c3} ) 0130: *signal* signal=19 0024: write_process_memory() = 0 ... 0024:Ret KERNEL32.WriteProcessMemory() retval=00000001 ret=00430409 --- snip ---
--- snip --- 00A8C950 | 8BFF | mov edi,edi | org copy of 00A8C952 | 55 | push ebp | NtCreateKey 00A8C953 | 8BEC | mov ebp,esp | relay entry 00A8C955 | 5D | pop ebp | 00A8C956 | 68 83000700 | push 70083 | 00A8C95B | B8 00C0C67B | mov eax,ntdll.7BC6C000 | 00A8C960 | 6C | insb | *boom* 00A8C962 | 0000 | add byte ptr ds:[eax],al | 00A8C964 | 0000 | add byte ptr ds:[eax],al | 00A8C966 | 0000 | add byte ptr ds:[eax],al | 00A8C967 | 00 00A8C968 | 83EC 08 | sub esp,8 | trampoline 00A8C96C | 52 | push edx | 00A8C970 | 8B5424 0C | mov edx,dword ptr ss:[esp+C] | 00A8C974 | 895424 08 | mov dword ptr ss:[esp+8],edx | 00A8C978 | C74424 0C 50C9A800 | mov dword ptr ss:[esp+C],A8C950 | NtCreateKey 00A8C980 | C74424 04 708A4100 | mov dword ptr ss:[esp+4],acrord32.418A70 | hook impl. 00A8C988 | 5A | pop edx | 00A8C989 | C3 | ret | --- snip ---
and patches original <ntdll.NtCreateKey> relay entry:
--- snip --- 0024:Call KERNEL32.WriteProcessMemory(0000010c,7bc036ac,0031f7d4,0000000c,0031f798) ret=004304ed ... 0024: write_process_memory( handle=010c, addr=7bc036ac, data={b8,ff,55,8b,ec,ba,68,c9,a8,00,ff,e2} ) 0130: *signal* signal=19 0024: write_process_memory() = 0 ... 0024:Ret KERNEL32.WriteProcessMemory() retval=00000001 ret=004304ed --- snip ---
Patched <ntdll.NtCreateKey> relay entry:
--- snip --- 7BC036AC | B8 FF558BEC | mov eax,EC8B55FF | garbage #sysc 7BC036B1 | BA 68C9A800 | mov edx,A8C968 | trampoline 7BC036B6 | FFE2 | jmp edx | --- snip ---
It crashes when the hook calls the original relay entry code which was only partially copied.
For reference how it looks like without relay - working as intended:
--- snip --- 00576950 | B8 1C000000 | mov eax,1C | org copy of 00576955 | BA D0C5C07B | mov edx,ntdll.7BC0C5D0 | NtCreateKey 0057695A | FFD2 | call edx | entry 0057695C | C2 1C00 | ret 1C | 0057695F | 90 | nop | 00576960 | 6C | insb | fill 00576961 | 0000 | add byte ptr ds:[eax],al | 00576963 | 0000 | add byte ptr ds:[eax],al | 00576965 | 0000 | add byte ptr ds:[eax],al | 00576967 | 00 00576968 | 83EC 08 | sub esp,8 | trampoline 0057696C | 52 | push edx | 00576970 | 895424 08 | mov dword ptr ss:[esp+8],edx | 00576974 | C74424 0C 50695700 | mov dword ptr ss:[esp+C],576950 | NtCreateKey 0057697C | C74424 04 708A4100 | mov dword ptr ss:[esp+4],acrord32.418A70 | hook impl. 00576984 | 5A | pop edx | 00576985 | C3 | ret | --- snip ---
Patched <ntdll.NtCreateKey> entry:
--- snip --- 7BC0B9B0 | B8 1C000000 | mov eax,1C | org #sysc 7BC0B9B5 | BA 68695700 | mov edx,576968 | trampoline 7BC0B9BA | FFE2 | jmp edx 7BC0B9BC | C2 1C00 | retn 1C 7BC0B9BF | 90 | nop --- snip ---
Yes, I know it's possible to disable relay thunks for native API (RelayExclude -> ntdll.*), even for a sub-set (the ones being hooked) to avoid crashes. Maybe something can be done without resorting to that functionality. If it's not worth to do extra handling for NT syscall relay thunks then you might resolve this one as 'WONTFIX'.
$ sha1sum AdbeRdr11004_en_US.exe 9c295c16d374735bf292ef6c630c9ab392c22500 AdbeRdr11004_en_US.exe
$ du -sh AdbeRdr11004_en_US.exe 49M AdbeRdr11004_en_US.exe
$ wine --version wine-5.21
Regards