Fix a crash during commandline handoff (#19096)

The crash occurs because WinRT `abort()`s when it encounters
a `std::wstring_view` without null-terminator.

Closes #19093

## Validation Steps Performed
* Set `wtd` as the default terminal
* Launch `cmd`
* Launch `wtd`
* 2 windows 
This commit is contained in:
Leonard Hecker 2025-07-04 00:19:12 +02:00 committed by GitHub
parent 4500d42831
commit ac07afebcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 20 additions and 18 deletions

View File

@ -49,7 +49,7 @@ static std::vector<winrt::hstring> commandlineToArgArray(const wchar_t* commandL
} }
// Returns the length of a double-null encoded string *excluding* the trailing double-null character. // Returns the length of a double-null encoded string *excluding* the trailing double-null character.
static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg) static wil::zwstring_view stringFromDoubleNullTerminated(const wchar_t* beg)
{ {
auto end = beg; auto end = beg;
@ -57,7 +57,7 @@ static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg)
{ {
} }
return { beg, end }; return { beg, gsl::narrow_cast<size_t>(end - beg) };
} }
// Appends an uint32_t to a byte vector. // Appends an uint32_t to a byte vector.
@ -78,36 +78,39 @@ static const uint8_t* deserializeUint32(const uint8_t* it, const uint8_t* end, u
return it + sizeof(uint32_t); return it + sizeof(uint32_t);
} }
// Writes an uint32_t length prefix, followed by the string data, to the output vector. // Writes a null-terminated string to `out`: A uint32_t length prefix,
static void serializeString(std::vector<uint8_t>& out, std::wstring_view str) // *including null byte*, followed by the string data, followed by the null-terminator.
static void serializeString(std::vector<uint8_t>& out, wil::zwstring_view str)
{ {
const auto ptr = reinterpret_cast<const uint8_t*>(str.data()); const auto ptr = reinterpret_cast<const uint8_t*>(str.data());
const auto len = gsl::narrow<uint32_t>(str.size()); const auto len = str.size() + 1;
serializeUint32(out, len); serializeUint32(out, gsl::narrow<uint32_t>(len));
out.insert(out.end(), ptr, ptr + len * sizeof(wchar_t)); out.insert(out.end(), ptr, ptr + len * sizeof(wchar_t));
} }
// Parses the next string from the input iterator. Performs bounds-checks. // Counter-part to `serializeString`. Performs bounds-checks.
// Returns an iterator that points past it. // Returns an iterator that points past it.
static const uint8_t* deserializeString(const uint8_t* it, const uint8_t* end, std::wstring_view& str) static const uint8_t* deserializeString(const uint8_t* it, const uint8_t* end, wil::zwstring_view& str)
{ {
uint32_t len; uint32_t len;
it = deserializeUint32(it, end, len); it = deserializeUint32(it, end, len);
if (static_cast<size_t>(end - it) < len * sizeof(wchar_t)) const auto bytes = static_cast<size_t>(len) * sizeof(wchar_t);
if (bytes == 0 || static_cast<size_t>(end - it) < bytes)
{ {
throw std::out_of_range("Not enough data for string content"); throw std::out_of_range("Not enough data for string content");
} }
str = { reinterpret_cast<const wchar_t*>(it), len }; str = { reinterpret_cast<const wchar_t*>(it), len - 1 };
return it + len * sizeof(wchar_t); return it + bytes;
} }
struct Handoff struct Handoff
{ {
std::wstring_view args; wil::zwstring_view args;
std::wstring_view env; wil::zwstring_view env;
std::wstring_view cwd; wil::zwstring_view cwd;
uint32_t show; uint32_t show;
}; };
@ -612,7 +615,7 @@ void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs arg
} }
} }
void WindowEmperor::_dispatchCommandlineCommon(winrt::array_view<const winrt::hstring> args, std::wstring_view currentDirectory, std::wstring_view envString, uint32_t showWindowCommand) void WindowEmperor::_dispatchCommandlineCommon(winrt::array_view<const winrt::hstring> args, wil::zwstring_view currentDirectory, wil::zwstring_view envString, uint32_t showWindowCommand)
{ {
winrt::TerminalApp::CommandlineArgs c; winrt::TerminalApp::CommandlineArgs c;
c.Commandline(args); c.Commandline(args);
@ -929,8 +932,7 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
if (const auto cds = reinterpret_cast<COPYDATASTRUCT*>(lParam); cds->dwData == TERMINAL_HANDOFF_MAGIC) if (const auto cds = reinterpret_cast<COPYDATASTRUCT*>(lParam); cds->dwData == TERMINAL_HANDOFF_MAGIC)
{ {
const auto handoff = deserializeHandoffPayload(static_cast<const uint8_t*>(cds->lpData), static_cast<const uint8_t*>(cds->lpData) + cds->cbData); const auto handoff = deserializeHandoffPayload(static_cast<const uint8_t*>(cds->lpData), static_cast<const uint8_t*>(cds->lpData) + cds->cbData);
const winrt::hstring args{ handoff.args }; const auto argv = commandlineToArgArray(handoff.args.c_str());
const auto argv = commandlineToArgArray(args.c_str());
_dispatchCommandlineCommon(argv, handoff.cwd, handoff.env, handoff.show); _dispatchCommandlineCommon(argv, handoff.cwd, handoff.env, handoff.show);
} }
return 0; return 0;

View File

@ -52,7 +52,7 @@ private:
void _summonAllWindows() const; void _summonAllWindows() const;
void _dispatchSpecialKey(const MSG& msg) const; void _dispatchSpecialKey(const MSG& msg) const;
void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args); void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args);
void _dispatchCommandlineCommon(winrt::array_view<const winrt::hstring> args, std::wstring_view currentDirectory, std::wstring_view envString, uint32_t showWindowCommand); void _dispatchCommandlineCommon(winrt::array_view<const winrt::hstring> args, wil::zwstring_view currentDirectory, wil::zwstring_view envString, uint32_t showWindowCommand);
safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args); safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args);
LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept; LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
void _createMessageWindow(const wchar_t* className); void _createMessageWindow(const wchar_t* className);