You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While emulating x86 code, I tried to handle the divide-by-zero exception by setting the UC_HOOK_INTR hook. However, when the hook function returns, unicorn does not consider the exception handled, and when the next exception is encountered, unicorn calls the hook function with a double fault as an argument, and when the exception is encountered for the third time, the simulation execution terminates because of the triple fault.
The following code will reproduce the problem.
#include<unicorn/unicorn.h>// code to be emulated
#defineX86_CODE32"\xF7\xF1\xF7\xF1\xF7\xF1"// DIV ecx * 3// memory address where emulation starts
#defineADDRESS0x1000000voidintr_callback(uc_engine *uc, uint32_t intno, void *user_data) {
// ignore the exceptionprintf("Interrupt %d\n", intno);
// set EIP to next instructionuint32_t eip;
uc_reg_read(uc, UC_X86_REG_EIP, &eip);
eip += 2; // div lengthuc_reg_write(uc, UC_X86_REG_EIP, &eip);
}
intmain(int argc, char **argv, char **envp)
{
uc_engine *uc;
uc_err err;
int r_eax = 1;
int r_ecx = 0;
int r_edx = 0;
printf("Emulate i386 code\n");
// Initialize emulator in X86-32bit mode
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_open() with error returned: %u\n", err);
return -1;
}
// map 2MB memory for this emulationuc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
// write machine code to be emulated to memoryif (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1)) {
printf("Failed to write emulation code to memory, quit!\n");
return -1;
}
// initialize machine registersuc_reg_write(uc, UC_X86_REG_EAX, &r_eax);
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
// add hook
uc_hook intr_registry;
if (uc_hook_add(uc, &intr_registry, UC_HOOK_INTR, intr_callback, NULL, 1, 0)) {
printf("Failed to add interrupt hook\n");
return -1;
}
// emulate code in infinite time & unlimited instructions
err=uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned %u: %s\n",
err, uc_strerror(err));
}
// delete hookuc_hook_del(uc, intr_registry);
uc_close(uc);
return0;
}
Here's the output. 0 indicates a divide-by-zero exception, 8 indicates a double fault, and when the exception is encountered for the third time, a triple fault occurs and the emulation ends, so the third div instruction is not executed.
Emulate i386 code
Interrupt 0
Interrupt 8
I suspect that unicorn doesn't clean up env->old_exception after the hook function returns, and so when a following exception occurs and the check_exception function (in qemu/target/i386/excp_helper.c) is called, it will result in a misclassification as a nested exception. I'm not sure what the special meaning of the exception numbers 0, 10 to 13 in this code is.
/* * Check nested exceptions and change to double or triple fault if * needed. It should only be called, if this is not an interrupt. * Returns the new exception number.*/staticintcheck_exception(CPUX86State *env, int intno, int *error_code,
uintptr_t retaddr)
{
int first_contributory = env->old_exception == 0 ||
(env->old_exception >= 10 &&
env->old_exception <= 13);
int second_contributory = intno == 0 ||
(intno >= 10 && intno <= 13);
qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
env->old_exception, intno);
if (env->old_exception == EXCP08_DBLE) {
if (env->hflags & HF_GUEST_MASK) {
cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */
}
qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
qemu_system_reset_request(env->uc);
return EXCP_HLT;
}
if ((first_contributory && second_contributory)
|| (env->old_exception == EXCP0E_PAGE &&
(second_contributory || (intno == EXCP0E_PAGE)))) {
intno = EXCP08_DBLE;
*error_code = 0;
}
if (second_contributory || (intno == EXCP0E_PAGE) ||
(intno == EXCP08_DBLE)) {
env->old_exception = intno;
}
return intno;
}
The text was updated successfully, but these errors were encountered:
While emulating x86 code, I tried to handle the divide-by-zero exception by setting the
UC_HOOK_INTR
hook. However, when the hook function returns, unicorn does not consider the exception handled, and when the next exception is encountered, unicorn calls the hook function with a double fault as an argument, and when the exception is encountered for the third time, the simulation execution terminates because of the triple fault.The following code will reproduce the problem.
Here's the output. 0 indicates a divide-by-zero exception, 8 indicates a double fault, and when the exception is encountered for the third time, a triple fault occurs and the emulation ends, so the third div instruction is not executed.
I suspect that unicorn doesn't clean up
env->old_exception
after the hook function returns, and so when a following exception occurs and thecheck_exception
function (inqemu/target/i386/excp_helper.c
) is called, it will result in a misclassification as a nested exception. I'm not sure what the special meaning of the exception numbers 0, 10 to 13 in this code is.The text was updated successfully, but these errors were encountered: