diff --git a/pwndbg/commands/buddydump.py b/pwndbg/commands/buddydump.py index fa1deecda..d4f944dc1 100644 --- a/pwndbg/commands/buddydump.py +++ b/pwndbg/commands/buddydump.py @@ -122,7 +122,7 @@ def check_find(counter: int, physmap_addr: int, pba: ParsedBuddyArgs, cbp: Curre if pba.find is None: return False start = physmap_addr - end = physmap_addr + (1 << cbp.order) + end = physmap_addr + 0x1000 * (1 << cbp.order) return pba.find >= start and pba.find < end @@ -343,5 +343,5 @@ def buddydump( print_pcp_set(pba, cbp) if not pcp_only: print_free_area(pba, cbp) - if not cbp.found: - log.warning("No free pages with specified filters found.") + if not cbp.found: + log.warning("No free pages with specified filters found.") diff --git a/pwndbg/dbg/gdb/__init__.py b/pwndbg/dbg/gdb/__init__.py index b560e57d5..c033ab0d3 100644 --- a/pwndbg/dbg/gdb/__init__.py +++ b/pwndbg/dbg/gdb/__init__.py @@ -1431,6 +1431,11 @@ class GDB(pwndbg.dbg_mod.Debugger): f"alias -a {deprecated_cmd} = echo Use `{fixed_cmd}` instead (Pwndbg changed `_` to `-` in command names)\\n" ) + for deprecated_cmd, new_cmd in (("pcplist", "buddydump"),): + gdb.execute( + f"alias -a {deprecated_cmd} = echo deprecation warning for old name, use `{new_cmd}` instead\\n" + ) + # This may throw an exception, see pwndbg/pwndbg#27 try: gdb.execute("set disassembly-flavor intel") diff --git a/tests/qemu-tests/tests/system/test_commands_kernel.py b/tests/qemu-tests/tests/system/test_commands_kernel.py index 6cdadb993..a130d5525 100644 --- a/tests/qemu-tests/tests/system/test_commands_kernel.py +++ b/tests/qemu-tests/tests/system/test_commands_kernel.py @@ -1,5 +1,8 @@ from __future__ import annotations +import random +import re + import gdb import pytest @@ -90,8 +93,6 @@ def test_x64_extra_registers_under_kernel_mode(): def get_slab_object_address(): """helper function to get the address of some kmalloc slab object and the associated slab cache name""" - import re - caches = pwndbg.aglib.kernel.slab.caches() for cache in caches: cache_name = cache.name @@ -135,6 +136,42 @@ def test_command_msr_write(): ) def test_command_buddydump(): res = gdb.execute("buddydump", to_string=True) - assert ( - "Order" in res and "Zone" in res and ("per_cpu_pageset" in res or "free_area" in res) - ) or res == "WARNING: Symbol 'node_data' not found\n" + NOFREEPAGE = "No free pages with specified filters found.\n" + if res == "WARNING: Symbol 'node_data' not found\n" or NOFREEPAGE == res: + return + # this indicates the buddy allocator contains at least one entry + assert "Order" in res and "Zone" in res and ("per_cpu_pageset" in res or "free_area" in res) + + ansi_escape = re.compile(r"\x1b\[[0-9;]*m") + res = ansi_escape.sub("", res) + # find the starting addresses of all entries within the freelists + matches = re.findall(r"\[0x[0-9a-fA-F\-]{2}\] (0x[0-9a-fA-F]{16})", res) + for i in range(0, len(matches), 20): + # check every 20 elements so tests do not take too long + match = int(matches[i], 16) + res = gdb.execute(f"bud -f {hex(match + random.randint(0, 0x1000 - 1))}", to_string=True) + res = ansi_escape.sub("", res) + _matches = re.findall(r"\[0x[0-9a-fA-F\-]{2}\] (0x[0-9a-fA-F]{16})", res) + # asserting `bud -f` behaviour -- should be able to find the corresponding entry to an address + # even if the address is not aligned + assert len(_matches) == 1 and int(_matches[0], 16) == match + + # nonexistent node index should not contain any entries + no_output = gdb.execute("buddydump -n 10", to_string=True) + assert NOFREEPAGE == no_output + + # below checks are for filters + # for example, if a zone name is specified, other zones should not be present + filter_res = gdb.execute("bud -z DMA", to_string=True) + for name in ["DMA32", "Normal", "HighMem", "Movable", "Device"]: + assert f"Zone {name}" not in filter_res + filter_res = gdb.execute("bud -m Unmovable", to_string=True) + for name in ["Movable", "Reclaimable", "HighAtomic", "CMA", "Isolate"]: + assert f"- {name}" not in filter_res + filter_res = gdb.execute("bud -o 1", to_string=True) + for i in range(11): + if i == 1: + continue + assert f"Order {i}" not in filter_res + filter_res = gdb.execute("bud -p", to_string=True) + assert "free_area" not in filter_res