[Bug 44460] New: Wine' s loader should prevent multiple executable mappings of images (dlls) backed by the same physical file (long path vs. short path)

wine-bugs at winehq.org wine-bugs at winehq.org
Thu Feb 1 15:58:57 CST 2018


https://bugs.winehq.org/show_bug.cgi?id=44460

            Bug ID: 44460
           Summary: Wine's loader should prevent multiple executable
                    mappings of images (dlls) backed by the same physical
                    file (long path vs. short path)
           Product: Wine
           Version: 3.0
          Hardware: x86-64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: ntdll
          Assignee: wine-bugs at winehq.org
          Reporter: focht at gmx.net
      Distribution: ---

Hello folks,

split out from bug 35106 because there are multiple issues and this one affects
a different area: Wine loader.

Currently, loading a dll with default load flags one time with long path name
and the other time with short path name (= same physical file) results in two
different mappings of the dll which creates problems with apps expecting unique
instance data.
In short: Wine's loader currently lacks a more elaborate identity check to
prevent this case.

CMS DVR (video surveillance app) v1.0.0.8 uses multiple MFC-based ActiveX/COM
servers.

Taking the first one 'WndManager.ocx' as example:

--- snip ---
-=[ ProtectionID v0.6.9.0 DECEMBER]=-
(c) 2003-2017 CDKiLLER & TippeX
Build 24/12/17-21:05:42
Ready...
Scanning -> C:\Program Files\CMS\WndManager.ocx
File Type : 32-Bit Dll (Subsystem : Win GUI / 2), Size : 266240 (041000h)
Byte(s) | Machine: 0x14C (I386)
Compilation TimeStamp : 0x4B583655 -> Thu 21st Jan 2010 11:11:17 (GMT)
[TimeStamp] 0x4B583655 -> Thu 21st Jan 2010 11:11:17 (GMT) | PE Header | - |
Offset: 0x000000F8 | VA: 0x100000F8 | -
[TimeStamp] 0x4B583655 -> Thu 21st Jan 2010 11:11:17 (GMT) | Export | - |
Offset: 0x00020A74 | VA: 0x10020A74 | -
[LoadConfig] CodeIntegrity -> Flags 0xA3F0 | Catalog 0x46 (70) | Catalog Offset
0x2000001 | Reserved 0x46A4A0
[LoadConfig] GuardAddressTakenIatEntryTable 0x8000011 | Count 0x46A558
(4629848)
[LoadConfig] GuardLongJumpTargetTable 0x8000001 | Count 0x46A5F8 (4630008)
[LoadConfig] HybridMetadataPointer 0x8000011 | DynamicValueRelocTable 0x46A66C
[LoadConfig] FailFastIndirectProc 0x8000011 | FailFastPointer 0x46C360
[LoadConfig] UnknownZero1 0x8000011
[File Heuristics] -> Flag #1 : 00000000000000000000010100000000 (0x00000500)
[Entrypoint Section Entropy] : 5.56 (section #0) ".text   " | Size : 0x1B276
(111222) byte(s)
[DllCharacteristics] -> Flag : (0x0000) -> NONE
[SectionCount] 5 (0x5) | ImageSize 0x44000 (278528) byte(s)
[Export] 100% of function(s) (4 of 4) are in file | 0 are forwarded | 4 code |
0 data | 0 uninit data | 0 unknown |
[VersionInfo] Product Name : VideoWindow ActiveX Control Module
[VersionInfo] Product Version : 1. 0. 0. 4
[VersionInfo] File Description : VideoWindow ActiveX Control Module
[VersionInfo] File Version : 1. 0. 0. 4
[VersionInfo] Original FileName : VideoWindow.OCX
[VersionInfo] Internal Name : VideoWindow
[VersionInfo] Legal Copyrights : Copyright (C) 2006
[ModuleReport] [IAT] Modules -> MFC42.DLL | MSVCRT.dll | KERNEL32.dll |
USER32.dll | GDI32.dll | COMCTL32.dll | ole32.dll | OLEPRO32.DLL | OLEAUT32.dll
[!] File appears to have no protection or is using an unknown protection
- Scan Took : 0.224 Second(s) [0000000E0h (224) tick(s)] [246 of 580 scan(s)
done]
--- snip ---

The app explicitly loads its ActiveX/OCX to register these inproc servers
(registry):

--- snip ---
$ pwd
/home/focht/.wine/drive_c/Program Files/CMS
...
002d:Call KERNEL32.LoadLibraryA(01421b78 "C:\\Program
Files\\CMS\\WndManager.ocx") ret=00404efc
...
002d:Call PE DLL (proc=0x154b467,module=0x1530000
L"WndManager.ocx",reason=PROCESS_ATTACH,res=(nil)) 
...
002d:Ret  PE DLL (proc=0x154b467,module=0x1530000
L"WndManager.ocx",reason=PROCESS_ATTACH,res=(nil)) retval=1
002d:Ret  KERNEL32.LoadLibraryA() retval=01530000 ret=00404efc
002d:Call KERNEL32.GetProcAddress(01530000,0050d090 "DllRegisterServer")
ret=00404fa9
002d:Ret  KERNEL32.GetProcAddress() retval=01580022 ret=00404fa9
002d:CALL VIDEOWINDOW.OCX.DllRegisterServer(<unknown, check return>)
ret=00404fb3
...
002d:CALL MFC42.1212(<unknown, check return>) ret=01545e8f
002d:Call KERNEL32.LoadLibraryA(5f49ed88 "ole32.dll") ret=5f405b81
002d:Ret  KERNEL32.LoadLibraryA() retval=7e580000 ret=5f405b81
...
002d:Call KERNEL32.GetProcAddress(7e580000,5f4c5ace "StringFromGUID2")
ret=5f405b3f
002d:Ret  KERNEL32.GetProcAddress() retval=7e58ba80 ret=5f405b3f
002d:Call ole32.StringFromGUID2(01553df0,0032bf28,00000027) ret=5f4392e7
002d:Ret  ole32.StringFromGUID2() retval=00000027 ret=5f4392e7
002d:Call KERNEL32.lstrlenW(0032bf28 L"{2FBB7C49-53D0-4FFF-B3A0-B3880750976A}")
ret=5f43931c
002d:Ret  KERNEL32.lstrlenW() retval=00000026 ret=5f43931c
002d:Call KERNEL32.WideCharToMultiByte(00000000,00000000,0032bf28
L"{2FBB7C49-53D0-4FFF-B3A0-B3880750976A}",ffffffff,0032bdc8,0000004e,00000000,00000000)
ret=5f439345
002d:Ret  KERNEL32.WideCharToMultiByte() retval=00000027 ret=5f439345
002d:Call KERNEL32.GetModuleFileNameA(01530000,0032bcb4,00000104) ret=5f411dc5
002d:Ret  KERNEL32.GetModuleFileNameA() retval=00000023 ret=5f411dc5
002d:Call msvcrt.memcpy(00fd04a0,5f4d1c58,00000001) ret=5f402437
002d:Ret  msvcrt.memcpy() retval=00fd04a0 ret=5f402437
002d:Call KERNEL32.GetShortPathNameA(0032bcb4 "C:\\Program
Files\\CMS\\WndManager.ocx",00fd04a0,00000104) ret=5f411ddd
002d:Ret  KERNEL32.GetShortPathNameA() retval=0000001c ret=5f411ddd
002d:Call KERNEL32.lstrlenA(00fd04a0 "C:\\PROG~FBU\\CMS\\WNDM~VNJ.OCX")
ret=5f40248f
002d:Ret  KERNEL32.lstrlenA() retval=0000001c ret=5f40248f
002d:Call KERNEL32.FindResourceA(01530000,00000001,00000006) ret=5f403a4e
002d:Ret  KERNEL32.FindResourceA() retval=01556f98 ret=5f403a4e
002d:Call user32.LoadStringA(01530000,00000002,0032bcb8,00000100) ret=5f403a5e
002d:Ret  user32.LoadStringA() retval=00000019 ret=5f403a5e
002d:Call KERNEL32.lstrlenA(0032bcb8 "VideoWindow Property Page") ret=5f4038d7
002d:Ret  KERNEL32.lstrlenA() retval=00000019 ret=5f4038d7
002d:Call msvcrt.memcpy(00fce610,0032bcb8,00000019) ret=5f403905
002d:Ret  msvcrt.memcpy() retval=00fce610 ret=5f403905
...
002d:Call KERNEL32.GetProcAddress(7e9d0000,5f4c549a "RegCreateKeyA")
ret=5f405b3f
002d:Ret  KERNEL32.GetProcAddress() retval=7e9dcd60 ret=5f405b3f
002d:Call advapi32.RegCreateKeyA(80000000,0032be24
"CLSID\\{2FBB7C49-53D0-4FFF-B3A0-B3880750976A}",0032bfa8) ret=5f4393b1
002d:Ret  advapi32.RegCreateKeyA() retval=00000000 ret=5f4393b1
...
--- snip ---

The interesting part is that all inproc server paths are created with short
path name in registry (see 'GetShortPathNameA' calls).
I found little information on the "why". This seems to be MFC built-in/enforced
behaviour, for example mentioned here:

https://jeffpar.github.io/kbarchive/kb/179/Q179690/

--- quote ---
MFC uses short file names for registration.
--- quote ---

Later the same ActiveX/inproc server was loaded using 'ole32.CoGetClassObject'
which used the short path name from registry data but still referred to the
same physical file.
To my surprise it was mapped a second time at a different base address
0x2ab0000 (relocated), resulting in another set of instance data.

--- snip ---
...
002d:Call ole32.CoGetClassObject(0032a304,00000003,00000000,0050bddc,00329fa0)
ret=004b34b4
002d:Call
KERNEL32.FindActCtxSectionGuid(00000001,00000000,00000004,0032a304,00329e40)
ret=7e59e1f6
002d:Ret  KERNEL32.FindActCtxSectionGuid() retval=00000000 ret=7e59e1f6
002d:Call ntdll.RtlInitUnicodeString(00329cd4,7e665600
L"\\Registry\\Machine\\Software\\Classes") ret=7e598c09
002d:Ret  ntdll.RtlInitUnicodeString() retval=00329cd4 ret=7e598c09
002d:Call
ntdll.NtCreateKey(00329cf4,02000000,00329cdc,00000000,00000000,00000000,00000000)
ret=7e598a08
002d:Ret  ntdll.NtCreateKey() retval=00000000 ret=7e598a08
002d:Call ntdll.RtlInitUnicodeString(00329d70,00329dc2
L"CLSID\\{6AD651C3-AB21-4FCB-90D8-FB1396B04A07}") ret=7e598e9a
002d:Ret  ntdll.RtlInitUnicodeString() retval=00329d70 ret=7e598e9a
002d:Call ntdll.NtOpenKey(00329dbc,00020019,00329d78) ret=7e598eaf
002d:Ret  ntdll.NtOpenKey() retval=00000000 ret=7e598eaf
002d:Call ntdll.RtlNtStatusToDosError(00000000) ret=7e598ebb
002d:Ret  ntdll.RtlNtStatusToDosError() retval=00000000 ret=7e598ebb
002d:Call ntdll.RtlInitUnicodeString(00329d70,7e666c48 L"InprocServer32")
ret=7e598e9a
002d:Ret  ntdll.RtlInitUnicodeString() retval=00329d70 ret=7e598e9a
002d:Call ntdll.NtOpenKey(00329e84,00020019,00329d78) ret=7e598eaf
002d:Ret  ntdll.NtOpenKey() retval=00000000 ret=7e598eaf
002d:Call ntdll.RtlNtStatusToDosError(00000000) ret=7e598ebb
002d:Ret  ntdll.RtlNtStatusToDosError() retval=00000000 ret=7e598ebb
002d:Call advapi32.RegCloseKey(00000170) ret=7e59cf21
002d:Ret  advapi32.RegCloseKey() retval=00000000 ret=7e59cf21
002d:Call advapi32.RegQueryValueExW(00000174,7e666bd0
L"ThreadingModel",00000000,00329ba0,00329ba8,00329ba4) ret=7e59dd41
002d:Ret  advapi32.RegQueryValueExW() retval=00000000 ret=7e59dd41
002d:Call
advapi32.RegQueryValueExW(00000174,00000000,00000000,00329bac,0032999c,00329ba8)
ret=7e59b18a
002d:Ret  advapi32.RegQueryValueExW() retval=00000000 ret=7e59b18a
...
002d:Call KERNEL32.LoadLibraryExW(00329bee
L"C:\\PROG~FBU\\CMS\\WNDM~VNJ.OCX",00000000,00000008) ret=7e598ff2
...
002d:Call PE DLL (proc=0x2acb467,module=0x2ab0000
L"WNDM~VNJ.OCX",reason=PROCESS_ATTACH,res=(nil))
002d:Call KERNEL32.LocalAlloc(00000000,00002000) ret=02acb1be
...
--- snip ---

Winedbg:

--- snip ---
Wine-dbg>bt

Backtrace:
=>0 0x7b463443 MODULE_get_dll_load_path(module="C:\PROG~FBU\CMS\WNDM~VNJ.OCX",
safe_mode=0xffffffff)
[/home/focht/projects/wine/wine.repo/src/dlls/kernel32/module.c:974] in
kernel32 (0x00329ba8)
  1 0x7b463df7 LoadLibraryExW+0x73(libnameW=<couldn't compute location>,
hfile=<couldn't compute location>, flags=<couldn't compute location>)
[/home/focht/projects/wine/wine.repo/src/dlls/kernel32/module.c:1272] in
kernel32 (0x00329bd8)
  2 0x7e5a6ff2
COMPOBJ_DllList_Add+0x8d(library_name="C:\PROG~FBU\CMS\WNDM~VNJ.OCX",
ret=0x1c7dcf8)
[/home/focht/projects/wine/wine.repo/src/dlls/ole32/compobj.c:521] in ole32
(0x00329c48)
  3 0x7e5a8fe1 apartment_getclassobject+0x1d3(apt=0x1f7ed08,
dllpath="C:\PROG~FBU\CMS\WNDM~VNJ.OCX", apartment_threaded=0x1,
rclsid=0x32a3c4, riid=0x50bddc, ppv=0x32a060)
[/home/focht/projects/wine/wine.repo/src/dlls/ole32/compobj.c:1366] in ole32
(0x00329cb8)
  4 0x7e5abfbb get_inproc_class_object+0x1e1(apt=0x1f7ed08, regdata=0x329f8c,
rclsid=0x32a3c4, riid=0x50bddc, hostifnecessary=0x1, ppv=0x32a060)
[/home/focht/projects/wine/wine.repo/src/dlls/ole32/compobj.c:2954] in ole32
(0x00329f18)
  5 0x7e5ac42d CoGetClassObject+0x457(rclsid=<couldn't compute location>,
dwClsContext=<couldn't compute location>, pServerInfo=<couldn't compute
location>, iid=<couldn't compute location>, ppv=<couldn't compute location>)
[/home/focht/projects/wine/wine.repo/src/dlls/ole32/compobj.c:3094] in ole32
(0x0032a018)
  6 0x004b34b4 in cms (+0xb34b3) (0x0032a044)
  7 0x004b4d92 in cms (+0xb4d91) (0x0032a0e8)
  8 0x004b48d4 in cms (+0xb48d3) (0x0032a158)
  9 0x004b344b in cms (+0xb344a) (0x0032a1c8)
  10 0x004aeace in cms (+0xaeacd) (0x0032a224)
  11 0x004aebb1 in cms (+0xaebb0) (0x0032a298)
  12 0x004aef64 in cms (+0xaef63) (0x0032a2f0)
  13 0x004abc5d in cms (+0xabc5c) (0x0032a38c)
  14 0x0032a3b0 (0x0032a38c)
...
--- snip ---

Another session, showing the same dll present two times:

--- snip ---
Wine-dbg>info share
Module    Address            Debug info    Name (100 modules)
PE      340000-  36f000    Export          h264play
PE      370000-  380000    Export          dlldeinterlace
PE      380000-  391000    Export          streamreader
PE      3a0000-  3ab000    Export          password
PE      400000-  5c7000    Export          cms
PE      5d0000-  659000    Export          playback
PE      660000-  736000    Export          configmodule
PE      740000-  7ae000    Export          localrecord
PE     1480000- 14c4000    Export          wndmanager
PE     14d0000- 1571000    Export          mapctrl
PE     2380000- 23c4000    Deferred        wndm~vnj
PE    10000000-10083000    Export          netsdk
PE    5f400000-5f4f2000    Export          mfc42
ELF    7a800000-7a949000    Dwarf           opengl32<elf>
  \-PE    7a840000-7a949000    \               opengl32
ELF    7b400000-7b7f0000    Dwarf           kernel32<elf>
  \-PE    7b420000-7b7f0000    \               kernel32
ELF    7bc00000-7bd0a000    Dwarf           ntdll<elf>
  \-PE    7bc30000-7bd0a000    \               ntdll
ELF    7c000000-7c004000    Dwarf           <wine-loader>
...
--- snip ---

wndmanager -> 0x1480000 (by long path name "C:\\Program
Files\\CMS\\WndManager.ocx"))
wndm~vnj -> 0x2380000 (by short path name "C:\\PROG~FBU\\CMS\\WNDM~VNJ.OCX")

To avoid this, Wine loader 'load_native_dll' function needs to implement
additional checks after creating the file-backed section view, before doing
anything further (reloc fixups, allocating module entry, ...).

Essentially walk the current module list and for each entry:

* basic (quick) check: time/date stamp and image size against NT file header
data from the newly created section view (NOTE: 'alloc_module' currently
doesn't copy 'TimeDateStamp' from 'FileHeader.TimeDateStamp' which is needed
for comparison)
* memory compare IMAGE_NT_HEADERS
* use NtAreMappedFilesTheSame() to check if the views from the image sections
are backed by the same physical file

If these conditions are met, the existing module entry shall be used and the
new mapping must be destroyed (NtUnmapViewOfSection).

I've already tested this using an own prototype. It seems to work fine without
breaking other apps.

NOTE: It doesn't prevent the crash reported in bug 35106 (another issue,
unrelated to loader) but makes Wine more compliant with Windows OS loader
behaviour.

$ sha1sum General_CMS_Eng_V1.0.0.8.T.20101202.exe 
902e9408ebfc295ce2477fe3d9ca05e8a7588589 
General_CMS_Eng_V1.0.0.8.T.20101202.exe

$ du -sh General_CMS_Eng_V1.0.0.8.T.20101202.exe
4.7M    General_CMS_Eng_V1.0.0.8.T.20101202.exe

$ wine --version
wine-3.0-260-gdf715e5a9a

Regards

-- 
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