128 lines
2.8 KiB
C
128 lines
2.8 KiB
C
#include "exit.h"
|
|
|
|
#include "sysemu/runstate.h"
|
|
#include "cpu.h"
|
|
|
|
#ifdef CONFIG_USER_ONLY
|
|
#define THREAD_MODIFIER __thread
|
|
#else
|
|
#define THREAD_MODIFIER
|
|
#endif
|
|
|
|
struct libafl_breakpoint* libafl_qemu_breakpoints = NULL;
|
|
|
|
int libafl_qemu_set_breakpoint(target_ulong pc)
|
|
{
|
|
CPUState *cpu;
|
|
|
|
CPU_FOREACH(cpu) {
|
|
libafl_breakpoint_invalidate(cpu, pc);
|
|
}
|
|
|
|
struct libafl_breakpoint* bp = calloc(sizeof(struct libafl_breakpoint), 1);
|
|
bp->addr = pc;
|
|
bp->next = libafl_qemu_breakpoints;
|
|
libafl_qemu_breakpoints = bp;
|
|
return 1;
|
|
}
|
|
|
|
int libafl_qemu_remove_breakpoint(target_ulong pc)
|
|
{
|
|
CPUState *cpu;
|
|
int r = 0;
|
|
|
|
struct libafl_breakpoint** bp = &libafl_qemu_breakpoints;
|
|
while (*bp) {
|
|
if ((*bp)->addr == pc) {
|
|
CPU_FOREACH(cpu) {
|
|
libafl_breakpoint_invalidate(cpu, pc);
|
|
}
|
|
|
|
*bp = (*bp)->next;
|
|
r = 1;
|
|
} else {
|
|
bp = &(*bp)->next;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static THREAD_MODIFIER struct libafl_exit_reason last_exit_reason;
|
|
static THREAD_MODIFIER bool expected_exit = false;
|
|
|
|
#if defined(TARGET_ARM)
|
|
#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;
|
|
}
|