Skip to content

Commit a1a1a4c

Browse files
authored
[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
1 parent 87bfa58 commit a1a1a4c

File tree

5 files changed

+153
-1
lines changed

5 files changed

+153
-1
lines changed

lldb/source/Target/Process.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6184,7 +6184,12 @@ Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr,
61846184
MemoryRegionInfo &range_info) {
61856185
if (const lldb::ABISP &abi = GetABI())
61866186
load_addr = abi->FixAnyAddress(load_addr);
6187-
return DoGetMemoryRegionInfo(load_addr, range_info);
6187+
Status error = DoGetMemoryRegionInfo(load_addr, range_info);
6188+
// Reject a region that does not contain the requested address.
6189+
if (error.Success() && !range_info.GetRange().Contains(load_addr))
6190+
error = Status::FromErrorString("Invalid memory region");
6191+
6192+
return error;
61886193
}
61896194

61906195
Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos &region_list) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Test python scripted process which returns an empty SBMemoryRegionInfo
3+
"""
4+
5+
import os, shutil
6+
7+
import lldb
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
from lldbsuite.test import lldbutil
11+
from lldbsuite.test import lldbtest
12+
13+
14+
class ScriptedProcessEmptyMemoryRegion(TestBase):
15+
NO_DEBUG_INFO_TESTCASE = True
16+
17+
def test_scripted_process_empty_memory_region(self):
18+
"""Test that lldb handles an empty SBMemoryRegionInfo object from
19+
a scripted process plugin."""
20+
self.build()
21+
22+
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
23+
self.assertTrue(target, VALID_TARGET)
24+
25+
scripted_process_example_relpath = "dummy_scripted_process.py"
26+
self.runCmd(
27+
"command script import "
28+
+ os.path.join(self.getSourceDir(), scripted_process_example_relpath)
29+
)
30+
31+
self.expect("memory region 0", error=True, substrs=["Invalid memory region"])
32+
33+
self.expect("expr -- 5", substrs=["5"])
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import os, struct, signal
2+
3+
from typing import Any, Dict
4+
5+
import lldb
6+
from lldb.plugins.scripted_process import ScriptedProcess
7+
from lldb.plugins.scripted_process import ScriptedThread
8+
9+
10+
class DummyScriptedProcess(ScriptedProcess):
11+
memory = None
12+
13+
def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
14+
super().__init__(exe_ctx, args)
15+
self.threads[0] = DummyScriptedThread(self, None)
16+
self.memory = {}
17+
addr = 0x500000000
18+
debugger = self.target.GetDebugger()
19+
index = debugger.GetIndexOfTarget(self.target)
20+
self.memory[addr] = "Hello, target " + str(index)
21+
self.handled_stop = False
22+
23+
def read_memory_at_address(
24+
self, addr: int, size: int, error: lldb.SBError
25+
) -> lldb.SBData:
26+
data = lldb.SBData().CreateDataFromCString(
27+
self.target.GetByteOrder(), self.target.GetCodeByteSize(), self.memory[addr]
28+
)
29+
30+
return data
31+
32+
def get_memory_region_containing_address(
33+
self, addr: int
34+
) -> lldb.SBMemoryRegionInfo:
35+
return lldb.SBMemoryRegionInfo()
36+
37+
def write_memory_at_address(self, addr, data, error):
38+
self.memory[addr] = data.GetString(error, 0)
39+
return len(self.memory[addr]) + 1
40+
41+
def get_loaded_images(self):
42+
return self.loaded_images
43+
44+
def get_process_id(self) -> int:
45+
return 42
46+
47+
def should_stop(self) -> bool:
48+
return True
49+
50+
def is_alive(self) -> bool:
51+
return True
52+
53+
def get_scripted_thread_plugin(self):
54+
return DummyScriptedThread.__module__ + "." + DummyScriptedThread.__name__
55+
56+
def my_super_secret_method(self):
57+
if hasattr(self, "my_super_secret_member"):
58+
return self.my_super_secret_member
59+
else:
60+
return None
61+
62+
63+
class DummyScriptedThread(ScriptedThread):
64+
def __init__(self, process, args):
65+
super().__init__(process, args)
66+
self.frames.append({"pc": 0x0100001B00})
67+
68+
def get_thread_id(self) -> int:
69+
return 0x19
70+
71+
def get_name(self) -> str:
72+
return DummyScriptedThread.__name__ + ".thread-1"
73+
74+
def get_state(self) -> int:
75+
return lldb.eStateStopped
76+
77+
def get_stop_reason(self) -> Dict[str, Any]:
78+
return {"type": lldb.eStopReasonTrace, "data": {}}
79+
80+
def get_register_context(self) -> str:
81+
return struct.pack(
82+
"21Q",
83+
1,
84+
2,
85+
3,
86+
4,
87+
5,
88+
6,
89+
7,
90+
8,
91+
9,
92+
10,
93+
11,
94+
12,
95+
13,
96+
14,
97+
15,
98+
16,
99+
17,
100+
18,
101+
19,
102+
20,
103+
21,
104+
)
105+
106+
107+
def __lldb_init_module(debugger, dict):
108+
debugger.HandleCommand(
109+
"process launch -C %s.%s" % (__name__, DummyScriptedProcess.__name__)
110+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int main() {}

0 commit comments

Comments
 (0)