Skip to content

s390x: keep the EXRL prefetch inside the code buffer - #89

Open
zardus wants to merge 1 commit into
masterfrom
feature/fix-pyvex-s390x
Open

s390x: keep the EXRL prefetch inside the code buffer#89
zardus wants to merge 1 commit into
masterfrom
feature/fix-pyvex-s390x

Conversation

@zardus

@zardus zardus commented Aug 11, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

s390_irgen_EXRL reads its target straight out of the buffer being translated, at exrl_bytes + offset * 2, with nothing bounding the result — and it reads the displacement as unsigned, unlike every other RIL-b handler. A bogus or truncated EXRL therefore reads far outside the buffer: lifting the seven bytes a75bffff c650ff as s390x is enough to segfault the process.

Record the buffer base in disInstr_S390, sign-extend the displacement, and prefetch only when the whole six-byte target lies inside the buffer. Otherwise s390_irgen_EX emits the run-time lookup it already emits for an unknown target. VexControl gains guest_bytes_size so a client whose buffer is larger than the block being translated can say how much of it may be read.

Consumed by angr/pyvex#564, which carries the regression tests. Validation: #89 (comment)

s390_irgen_EXRL reads its target straight out of the buffer being
translated, at exrl_bytes + offset * 2, with nothing bounding the
result. Nothing constrains the displacement field either, so a bogus or
truncated EXRL points anywhere within 4GB of the buffer: lifting the
seven bytes a75bffff c650ff as s390x is enough to kill the process. The
field is also read as unsigned, unlike every other RIL-b handler, which
turns a negative displacement into a read about 8GB ahead of the buffer
and puts that address in the emitted lookup as well.

Record the buffer base in disInstr_S390, sign-extend the displacement,
and prefetch only when the whole six-byte target lies inside the buffer.
Leaving last_execute_target at zero makes s390_irgen_EX emit the generic
run-time lookup, which is what it already does when the target is
unknown.

The buffer is normally larger than the block being translated, so
VexControl gains guest_bytes_size for the client to say how much of it
may be read; without it only guest_max_bytes counts.
@zardus

zardus commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 88aa12d54fb50e91635e81c0f3c0bffa1b712691 against baseline 875f7c9a5f6be621b4f000c29c016e15ddf32207, built and exercised through pyvex bc8b2245ff1ef9ffa5250b06972e14bd134bcf43 (angr/pyvex#564), which is where s390x lifting has tests. CPython 3.12.13, Linux x86-64, GCC 15.2.0.

  • Regression: python -c 'import archinfo, pyvex; pyvex.lift(bytes.fromhex("a75bffffc650ff"), 0x1000, archinfo.arch_from_id("s390x"))' — baseline dies with SIGSEGV, exit 139; head returns a 4-byte Ijk_Boring block
  • Focused: pytest tests/test_s390x_exrl.py — 6 passed
  • Full suite: pytest tests — 69 passed, 0 skipped

EXRL survey over the 67 s390x ELFs in angr/binaries 12d015e510e2a5fe1217ef061166b7ed7fda7a04: every six-byte c6?0 site in every executable segment, 1309 in total, translated with the whole segment as the code buffer and guest_max_bytes at 400, one fork per site, IRSB text digests compared.

Outcome Baseline Head
Target prefetched, block continues past the EXRL 157 157
Ijk_InvalICache, target left to the run-time lookup 21 90
Ijk_NoDecode 1066 1062
Killed by SIGSEGV 65 0
  • The 157 that prefetch are byte-identical on both revisions, so no resolution is lost. The exrl at 0xcf204 in tests/s390x/libc.so.6 executes the xc at 0xcf22a on both, for instance.
  • All 69 sites that differ had the baseline reading outside the buffer, and all 69 now stop at Ijk_InvalICache.

Other front ends are untouched by construction, and measured so: 27,918 lifts over X86, AMD64, ARMEL, AARCH64, MIPS32, MIPS64, PPC32, PPC64 and S390X from the same angr/binaries revision are byte-identical on baseline and head.

Caveats: no s390x hardware was involved; this is translation evidence only. A target inside the code buffer but before guest_code is not prefetched, which lookback_amount could allow later; no such site exists in the corpus above.

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.

1 participant