[lldb] Handle partial memory region coverage in IRMemoryMap::FindSpace (#194001)

FindSpace walks process memory regions to find addresses that won't
collide with the inferior's real memory. This is necessary even for
host-only allocations because the IR Interpreter works entirely in
inferior virtual addresses. IRMemoryMap remaps those addresses to read
from host memory instead of inferior memory, so overlapping ranges would
silently read the wrong data.

For WebAssembly, GetMemoryRegionInfo succeeds for the first region
(linear memory) but fails for addresses beyond it. The previous fix
(#193124) skipped the memory region walk entirely when `CanJIT()` is
false. However, as Jason points out, that removes the
collision-avoidance mechanism and risks overlapping with real inferior
memory.

Instead, handle the GetMemoryRegionInfo failure gracefully. If the
target can't describe memory beyond a certain point, treat the remaining
address space as unmapped and use it for the allocation. This preserves
the collision avoidance while avoiding the lldbassert.
This commit is contained in:
Jonas Devlieghere
2026-04-24 17:34:09 -07:00
committed by GitHub
parent e38b8da23b
commit 0c472c1401
2 changed files with 12 additions and 14 deletions

View File

@@ -111,11 +111,7 @@ lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
// Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped
// regions, walk forward through memory until a region is found that has
// adequate space for our allocation.
//
// Skip this when the process can't JIT. In that case, allocations are
// host-only and never written to process memory, so there's no need to probe
// the process's memory map.
if (process_is_alive && process_sp->CanJIT()) {
if (process_is_alive) {
MemoryRegionInfo region_info;
Status err = process_sp->GetMemoryRegionInfo(ret, region_info);
if (err.Success()) {
@@ -141,12 +137,14 @@ lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
// ret stays the same. We just need to walk a bit further.
}
// FIXME: When we're able to JIT WebAssembly, this strategy won't work
// because we might probe beyond its linear memory.
err = process_sp->GetMemoryRegionInfo(
region_info.GetRange().GetRangeEnd(), region_info);
if (err.Fail()) {
lldbassert(0 && "GetMemoryRegionInfo() succeeded, then failed");
// The target can't describe memory beyond this point (e.g.
// WebAssembly linear memory). Treat the remaining address space
// as unmapped.
if (ret + size < end_of_memory)
return ret;
ret = LLDB_INVALID_ADDRESS;
break;
}

View File

@@ -79,10 +79,8 @@ public:
} // namespace
// Verify that host-only allocations succeed when the process is alive but
// can't JIT. Before the fix, FindSpace would probe GetMemoryRegionInfo,
// which could assert/crash for targets with partial address-space coverage.
TEST_F(IRMemoryMapTest, FindSpaceNoJIT) {
// Verify that FindSpace handles partial memory region coverage gracefully.
TEST_F(IRMemoryMapTest, FindSpacePartialRegionCoverage) {
ArchSpec arch("i386-pc-linux");
Platform::SetHostPlatform(
platform_linux::PlatformLinux::CreateInstance(true, &arch));
@@ -105,13 +103,15 @@ TEST_F(IRMemoryMapTest, FindSpaceNoJIT) {
TestIRMemoryMap memory_map(target_sp);
memory_map.SetProcess(process_sp);
// This would previously crash in FindSpace via lldbassert when
// GetMemoryRegionInfo succeeded then failed on a subsequent call.
// FindSpace treats the remaining address space as unmapped.
auto addr_or_err =
memory_map.Malloc(1024, 8, ePermissionsReadable | ePermissionsWritable,
IRMemoryMap::eAllocationPolicyHostOnly, false);
ASSERT_THAT_EXPECTED(addr_or_err, llvm::Succeeded());
EXPECT_NE(*addr_or_err, LLDB_INVALID_ADDRESS);
// The allocation must not overlap with the inferior's mapped region
// [0, 0x10000).
EXPECT_GE(*addr_or_err, 0x10000ULL);
// A second allocation should also succeed and not overlap.
auto addr2_or_err =