Skip to content

Commit

Permalink
Fix UAF when deleting hook while in hook callback unicorn-engine#1127 (
Browse files Browse the repository at this point in the history
…unicorn-engine#1130)

* Handle the cpu context save in a more pythonic way, so the context can be serialized and reuse in an other process using the same emulator architecture and modes

* Fix type error ; mistakes a size_t uint64_t ; breaks in 32bit...

* Fix the UAF situation when deleting a hook while being in a hook callback. Added an attribute 'to_delete' to hooks, and a list hooks_to_del to delay the free of the hooks

* Minor fixes ; forgot return type of clear_deleted_hooks ; do not declare variable in for predicate
  • Loading branch information
cbayet authored May 7, 2020
1 parent f435efd commit 881e08d
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 11 deletions.
3 changes: 3 additions & 0 deletions include/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions include/uc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions list.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 4 additions & 0 deletions qemu/cpu-exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
12 changes: 12 additions & 0 deletions qemu/ioport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
40 changes: 38 additions & 2 deletions qemu/softmmu_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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)))
Expand All @@ -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)))
Expand All @@ -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);
Expand All @@ -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)))
Expand Down Expand Up @@ -399,6 +409,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
// 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);
Expand Down Expand Up @@ -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)))
Expand All @@ -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)))
Expand All @@ -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)))
Expand All @@ -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);
Expand All @@ -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)))
Expand Down Expand Up @@ -625,6 +647,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx,
// 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);
Expand Down Expand Up @@ -697,15 +721,19 @@ 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);
}

// Unicorn: callback on invalid memory
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)))
Expand All @@ -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)))
Expand Down Expand Up @@ -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);
Expand All @@ -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)))
Expand All @@ -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)))
Expand Down
4 changes: 4 additions & 0 deletions qemu/target-i386/seg_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 881e08d

Please sign in to comment.