Skip to content

Commit 067bb1f

Browse files
committed
[lldb] Fix out of bounds read in DataExtractor::GetCStr and add unit test that function.
Summary: The `if (*cstr_end == '\0')` in the previous code checked if the previous loop terminated because it found a null terminator or because it reached the end of the data. However, in the case that we hit the end of the data before finding a null terminator, `cstr_end` points behind the last byte in our data and `*cstr_end` reads the memory behind the array (which may be uninitialised) This patch just rewrites that function use `std::find` and adds the relevant unit tests. Reviewers: labath Reviewed By: labath Subscribers: abidh, JDevlieghere, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D68773 llvm-svn: 374311
1 parent eb8b6fe commit 067bb1f

File tree

2 files changed

+63
-19
lines changed

2 files changed

+63
-19
lines changed

lldb/source/Utility/DataExtractor.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -816,26 +816,25 @@ DataExtractor::CopyByteOrderedData(offset_t src_offset, offset_t src_len,
816816
// non-zero and there aren't enough available bytes, nullptr will be returned
817817
// and "offset_ptr" will not be updated.
818818
const char *DataExtractor::GetCStr(offset_t *offset_ptr) const {
819-
const char *cstr = reinterpret_cast<const char *>(PeekData(*offset_ptr, 1));
820-
if (cstr) {
821-
const char *cstr_end = cstr;
822-
const char *end = reinterpret_cast<const char *>(m_end);
823-
while (cstr_end < end && *cstr_end)
824-
++cstr_end;
825-
826-
// Now we are either at the end of the data or we point to the
827-
// NULL C string terminator with cstr_end...
828-
if (*cstr_end == '\0') {
829-
// Advance the offset with one extra byte for the NULL terminator
830-
*offset_ptr += (cstr_end - cstr + 1);
831-
return cstr;
832-
}
819+
const char *start = reinterpret_cast<const char *>(PeekData(*offset_ptr, 1));
820+
// Already at the end of the data.
821+
if (!start)
822+
return nullptr;
833823

834-
// We reached the end of the data without finding a NULL C string
835-
// terminator. Fall through and return nullptr otherwise anyone that would
836-
// have used the result as a C string can wander into unknown memory...
837-
}
838-
return nullptr;
824+
const char *end = reinterpret_cast<const char *>(m_end);
825+
826+
// Check all bytes for a null terminator that terminates a C string.
827+
const char *terminator_or_end = std::find(start, end, '\0');
828+
829+
// We didn't find a null terminator, so return nullptr to indicate that there
830+
// is no valid C string at that offset.
831+
if (terminator_or_end == end)
832+
return nullptr;
833+
834+
// Update offset_ptr for the caller to point to the data behind the
835+
// terminator (which is 1 byte long).
836+
*offset_ptr += (terminator_or_end - start + 1UL);
837+
return start;
839838
}
840839

841840
// Extracts a NULL terminated C string from the fixed length field of length

lldb/unittests/Utility/DataExtractorTest.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,51 @@ TEST(DataExtractorTest, PeekData) {
4949
EXPECT_EQ(nullptr, E.PeekData(4, 1));
5050
}
5151

52+
TEST(DataExtractorTest, GetCStr) {
53+
uint8_t buffer[] = {'X', 'f', 'o', 'o', '\0'};
54+
DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4);
55+
56+
lldb::offset_t offset = 1;
57+
EXPECT_STREQ("foo", E.GetCStr(&offset));
58+
EXPECT_EQ(5U, offset);
59+
}
60+
61+
TEST(DataExtractorTest, GetCStrEmpty) {
62+
uint8_t buffer[] = {'X', '\0'};
63+
DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4);
64+
65+
lldb::offset_t offset = 1;
66+
EXPECT_STREQ("", E.GetCStr(&offset));
67+
EXPECT_EQ(2U, offset);
68+
}
69+
70+
TEST(DataExtractorTest, GetCStrUnterminated) {
71+
uint8_t buffer[] = {'X', 'f', 'o', 'o'};
72+
DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4);
73+
74+
lldb::offset_t offset = 1;
75+
EXPECT_EQ(nullptr, E.GetCStr(&offset));
76+
EXPECT_EQ(1U, offset);
77+
}
78+
79+
TEST(DataExtractorTest, GetCStrAtEnd) {
80+
uint8_t buffer[] = {'X'};
81+
DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4);
82+
83+
lldb::offset_t offset = 1;
84+
EXPECT_EQ(nullptr, E.GetCStr(&offset));
85+
EXPECT_EQ(1U, offset);
86+
}
87+
88+
TEST(DataExtractorTest, GetCStrAtNullOffset) {
89+
uint8_t buffer[] = {'f', 'o', 'o', '\0'};
90+
DataExtractor E(buffer, sizeof buffer, lldb::eByteOrderLittle, 4);
91+
92+
lldb::offset_t offset = 0;
93+
EXPECT_STREQ("foo", E.GetCStr(&offset));
94+
EXPECT_EQ(4U, offset);
95+
}
96+
5297
TEST(DataExtractorTest, GetMaxU64) {
5398
uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
5499
DataExtractor LE(buffer, sizeof(buffer), lldb::eByteOrderLittle,

0 commit comments

Comments
 (0)