[Bug 21406] Pcsp Crash aplication Unhandled exception msvcr90.dll

wine-bugs at winehq.org wine-bugs at winehq.org
Tue Jan 19 06:14:12 CST 2010


http://bugs.winehq.org/show_bug.cgi?id=21406





--- Comment #3 from Anastasius Focht <focht at gmx.net>  2010-01-19 06:14:11 ---
Hello,

interesting problem ;-)

The crash seems to happen in some kind of custom ELF/PRX (PSP Relocation
eXecutable) loader.
Though the real problem starts much earlier.

The emulator creates a mapping to emulate the target machine memory:

--- snip ---
...

0032:Call
KERNEL32.CreateFileMappingA(ffffffff,00000000,00000004,00000000,04440000,004d3e80
"pcspmemory") ret=00452218

0032:Ret  KERNEL32.CreateFileMappingA() retval=0000019c ret=00452218

--- snip ---

The emulator reserves a large chunk of memory and frees it immediately -
probably to get the base address of a large contiguous area that is available
at this time.
This is a dangerous approach because the process VM layout might change at
times and not all (implicit) allocations are under control of the application. 

--- snip ---
0032:Call KERNEL32.VirtualAlloc(00000000,10000000,00002000,00000001)
ret=0045223a

0032:Ret  KERNEL32.VirtualAlloc() retval=02b60000 ret=0045223a

0032:Call KERNEL32.VirtualFree(02b60000,00000000,00008000) ret=00452255

0032:Ret  KERNEL32.VirtualFree() retval=00000001 ret=00452255

--- snip ---

A large number of sections is mapped into that contiguous range, having their
explicit mapping base specified (increasing).

--- snip ---
...
0032:Call
KERNEL32.MapViewOfFileEx(0000019c,00000006,00000000,04430000,00010000,02b60000)
ret=0045216a

0032:Ret  KERNEL32.MapViewOfFileEx() retval=02b60000 ret=0045216a 

...
--- snip ---

If mapping base is explicitly specified, MSDN states:

--- quote ---
While it is possible to specify an address that is safe now (not used by the
operating system), there is no guarantee that the address will remain safe over
time. Therefore, it is better to let the operating system choose the address.
In this case, you would not store pointers in the memory mapped file, you would
store offsets from the base of the file mapping so that the mapping can be used
at any address.
--- quote ---

Now to the culprit:

--- snip ---
...
0032:Call
KERNEL32.MapViewOfFileEx(0000019c,00000006,00000000,04410000,00010000,05830000)
ret=004521a9

0032:Ret  KERNEL32.MapViewOfFileEx() retval=05830000 ret=004521a9

0032:Call
KERNEL32.MapViewOfFileEx(0000019c,00000006,00000000,04410000,00010000,05840000)
ret=004521a9

0033:Ret  user32.GetDC() retval=000005f0 ret=650bac44

0033:Call gdi32.CreateCompatibleDC(000005f0) ret=650bac4d

0033:Ret  gdi32.CreateCompatibleDC() retval=000006e8 ret=650bac4d

0033:Call user32.ReleaseDC(00000000,000005f0) ret=650bac58

0033:Ret  user32.ReleaseDC() retval=00000001 ret=650bac58

0033:Call
gdi32.CreateDIBSection(000006e8,0032d668,00000000,0032d654,00000000,00000000)
ret=650bac73

0032:Ret  KERNEL32.MapViewOfFileEx() retval=00000000 ret=004521a9

0032:Call KERNEL32.UnmapViewOfFile(02b60000) ret=004791cc

0032:Ret  KERNEL32.UnmapViewOfFile() retval=00000001 ret=004791cc

0032:Call KERNEL32.CloseHandle(0000019c) ret=004791dd

0032:Ret  KERNEL32.CloseHandle() retval=00000001 ret=004791dd
... 
--- snip ---

The application is multithreaded.
There is a second gui/message pumping thread (QT4) running in background.
At a time this second thread kicks in and does some GUI processing
(timer/message).
There are various heap allocations and deallocations happening during this time
in this thread that are harmless because the C runtime uses its own (reserved)
heap range.

The problem comes from Wine's DIB section bits allocation code. 

