Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -788,9 +788,14 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,

// Copy the packet from m_bytes to packet_str expanding the run-length
// encoding in the process.
std ::string packet_str =
auto maybe_packet_str =
ExpandRLE(m_bytes.substr(content_start, content_end - content_start));
packet = StringExtractorGDBRemote(packet_str);
if (!maybe_packet_str) {
m_bytes.erase(0, total_length);
packet.Clear();
return GDBRemoteCommunication::PacketType::Invalid;
}
packet = StringExtractorGDBRemote(*maybe_packet_str);

if (m_bytes[0] == '$' || m_bytes[0] == '%') {
assert(checksum_idx < m_bytes.size());
Expand Down Expand Up @@ -1311,25 +1316,32 @@ void llvm::format_provider<GDBRemoteCommunication::PacketResult>::format(
}
}

std::string GDBRemoteCommunication::ExpandRLE(std::string packet) {
std::optional<std::string>
GDBRemoteCommunication::ExpandRLE(std::string packet) {
// Reserve enough byte for the most common case (no RLE used).
std::string decoded;
decoded.reserve(packet.size());
for (std::string::const_iterator c = packet.begin(); c != packet.end(); ++c) {
if (*c == '*') {
if (decoded.empty())
return std::nullopt;
// '*' indicates RLE. Next character will give us the repeat count and
// previous character is what is to be repeated.
char char_to_repeat = decoded.back();
// Number of time the previous character is repeated.
int repeat_count = *++c + 3 - ' ';
if (++c == packet.end())
return std::nullopt;
int repeat_count = *c + 3 - ' ';
// We have the char_to_repeat and repeat_count. Now push it in the
// packet.
for (int i = 0; i < repeat_count; ++i)
decoded.push_back(char_to_repeat);
} else if (*c == 0x7d) {
// 0x7d is the escape character. The next character is to be XOR'd with
// 0x20.
char escapee = *++c ^ 0x20;
if (++c == packet.end())
return std::nullopt;
char escapee = *c ^ 0x20;
decoded.push_back(escapee);
} else {
decoded.push_back(*c);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class GDBRemoteCommunication : public Communication {
GDBRemoteCommunication &server);

/// Expand GDB run-length encoding.
static std::string ExpandRLE(std::string);
static std::optional<std::string> ExpandRLE(std::string);

protected:
std::chrono::seconds m_packet_timeout;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,26 @@ TEST_F(GDBRemoteCommunicationTest, ReadPacket) {
ASSERT_EQ(PacketResult::Success, server.GetAck());
}
}

// Test that packets with incorrect RLE sequences do not cause a crash and
// reported as invalid.
TEST_F(GDBRemoteCommunicationTest, CheckForPacket) {
using PacketType = GDBRemoteCommunication::PacketType;
struct TestCase {
llvm::StringLiteral Packet;
PacketType Result;
};
static constexpr TestCase Tests[] = {
{{"$#00"}, PacketType::Standard},
{{"$xx*#00"}, PacketType::Invalid}, // '*' without a count
{{"$*#00"}, PacketType::Invalid}, // '*' without a preceding character
{{"$xx}#00"}, PacketType::Invalid}, // bare escape character '}'
{{"%#00"}, PacketType::Notify}, // a correct packet after an invalid
};
for (const auto &Test : Tests) {
SCOPED_TRACE(Test.Packet);
StringExtractorGDBRemote response;
EXPECT_EQ(Test.Result, client.CheckForPacket(Test.Packet.bytes_begin(),
Test.Packet.size(), response));
}
}