NationalSecurityAgency/ghidra · error · RuntimeError
Cannot read memory at {start:x}
Error message
Cannot read memory at {start:x} What it means
Raised inside `put_bytes` (used by putmem, putmem-state, and the put-* hooks) when `proc.ReadMemory(start, size, error)` returns a non-success error or a null buffer. The offending start address is reported in hex. It usually means the address is unmapped or unreadable, or the process is not stopped at a state where that region is accessible.
Source
Thrown at Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py:675
if error.Success() and buf is not None:
base, addr = trace.extra.require_mm().map(proc, start)
if base != addr.space:
trace.create_overlay_space(base, addr.space)
count = trace.put_bytes(addr, buf)
if result is None:
pass
elif isinstance(count, Future):
if count.done():
result.PutCString(f"Wrote {count.result()} bytes")
else:
count.add_done_callback(lambda c: check_count(c.result(), len(buf)))
result.PutCString(
f"Wrong {len(buf)} bytes, perhaps in the future")
else:
result.PutCString(f"Wrote {count} bytes")
else:
raise RuntimeError(f"Cannot read memory at {start:x}")
def eval_address(address: str) -> Optional[int]:
try:
return util.parse_and_eval(address)
except BaseException as e:
if "can't evaluate expressions when the process is running" not in str(e):
raise RuntimeError(f"Cannot convert '{address}' to address: {e}")
return None
def eval_range(address: str, length: str) -> Tuple[Optional[int], Optional[int]]:
start = eval_address(address)
if start is None:
return None, None
try:
end = start + util.parse_and_eval(length)
except BaseException as e:View on GitHub (pinned to d5f144c24d)
Solutions
- Verify the address is mapped and readable in LLDB first: `memory read --size 1 --count 16 0x<addr>`
- Ensure the process is stopped (not running) before putting memory
- Re-derive the address from a current symbol or expression instead of a hardcoded value
- If page quantization is on, confirm the whole page is readable and not a guard/no-access page
Example fix
// before ghidra trace putmem 0xdeadbeef 0x10 // after (lldb) memory read --size 1 --count 16 0x1000 # confirm readable ghidra trace putmem 0x1000 0x10
Defensive patterns
Strategy: validation
Validate before calling
import lldb
def memory_readable(start: int, size: int) -> bool:
proc = lldb.debugger.GetTargetAtIndex(0).process
if proc is None or not proc.IsValid():
return False
err = lldb.SBError()
buf = proc.ReadMemory(start, size, err)
return err.Success() and buf is not None Type guard
import lldb
def is_readable_region(start: int, size: int) -> bool:
proc = lldb.debugger.GetTargetAtIndex(0).process
if proc is None:
return False
info = lldb.SBMemoryRegionInfo()
if proc.GetMemoryRegionInfo(start, info).Success():
return info.IsReadable()
return False Try / catch
try:
put_bytes(start, end, result, pages)
except RuntimeError as e:
if str(e).startswith('Cannot read memory at '):
# the address is unmapped/unreadable or the process is not stopped;
# re-derive start from a live symbol or skip this range
...
raise Prevention
- Verify readability with `memory read` before putting memory
- Ensure the process is stopped, not running
- Derive addresses from current symbols rather than hardcoded values to survive ASLR
- Watch for guard/no-access pages when page quantization is enabled
When it happens
Trigger: `ghidra trace putmem 0xdeadbeef 0x10` on an unmapped address; reading a guard or no-access page; the process is running so the region is unavailable; an address expression that resolved to a now-stale location after ASLR/relocation.
Common situations: Putting memory before the process is loaded or stopped; a hardcoded address from a previous run that no longer maps; ASLR moved the image; reading a protected page.
Related errors
- Cannot convert '{address}' to address: {e}
- Failed to read memory
- Cannot convert '{address}' to address
- Cannot convert '{length}' to length
- Value '{address}' does not evaluate to an int
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/9dc31e7f344cce70.
Report an issue: GitHub.