-
Notifications
You must be signed in to change notification settings - Fork 127
Description
Description
VEX panics on certain invalid instructions, which results in lift() outputting nothing for the entire basic block (not even IR for instructions prior to the invalid one). Ideally, it would return a partial lifted block instead. This breaks angr's handling of self-modifying code containing these instructions (which can be worked around by single-stepping, but that comes with a performance penalty).
Steps to reproduce the bug
Example:
import pyvex, archinfo
# mov word ptr [rip], 0x9090
>>> inst0 = b'\x66\xc7\x05\x00\x00\x00\x00\x90\x90'
# invalid instruction referencing a nonexistent segment register
>>> inst1 = b'\x8c\xf0'
# Lifting the first instruction works
>>> pyvex.lift(inst0, 0, archinfo.ArchAMD64()).pp()
IRSB {
t0:Ity_I64 t1:Ity_I64
00 | ------ IMark(0x0, 9, 0) ------
01 | STle(0x0000000000000009) = 0x9090
NEXT: PUT(rip) = 0x0000000000000009; Ijk_Boring
}
# Appending the invalid instruction results in no output
>>> pyvex.lift(inst0 + inst1, 0, archinfo.ArchAMD64()).pp()
IRSB {
NEXT: PUT(rip) = 0x0000000000000000; Ijk_NoDecode
}
# The failure is due to the panic at https://github.com/angr/vex/blob/c0bace1c7e86a12e4d845139fc246334e7bc402f/priv/guest_x86_toIR.c#L519
>>> print(pyvex.lifting.libvex.LibVEXLifter.get_vex_log())
vex: the `impossible' happened:
segmentGuestRegOffset(amd64)
Environment
No response
Additional context
While VEX's disassembler could be fixed to handle this specific failure gracefully, I worry (though I have not verified) that there may be enough other reachable panics strewn around that it would take a fair amount of effort to fix up all of the error handling. Perhaps an easier, if somewhat hacky solution would be to have bb_to_IR update a lifted_upto variable in VexTranslateResult after every successfully lifted instruction so that pyvex can retry lifting with max_bytes to obtain a partial lift.