From 696c58f9f0f2f8570c2437ad9a0b4b58a92d7346 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 3 Nov 2015 14:11:49 +0100 Subject: [PATCH 1/6] Add test case for segfault-on-emu_stop() bug. The following code segfaults: unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64).emu_stop() Tested under Linux and OS X. --- tests/regress/segfault_on_stop.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 tests/regress/segfault_on_stop.py diff --git a/tests/regress/segfault_on_stop.py b/tests/regress/segfault_on_stop.py new file mode 100755 index 00000000..8d577109 --- /dev/null +++ b/tests/regress/segfault_on_stop.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import regress +import unicorn + + +class SegfaultOnStop(regress.RegressTest): + def test(self): + unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64).emu_stop() + self.assertTrue(True, "If not reached, then we have a crashing bug.") + +if __name__ == '__main__': + regress.main() From 4151d1d60087558e8adac8f01df97b25f7c4b22f Mon Sep 17 00:00:00 2001 From: practicalswift Date: Tue, 3 Nov 2015 14:34:31 +0100 Subject: [PATCH 2/6] Fix segfault-on-emu_stop() bug. --- uc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/uc.c b/uc.c index 00efc805..e3107b70 100644 --- a/uc.c +++ b/uc.c @@ -525,8 +525,10 @@ uc_err uc_emu_stop(uc_engine *uc) return UC_ERR_OK; uc->stop_request = true; - // exit the current TB - cpu_exit(uc->current_cpu); + if (uc->current_cpu) { + // exit the current TB + cpu_exit(uc->current_cpu); + } return UC_ERR_OK; } From 9f7d1812a39c6b31ad5b22ec10ab82533d69c19c Mon Sep 17 00:00:00 2001 From: mariano Date: Tue, 3 Nov 2015 12:58:20 -0800 Subject: [PATCH 3/6] SIGABRT issue --- tests/regress/init.py | 68 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/regress/init.py diff --git a/tests/regress/init.py b/tests/regress/init.py new file mode 100644 index 00000000..3c003e5a --- /dev/null +++ b/tests/regress/init.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +# By Mariano Graziano + +from unicorn import * +from unicorn.x86_const import * + +import regress, struct + +mu = 0 + +class Init(regress.RegressTest): + + def init_unicorn(self, ip, sp, counter): + global mu + print "[+] Emulating IP: %x SP: %x - Counter: %x" % (ip, sp, counter) + mu = Uc(UC_ARCH_X86, UC_MODE_64) + mu.mem_map(0x1000000, 2 * 1024 * 1024) + mu.mem_write(0x1000000, "\x90") + mu.mem_map(0x8000000, 8 * 1024 * 1024) + mu.reg_write(UC_X86_REG_RSP, sp) + content = self.generate_value(counter) + mu.mem_write(sp, content) + self.set_hooks() + + def generate_value(self, counter): + start = 0xffff880026f02000 + offset = counter * 8 + address = start + offset + return struct.pack(">> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value) + address_page = address & 0xFFFFFFFFFFFFF000 + mu.mem_map(address_page, 2 * 1024 * 1024) + mu.mem_write(address, str(value)) + return True + else: + return False + + def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data): + global mu + print "[ HOOK_MEM_FETCH - Address: %s ]" % hex(address).strip("L") + print "[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L") + mu.mem_write(0x1000003, "\x90") + mu.reg_write(UC_X86_REG_RIP, 0x1000001) + return True + + def runTest(self): + global mu + ips = list(xrange(0x1000000, 0x1001000, 0x1)) + sps = list(xrange(0x8000000, 0x8001000, 0x1)) + j = 0 + for i in ips: + j += 1 + index = ips.index(i) + self.init_unicorn(i, sps[index], j) + mu.emu_start(0x1000000, 0x1000000 + 0x1) + +if __name__ == '__main__': + regress.main() From 894739515e4fb9c935c008d783e3ae2b28117cbb Mon Sep 17 00:00:00 2001 From: farmdve Date: Wed, 4 Nov 2015 09:38:57 +0200 Subject: [PATCH 4/6] X86 EFLAGS not synced properly. these commits should fix my previous ones. --- tests/regress/Makefile | 1 + tests/regress/eflags_nosync.c | 184 ++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 tests/regress/eflags_nosync.c diff --git a/tests/regress/Makefile b/tests/regress/Makefile index 16ddeb28..a1f5e586 100644 --- a/tests/regress/Makefile +++ b/tests/regress/Makefile @@ -12,6 +12,7 @@ TESTS += mem_double_unmap TESTS += mem_protect TESTS += mem_exec TESTS += mips_kseg0_1 +TESTS += eflags_nosync all: $(TESTS) diff --git a/tests/regress/eflags_nosync.c b/tests/regress/eflags_nosync.c new file mode 100644 index 00000000..f31d6f3a --- /dev/null +++ b/tests/regress/eflags_nosync.c @@ -0,0 +1,184 @@ +#include +#include +#include + +#include + +#define X86_CODE32 "\x33\xD2\x8A\xD4\x8B\xC8\x81\xE1\xFF\x00\x00\x00" // XOR edx,edx; MOV dl,ah; MOV ecx,eax; AND ecx,FF +#define ADDRESS 0x1000000 +#define PAGE_8K (1 << 13) +#define PAGE_4K (1 << 12) +#define TARGET_PAGE_MASK ~(PAGE_4K - 1) +#define TARGET_PAGE_PREPARE(addr) (((addr) + PAGE_4K - 1) & TARGET_PAGE_MASK) +#define TARGET_PAGE_ALIGN(addr) (addr - (TARGET_PAGE_PREPARE(addr) - addr) & TARGET_PAGE_MASK) + +static uint64_t instructions = 0; + +static void hook_ins(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) +{ + instructions++; +} + +static bool hook_invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) +{ + uc_err err; + uint64_t address_align = TARGET_PAGE_ALIGN(address); + + if(address == 0) + { + printf("Address is 0, proof 0x%llX\n", address); + return false; + } + + switch(type) + { + default: + return false; + break; + case UC_MEM_WRITE_UNMAPPED: + printf("Mapping write address 0x%08llX to aligned 0x%08llX\n", address, address_align); + + err = uc_mem_map(uc, address_align, PAGE_8K, UC_PROT_ALL); + if(err != UC_ERR_OK) + { + printf("Failed to map memory on UC_MEM_WRITE_UNMAPPED %s\n", uc_strerror(err)); + return false; + } + + return true; + break; + case UC_MEM_READ_UNMAPPED: + + printf("Mapping read address 0x%08llX to aligned 0x%08llX\n", address, address_align); + + + err = uc_mem_map(uc, address_align, PAGE_8K, UC_PROT_ALL); + if(err != UC_ERR_OK) + { + printf("Failed to map memory on UC_MEM_READ_UNMAPPED %s\n", uc_strerror(err)); + return false; + } + + return true; + break; + } +} + +static void VM_exec() +{ + uc_engine *uc; + uc_err err; + uint32_t tmp; + uc_hook trace1, trace2; + unsigned int r_eax, r_ebx, r_ecx, r_edx, r_ebp, r_esp, r_esi, r_edi, r_eip, eflags; + unsigned int tr_eax, tr_ebx, tr_ecx, tr_edx, tr_ebp, tr_esp, tr_esi, tr_edi, tr_eip, t_eflags; + + + r_eax = tr_eax = 0x1DB10106; + r_ebx = tr_ebx = 0x7EFDE000; + r_ecx = tr_ecx = 0x7EFDE000; + r_edx = tr_edx = 0x00001DB1; + r_ebp = tr_ebp = 0x0018FF88; + r_esp = tr_esp = 0x0018FF14; + r_esi = tr_esi = 0x0; + r_edi = tr_edi = 0x0; + r_eip = tr_eip = 0x004939F3; + t_eflags = eflags = 0x00000206; + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + if(err) + { + printf("Failed on uc_open() with error returned: %s", uc_strerror(err)); + return; + } + + err = uc_mem_map(uc, ADDRESS, (4 * 1024 * 1024), UC_PROT_ALL); + if(err != UC_ERR_OK) + { + printf("Failed to map memory %s", uc_strerror(err)); + return; + } + + // write machine code to be emulated to memory + err = uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1); + if(err != UC_ERR_OK) + { + printf("Failed to write emulation code to memory, quit!: %s(len %d)", uc_strerror(err), sizeof(X86_CODE32) - 1); + return; + } + + // initialize machine registers + uc_reg_write(uc, UC_X86_REG_EAX, &r_eax); + uc_reg_write(uc, UC_X86_REG_EBX, &r_ebx); + uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); + uc_reg_write(uc, UC_X86_REG_EBP, &r_ebp); + uc_reg_write(uc, UC_X86_REG_ESP, &r_esp); + uc_reg_write(uc, UC_X86_REG_ESI, &r_esi); + uc_reg_write(uc, UC_X86_REG_EDI, &r_edi); + uc_reg_write(uc, UC_X86_REG_EFLAGS, &eflags); + + uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, (void *)hook_invalid_mem, NULL); + + // tracing all instruction by having @begin > @end + uc_hook_add(uc, &trace2, UC_HOOK_CODE, (void *)hook_ins, NULL, (uint64_t)1, (uint64_t)0); + + // emulate machine code in infinite time + 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", err, uc_strerror(err)); + instructions = 0; + + uc_close(uc); + return; + } + + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + uc_reg_read(uc, UC_X86_REG_EBX, &r_ebx); + uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); + uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); + uc_reg_read(uc, UC_X86_REG_EBP, &r_ebp); + uc_reg_read(uc, UC_X86_REG_ESP, &r_esp); + uc_reg_read(uc, UC_X86_REG_ESI, &r_esi); + uc_reg_read(uc, UC_X86_REG_EDI, &r_edi); + uc_reg_read(uc, UC_X86_REG_EIP, &r_eip); + uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags); + + uc_close(uc); + + printf(">>> Emulation done. Below is the CPU context\n"); + printf(">>> EAX = 0x%08X %s\n", r_eax, (r_eax == tr_eax ? "" : "(m)")); + printf(">>> EBX = 0x%08X %s\n", r_ebx, (r_ebx == tr_ebx ? "" : "(m)")); + printf(">>> ECX = 0x%08X %s\n", r_ecx, (r_ecx == tr_ecx ? "" : "(m)")); + printf(">>> EDX = 0x%08X %s\n", r_edx, (r_edx == tr_edx ? "" : "(m)")); + printf(">>> EBP = 0x%08X %s\n", r_ebp, (r_ebp == tr_ebp ? "" : "(m)")); + printf(">>> ESP = 0x%08X %s\n", r_esp, (r_esp == tr_esp ? "" : "(m)")); + printf(">>> ESI = 0x%08X %s\n", r_esi, (r_esi == tr_esi ? "" : "(m)")); + printf(">>> EDI = 0x%08X %s\n", r_edi, (r_edi == tr_edi ? "" : "(m)")); + printf(">>> EIP = 0x%08X %s\n", (r_eip - ADDRESS) + tr_eip, (r_eip == tr_eip ? "" : "(m)\n")); + printf(">>> EFLAGS = 0x%08X %s", eflags, (eflags == t_eflags ? "" : "(m)\n")); + + printf(">>> Instructions executed %llu\n", instructions); + + assert(r_eax == 0x1DB10106); + assert(r_ebx == 0x7EFDE000); + assert(r_ecx == 0x00000006); + assert(r_edx == 0x00000001); + assert(r_ebp == 0x0018FF88); + assert(r_esp == 0x0018FF14); + assert(r_esi == 0x00000000); + assert(r_edi == 0x00000000); + assert(eflags == 0x00000206); //we shouldn't fail this assert, eflags should be 0x00000206 because the last AND instruction produces a non-zero result. + + instructions = 0; + +} + + +int main(int argc, char *argv[]) +{ + VM_exec(); + return 0; +} \ No newline at end of file From 95745eff3be7f9b5db4febc7983d368323f3b6cf Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Thu, 5 Nov 2015 20:22:07 +0800 Subject: [PATCH 5/6] regress: fix compiled warnings in eflags_nosync.c --- .gitignore | 1 + tests/regress/eflags_nosync.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 05188003..659a6cbd 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ timeout_segfault rep_movsb mem_double_unmap mips_kseg0_1 +eflags_nosync ################# diff --git a/tests/regress/eflags_nosync.c b/tests/regress/eflags_nosync.c index f31d6f3a..3e141414 100644 --- a/tests/regress/eflags_nosync.c +++ b/tests/regress/eflags_nosync.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -26,7 +27,7 @@ static bool hook_invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, if(address == 0) { - printf("Address is 0, proof 0x%llX\n", address); + printf("Address is 0, proof 0x%" PRIx64 "\n", address); return false; } @@ -34,9 +35,9 @@ static bool hook_invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, { default: return false; - break; + break; case UC_MEM_WRITE_UNMAPPED: - printf("Mapping write address 0x%08llX to aligned 0x%08llX\n", address, address_align); + printf("Mapping write address 0x%" PRIx64 " to aligned 0x%" PRIx64 "\n", address, address_align); err = uc_mem_map(uc, address_align, PAGE_8K, UC_PROT_ALL); if(err != UC_ERR_OK) @@ -46,10 +47,10 @@ static bool hook_invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, } return true; - break; + break; case UC_MEM_READ_UNMAPPED: - printf("Mapping read address 0x%08llX to aligned 0x%08llX\n", address, address_align); + printf("Mapping read address 0x%" PRIx64 " to aligned 0x%" PRIx64 "\n", address, address_align); err = uc_mem_map(uc, address_align, PAGE_8K, UC_PROT_ALL); @@ -60,7 +61,7 @@ static bool hook_invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, } return true; - break; + break; } } @@ -104,7 +105,7 @@ static void VM_exec() err = uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1); if(err != UC_ERR_OK) { - printf("Failed to write emulation code to memory, quit!: %s(len %d)", uc_strerror(err), sizeof(X86_CODE32) - 1); + printf("Failed to write emulation code to memory, quit!: %s(len %lu)", uc_strerror(err), sizeof(X86_CODE32) - 1); return; } @@ -130,7 +131,7 @@ static void VM_exec() { printf("Failed on uc_emu_start() with error returned %u: %s", err, uc_strerror(err)); instructions = 0; - + uc_close(uc); return; } @@ -158,9 +159,9 @@ static void VM_exec() printf(">>> ESI = 0x%08X %s\n", r_esi, (r_esi == tr_esi ? "" : "(m)")); printf(">>> EDI = 0x%08X %s\n", r_edi, (r_edi == tr_edi ? "" : "(m)")); printf(">>> EIP = 0x%08X %s\n", (r_eip - ADDRESS) + tr_eip, (r_eip == tr_eip ? "" : "(m)\n")); - printf(">>> EFLAGS = 0x%08X %s", eflags, (eflags == t_eflags ? "" : "(m)\n")); + printf(">>> EFLAGS = 0x%08X %s\n", eflags, (eflags == t_eflags ? "" : "(m)")); - printf(">>> Instructions executed %llu\n", instructions); + printf(">>> Instructions executed %" PRIu64 "\n", instructions); assert(r_eax == 0x1DB10106); assert(r_ebx == 0x7EFDE000); @@ -173,7 +174,6 @@ static void VM_exec() assert(eflags == 0x00000206); //we shouldn't fail this assert, eflags should be 0x00000206 because the last AND instruction produces a non-zero result. instructions = 0; - } @@ -181,4 +181,4 @@ int main(int argc, char *argv[]) { VM_exec(); return 0; -} \ No newline at end of file +} From 51323c9c17a32f204b169bebc10d1c79553e966f Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Thu, 5 Nov 2015 20:26:39 +0800 Subject: [PATCH 6/6] x86: properly calculate EFLAGS when UC_HOOK_CODE is used. this should fix issue #246 --- qemu/target-i386/cc_helper.c | 7 +------ qemu/target-i386/cpu.h | 3 ++- qemu/target-i386/helper.c | 1 + qemu/target-i386/unicorn.c | 3 +++ 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qemu/target-i386/cc_helper.c b/qemu/target-i386/cc_helper.c index ecbf0ec0..2de54053 100644 --- a/qemu/target-i386/cc_helper.c +++ b/qemu/target-i386/cc_helper.c @@ -328,12 +328,7 @@ void helper_write_eflags(CPUX86State *env, target_ulong t0, target_ulong helper_read_eflags(CPUX86State *env) { - uint32_t eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - eflags |= (env->df & DF_MASK); - eflags |= env->eflags & ~(VM_MASK | RF_MASK); - return eflags; + return cpu_compute_eflags(env); } void helper_clts(CPUX86State *env) diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h index e14e6612..4628a8df 100644 --- a/qemu/target-i386/cpu.h +++ b/qemu/target-i386/cpu.h @@ -839,6 +839,7 @@ typedef struct CPUX86State { /* standard registers */ target_ulong regs[CPU_NB_REGS]; target_ulong eip; + target_ulong eflags0; // copy of eflags that does not change thru the BB target_ulong eflags; /* eflags register. During CPU emulation, CC flags and DF are set to zero because they are stored elsewhere */ @@ -1314,7 +1315,7 @@ void update_fp_status(CPUX86State *env); static inline uint32_t cpu_compute_eflags(CPUX86State *env) { - return env->eflags | cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK); + return env->eflags0 | cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK); } /* NOTE: the translator must set DisasContext.cc_op to CC_OP_EFLAGS diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c index b098c221..ca8af555 100644 --- a/qemu/target-i386/helper.c +++ b/qemu/target-i386/helper.c @@ -1141,4 +1141,5 @@ void x86_cpu_exec_exit(CPUState *cs) CPUX86State *env = &cpu->env; env->eflags = cpu_compute_eflags(env); + env->eflags0 = env->eflags; } diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index acac9638..b7c62e30 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -70,6 +70,7 @@ void x86_reg_reset(struct uc_struct *uc) env->eip = 0; env->eflags = 0; + env->eflags0 = 0; env->fpstt = 0; /* top of stack index */ env->fpus = 0; @@ -580,6 +581,7 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_EFLAGS: X86_CPU(uc, mycpu)->env.eflags = *(uint32_t *)value; + X86_CPU(uc, mycpu)->env.eflags0 = *(uint32_t *)value; break; case UC_X86_REG_EAX: X86_CPU(uc, mycpu)->env.regs[R_EAX] = *(uint32_t *)value; @@ -693,6 +695,7 @@ int x86_reg_write(struct uc_struct *uc, unsigned int regid, const void *value) break; case UC_X86_REG_EFLAGS: X86_CPU(uc, mycpu)->env.eflags = *(uint64_t *)value; + X86_CPU(uc, mycpu)->env.eflags0 = *(uint64_t *)value; break; case UC_X86_REG_RAX: X86_CPU(uc, mycpu)->env.regs[R_EAX] = *(uint64_t *)value;