diff --git a/core/adapters/corelliumadapter.cpp b/core/adapters/corelliumadapter.cpp index 02815426..6b89eea4 100644 --- a/core/adapters/corelliumadapter.cpp +++ b/core/adapters/corelliumadapter.cpp @@ -348,7 +348,7 @@ std::vector CorelliumAdapter::GetThreadList() reply.AsString().substr(1); const auto tids = RspConnector::Split(shortened_string, ","); for ( const auto& tid : tids ) - threads.emplace_back(std::stoi(tid, nullptr, 16)); + threads.emplace_back(RspConnector::ParseInt(tid)); reply = this->m_rspConnector->TransmitAndReceive(RspData("qsThreadInfo")); } @@ -827,7 +827,7 @@ DebugStopReason CorelliumAdapter::ResponseHandler() if (replyString.length() >= 3) { std::string signalString = replyString.substr(1, 2); - uint64_t signal = std::stoull(signalString, nullptr, 16); + uint64_t signal = RspConnector::ParseInt(signalString); m_isTargetRunning = false; @@ -993,7 +993,7 @@ static std::string HexToAscii(const std::string& hex) { // Convert the two hex characters to a byte (using a stringstream) std::string byte_string = hex.substr(i, 2); - unsigned char byte = static_cast(std::stoi(byte_string, nullptr, 16)); // Convert to byte + unsigned char byte = static_cast(RspConnector::ParseInt(byte_string)); // Convert to byte // Append the byte (ASCII char) to the resulting string ascii.push_back(byte); diff --git a/core/adapters/gdbadapter.cpp b/core/adapters/gdbadapter.cpp index 4e6ab3bb..1ca4fad0 100644 --- a/core/adapters/gdbadapter.cpp +++ b/core/adapters/gdbadapter.cpp @@ -377,7 +377,7 @@ std::vector GdbAdapter::GetThreadList() reply.AsString().substr(1); const auto tids = RspConnector::Split(shortened_string, ","); for ( const auto& tid : tids ) - threads.emplace_back(std::stoi(tid, nullptr, 16)); + threads.emplace_back(RspConnector::ParseInt(tid)); reply = this->m_rspConnector->TransmitAndReceive(RspData("qsThreadInfo")); } @@ -1000,8 +1000,8 @@ DebugStopReason GdbAdapter::ResponseHandler(bool notifyStopped) if (replyString.length() >= 3) { std::string signalString = replyString.substr(1, 2); - uint64_t signal = std::stoull(signalString, nullptr, 16); - + uint64_t signal = RspConnector::ParseInt(signalString); + m_isTargetRunning = false; CheckApplyPendingBreakpoints(); @@ -1457,7 +1457,7 @@ static std::string HexToAscii(const std::string& hex) { // Convert the two hex characters to a byte (using a stringstream) std::string byte_string = hex.substr(i, 2); - unsigned char byte = static_cast(std::stoi(byte_string, nullptr, 16)); // Convert to byte + unsigned char byte = static_cast(RspConnector::ParseInt(byte_string)); // Convert to byte // Append the byte (ASCII char) to the resulting string ascii.push_back(byte); diff --git a/core/adapters/gdbmiadapter.cpp b/core/adapters/gdbmiadapter.cpp index 0170b083..32b7bed8 100644 --- a/core/adapters/gdbmiadapter.cpp +++ b/core/adapters/gdbmiadapter.cpp @@ -1,5 +1,6 @@ #include "gdbmiadapter.h" #include +#include #include #include "../debuggercontroller.h" #include "../../cli/log.h" @@ -953,7 +954,16 @@ DataBuffer GdbMiAdapter::ReadMemory(std::uintptr_t address, size_t size) { std::string hex_contents = value["memory"][0]["contents"].GetString(); DataBuffer buffer(hex_contents.length() / 2); for(size_t i = 0; i < buffer.GetLength(); i++) { - buffer[i] = std::stoul(hex_contents.substr(i*2, 2), nullptr, 16); + // Parse with the non-throwing std::from_chars, since std::stoul throws on + // malformed data coming from the gdb process + unsigned int byte = 0; + const char* first = hex_contents.data() + i * 2; + if (std::from_chars(first, first + 2, byte, 16).ec != std::errc()) + { + LogDebug("Malformed hex contents in memory read reply"); + return zero; + } + buffer[i] = byte; } return buffer; } diff --git a/core/adapters/rspconnector.cpp b/core/adapters/rspconnector.cpp index 9fdc93b2..8d25def9 100644 --- a/core/adapters/rspconnector.cpp +++ b/core/adapters/rspconnector.cpp @@ -93,9 +93,12 @@ RspData RspConnector::DecodeRLE(const RspData& data) std::unordered_map RspConnector::PacketToUnorderedMap(const RspData& data) { std::unordered_map packet_map{}; - packet_map["signal"] = std::stoull(data.AsString().substr(1, 2), nullptr, 16); - const auto data_string = data.AsString(); + if (data_string.length() < 3) + return packet_map; + + packet_map["signal"] = ParseInt(data_string.substr(1, 2)); + const auto after_signal = data_string.substr(3); for ( const auto& entries : RspConnector::Split(after_signal, ";")) { @@ -117,15 +120,16 @@ std::unordered_map RspConnector::PacketToUnorderedMa if (key == "thread") { if (value[0] == 'p' && value.find('.') != std::string::npos) { auto core_id_and_thread_id = RspConnector::Split(value.substr(1), "."); - packet_map["thread"] = std::stoull(core_id_and_thread_id[1], nullptr, 16); + if (core_id_and_thread_id.size() >= 2) + packet_map["thread"] = ParseInt(core_id_and_thread_id[1]); } else { - packet_map["thread"] = std::stoull(value, nullptr, 16); + packet_map["thread"] = ParseInt(value); } } else if (std::regex_search(key, std::regex("^[0-9a-fA-F]+$"))) { - packet_map[fmt::format("r{}", std::stoi(key, nullptr, 16))] = - static_cast( RspConnector::SwapEndianness(std::stoull(value, nullptr, 16))); + packet_map[fmt::format("r{}", ParseInt(key))] = + static_cast( RspConnector::SwapEndianness(ParseInt(value))); } else { - packet_map[key] = std::stoull(value, nullptr, 16); + packet_map[key] = ParseInt(value); } } else @@ -204,8 +208,8 @@ void RspConnector::NegotiateCapabilities(const std::vector & capabi { if ( reply_token.find("PacketSize=") != std::string::npos ) { - if (auto packet_tokens = RspConnector::Split(reply_token, "="); !packet_tokens.empty()) - this->m_maxPacketLength = std::stoi(packet_tokens[1], nullptr, 16); + if (auto packet_tokens = RspConnector::Split(reply_token, "="); packet_tokens.size() >= 2) + this->m_maxPacketLength = ParseInt(packet_tokens[1], 16, this->m_maxPacketLength); continue; } @@ -413,11 +417,13 @@ int32_t RspConnector::HostFileIO(const RspData& data, RspData& output, int32_t& if (resultErrno.find(',') != std::string::npos) { const auto split = RspConnector::Split(resultErrno, ","); if ((split.size() >= 2) && (split[1] != "")) - error = std::stol(split[1].c_str(), nullptr, 16); + error = ParseInt(split[1]); - return std::stol(split[0].c_str(), nullptr, 16); + if (split.empty()) + return -1; + return ParseInt(split[0], 16, -1); } - return std::stol(resultErrno.c_str(), nullptr, 16); + return ParseInt(resultErrno, 16, -1); } diff --git a/core/adapters/rspconnector.h b/core/adapters/rspconnector.h index 2c9c7bb1..8e7a0f67 100644 --- a/core/adapters/rspconnector.h +++ b/core/adapters/rspconnector.h @@ -19,6 +19,7 @@ limitations under the License. #include #include #include +#include #include #include #include "binaryninjaapi.h" @@ -153,6 +154,18 @@ namespace BinaryNinjaDebugger static std::unordered_map PacketToUnorderedMap(const RspData& data); static std::vector Split(const std::string& string, const std::string& regex); + // Parse an integer from remote protocol data without throwing. std::stoi and friends + // raise std::invalid_argument/std::out_of_range on malformed input, which crashes the + // process when the string comes from an untrusted remote stub. + template + static Ty ParseInt(const std::string& str, int base = 16, Ty fallback = 0) + { + Ty value = fallback; + if (std::from_chars(str.data(), str.data() + str.size(), value, base).ec != std::errc()) + return fallback; + return value; + } + static uint64_t SwapEndianness(uint64_t value, size_t len) { switch (len)