https://bugs.winehq.org/show_bug.cgi?id=39569
Bug ID: 39569 Summary: Multiple applications using ntdll.NtQueryObject 'ObjectNameInformation' info class get incorrect names for file objects Product: Wine Version: 1.7.54 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: ntdll Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
consider the following trace:
* explicitly removed relay excludes to show internal calls * Wine builtin as example
--- snip --- ... 002a:Call KERNEL32.CreateFileW(00326840 L"C:\windows\notepad.exe",80000000,00000001,00000000,00000003,00000000,00000000) ret=00b16742 002a:Call ntdll.RtlIsDosDeviceName_U(00326840 L"C:\windows\notepad.exe") ret=7f40b89ac80d 002a:Ret ntdll.RtlIsDosDeviceName_U() retval=00000000 ret=7f40b89ac80d 002a:Call ntdll.RtlDosPathNameToNtPathName_U(00326840 L"C:\windows\notepad.exe",0269d820,00000000,00000000) ret=7f40b89ac99c 002a:Ret ntdll.RtlDosPathNameToNtPathName_U() retval=00000001 ret=7f40b89ac99c 002a:Call ntdll.NtCreateFile(0269d808,80100000,0269d830,0269d810,00000000,00000000,00000001,f00000001,00000060,00000000,00000000) ret=7f40b89acb9b 002a:trace:ntdll:FILE_CreateFile handle=0x269d808 access=80100000 name=L"\??\C:\windows\notepad.exe" objattr=00000040 root=(nil) sec=(nil) io=0x269d810 alloc_size=(nil) attr=00000000 sharing=00000001 disp=1 options=00000060 ea=(nil).0x00000000 002a: create_file( access=80100000, attributes=00000040, sharing=00000001, create=1, options=00000060, attrs=00000000, objattr={rootdir=0000,sd={},name=L""}, filename="/home/focht/wine64/dosdevices/c:/windows/notepad.exe" ) 002a: create_file() = 0 { handle=0384 } 002a:Ret ntdll.NtCreateFile() retval=00000000 ret=7f40b89acb9b ... 002a:Ret KERNEL32.CreateFileW() retval=00000384 ret=00b16742 ... 002a:Call ntdll.NtQueryObject(00000384,00000001,00000000,00000000,0269d778) ret=00d222ab 002a:trace:ntdll:NtQueryObject (0x384,0x00000001,(nil),0x00000000,0x269d778) 002a: get_handle_unix_name( handle=0384 ) 002a: get_handle_unix_name() = 0 { name_len=52, name="/home/focht/wine64/dosdevices/c:/windows/notepad.exe" } 002a:Ret ntdll.NtQueryObject() retval=c0000004 ret=00d222ab ... 002a:Call ntdll.NtQueryObject(00000384,00000001,000e0da0,00002046,00000000) ret=00d222f7 002a:trace:ntdll:NtQueryObject (0x384,0x00000001,0xe0da0,0x00002046,(nil)) 002a: get_handle_unix_name( handle=0384 ) 002a: get_handle_unix_name() = 0 { name_len=52, name="/home/focht/wine64/dosdevices/c:/windows/notepad.exe" } 002a:Ret ntdll.NtQueryObject() retval=00000000 ret=00d222f7 ... 002a:Call KERNEL32.QueryDosDeviceW(0269d260 L"c:",0269d270,00000104) ret=00d216f4 002a:Call ntdll.RtlIsDosDeviceName_U(0269d260 L"c:") ret=7f40b8a11373 002a:Ret ntdll.RtlIsDosDeviceName_U() retval=00000000 ret=7f40b8a11373 ... 002a:Call ntdll.RtlInitUnicodeString(0269c910,027fddf0 L"\DosDevices\c:") ret=7f40b8a0cfba 002a:Ret ntdll.RtlInitUnicodeString() retval=0269c910 ret=7f40b8a0cfba 002a:Call ntdll.NtOpenSymbolicLinkObject(0269c908,00000001,0269c920) ret=7f40b8a0cfd9 002a:trace:ntdll:NtOpenSymbolicLinkObject (0x269c908,0x00000001,{name=L"\DosDevices\c:", attr=0x00000040, hRoot=(nil), sd=(nil)} 002a: open_symlink( access=00000001, attributes=00000040, rootdir=0000, name=L"\DosDevices\c:" ) 002a: open_symlink() = 0 { handle=038c } 002a:Ret ntdll.NtOpenSymbolicLinkObject() retval=00000000 ret=7f40b8a0cfd9 002a:Call ntdll.NtQuerySymbolicLinkObject(0000038c,0269c8f0,00000000) ret=7f40b8a0d016 002a:trace:ntdll:NtQuerySymbolicLinkObject (0x38c,0x269c8f0,(nil)) 002a: query_symlink( handle=038c ) 002a: query_symlink() = 0 { total=46, target_name=L"\Device\HarddiskVolume1" } 002a:Ret ntdll.NtQuerySymbolicLinkObject() retval=00000000 ret=7f40b8a0d016 002a:Call ntdll.NtClose(0000038c) ret=7f40b8a0d04c 002a: close_handle( handle=038c ) 002a: close_handle() = 0 002a:Ret ntdll.NtClose() retval=00000000 ret=7f40b8a0d04c ... 002a:Ret KERNEL32.QueryDosDeviceW() retval=00000019 ret=00d216f4 ... 002a:Call KERNEL32.CreateFileW(02086700 L"\??\C:\windows\notepad.exe",80000000,00000001,00000000,00000003,ffffffff00000000,00000000) ret=00b71906 002a:Call ntdll.RtlIsDosDeviceName_U(02086700 L"\??\C:\windows\notepad.exe") ret=7f40b89ac80d 002a:Ret ntdll.RtlIsDosDeviceName_U() retval=00000000 ret=7f40b89ac80d 002a:Call ntdll.RtlDosPathNameToNtPathName_U(02086700 L"\??\C:\windows\notepad.exe",0269c780,00000000,00000000) ret=7f40b89ac99c 002a:Ret ntdll.RtlDosPathNameToNtPathName_U() retval=00000001 ret=7f40b89ac99c 002a:Call ntdll.NtCreateFile(0269c768,80100000,0269c790,0269c770,00000000,00000000,00000001,f00000001,00000060,00000000,00000000) ret=7f40b89acb9b 002a:trace:ntdll:FILE_CreateFile handle=0x269c768 access=80100000 name=L"\??\Z:\??\C:\windows\notepad.exe" objattr=00000040 root=(nil) sec=(nil) io=0x269c770 alloc_size=(nil) attr=00000000 sharing=00000001 disp=1 options=00000060 ea=(nil).0x00000000 002a:warn:ntdll:FILE_CreateFile L"\??\Z:\??\C:\windows\notepad.exe" not found (c0000033) 002a:Ret ntdll.NtCreateFile() retval=c0000033 ret=7f40b89acb9b 002a:Call ntdll.RtlNtStatusToDosError(c0000033) ret=7f40b89acd3c 002a:Ret ntdll.RtlNtStatusToDosError() retval=0000007b ret=7f40b89acd3c ... 002a:Ret KERNEL32.CreateFileW() retval=ffffffffffffffff ret=00b71906 ... 002b:Call KERNEL32.CreateProcessW(02032b40 L"\??\C:\windows\notepad.exe",00000000,00000000,00000000,00000000,00000013,00000000,02061e60 L"\??\C:\windows",00f6d000,00f6cef8) ret=00ef2163 002b:Call ntdll.RtlIsDosDeviceName_U(02abd720 L"\??\C:\windows\notepad.exe") ret=7f40b89ac80d 002b:Ret ntdll.RtlIsDosDeviceName_U() retval=00000000 ret=7f40b89ac80d 002b:Call ntdll.RtlDosPathNameToNtPathName_U(02abd720 L"\??\C:\windows\notepad.exe",02abcfb0,00000000,00000000) ret=7f40b89ac99c 002b:Ret ntdll.RtlDosPathNameToNtPathName_U() retval=00000001 ret=7f40b89ac99c 002b:Call ntdll.NtCreateFile(02abcf98,80100000,02abcfc0,02abcfa0,00000000,00000000,00000005,00000001,00000060,00000000,00000000) ret=7f40b89acb9b 002b:trace:ntdll:FILE_CreateFile handle=0x2abcf98 access=80100000 name=L"\??\Z:\??\C:\windows\notepad.exe" objattr=00000040 root=(nil) sec=(nil) io=0x2abcfa0 alloc_size=(nil) attr=00000000 sharing=00000005 disp=1 options=00000060 ea=(nil).0x00000000 002b:warn:ntdll:FILE_CreateFile L"\??\Z:\??\C:\windows\notepad.exe" not found (c0000033) 002b:Ret ntdll.NtCreateFile() retval=c0000033 ret=7f40b89acb9b ... 002b:Call ntdll.RtlGetFullPathName_U(02abd720 L"\??\C:\windows\notepad.exe",00000410,02abd190,02abd110) ret=7f40b89dd82b 002b:Ret ntdll.RtlGetFullPathName_U() retval=00000038 ret=7f40b89dd82b ... 002b:Ret KERNEL32.CreateProcessW() retval=00000000 ret=00ef2163 --- snip ---
Calling 'kernel32' API with native NT paths is obviously wrong and not intended here.
The relay trace partially corresponds to the following source code - at least for the preceding device/filename lookup/translation:
https://bitbucket.org/mrexodia/devicenameresolver/src/3fc6704267d469bd08633e...
--- snip --- ... __declspec(dllexport) bool DevicePathFromFileHandleW(HANDLE hFile, wchar_t* szDevicePath, size_t nSize) { NativeWinApi::initialize(); ULONG ReturnLength; bool bRet=false; if(NativeWinApi::NtQueryObject(hFile, ObjectNameInformation, 0, 0, &ReturnLength)==STATUS_INFO_LENGTH_MISMATCH) { ReturnLength+=0x2000; //on Windows XP SP3 ReturnLength will not be set just add some buffer space to fix this POBJECT_NAME_INFORMATION NameInformation=(POBJECT_NAME_INFORMATION)GlobalAlloc(0, ReturnLength); if(NativeWinApi::NtQueryObject(hFile, ObjectNameInformation, NameInformation, ReturnLength, 0)==STATUS_SUCCESS) { NameInformation->Name.Buffer[NameInformation->Name.Length/2]=L'\0'; //null-terminate the UNICODE_STRING if(wcslen(NameInformation->Name.Buffer)<nSize) { wcscpy_s(szDevicePath, nSize/sizeof(wchar_t), NameInformation->Name.Buffer); bRet=true; } } GlobalFree(NameInformation); } if(_wcsnicmp(szDevicePath, L"\Device\LanmanRedirector\", 25) == 0) // Win XP { wcscpy_s(szDevicePath, nSize / sizeof(wchar_t), L"\\"); wcscat_s(szDevicePath, nSize / sizeof(wchar_t), &szDevicePath[25]); } else if(_wcsnicmp(szDevicePath, L"\Device\Mup\", 12) == 0) // Win 7 { wcscpy_s(szDevicePath, nSize / sizeof(wchar_t), L"\\"); wcscat_s(szDevicePath, nSize / sizeof(wchar_t), &szDevicePath[12]); } return bRet; }
__declspec(dllexport) bool DevicePathFromFileHandleA(HANDLE hFile, char* szDevicePath, size_t nSize) { DynBuf newDevicePathBuf(nSize*sizeof(wchar_t)); wchar_t* newDevicePath = (wchar_t*)newDevicePathBuf.GetPtr(); if(!DevicePathFromFileHandleW(hFile, newDevicePath, nSize*sizeof(wchar_t))) return false; if(!WideCharToMultiByte(CP_ACP, NULL, newDevicePath, -1, szDevicePath, (int)wcslen(newDevicePath)+1, NULL, NULL)) return false; return true; }
__declspec(dllexport) bool PathFromFileHandleW(HANDLE hFile, wchar_t* szPath, size_t nSize) { typedef DWORD (WINAPI* GETFINALPATHNAMEBYHANDLEW) ( IN HANDLE hFile, OUT wchar_t* lpszFilePath, IN DWORD cchFilePath, IN DWORD dwFlags ); static GETFINALPATHNAMEBYHANDLEW GetFPNBHW=(GETFINALPATHNAMEBYHANDLEW)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetFinalPathNameByHandleW"); if(GetFPNBHW && GetFPNBHW(hFile, szPath, (DWORD)(nSize/sizeof(wchar_t)), 0)) { if(_wcsnicmp(szPath, L"\\?\UNC\", 8) == 0) // Server path { wcscpy_s(szPath, nSize / sizeof(wchar_t), L"\\"); wcscat_s(szPath, nSize / sizeof(wchar_t), &szPath[8]); } else if(_wcsnicmp(szPath, L"\\?\", 4) == 0 && szPath[5]==L':') // Drive path { wcscpy_s(szPath, nSize/sizeof(wchar_t), &szPath[4]); } return true; } if(!DevicePathFromFileHandleW(hFile, szPath, nSize)) return false; std::wstring oldPath(szPath); if (!DevicePathToPathW(szPath, szPath, nSize)) wcscpy_s(szPath, nSize / sizeof(wchar_t), oldPath.c_str()); return true; } ... --- snip ---
https://bitbucket.org/mrexodia/devicenameresolver/src/3fc6704267d469bd08633e...
--- snip --- ... bool DeviceNameResolver::resolveDeviceLongNameToShort(const TCHAR * sourcePath, TCHAR * targetPath) { for (unsigned int i = 0; i < deviceNameList.size(); i++) { if (!_tcsnicmp(deviceNameList.at(i).longName, sourcePath, deviceNameList.at(i).longNameLength) && sourcePath[deviceNameList.at(i).longNameLength]==TEXT('\')) { _tcscpy_s(targetPath, MAX_PATH, deviceNameList.at(i).shortName); _tcscat_s(targetPath, MAX_PATH, sourcePath + deviceNameList.at(i).longNameLength); return true; } } return false; } ... --- snip ---
In short: Wine returns incorrect names for file objects in 'NtQueryObject'.
The app code passes 'C:\windows\notepad.exe', gets '\??\C:\windows\notepad.exe' and tries to translate the path to short form, removing the device name part by matching against device list.
This fails hence the app uses whatever was returned by 'NtQueryObject' as fallback (= native NT path).
The proper object name would have been:
'\Device\HarddiskVolume1\Windows\notepad.exe'
Source: https://source.winehq.org/git/wine.git/blob/71080cc0818e8cc3e8be0c30bcc6602b...
Another article (just to reference the list from second answer):
https://stackoverflow.com/questions/65170/how-to-get-name-associated-with-op... ("How to get name associated with open HANDLE")
--- quote --- ... // returns // "\Device\HarddiskVolume3" (Harddisk Drive) // "\Device\HarddiskVolume3\Temp" (Harddisk Directory) // "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File) // "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick) // "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume) // "\Device\Floppy0\Autoexec.bat" (Floppy disk) // "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive) // "\Device\Serial1" (real COM port) // "\Device\USBSER000" (virtual COM port) // "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7) // "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP) // "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP) // "\Device\Afd" (internet socket) // "\Device\Console000F" (unique name for any Console handle) // "\Device\NamedPipe\Pipename" (named pipe) // "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore) // "\REGISTRY\MACHINE\SOFTWARE\Classes.txt" (HKEY_CLASSES_ROOT.txt) ... --- quote ---
$ wine --version wine-1.7.54-212-gf97ac58
Regards
https://bugs.winehq.org/show_bug.cgi?id=39569
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |source
https://bugs.winehq.org/show_bug.cgi?id=39569
Qian Hong fracting@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |fracting@gmail.com
https://bugs.winehq.org/show_bug.cgi?id=39569
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |download URL| |https://github.com/x64dbg/x | |64dbg/releases/download/sna | |pshot/snapshot_2018-02-24_1 | |3-52.zip
--- Comment #1 from Anastasius Focht focht@gmx.net --- Hello folks,
revisiting, still present.
You have to revert Wine commit https://source.winehq.org/git/wine.git/commitdiff/6e12aba9c7f0fef8d2b1b7202a... ("kernel32: Implement GetFinalPathNameByHandle.") to get to the fallback code path for reproducing with 'x64dbg'.
Not much activity compared to my original post in the third-party library 'devicenameresolver' git repo, so the original issue can be still cleanly reproduced (only one bugfix https://bitbucket.org/mrexodia/devicenameresolver/commits/0850d88fa6a759d79b...).
https://bitbucket.org/mrexodia/devicenameresolver/src/0850d88fa6a759d79b3c85...
$ sha1sum snapshot_2018-02-24_13-52.zip 1d64a63153aad56e23c626dcdd9a0fd684f9064b snapshot_2018-02-24_13-52.zip
$ du -sh snapshot_2018-02-24_13-52.zip 33M snapshot_2018-02-24_13-52.zip
$ wine --version wine-3.3
Regards
https://bugs.winehq.org/show_bug.cgi?id=39569
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL|https://github.com/x64dbg/x |https://sourceforge.net/pro |64dbg/releases/download/sna |jects/x64dbg/files/snapshot |pshot/snapshot_2018-02-24_1 |s/ |3-52.zip |
--- Comment #2 from Anastasius Focht focht@gmx.net --- Hello folks,
revisiting, still present.
Prerequisites:
* native 'msvcr120' override (bug 45916) * hide 'GetFinalPathNameByHandle{A,W}' exports in '{kernel32,kernelbase}.spec' to get fallback code path
--- snip --- $ WINEDLLOVERRIDES=msvcr120=n WINEDEBUG=+seh,+relay,+ntdll wine ./x64dbg.exe "c:\windows\notepad.exe" >>log.txt 2>&1 ...
$ egrep "(.*notepad.*not found)" log.txt ... 003f:warn:ntdll:FILE_CreateFile L"\??\Z:\??\C:\windows\notepad.exe" not found (c0000033) 003f:warn:ntdll:NtQueryAttributesFile L"\??\Z:\??\C:\windows\notepad.exe" not found (c0000033) ... --- snip ---
$ sha1sum snapshot_2019-09-01_17-37.zip ba592cee33d3d43b8632331214f7d222544b3550 snapshot_2019-09-01_17-37.zip
$ du -sh snapshot_2019-09-01_17-37.zip 31M snapshot_2019-09-01_17-37.zip
$ wine --version wine-4.15-104-g765815729f
Regards
https://bugs.winehq.org/show_bug.cgi?id=39569
Zebediah Figura z.figura12@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |z.figura12@gmail.com
--- Comment #3 from Zebediah Figura z.figura12@gmail.com --- There are a number of difficulties I see in solving this:
(1) We currently rely on mountmgr to create devices, including determining the device type. But we run into a bootstrapping problem, because various components (including wineboot) need to access the C: and Z: drives before mountmgr is even started.
One way to solve this is to retain the current symlinks and drive resolution logic from ntdll, and fall back to it if creating a device on the server side fails.
Another way to solve this is to create at least the C and Z volumes from the server side. Probably in that case we'd want the server to have a server-side "volume" device object, that mountmgr creates? Or maybe mountmgr should somehow replace the drives or the symlinks once it's started?
(2) Generally, retaining current performance. While feeding file operations directly through to mountmgr as if it were any other NT kernel driver would work fine, the overhead is pretty significant (RPC to the server for each file operation, and another essentially two RPCs from mountmgr to the server.) This can be improved dramatically by keeping unix fds cached the same way we do now (I guess we'd want a way for mountmgr to tell the server to associate a given unix fd with its device file?) but that doesn't help create or close requests (which require a server call anyway, but at least it was just one server call).
The most obvious way to solve this is, as was partially proposed to fix problem (1), to have the server keep track of volumes specially. In that case I'm not even sure mountmgr would create volume devices directly, but rather it would probably go through some "create_volume" server request, passing through the mount point.
https://bugs.winehq.org/show_bug.cgi?id=39569
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- URL|https://sourceforge.net/pro |https://web.archive.org/web |jects/x64dbg/files/snapshot |/20210725105024/https://cfh |s/ |cable.dl.sourceforge.net/pr | |oject/x64dbg/snapshots/snap | |shot_2019-09-01_17-37.zip