1import nose
2import archinfo
3from pyvex import IRSB, lift, ffi
4from pyvex.lifting.util import Instruction, GymratLifter, JumpKind
5from pyvex.errors import PyVEXError
6
7def test_partial_lift():
8    """This tests that gymrat correctly handles the case where an
9    instruction is longer than the remaining input.
10    """
11    class NOP(Instruction):
12        name = "nop"
13        bin_format = "0000111100001111"
14
15        def compute_result(self, *args):
16            pass
17
18    class NOPLifter(GymratLifter):
19        instrs = [NOP]
20
21    lifter = NOPLifter(archinfo.ArchAMD64(), 0)
22    # this should not throw an exception
23    block = lifter._lift("\x0F\x0Fa")
24    nose.tools.assert_equal(block.size, 2)
25    nose.tools.assert_equal(block.instructions, 1)
26    nose.tools.assert_equal(block.jumpkind, JumpKind.NoDecode)
27
28
29def test_skipstmts_toomanyexits():
30
31    # https://github.com/angr/pyvex/issues/153
32
33    old_exit_limit = IRSB.MAX_EXITS
34    IRSB.MAX_EXITS = 32
35
36    bytes_ = bytes.fromhex("0DF1B00B2EAB94E8030008938BE803000DF1C0089AE8030083E80300019B0DF1F00A339AE669E26193E8030085E8030098E8030083E80300069B95E8030088E80300A26993E803004A92002363622362A361E362A36238AC029A069484E8030012AC0998299328932B9303C885E8030092E8030084E803009AE8030082E803002A460A9D26993E910B9941910D9942910C992A93409548AD439194E803008AE8030027983F9927913F909BE803000DF5887B269335938BE803000DF58C7B089903C98BE8030098E8030084E8030095E8030088E803004B993391329394E80300349337933693069C059B4C93049B4E9350ABCDF834C1CDF83CE185E8030094E803004B9683E8030015A94498C4F7E2EA")
37    arch = archinfo.arch_from_id("ARMEL")
38    # Lifting the first four bytes will not cause any problem. Statements should be skipped as expected
39    b = IRSB(bytes_[:34], 0xC6951, arch, opt_level=1, bytes_offset=5, skip_stmts=True)
40    nose.tools.assert_greater(len(b.exit_statements), 0)
41    nose.tools.assert_false(b.has_statements, None)
42
43    # Lifting the entire block will cause the number of exit statements go beyond the limit (currently 32). PyVEX will
44    # automatically relift this block without skipping the statements
45    b = IRSB(bytes_, 0xC6951, arch, opt_level=1, bytes_offset=5, skip_stmts=True)
46    nose.tools.assert_is_not(b.statements, None)
47    nose.tools.assert_greater(len(b.exit_statements), 32)
48
49    # Restore the setting
50    IRSB.MAX_EXITS = old_exit_limit
51
52def test_max_bytes():
53    data = bytes.fromhex('909090909090c3')
54    arch = archinfo.ArchX86()
55    nose.tools.assert_equal(lift(data, 0x1000, arch, max_bytes=None).size, len(data))
56    nose.tools.assert_equal(lift(data, 0x1000, arch, max_bytes=len(data) - 1).size, len(data) - 1)
57    nose.tools.assert_equal(lift(data, 0x1000, arch, max_bytes=len(data) + 1).size, len(data))
58
59    data2 = ffi.from_buffer(data)
60    nose.tools.assert_raises(PyVEXError, lift, data2, 0x1000, arch)
61    nose.tools.assert_equal(lift(data2, 0x1000, arch, max_bytes=len(data)).size, len(data))
62    nose.tools.assert_equal(lift(data2, 0x1000, arch, max_bytes=len(data) - 1).size, len(data) - 1)
63
64if __name__ == '__main__':
65    test_partial_lift()
66    test_skipstmts_toomanyexits()
67    test_max_bytes()
68