diff --git a/cle/memory.py b/cle/memory.py index e2de3f13..43300121 100644 --- a/cle/memory.py +++ b/cle/memory.py @@ -263,7 +263,10 @@ def split_backer(self, addr: int): return if addr <= start_addr: return - if isinstance(backer, ClemoryBase): + # backers() recurses into nested clemories and yields the child's own backers, while remove_backer() below + # removes from self._backers, where the child itself sits. + idx = bisect.bisect_left(self._backers, start_addr, key=lambda x: x[0]) + if idx >= len(self._backers) or self._backers[idx][1] is not backer: raise ValueError("Cannot split a backer which is itself a clemory") if addr >= start_addr + len(backer): return diff --git a/tests/test_clemory.py b/tests/test_clemory.py index 432dfc16..7dd715cf 100644 --- a/tests/test_clemory.py +++ b/tests/test_clemory.py @@ -1,10 +1,12 @@ from __future__ import annotations +import os import sys import timeit import unittest import cffi +import pytest import cle @@ -87,6 +89,38 @@ def performance_clemory_contains(): print(t) +def test_split_backer_refuses_to_split_through_a_nested_clemory(): + child = cle.Clemory(None) # type: ignore[arg-type] + child.add_backer(0, b"A" * 0x200) + child.add_backer(0x200, b"B" * 0x200) + + clemory = cle.Clemory(None, root=True) # type: ignore[arg-type] + clemory.add_backer(0, b"C" * 0x400) + clemory.add_backer(0x400, child) + + before = [(start, bytes(backer)) for start, backer in clemory.backers()] + + with pytest.raises(ValueError, match="itself a clemory"): + clemory.split_backer(0x401) + + assert [(start, bytes(backer)) for start, backer in clemory.backers()] == before + assert clemory.load(0x600, 0x10) == b"B" * 0x10 + + +def test_split_backer_refuses_to_split_through_a_loaded_object(): + filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../binaries/tests/x86_64/fauxware") + ld = cle.Loader(filename, auto_load_libs=False) + assert any(isinstance(backer, cle.Clemory) for _, backer in ld.memory._backers) + + addr = ld.main_object.entry + before = [(start, bytes(backer)) for start, backer in ld.memory.backers()] + + with pytest.raises(ValueError, match="itself a clemory"): + ld.memory.split_backer(addr + 1) + + assert [(start, bytes(backer)) for start, backer in ld.memory.backers()] == before + + def test_clemory_contains(): clemory = cle.Clemory(None, root=True) assert clemory.min_addr == 0