Repository navigation
Conversation
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head Rebased because pyvex master
Consumer check, and what it varies. The two arms are nix-built environments that differ in libVEX and nothing else: The 24 memory forms of a 16-bit ModRM byte, Exhaustive opcode differential. Every combination of the
The top-left cell is the no-regression control: in the mode where this change is not supposed to be reachable without the prefix, nothing without the prefix moves. In protected mode 84 of the 42,962 differences carry their Real-mode census over
Caveats. |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Three instructions from the Before — the first two decode and read the wrong base register; the third does not decode: angr/vex master `e3062871`After — with this change |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/pyvex_581 |
4b142af to
41006c1
Compare
libVEX sends a ModRM byte through disAMode16 whenever the address size is 16 bits -- under a 0x67 prefix in 32-bit mode, and with no prefix at all when cr0.PE is clear -- and that function never applied the 16-bit r/m table. `67 8b 47 10`, which is mov 0x10(%bx),%eax, lifted to Ijk_Boring reading %di, and the four base+index forms did not decode at all. Bump the vex submodule to the rewrite of disAMode16 and lengthAMode16. tests/test_x86_addr16.py walks all 24 memory forms of the 16-bit ModRM byte and asserts each reads the registers the r/m table names, then covers the negative 8-bit displacement, the segment override, the immediate that lengthAMode locates, and the real-mode route in that needs no prefix. Five of its seven tests fail on master.
41006c1 to
859613b
Compare
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
cmp dx, (%di)reads%bp. This is3b 15at0xf9c98intests/i386/bios.bin.elf, a SeaBIOS image, lifted in the real mode a BIOS runs in —pyvex.IRSB(b"\x3b\x15", 0xf9c98, arch, opt_level=0, num_inst=1).pp()on a copy ofARCH_X86whosevex_archinfo["x86_cr0"]is0xFFFFFFFE:Line 01 is
ebp. The instruction names%di.It does not fault, does not decline, and comes back
Ijk_Boringwith the right length. Of the 24 memory forms of a 16-bit ModRM byte, one is right, eleven read the wrong base register like this, and twelve come backIjk_NoDecodewith size 0; put a segment override in front and all 24 refuse. A refusal ends the block at zero bytes, so it gets noticed; a wrong base register does not. The bytes reported in 2017 at #80 still show it:67 20 74 6fisand %dh, 0x6f(%si), and on master it lifts cleanly reading%sp. Only thesanityCheckFailthat issue was closed on is gone.Both routes in are live.
67in 32-bit mode is one. The other has no prefix: libVEX sends every memory ModRM through this code whenarchinfo->x86_cr0 & 1is clear, and angr sets that from a guestmov cr0throughengines/vex/heavy/dirty.pyandengines/vex/lifter.py--test_cfgemulated.py'sCFGEmulatedrun over this file already takes that route four times. Over the 4,323 such instructions in that image's.text, 3,521 refuse, 687 compute the address from the wrong registers, 66 come out the wrong length, 2 emit no load or store to check, and 47 show no defect under those checks -- one of which takes the wrong immediate anyway.Root cause
Entirely in the submodule.
disAMode16inpriv/guest_x86_toIR.cpasses the raw r/m field togetIReg, which numbers registersax cx dx bx sp bp si di, while a 16-bit r/m field means(%bx,%si) (%bx,%di) (%bp,%si) (%bp,%di) (%si) (%di) (%bp) (%bx), so(%si)reads%spand(%bx)reads%di. The base+index forms have no implementation at all, a negative 8-bit displacement failsmkU16's assertion, and a segment override reacheshandleSegOverrideas anIty_I16. Separately,lengthAModedispatches on the processor mode whiledisAModedispatches on the address size, so a67-prefixed instruction's immediate is read from the wrong byte — andlengthAMode16's own lengths are wrong for 17 of the 32 mod/rm combinations, which bites in real mode with no prefix:83 3e 20 df 00,cmpw $0, 0xdf20, takes0xffdfinstead of0.Fix
Bump the
vexsubmodule to angr/vex#97, which rewritesdisAMode16andlengthAMode16aroundmodandr/mwith the 16-bit table in one place, masks the displacement back to 16 bits, widens the effective address before the segment base is added, and pointslengthAModeatcurrent_sz_addr. That bump andtests/test_x86_addr16.pyare the whole diff.What a 16-bit address size means for the instructions that take no ModRM byte is deliberately still missing:
MOVS,CMPS,STOS,LODSandSCASaddress off%esi/%edi,LOOPandJCXZcount in%ecx,XLATindexes off%ebx, theA0-A3moffs forms take a 32-bit offset, and in real modePUSHandPOPaddress the stack through 32-bit%esp. That is a separate change.Testing
tests/test_x86_addr16.pywalks all 24 memory forms of the 16-bit ModRM byte at each of the threemodvalues and asserts that each reads exactly the registers the r/m table names and lifts to the right length; then the negative 8-bit displacement, the segment override, the immediate, and the real-mode route that needs no prefix, with the bare 16-bit literal address and an unprefixed 32-bit amode as controls. Five of its seven tests fail on the merge base and all seven pass here. Swapping onlylibpyvex.sounder one fixed angr tree leaves angr'stests/analyses/cfgat the same counts with nothing failing on either side,test_cfg_6-- the BIOS binary above -- included.The file lifts byte strings rather than loading a fixture. Real objects do contain these encodings --
bios.bin.elfholds all 24 forms -- but only the ecosystem job has a sibling checkout. The four-legTestmatrix andTest (Pyodide)each take this repository and its submodule and nothing else, and pyvex has noconftest.pyand no skip guard, so a test opening anangr/binariesfixture would not skip on those five, it would fail. No pyvex test loads one today. Validation: #581 (comment)Merge order: angr/vex#97 first. This head's
vexsubmodule is5e25551, that pull request's head and one commit ahead ofvexmaster, so merging this half alone would pointpyvexmaster's submodule at a commit that is not onvexmaster. If it is squashed rather than merged, the submodule needs re-pointing at the squashed commit first.angr/vexis not in the CI repository list, so thesync:line below is a note to the reader, not an instruction to CI.sync: angr/vex#97
🤖 Generated with Claude Code
session: sharpen