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); 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)))) } diff --git a/include/uc_priv.h b/include/uc_priv.h index 7e76513a..695b5fc6 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -98,7 +98,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/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} } diff --git a/qemu/memory.c b/qemu/memory.c index 95336f5f..f44d32c2 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; } void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) 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) 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; +} diff --git a/uc.c b/uc.c index ec85ea61..454d1a04 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; }