[Bug 21777] New: CreateMutexExA(): use heap for A to W conversion to work around transbase db engine app bug (affects multiple apps, TecDoc CATALOG ...)

wine-bugs at winehq.org wine-bugs at winehq.org
Sat Feb 20 08:24:55 CST 2010


           Summary: CreateMutexExA(): use heap for A to W conversion to
                    work around transbase db engine app bug (affects
                    multiple apps, TecDoc CATALOG ...)
           Product: Wine
           Version: 1.1.39
          Platform: x86
               URL: http://www.tecdoc.de
        OS/Version: Linux
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: kernel32
        AssignedTo: wine-bugs at winehq.org
        ReportedBy: focht at gmx.net


at the end of TecDoc CATALOG 1/2010 DVD install a crash dialog is displayed
while starting a service (the msi part of install is finished anyway).
The database engine service has auto-start type set hence on every program
start (wine notepad) the infamous crash dialog is displayed.

This turns out to be an app bug but due to differences in Wine vs. Windows API
design/usage this bug has no consequences in Windows and goes on unnoticed.

Unfortunately the part of software that crashes comes from another software
supplier "Transaction Software" (http://www1.transaction.de/transaction/).
Many automotive spare parts catalogue software (BMW, VW, Audi, GM, Fiat ...)
internally use the Transbase SQL database engine for years.

In case of Wine/Linux this means many broken apps/versions in the wild due to
this bug and even if the original software supplier fixes the bug, it will take
years until spare parts catalogue software vendors using the engine to pick up
and release new versions of their own products.

Despite being an app bug I suggest to modify Wine to work around this developer

Here it goes, first the crash itself:

--- snip ---
wine: Unhandled page fault on read access to 0x012f0000 at address 0x681ee546
(thread 001d), starting debugger...
Unhandled exception: page fault on read access to 0x012f0000 in 32-bit code
process  tid      prio (all id:s are in hex)
00000019 (D) C:\TECDOC_CD\1_2010\db\tbmux32.exe
    0000001d    0 <==
    0000001b    0
    0000001a    0
=>0 0x681ee546 (0x012eddd4)
  1 0x7bc79ac0 NtCreateMutant+0xae(MutantHandle=0x12eded4, access=0x1f0001,
attr=0x12edeb4, InitialOwner=0) [/opt/wine/wine-git/dlls/ntdll/sync.c:429] in
ntdll (0x012ede94)
  2 0x7b869dc8 CreateMutexExW+0xb2(sa=0x12ee16c, name="Global\TBSEM ( TECDOC_C
) = (Key=32769, Ind=0)", flags=0, access=0x1f0001)
[/opt/wine/wine-git/dlls/kernel32/sync.c:653] in kernel32 (0x012edee4)
  3 0x7b869d0c CreateMutexExA+0xb6(sa=0x12ee16c, name="Global\TBSEM ( TECDOC_C
) = (Key=32769, Ind=0)", flags=0, access=0x1f0001)
[/opt/wine/wine-git/dlls/kernel32/sync.c:626] in kernel32 (0x012ee124)
  4 0x7b869c08 CreateMutexA+0x3a(sa=0x12ee16c, owner=0, name="Global\TBSEM (
TECDOC_C ) = (Key=32769, Ind=0)") [/opt/wine/wine-git/dlls/kernel32/sync.c:599]
in kernel32 (0x012ee144)
  5 0x0041a2a3 in tbmux32 (+0x1a2a3) (0x74fd9c13)
  6 0x458d28ec (0x83e58955)
--- snip ---

Starting the whole thing (db service) with +relay exhibits a different
behaviour: the service doesn't crash but it doesn't really work, indicating a
stack usage problem.

Have a close look at security descriptor dump, DACL part:

--- snip ---
001e:Call advapi32.InitializeSecurityDescriptor(012ee2bc,00000001) ret=00419b06
001e:Ret  advapi32.InitializeSecurityDescriptor() retval=00000001 ret=00419b06
001e:Call advapi32.InitializeAcl(012ede8c,00000400,00000002) ret=00419b28
001e:Ret  advapi32.InitializeAcl() retval=00000001 ret=00419b28
001e:Call advapi32.InitializeSid(012eda8c,012eda84,00000001) ret=00419b47
001e:Ret  advapi32.InitializeSid() retval=00000001 ret=00419b47
001e:Call advapi32.GetSidSubAuthority(012eda8c,00000000) ret=00419b60
001e:Ret  advapi32.GetSidSubAuthority() retval=012eda94 ret=00419b60
001e:Call advapi32.AddAccessAllowedAce(012ede8c,00000002,10000000,012eda8c)
001e:Ret  advapi32.AddAccessAllowedAce() retval=00000001 ret=00419b7c
001e:Ret  advapi32.SetSecurityDescriptorDacl() retval=00000001 ret=00419b9e
001e:Call KERNEL32.CreateMutexA(012ee2b0,00000000,012ee2d0 "Global\\TBSEM (
TECDOC_C ) = (Key=808607544, Ind=0)") ret=0041a2a3
001e: create_mutex( access=001f0001, attributes=00000080, owned=0,
objattr={rootdir=0018,sd={control=00000004,owner=<not present>,group=<not
( TECDOC_C ) = (Key=808607544, Ind=0)"} )
001e: create_mutex() = INVALID_SECURITY_DESCR { handle=0000 }
001e:Ret  KERNEL32.CreateMutexA() retval=00000000 ret=0041a2a3 
--- snip ---

Pretty much messed up.

To keep the story short, I translated debugger disassembly snippets and
annotated stack dumps into a snippet of C code so you won't get annoyed with
too much detail ;-)
This is most likely what the app does (might not be 100% accurate, just to
illustrate the problem):

--- stupid app code start ---

static int prepare_sd( SECURITY_ATTRIBUTES *sa)
    BYTE user_sid[0x400];
    BYTE acl_buffer[0x400];

    int res = InitializeSecurityDescriptor( sa->lpSecurityDescriptor,
    if( !retval)
        return res;

    res = InitializeAcl( (PACL) acl_buffer, sizeof(acl_buffer), ACL_REVISION);
    if( !retval)
        return res;

    res = InitializeSid( (PSID) user_sid, &sia_world, 1);
    if( !retval)
        return res;

    *GetSidSubAuthority( (PSID) user_sid, 0) = SECURITY_WORLD_RID;
    res = AddAccessAllowedAce( (PACL) acl_buffer, ACL_REVISION, 0x10000000,
(PSID) user_sid);
    if( !retval)
        return res;

    return SetSecurityDescriptorDacl( sa->lpSecurityDescriptor, TRUE,
(PACL)acl_buffer, FALSE);

static int open_app_mutex( ...)

    char name[0x80];
    wsprintfA( name, "xxx", ...);

    sa.nLength= sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;

    prepare_sd( &sa);

    CreateMutexA( &sa, FALSE, name);
--- stupid app code end ---

You can probably spot the biggest mistake of all pretty soon: some "genius"
decided to put everything on stack in prepare_sd().

Upon return of prepare_sd() helper function, the stack locals used within
prepare_sd() are still *intact*
When the app calls CreateMutexA(), the call sequence is as follows:

CreateMutexA -> CreateMutexExA -> CreateMutexExW -> NtCreateMutant

--- snip dlls/kernel32/sync.c ---
    return CreateMutexExA( sa, name, owner ? CREATE_MUTEX_INITIAL_OWNER : 0,


flags, DWORD access )
    WCHAR buffer[MAX_PATH];

    if (!name) return CreateMutexExW( sa, NULL, flags, access );

    if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
        return 0;
    return CreateMutexExW( sa, buffer, flags, access );

--- snip dlls/kernel32/sync.c ---

The problem is buried within CreateMutexExA(): the A to W conversion uses a
stack based buffer of MAX_PATH len.
This eats stack, partly overwriting the ACL buffer (DACL) from previous
prepare_sd() which used to be a stack local too.

The reason that this doesn't happen on Windows is most likely conversions are
heap based or the call path to kernel mode is very short, not consuming that
much stack as Wine does.

I verified with a patch to use heap based buffer in CreateMutexExA() for A to W
conversion (similar what's being done in CreateMailslotA()) and it lets the app
succeed despite that nasty stack bug.

I'll leave it to Alexandre to judge this (change or leave Wine as it is).

If you decide for WONTFIX (that app bug would really deserve such treatment), I
will attach a patch to give users a chance work around various broken transbase


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