From e517cc3a595ee7def271ee398ae8d5c71a9b2d9a Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 20 Jan 2026 11:17:31 -0800 Subject: [PATCH] Fix issue with svccommio incorrectly resetting console state in the move operator The ConsoleInput and ConsoleOutput classes used = default move operations, which caused the moved-from temporary's destructor to restore the original console mode (re-enabling ENABLE_PROCESSED_INPUT) after the object was moved into the std::optional. The fix uses emplace() to construct objects directly in-place, avoiding moves entirely. Move operations are now deleted to prevent future misuse. --- src/windows/common/svccommio.cpp | 57 +++++++++++--------------------- src/windows/common/svccommio.hpp | 26 ++++++--------- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/src/windows/common/svccommio.cpp b/src/windows/common/svccommio.cpp index 33c966f5e..46895fee1 100644 --- a/src/windows/common/svccommio.cpp +++ b/src/windows/common/svccommio.cpp @@ -72,22 +72,9 @@ CATCH_LOG() namespace wsl::windows::common { -std::optional ConsoleInput::Create(HANDLE Handle) +ConsoleInput::ConsoleInput(HANDLE Handle, DWORD SavedMode) : + m_Handle(Handle), m_SavedMode(SavedMode), m_SavedCodePage(GetConsoleCP()) { - DWORD Mode; - if (GetFileType(Handle) == FILE_TYPE_CHAR && GetConsoleMode(Handle, &Mode)) - { - return ConsoleInput(Handle, Mode); - } - - return std::nullopt; -} - -ConsoleInput::ConsoleInput(HANDLE Handle, DWORD SavedMode) : m_Handle(Handle), m_SavedMode(SavedMode) -{ - // Save code page. - m_SavedCodePage = GetConsoleCP(); - // Configure for raw input with VT support. DWORD NewMode = m_SavedMode; WI_SetAllFlags(NewMode, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT); @@ -104,29 +91,9 @@ ConsoleInput::~ConsoleInput() LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(m_SavedCodePage)); } -std::optional ConsoleOutput::Create() -{ - wil::unique_hfile ConsoleHandle( - CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr)); - - if (ConsoleHandle) - { - DWORD Mode; - if (GetConsoleMode(ConsoleHandle.get(), &Mode)) - { - return ConsoleOutput(std::move(ConsoleHandle), Mode); - } - } - - return std::nullopt; -} - ConsoleOutput::ConsoleOutput(wil::unique_hfile&& ConsoleHandle, DWORD SavedMode) : - m_ConsoleHandle(std::move(ConsoleHandle)), m_SavedMode(SavedMode) + m_ConsoleHandle(std::move(ConsoleHandle)), m_SavedMode(SavedMode), m_SavedCodePage(GetConsoleOutputCP()) { - // Save code page. - m_SavedCodePage = GetConsoleOutputCP(); - // Configure for VT output. DWORD NewMode = m_SavedMode; WI_SetAllFlags(NewMode, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN); @@ -149,10 +116,24 @@ SvcCommIo::SvcCommIo() const HANDLE ErrorHandle = GetStdHandle(STD_ERROR_HANDLE); // Configure input console - m_ConsoleInput = ConsoleInput::Create(InputHandle); + DWORD InputMode; + if (GetFileType(InputHandle) == FILE_TYPE_CHAR && GetConsoleMode(InputHandle, &InputMode)) + { + m_ConsoleInput.emplace(InputHandle, InputMode); + } // Configure output console - m_ConsoleOutput = ConsoleOutput::Create(); + wil::unique_hfile ConsoleHandle( + CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr)); + + if (ConsoleHandle) + { + DWORD OutputMode; + if (GetConsoleMode(ConsoleHandle.get(), &OutputMode)) + { + m_ConsoleOutput.emplace(std::move(ConsoleHandle), OutputMode); + } + } // Initialize the standard handles structure const bool IsConsoleInput = m_ConsoleInput.has_value(); diff --git a/src/windows/common/svccommio.hpp b/src/windows/common/svccommio.hpp index 64533dbcc..a3e8929f7 100644 --- a/src/windows/common/svccommio.hpp +++ b/src/windows/common/svccommio.hpp @@ -25,38 +25,34 @@ namespace wsl::windows::common { class ConsoleInput { public: - static std::optional Create(HANDLE Handle); + ConsoleInput(HANDLE Handle, DWORD SavedMode); ~ConsoleInput(); ConsoleInput(const ConsoleInput&) = delete; ConsoleInput& operator=(const ConsoleInput&) = delete; - ConsoleInput(ConsoleInput&&) = default; - ConsoleInput& operator=(ConsoleInput&&) = default; + ConsoleInput(ConsoleInput&&) = delete; + ConsoleInput& operator=(ConsoleInput&&) = delete; private: - ConsoleInput(HANDLE Handle, DWORD SavedMode); - - HANDLE m_Handle = nullptr; - DWORD m_SavedMode = 0; - UINT m_SavedCodePage = 0; + HANDLE m_Handle{}; + DWORD m_SavedMode{}; + UINT m_SavedCodePage{}; }; // RAII wrapper for console output configuration and restoration class ConsoleOutput { public: - static std::optional Create(); + ConsoleOutput(wil::unique_hfile&& ConsoleHandle, DWORD SavedMode); ~ConsoleOutput(); ConsoleOutput(const ConsoleOutput&) = delete; ConsoleOutput& operator=(const ConsoleOutput&) = delete; - ConsoleOutput(ConsoleOutput&&) = default; - ConsoleOutput& operator=(ConsoleOutput&&) = default; + ConsoleOutput(ConsoleOutput&&) = delete; + ConsoleOutput& operator=(ConsoleOutput&&) = delete; private: - ConsoleOutput(wil::unique_hfile&& ConsoleHandle, DWORD SavedMode); - wil::unique_hfile m_ConsoleHandle; - DWORD m_SavedMode = 0; - UINT m_SavedCodePage = 0; + DWORD m_SavedMode{}; + UINT m_SavedCodePage{}; }; class SvcCommIo