Skip to content

Place rebased objects in the free space, not one granule at a time - #735

Merged
ltfish merged 3 commits into
masterfrom
feature/fix-cle-rebase-room
Aug 10, 2026
Merged

ltfish merged 3 commits into
masterfrom
feature/fix-cle-rebase-room

Conversation

@zardus

@zardus zardus commented Aug 10, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Loader._find_safe_rebase_addr steps upward from the main object's max_addr one rebase granule at a time, so it raises CLEOperationError("Ran out of room in address space") with the address space nearly empty: an i386 firmware image with two PT_LOADs 4 GB apart has nowhere to put the extern object, and a static archive is capped at one member per granule however small the members are.

The fallback meant to cover that reads segments.raw_list, which is in program-header order rather than address order, so it computes a negative gap, or returns an address inside the main object's span, where Loader.memory cannot read it.

This enumerates the free space between the loaded objects and retries at finer alignments before giving up. Placement is unchanged for images that fit at the configured granularity, and the regression test synthesizes its own input, so there is no new fixture.

Validation: #735 (comment).

_find_safe_rebase_addr only ever stepped forward from the main object's
max_addr, rounding each object up to the rebase granularity, so it ran out of
room with the address space nearly empty. A two-segment i386 image whose
segments sit 4 GB apart has nowhere to put the extern object, and a static
archive is capped at one member per granule regardless of member size.

The fallback meant to cover this read segments.raw_list, which is in
program-header order rather than address order, so it computed negative gaps
and missed the free space; when the headers did happen to be sorted it returned
an address inside the main object's span, where the object is unreachable
through Loader.memory, since the main object is a single backer covering that
whole span.

Enumerate the free space between the loaded objects instead, and retry at finer
alignments before declaring the space full. Placement is unchanged for every
image that fits at the configured granularity.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zardus

zardus commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head dee9938a6c75729402ada81a999e4a2319f09205 against baseline b58ea02a446106647cdaae32bdf91b7062404cc1, with angr/binaries at 58841bf0d9e71ca7b215f404ba61e1924c712906.

  • Regression: python -m pytest tests/test_rebase.py — 3 passed on head; all 3 fail on the baseline, two with CLEOperationError: Ran out of room in address space and one because the extern object lands at 0x100000, inside the main object's span
  • Focused: python -m pytest tests/ — 205 passed, 9 skipped
  • Lint: pylint under the CI image's pylintrccle/loader.py 9.92 -> 9.93, tests/test_rebase.py 10.00/10.00
  • Type: pyright badness per changed file — cle/loader.py 0.02941 -> 0.02894, tests/test_rebase.py 0.0
  • Hooks: pre-commit run --all-files — every hook passes, no file rewritten
  • Workspace gate: cle is the only repository this change touches; the complete local gate over it is green

Placement is identical to the baseline on every image that already loaded. Each sweep records (class, min_addr, max_addr) for every object in Loader.all_objects after forcing extern_object and tls.new_thread(), then compares baseline against head:

Sweep over angr/binaries Inputs Loaded Objects placed elsewhere
cle.Loader(path, auto_load_libs=False) 1151 719 0
cle.Loader(path, auto_load_libs=True) 1048 619 0
angr.Project(path, auto_load_libs=False) 315 305 0

That includes tests/x86_64/coredump/true-libc.so.6-ld-linux-x86-64.so.2.core, the case the deleted fallback was written for; its layout does not move.

Beyond the synthesized regressions, a 5000-member ar archive of tests/mips/mips-hilo.o fails on the baseline with Ran out of room in address space and loads every member on head, 20.3s -> 22.9s. Placing 4200 objects into a 32-bit space takes 2.5s, against a baseline that cannot place more than about 4090 of them at all.

Caveats: an object still cannot be placed inside another object's [min_addr, max_addr] span, because Clemory resolves an address to the first backer covering it and a nested placement would be unreadable. An S-record image is still reported as covering its whole address space, since Backend.min_addr returns mapped_base and ignores the lower bound SRec computes; that is a separate defect in the backend rather than in this allocator.

@angr-bot

Copy link
Copy Markdown
Member

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

@ltfish

ltfish commented Aug 10, 2026

Copy link
Copy Markdown
Member

Finally!

I'm moving the test binary to the binaries repo.

@ltfish ltfish self-assigned this Aug 10, 2026
@ltfish
ltfish merged commit 45c6509 into master Aug 10, 2026
19 checks passed
@ltfish
ltfish deleted the feature/fix-cle-rebase-room branch August 10, 2026 19:08
zardus added a commit to angr/angr that referenced this pull request Aug 11, 2026
The 16-bit test passed rebase_granularity=0x100 because cle aligned a rebased
object up to the default 1 MiB granularity, which lands past the end of a 16-bit
address space, so placing the extern object raised "Ran out of room in address
space".

angr/cle#735 now searches the free space with an alignment ladder that falls back
to 0x1000 and then to a single byte when the granularity does not fit, so the
default works and the argument only obscures what the test is about. angr
resolves cle from master, which carries that fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zardus added a commit to angr/angr that referenced this pull request Aug 19, 2026
The 16-bit test passed rebase_granularity=0x100 because cle aligned a rebased
object up to the default 1 MiB granularity, which lands past the end of a 16-bit
address space, so placing the extern object raised "Ran out of room in address
space".

angr/cle#735 now searches the free space with an alignment ladder that falls back
to 0x1000 and then to a single byte when the granularity does not fit, so the
default works and the argument only obscures what the test is about. angr
resolves cle from master, which carries that fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zardus added a commit to angr/angr that referenced this pull request Aug 21, 2026
The 16-bit test passed rebase_granularity=0x100 because cle aligned a rebased
object up to the default 1 MiB granularity, which lands past the end of a 16-bit
address space, so placing the extern object raised "Ran out of room in address
space".

angr/cle#735 now searches the free space with an alignment ladder that falls back
to 0x1000 and then to a single byte when the granularity does not fit, so the
default works and the argument only obscures what the test is about. angr
resolves cle from master, which carries that fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zardus added a commit to angr/angr that referenced this pull request Aug 26, 2026
The 16-bit test passed rebase_granularity=0x100 because cle aligned a rebased
object up to the default 1 MiB granularity, which lands past the end of a 16-bit
address space, so placing the extern object raised "Ran out of room in address
space".

angr/cle#735 now searches the free space with an alignment ladder that falls back
to 0x1000 and then to a single byte when the granularity does not fit, so the
default works and the argument only obscures what the test is about. angr
resolves cle from master, which carries that fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants