Skip to content

Commit 69200d4

Browse files
committed
Fix regression: If invalid instruction is handled, allow emulation to continue
1 parent 3b2f54f commit 69200d4

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

qemu/accel/tcg/cpu-exec.c

+3
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
359359
// we want to stop emulation
360360
*ret = EXCP_HLT;
361361
return true;
362+
} else {
363+
// Continue execution because user hints us it has been handled
364+
cpu->exception_index = -1;
362365
}
363366
}
364367

tests/regress/invalid_insn.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
2+
import regress
3+
4+
from unicorn import *
5+
from unicorn.x86_const import *
6+
7+
from capstone import *
8+
9+
10+
CODE = bytes.fromhex(
11+
'48 31 c0' # xor rax,rax
12+
'48 0f c7 f0' # rdrand rax
13+
'f4' # hlt
14+
)
15+
16+
BASE = 0x100000
17+
PAGE_SIZE = 0x1000
18+
19+
# max possible length of a x86 instruction
20+
MAX_INSN_LEN = 15
21+
22+
23+
def hook_invalid_insn(uc, ud):
24+
regress.logger.debug('entered invalid instruction handler')
25+
26+
pc = uc.reg_read(UC_X86_REG_RIP)
27+
data = uc.mem_read(pc, MAX_INSN_LEN)
28+
29+
md = Cs(CS_ARCH_X86, CS_MODE_64)
30+
insn = next(md.disasm(data, pc, 1))
31+
32+
if insn.mnemonic == 'rdrand':
33+
# chosen by fair dice roll, guaranteed to be random
34+
rax = 4
35+
36+
# set result to rax
37+
uc.reg_write(UC_X86_REG_RAX, rax)
38+
39+
# resume emulation from next instruction
40+
uc.reg_write(UC_X86_REG_RIP, pc + insn.size)
41+
42+
# signal uc we are ok
43+
return True
44+
45+
# not handled, uc will crash
46+
return False
47+
48+
49+
class TestHooks(regress.RegressTest):
50+
def test_invalid_insn_recover(self):
51+
mu = Uc(UC_ARCH_X86, UC_MODE_64)
52+
53+
mu.mem_map(BASE, PAGE_SIZE)
54+
mu.mem_write(BASE, CODE)
55+
56+
mu.hook_add(UC_HOOK_INSN_INVALID, hook_invalid_insn)
57+
58+
try:
59+
mu.emu_start(BASE, BASE + len(CODE))
60+
except UcError as ex:
61+
if ex.errno == UC_ERR_INSN_INVALID:
62+
self.fail('invalid instruction did not recover properly')
63+
64+
# unexpected exception, re-raise
65+
raise
66+
67+
self.assertNotEqual(0, mu.reg_read(UC_X86_REG_RAX))
68+
69+
70+
if __name__ == '__main__':
71+
regress.main()

0 commit comments

Comments
 (0)