diff --git a/include/list.h b/include/list.h index 84917ab3..5699e0a2 100644 --- a/include/list.h +++ b/include/list.h @@ -27,4 +27,7 @@ void *list_append(struct list *list, void *data); // returns true if entry was removed, false otherwise bool list_remove(struct list *list, void *data); +// returns true if the data exists in the list +bool list_exists(struct list *list, void *data); + #endif diff --git a/include/uc_priv.h b/include/uc_priv.h index ee5859c1..a3023e33 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -86,6 +86,7 @@ struct hook { int type; // UC_HOOK_* int insn; // instruction for HOOK_INSN int refs; // reference count to free hook stored in multiple lists + bool to_delete; // set to true when the hook is deleted by the user. The destruction of the hook is delayed. uint64_t begin, end; // only trigger if PC or memory access is in this address (depends on hook type) void *callback; // a uc_cb_* type void *user_data; @@ -212,6 +213,7 @@ struct uc_struct { // linked lists containing hooks per type struct list hook[UC_HOOK_MAX]; + struct list hooks_to_del; // hook to count number of instructions for uc_emu_start() uc_hook count_hook; diff --git a/list.c b/list.c index 0703c10c..164efcbe 100644 --- a/list.c +++ b/list.c @@ -90,3 +90,22 @@ bool list_remove(struct list *list, void *data) } return false; } + +// returns true if the data exists in the list +bool list_exists(struct list *list, void *data) +{ + struct list_item *next, *cur = NULL; + // is list empty? + if (list->head == NULL) { + return false; + } + cur = list->head; + while (cur != NULL) { + next = cur->next; + if (cur->data == data) { + return true; + } + cur = next; + } + return false; +} \ No newline at end of file diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index 37eb812c..7da6e3f4 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -135,6 +135,8 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq // Unicorn: call registered invalid instruction callbacks HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN_INVALID) { + if (hook->to_delete) + continue; catched = ((uc_cb_hookinsn_invalid_t)hook->callback)(uc, hook->user_data); if (catched) break; @@ -145,6 +147,8 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq // Unicorn: call registered interrupt callbacks HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INTR) { + if (hook->to_delete) + continue; ((uc_cb_hookintr_t)hook->callback)(uc, cpu->exception_index, hook->user_data); catched = true; } diff --git a/qemu/ioport.c b/qemu/ioport.c index bed2c13d..336b43dc 100644 --- a/qemu/ioport.c +++ b/qemu/ioport.c @@ -70,6 +70,8 @@ void cpu_outb(struct uc_struct *uc, pio_addr_t addr, uint8_t val) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_OUT) ((uc_cb_insn_out_t)hook->callback)(uc, addr, 1, val, hook->user_data); } @@ -82,6 +84,8 @@ void cpu_outw(struct uc_struct *uc, pio_addr_t addr, uint16_t val) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_OUT) ((uc_cb_insn_out_t)hook->callback)(uc, addr, 2, val, hook->user_data); } @@ -94,6 +98,8 @@ void cpu_outl(struct uc_struct *uc, pio_addr_t addr, uint32_t val) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_OUT) ((uc_cb_insn_out_t)hook->callback)(uc, addr, 4, val, hook->user_data); } @@ -106,6 +112,8 @@ uint8_t cpu_inb(struct uc_struct *uc, pio_addr_t addr) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_IN) return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 1, hook->user_data); } @@ -120,6 +128,8 @@ uint16_t cpu_inw(struct uc_struct *uc, pio_addr_t addr) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_IN) return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 2, hook->user_data); } @@ -134,6 +144,8 @@ uint32_t cpu_inl(struct uc_struct *uc, pio_addr_t addr) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (hook->insn == UC_X86_INS_IN) return ((uc_cb_insn_in_t)hook->callback)(uc, addr, 4, hook->user_data); } diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 6e5e896b..f39509bd 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -202,6 +202,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) @@ -210,6 +212,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #else error_code = UC_ERR_READ_UNMAPPED; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) @@ -233,6 +237,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) @@ -258,6 +264,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // about successful read if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); @@ -268,6 +276,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) @@ -399,6 +409,8 @@ _out: // Unicorn: callback on successful read if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_AFTER) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ_AFTER, addr, DATA_SIZE, res, hook->user_data); @@ -433,6 +445,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #if defined(SOFTMMU_CODE_ACCESS) error_code = UC_ERR_FETCH_UNMAPPED; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) @@ -441,6 +455,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #else error_code = UC_ERR_READ_UNMAPPED; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_UNMAPPED, addr, DATA_SIZE, 0, hook->user_data))) @@ -464,6 +480,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { // non-executable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_FETCH_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, hook->user_data))) @@ -489,6 +507,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // about successful read if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, hook->user_data); @@ -499,6 +519,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, if (READ_ACCESS_TYPE == MMU_DATA_LOAD && mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, hook->user_data))) @@ -625,6 +647,8 @@ _out: // Unicorn: callback on successful read if (READ_ACCESS_TYPE == MMU_DATA_LOAD) { HOOK_FOREACH(uc, hook, UC_HOOK_MEM_READ_AFTER) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; ((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ_AFTER, addr, DATA_SIZE, res, hook->user_data); @@ -697,8 +721,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on memory write HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { - if (!HOOK_BOUND_CHECK(hook, addr)) - continue; + if (hook->to_delete) + continue; + if (!HOOK_BOUND_CHECK(hook, addr)) + continue; ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); } @@ -706,6 +732,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (mr == NULL) { handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) @@ -729,6 +757,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) @@ -856,6 +886,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on memory write HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; ((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, addr, DATA_SIZE, val, hook->user_data); @@ -865,6 +897,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (mr == NULL) { handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_UNMAPPED) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_UNMAPPED, addr, DATA_SIZE, val, hook->user_data))) @@ -888,6 +922,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable handled = false; HOOK_FOREACH(uc, hook, UC_HOOK_MEM_WRITE_PROT) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, addr)) continue; if ((handled = ((uc_cb_eventmem_t)hook->callback)(uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, val, hook->user_data))) diff --git a/qemu/target-i386/seg_helper.c b/qemu/target-i386/seg_helper.c index a86a89e4..fd9765a1 100644 --- a/qemu/target-i386/seg_helper.c +++ b/qemu/target-i386/seg_helper.c @@ -949,6 +949,8 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, env->eip)) continue; if (hook->insn == UC_X86_INS_SYSCALL) @@ -2389,6 +2391,8 @@ void helper_sysenter(CPUX86State *env, int next_eip_addend) struct hook *hook; HOOK_FOREACH_VAR_DECLARE; HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) { + if (hook->to_delete) + continue; if (!HOOK_BOUND_CHECK(hook, env->eip)) continue; if (hook->insn == UC_X86_INS_SYSENTER) diff --git a/uc.c b/uc.c index adf6edd2..f84895b4 100644 --- a/uc.c +++ b/uc.c @@ -530,6 +530,28 @@ static void hook_count_cb(struct uc_struct *uc, uint64_t address, uint32_t size, uc_emu_stop(uc); } +static void clear_deleted_hooks(uc_engine *uc) +{ + struct list_item * cur; + struct hook * hook; + int i; + + for (cur = uc->hooks_to_del.head; cur != NULL && (hook = (struct hook *)cur->data); cur = cur->next) + { + assert(hook->to_delete); + for (i = 0; i < UC_HOOK_MAX; i++) { + if (list_remove(&uc->hook[i], (void *)hook)) { + if (--hook->refs == 0) { + free(hook); + } + // a hook cannot be twice in the same list + break; + } + } + } + list_clear(&uc->hooks_to_del); +} + UNICORN_EXPORT uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count) { @@ -631,6 +653,12 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time // emulation is done uc->emulation_done = true; + + + // remove hooks to delete + clear_deleted_hooks(uc); + + if (timeout) { // wait for the timer to finish qemu_thread_join(&uc->timer); @@ -1084,6 +1112,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, hook->callback = callback; hook->user_data = user_data; hook->refs = 0; + hook->to_delete = false; *hh = (uc_hook)hook; // UC_HOOK_INSN has an extra argument for instruction ID @@ -1151,24 +1180,26 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, return ret; } + UNICORN_EXPORT uc_err uc_hook_del(uc_engine *uc, uc_hook hh) { int i; struct hook *hook = (struct hook *)hh; + // we can't dereference hook->type if hook is invalid // so for now we need to iterate over all possible types to remove the hook // which is less efficient // an optimization would be to align the hook pointer // and store the type mask in the hook pointer. for (i = 0; i < UC_HOOK_MAX; i++) { - if (list_remove(&uc->hook[i], (void *)hook)) { - if (--hook->refs == 0) { - free(hook); - break; - } + if (list_exists(&uc->hook[i], (void *) hook)) + { + hook->to_delete = true; + list_append(&uc->hooks_to_del, hook); } } + return UC_ERR_OK; } @@ -1177,7 +1208,7 @@ void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t address) { struct uc_struct *uc = handle; - struct list_item *cur = uc->hook[type].head; + struct list_item *cur; struct hook *hook; // sync PC in CPUArchState with address @@ -1185,12 +1216,12 @@ void helper_uc_tracecode(int32_t size, uc_hook_type type, void *handle, int64_t uc->set_pc(uc, address); } - while (cur != NULL && !uc->stop_request) { - hook = (struct hook *)cur->data; + for (cur = uc->hook[type].head; cur != NULL && (hook = (struct hook *)cur->data); cur = cur->next){ + if (hook->to_delete) + continue; if (HOOK_BOUND_CHECK(hook, (uint64_t)address)) { ((uc_cb_hookcode_t)hook->callback)(uc, address, size, hook->user_data); } - cur = cur->next; } }