mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 01:58:44 +08:00
[lldb] Handle an empty SBMemoryRegionInfo from scripted process (#115963)
A scripted process implementation might return an SBMemoryRegionInfo object in its implementation of `get_memory_region_containing_address` which will have an address 0 and size 0, without realizing the problems this can cause. Several algorithms in lldb will try to iterate over the MemoryRegions of the process, starting at address 0 and expecting to iterate up to the highest vm address, stepping by the size of each region, so a 0-length region will result in an infinite loop. Add a check to Process::GetMemoryRegionInfo that rejects a MemoryRegion which does not contain the requested address; a 0-length memory region will therefor always be rejected. rdar://139678032
This commit is contained in:
@@ -6184,7 +6184,12 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr,
|
||||
MemoryRegionInfo &range_info) {
|
||||
if (const lldb::ABISP &abi = GetABI())
|
||||
load_addr = abi->FixAnyAddress(load_addr);
|
||||
return DoGetMemoryRegionInfo(load_addr, range_info);
|
||||
Status error = DoGetMemoryRegionInfo(load_addr, range_info);
|
||||
// Reject a region that does not contain the requested address.
|
||||
if (error.Success() && !range_info.GetRange().Contains(load_addr))
|
||||
error = Status::FromErrorString("Invalid memory region");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
C_SOURCES := main.c
|
||||
|
||||
include Makefile.rules
|
||||
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
Test python scripted process which returns an empty SBMemoryRegionInfo
|
||||
"""
|
||||
|
||||
import os, shutil
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
from lldbsuite.test import lldbtest
|
||||
|
||||
|
||||
class ScriptedProcessEmptyMemoryRegion(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def test_scripted_process_empty_memory_region(self):
|
||||
"""Test that lldb handles an empty SBMemoryRegionInfo object from
|
||||
a scripted process plugin."""
|
||||
self.build()
|
||||
|
||||
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
scripted_process_example_relpath = "dummy_scripted_process.py"
|
||||
self.runCmd(
|
||||
"command script import "
|
||||
+ os.path.join(self.getSourceDir(), scripted_process_example_relpath)
|
||||
)
|
||||
|
||||
self.expect("memory region 0", error=True, substrs=["Invalid memory region"])
|
||||
|
||||
self.expect("expr -- 5", substrs=["5"])
|
||||
@@ -0,0 +1,110 @@
|
||||
import os, struct, signal
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
import lldb
|
||||
from lldb.plugins.scripted_process import ScriptedProcess
|
||||
from lldb.plugins.scripted_process import ScriptedThread
|
||||
|
||||
|
||||
class DummyScriptedProcess(ScriptedProcess):
|
||||
memory = None
|
||||
|
||||
def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
|
||||
super().__init__(exe_ctx, args)
|
||||
self.threads[0] = DummyScriptedThread(self, None)
|
||||
self.memory = {}
|
||||
addr = 0x500000000
|
||||
debugger = self.target.GetDebugger()
|
||||
index = debugger.GetIndexOfTarget(self.target)
|
||||
self.memory[addr] = "Hello, target " + str(index)
|
||||
self.handled_stop = False
|
||||
|
||||
def read_memory_at_address(
|
||||
self, addr: int, size: int, error: lldb.SBError
|
||||
) -> lldb.SBData:
|
||||
data = lldb.SBData().CreateDataFromCString(
|
||||
self.target.GetByteOrder(), self.target.GetCodeByteSize(), self.memory[addr]
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def get_memory_region_containing_address(
|
||||
self, addr: int
|
||||
) -> lldb.SBMemoryRegionInfo:
|
||||
return lldb.SBMemoryRegionInfo()
|
||||
|
||||
def write_memory_at_address(self, addr, data, error):
|
||||
self.memory[addr] = data.GetString(error, 0)
|
||||
return len(self.memory[addr]) + 1
|
||||
|
||||
def get_loaded_images(self):
|
||||
return self.loaded_images
|
||||
|
||||
def get_process_id(self) -> int:
|
||||
return 42
|
||||
|
||||
def should_stop(self) -> bool:
|
||||
return True
|
||||
|
||||
def is_alive(self) -> bool:
|
||||
return True
|
||||
|
||||
def get_scripted_thread_plugin(self):
|
||||
return DummyScriptedThread.__module__ + "." + DummyScriptedThread.__name__
|
||||
|
||||
def my_super_secret_method(self):
|
||||
if hasattr(self, "my_super_secret_member"):
|
||||
return self.my_super_secret_member
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class DummyScriptedThread(ScriptedThread):
|
||||
def __init__(self, process, args):
|
||||
super().__init__(process, args)
|
||||
self.frames.append({"pc": 0x0100001B00})
|
||||
|
||||
def get_thread_id(self) -> int:
|
||||
return 0x19
|
||||
|
||||
def get_name(self) -> str:
|
||||
return DummyScriptedThread.__name__ + ".thread-1"
|
||||
|
||||
def get_state(self) -> int:
|
||||
return lldb.eStateStopped
|
||||
|
||||
def get_stop_reason(self) -> Dict[str, Any]:
|
||||
return {"type": lldb.eStopReasonTrace, "data": {}}
|
||||
|
||||
def get_register_context(self) -> str:
|
||||
return struct.pack(
|
||||
"21Q",
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
)
|
||||
|
||||
|
||||
def __lldb_init_module(debugger, dict):
|
||||
debugger.HandleCommand(
|
||||
"process launch -C %s.%s" % (__name__, DummyScriptedProcess.__name__)
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
int main() {}
|
||||
Reference in New Issue
Block a user