interop: fix issues in IPC message construction (#9035)

* interop: fix issues in IPC message construction

- simplify logic
- handle exceptions to prevent crashes
- log errors

* Update src/runner/settings_window.cpp

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

* Update src/runner/settings_window.cpp

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

* fixup

* comments + fix build

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>
This commit is contained in:
Andrey Nekrasov
2021-01-12 20:42:16 +03:00
committed by GitHub
parent 1364f78b30
commit 7a562950b0
3 changed files with 36 additions and 59 deletions

View File

@@ -3,6 +3,8 @@
#include <iterator> #include <iterator>
constexpr DWORD BUFSIZE = 1024;
TwoWayPipeMessageIPC::TwoWayPipeMessageIPC( TwoWayPipeMessageIPC::TwoWayPipeMessageIPC(
std::wstring _input_pipe_name, std::wstring _input_pipe_name,
std::wstring _output_pipe_name, std::wstring _output_pipe_name,
@@ -347,70 +349,43 @@ HANDLE TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::create_medium_integrity_t
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::handle_pipe_connection(HANDLE input_pipe_handle) void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::handle_pipe_connection(HANDLE input_pipe_handle)
{ {
//Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server if (!input_pipe_handle)
HANDLE hHeap = GetProcessHeap();
uint8_t* pchRequest = (uint8_t*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(uint8_t));
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
// Do some extra error checking since the app will keep running even if this thread fails.
std::list<std::vector<uint8_t>> message_parts;
if (input_pipe_handle == NULL)
{
if (pchRequest != NULL)
HeapFree(hHeap, 0, pchRequest);
return;
}
if (pchRequest == NULL)
{ {
return; return;
} }
constexpr DWORD readBlockBytes = BUFSIZE;
// Loop until done reading std::wstring message;
size_t iBlock = 0;
message.reserve(BUFSIZE);
bool ok;
do do
{ {
// Read client requests from the pipe. This simplistic code only allows messages constexpr size_t charsPerBlock = readBlockBytes / sizeof(message[0]);
// up to BUFSIZE characters in length. message.resize(message.size() + charsPerBlock);
ZeroMemory(pchRequest, BUFSIZE * sizeof(uint8_t)); DWORD bytesRead = 0;
fSuccess = ReadFile( ok = ReadFile(
input_pipe_handle, // handle to pipe input_pipe_handle,
pchRequest, // buffer to receive data // read the message directly into the string block by block simultaneously resizing it
BUFSIZE * sizeof(uint8_t), // size of buffer message.data() + iBlock * charsPerBlock,
&cbBytesRead, // number of bytes read readBlockBytes,
NULL); // not overlapped I/O &bytesRead,
nullptr);
if (!fSuccess && GetLastError() != ERROR_MORE_DATA) if (!ok && GetLastError() != ERROR_MORE_DATA)
{ {
break; break;
} }
std::vector<uint8_t> part_vector; iBlock++;
part_vector.reserve(cbBytesRead); } while (!ok);
std::copy(pchRequest, pchRequest + cbBytesRead, std::back_inserter(part_vector)); // trim the message's buffer
message_parts.push_back(part_vector); const auto nullCharPos = message.find_last_not_of(L'\0');
} while (!fSuccess); if (nullCharPos != std::wstring::npos)
if (fSuccess)
{ {
// Reconstruct the total_message. message.resize(nullCharPos + 1);
std::vector<uint8_t> reconstructed_message;
size_t total_size = 0;
for (auto& part_vector : message_parts)
{
total_size += part_vector.size();
}
reconstructed_message.reserve(total_size);
for (auto& part_vector : message_parts)
{
std::move(part_vector.begin(), part_vector.end(), std::back_inserter(reconstructed_message));
}
std::wstring unicode_msg;
unicode_msg.assign(reinterpret_cast<std::wstring::const_pointer>(reconstructed_message.data()), reconstructed_message.size() / sizeof(std::wstring::value_type));
input_queue.queue_message(unicode_msg);
} }
input_queue.queue_message(std::move(message));
// Flush the pipe to allow the client to read the pipe's contents // Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the // before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance. // handle to this pipe instance.
@@ -418,10 +393,6 @@ void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::handle_pipe_connection(HAND
FlushFileBuffers(input_pipe_handle); FlushFileBuffers(input_pipe_handle);
DisconnectNamedPipe(input_pipe_handle); DisconnectNamedPipe(input_pipe_handle);
CloseHandle(input_pipe_handle); CloseHandle(input_pipe_handle);
HeapFree(hHeap, 0, pchRequest);
printf("InstanceThread exiting.\n");
} }
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::start_named_pipe_server(HANDLE token) void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::start_named_pipe_server(HANDLE token)

View File

@@ -29,7 +29,6 @@ private:
HANDLE current_connect_pipe_handle = NULL; HANDLE current_connect_pipe_handle = NULL;
bool closed = false; bool closed = false;
TwoWayPipeMessageIPC::callback_function dispatch_inc_message_function; TwoWayPipeMessageIPC::callback_function dispatch_inc_message_function;
const DWORD BUFSIZE = 1024;
void send_pipe_message(std::wstring message); void send_pipe_message(std::wstring message);
void consume_output_queue_thread(); void consume_output_queue_thread();

View File

@@ -41,7 +41,7 @@ json::JsonObject get_power_toys_settings()
} }
catch (...) catch (...)
{ {
// TODO: handle malformed JSON. Logger::error("get_power_toys_settings: got malformed json");
} }
} }
return result; return result;
@@ -154,7 +154,14 @@ void dispatch_json_config_to_modules(const json::JsonObject& powertoys_configs)
void dispatch_received_json(const std::wstring& json_to_parse) void dispatch_received_json(const std::wstring& json_to_parse)
{ {
const json::JsonObject j = json::JsonObject::Parse(json_to_parse); json::JsonObject j;
const bool ok = json::JsonObject::TryParse(json_to_parse, j);
if (!ok)
{
Logger::error(L"dispatch_received_json: got malformed json: {}", json_to_parse);
return;
}
for (const auto& base_element : j) for (const auto& base_element : j)
{ {
if (!current_settings_ipc) if (!current_settings_ipc)