Skip to content

Fix a crash during commandline handoff #19096

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions src/cascadia/WindowsTerminal/WindowEmperor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ 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.
static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg)
static wil::zwstring_view stringFromDoubleNullTerminated(const wchar_t* beg)
{
auto end = beg;

for (; *end; end += wcsnlen(end, SIZE_T_MAX) + 1)
{
}

return { beg, end };
return { beg, gsl::narrow_cast<size_t>(end - beg) };
}

// Appends an uint32_t to a byte vector.
Expand All @@ -78,36 +78,39 @@ static const uint8_t* deserializeUint32(const uint8_t* it, const uint8_t* end, u
return it + sizeof(uint32_t);
}

// Writes an uint32_t length prefix, followed by the string data, to the output vector.
static void serializeString(std::vector<uint8_t>& out, std::wstring_view str)
// Writes a null-terminated string to `out`: A uint32_t length prefix,
// *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 len = gsl::narrow<uint32_t>(str.size());
serializeUint32(out, len);
const auto len = str.size() + 1;
serializeUint32(out, gsl::narrow<uint32_t>(len));
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.
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;
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");
}

str = { reinterpret_cast<const wchar_t*>(it), len };
return it + len * sizeof(wchar_t);
str = { reinterpret_cast<const wchar_t*>(it), len - 1 };
return it + bytes;
}

struct Handoff
{
std::wstring_view args;
std::wstring_view env;
std::wstring_view cwd;
wil::zwstring_view args;
wil::zwstring_view env;
wil::zwstring_view cwd;
uint32_t show;
};

Expand Down Expand Up @@ -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;
c.Commandline(args);
Expand Down Expand Up @@ -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)
{
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(args.c_str());
const auto argv = commandlineToArgArray(handoff.args.c_str());
_dispatchCommandlineCommon(argv, handoff.cwd, handoff.env, handoff.show);
}
return 0;
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/WindowsTerminal/WindowEmperor.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class WindowEmperor
void _summonAllWindows() const;
void _dispatchSpecialKey(const MSG& msg) const;
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);
LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
void _createMessageWindow(const wchar_t* className);
Expand Down