WineHQ
Bug Tracking Database – Bug 45749

 Bugzilla

 

Last modified: 2018-09-02 16:18:37 CDT  

Visual Studio 2017 Installer needs NtQueryInformationFile to handle FileModeInformation

Bug 45749 - Visual Studio 2017 Installer needs NtQueryInformationFile to handle FileModeInformation
Visual Studio 2017 Installer needs NtQueryInformationFile to handle FileModeI...
Status: NEW
AppDB: Show Apps affected by this bug
Product: Wine
Classification: Unclassified
Component: ntdll
3.14
x86-64 Linux
: P2 normal
: ---
Assigned To: Mr. Bugs
https://visualstudio.microsoft.com/do...
: download, patch
Depends on:
Blocks: 45757
  Show dependency tree
 
Reported: 2018-09-01 05:58 CDT by jimbo1qaz
Modified: 2018-09-02 16:18 CDT (History)
1 user (show)

See Also:
Regression SHA1:
Fixed by SHA1:
Distribution: ---
Staged patchset:


Attachments
Visual Studio Installer logs (mostly useless) (10.59 KB, text/plain)
2018-09-01 05:58 CDT, jimbo1qaz
Details
Hack to handle FileModeInformation in NtQueryInformationFile (1.69 KB, patch)
2018-09-02 10:58 CDT, Fabian Maurer
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description jimbo1qaz 2018-09-01 05:58:44 CDT
Created attachment 62190 [details]
Visual Studio Installer logs (mostly useless)

