Malformed condrv request on SetConsoleNumberOfCommands call #20699

Closed
opened 2026-01-31 07:21:35 +00:00 by claunia · 8 comments
Owner

Originally created by @o-sdn-o on GitHub (Oct 19, 2023).

Windows Terminal version

current main branch

Windows build number

10.0.19045.3516

Other Software

No response

Steps to reproduce

Openconsole.exe receives malformed request from condrv during dispatching Win32 API SetConsoleNumberOfCommandsA/W call. The length of the exe name is always equals 6 (bytes). It is independent of the exe name string specified in the SetConsoleNumberOfCommands call. Even if the exe name consists of one character.

Repro:

  • Build WT in debug configuration.
  • Run WT with any cmd profile.
  • Attach openconsole.exe process to visual studio debugger and set breakpoint at src\server\ApiDispatchers.cpp:1529:
    08f30330d1/src/server/ApiDispatchers.cpp (L1520-L1541)
  • Run the following C++20 code inside wt to trigger the breakpoint on SetConsoleNumberOfCommands call:
#include <iostream>
#include <windows.h>

using namespace std::literals;

int main()
{
	auto exe_a = "program_name.exe"s;
	auto exe_w = L"program_name.exe"s;
	using SetConsoleNumberOfCommandsA_ptr = std::decay<decltype(::SetConsoleNumberOfCommandsA)>::type;
	using SetConsoleNumberOfCommandsW_ptr = std::decay<decltype(::SetConsoleNumberOfCommandsW)>::type;
	auto kernel32_dll = ::LoadLibraryExA("kernel32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
	auto NtSetConsoleNumberOfCommandsA = reinterpret_cast<SetConsoleNumberOfCommandsA_ptr>(::GetProcAddress(kernel32_dll, "SetConsoleNumberOfCommandsA"));
	auto NtSetConsoleNumberOfCommandsW = reinterpret_cast<SetConsoleNumberOfCommandsW_ptr>(::GetProcAddress(kernel32_dll, "SetConsoleNumberOfCommandsW"));
	auto rc_a = NtSetConsoleNumberOfCommandsA(10, exe_a.data());
	auto rc_w = NtSetConsoleNumberOfCommandsW(10, exe_w.data());
	std::cout << "rc_a="<< rc_a << " rc_w="<< rc_w << "\n";
}

Expected Behavior

ULONG cbExeNameLength equals the exe name length in bytes.

Actual Behavior

ULONG cbExeNameLength always equals 6.

Originally created by @o-sdn-o on GitHub (Oct 19, 2023). ### Windows Terminal version current main branch ### Windows build number 10.0.19045.3516 ### Other Software _No response_ ### Steps to reproduce Openconsole.exe receives malformed request from condrv during dispatching Win32 API `SetConsoleNumberOfCommandsA/W` call. The length of the exe name is always equals 6 (bytes). It is independent of the exe name string specified in the SetConsoleNumberOfCommands call. Even if the exe name consists of one character. Repro: - Build WT in debug configuration. - Run WT with any cmd profile. - Attach openconsole.exe process to visual studio debugger and set breakpoint at `src\server\ApiDispatchers.cpp:1529`: https://github.com/microsoft/terminal/blob/08f30330d15e22adf23ee04164fc2474f290348a/src/server/ApiDispatchers.cpp#L1520-L1541 - Run the following C++20 code inside wt to trigger the breakpoint on SetConsoleNumberOfCommands call: ```c++ #include <iostream> #include <windows.h> using namespace std::literals; int main() { auto exe_a = "program_name.exe"s; auto exe_w = L"program_name.exe"s; using SetConsoleNumberOfCommandsA_ptr = std::decay<decltype(::SetConsoleNumberOfCommandsA)>::type; using SetConsoleNumberOfCommandsW_ptr = std::decay<decltype(::SetConsoleNumberOfCommandsW)>::type; auto kernel32_dll = ::LoadLibraryExA("kernel32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); auto NtSetConsoleNumberOfCommandsA = reinterpret_cast<SetConsoleNumberOfCommandsA_ptr>(::GetProcAddress(kernel32_dll, "SetConsoleNumberOfCommandsA")); auto NtSetConsoleNumberOfCommandsW = reinterpret_cast<SetConsoleNumberOfCommandsW_ptr>(::GetProcAddress(kernel32_dll, "SetConsoleNumberOfCommandsW")); auto rc_a = NtSetConsoleNumberOfCommandsA(10, exe_a.data()); auto rc_w = NtSetConsoleNumberOfCommandsW(10, exe_w.data()); std::cout << "rc_a="<< rc_a << " rc_w="<< rc_w << "\n"; } ``` ### Expected Behavior `ULONG cbExeNameLength` equals the exe name length in bytes. ### Actual Behavior `ULONG cbExeNameLength` always equals 6.
claunia added the Needs-TriageIssue-Bug labels 2026-01-31 07:21:35 +00:00
Author
Owner

@lhecker commented on GitHub (Oct 19, 2023):

Is this perhaps an issue with Windows 10? It works fine on Windows 11 10.0.25962.1000. But I also don't think anything changed with that code since 19045...

@lhecker commented on GitHub (Oct 19, 2023): Is this perhaps an issue with Windows 10? It works fine on Windows 11 `10.0.25962.1000`. But I also don't think anything changed with that code since 19045...
Author
Owner

@o-sdn-o commented on GitHub (Oct 19, 2023):

Yes, this is on Windows 10.

@o-sdn-o commented on GitHub (Oct 19, 2023): Yes, this is on Windows 10.
Author
Owner

@o-sdn-o commented on GitHub (Oct 19, 2023):

The problem is not in the WT/openconsole source code, the problem is in the unknown condrv code.

@o-sdn-o commented on GitHub (Oct 19, 2023): The problem is not in the WT/openconsole source code, the problem is in the unknown condrv code.
Author
Owner

@o-sdn-o commented on GitHub (Oct 19, 2023):

Since this was fixed in Win11, this is no longer an issue at all. 😉

@o-sdn-o commented on GitHub (Oct 19, 2023): Since this was fixed in Win11, this is no longer an issue at all. 😉
Author
Owner

@o-sdn-o commented on GitHub (Oct 19, 2023):

I confirm, this works fine on Win11.

@o-sdn-o commented on GitHub (Oct 19, 2023): I confirm, this works fine on Win11.
Author
Owner

@DHowett commented on GitHub (Oct 19, 2023):

Ugh, I remember this one. The issue was actually over in conclnt (static lib in kernelbase).

Here's the commit message:

Merged PR 4435591: Fix two bugs with DOSKEY

The first issue is in the console host: when we erase a command history,
we also clear its allocated flag. It's supposed to remain allocated
but become "reset". When we later check that a command history that
exists in the list is allocated, we fail loudly because allocated has
been cleared.

The second is that in Windows Server 2003, we rewrote the console client
APIs (in kernelbase!) regarding command history and changed one internal
function from taking char** to taking char*. Since the signature was
actually void** and that changed to void*, the compiler didn't notice
when in only one single place we continued to pass a char** instead of a
char*. This caused us to send the wrong filename length for the ExeName
in SetConsoleNumberOfCommands.

Fixes MSFT-25265854

You can tell I'm the one who fixed it because it was needlessly wordy. 😅

Aaaand here's the diff for the SetConsoleNumberOfCommands issue:

     InputBuffer.Buffer = ExeName;
-    InputBuffer.Size = GetExeName(&ExeName, Unicode);
+    InputBuffer.Size = GetExeName(ExeName, Unicode);
     if (InputBuffer.Size == 0) {
         SET_LAST_ERROR(ERROR_INVALID_PARAMETER);

The fix went out in Insider build 19611.1000.rs_prerelease.200415-1433... and using my decoder ring I learn that this means it only shipped in Windows 11+ 😄

@DHowett commented on GitHub (Oct 19, 2023): _Ugh,_ I remember this one. The issue was actually over in conclnt (static lib in kernelbase). Here's the commit message: > Merged PR 4435591: Fix two bugs with DOSKEY > > The first issue is in the console host: when we erase a command history, > we also clear its allocated flag. It's supposed to remain allocated > but become "reset". When we later check that a command history that > exists in the list is allocated, we fail loudly because allocated has > been cleared. > > The second is that in Windows Server 2003, we rewrote the console client > APIs (in kernelbase!) regarding command history and changed one internal > function from taking char** to taking char*. Since the signature was > actually void** and that changed to void*, the compiler didn't notice > when in only one single place we continued to pass a char** instead of a > char*. This caused us to send the wrong filename length for the ExeName > in SetConsoleNumberOfCommands. > > Fixes MSFT-25265854 You can tell I'm the one who fixed it because it was needlessly wordy. 😅 Aaaand here's the diff for the SetConsoleNumberOfCommands issue: ```diff InputBuffer.Buffer = ExeName; - InputBuffer.Size = GetExeName(&ExeName, Unicode); + InputBuffer.Size = GetExeName(ExeName, Unicode); if (InputBuffer.Size == 0) { SET_LAST_ERROR(ERROR_INVALID_PARAMETER); ``` The fix went out in Insider build `19611.1000.rs_prerelease.200415-1433`... and using my decoder ring I learn that this means it only shipped in Windows 11+ :smile:
Author
Owner

@alexolog commented on GitHub (Oct 21, 2023):

Will this be fixed for Win10?

@alexolog commented on GitHub (Oct 21, 2023): Will this be fixed for Win10?
Author
Owner

@DHowett commented on GitHub (Oct 23, 2023):

Will this be fixed for Win10?

No. We closed the book on Windows 10 a while ago, and low-severity bugs like this don't meet the bar for servicing the fix back to older versions of Windows... even though I wish that weren't the case.

@DHowett commented on GitHub (Oct 23, 2023): > Will this be fixed for Win10? No. We closed the book on Windows 10 a while ago, and low-severity bugs like this don't meet the bar for servicing the fix back to older versions of Windows... even though I wish that weren't the case.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#20699