mips32: Reject MIPS64-only encodings instead of asserting - #91
Conversation
A MIPS64-only instruction is a Reserved Instruction on a 32-bit MIPS, so the decoder has to report a decode failure for it. This file instead decodes ld, daddiu, the doubleword shifts and multiplies, ldl/ldr, sdl/sdr, dmfc1/dmtc1, dext/dins, dsbh/dshd, dclz/dclo and their siblings unconditionally, assigning an I64 to an I32 guest register. That trips either the vassert in putIReg or the IR sanity check at the end of the superblock, and both unwind out of the whole translation: the caller gets an empty IRSB whose Ijk_NoDecode points at the start of the block instead of at the instruction that failed, so the instructions decoded ahead of it are thrown away too. Reject those encodings before the main opcode switch, which reaches the same decode_failure every other unsupported encoding uses and ends the block cleanly at the offending instruction. Only encodings that fail today are listed, so the check cannot take away a translation that currently succeeds: LWU and SD are decoded in 32-bit mode by the cases below and are left alone. Over 1,000,961 encodings -- every one of the 131,072 (opcode, rs, function) dispatch triples twice, a shift-amount sweep for the sub-opcode groups that dispatch on it, every distinct instruction word of six MIPS III binaries, and 300,000 uniformly random words -- 116,238 fall in the rejected set, and libVEX decoded none of them on a 32-bit guest before this change: 93,595 aborted on an assertion, 16,938 on the IR sanity check, and 5,705 already reported a plain decode failure. MIPS64 lifts every one of the 1,000,961 to bit-identical IR before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Lifter differential, 1,000,961 encodings on each of the four MIPS guests: every one of the 131,072
CFG measurement on the affected population — MIPS III or n32 code in an
447 MIPS control objects covering every container and bitness in the sample — ELF, blob, ihex, srec, ar, elfcore, cart, 32- and 64-bit, VEX- and p-code-lifted:
A/B method: two builds of the same worktree differing only in Prior art and scope: angr/pyvex#76 reported this assertion on Caveats: no MIPS hardware was involved; this is translation and CFG evidence only. The differential and CFG figures above were measured on the paired pyvex branch at this exact submodule pin and are reproduced here rather than re-run; what was re-run for this record is the focused regression in the first row. On a 64-bit guest, 550 of the sampled encodings still abort on an assertion — separate MIPS64-side defects, untouched here. |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS The block reported in pyvex issue 76 -- six MIPS32 instructions then Before -- the whole superblock is discarded and vex master (875f7c9), via pyvex bdd5441After -- the reserved encoding takes the ordinary with this change (vex 561795c, via pyvex dca2871) |
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
A MIPS64-only encoding is a Reserved Instruction on a 32-bit MIPS, but
priv/guest_mips_toIR.cdecodes 37 opcode slots' worth of them without consultingmode64first. The block reported in pyvex issue 76 is six ordinary MIPS32 instructions followed bydaddu $a0, $v0, $zero(0x0040202d) at0x3c5f0:The four loads, the store and the
addiuahead of thedadduare lost with it, andnextpoints at the start of the block rather than at the encoding that failed, so the caller is handed no bytes at all and no address to resume from.Root cause
These cases build 64-bit IR and hand it to a 32-bit guest register. libVEX then
longjmps out of the whole translation from one of three places, all reachable today:The unwind happens after
disInstr_MIPS_WRKhas already emitted IR for the preceding instructions, and past the code that would have kept it.Fix
is_MIPS64_only_insnrecognises the encodings that fail this way -- the SPECIAL doubleword shifts, multiplies, divides and adds;DMFC1/DMTC1;DCLZ/DCLO; the SPECIAL3 doubleword bitfield and byte-swap group;DADDI/DADDIU,LDL/LDR,SDL/SDR,LLD,LDandSCD-- anddisInstr_MIPS_WRKconsults it before the main opcode switch:decode_failureis the path every other unsupported encoding already takes; it emitsjmp_lit32(&dres, Ijk_NoDecode, guest_PC_curr_instr), so the block ends at the offending address with everything before it intact (the six decoded instructions elided here, complete in the comment below):Only encodings that fail today are listed, so the predicate cannot take away a translation that currently succeeds.
LWUandSDare decoded in 32-bit mode by the cases below, correctly or not, and are left alone for that reason; so are the Cavium OCTEON encodings.Testing
tests/test_mips32_reserved.py, which lands with the consumer, names one encoding per affected group -- fifteen in all -- and asserts the two-instruction prefix survives each. On the merge base it fails on its first entry withassert 0 == 8, the block being empty where the prefix should be; here all three tests in the file pass. The other two pin that MIPS64 still decodes the same fifteen encodings and thatsd $ra, 0x5c8($sp)andlwu $a0, 0x10($v1)still decode on MIPS32.Consumed by angr/pyvex#567, which carries the regression tests and the submodule bump. Validation: #91 (comment)
Merge order: this lands first, then angr/pyvex#567, whose
vexsubmodule is pinned to this head (561795c). Merging the pyvex half on its own leaves pyvex master's submodule pointing at a commit that is on no vex branch. Nothing in this repository reads async:line —.github/workflows/build.ymlis the only workflow and it is three build jobs — so the line below is a note to the reader.sync: angr/pyvex#567
session: sharpen