Repository navigation
Conversation
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Loader comparison over 1710 corpus objects spanning 55 architectures and 10 container formats, and separately over 39 relocatable ELF objects that carry an allocated note or
CFGFast ( Over the 39 affected objects:
Six objects change; every difference:
Net across the 39: 18 function entries removed, 9 added, 0 bytes of recovered code lost, 44 gained. The CFGFast comparison and the loader comparison were run in opposite revision orders and agree exactly on all 39 objects in both revisions, so none of these differences is nondeterminism. Caveats: the corpus objects are named only by architecture, container and sha256 because the dataset is not public; Corpus measurement, 2026-08-19A sweep of this corpus ranks
By architecture: mips 2,654, arm 1,083, x86 775, aarch64 287, ppc64 166, xtensa 15, hppa 2, x86_64 1. So essentially the entire category is this defect. On one of them, a mips kernel module, master places and the finding clears with the analysis untouched — 301 blocks on both sides, It is also visible without the corpus: 51 relocatable fixtures in Corpus evidence added 2026-08-26, measured on head A sweep of 452,707 objects counts basic blocks that land inside a region the loader reports as data. 14,503 objects carry at least one, 48,658 instances in total, and 99.99% of those objects are relocatable — 14,416 kernel modules plus 84 other relocatables. The distribution is flat rather than outlier-driven: median 3 per object, mean 3.4, and the hundred worst objects hold only 5.5% of the instances. The mechanism is this defect. On a MIPS kernel module the section table reads: Both notes keep the address Measured over 108 objects spanning the affected architectures, comparing this head against
Every architecture except xtensa goes to 0; the 14 that remain are a separate issue in the p-code lifter overrunning a block budget by one byte, which has nothing to do with the loader. Extrapolated over the sweep, this accounts for roughly 46,900 of the 48,658 instances, about 96%. The remainder is the COFF Reproducing the count on the pinned toolchain the sweep used gives 255 as well, so this is not something that has drifted on master: Re-keyed 2026-08-28. The figures above were measured at Corpus attribution, 2026-08-28, keyed to head A category sweep, deduplicated by object digest, scores 460,618 distinct objects and asks whether a recovered block overlaps a section the loader reports as non-executable data. 14,503 objects carry at least one, 48,658 blocks in total. Attributing those by mechanism, from the objects' own section headers read straight out of the file rather than through CLE:
The first row is this change. It is the largest single mechanism the sweep attributed anywhere in this work, ahead of the 12,204 Mach-O objects of the outside-executable category. Measured on 69 stratified objects across four containers and ten architectures, which reproduce the sweep's own count on 68 of 69 at its revisions:
The six survivors are Xtensa objects and are the p-code row above, not this. Cost of the pair over the same 69: recovered blocks +162, recovered functions -71 — the function delta is entirely two ARM kernel modules losing spurious heads planted inside real functions, and the COFF objects gain both blocks and functions because Independent of any CFG run, a loader-only check over 262 objects of this category asks whether any section the scorer would call data overlaps any section it would call executable: 237 of 262 collide, and every colliding ELF object is Nothing upstream has fixed this: over the same 69 objects, cle master scores 1,291 against 1,289 at the sweep's revisions, and not one object goes to zero. |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_739 |
eb63f1e to
110e458
Compare
A relocatable object carries no addresses of its own, so __register_sections lays its allocated sections out itself. A section whose type is in _NON_ALLOCATED_SECTION_NAMES took an address from that layout without reserving any space, and no backer is ever added for it, so it landed on top of the section that follows. On a Linux kernel module the allocated .note.Linux therefore covers the first 0x30 bytes of .text, and on MIPS .reginfo shares an address with .MIPS.abiflags. Regions documents that its members do not overlap and finds one by bisecting on their end addresses, so a single overlapping section leaves that list unsorted by the search key and the lookup misses regions that are really there. find_section_containing() then returns None for most of the object's mapped range, or a stale section, depending on what was looked up before it, because Backend caches the previous hit. CFGFast reads it twice: a block whose section is not executable is discarded, and JumpTableResolver requires the table to be inside a mapped section when the object has no segments, so an ARM module loses both the code the note covers and every jump table. These sections now report that they occupy no memory, which is what the constant already claims about them. Every section address, every loaded byte, every relocation and every symbol stays as it was; the sections only leave the address map, the way a section without SHF_ALLOC already does.
110e458 to
ab2d0da
Compare
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Full section-layout report for Before — cle at the merge base, d2ecea0After — the note section claims no memory, no two mapped sections overlap, and with this change, ab2d0da |
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
In a relocatable object, a section that cle deliberately does not map still takes an address from the layout, so it sits on top of the section that follows. On
tests/x86_64/switch_default_abort.otheSHF_ALLOC.note.gnu.propertylands at the address.eh_framestarts at, andfind_section_containingthen answersNoneover the whole of.eh_frame:Whatever the shadowed section is, consumers that gate on the containing section lose it:
CFGFastdiscards blocks whose section is not executable, andJumpTableResolverrejects a table that falls outside a mapped section.Root cause
ELF._load_sectionslays a relocatable object's allocated sections out itself, because theirsh_addrvalues are meaningless. A section whose type is in_NON_ALLOCATED_SECTION_NAMESis given the running address but reserves no space and gets no backer:ELFSection.occupies_memorythen had no way to know that, being onlyself.flags & self.SHF_ALLOC != 0 and self.memsize > 0, and this note section isSHF_ALLOCwithsh_size0x20.Regionsdocuments that its members do not overlap and bisects on their end addresses, so one such section makes the lookup returnNone— or a stale neighbour, depending on what was looked up before — over the range it covers.Fix
_load_sectionscarries anoccupies_memoryflag, sets it false for exactly the types it already refuses to reserve space for, and passes it toELFSection, whose property ANDs it in. That is what the constant's own comment, "Sections that do not occupy memory at runtime", already claims about them. No address, byte, relocation or symbol assignment moves — the sections keep the addresses they were given and stop claiming to occupy memory there:Testing
TestRelocatableSections.test_every_mapped_address_resolves_to_its_sectionwalks every section that reportsoccupies_memoryand assertsself.assertEqual(name_of(ld.find_section_containing(addr)), section.name)at each of its addresses;test_unloaded_section_claims_no_addresspins that the note section reports no memory. Both fail on the merge base and pass on this head, ontests/x86_64/switch_default_abort.o, which is already onangr/binariesmaster.Validation: #739 (comment)
session: sharpen