#include "exit.h" #include "sysemu/runstate.h" #include "cpu.h" #ifdef CONFIG_USER_ONLY #define THREAD_MODIFIER __thread #else #define THREAD_MODIFIER #endif static THREAD_MODIFIER struct libafl_exit_reason last_exit_reason; static THREAD_MODIFIER bool expected_exit = false; #if defined(TARGET_ARM) && !defined(TARGET_AARCH64) #define THUMB_MASK(cpu, value) (value | cpu_env(cpu)->thumb) #else #define THUMB_MASK(cpu, value) value #endif // called before exiting the cpu exec with the custom exception 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.cpu, last_exit_reason.next_pc)); } last_exit_reason.next_pc = 0; } bool libafl_exit_asap(void) { return expected_exit; } static void prepare_qemu_exit(CPUState* cpu, target_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; // TODO check if still needed #endif // in usermode, this may be called from the syscall hook, thus already out of the cpu_exec but still in the cpu_loop if (cpu->running) { cpu->exception_index = EXCP_LIBAFL_EXIT; cpu_loop_exit(cpu); } } 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; }