Skip to content

Stop container objects from claiming their children's addresses - #730

Open
zardus wants to merge 1 commit into
masterfrom
feature/fix-cle-container-overlap
Open

Stop container objects from claiming their children's addresses#730
zardus wants to merge 1 commit into
masterfrom
feature/fix-cle-container-overlap

Conversation

@zardus

@zardus zardus commented Aug 10, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

A binary that loads on its own fails inside a container backend when it is linked at the address the container was placed at: Position-DEPENDENT object None cannot be loaded at 0x0. An outer backend holds no memory, so both its bounds report the base it was mapped at, a one-byte span, and placement counted that byte against the object it unpacks.

find_object_containing already skips outer objects; _is_range_free and _free_gaps now do too, which also makes a wrapped binary lay out the way the unwrapped one does. That error covered three unrelated conditions, so it now names the one that applies.

The regression tests load tests/x86_64/1after909.cart, a real CaRT container: mapping its contents at 0 with the blob backend reproduces the refusal, and loading it high, wrapped and unwrapped, shows the address the loader rebases into drifting by a granule. The message about running out of address space is covered by loading tests/i386/manysum as a blob near the top of the 32-bit space. No test packs or assembles a container of its own, and every fixture is already on angr/binaries master, so there is no companion binaries PR and this does not wait on anything.

Rebased onto master after #735, which rewrote _find_safe_rebase_addr into the _free_gaps search this branch also touches.

Validation: #730 (comment)

@zardus

zardus commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Rebased onto master to resolve a conflict with #735, which rewrote _find_safe_rebase_addr into the _free_gaps search this branch also touched. Re-validated below; the earlier record for 1f3dd06 no longer applies.

Validation record for head b680cde against baseline 45c6509c753d07f740099035cd41f7f473dc6f31, Python 3.12.13, angr/binaries master 12d015e510e2a5fe1217ef061166b7ed7fda7a04.

  • Focused: python -m pytest tests/test_overlap.py tests/test_cart.py tests/test_rebase.py — 15 passed. tests/test_rebase.py is Place rebased objects in the free space, not one granule at a time #735's, included because this branch edits the function it covers.
  • Full suite: python -m pytest tests/ — 231 passed, 9 skipped. The skips are the pre-existing @skip("TODO") markers in tests/test_macho_bindinghelper.py.
  • Fails without the fix: reverting cle/loader.py and cle/backends/cartfile.py to the baseline gives 5 failed, 10 passed. test_outer_object_does_not_occupy_address_space raises cle.errors.CLEError: Position-DEPENDENT object None cannot be loaded at 0x0 from Loader._map_object while the blob unpacked from the CaRT file is being placed at 0 — the defect itself. test_memoryless_region_still_reserves_address_space and test_placement_past_the_end_of_the_address_space get messages that do not say what is wrong, and test_cart_child_is_named_after_the_wrapper gets None for the name.
  • Hooks: pre-commit run --all-files — all hooks pass, tree unchanged.
  • Fixtures: the tests read tests/x86_64/1after909.cart, tests/x86_64/1after909, tests/i386/manysum and tests/x86_64/windows/6f289eb8c8cd826525d79b195b1cf187df509d56120427b10ea3fb1b4db1b7b5.sys.cart, all on angr/binaries master. Nothing is packed or assembled at test time, so there is no companion binaries PR.

One test changed substantively in the rebase. #735 made the rebase search start above the main object, so a container mapped at 0 now falls below the search range in the ordinary case, and test_cart_layout_matches_unwrapped passed with or without this branch's change to _free_gaps. The skip is still load-bearing: when the main object reaches into the top half of the address space the search starts at 0, where the container's byte sits. test_outer_object_does_not_move_rebased_objects loads 1after909 high, wrapped and unwrapped, and rebases an object into each; without the skip the wrapped loader returns 0x100000 where the unwrapped one returns 0x0. test_cart_layout_matches_unwrapped is kept as-is since it still pins wrapped/unwrapped parity in the common configuration.

The defect first showed up in a corpus sweep, on three CaRT containers holding crash dumps in minidump format (sha256 d90692031a4d4529df8efcf1b07caf45cc5892fa1a4e655294a81b719452a0b6, ee7f0cd6d7695b3cc9a633581ec38e0f6beb60473f78bfeaaeffed977df5f553, 89323f692282267d78b84ac0cf2b59dfb678bd3c626bf4cff19d7274c102156e). Nothing about minidumps is involved; that observation was made on an older revision and the reproduction above stands on its own.

Caveats: the fix is in Loader, so it applies to every outer backend, but only the CaRT path has a test. StaticArchive and Universal2 are position independent and land on 0x400000, where a non-PIE x86-64 member is linked, so they hit the same refusal; angr/binaries has no archive or fat binary with such a member, and covering them would mean assembling one during the test, which is what an earlier version of these tests did. A position-dependent object that spans an outer object's base address can now be mapped, and find_object_containing still answers None from that base upwards; before this change such an object did not load at all.

@angr-bot

Copy link
Copy Markdown
Member

Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_730

@zardus
zardus force-pushed the feature/fix-cle-container-overlap branch 2 times, most recently from 1f3dd06 to 1288a51 Compare August 10, 2026 23:33
An outer object holds the objects it unpacks and backs no memory, so
Backend.min_addr and Backend.max_addr both report the base it was placed
at: a one-byte span. _map_object inserts it into all_objects anyway, and
_is_range_free counted it, so a position-dependent child linked at that
same address was refused for overlapping its own container. A CaRT file
keeps its linked base of 0 and blocks a child linked at 0; StaticArchive
and Universal2 are position independent and land on 0x400000, which is
where a non-PIE x86-64 member is linked. find_object_containing already
excludes outer objects; placement now does too, in _is_range_free and in
_free_gaps, where the same byte was moving the address the loader picks
for the objects it rebases and laying a wrapped binary out differently
from the same binary unwrapped.

The same raise covered three unrelated conditions and described only the
last one, so an object that simply did not fit in the architecture's
address space was reported as an overlap. Split the conditions apart in
_describe_range_conflict and append the reason to the message. Name the
object by binary_basename, and give the object a CaRT file unpacks the
wrapper's unpacked_name, so an object loaded from a stream is no longer
reported as "None".

Every case is covered from a fixture that is already in angr/binaries.
tests/x86_64/1after909.cart is a real container: loading its contents at
0 with the blob backend reproduces the refusal exactly as the wrapped
crash dumps that first showed it do. Since _free_gaps searches upward
from the main object, the placement case loads the image high, which puts
the free space, and so the container's byte, below it; the wrapped binary
then rebases a granule away from where the unwrapped one does. The
address-space case loads tests/i386/manysum as a blob near the top of the
32-bit space. No test writes a container of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zardus
zardus force-pushed the feature/fix-cle-container-overlap branch from 1288a51 to b680cde Compare August 10, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants