http://bugs.winehq.org/show_bug.cgi?id=10417
Summary: OLEAUT32: crash if >128 methods in an interface Product: Wine Version: 0.9.49. Platform: Macintosh OS/Version: Mac OS X 10.5 Status: UNCONFIRMED Severity: normal Priority: P1 Component: wine-ole AssignedTo: wine-bugs@winehq.org ReportedBy: mjk@cardbox.com
This bug was encountered in build cxoffice-6.2.0rc1-2-g024be42 of Wine (part of CrossOver Mac). The bug has been identified in the current source code at http://source.winehq.org/source/dlls/oleaut32/tmarshal.c.
Using any marshaled interface with more than 128 methods causes a crash within OLEAUT32 if any method at position >=128 is called. This was detected when using Cardbox (http://www.cardbox.com) and is a SHOW-STOPPER because it makes the use of VBScript macros impossible.
However, the bug is completely general and applies to any application at all that has interfaces with large number of methods. It is quite possible that many random OLE / COM - related bugs that have already been reported have this bug as their underlying cause.
The version of Cardbox on which the bug was found is more recent than the one currently available on the web site. If anyone wants to have a copy for testing, together with instructions for reproducing the crash, please contact me.
LOCATION OF THE BUG
The bug is in dlls/oleaut32/tmarshal.c. When constructing a proxy interface, PSFacBuf_CreateProxy at line #1712 constructs the following proxy code for each method:
popl %eax pushl <nr> pushl %eax call xCall lret <n> (+4)
where <nr> is the position of the method in the list of methods: 0, 1, 2, and so on.
The pushl <nr> instruction is defined by following code:
374 BYTE pushlval; // set to 0x6a by line #1712 375 BYTE nr;
The fact that the method position is a byte already limits the maximum size of an interface to 256 methods, which is less than the 512-method limit of Windows NT4.0 SP3, and the 1024-method limit of Windows 2000: see "MIDL2362" in http://msdn2.microsoft.com/en-us/library/aa366756.aspx for details. Thus this needs to be corrected in any case. The proxy code as it stands will call method 0 instead of method 256, method 1 instead of method 257, and so on, leading to random behaviour and possible stack corruption.
The crash when method 128 is called has a different cause. The proxy for method 128 contains the instruction 6A 80, because the programmer thought that this would push 00000080 onto the stack. In fact the PUSH instruction with opcode 6A SIGN-EXTENDS its operand and does not zero-extend it. Thus the proxy for the 128th method pushes FFFFFF80 onto the stack before calling xCall. xCall interprets this as a negative number (-128) and thus attempts to synthesize a call not to method 128 but to a non-existent method -128. In the same way it will call method -127 instead of method 129,... and so on.
SUGGESTED CORRECTION
The very simple correction to this bug, which is guaranteed to work, is to alter line 375 to
375 DWORD nr;
and line 1712 to
1712 xasm->pushlval = 0x68;
which expects a 32-bit operand rather than an 8-bit one.
This will result in every proxy using 15 bytes per method instead of 12 bytes. This does not seem an excessive price to pay for complete reliability in the future: there will then be no limit to the number of methods that can be supported.
ALTERNATIVE CORRECTIONS
If the 25% expansion in proxy size is considered unacceptable (it should not really be: proxies are small) then there are several ways round the problem. An increase to 256 methods could be achieved simply by adding a line at the very beginning of xCall:
method &= 0xff;
but this would HAVE to be accompanied by an explicit test for the method count limit (now 256) in PSFacBuf_CreateProxy so that the attempt to create a proxy with too methods would simply fail rather than (as now) generate a proxy that will randomly crash the application.
Another approach would be to create dummy functions (in assembler) that would add 128, 256, 384, 512, etc to the 'method' argument before forwarding it on to xCall. In that case, method numbers after 127 would generate proxies that called one of the variant xCalls instead of the original one. The programming in PSFacBuf_CreateProxy would be relatively straightforward, and the dummy functions would not need to do any stack manipulation: they would simply add an offset to the DWORD at [ESP+8] and then JMP straight to the start of xCall.
This would *still* give a finite limit to the number of methods, but the limit would be much larger. Again, good engineering practice dictates that PSFacBuf_CreateProxy should report an error if it encounters a number of methods beyond the number that it was designed to cope with.