Conversation
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Sweep over a copy of
The last row is a separate pre-existing crash from the same assumption, fixed by the same change. No substituted ELF magic survives in the loaded image in any row. Caveats: the unmapped-header layout comes from real CGC challenge binaries seen in a corpus sweep, none of which can be redistributed here; every CGC fixture in angr/binaries maps its header at file offset 0, which is why CI never reached this. A first PT_LOAD that begins inside the header is not a layout that sweep saw, and it is covered because the fix would otherwise have left substituted bytes in memory there. Load time for Rebased 2026-08-27. This record was measured at Corpus evidence, 2026-08-28Load-only measurement over a private corpus, so objects are cited by architecture, container format and sha256 only. Each object is loaded in its own process as 277 distinct objects terminate in None of them is fixed on master: 30 of a 30-object sample still terminate there. On this head, |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_726 |
aa99be0 to
f59456e
Compare
The CGC backend substitutes an ELF magic into the stream so pyelftools will parse the file, then writes the real CGC header back into memory afterwards. That store assumed the whole 16-byte header has a virtual address, but nothing requires the header to be inside a PT_LOAD. When file offset 0 is not loaded, offset_to_addr returns None and AT.from_raw raises TypeError from inside address arithmetic, so the load fails outright. Real CGC challenge binaries are laid out this way, with no PT_PHDR and a first PT_LOAD starting well past the header. Store back only the bytes that the substitution actually reached. A binary that maps none of the header now loads unchanged, and one whose first PT_LOAD starts partway into the header keeps the bytes it really contains instead of the substituted magic.
f59456e to
e8e98e1
Compare
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Full load report for Before — the stock binary loads, but both moved copies die inside cle at the merge base, 46a3733After — all three load, the unmapped copy keeps no substituted magic anywhere, and the partially mapped one gets exactly the tail of with this change, e8e98e1 |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS How much of a corpus's failure surface this removes, measured rather than Sample. 12,000 objects drawn uniformly at random, from a seeded permutation, Method. Each object is loaded with the catalogue's declared load recipe and Before. 7 / 11,989 = 0.06% of the sample (CI 0.03–0.12), and 4.1% of its After. This head clears all 7, and all 7 go on to reach CFG — the whole Control. 722 objects that already reached CFG on The corpus is not redistributable, so its objects are described by architecture, session: sharpen |
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
A CGC binary whose first
PT_LOADstarts past file offset 0 does not load. Moving that segment in a copy oftests/cgc/CADET_00002to offset0xa0, so the 16-byte header is not mapped at all:The failure is in the constructor, so the whole object is lost — entry point, segments and symbols — over bytes that were never going to be in memory. Real challenge binaries with no
PT_PHDRand a firstPT_LOADpast the header have this shape.Root cause
The CGC backend substitutes an ELF magic into the stream so pyelftools will parse the file, then stores the real CGC header back into memory with one unconditional write:
AT.from_raw(0, self)callsowner.offset_to_addr(0), which returnsNonewhen no segment maps file offset 0, and the address translator then subtracts anintfrom it. Nothing in ELF requires the header to be mapped.Fix
The backend that makes the substitution is what has to undo it, so it now stores back only the bytes that were loaded, walking the 16 header bytes and writing each one whose file offset maps to an address. A binary mapping none of the header loads unchanged, and one whose first
PT_LOADbegins partway into the header keeps its own bytes rather than the substituted magic. The substitution in the stream itself is untouched.Testing
tests/test_cgc.py::test_cgc_mapped_headerpins the stock repair, and::test_cgc_unmapped_headerand::test_cgc_partially_mapped_headermove the firstPT_LOADoftests/cgc/CADET_00002in a temporary copy, keeping the bytes it maps at the addresses they had. The load-bearing assertion isld.memory.load(tail_addr, len(CGC_HEADER) - PARTIAL_OFFSET) == CGC_HEADER[PARTIAL_OFFSET:]. The two moved cases fail on the merge base with theTypeErrorabove, and no new fixture is needed.Validation: #726 (comment)
session: sharpen