Skip to content

Commit 825460a

Browse files
authored
[lldb/gdb-remote] Do not crash on an invalid server response (#131979)
An invalid RLE sequence in the received packet could result in an out-of-bounds reading that could cause a crash.
1 parent bda87e0 commit 825460a

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -788,9 +788,14 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
788788

789789
// Copy the packet from m_bytes to packet_str expanding the run-length
790790
// encoding in the process.
791-
std ::string packet_str =
791+
auto maybe_packet_str =
792792
ExpandRLE(m_bytes.substr(content_start, content_end - content_start));
793-
packet = StringExtractorGDBRemote(packet_str);
793+
if (!maybe_packet_str) {
794+
m_bytes.erase(0, total_length);
795+
packet.Clear();
796+
return GDBRemoteCommunication::PacketType::Invalid;
797+
}
798+
packet = StringExtractorGDBRemote(*maybe_packet_str);
794799

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

1314-
std::string GDBRemoteCommunication::ExpandRLE(std::string packet) {
1319+
std::optional<std::string>
1320+
GDBRemoteCommunication::ExpandRLE(std::string packet) {
13151321
// Reserve enough byte for the most common case (no RLE used).
13161322
std::string decoded;
13171323
decoded.reserve(packet.size());
13181324
for (std::string::const_iterator c = packet.begin(); c != packet.end(); ++c) {
13191325
if (*c == '*') {
1326+
if (decoded.empty())
1327+
return std::nullopt;
13201328
// '*' indicates RLE. Next character will give us the repeat count and
13211329
// previous character is what is to be repeated.
13221330
char char_to_repeat = decoded.back();
13231331
// Number of time the previous character is repeated.
1324-
int repeat_count = *++c + 3 - ' ';
1332+
if (++c == packet.end())
1333+
return std::nullopt;
1334+
int repeat_count = *c + 3 - ' ';
13251335
// We have the char_to_repeat and repeat_count. Now push it in the
13261336
// packet.
13271337
for (int i = 0; i < repeat_count; ++i)
13281338
decoded.push_back(char_to_repeat);
13291339
} else if (*c == 0x7d) {
13301340
// 0x7d is the escape character. The next character is to be XOR'd with
13311341
// 0x20.
1332-
char escapee = *++c ^ 0x20;
1342+
if (++c == packet.end())
1343+
return std::nullopt;
1344+
char escapee = *c ^ 0x20;
13331345
decoded.push_back(escapee);
13341346
} else {
13351347
decoded.push_back(*c);

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class GDBRemoteCommunication : public Communication {
168168
GDBRemoteCommunication &server);
169169

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

173173
protected:
174174
std::chrono::seconds m_packet_timeout;

lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,26 @@ TEST_F(GDBRemoteCommunicationTest, ReadPacket) {
6868
ASSERT_EQ(PacketResult::Success, server.GetAck());
6969
}
7070
}
71+
72+
// Test that packets with incorrect RLE sequences do not cause a crash and
73+
// reported as invalid.
74+
TEST_F(GDBRemoteCommunicationTest, CheckForPacket) {
75+
using PacketType = GDBRemoteCommunication::PacketType;
76+
struct TestCase {
77+
llvm::StringLiteral Packet;
78+
PacketType Result;
79+
};
80+
static constexpr TestCase Tests[] = {
81+
{{"$#00"}, PacketType::Standard},
82+
{{"$xx*#00"}, PacketType::Invalid}, // '*' without a count
83+
{{"$*#00"}, PacketType::Invalid}, // '*' without a preceding character
84+
{{"$xx}#00"}, PacketType::Invalid}, // bare escape character '}'
85+
{{"%#00"}, PacketType::Notify}, // a correct packet after an invalid
86+
};
87+
for (const auto &Test : Tests) {
88+
SCOPED_TRACE(Test.Packet);
89+
StringExtractorGDBRemote response;
90+
EXPECT_EQ(Test.Result, client.CheckForPacket(Test.Packet.bytes_begin(),
91+
Test.Packet.size(), response));
92+
}
93+
}

0 commit comments

Comments
 (0)