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
11 changes: 11 additions & 0 deletions lldb/source/Target/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len,
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
MemoryCache::ReadRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
llvm::MutableArrayRef<uint8_t> buffer) {
// A cache hit writes into `buffer` below, so check its size before that
// write. Fail the same way Process::DoReadMemoryRanges does.
auto total_ranges_len = llvm::sum_of(
llvm::map_range(ranges, [](auto range) { return range.size; }));
assert(buffer.size() >= total_ranges_len &&
"MemoryCache::ReadRanges: provided buffer is too short");
if (buffer.size() < total_ranges_len) {
llvm::MutableArrayRef<uint8_t> empty;
return {ranges.size(), empty};
}

std::lock_guard<std::recursive_mutex> guard(m_mutex);

llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> results;
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2102,7 +2102,8 @@ Process::DoReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
// If the buffer is not large enough, this is a programmer error.
// In production builds, gracefully fail by returning a length of 0 for all
// ranges.
assert(buffer.size() >= total_ranges_len && "provided buffer is too short");
assert(buffer.size() >= total_ranges_len &&
"Process::DoReadMemoryRanges: provided buffer is too short");
if (buffer.size() < total_ranges_len) {
llvm::MutableArrayRef<uint8_t> empty;
return {ranges.size(), empty};
Expand Down
39 changes: 38 additions & 1 deletion lldb/unittests/Target/MemoryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,37 @@ TEST_F(MemoryDeathTest, TestReadMemoryRangesReturnsTooMuch) {
#endif
}

TEST_F(MemoryDeathTest, TestReadRangesWithShortBufferAndCacheHit) {
GTEST_FLAG_SET(death_test_style, "threadsafe");

ArchSpec arch("arm64-apple-macosx");
Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
DebuggerSP debugger_sp = Debugger::CreateInstance();
ASSERT_TRUE(debugger_sp);
TargetSP target_sp = CreateTarget(debugger_sp, arch);
ASSERT_TRUE(target_sp);
ProcessSP process_sp = CreateProcess(target_sp);
ASSERT_TRUE(process_sp);

DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
TestMemoryCache cache(*process);
cache.AddL1CacheData(0x1000, std::make_shared<DataBufferHeap>(16, 0xAA));
ASSERT_EQ(cache.GetL1Cache().count(0x1000), 1u);

llvm::SmallVector<uint8_t, 0> short_buffer(8, 0);
llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x1000, 16}};
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
ASSERT_DEBUG_DEATH(
{ read_results = cache.ReadRanges(ranges, short_buffer); },
"MemoryCache::ReadRanges: provided buffer is too short");
#ifdef NDEBUG
// With asserts off, the ranges come back empty instead.
ASSERT_EQ(read_results.size(), ranges.size());
for (llvm::MutableArrayRef<uint8_t> result : read_results)
ASSERT_TRUE(result.empty());
#endif
}

TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) {
// gtest death-tests execute in a sub-process (fork), which invalidates
// any signpost handles and would cause spurious crashes if used. Use the
Expand All @@ -710,13 +741,19 @@ TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) {
std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
ASSERT_TRUE(process_sp);

// Memory cache has to be off to reach the one in Process::DoReadMemoryRanges.
Status set_error = process_sp->SetPropertyValue(
nullptr, eVarSetOperationAssign, "disable-memory-cache", "true");
ASSERT_TRUE(set_error.Success()) << set_error.AsCString();
ASSERT_TRUE(process_sp->GetDisableMemoryCache());

llvm::SmallVector<uint8_t, 0> short_buffer(10, 0);
llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128},
{0x11, 128}};
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
ASSERT_DEBUG_DEATH(
{ read_results = process_sp->ReadMemoryRanges(ranges, short_buffer); },
"provided buffer is too short");
"Process::DoReadMemoryRanges: provided buffer is too short");
#ifdef NDEBUG
// With asserts off, the read should return empty ranges.
ASSERT_EQ(read_results.size(), ranges.size());
Expand Down
Loading