Repository navigation
Conversation
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Reproducer on a public fixture: Both lookups were evaluated against the same already-loaded object, so the comparison contains nothing but the lookup. Over the 821 loadable files in
Caveats: corpus objects are referred to by architecture, container and digest because the dataset is not public. The remaining 51 overlapping objects are relocatable ELFs placing several sections at one address, which #739 addresses separately. Re-keyed 2026-08-28. The figures above were measured at |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_760 |
b759dea to
a493c20
Compare
… not have Regions keeps its members sorted by start address and then bisects their end addresses, which is a total order only while the regions are disjoint. They are not always, so a lookup can walk past a region that does cover the address and report there is none, and max_addr can name an address that is not the highest. On binaries/tests/armel/btrfs.ko, find_section_containing(0x400024) returns None although .text spans 0x400000-0x4b9b58. .tbss is the common cause in ordinary linked ELFs, and it is not a region of the image at all: its address is where a thread's own copy of the thread-local template begins, and the linker places the section after it over the top. Keeping it out of the sorted list removes that overlap and stops address lookups answering with a section that holds none of the bytes it claims. On angr/binaries master 55 of 817 loadable files overlap for that reason alone. Overlap cannot be ruled out in general, so the lookups now carry a running maximum of the end addresses, which is sorted whether or not the regions are. Where the regions are disjoint that maximum is each region's own end address and both lookups are the bisection they were. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a493c20 to
9aa4acf
Compare
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Section lookups over an ELF whose non-allocated sections all land on the start of .text. the reproducerimport logging, os
logging.getLogger("cle").setLevel(logging.CRITICAL)
import cle
BIN = os.environ["BINARIES"] # a checkout of angr/binaries
T = lambda *p: os.path.join(BIN, "tests", *p)
path = T("armel", "btrfs.ko")
obj = cle.Loader(path, auto_load_libs=False).main_object
text = next(s for s in obj.sections if s.name == ".text")
print(f".text spans {text.vaddr:#x}-{text.vaddr + text.memsize:#x}")
sharing = [s for s in obj.sections if s.vaddr == text.vaddr and s is not text and s.memsize]
print(f"{len(sharing)} other sections start at {text.vaddr:#x} and end inside it, e.g. "
+ ", ".join(f"{s.name} (ends {s.vaddr + s.memsize:#x})" for s in sorted(sharing, key=lambda s: s.memsize)[:3]))
for addr in (0x400024, 0x400100, 0x4B9B00):
print(f"find_section_containing({addr:#x}) -> {obj.find_section_containing(addr)}")Before — the bisection over end addresses stops at a short-ended neighbour and reports no section covers the address: cle master at
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
An address lookup can walk past a region that does cover the address and report
there is none. On
binaries/tests/armel/btrfs.ko,.textspans0x400000-0x4b9b58and no address in it can be attributed to a section:find_section_containingis what tells a consumer which bytes are code, so onthis object every block in
.textlooks like it belongs to no section at all.Root cause
Regionskeeps its members sorted by start address and then bisects their endaddresses:
That assumption makes the two orders the same order. It does not hold here: the
19 non-allocated sections of a kernel module all state
sh_addr0 and land on.text's start, so the end-address sequence is0x400008, 0x400010, 0x400024, … , 0x4b9b58interleaved with.textlast. The bisection lands on the firstregion whose end exceeds the address, finds a region that starts at
0x400000but ends before the address it was asked about, and returns
None-- oneshort-ended neighbour is enough to hide every region behind it.
Fix
The lookups now carry a running maximum of the end addresses beside the sorted
list:
_max_end[i]is the highest end address among the firsti + 1regions.That sequence is nondecreasing whether or not the regions overlap, so the
bisection over it means something in either case, and where the regions are
disjoint it is each region's own end address and both reduce to exactly the
bisection they were.
.tbssis also kept out of the ELF section list, since itsaddress is where a thread's own copy of the template begins rather than a range of
the image, and the linker places the following section over the top of it.
Testing
tests/test_regions.py::TestOverlappingRegions::test_lookups_survive_an_overlapputs a small region inside a larger one and asserts the four lookups; on the
merge base
find_region_containing(0x3500)returnsNone.test_tbss_does_not_answer_for_what_is_over_itchecks the exclusion onbinaries/tests/x86_64/libc.so.6, where.tbsscovers.init_arrayand__libc_subfreeres, which holdthe bytes at those addresses.
btrfs.koabove is the reproducer in the wild, nota fixture the tests load.
Fixes #742. Validation: #760 (comment)
session: sharpen