Running Kubuntu 18.04 64-bit. I created a 32-bit .wine32 prefix, installed dotnet46 via winetricks, then the Build Tools for Visual Studio installer (https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017 ) (which shares the core with VS2017 installer. It's written in node.js and involves multiple processes communicating via what the source describes as "pipe"s).

The error is "Hub Controller process exited prematurely with exit code 1 (NodeUncaughtFatalException)."

Interestingly, the installer's source is user-readable and located in WINEPREFIX / drive_c/Program Files/Microsoft Visual Studio/Installer/resources/app/node_modules/microsoft-servicehub/host/. I was able to edit the .js files to add control flow logging:

const fs = require('fs');
function logg(val, args){
        fs.writeFileSync("C:\\"+process.hrtime()[1] + '_'+val, ""+args, function(err) {});
}

Installer seems to be codenamed "microsoft-servicehub". Strangely, many of the Installer files appear to test for Windows and/or *nix, despite VS being Windows-only. It also references open-source VS Code, but I was unable to find any hits when Googling for quoted strings.

----------

The error message "Hub Controller process exited prematurely with exit code" is raised from within "drive_c/Program Files/Microsoft Visual Studio/Installer/resources/app/node_modules/microsoft-servicehub/controllerConnection.js":

ControllerConnection.prototype.startController `var connectPromise` fails, calling `function (err)` to spawn a process (which crashes).

ControllerConnection.prototype.onControllerProcessExited is responsible for noticing the process has terminated, and raising the exception.

---------

The actual crash originates from "drive_c/Program Files/Microsoft Visual Studio/Installer/resources/app/node_modules/microsoft-servicehub/host/HubController.js".

HubController.js is executed by several distinct processes, and `process.pid` is the same within `function HubController()` and the global scope.

I systematically replaced all calls to `logger.error()` (info, etc.) to `logg()` (since Logger.error() called within subprocess did not show up in Visual Studio's debug logs). I get two sequentially numbered files, 1 millisecond apart:

"429988399_Successfully started server on pipe 5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"
"430873100_Terminating controller due to some unknown error: " with contents "listen EINVAL \\?\pipe\5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"

This error is thrown by `function retryStartServer`. `e.code` is EINVAL, while the code only handles `e.code === ec.EADDRINUSE.code`.

Looking at https://stackoverflow.com/questions/11040460/get-the-listen-einval-when-starting-up-node-js-server-why suggests:

> > EINVAL The socket is already connected.
> Make sure something isn't already listening on that port.

`man errno` suggests otherwise: > EINVAL Invalid argument (POSIX.1-2001).

Adding `|| e.code === ec.EINVAL.code` and commenting `process.platform === 'win32' ||` sends the installer into an endless "kill and restart HubController" loop, which isn't what we want.

Extra notes on control flow at https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#server

--------

The `EINVAL` error comes with a node.js stack trace: https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#file-networkstacktrace-txt

(The hexadecimal path originates from https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#deterministic-pipe-names )

https://github.com/nodejs/node/blob/68dff4a67b7222770f90fc5d9bdd884f8db4a24b/lib/internal/errors.js#L340
`const ex = new Error(`${syscall} ${code}${details}`);`
"listen EINVAL \\?\pipe\5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610"

I think the call which failed is `listen`, which returns EINVAL for some reason.

------------

Comparing my stack trace with node.js source code (node.dll 8.9.3):

`Server.listen (net.js:1487:5)` = https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1487
- listenInCluster(server=this, address=pipeName, port=-1, addressType=-1,
    backlog=backlog, fd=undefined, exclusive=options.exclusive);
`listenInCluster (net.js:1392:12)` = https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1392
- server._listen2(=address, =port, =addressType, =backlog, =fd);

Unpeeling layers of abstraction on top of Windows named pipes:

`Server.setupListenHandle [as _listen2] (net.js:1351:14)` = https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1351

(Bullet points are not in stack trace, but I assume they executed)
- `handle = new Pipe(PipeConstants.SERVER);` https://github.com/nodejs/node/blob/v8.9.3/lib/net.js#L1267
  - `const { Pipe } = process.binding('pipe_wrap');` which refers to C++ code at https://github.com/nodejs/node/blob/v8.9.3/src/pipe_wrap.cc
    - pipes are handled by https://github.com/libuv/libuv

- `var err = this._handle.listen(backlog || 511);` is the operation which fails = https://github.com/nodejs/node/blob/8a44289089a08b7b19fa3c4651b5f1f5d1edd71b/lib/net.js#L1348`
- Routes to https://github.com/nodejs/node/blob/v8.9.3/src/pipe_wrap.cc#L154
- which calls uv_listen(). `return uv_translate_sys_error(err);` and EINVAL is located at https://github.com/libuv/libuv/blob/v1.x/src/win/error.c#L101 ... There are 7 Win32 errors all mapping to UV_EINVAL==EINVAL, but only WSAEINVAL can happen.
- uv_listen() either [assert(0) and sets err=ERROR_INVALID_PARAMETER] (I hope not), calls uv_tcp_listen (I hope not)
- Most likely calls uv_pipe_listen() = https://github.com/libuv/libuv/blob/1391a3d9d0996fcf1a116a9c6c58e8b1c7303193/src/win/pipe.c#L938
- It has 4 return paths, only `if (!(handle->flags & UV_HANDLE_BOUND)) { return WSAEINVAL;` is happening = https://github.com/libuv/libuv/blob/1391a3d9d0996fcf1a116a9c6c58e8b1c7303193/src/win/pipe.c#L947
- What initializes `handle->flags` without |=UV_HANDLE_BOUND?

ok i'm getting a headache
Comment 1 jimbo1qaz 2018-09-01 06:12:41 CDT
Corrections:
- `handle = new Pipe();` (PipeConstants.SERVER did not exist in node 8.9.3)
- node.js ships libuv. The relevant portion is located at "deps/uv/src/win/pipe.c".

How is `handle` initialized, and why is `handle->flags` lacking |=UV_HANDLE_BOUND?

handle = new Pipe();
- uv_pipe_init()

(unsure if called) if (!isNaN(instances)) { handle.setPendingInstances(instances);
- uv_pipe_pending_instances() seems unimportant

handle.bind(address="\\?\pipe\5d74...", port=-1);
- uv_pipe_bind() sets `handle->flags |= UV_HANDLE_BOUND;` if no errors occur (and no errors occur in net.js/createServerHandle())

rval = createServerHandle(...
this._handle = rval;
this._handle.onconnection = onconnection;
this._handle.owner = this;
var err = this._handle.listen(backlog || 511);
err == EINVAL

So why isn't UV_HANDLE_BOUND set?? i don't have a clue.
Comment 2 jimbo1qaz 2018-09-01 06:21:31 CDT
Also Microsoft, fix your VS2017 subprocess logging, right now they're delivered to `users/jimbo1qaz/Temp/servicehub/logs/hubController-...log` but not incorporated into the failure logs accessible through the GUI.

Also the failed installer window has no close button. I assume this is another Wine bug.
Comment 3 Fabian Maurer 2018-09-01 07:00:07 CDT
Confirming.
Comment 4 jimbo1qaz 2018-09-02 00:10:33 CDT
i think trying to debug a spawned process is futile. instead let winedbg or x32dbg-in-wine launch the process itself.

Unfortunately vs_installershell.exe kept popping up a win32 dialog box error and/or argument error. inspecting servicehub.config.json reveals:
..."additionalEnvironmentVariables" : { "ELECTRON_RUN_AS_NODE": "true" }

I have constructed a method of debugging the subprocess directly via winedbg:

cd WINEPREFIX/drive_c/Program Files/Microsoft Visual Studio/Installer/resources/app
ELECTRON_RUN_AS_NODE=true winedbg "C:\Program Files\Microsoft Visual Studio\Installer\vs_installershell.exe" ./node_modules/microsoft-servicehub/host/HubController.js 5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610

Then type:

b uv_pipe_init
b uv_pipe_pending_instances
b uv_pipe_bind
b uv_listen
b uv_pipe_listen
c

Sadly uv_pipe_listen cannot be breakpointed (all others work fine).

--------

uv_pipe_init get called 4 times.
uv_pipe_bind gets called once.
The process terminates and uv_listen is not called.

I am currently debugging uv_pipe_init via winedbg, using https://github.com/nodejs/node/blob/v8.9.3/deps/uv/src/win/pipe.c as a reference.
Comment 5 jimbo1qaz 2018-09-02 04:50:51 CDT
I wiped my Javascript logging hooks completely.

When running the process in a debugger, node.js createServerHandle() seems to fail in node.js handle.bind() -> C++ something -> libuv uv_pipe_bind->uv_set_pipe_handle (I didn't debug uv_set_pipe_handle).

This winedbg command breaks on uv_set_pipe_handle:

b *0x10709ba0
c

uv_set_pipe_handle() returns nonzero, so does uv_pipe_bind and handle.bind(). If `handle.bind()` fails, https://github.com/nodejs/node/blob/v8.9.1/lib/net.js#L1292
`createServerHandle()` returns an error code and slh constructs an error at https://github.com/nodejs/node/blob/v8.9.1/lib/net.js#L1334 . Or one would think...

----------

But my stack trace indicates `createServerHandle()` succeeded and an exception was thrown later on net.js:1351 = https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#file-networkstacktrace-txt-L4

This is regardless of "heavily modded installer", "deleted and redownloaded installer plus 1 logger hook", and "the latter plus launch HubController.js subprocess via unix wine", or "winedbg with breakpoint when the function fails".

-----------

Debugging Node's internal javascript: passing --inspect-brk to vs_installershell.exe then debugging via Chromium! Except breakpoints don't work, even when I symlink /C: to drive_c... Then it started working... (Sadly about:inspect "automatic connection" and NIM don't work on VS Installer)

I guess I don't need to run Chrome under Wine (https://gist.github.com/jimbo1qaz/1c972422fd5c31706920b9be5c2a0f42#chrome-under-wine )

--------

I managed to combine winedbg and Chrome debugging.

cd WINEPREFIX/drive_c/Program Files/Microsoft Visual Studio/Installer/resources/app ; ELECTRON_RUN_AS_NODE=true winedbg "C:\Program Files\Microsoft Visual Studio\Installer\vs_installershell.exe" --inspect-brk ./node_modules/microsoft-servicehub/host/HubController.js 5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610

Since I can't see Node's debug "key" url in stdout, I must look at http://localhost:9229/json/list to connect to node instead.

Interesting issue within uv_pipe_bind():
- uv_set_pipe_handle returns -1 or 0xFFFFFFFF.
- Yet Wine's GetLastError(); returns 0, so all callers and Node thinks nothing went wrong creating the named pipe.

  if (uv_set_pipe_handle(loop,
                         handle,
                         handle->pipe.serv.accept_reqs[0].pipeHandle,
                         -1,
                         0)) {
    err = GetLastError();

I am convinced that uv_set_pipe_handle() calling Wine is directly responsible for this bug.

(am I a bit obsessed with debugging this issue?)
Comment 6 jimbo1qaz 2018-09-02 06:38:16 CDT
(Unrelated tangent: I tried debugging vs_installershell.exe in x64dbg x32dbg.exe, but it crashes on Wine 3.14 (Unhandled exception: unimplemented function msvcr120.dll.?wait@Concurrency@@YAXI@Z called in 32-bit code (0x7b43d3bc).). Overriding with real msvcr120.dll fixes the crash.

Then when I open vs_installershell.exe, it crashes with a near-null pointer exception (which you're not supposed to report while using DLL overrides). Haven't verified it doesn't crash on Windows.

-----------

I decided it would be less unpleasant (compared to looking at asm) to log all Wine system calls while looking at libuv uv_set_pipe_handle() source code. I should've done this from the very beginning.

ELECTRON_RUN_AS_NODE=true WINEDEBUG=+relay winedbg "C:\Program Files\Microsoft Visual Studio\Installer\vs_installershell.exe" ./node_modules/microsoft-servicehub/host/HubController.js 5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610 2> ~/relay

System calls relevant to uv_set_pipe_handle():

0081:Call KERNEL32.SetNamedPipeHandleState(00000118,0033eb50,00000000,00000000) ret=10709bec
0081:Ret  KERNEL32.SetNamedPipeHandleState() retval=00000001 ret=10709bec

  if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) {
if not 1: something went wrong.
so nothing went wrong.

--------

0081:Call ntdll.NtQueryInformationFile(00000118,0033eb40,0033eb4c,00000004,00000010) ret=10709c62
0081:Ret  ntdll.NtQueryInformationFile() retval=c0000024 ret=10709c62

  nt_status = pNtQueryInformationFile(pipeHandle,
                                      &io_status,
                                      &mode_info,
                                      sizeof(mode_info),
                                      FileModeInformation);
  if (nt_status != STATUS_SUCCESS) {
    return -1;
  }

Is the absence of SetLastError a bug on libuv's part, or does it expect Wine's API to set said error flag? Either way uv_set_pipe_handle returns 0 to uv_pipe_bind to uv_bind to "net.js" handle.bind(address, port) to createServerHandle() despite failing.

---------

Issue: NtQueryInformationFile is returning STATUS_OBJECT_TYPE_MISMATCH on what I assume is a named pipe.

> 0xC0000024
> STATUS_OBJECT_TYPE_MISMATCH
> {Wrong Type} There is a mismatch between the type of object that is required by the requested operation and the type of object that is specified in the request.


https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntqueryinformationfile
https://source.winehq.org/WineAPI/NtQueryInformationFile.html
https://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FFile%2FNtQueryInformationFile.html (presses Ctrl+U) There's something so anachronistic about the combination of Cloudflare scripts with the retro aesthetic of chm2web copyright 2002-2005...


ELECTRON_RUN_AS_NODE=true winedbg "C:\Program Files\Microsoft Visual Studio\Installer\vs_installershell.exe" ./node_modules/microsoft-servicehub/host/HubController.js 5d74f51d8cc5d690c261c26d3e9e3408e060bd352875558b9320be9c213e0610

b uv_pipe_bind
c
b NtQueryInformationFile
c

Don't run `next`, you'll get:
00e8:fixme:winedbg:be_i386_is_jump unknown 8b (wait 1 second)
00e8:fixme:winedbg:be_i386_is_jump unknown 55 (wait 1 second)
and an endless flood of these messages.

libuv calls NtQueryInformationFile with FILE_INFORMATION_CLASS = FileModeInformation.

I couldn't view source for ntdll, even after installing `sudo apt install wine-devel-dbg`. I think NtQueryInformationFile *jumps to* (not calls) server_get_file_info, passing in `FileModeInformation`. Afterwards, server_get_file_info *jumps to* a hardcoded return EIP.

The call to `io->u.Status = wine_server_call( req );` (https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/file.c#L209 ) leaves EAX with a value of c0000024, which is returned from wine_server_call and from NtQueryInformationFile.

FileModeInformation barely appears in the Wine source code, I suspect it's unimplemented (which explains "There is a mismatch between the type of object that is required by the requested operation and the type of object that is specified in the request"). But libuv uses FileModeInformation and expects a successful response.
Comment 7 Fabian Maurer 2018-09-02 10:58:53 CDT
Created attachment 62200 [details]
Hack to handle FileModeInformation in NtQueryInformationFile

FWIW, chromium issues can be worked around with my patches at bug 45649, bug 45650 and bug 45645 - so you don't need to disable the sandbox. But that's not very relevant for this issue, I think.

> (am I a bit obsessed with debugging this issue?)

I dunno, but you did a very good job, thank you for that!
It's indeed NtQueryInformationFile with NtQueryInformationFile that's missing here. I'm attaching a quick hack (on top of wine git) to work around this issue.
Comment 8 Fabian Maurer 2018-09-02 11:06:17 CDT
After applying the workaround, the installer starts, but still doesn't work. See bug 45757 for the "The installer manifest failed signature validation" issue.
Comment 9 jimbo1qaz 2018-09-02 16:06:57 CDT
Fun fact: one forum user commented on my debugging process, "I'll take *Nightmare Fuel* for $2000, Alex."

Where should I report documentation issues? (Maybe you should delete `symbolfile pathname` from https://wiki.winehq.org/Wine_Developer%27s_Guide/Debugging_Wine#Directory_.26_source_file_manipulation , considering the function has been stubbed out completely. How *do* we load symbols from EXE files?)

Additionally I don't think I want to spend the effort recompiling Wine in order to debug further.
Comment 10 Fabian Maurer 2018-09-02 16:18:37 CDT
> Where should I report documentation issues?

AFAIK just in the bugtracker here should be fine. I sadly don't know much about winedbg, almost never use it.
What exactly are you trying to get from the symbols?

> Additionally I don't think I want to spend the effort recompiling Wine in
> order to debug further.

I can understand that. Although it's not that hard, when you got it to build once. Maybe we can get a fix into one of the next development releases.


Privacy Policy
If you have a privacy inquiry regarding this site, please write to privacy@winehq.org

Hosted By CodeWeavers