diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 8e207afd3..3623db1f8 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -77,7 +77,7 @@ jobs: - name: Run test coverage if: matrix.os == 'ubuntu-22.04' env: - ALLOWED_MARGIN: 0.05 + ALLOWED_MARGIN: 0.01 MIN_COVERAGE: 70 run: | current_score=$(curl --silent https://hugsy.github.io/gef/coverage/gef_py.html | grep pc_cov | sed 's?.*\([^%]*\)%?\1?g') @@ -85,4 +85,4 @@ jobs: new_score=$(cat docs/coverage/gef_py.html | grep pc_cov | sed 's?.*\([^%]*\)%?\1?g') echo "New coverage score: ${new_score}% (current ${current_score}%)" python${{ env.PY_VER }} -c "( ${new_score} < ${{ env.MIN_COVERAGE}} ) and exit(1)" - python${{ env.PY_VER }} -c "( ${new_score} < ( ${current_score} - ${{ env.ALLOWED_MARGIN}} ) ) and exit(2)" + python${{ env.PY_VER }} -c "(( abs( ${current_score}-${new_score}) / ${current_score} ) >= ${{ env.ALLOWED_MARGIN}}) and exit(2)" diff --git a/gef.py b/gef.py index 19ac68ee6..ce85dc53c 100644 --- a/gef.py +++ b/gef.py @@ -65,7 +65,6 @@ import importlib.util import inspect import itertools -import json import os import pathlib import platform @@ -1977,7 +1976,7 @@ def gdb_get_location_from_symbol(address: int) -> Optional[Tuple[str, int]]: Return a tuple with the name and offset if found, None otherwise.""" # this is horrible, ugly hack and shitty perf... # find a *clean* way to get gdb.Location from an address - sym = gdb.execute(f"info symbol {address:#x}", to_string=True) + sym = str(gdb.execute(f"info symbol {address:#x}", to_string=True)) if sym.startswith("No symbol matches"): return None @@ -1990,8 +1989,10 @@ def gdb_get_location_from_symbol(address: int) -> Optional[Tuple[str, int]]: def gdb_disassemble(start_pc: int, **kwargs: int) -> Generator[Instruction, None, None]: - """Disassemble instructions from `start_pc` (Integer). Accepts the following named parameters: - - `end_pc` (Integer) only instructions whose start address fall in the interval from start_pc to end_pc are returned. + """Disassemble instructions from `start_pc` (Integer). Accepts the following named + parameters: + - `end_pc` (Integer) only instructions whose start address fall in the interval from + start_pc to end_pc are returned. - `count` (Integer) list at most this many disassembled instructions If `end_pc` and `count` are not provided, the function will behave as if `count=1`. Return an iterator of Instruction objects @@ -2091,18 +2092,20 @@ def gef_disassemble(addr: int, nb_insn: int, nb_prev: int = 0) -> Generator[Inst nb_insn = max(1, nb_insn) if nb_prev: - start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) - if start_addr: - for insn in gdb_disassemble(start_addr, count=nb_prev): - if insn.address == addr: break - yield insn + try: + start_addr = gdb_get_nth_previous_instruction_address(addr, nb_prev) + if start_addr: + for insn in gdb_disassemble(start_addr, count=nb_prev): + if insn.address == addr: break + yield insn + except gdb.MemoryError: + # If the address pointing to the previous instruction(s) is not mapped, simply skip them + pass for insn in gdb_disassemble(addr, count=nb_insn): yield insn - - def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[str, List[str]]: """Execute an external command and return the result.""" res = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=kwargs.get("shell", False)) @@ -4670,6 +4673,8 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: value = struct.unpack(fmt, gef.memory.read(addr, size))[0] data += [value] sdata = ", ".join(map(hex, data)) + else: + sdata = "" if args.lang == "bytearray": data = gef.memory.read(start_addr, args.length) @@ -8313,7 +8318,7 @@ def do_invoke(self, argv: List[str]) -> None: argc = len(argv) if argc == 0: - ret = gdb.execute("show disable-randomization", to_string=True) + ret = gdb.execute("show disable-randomization", to_string=True) or "" i = ret.find("virtual address space is ") if i < 0: return diff --git a/tests/api/gef_disasemble.py b/tests/api/gef_disasemble.py new file mode 100644 index 000000000..8af39beba --- /dev/null +++ b/tests/api/gef_disasemble.py @@ -0,0 +1,30 @@ +""" +`gef.heap` test module. +""" + +import pytest + +from tests.utils import ARCH, _target, gdb_run_silent_cmd +from tests.utils import GefUnitTestGeneric + + +class GefDisassembleApiFunction(GefUnitTestGeneric): + """`gef_disassemble` function test module.""" + + @pytest.mark.skipif(ARCH not in ("x86_64", "i686"), reason=f"Skipped for {ARCH}") + def test_func_gef_disassemble(self): + cmd = "gef_disassemble(0x2337100, 4, 4)" + res = gdb_run_silent_cmd(f"pi os.linesep.join([str(i) for i in {cmd}])", target=_target("mmap-known-address")) + self.assertNoException(res) + self.assertIn( + ' 0x23370fc int3 \\n 0x23370fd int3 \\n 0x23370fe int3 \\n 0x23370ff int3 \\n 0x2337100 int3 \\n 0x2337101 int3 \\n 0x2337102 int3 \\n 0x2337103 int3 ', res) + + @pytest.mark.skipif(ARCH not in ("x86_64", "i686"), reason=f"Skipped for {ARCH}") + def test_func_gef_disassemble_page_border(self): + # Regression test for issue #922 + cmd = "gef_disassemble(0x2337000, 4, 4)" + res = gdb_run_silent_cmd( + f"pi os.linesep.join([str(i) for i in {cmd}])", target=_target("mmap-known-address")) + self.assertNoException(res) + self.assertIn( + '0x2337000 int3 \\n 0x2337001 int3 \\n 0x2337002 int3 \\n 0x2337003 int3 ', res) diff --git a/tests/binaries/mmap-known-address.c b/tests/binaries/mmap-known-address.c index 8d541c449..a2df86f1b 100644 --- a/tests/binaries/mmap-known-address.c +++ b/tests/binaries/mmap-known-address.c @@ -13,21 +13,37 @@ #include #include #include +#include #include "utils.h" int main(int argc, char **argv, char **envp) { - void *p = mmap((void *)0x1337000, - getpagesize(), - PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, - -1, - 0); - - if (p == (void *)-1) + const size_t pgsz = getpagesize(); + + void *a1 = mmap((void *)0x1337000, + pgsz, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, + -1, + 0); + + if (a1 == (void *)-1) + return EXIT_FAILURE; + + memset(a1, 0x41, pgsz); + + void *a2 = mmap((void *)0x2337000, + pgsz, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, + -1, + 0); + if (a2 == (void *)-1) return EXIT_FAILURE; + memset(a2, 0xcc, pgsz); + DebugBreak(); return EXIT_SUCCESS;