From 7ec29e7bc418ab3ead34a4d793d38d996e7aee27 Mon Sep 17 00:00:00 2001 From: cherepanov74 Date: Sat, 29 Aug 2015 17:04:48 +0200 Subject: [PATCH 1/7] Fixing compile on windows with mingw It does not break cross-compiling on Linux --- make.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make.sh b/make.sh index 92d2db3a..98cac449 100755 --- a/make.sh +++ b/make.sh @@ -38,8 +38,8 @@ build_cross() { [ "$UNAME" = Darwin ] && LIBARCHS="i386 x86_64" CROSS=$1 CC=$CROSS-gcc \ - AR=$CROSS-ar \ - RANLIB=$CROSS-ranlib \ + AR=$CROSS-gcc-ar \ + RANLIB=$CROSS-gcc-ranlib \ GLIB="-L/usr/$CROSS/lib/ -lglib-2.0" \ ${MAKE} } From e2036424c12fa65e76926a6ae953624218ec4cc7 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Sat, 29 Aug 2015 08:32:58 -0700 Subject: [PATCH 2/7] improve Go binding safety --- bindings/go/unicorn/unicorn.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bindings/go/unicorn/unicorn.go b/bindings/go/unicorn/unicorn.go index f2e925f9..04f161c0 100644 --- a/bindings/go/unicorn/unicorn.go +++ b/bindings/go/unicorn/unicorn.go @@ -72,10 +72,16 @@ func (u *Uc) RegRead(reg int) (uint64, error) { } func (u *Uc) MemWrite(addr uint64, data []byte) error { + if len(data) == 0 { + return nil + } return errReturn(C.uc_mem_write(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&data[0])), C.size_t(len(data)))) } func (u *Uc) MemReadInto(dst []byte, addr uint64) error { + if len(dst) == 0 { + return nil + } return errReturn(C.uc_mem_read(u.Handle, C.uint64_t(addr), (*C.uint8_t)(unsafe.Pointer(&dst[0])), C.size_t(len(dst)))) } From cb09df3027aefac37ab464dfcb2d12debd0d7541 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Sat, 29 Aug 2015 09:42:36 -0700 Subject: [PATCH 3/7] fix invalid memory hook in Go bindings --- bindings/go/unicorn/hook.c | 4 ++-- bindings/go/unicorn/hook.go | 13 +++++++++---- bindings/go/unicorn/hook.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/bindings/go/unicorn/hook.c b/bindings/go/unicorn/hook.c index b928ae77..e715e1a2 100644 --- a/bindings/go/unicorn/hook.c +++ b/bindings/go/unicorn/hook.c @@ -9,8 +9,8 @@ void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user) { hookCode(handle, addr, size, user); } -bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int64_t value, void *user) { - return hookMemInvalid(handle, type, addr, value, user); +bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user) { + return hookMemInvalid(handle, type, addr, size, value, user); } void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user) { diff --git a/bindings/go/unicorn/hook.go b/bindings/go/unicorn/hook.go index ac4d8dcf..b9e3c14b 100644 --- a/bindings/go/unicorn/hook.go +++ b/bindings/go/unicorn/hook.go @@ -23,15 +23,15 @@ func hookCode(handle C.uch, addr C.uint64_t, size C.uint32_t, user unsafe.Pointe } //export hookMemInvalid -func hookMemInvalid(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, value C.int64_t, user unsafe.Pointer) C.bool { +func hookMemInvalid(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, size int, value C.int64_t, user unsafe.Pointer) C.bool { hook := (*HookData)(user) - return C.bool(hook.Callback.(func(*Uc, int, uint64, int64) bool)(hook.Uc, int(typ), uint64(addr), int64(value))) + return C.bool(hook.Callback.(func(*Uc, int, uint64, int, int64) bool)(hook.Uc, int(typ), uint64(addr), size, int64(value))) } //export hookMemAccess func hookMemAccess(handle C.uch, typ C.uc_mem_type, addr C.uint64_t, size int, value C.int64_t, user unsafe.Pointer) { hook := (*HookData)(user) - hook.Callback.(func(*Uc, int, uint64, uint32, int64))(hook.Uc, int(typ), uint64(addr), uint32(size), int64(value)) + hook.Callback.(func(*Uc, int, uint64, int, int64))(hook.Uc, int(typ), uint64(addr), size, int64(value)) } //export hookX86In @@ -52,6 +52,8 @@ func hookX86Syscall(handle C.uch, user unsafe.Pointer) { hook.Callback.(func(*Uc))(hook.Uc) } +var hookRetain = make(map[C.uch]*HookData) + func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) { var callback unsafe.Pointer var extra C.int @@ -78,10 +80,13 @@ func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) { return 0, errors.New("Unknown hook type.") } var h2 C.uch - C.uc_hook_add2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(&HookData{u, cb}), extra) + data := &HookData{u, cb} + C.uc_hook_add2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(data), extra) + hookRetain[h2] = data return h2, nil } func (u *Uc) HookDel(hook *C.uch) error { + delete(hookRetain, *hook) return errReturn(C.uc_hook_del(u.Handle, hook)) } diff --git a/bindings/go/unicorn/hook.h b/bindings/go/unicorn/hook.h index 8bafb526..a89d8ec0 100644 --- a/bindings/go/unicorn/hook.h +++ b/bindings/go/unicorn/hook.h @@ -1,6 +1,6 @@ uc_err uc_hook_add2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int extra); void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user); -bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int64_t value, void *user); +bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user); void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user); uint32_t hookX86In_cgo(uch handle, uint32_t port, uint32_t size, void *user); void hookX86Out_cgo(uch handle, uint32_t port, uint32_t size, uint32_t value, void *user); From be98e2813263fb566938497a8d75d0e977761a8f Mon Sep 17 00:00:00 2001 From: gaffe Date: Sat, 29 Aug 2015 12:44:29 -0700 Subject: [PATCH 4/7] add sample regression code for issue #78 --- regress/timeout_segfault.c | 149 +++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 regress/timeout_segfault.c diff --git a/regress/timeout_segfault.c b/regress/timeout_segfault.c new file mode 100644 index 00000000..a1378e6a --- /dev/null +++ b/regress/timeout_segfault.c @@ -0,0 +1,149 @@ +/* +timeout_segfault.c + +This program shows a case where the emulation timer keeps running after +emulation has ended. It triggers an intermittent segfault when _timeout_fn() +tries to call uc_emu_stop() after emulation has already been cleaned up. This +code is the same as samples/sample_arm.c, except that it adds a timeout on each +call to uc_emu_start(). See issue #78 for more details: +https://github.com/unicorn-engine/unicorn/issues/78 +*/ + +#include + +#include + + +// code to be emulated +#define ARM_CODE "\x37\x00\xa0\xe3\x03\x10\x42\xe0" // mov r0, #0x37; sub r1, r2, r3 +#define THUMB_CODE "\x83\xb0" // sub sp, #0xc + +// memory address where emulation starts +#define ADDRESS 0x10000 + +// number of seconds to wait before timeout +#define TIMEOUT 5 + +static void hook_block(uch handle, uint64_t address, uint32_t size, void *user_data) +{ + printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size); +} + +static void hook_code(uch handle, uint64_t address, uint32_t size, void *user_data) +{ + printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size); +} + +static void test_arm(void) +{ + uch handle; + uc_err err; + uch trace1, trace2; + + int r0 = 0x1234; // R0 register + int r2 = 0x6789; // R1 register + int r3 = 0x3333; // R2 register + int r1; // R1 register + + printf("Emulate ARM code\n"); + + // Initialize emulator in ARM mode + err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &handle); + if (err) { + printf("Failed on uc_open() with error returned: %u (%s)\n", + err, uc_strerror(err)); + return; + } + + // map 2MB memory for this emulation + uc_mem_map(handle, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); + + // write machine code to be emulated to memory + uc_mem_write(handle, ADDRESS, (uint8_t *)ARM_CODE, sizeof(ARM_CODE) - 1); + + // initialize machine registers + uc_reg_write(handle, UC_ARM_REG_R0, &r0); + uc_reg_write(handle, UC_ARM_REG_R2, &r2); + uc_reg_write(handle, UC_ARM_REG_R3, &r3); + + // tracing all basic blocks with customized callback + uc_hook_add(handle, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + + // tracing one instruction at ADDRESS with customized callback + uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + + // emulate machine code in infinite time (last param = 0), or when + // finishing all the code. + err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(ARM_CODE) -1, UC_SECOND_SCALE * TIMEOUT, 0); + if (err) { + printf("Failed on uc_emu_start() with error returned: %u\n", err); + } + + // now print out some registers + printf(">>> Emulation done. Below is the CPU context\n"); + + uc_reg_read(handle, UC_ARM_REG_R0, &r0); + uc_reg_read(handle, UC_ARM_REG_R1, &r1); + printf(">>> R0 = 0x%x\n", r0); + printf(">>> R1 = 0x%x\n", r1); + + uc_close(&handle); +} + +static void test_thumb(void) +{ + uch handle; + uc_err err; + uch trace1, trace2; + + int sp = 0x1234; // R0 register + + printf("Emulate THUMB code\n"); + + // Initialize emulator in ARM mode + err = uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &handle); + if (err) { + printf("Failed on uc_open() with error returned: %u (%s)\n", + err, uc_strerror(err)); + return; + } + + // map 2MB memory for this emulation + uc_mem_map(handle, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL); + + // write machine code to be emulated to memory + uc_mem_write(handle, ADDRESS, (uint8_t *)THUMB_CODE, sizeof(THUMB_CODE) - 1); + + // initialize machine registers + uc_reg_write(handle, UC_ARM_REG_SP, &sp); + + // tracing all basic blocks with customized callback + uc_hook_add(handle, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0); + + // tracing one instruction at ADDRESS with customized callback + uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS); + + // emulate machine code in infinite time (last param = 0), or when + // finishing all the code. + err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(THUMB_CODE) -1, UC_SECOND_SCALE * TIMEOUT, 0); + if (err) { + printf("Failed on uc_emu_start() with error returned: %u\n", err); + } + + // now print out some registers + printf(">>> Emulation done. Below is the CPU context\n"); + + uc_reg_read(handle, UC_ARM_REG_SP, &sp); + printf(">>> SP = 0x%x\n", sp); + + uc_close(&handle); +} + +int main(int argc, char **argv, char **envp) +{ + test_arm(); + printf("==========================\n"); + test_thumb(); + + return 0; +} From e788657a168e608df9d21ce1abbf79d2e812524f Mon Sep 17 00:00:00 2001 From: gaffe Date: Sat, 29 Aug 2015 12:51:35 -0700 Subject: [PATCH 5/7] also update Makefile to build timeout_segfault.c --- regress/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/regress/Makefile b/regress/Makefile index a75cde02..bbb49c2a 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -5,6 +5,7 @@ TESTS = map_crash map_write TESTS += sigill sigill2 TESTS += block_test TESTS += ro_mem_test nr_mem_test +TESTS += timeout_segfault all: $(TESTS) From b4d7347fa8105d7c339e53e5477157f300dcddd4 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Sun, 30 Aug 2015 05:12:04 +0800 Subject: [PATCH 6/7] wait for the timer thread to finish at the end of uc_emu_start(). this may fix the issue #78 --- uc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/uc.c b/uc.c index 48f810bc..6d93d0e7 100644 --- a/uc.c +++ b/uc.c @@ -549,6 +549,11 @@ uc_err uc_emu_start(uch handle, uint64_t begin, uint64_t until, uint64_t timeout // emulation is done uc->emulation_done = true; + if (timeout) { + // wait for the timer to finish + qemu_thread_join(&uc->timer); + } + return uc->invalid_error; } From c23d387e2f1f755724f3f3fdc06382e573a45cd9 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Sun, 30 Aug 2015 10:51:28 +0800 Subject: [PATCH 7/7] remove redundant uc_struct.ram --- include/uc_priv.h | 1 - qemu/memory.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index 686ab9e5..007fecea 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -95,7 +95,6 @@ struct uc_struct { void* cpu; MemoryRegion *system_memory; // qemu/exec.c - MemoryRegion *ram; MemoryRegion io_mem_rom; // qemu/exec.c MemoryRegion io_mem_notdirty; // qemu/exec.c MemoryRegion io_mem_unassigned; // qemu/exec.c diff --git a/qemu/memory.c b/qemu/memory.c index 3f8169d9..60f86ae4 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -33,16 +33,16 @@ // Unicorn engine MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms) { - uc->ram = g_new(MemoryRegion, 1); + MemoryRegion *ram = g_new(MemoryRegion, 1); - memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, perms, &error_abort); + memory_region_init_ram(uc, ram, NULL, "pc.ram", size, perms, &error_abort); - memory_region_add_subregion(get_system_memory(uc), begin, uc->ram); + memory_region_add_subregion(get_system_memory(uc), begin, ram); if (uc->current_cpu) tlb_flush(uc->current_cpu, 1); - return uc->ram; + return ram; } int memory_free(struct uc_struct *uc)