--- snip dlls/gdi/dib.c ---
HBITMAP WINAPI CreateDIBSection(HDC hdc, CONST BITMAPINFO *bmi, UINT usage,
                                VOID **bits, HANDLE section, DWORD offset)
{
...
 dib->dsBm.bmBits = VirtualAlloc( NULL, dib->dsBmih.biSizeImage,
             MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE );
...
--- snip dlls/gdi/dib.c ---

This little snippet causes a chunk to be stolen from that large contiguous VM
area that the app assumed to be "free".
The MapViewOfFileEx() from the loader thread that supplies the previously
"stolen" VM base address fails.
Because the failure doesn't seem to be propagated properly to top error
handling this goes on unnoticed a while ...

Later when the app's ELF/PRX loader maps/processes additional file object based
sections, the fault is triggered:

--- snip ---
...

0032:Call KERNEL32.CreateFileA(02864a90
"C:/psp/ms0/PSP/GAMEsdk/cube/EBOOT.pbp",80000000,00000003,02b5e868,00000003,00000080,00000000)
ret=78587bb4

0032:Ret  KERNEL32.CreateFileA() retval=00000d10 ret=78587bb4 

...

0032:trace:seh:raise_exception code=c0000005 flags=0 addr=0x78544742
ip=78544742 tid=0032

0032:trace:seh:raise_exception  info[0]=00000001

0032:trace:seh:raise_exception  info[1]=0b470018

0032:trace:seh:raise_exception  eax=0b470018 ebx=02884728 ecx=00000008
edx=00000008 esi=02884728 edi=0b470018

0032:trace:seh:raise_exception  ebp=02b5e78c esp=02b5e770 cs=0023 ds=002b
es=002b fs=0063 gs=006b flags=00010203

0032:trace:seh:call_vectored_handlers calling handler at 0x778bee52
code=c0000005 flags=0

0032:trace:seh:call_vectored_handlers handler at 0x778bee52 returned 0

0032:trace:seh:call_vectored_handlers calling handler at 0x68cbc766
code=c0000005 flags=0

0032:trace:seh:call_vectored_handlers handler at 0x68cbc766 returned 0

0032:trace:seh:call_stack_handlers calling handler at 0x484c2d code=c0000005
flags=0 
--- snip ---

Wineserver trace, better illustrating the "stolen" chunk problem:

0x1b: app ELF/PRX loader thread
0x09: app QT4 GUI/message thread

--- snip ---
001b:Call
KERNEL32.MapViewOfFileEx(00000194,00000006,00000000,04410000,00010000,04050000)
ret=004521a9
001b:trace:virtual:NtMapViewOfSection handle=0x194 process=0xffffffff
addr=0x4050000 off=004410000 size=10000 access=4
001b: get_mapping_info( handle=0194, access=00000002 )
001b: get_mapping_info() = 0 { size=04440000, protect=67, header_size=0,
base=00000000, mapping=06e8, shared_file=0000 }
0009: set_caret_info( flags=00000006, handle=0001003a, x=0, y=0, hide=-1,
state=1 )
0009: set_caret_info() = ACCESS_DENIED { full_handle=00000000,
old_rect={0,0;0,0}, old_hide=1, old_state=0 }
0009: set_foreground_window( handle=0001003a )
0009: set_foreground_window() = 0 { previous=00010020, send_msg_old=1,
send_msg_new=0 }
...
0009:Call
gdi32.CreateDIBSection(000006e8,0032d668,00000000,0032d654,00000000,00000000)
ret=650bac73
0009:trace:virtual:NtAllocateVirtualMemory 0xffffffff (nil) 00091c80 3000
00000004
0009:trace:virtual:map_view got mem in reserved area 0x4050000-0x40e2000
0009:trace:virtual:VIRTUAL_DumpView View: 0x4050000 - 0x40e1fff (valloc)
0009:trace:virtual:VIRTUAL_DumpView       0x4050000 - 0x40e1fff c-rw-
0009:trace:virtual:create_view forcing exec permission on 0x4050000-0x40e1fff
001b: close_handle( handle=06e8 )
001b: close_handle() = 0
001b:Ret  KERNEL32.MapViewOfFileEx() retval=00000000 ret=004521a9
--- snip ---

The app loader thread expects base addr 0x04050000 to be free.
The interleaving DIB section alloc from the GUI thread steals this chunk,
causing the mapping operation to fail.

For testing/verification purposes I used top-down allocation hint for DIB bits
(VM) allocations to not collide with the app ones and it helped the application
to load and run to image. Though this is only a hack to proof my findings.

So the question remains if Wine should support such questionable behaviour that
even MSDN states "dangerous" to rely on. This would force Wine to alloc DIB
bits from its own reserved range.

The emulator could initially reserve that large chunk without committing the
memory and later map sections into that range.
Additionally the app could fix the error handling to propagate mapping failures
better.

Regards

-- 
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
Do not reply to this email, post in Bugzilla using the
above URL to reply.
------- You are receiving this mail because: -------
You are watching all bug changes.



More information about the wine-bugs mailing list