Skip to content

SRec: Fix record matching and entry point parsing - #714

Open
zardus wants to merge 1 commit into
masterfrom
feature/srec-parsing
Open

SRec: Fix record matching and entry point parsing#714
zardus wants to merge 1 commit into
masterfrom
feature/srec-parsing

Conversation

@zardus

@zardus zardus commented Aug 9, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

The SRec backend cannot load objcopy -O srec output. The record regex demands
4 to 64 hex digits of data, which rejects a one-byte data record and every
S7/S8/S9 termination record, and on a record carrying more than 32 data bytes it
matches only a prefix, so the checksum group lands inside the data and the record
is rejected for a checksum it never had. The entry point is also read from the
termination record's data field, which is empty by definition, so relaxing the
match on its own would report an entry of 0.

The parser now matches the whole record, accepts any data length, and takes the
entry point from the address field. Malformed records raise CLEError instead of
letting KeyError, IndexError, binascii.Error or a TypeError escape.

tests/test_srec.py covers both, including three verbatim lines of objcopy
output; every test in it fails on master.

Validation: #714 (comment)

The SRec backend could not load a file that any real toolchain produces:

* The data field of the record regex demanded between 4 and 64 hex digits, so a
  record carrying one byte of data, or a termination record carrying none, never
  matched.  Matching only a prefix compounded this: on a record with more than 32
  data bytes the checksum group landed inside the data, and the record was
  rejected for a checksum it never had.  objcopy writes the output file name into
  the S0 header record, so that is reachable on the very first line.
* The entry point was read from the data field of the termination record.  S7/S8/S9
  records carry the start address in their address field and have no data, so the
  entry point always came out as 0.
* Malformed input escaped as KeyError, IndexError, binascii.Error, or a TypeError
  from concatenating bytes onto a str, rather than as CLEError.

Match the whole record, let the data field hold any number of bytes, take the entry
point from the address field, and report every malformed record as a CLEError.  A
data record with an empty data field is now skipped instead of backing an empty
memory region.
@zardus

zardus commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 55b7f1a6bff70e239b6366da53541d2e859bc85f against
baseline b58ea02a446106647cdaae32bdf91b7062404cc1.

Reproducer, with GNU binutils 2.46 objcopy and any ELF:

objcopy -O srec /bin/true true.srec
python -c "import cle; print(hex(cle.Loader('true.srec', main_opts={'backend': 'srec', 'arch': 'AMD64'}).main_object.entry))"
  • Baseline: CLEError: Invalid SRec record: b'S7...', raised on the termination record
  • Head: prints 0x2db0, the entry point of /bin/true
  • Entry defect in isolation: with only the record regex relaxed on baseline, the same command prints 0x0
  • Regression: python -m pytest -q tests/test_srec.py — 17 tests; all 17 fail on baseline, all 17 pass on head
  • Full suite: python -m pytest -q in cle — 219 passed, 9 skipped
  • Lint: pre-commit run --all-files clean; pylint (angr CI config) 10.00 unchanged on cle/backends/srec.py, 10.00 on the new test file
  • Type: pyright badness per changed file, merge-base relative — cle/backends/srec.py 0.385 -> 0.353, tests/test_srec.py 0.0 -> 0.0

Beyond the two defects in the description, a data record whose data field is
empty used to be rejected by the old regex; it parses now, so it is skipped
rather than added as an empty memory backer, which Clemory.add_backer refuses.
Malformed input was also checked by parsing 30k random byte strings through
SRec.parse_record: nothing escapes as anything other than CLEError.

Caveats: S5/S6 record-count records are still refused as unimplemented, exactly
as before this change. Only cle's suite was run locally; the other ecosystem
packages were installed as wheels. Test windows-2022 failed on its first
attempt and passed on a re-run: every test module errored during collection at
import cle, in pyvex.native._parse_ffi_str, where os.replace onto the
shared FFI cache path raises WinError 5 when a concurrent pytest -n auto
worker holds that file open. Same pyvex commit, nothing to do with this change.

@angr-bot

angr-bot commented Aug 9, 2026

Copy link
Copy Markdown
Member

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

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