conhost can take over keyboard keys when launched from .exe symbolic link #10647

Closed
opened 2026-01-31 02:26:28 +00:00 by claunia · 12 comments
Owner

Originally created by @DHowett on GitHub (Sep 17, 2020).

Originally assigned to: @miniksa on GitHub.

You're going to think this is crazy talk, but hear me out.

Using Vb (19041) conhost, do this:

cmd

mklink %USERPROFILE%\cmd_link.exe c:\windows\system32\cmd.exe

Open up File Explorer to %USERPROFILE% and double-click cmd_link.exe.

If you have a repro, any time you press L that console window will come to the foreground.

Wild, right?

It looks like the shell invokes cmd_link.exe with STARTF_TITLEISLINKNAME and an lpTitle of ...\cmd_link.exe.

STARTF_TITLEISLINKNAME: The lpTitle member contains the path of the shortcut file (.lnk) that the user invoked to start this process. This is typically set by the shell when a .lnk file pointing to the launched application is invoked. Most applications will not need to set this value.

It turns out that this excerpt from the documentation is a fabrication, and lpTitle might point to an .exe file.

Anyway, when the title is the "link name", conhost uses the title to find the .lnk file. It gets in here:

f91b53d5fd/src/interactivity/win32/SystemConfigurationProvider.cpp (L94-L106)

This fails. s_GetLinkValues attempts to get an IShellLink... for what is clearly not a .lnk file. However, check out that last parameter.

It's never cleared. It's filled with stack garbage.

Moments later, we do this:

f91b53d5fd/src/interactivity/win32/SystemConfigurationProvider.cpp (L114-L115)

...which sets up the hotkey that will later be used to activate the window, down here:

f91b53d5fd/src/interactivity/win32/window.cpp (L350-L353)

We should probably zero-initialize wHotKey.

Originally created by @DHowett on GitHub (Sep 17, 2020). Originally assigned to: @miniksa on GitHub. You're going to think this is crazy talk, but hear me out. Using Vb (19041) conhost, do this: ### cmd ``` mklink %USERPROFILE%\cmd_link.exe c:\windows\system32\cmd.exe ``` Open up File Explorer to `%USERPROFILE%` and _double-click `cmd_link.exe`_. If you have a repro, any time you press <kbd>L</kbd> that console window will come to the foreground. Wild, right? It looks like the shell invokes `cmd_link.exe` with `STARTF_TITLEISLINKNAME` and an `lpTitle` of `...\cmd_link.exe`. > **`STARTF_TITLEISLINKNAME`**: The `lpTitle` member contains the path of the shortcut file (.lnk) that the user invoked to start this process. This is typically set by the shell when a .lnk file pointing to the launched application is invoked. Most applications will not need to set this value. It turns out that this [excerpt from the documentation](https://docs.microsoft.com/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa) is a fabrication, and `lpTitle` might point to an `.exe` file. Anyway, when the title is the "link name", conhost uses the title to find the `.lnk` file. It gets in here: https://github.com/microsoft/terminal/blob/f91b53d5fdc5b20387e297357668f5f14a795d4c/src/interactivity/win32/SystemConfigurationProvider.cpp#L94-L106 This fails. `s_GetLinkValues` attempts to get an `IShellLink...` for what is clearly not a `.lnk` file. However, check out that last parameter. It's never cleared. It's filled with stack garbage. Moments later, we do this: https://github.com/microsoft/terminal/blob/f91b53d5fdc5b20387e297357668f5f14a795d4c/src/interactivity/win32/SystemConfigurationProvider.cpp#L114-L115 ...which sets up the hotkey that will later be used to activate the window, down here: https://github.com/microsoft/terminal/blob/f91b53d5fdc5b20387e297357668f5f14a795d4c/src/interactivity/win32/window.cpp#L350-L353 We should probably zero-initialize `wHotKey`.
Author
Owner

@DHowett commented on GitHub (Sep 17, 2020):

It hits randomly because, well, it's stack memory. Sometimes it lines up and happens to be L, and sometimes it stays \0 and everything is fine.

@DHowett commented on GitHub (Sep 17, 2020): It hits randomly because, well, it's stack memory. Sometimes it lines up and happens to be `L`, and sometimes it stays `\0` and everything is fine.
Author
Owner

@DHowett commented on GitHub (Sep 17, 2020):

Notes for future reproducers: It doesn't reproduce at all in a debug build. You will likely figure out why.

@DHowett commented on GitHub (Sep 17, 2020): Notes for future reproducers: It doesn't reproduce at all in a debug build. You will likely figure out why.
Author
Owner

@dmex commented on GitHub (Sep 17, 2020):

We should probably zero-initialize wHotKey.
It doesn't reproduce at all in a debug build.

You can reproduce uninitialized variables more frequently by disabling ASLR for the debug binaries. Add to the debug vcxproj:

<Link>
    <RandomizedBaseAddress>false</RandomizedBaseAddress>
</Link>
@dmex commented on GitHub (Sep 17, 2020): > We should probably zero-initialize wHotKey. > It doesn't reproduce at all in a debug build. You can reproduce uninitialized variables more frequently by disabling ASLR for the debug binaries. Add to the debug vcxproj: ``` <Link> <RandomizedBaseAddress>false</RandomizedBaseAddress> </Link> ```
Author
Owner

@eryksun commented on GitHub (Sep 17, 2020):

It turns out that this excerpt from the documentation is a fabrication, and lpTitle might point to an .exe file.

The shell team should be notified about their bug. The shell API (e.g. ShellExecuteExW) shouldn't use STARTF_TITLEISLINKNAME for a filesystem symlink. This flag means the title is the path of a shell link (.lnk), not any other kind of link.


