Fix UAF when deleting hook while in hook callback #1127 (#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
This commit is contained in:
BAYET 2020-05-07 08:24:48 +02:00 committed by GitHub
parent f435efd4a7
commit 881e08da01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 122 additions and 11 deletions

View File

@ -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

View File

@ -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;

19
list.c
View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)))

View File

@ -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)

49
uc.c
View File

@ -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;
}
}