From 466658fc5298b5cdef644d0426edd5f108d6e0fa Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 21 Nov 2023 10:48:27 +0100 Subject: [PATCH] Sync Exit: - Now the VM can trigger a synchronous backdoor stopping the VM and returning to LibAFL. - LibAFL will exit with a corresponding exit reason to perform actions accordingly (checkout the LibAFL patch for more details). - The breakpoint mechanism has been merged with this system (not tested yet, may not work out of the box). - The main difference with the backdoor is that it will always stop the VM. --- accel/tcg/cpu-exec.c | 8 +-- accel/tcg/tcg-runtime.c | 55 +++++---------------- accel/tcg/tcg-runtime.h | 5 +- accel/tcg/translator.c | 7 +++ cpu-target.c | 9 +--- libafl_extras/exit.c | 93 +++++++++++++++++++++++++++++++++++ libafl_extras/exit.h | 38 ++++++++++++++ linux-user/aarch64/cpu_loop.c | 4 +- linux-user/arm/cpu_loop.c | 4 +- linux-user/hexagon/cpu_loop.c | 4 +- linux-user/i386/cpu_loop.c | 6 +-- linux-user/mips/cpu_loop.c | 6 +-- linux-user/ppc/cpu_loop.c | 4 +- system/cpus.c | 5 +- 14 files changed, 177 insertions(+), 71 deletions(-) create mode 100644 libafl_extras/exit.c create mode 100644 libafl_extras/exit.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 9ad3aec386..8718a0d246 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -715,7 +715,7 @@ static inline void cpu_handle_debug_exception(CPUState *cpu) //// --- Begin LibAFL code --- -void libafl_sync_breakpoint_cpu(void); +void libafl_sync_exit_cpu(void); //// --- End LibAFL code --- @@ -723,13 +723,13 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) { //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - if (cpu->exception_index == EXCP_LIBAFL_BP) { + if (cpu->exception_index == EXCP_LIBAFL_EXIT) { *ret = cpu->exception_index; cpu->exception_index = -1; - libafl_sync_breakpoint_cpu(); + libafl_sync_exit_cpu(); return true; } diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 5daa5e7a6b..2f1bfc51d9 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -49,6 +49,8 @@ #include #include +#include "libafl_extras/exit.h" + void libafl_save_qemu_snapshot(char *name, bool sync); void libafl_load_qemu_snapshot(char *name, bool sync); @@ -130,57 +132,24 @@ void libafl_load_qemu_snapshot(char *name, bool sync) #endif -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 #ifdef CONFIG_USER_ONLY -__thread int libafl_qemu_break_asap = 0; -__thread CPUState* libafl_breakpoint_cpu; -__thread vaddr libafl_breakpoint_pc; +extern __thread int libafl_qemu_break_asap; #else -int libafl_qemu_break_asap = 0; -CPUState* libafl_breakpoint_cpu; -vaddr libafl_breakpoint_pc; +extern int libafl_qemu_break_asap; #endif -#ifdef TARGET_ARM -#define THUMB_MASK(value) (value | cpu_env(libafl_breakpoint_cpu)->thumb) -#else -#define THUMB_MASK(value) value -#endif - -void libafl_qemu_trigger_breakpoint(CPUState* cpu); - -void libafl_sync_breakpoint_cpu(void); - -void libafl_sync_breakpoint_cpu(void) -{ - if (libafl_breakpoint_pc) { - CPUClass* cc = CPU_GET_CLASS(libafl_breakpoint_cpu); - cc->set_pc(libafl_breakpoint_cpu, THUMB_MASK(libafl_breakpoint_pc)); - } - libafl_breakpoint_pc = 0; -} - -void libafl_qemu_trigger_breakpoint(CPUState* cpu) -{ - libafl_breakpoint_cpu = cpu; -#ifndef CONFIG_USER_ONLY - qemu_system_debug_request(); - cpu->stopped = true; -#endif - if (cpu->running) { - cpu->exception_index = EXCP_LIBAFL_BP; - cpu_loop_exit(cpu); - } else { - libafl_qemu_break_asap = 1;//TODO add a field to CPU - } -} - void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env, uint64_t pc) { CPUState* cpu = env_cpu(env); - libafl_breakpoint_pc = (target_ulong)pc; - libafl_qemu_trigger_breakpoint(cpu); + libafl_exit_request_breakpoint(cpu, (target_ulong) pc); +} + +void HELPER(libafl_qemu_handle_sync_backdoor)(CPUArchState *env, uint64_t pc) +{ + CPUState* cpu = env_cpu(env); + libafl_exit_request_sync_backdoor(cpu, (target_ulong) pc); } //// --- End LibAFL code --- diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 18fbaa82ed..27668b6175 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -327,6 +327,9 @@ DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) //// --- Begin LibAFL code --- DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, - void, env, i64) + void, env, i64) + +DEF_HELPER_FLAGS_2(libafl_qemu_handle_sync_backdoor, TCG_CALL_NO_RWG, + void, env, i64) //// --- End LibAFL code --- diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 6bc49f5064..2df93534c5 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -278,6 +278,13 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->pc_next += 4; goto post_translate_insn; + } else if (backdoor == 0x66) { + // First update pc_next to restart at next instruction + db->pc_next += 4; + + TCGv_i64 tmp0 = tcg_constant_i64((uint64_t)db->pc_next); + gen_helper_libafl_qemu_handle_sync_backdoor(tcg_env, tmp0); + tcg_temp_free_i64(tmp0); } } } diff --git a/cpu-target.c b/cpu-target.c index cc2d84b4a2..7f1f95f1ca 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -49,6 +49,7 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-internal.h" #include "exec/helper-head.h" +#include "libafl_extras/exit.h" #define LIBAFL_TABLES_SIZE 16384 #define LIBAFL_TABLES_HASH(p) (((13*((size_t)(p))) ^ (((size_t)(p)) >> 15)) % LIBAFL_TABLES_SIZE) @@ -96,12 +97,6 @@ int libafl_qemu_remove_hook(size_t num, int invalidate); struct libafl_hook* libafl_search_hook(target_ulong addr); void libafl_flush_jit(void); -#ifdef CONFIG_USER_ONLY -extern __thread CPUState* libafl_breakpoint_cpu; -#else -extern CPUState* libafl_breakpoint_cpu; -#endif - extern int libafl_restoring_devices; /* @@ -147,7 +142,7 @@ CPUState* libafl_qemu_current_cpu(void) { #ifndef CONFIG_USER_ONLY if (current_cpu == NULL) { - return libafl_breakpoint_cpu; + return libafl_last_exit_cpu(); } #endif return current_cpu; diff --git a/libafl_extras/exit.c b/libafl_extras/exit.c new file mode 100644 index 0000000000..4008023b81 --- /dev/null +++ b/libafl_extras/exit.c @@ -0,0 +1,93 @@ +#include "exit.h" + +#include "sysemu/runstate.h" + +// TODO: merge with definition in tcg-runtime.c +#define EXCP_LIBAFL_EXIT 0xf4775747 + +#ifdef CONFIG_USER_ONLY +__thread int libafl_qemu_break_asap = 0; +__thread CPUState* libafl_breakpoint_cpu; +__thread vaddr libafl_breakpoint_pc; +__thread static struct libafl_exit_reason last_exit_reason; +#else +static struct libafl_exit_reason last_exit_reason; +#endif + +#ifdef TARGET_ARM +#define THUMB_MASK(value) (value | cpu_env(libafl_breakpoint_cpu)->thumb) +#else +#define THUMB_MASK(value) value +#endif + +static bool expected_exit = false; + +void libafl_sync_exit_cpu(void) +{ + if (last_exit_reason.next_pc) { + CPUClass* cc = CPU_GET_CLASS(last_exit_reason.cpu); + cc->set_pc(last_exit_reason.cpu, THUMB_MASK(last_exit_reason.next_pc)); + } + last_exit_reason.next_pc = 0; +} + +bool libafl_exit_asap(void){ + return last_exit_reason.exit_asap; +} + +static void prepare_qemu_exit(CPUState* cpu, ulong next_pc) +{ + expected_exit = true; + last_exit_reason.cpu = cpu; + last_exit_reason.next_pc = next_pc; + +#ifndef CONFIG_USER_ONLY + qemu_system_debug_request(); + cpu->stopped = true; +#endif + if (cpu->running) { + cpu->exception_index = EXCP_LIBAFL_EXIT; + cpu_loop_exit(cpu); + } else { + last_exit_reason.exit_asap = 1; + } +} + +CPUState* libafl_last_exit_cpu(void) +{ + if (expected_exit) { + return last_exit_reason.cpu; + } + + return NULL; +} + +void libafl_exit_request_sync_backdoor(CPUState* cpu, target_ulong pc) +{ + last_exit_reason.kind = SYNC_BACKDOOR; + + prepare_qemu_exit(cpu, pc); +} + +void libafl_exit_request_breakpoint(CPUState* cpu, target_ulong pc) +{ + last_exit_reason.kind = BREAKPOINT; + last_exit_reason.data.breakpoint.addr = pc; + + prepare_qemu_exit(cpu, pc); +} + +void libafl_exit_signal_vm_start(void) +{ + last_exit_reason.cpu = NULL; + expected_exit = false; +} + +struct libafl_exit_reason* libafl_get_exit_reason(void) +{ + if (expected_exit) { + return &last_exit_reason; + } + + return NULL; +} diff --git a/libafl_extras/exit.h b/libafl_extras/exit.h new file mode 100644 index 0000000000..bcf9ae4e60 --- /dev/null +++ b/libafl_extras/exit.h @@ -0,0 +1,38 @@ +#pragma once + +#include "qemu/osdep.h" +#include "exec/cpu-defs.h" + +enum libafl_exit_reason_kind { + BREAKPOINT = 0, + SYNC_BACKDOOR = 1 +}; + +struct libafl_exit_reason_breakpoint { + target_ulong addr; +}; + +struct libafl_exit_reason_sync_backdoor { }; + +struct libafl_exit_reason { + enum libafl_exit_reason_kind kind; + CPUState* cpu; // CPU that triggered an exit. + vaddr next_pc; // The PC that should be stored in the CPU when re-entering. + int exit_asap; // TODO: add a field to CPU + union { + struct libafl_exit_reason_breakpoint breakpoint; // kind == BREAKPOINT + struct libafl_exit_reason_sync_backdoor backdoor; // kind == SYNC_BACKDOOR + } data; +}; + +// Only makes sense to call if an exit was expected +// Will return NULL if there was no exit expected. +CPUState* libafl_last_exit_cpu(void); + +void libafl_exit_signal_vm_start(void); +bool libafl_exit_asap(void); +void libafl_sync_exit_cpu(void); + +void libafl_exit_request_sync_backdoor(CPUState* cpu, target_ulong pc); +void libafl_exit_request_breakpoint(CPUState* cpu, target_ulong pc); +struct libafl_exit_reason* libafl_get_exit_reason(void); diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 9980088efa..7272a0a2b2 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -98,9 +98,9 @@ void cpu_loop(CPUARMState *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index e429cfc0c9..812317d71b 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -340,9 +340,9 @@ void cpu_loop(CPUARMState *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index d97df6cc07..0fe37e1865 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -49,9 +49,9 @@ void cpu_loop(CPUHexagonState *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index ab8461a65e..4a5578c59a 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -213,7 +213,7 @@ void cpu_loop(CPUX86State *env) //// --- Begin LibAFL code --- - if (libafl_qemu_break_asap) return; + if (libafl_exit_asap()) return; //// --- End LibAFL code --- @@ -226,9 +226,9 @@ void cpu_loop(CPUX86State *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 47e617789e..0f32413402 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -74,7 +74,7 @@ void cpu_loop(CPUMIPSState *env) //// --- Begin LibAFL code --- - if (libafl_qemu_break_asap) return; + if (libafl_exit_asap()) return; //// --- End LibAFL code --- @@ -87,9 +87,9 @@ void cpu_loop(CPUMIPSState *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 847b5ceddb..a9cd9c5d42 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -84,9 +84,9 @@ void cpu_loop(CPUPPCState *env) //// --- Begin LibAFL code --- -#define EXCP_LIBAFL_BP 0xf4775747 +#define EXCP_LIBAFL_EXIT 0xf4775747 - case EXCP_LIBAFL_BP: + case EXCP_LIBAFL_EXIT: return; //// --- End LibAFL code --- diff --git a/system/cpus.c b/system/cpus.c index 556129385e..eeec5a33f3 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -714,7 +714,8 @@ int vm_prepare_start(bool step_pending) //// --- Begin LibAFL code --- -extern CPUState* libafl_breakpoint_cpu; +void libafl_exit_signal_vm_start(void); + //// --- End LibAFL code --- @@ -723,7 +724,7 @@ void vm_start(void) //// --- Begin LibAFL code --- - libafl_breakpoint_cpu = NULL; // Rely on current_cpu in the hooks + libafl_exit_signal_vm_start(); //// --- End LibAFL code ---