vmmap: fix riscv64 kernel (#3252)

* vmmap: fix crash with pi.markers

* fix refactor

* vmmap: fix riscv64 `info mem`
pull/3258/head
patryk4815 4 months ago committed by GitHub
parent 89095d1214
commit 260a7204a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -427,49 +427,33 @@ class Aarch64Ops(ArchOps):
return int(pwndbg.aglib.regs.SCTLR) & BIT(0) != 0 return int(pwndbg.aglib.regs.SCTLR) & BIT(0) != 0
_arch_paginginfo: ArchPagingInfo = None
@pwndbg.lib.cache.cache_until("start") @pwndbg.lib.cache.cache_until("start")
def arch_paginginfo() -> ArchPagingInfo: def arch_paginginfo() -> ArchPagingInfo | None:
global _arch_paginginfo if pwndbg.aglib.arch.name == "aarch64":
if _arch_paginginfo is None: return pwndbg.aglib.kernel.paging.Aarch64PagingInfo()
if pwndbg.aglib.arch.name == "aarch64": elif pwndbg.aglib.arch.name == "x86-64":
_arch_paginginfo = pwndbg.aglib.kernel.paging.Aarch64PagingInfo() return pwndbg.aglib.kernel.paging.x86_64PagingInfo()
elif pwndbg.aglib.arch.name == "x86-64": return None
_arch_paginginfo = pwndbg.aglib.kernel.paging.x86_64PagingInfo()
return _arch_paginginfo
_arch_ops: ArchOps = None
@pwndbg.lib.cache.cache_until("start") @pwndbg.lib.cache.cache_until("start")
def arch_ops() -> ArchOps: def arch_ops() -> ArchOps | None:
global _arch_ops if pwndbg.aglib.arch.name == "aarch64":
if _arch_ops is None: return Aarch64Ops()
if pwndbg.aglib.arch.name == "aarch64": elif pwndbg.aglib.arch.name == "x86-64":
_arch_ops = Aarch64Ops() return x86_64Ops()
elif pwndbg.aglib.arch.name == "x86-64": elif pwndbg.aglib.arch.name == "i386":
_arch_ops = x86_64Ops() return i386Ops()
elif pwndbg.aglib.arch.name == "i386": return None
_arch_ops = i386Ops()
return _arch_ops
_arch_symbols: pwndbg.aglib.kernel.symbol.ArchSymbols = None
def arch_symbols() -> pwndbg.aglib.kernel.symbol.ArchSymbols:
global _arch_symbols
if _arch_symbols is None:
if pwndbg.aglib.arch.name == "aarch64":
_arch_symbols = pwndbg.aglib.kernel.symbol.Aarch64Symbols()
elif pwndbg.aglib.arch.name == "x86-64":
_arch_symbols = pwndbg.aglib.kernel.symbol.x86_64Symbols()
return _arch_symbols @pwndbg.lib.cache.cache_until("start")
def arch_symbols() -> pwndbg.aglib.kernel.symbol.ArchSymbols | None:
if pwndbg.aglib.arch.name == "aarch64":
return pwndbg.aglib.kernel.symbol.Aarch64Symbols()
elif pwndbg.aglib.arch.name == "x86-64":
return pwndbg.aglib.kernel.symbol.x86_64Symbols()
return None
def ptr_size() -> int: def ptr_size() -> int:

@ -32,9 +32,8 @@ class KernelVmmap:
self.pages = pages self.pages = pages
self.sections = None self.sections = None
self.pi = pwndbg.aglib.kernel.arch_paginginfo() self.pi = pwndbg.aglib.kernel.arch_paginginfo()
if self.pi and not pwndbg.aglib.kernel.has_debug_symbols(): if self.pi and pwndbg.aglib.kernel.has_debug_symbols():
return self.sections = self.pi.markers()
self.sections = self.pi.markers()
def get_name(self, addr: int) -> str: def get_name(self, addr: int) -> str:
if addr is None or self.sections is None: if addr is None or self.sections is None:
@ -44,12 +43,12 @@ class KernelVmmap:
_, next = self.sections[i + 1] _, next = self.sections[i + 1]
if cur is None or next is None: if cur is None or next is None:
continue continue
if addr >= cur and addr < next: if cur <= addr < next:
return name return name
return None return None
def adjust(self): def adjust(self):
if self.pages is None or len(self.pages) == 0: if self.pi is None or self.pages is None or len(self.pages) == 0:
return return
for i, page in enumerate(self.pages): for i, page in enumerate(self.pages):
name = self.get_name(page.start) name = self.get_name(page.start)
@ -248,6 +247,84 @@ def kernel_vmmap_via_page_tables() -> Tuple[pwndbg.lib.memory.Page, ...]:
monitor_info_mem_not_warned = True monitor_info_mem_not_warned = True
def _parser_mem_info_line_x86(line: str) -> pwndbg.lib.memory.Page | None:
"""
Example response from `info mem`:
```
ffff903580000000-ffff903580099000 0000000000099000 -rw
ffff903580099000-ffff90358009b000 0000000000002000 -r-
ffff90358009b000-ffff903582200000 0000000002165000 -rw
ffff903582200000-ffff903582803000 0000000000603000 -r-
```
"""
dash_idx = line.index("-")
space_idx = line.index(" ")
rspace_idx = line.rindex(" ")
start = int(line[:dash_idx], 16)
end = int(line[dash_idx + 1 : space_idx], 16)
size = int(line[space_idx + 1 : rspace_idx], 16)
perm = line[rspace_idx + 1 :]
flags = 0
if "r" in perm:
flags |= 4
if "w" in perm:
flags |= 2
if "x" in perm:
flags |= 1
global monitor_info_mem_not_warned
if end - start != size and monitor_info_mem_not_warned:
print(
M.warn(
(
"The vmmap output may be incorrect as `monitor info mem` output assertion/assumption\n"
"that end-start==size failed. The values are:\n"
"end=%#x; start=%#x; size=%#x; end-start=%#x\n"
"Note that this warning will not show up again in this Pwndbg/GDB session."
)
% (end, start, size, end - start)
)
)
monitor_info_mem_not_warned = False
return pwndbg.lib.memory.Page(start, size, flags, 0, "<qemu>")
def _parser_mem_info_line_riscv64(line: str) -> pwndbg.lib.memory.Page | None:
"""
Example response from `info mem`:
```
vaddr paddr size attr
---------------- ---------------- ---------------- -------
0000000000010000 00000000feece000 0000000000001000 r-xu-a-
0000000000011000 00000000fefeb000 0000000000002000 r-xu-a-
0000000000013000 00000000a0a7a000 0000000000002000 r-xu-a-
0000000000015000 00000000bfe02000 0000000000002000 r-xu-a-
```
"""
arr = line.split(" ", 3)
if len(arr) != 4:
raise ValueError("invalid line format")
start, _, size, perm = arr
start = int(start, 16)
size = int(size, 16)
flags = 0
if "r" in perm:
flags |= 4
if "w" in perm:
flags |= 2
if "x" in perm:
flags |= 1
return pwndbg.lib.memory.Page(start, size, flags, 0, "<qemu>")
@pwndbg.lib.cache.cache_until("stop") @pwndbg.lib.cache.cache_until("stop")
def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]: def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]:
""" """
@ -260,13 +337,6 @@ def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]:
See also: https://github.com/pwndbg/pwndbg/pull/685 See also: https://github.com/pwndbg/pwndbg/pull/685
(TODO: revisit with future QEMU versions) (TODO: revisit with future QEMU versions)
# Example output from the command:
# pwndbg> monitor info mem
# ffff903580000000-ffff903580099000 0000000000099000 -rw
# ffff903580099000-ffff90358009b000 0000000000002000 -r-
# ffff90358009b000-ffff903582200000 0000000002165000 -rw
# ffff903582200000-ffff903582803000 0000000000603000 -r-
""" """
if not pwndbg.aglib.qemu.is_qemu_kernel(): if not pwndbg.aglib.qemu.is_qemu_kernel():
return () return ()
@ -275,69 +345,35 @@ def kernel_vmmap_via_monitor_info_mem() -> Tuple[pwndbg.lib.memory.Page, ...]:
monitor_info_mem = pwndbg.dbg.selected_inferior().send_monitor("info mem") monitor_info_mem = pwndbg.dbg.selected_inferior().send_monitor("info mem")
except pwndbg.dbg_mod.Error: except pwndbg.dbg_mod.Error:
# Exception should not happen in new qemu, can we clean up it? # Exception should not happen in new qemu, can we clean up it?
monitor_info_mem = None
is_error = monitor_info_mem is None or "unknown command" in monitor_info_mem
if is_error:
# Older versions of QEMU/GDB may throw `gdb.error: "monitor" command # Older versions of QEMU/GDB may throw `gdb.error: "monitor" command
# not supported by this target`. Newer versions will not throw, but will # not supported by this target`. Newer versions will not throw, but will
# return a string starting with 'unknown command:'. We handle both of # return a string starting with 'unknown command:'.
# these cases in a `finally` block instead of an `except` block. monitor_info_mem = "unknown command"
# TODO: Find out which other architectures don't support this command
if pwndbg.aglib.arch.name == "aarch64":
print(
M.error(
f"The {pwndbg.aglib.arch.name} architecture does"
" not support the `monitor info mem` command.\n"
"Run `help show kernel-vmmap` for other options."
)
)
return ()
lines = monitor_info_mem.splitlines() parser_func = None
if pwndbg.aglib.arch.name in ("i386", "x86-64"):
parser_func = _parser_mem_info_line_x86
elif pwndbg.aglib.arch.name == "rv64":
parser_func = _parser_mem_info_line_riscv64
# Handle disabled PG if parser_func is None or "unknown command" in monitor_info_mem:
# This will prevent a crash on abstract architectures print(
if len(lines) == 1 and lines[0] == "PG disabled": M.error(
f"The {pwndbg.aglib.arch.name} architecture does"
" not support the `monitor info mem` command.\n"
"Run `help show kernel-vmmap` for other options."
)
)
return () return ()
global monitor_info_mem_not_warned
pages: List[pwndbg.lib.memory.Page] = [] pages: List[pwndbg.lib.memory.Page] = []
for line in lines: for line in monitor_info_mem.splitlines():
try: try:
dash_idx = line.index("-") page = parser_func(line)
space_idx = line.index(" ")
rspace_idx = line.rindex(" ")
start = int(line[:dash_idx], 16)
end = int(line[dash_idx + 1 : space_idx], 16)
size = int(line[space_idx + 1 : rspace_idx], 16)
except Exception: except Exception:
# invalid format # invalid format
continue continue
if end - start != size and monitor_info_mem_not_warned: pages.append(page)
print(
M.warn(
(
"The vmmap output may be incorrect as `monitor info mem` output assertion/assumption\n"
"that end-start==size failed. The values are:\n"
"end=%#x; start=%#x; size=%#x; end-start=%#x\n"
"Note that this warning will not show up again in this Pwndbg/GDB session."
)
% (end, start, size, end - start)
)
)
monitor_info_mem_not_warned = False
perm = line[rspace_idx + 1 :]
flags = 0
if "r" in perm:
flags |= 4
if "w" in perm:
flags |= 2
if "x" in perm:
flags |= 1
pages.append(pwndbg.lib.memory.Page(start, size, flags, 0, "<qemu>"))
return tuple(pages) return tuple(pages)

Loading…
Cancel
Save