mklink %USERPROFILE%\cmd_link.exe c:\windows\system32\cmd.exe

Off topic: a symlink to an executable usually has to be in the same directory as the executable, even though it happens to work from another directory in a simple case such as cmd.exe. The system uses the directory of the symlink itself as the application directory instead of resolving its final path. The real application directory may be required for finding private DLLs and assemblies, among other resources.

@eryksun commented on GitHub (Sep 17, 2020): > It turns out that this excerpt from the documentation is a fabrication, and lpTitle might point to an .exe file. The shell team should be notified about their bug. The shell API (e.g. `ShellExecuteExW`) shouldn't use `STARTF_TITLEISLINKNAME` for a filesystem symlink. This flag means the title is the path of a shell link (.lnk), not any other kind of link. --- > mklink %USERPROFILE%\cmd_link.exe c:\windows\system32\cmd.exe Off topic: a symlink to an executable usually has to be in the same directory as the executable, even though it happens to work from another directory in a simple case such as cmd.exe. The system uses the directory of the symlink itself as the application directory instead of resolving its final path. The real application directory may be required for finding private DLLs and assemblies, among other resources.
Author
Owner

@zadjii-msft commented on GitHub (Sep 17, 2020):

This is gonna sound crazy, but we've totally seen this bug once before. I totally forgot what the resolution was, but maybe @miniksa does.

@zadjii-msft commented on GitHub (Sep 17, 2020): This is gonna sound crazy, but we've totally seen this bug once before. I totally forgot what the resolution was, but maybe @miniksa does.
Author
Owner

@DHowett commented on GitHub (Sep 22, 2020):

Filed MSFT-29529566 for the folks who own ShellExecute. 😄

@DHowett commented on GitHub (Sep 22, 2020): Filed MSFT-29529566 for the folks who own ShellExecute. :smile:
Author
Owner

@RJohnsonPGH commented on GitHub (Sep 23, 2020):

I am experiencing this in Powershell as well, so doesn't seem to be limited to just Terminal

@RJohnsonPGH commented on GitHub (Sep 23, 2020): I am experiencing this in Powershell as well, so doesn't seem to be limited to just Terminal
Author
Owner

@DHowett commented on GitHub (Sep 23, 2020):

Conhost, the windows console host, also lives in this repository. That is the application powershell uses to host itself.

@DHowett commented on GitHub (Sep 23, 2020): Conhost, the windows console host, also lives in this repository. That is the application powershell uses to host itself.
Author
Owner

@RJohnsonPGH commented on GitHub (Sep 23, 2020):

Ah, that explains it.

Additionally, my repro doesn't involve symlinks at all, but rather shortcuts to powershell.exe which obviously is a .lnk.

Example target:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File \\fileserver\Script.ps1

will reliably take focus when lower case 'L' is pressed.

Edit: After re-reading the whole thread, I see now that the issue with shortcuts triggering this is what is primarily described in the first comment and that the issue with symlinks is a combination of a shell bug and this bug. Sorry for any confusion on my part.

@RJohnsonPGH commented on GitHub (Sep 23, 2020): Ah, that explains it. Additionally, my repro doesn't involve symlinks at all, but rather shortcuts to powershell.exe which obviously is a `.lnk`. Example target: `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File \\fileserver\Script.ps1` will reliably take focus when lower case 'L' is pressed. Edit: After re-reading the whole thread, I see now that the issue with shortcuts triggering this is what is primarily described in the first comment and that the issue with symlinks is a combination of a shell bug and this bug. Sorry for any confusion on my part.
Author
Owner

@miniksa commented on GitHub (Jan 4, 2021):

This was MSFT-14659706 and my resolution there was literally to initialize the variables so this didn't happen. So I'm going to go figure out what happened to that fix.

@miniksa commented on GitHub (Jan 4, 2021): This was MSFT-14659706 and my resolution there was literally to initialize the variables so this didn't happen. So I'm going to go figure out what happened to that fix.
Author
Owner

@miniksa commented on GitHub (Jan 4, 2021):

Apparently I made this fix on the OS side on the same day and approximate time as MSFT-14612110. We were working in two repositories back then, the main OS one and our own "OpenConsole" one in preparation to one day do open engineering... like we do now! We had a replication bot "Git2Git" to move things back and forth. Somehow I dispatched both replications at the same time and the one for this fix looked like a revert of the one of the other fix. But it actually packed in this change too at the same time. I abandoned the replication and the fix was then lost to the sands of time. Human error... mine this time. Not a systemic issue, as far as I can see.

I will go dig the fix out of the sand and bring it in.

@miniksa commented on GitHub (Jan 4, 2021): Apparently I made this fix on the OS side on the same day and approximate time as MSFT-14612110. We were working in two repositories back then, the main OS one and our own "OpenConsole" one in preparation to one day do open engineering... like we do now! We had a replication bot "Git2Git" to move things back and forth. Somehow I dispatched both replications at the same time and the one for this fix looked like a revert of the one of the other fix. But it actually packed in this change too at the same time. I abandoned the replication and the fix was then lost to the sands of time. Human error... mine this time. Not a systemic issue, as far as I can see. I will go dig the fix out of the sand and bring it in.
Author
Owner

@gerardog commented on GitHub (Oct 20, 2022):

Today I had deja vu between this one and created PowerShell/PowerShell#18333

@gerardog commented on GitHub (Oct 20, 2022): Today I had deja vu between this one and created PowerShell/PowerShell#18333
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#10647