Add pre/post cpu_run hooks + Refactoring (#82)

* Add cpu_run pre/post exec hooks, to hook the cpu just before / after it runs target code.
* Refactor hooks to separate them in different files
* Remove most extern variables
* Reduce the amount of extra code in QEMU
* Add clang-format script
This commit is contained in:
Romain Malmain 2024-08-13 16:56:00 +02:00 committed by GitHub
parent 11b27cc216
commit 86d38fbfa7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 2143 additions and 1494 deletions

View File

@ -52,6 +52,12 @@
#include "hw/boards.h" #include "hw/boards.h"
#include "sysemu/stats.h" #include "sysemu/stats.h"
//// --- Begin LibAFL code ---
#include "libafl/hooks/cpu_run.h"
//// --- End LibAFL code ---
/* This check must be after config-host.h is included */ /* This check must be after config-host.h is included */
#ifdef CONFIG_EVENTFD #ifdef CONFIG_EVENTFD
#include <sys/eventfd.h> #include <sys/eventfd.h>
@ -2866,10 +2872,22 @@ int kvm_cpu_exec(CPUState *cpu)
*/ */
smp_rmb(); smp_rmb();
//// --- Begin LibAFL code ---
libafl_hook_cpu_run_pre_exec(cpu);
//// --- End LibAFL code ---
run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
attrs = kvm_arch_post_run(cpu, run); attrs = kvm_arch_post_run(cpu, run);
//// --- Begin LibAFL code ---
libafl_hook_cpu_run_post_exec(cpu);
//// --- End LibAFL code ---
#ifdef KVM_HAVE_MCE_INJECTION #ifdef KVM_HAVE_MCE_INJECTION
if (unlikely(have_sigbus_pending)) { if (unlikely(have_sigbus_pending)) {
bql_lock(); bql_lock();

View File

@ -68,7 +68,8 @@
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
#include "libafl/hook.h" #include "libafl/hooks/tcg/block.h"
#include "libafl/hooks/tcg/edge.h"
//// --- End LibAFL code --- //// --- End LibAFL code ---
@ -284,24 +285,7 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
struct libafl_block_hook* hook = libafl_block_hooks; libafl_qemu_hook_block_run(pc);
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, pc);
if (cur_id != (uint64_t)-1 && hook->helper_info.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) };
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
if (cur_id != (uint64_t)-1 && hook->jit) {
hook->jit(hook->data, cur_id);
}
hook = hook->next;
}
//// --- End LibAFL code --- //// --- End LibAFL code ---
@ -361,21 +345,9 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
#error Unhandled TARGET_INSN_START_EXTRA_WORDS value #error Unhandled TARGET_INSN_START_EXTRA_WORDS value
#endif #endif
struct libafl_edge_hook* hook = libafl_edge_hooks; // run edge hooks
while (hook) { libafl_qemu_hook_edge_run();
if (hook->cur_id != (uint64_t)-1 && hook->helper_info.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(hook->cur_id);
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) };
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
if (hook->cur_id != (uint64_t)-1 && hook->jit) {
hook->jit(hook->data, hook->cur_id);
}
hook = hook->next;
}
tcg_gen_goto_tb(0); tcg_gen_goto_tb(0);
tcg_gen_exit_tb(tb, 0); tcg_gen_exit_tb(tb, 0);
@ -430,16 +402,7 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS);
// edge hooks generation callbacks // edge hooks generation callbacks
struct libafl_edge_hook* hook = libafl_edge_hooks; bool no_exec_hook = libafl_qemu_hook_edge_gen(src_block, dst_block);
int no_exec_hook = 1;
while (hook) {
hook->cur_id = 0;
if (hook->gen)
hook->cur_id = hook->gen(hook->data, src_block, dst_block);
if (hook->cur_id != (uint64_t)-1 && (hook->helper_info.func || hook->jit))
no_exec_hook = 0;
hook = hook->next;
}
if (no_exec_hook) if (no_exec_hook)
return NULL; return NULL;
@ -749,13 +712,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb->tc.size = gen_code_size; tb->tc.size = gen_code_size;
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
struct libafl_block_hook *hook = libafl_block_hooks;
while (hook) libafl_qemu_hook_block_post_gen(tb, pc);
{
if (hook->post_gen)
hook->post_gen(hook->data, pc, tb->size);
hook = hook->next;
}
//// --- End LibAFL code --- //// --- End LibAFL code ---
/* /*

View File

@ -104,6 +104,9 @@ static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags,
#include "libafl/exit.h" #include "libafl/exit.h"
#include "libafl/hook.h" #include "libafl/hook.h"
#include "libafl/hooks/tcg/instruction.h"
#include "libafl/hooks/tcg/backdoor.h"
#ifndef TARGET_LONG_BITS #ifndef TARGET_LONG_BITS
#error "TARGET_LONG_BITS not defined" #error "TARGET_LONG_BITS not defined"
#endif #endif
@ -168,37 +171,10 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
struct libafl_hook* hk = libafl_search_instruction_hook(db->pc_next); libafl_qemu_hook_instruction_run(db->pc_next);
if (hk) {
TCGv_i64 tmp0 = tcg_constant_i64(hk->data);
#if TARGET_LONG_BITS == 32
TCGv_i32 tmp1 = tcg_constant_i32(db->pc_next);
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i32_temp(tmp1) };
#else
TCGv_i64 tmp1 = tcg_constant_i64(db->pc_next);
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) };
#endif
// tcg_gen_callN(hk->callback, NULL, 2, tmp2);
tcg_gen_callN(&hk->helper_info, NULL, tmp2);
#if TARGET_LONG_BITS == 32
tcg_temp_free_i32(tmp1);
#else
tcg_temp_free_i64(tmp1);
#endif
tcg_temp_free_i64(tmp0);
}
struct libafl_breakpoint* bp = libafl_qemu_breakpoints;
while (bp) {
if (bp->addr == db->pc_next) {
TCGv_i64 tmp0 = tcg_constant_i64((uint64_t)db->pc_next);
gen_helper_libafl_qemu_handle_breakpoint(tcg_env, tmp0);
tcg_temp_free_i64(tmp0);
}
bp = bp->next;
}
libafl_gen_cur_pc = db->pc_next; libafl_gen_cur_pc = db->pc_next;
libafl_qemu_breakpoint_run(libafl_gen_cur_pc);
// 0x0f, 0x3a, 0xf2, 0x44 // 0x0f, 0x3a, 0xf2, 0x44
uint8_t backdoor = translator_ldub(cpu_env(cpu), db, db->pc_next); uint8_t backdoor = translator_ldub(cpu_env(cpu), db, db->pc_next);
@ -209,16 +185,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
if (backdoor == 0xf2) { if (backdoor == 0xf2) {
backdoor = translator_ldub(cpu_env(cpu), db, db->pc_next +3); backdoor = translator_ldub(cpu_env(cpu), db, db->pc_next +3);
if (backdoor == 0x44) { if (backdoor == 0x44) {
struct libafl_backdoor_hook* bhk = libafl_backdoor_hooks; libafl_qemu_hook_backdoor_run(db->pc_next);
while (bhk) {
TCGv_i64 tmp0 = tcg_constant_i64(bhk->data);
TCGv tmp2 = tcg_constant_tl(db->pc_next);
TCGTemp *args[3] = { tcgv_i64_temp(tmp0), tcgv_ptr_temp(tcg_env), tcgv_tl_temp(tmp2) };
tcg_gen_callN(&bhk->helper_info, NULL, args);
bhk = bhk->next;
}
db->pc_next += 4; db->pc_next += 4;
goto post_translate_insn; goto post_translate_insn;

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "exec/cpu-defs.h" #include "exec/cpu-defs.h"
#include "exec/translator.h"
#define EXCP_LIBAFL_EXIT 0xf4775747 #define EXCP_LIBAFL_EXIT 0xf4775747
@ -10,14 +12,13 @@ struct libafl_breakpoint {
struct libafl_breakpoint* next; struct libafl_breakpoint* next;
}; };
extern struct libafl_breakpoint* libafl_qemu_breakpoints;
// in cpu-target.c // in cpu-target.c
void libafl_breakpoint_invalidate(CPUState *cpu, target_ulong pc); void libafl_breakpoint_invalidate(CPUState* cpu, target_ulong pc);
int libafl_qemu_set_breakpoint(target_ulong pc); int libafl_qemu_set_breakpoint(target_ulong pc);
int libafl_qemu_remove_breakpoint(target_ulong pc); int libafl_qemu_remove_breakpoint(target_ulong pc);
void libafl_qemu_trigger_breakpoint(CPUState* cpu); void libafl_qemu_trigger_breakpoint(CPUState* cpu);
void libafl_qemu_breakpoint_run(vaddr pc_next);
enum libafl_exit_reason_kind { enum libafl_exit_reason_kind {
INTERNAL = 0, INTERNAL = 0,
@ -31,7 +32,8 @@ struct libafl_exit_reason_breakpoint {
}; };
// A synchronous exit has been triggered. // A synchronous exit has been triggered.
struct libafl_exit_reason_sync_exit { }; struct libafl_exit_reason_sync_exit {
};
// QEMU exited on its own for some reason. // QEMU exited on its own for some reason.
struct libafl_exit_reason_internal { struct libafl_exit_reason_internal {
@ -46,7 +48,7 @@ struct libafl_exit_reason {
union { union {
struct libafl_exit_reason_internal internal; struct libafl_exit_reason_internal internal;
struct libafl_exit_reason_breakpoint breakpoint; // kind == BREAKPOINT struct libafl_exit_reason_breakpoint breakpoint; // kind == BREAKPOINT
struct libafl_exit_reason_sync_exit sync_exit; // kind == SYNC_EXIT struct libafl_exit_reason_sync_exit sync_exit; // kind == SYNC_EXIT
} data; } data;
}; };
@ -58,7 +60,8 @@ void libafl_exit_signal_vm_start(void);
bool libafl_exit_asap(void); bool libafl_exit_asap(void);
void libafl_sync_exit_cpu(void); void libafl_sync_exit_cpu(void);
void libafl_exit_request_internal(CPUState* cpu, uint64_t pc, ShutdownCause cause, int signal); void libafl_exit_request_internal(CPUState* cpu, uint64_t pc,
ShutdownCause cause, int signal);
void libafl_exit_request_sync_backdoor(CPUState* cpu, target_ulong pc); void libafl_exit_request_sync_backdoor(CPUState* cpu, target_ulong pc);
void libafl_exit_request_breakpoint(CPUState* cpu, target_ulong pc); void libafl_exit_request_breakpoint(CPUState* cpu, target_ulong pc);
struct libafl_exit_reason* libafl_get_exit_reason(void); struct libafl_exit_reason* libafl_get_exit_reason(void);

View File

@ -7,221 +7,55 @@
#include "tcg/tcg-internal.h" #include "tcg/tcg-internal.h"
#include "tcg/tcg-temp-internal.h" #include "tcg/tcg-temp-internal.h"
#define LIBAFL_TABLES_SIZE 16384
#define LIBAFL_TABLES_HASH(p) (((13*((size_t)(p))) ^ (((size_t)(p)) >> 15)) % LIBAFL_TABLES_SIZE)
#define LIBAFL_MAX_INSNS 16 #define LIBAFL_MAX_INSNS 16
void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args); #define GEN_REMOVE_HOOK(name) \
int libafl_qemu_remove_##name##_hook(size_t num, int invalidate) \
{ \
CPUState* cpu; \
struct libafl_##name##_hook** hk = &libafl_##name##_hooks; \
\
while (*hk) { \
if ((*hk)->num == num) { \
if (invalidate) { \
CPU_FOREACH(cpu) { tb_flush(cpu); } \
} \
\
void* tmp = *hk; \
*hk = (*hk)->next; \
free(tmp); \
return 1; \
} else { \
hk = &(*hk)->next; \
} \
} \
\
return 0; \
}
TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, #define GEN_REMOVE_HOOK1(name) \
target_ulong dst_block, int exit_n, int libafl_qemu_remove_##name##_hook(size_t num) \
target_ulong cs_base, uint32_t flags, { \
int cflags); struct libafl_##name##_hook** hk = &libafl_##name##_hooks; \
\
void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot); while (*hk) { \
void libafl_gen_backdoor(target_ulong pc); if ((*hk)->num == num) { \
void* tmp = *hk; \
*hk = (*hk)->next; \
free(tmp); \
return 1; \
} else { \
hk = &(*hk)->next; \
} \
} \
\
return 0; \
}
// TODO: cleanup this
extern target_ulong libafl_gen_cur_pc; extern target_ulong libafl_gen_cur_pc;
struct libafl_hook {
target_ulong addr;
// void (*callback)(uint64_t, target_ulong);
uint64_t data;
size_t num;
TCGHelperInfo helper_info;
struct libafl_hook* next;
};
extern struct libafl_hook* libafl_qemu_instruction_hooks[LIBAFL_TABLES_SIZE];
extern size_t libafl_qemu_hooks_num; extern size_t libafl_qemu_hooks_num;
size_t libafl_qemu_add_instruction_hooks(target_ulong pc, void (*callback)(uint64_t data, target_ulong pc), void tcg_gen_callN(TCGHelperInfo* info, TCGTemp* ret, TCGTemp** args);
uint64_t data, int invalidate);
size_t libafl_qemu_remove_instruction_hooks_at(target_ulong addr, int invalidate);
int libafl_qemu_remove_instruction_hook(size_t num, int invalidate);
struct libafl_hook* libafl_search_instruction_hook(target_ulong addr);
struct libafl_backdoor_hook { void libafl_tcg_gen_asan(TCGTemp* addr, size_t size);
void (*exec)(uint64_t data, CPUArchState* cpu, target_ulong pc);
uint64_t data;
size_t num;
TCGHelperInfo helper_info;
struct libafl_backdoor_hook* next;
};
extern struct libafl_backdoor_hook* libafl_backdoor_hooks;
size_t libafl_add_backdoor_hook(void (*exec)(uint64_t data, CPUArchState* cpu, target_ulong pc),
uint64_t data);
int libafl_qemu_remove_backdoor_hook(size_t num, int invalidate);
struct libafl_edge_hook {
uint64_t (*gen)(uint64_t data, target_ulong src, target_ulong dst);
// void (*exec)(uint64_t data, uint64_t id);
size_t (*jit)(uint64_t data, uint64_t id); // optional opt
uint64_t data;
size_t num;
uint64_t cur_id;
TCGHelperInfo helper_info;
struct libafl_edge_hook* next;
};
extern struct libafl_edge_hook* libafl_edge_hooks;
size_t libafl_add_edge_hook(uint64_t (*gen)(uint64_t data, target_ulong src, target_ulong dst),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data);
int libafl_qemu_remove_edge_hook(size_t num, int invalidate);
bool libafl_qemu_edge_hook_set_jit(size_t num, size_t (*jit)(uint64_t, uint64_t)); // no param names to avoid to be marked as safe
struct libafl_block_hook {
uint64_t (*gen)(uint64_t data, target_ulong pc);
void (*post_gen)(uint64_t data, target_ulong pc, target_ulong block_length);
// void (*exec)(uint64_t data, uint64_t id);
size_t (*jit)(uint64_t data, uint64_t id); // optional opt
uint64_t data;
size_t num;
TCGHelperInfo helper_info;
struct libafl_block_hook* next;
};
extern struct libafl_block_hook* libafl_block_hooks;
size_t libafl_add_block_hook(uint64_t (*gen)(uint64_t data, target_ulong pc),
void (*post_gen)(uint64_t data, target_ulong pc, target_ulong block_length),
void (*exec)(uint64_t data, uint64_t id), uint64_t data);
int libafl_qemu_remove_block_hook(size_t num, int invalidate);
bool libafl_qemu_block_hook_set_jit(size_t num, size_t (*jit)(uint64_t, uint64_t)); // no param names to avoid to be marked as safe
struct libafl_rw_hook {
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi);
/*void (*exec1)(uint64_t data, uint64_t id, target_ulong addr);
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr);
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr);
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr);
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size);*/
uint64_t data;
size_t num;
TCGHelperInfo helper_info1;
TCGHelperInfo helper_info2;
TCGHelperInfo helper_info4;
TCGHelperInfo helper_info8;
TCGHelperInfo helper_infoN;
struct libafl_rw_hook* next;
};
// alias
#define libafl_read_hook libafl_rw_hook
#define libafl_write_hook libafl_rw_hook
extern struct libafl_rw_hook* libafl_read_hooks;
extern struct libafl_rw_hook* libafl_write_hooks;
size_t libafl_add_read_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp *addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data);
size_t libafl_add_write_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp *addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data);
int libafl_qemu_remove_read_hook(size_t num, int invalidate);
int libafl_qemu_remove_write_hook(size_t num, int invalidate);
void libafl_gen_read(TCGTemp *addr, MemOpIdx oi);
void libafl_gen_write(TCGTemp *addr, MemOpIdx oi);
struct libafl_cmp_hook {
uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size);
/*void (*exec1)(uint64_t data, uint64_t id, uint8_t v0, uint8_t v1);
void (*exec2)(uint64_t data, uint64_t id, uint16_t v0, uint16_t v1);
void (*exec4)(uint64_t data, uint64_t id, uint32_t v0, uint32_t v1);
void (*exec8)(uint64_t data, uint64_t id, uint64_t v0, uint64_t v1);*/
uint64_t data;
size_t num;
TCGHelperInfo helper_info1;
TCGHelperInfo helper_info2;
TCGHelperInfo helper_info4;
TCGHelperInfo helper_info8;
struct libafl_cmp_hook* next;
};
extern struct libafl_cmp_hook* libafl_cmp_hooks;
size_t libafl_add_cmp_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size),
void (*exec1)(uint64_t data, uint64_t id, uint8_t v0, uint8_t v1),
void (*exec2)(uint64_t data, uint64_t id, uint16_t v0, uint16_t v1),
void (*exec4)(uint64_t data, uint64_t id, uint32_t v0, uint32_t v1),
void (*exec8)(uint64_t data, uint64_t id, uint64_t v0, uint64_t v1),
uint64_t data);
int libafl_qemu_remove_cmp_hook(size_t num, int invalidate);
struct syshook_ret {
target_ulong retval;
bool skip_syscall;
};
struct libafl_pre_syscall_hook {
struct syshook_ret (*callback)(uint64_t data, int sys_num, target_ulong arg0,
target_ulong arg1, target_ulong arg2,
target_ulong arg3, target_ulong arg4,
target_ulong arg5, target_ulong arg6,
target_ulong arg7);
uint64_t data;
size_t num;
struct libafl_pre_syscall_hook* next;
};
struct libafl_post_syscall_hook {
target_ulong (*callback)(uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7);
uint64_t data;
size_t num;
struct libafl_post_syscall_hook* next;
};
extern struct libafl_pre_syscall_hook* libafl_pre_syscall_hooks;
extern struct libafl_post_syscall_hook* libafl_post_syscall_hooks;
size_t libafl_add_pre_syscall_hook(struct syshook_ret (*callback)(
uint64_t data, int sys_num, target_ulong arg0,
target_ulong arg1, target_ulong arg2,
target_ulong arg3, target_ulong arg4,
target_ulong arg5, target_ulong arg6,
target_ulong arg7),
uint64_t data);
size_t libafl_add_post_syscall_hook(target_ulong (*callback)(
uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data);
int libafl_qemu_remove_pre_syscall_hook(size_t num);
int libafl_qemu_remove_post_syscall_hook(size_t num);
struct libafl_new_thread_hook {
bool (*callback)(uint64_t data, uint32_t tid);
uint64_t data;
size_t num;
struct libafl_new_thread_hook* next;
};
extern struct libafl_new_thread_hook* libafl_new_thread_hooks;
size_t libafl_add_new_thread_hook(bool (*callback)(uint64_t data, uint32_t tid),
uint64_t data);
int libafl_qemu_remove_new_thread_hook(size_t num);
void libafl_tcg_gen_asan(TCGTemp * addr, size_t size);

View File

@ -0,0 +1,36 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
typedef void (*libafl_cpu_run_fn)(uint64_t data, CPUState* cpu);
struct libafl_cpu_run_hook {
// functions
libafl_cpu_run_fn pre_cpu_run;
libafl_cpu_run_fn post_cpu_run;
// data
uint64_t data;
size_t num;
// next
struct libafl_cpu_run_hook* next;
};
size_t libafl_hook_cpu_run_add(libafl_cpu_run_fn pre_cpu_run,
libafl_cpu_run_fn post_cpu_run, uint64_t data);
int libafl_hook_cpu_run_remove(size_t num);
int libafl_qemu_remove_cpu_run_hook(size_t num);
void libafl_hook_cpu_run_pre_exec(CPUState* cpu);
void libafl_hook_cpu_run_post_exec(CPUState* cpu);

View File

@ -0,0 +1,76 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct syshook_ret {
target_ulong retval;
bool skip_syscall;
};
struct libafl_pre_syscall_hook {
// functions
struct syshook_ret (*callback)(uint64_t data, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7);
// data
uint64_t data;
size_t num;
// next
struct libafl_pre_syscall_hook* next;
};
struct libafl_post_syscall_hook {
// functions
target_ulong (*callback)(uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7);
// data
uint64_t data;
size_t num;
// next
struct libafl_post_syscall_hook* next;
};
size_t libafl_add_pre_syscall_hook(
struct syshook_ret (*callback)(uint64_t data, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data);
size_t libafl_add_post_syscall_hook(
target_ulong (*callback)(uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data);
int libafl_qemu_remove_pre_syscall_hook(size_t num);
int libafl_qemu_remove_post_syscall_hook(size_t num);
bool libafl_hook_syscall_pre_run(CPUArchState* env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8, abi_long* ret);
void libafl_hook_syscall_post_run(int num, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5,
abi_long arg6, abi_long arg7, abi_long arg8,
abi_long* ret);

View File

@ -0,0 +1,38 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct libafl_backdoor_hook {
// functions
void (*gen)(uint64_t data, CPUArchState* cpu, target_ulong pc);
// data
uint64_t data;
size_t num;
// helpers
TCGHelperInfo helper_info;
// next
struct libafl_backdoor_hook* next;
};
extern struct libafl_backdoor_hook* libafl_backdoor_hooks;
void libafl_gen_backdoor(target_ulong pc);
size_t libafl_add_backdoor_hook(void (*exec)(uint64_t data, CPUArchState* cpu,
target_ulong pc),
uint64_t data);
int libafl_qemu_remove_backdoor_hook(size_t num, int invalidate);
void libafl_qemu_hook_backdoor_run(vaddr pc_next);

View File

@ -0,0 +1,43 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct libafl_block_hook {
// functions
uint64_t (*gen)(uint64_t data, target_ulong pc);
void (*post_gen)(uint64_t data, target_ulong pc, target_ulong block_length);
size_t (*jit)(uint64_t data, uint64_t id); // optional opt
// data
uint64_t data;
size_t num;
// helpers
TCGHelperInfo helper_info;
// next
struct libafl_block_hook* next;
};
void libafl_qemu_hook_block_post_gen(TranslationBlock* tb, vaddr pc);
void libafl_qemu_hook_block_run(target_ulong pc);
bool libafl_qemu_block_hook_set_jit(
size_t num,
size_t (*jit)(uint64_t,
uint64_t)); // no param names to avoid to be marked as safe
int libafl_qemu_remove_block_hook(size_t num, int invalidate);
size_t libafl_add_block_hook(uint64_t (*gen)(uint64_t data, target_ulong pc),
void (*post_gen)(uint64_t data, target_ulong pc,
target_ulong block_length),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data);

View File

@ -0,0 +1,39 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct libafl_cmp_hook {
// functions
uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size);
// data
uint64_t data;
size_t num;
// helpers
TCGHelperInfo helper_info1;
TCGHelperInfo helper_info2;
TCGHelperInfo helper_info4;
TCGHelperInfo helper_info8;
// next
struct libafl_cmp_hook* next;
};
void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot);
size_t libafl_add_cmp_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size),
void (*exec1)(uint64_t data, uint64_t id, uint8_t v0, uint8_t v1),
void (*exec2)(uint64_t data, uint64_t id, uint16_t v0, uint16_t v1),
void (*exec4)(uint64_t data, uint64_t id, uint32_t v0, uint32_t v1),
void (*exec8)(uint64_t data, uint64_t id, uint64_t v0, uint64_t v1),
uint64_t data);
int libafl_qemu_remove_cmp_hook(size_t num, int invalidate);

View File

@ -0,0 +1,48 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct libafl_edge_hook {
// functions
uint64_t (*gen)(uint64_t data, target_ulong src, target_ulong dst);
size_t (*jit)(uint64_t data, uint64_t id); // optional opt
// data
uint64_t data;
size_t num;
uint64_t cur_id;
// helpers
TCGHelperInfo helper_info;
// next
struct libafl_edge_hook* next;
};
TranslationBlock* libafl_gen_edge(CPUState* cpu, target_ulong src_block,
target_ulong dst_block, int exit_n,
target_ulong cs_base, uint32_t flags,
int cflags);
size_t libafl_add_edge_hook(uint64_t (*gen)(uint64_t data, target_ulong src,
target_ulong dst),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data);
bool libafl_qemu_edge_hook_set_jit(
size_t num,
size_t (*jit)(uint64_t,
uint64_t)); // no param names to avoid to be marked as safe
int libafl_qemu_remove_edge_hook(size_t num, int invalidate);
bool libafl_qemu_hook_edge_gen(target_ulong src_block, target_ulong dst_block);
void libafl_qemu_hook_edge_run(void);

View File

@ -0,0 +1,43 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
#define LIBAFL_TABLES_SIZE 16384
#define LIBAFL_TABLES_HASH(p) \
(((13 * ((size_t)(p))) ^ (((size_t)(p)) >> 15)) % LIBAFL_TABLES_SIZE)
struct libafl_instruction_hook {
// data
uint64_t data;
size_t num;
target_ulong addr;
// helpers
TCGHelperInfo helper_info;
// next
struct libafl_instruction_hook* next;
};
size_t libafl_qemu_add_instruction_hooks(target_ulong pc,
void (*callback)(uint64_t data,
target_ulong pc),
uint64_t data, int invalidate);
int libafl_qemu_remove_instruction_hook(size_t num, int invalidate);
size_t libafl_qemu_remove_instruction_hooks_at(target_ulong addr,
int invalidate);
struct libafl_instruction_hook*
libafl_search_instruction_hook(target_ulong addr);
void libafl_qemu_hook_instruction_run(vaddr pc_next);

View File

@ -0,0 +1,60 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
#define libafl_read_hook libafl_rw_hook
#define libafl_write_hook libafl_rw_hook
#define LIBAFL_TABLES_SIZE 16384
#define LIBAFL_TABLES_HASH(p) \
(((13 * ((size_t)(p))) ^ (((size_t)(p)) >> 15)) % LIBAFL_TABLES_SIZE)
struct libafl_rw_hook {
// functions
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi);
// data
uint64_t data;
size_t num;
// helpers
TCGHelperInfo helper_info1;
TCGHelperInfo helper_info2;
TCGHelperInfo helper_info4;
TCGHelperInfo helper_info8;
TCGHelperInfo helper_infoN;
// next
struct libafl_rw_hook* next;
};
void libafl_gen_read(TCGTemp* addr, MemOpIdx oi);
void libafl_gen_write(TCGTemp* addr, MemOpIdx oi);
size_t libafl_add_read_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data);
size_t libafl_add_write_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data);
int libafl_qemu_remove_read_hook(size_t num, int invalidate);
int libafl_qemu_remove_write_hook(size_t num, int invalidate);

View File

@ -0,0 +1,31 @@
#pragma once
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "libafl/exit.h"
#include "libafl/hook.h"
struct libafl_new_thread_hook {
// functions
bool (*callback)(uint64_t data, CPUArchState* cpu, uint32_t tid);
// data
uint64_t data;
size_t num;
// next
struct libafl_new_thread_hook* next;
};
size_t libafl_add_new_thread_hook(bool (*callback)(uint64_t data,
CPUArchState* env,
uint32_t tid),
uint64_t data);
int libafl_qemu_remove_new_thread_hook(size_t num);
bool libafl_hook_new_thread_run(CPUArchState* env);

View File

@ -2,14 +2,18 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#define SYX_PRINTF(format, ...) fprintf(stderr, ("[QEMU-SYX] " format), ##__VA_ARGS__) #define SYX_PRINTF(format, ...) \
fprintf(stderr, ("[QEMU-SYX] " format), ##__VA_ARGS__)
#ifdef CONFIG_DEBUG_SYX #ifdef CONFIG_DEBUG_SYX
#define SYX_DEBUG(format, ...) fprintf(stderr, ("[QEMU-SYX] DEBUG: " format), ##__VA_ARGS__) #define SYX_DEBUG(format, ...) \
fprintf(stderr, ("[QEMU-SYX] DEBUG: " format), ##__VA_ARGS__)
#else #else
#define SYX_DEBUG(format, ...) #define SYX_DEBUG(format, ...)
#endif #endif
#define SYX_WARNING(format, ...) warn_report(("[QEMU-SYX] " format), ##__VA_ARGS__) #define SYX_WARNING(format, ...) \
warn_report(("[QEMU-SYX] " format), ##__VA_ARGS__)
#define SYX_ERROR(format, ...) error_report(("[QEMU-SYX] " format), ##__VA_ARGS__) #define SYX_ERROR(format, ...) \
error_report(("[QEMU-SYX] " format), ##__VA_ARGS__)

View File

@ -1,15 +1,16 @@
#pragma once #pragma once
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "migration/qemu-file.h"
#include "migration/qemu-file.h"
#include "io/channel.h" #include "io/channel.h"
#include "qom/object.h" #include "qom/object.h"
#define QEMU_FILE_RAM_LIMIT (32 * 1024 * 1024) #define QEMU_FILE_RAM_LIMIT (32 * 1024 * 1024)
#define TYPE_QIO_CHANNEL_BUFFER_WRITEBACK "qio-channel-buffer-writeback" #define TYPE_QIO_CHANNEL_BUFFER_WRITEBACK "qio-channel-buffer-writeback"
OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelBufferWriteback, QIO_CHANNEL_BUFFER_WRITEBACK) OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelBufferWriteback,
QIO_CHANNEL_BUFFER_WRITEBACK)
struct QIOChannelBufferWriteback { struct QIOChannelBufferWriteback {
QIOChannel parent; QIOChannel parent;
@ -26,7 +27,10 @@ struct QIOChannelBufferWriteback {
bool internal_allocation; bool internal_allocation;
}; };
QIOChannelBufferWriteback* qio_channel_buffer_writeback_new(size_t capacity, uint8_t* writeback_buf, size_t writeback_buf_capacity, size_t* writeback_buf_usage); QIOChannelBufferWriteback*
qio_channel_buffer_writeback_new(size_t capacity, uint8_t* writeback_buf,
size_t writeback_buf_capacity,
size_t* writeback_buf_usage);
/** /**
* qio_channel_buffer_new_external: * qio_channel_buffer_new_external:
@ -34,5 +38,6 @@ QIOChannelBufferWriteback* qio_channel_buffer_writeback_new(size_t capacity, uin
* @capacity: the total capacity of the underlying buffer * @capacity: the total capacity of the underlying buffer
* @usage: The size actually used by the buffer * @usage: The size actually used by the buffer
*/ */
QIOChannelBufferWriteback* QIOChannelBufferWriteback* qio_channel_buffer_writeback_new_external(
qio_channel_buffer_writeback_new_external(uint8_t* buf, size_t capacity, size_t usage, uint8_t* writeback_buf, size_t writeback_buf_capacity, size_t* writeback_buf_usage); uint8_t* buf, size_t capacity, size_t usage, uint8_t* writeback_buf,
size_t writeback_buf_capacity, size_t* writeback_buf_usage);

View File

@ -2,7 +2,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#define DEVICE_SAVE_KIND_FULL 0 #define DEVICE_SAVE_KIND_FULL 0
typedef struct DeviceSaveState { typedef struct DeviceSaveState {
uint8_t kind; uint8_t kind;

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
// Rewritten COW cache for block devices, heavily inspired by kAFL/NYX implementation. // Rewritten COW cache for block devices, heavily inspired by kAFL/NYX
// implementation.
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "block/block.h" #include "block/block.h"
@ -16,7 +18,7 @@ typedef struct SyxCowCacheDevice {
typedef struct SyxCowCacheLayer SyxCowCacheLayer; typedef struct SyxCowCacheLayer SyxCowCacheLayer;
typedef struct SyxCowCacheLayer { typedef struct SyxCowCacheLayer {
GHashTable *cow_cache_devices; // H(device) -> SyxCowCacheDevice GHashTable* cow_cache_devices; // H(device) -> SyxCowCacheDevice
uint64_t chunk_size; uint64_t chunk_size;
uint64_t max_nb_chunks; uint64_t max_nb_chunks;
@ -33,13 +35,17 @@ SyxCowCache* syx_cow_cache_new(void);
// rhs is freed and nulled. // rhs is freed and nulled.
void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs); void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs);
void syx_cow_cache_push_layer(SyxCowCache* scc, uint64_t chunk_size, uint64_t max_size); void syx_cow_cache_push_layer(SyxCowCache* scc, uint64_t chunk_size,
uint64_t max_size);
void syx_cow_cache_pop_layer(SyxCowCache* scc); void syx_cow_cache_pop_layer(SyxCowCache* scc);
void syx_cow_cache_flush_highest_layer(SyxCowCache* scc); void syx_cow_cache_flush_highest_layer(SyxCowCache* scc);
void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend* blk,
BdrvRequestFlags flags); int64_t offset, int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset, BdrvRequestFlags flags);
bool syx_cow_cache_write_entry(SyxCowCache* scc, BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, bool syx_cow_cache_write_entry(SyxCowCache* scc, BlockBackend* blk,
int64_t offset, int64_t bytes,
QEMUIOVector* qiov, size_t qiov_offset,
BdrvRequestFlags flags); BdrvRequestFlags flags);

View File

@ -9,11 +9,13 @@
#pragma once #pragma once
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qom/object.h" #include "qom/object.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "device-save.h" #include "device-save.h"
#include "syx-cow-cache.h" #include "syx-cow-cache.h"
#include "libafl/syx-misc.h" #include "libafl/syx-misc.h"
#define SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE 64 #define SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE 64
@ -31,7 +33,9 @@ typedef struct SyxSnapshot {
SyxSnapshotIncrement* last_incremental_snapshot; SyxSnapshotIncrement* last_incremental_snapshot;
SyxCowCache* bdrvs_cow_cache; SyxCowCache* bdrvs_cow_cache;
GHashTable *rbs_dirty_list; // hash map: H(rb) -> GHashTable(offset_within_ramblock). Filled lazily. GHashTable*
rbs_dirty_list; // hash map: H(rb) ->
// GHashTable(offset_within_ramblock). Filled lazily.
} SyxSnapshot; } SyxSnapshot;
typedef struct SyxSnapshotTracker { typedef struct SyxSnapshotTracker {
@ -54,7 +58,7 @@ typedef struct SyxSnapshotState {
// It is not updated anymore when an active bdrv cache snapshto is set. // It is not updated anymore when an active bdrv cache snapshto is set.
SyxCowCache* before_fuzz_cache; SyxCowCache* before_fuzz_cache;
// snapshot used to restore bdrv cache if enabled. // snapshot used to restore bdrv cache if enabled.
SyxSnapshot* active_bdrv_cache_snapshot; SyxSnapshot* active_bdrv_cache_snapshot;
// Root // Root
} SyxSnapshotState; } SyxSnapshotState;
@ -69,22 +73,24 @@ void syx_snapshot_init(bool cached_bdrvs);
// Snapshot API // Snapshot API
// //
SyxSnapshot *syx_snapshot_new(bool track, bool is_active_bdrv_cache, DeviceSnapshotKind kind, char **devices); SyxSnapshot* syx_snapshot_new(bool track, bool is_active_bdrv_cache,
DeviceSnapshotKind kind, char** devices);
void syx_snapshot_free(SyxSnapshot *snapshot); void syx_snapshot_free(SyxSnapshot* snapshot);
void syx_snapshot_root_restore(SyxSnapshot *snapshot); void syx_snapshot_root_restore(SyxSnapshot* snapshot);
SyxSnapshotCheckResult syx_snapshot_check(SyxSnapshot* ref_snapshot); SyxSnapshotCheckResult syx_snapshot_check(SyxSnapshot* ref_snapshot);
// Push the current RAM state and saves it // Push the current RAM state and saves it
void syx_snapshot_increment_push(SyxSnapshot *snapshot, DeviceSnapshotKind kind, char **devices); void syx_snapshot_increment_push(SyxSnapshot* snapshot, DeviceSnapshotKind kind,
char** devices);
// Restores the last push. Restores the root snapshot if no incremental snapshot is present. // Restores the last push. Restores the root snapshot if no incremental snapshot
void syx_snapshot_increment_pop(SyxSnapshot *snapshot); // is present.
void syx_snapshot_increment_pop(SyxSnapshot* snapshot);
void syx_snapshot_increment_restore_last(SyxSnapshot *snapshot);
void syx_snapshot_increment_restore_last(SyxSnapshot* snapshot);
// //
// Snapshot tracker API // Snapshot tracker API
@ -92,10 +98,10 @@ void syx_snapshot_increment_restore_last(SyxSnapshot *snapshot);
SyxSnapshotTracker syx_snapshot_tracker_init(void); SyxSnapshotTracker syx_snapshot_tracker_init(void);
void syx_snapshot_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot); void syx_snapshot_track(SyxSnapshotTracker* tracker, SyxSnapshot* snapshot);
void syx_snapshot_stop_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot);
void syx_snapshot_stop_track(SyxSnapshotTracker* tracker,
SyxSnapshot* snapshot);
// //
// Misc functions // Misc functions
@ -103,14 +109,13 @@ void syx_snapshot_stop_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot)
bool syx_snapshot_is_enabled(void); bool syx_snapshot_is_enabled(void);
// //
// Dirty list API // Dirty list API
// //
void syx_snapshot_dirty_list_add_hostaddr(void *host_addr); void syx_snapshot_dirty_list_add_hostaddr(void* host_addr);
void syx_snapshot_dirty_list_add_hostaddr_range(void *host_addr, uint64_t len); void syx_snapshot_dirty_list_add_hostaddr_range(void* host_addr, uint64_t len);
/** /**
* @brief Same as syx_snapshot_dirty_list_add. The difference * @brief Same as syx_snapshot_dirty_list_add. The difference
@ -124,10 +129,14 @@ void syx_snapshot_dirty_list_add_hostaddr_range(void *host_addr, uint64_t len);
* tcg-target.inc.c specific environment. * tcg-target.inc.c specific environment.
* @param host_addr The host address where the dirty page is located. * @param host_addr The host address where the dirty page is located.
*/ */
void syx_snapshot_dirty_list_add_tcg_target(uint64_t dummy, void *host_addr); void syx_snapshot_dirty_list_add_tcg_target(uint64_t dummy, void* host_addr);
bool syx_snapshot_cow_cache_read_entry(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, bool syx_snapshot_cow_cache_read_entry(BlockBackend* blk, int64_t offset,
BdrvRequestFlags flags); int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset,
BdrvRequestFlags flags);
bool syx_snapshot_cow_cache_write_entry(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, bool syx_snapshot_cow_cache_write_entry(BlockBackend* blk, int64_t offset,
BdrvRequestFlags flags); int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset,
BdrvRequestFlags flags);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/osdep.h"
struct libafl_mapinfo { struct libafl_mapinfo {
target_ulong start; target_ulong start;
@ -13,5 +13,9 @@ struct libafl_mapinfo {
bool is_valid; bool is_valid;
}; };
IntervalTreeNode * libafl_maps_first(IntervalTreeRoot * map_info); void libafl_qemu_handle_crash(int host_sig, siginfo_t* info, void* puc);
IntervalTreeNode * libafl_maps_next(IntervalTreeNode *pageflags_maps_node, IntervalTreeRoot *proc_maps_node, struct libafl_mapinfo* ret);
IntervalTreeNode* libafl_maps_first(IntervalTreeRoot* map_info);
IntervalTreeNode* libafl_maps_next(IntervalTreeNode* pageflags_maps_node,
IntervalTreeRoot* proc_maps_node,
struct libafl_mapinfo* ret);

9
libafl/.clang-format Normal file
View File

@ -0,0 +1,9 @@
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
BreakBeforeBraces: Linux
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
SortIncludes: Never
IncludeBlocks: Preserve
PointerAlignment: Left

View File

@ -1,6 +1,11 @@
#include "libafl/exit.h" #include "libafl/exit.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-temp-internal.h"
#include "sysemu/runstate.h" #include "sysemu/runstate.h"
#include "exec/translator.h"
#include "cpu.h" #include "cpu.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
@ -13,11 +18,9 @@ struct libafl_breakpoint* libafl_qemu_breakpoints = NULL;
int libafl_qemu_set_breakpoint(target_ulong pc) int libafl_qemu_set_breakpoint(target_ulong pc)
{ {
CPUState *cpu; CPUState* cpu;
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) { libafl_breakpoint_invalidate(cpu, pc); }
libafl_breakpoint_invalidate(cpu, pc);
}
struct libafl_breakpoint* bp = calloc(sizeof(struct libafl_breakpoint), 1); struct libafl_breakpoint* bp = calloc(sizeof(struct libafl_breakpoint), 1);
bp->addr = pc; bp->addr = pc;
@ -28,15 +31,13 @@ int libafl_qemu_set_breakpoint(target_ulong pc)
int libafl_qemu_remove_breakpoint(target_ulong pc) int libafl_qemu_remove_breakpoint(target_ulong pc)
{ {
CPUState *cpu; CPUState* cpu;
int r = 0; int r = 0;
struct libafl_breakpoint** bp = &libafl_qemu_breakpoints; struct libafl_breakpoint** bp = &libafl_qemu_breakpoints;
while (*bp) { while (*bp) {
if ((*bp)->addr == pc) { if ((*bp)->addr == pc) {
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) { libafl_breakpoint_invalidate(cpu, pc); }
libafl_breakpoint_invalidate(cpu, pc);
}
*bp = (*bp)->next; *bp = (*bp)->next;
r = 1; r = 1;
@ -61,15 +62,13 @@ void libafl_sync_exit_cpu(void)
{ {
if (last_exit_reason.next_pc) { if (last_exit_reason.next_pc) {
CPUClass* cc = CPU_GET_CLASS(last_exit_reason.cpu); 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)); cc->set_pc(last_exit_reason.cpu,
THUMB_MASK(last_exit_reason.cpu, last_exit_reason.next_pc));
} }
last_exit_reason.next_pc = 0; last_exit_reason.next_pc = 0;
} }
bool libafl_exit_asap(void) bool libafl_exit_asap(void) { return expected_exit; }
{
return expected_exit;
}
static void prepare_qemu_exit(CPUState* cpu, target_ulong next_pc) static void prepare_qemu_exit(CPUState* cpu, target_ulong next_pc)
{ {
@ -81,7 +80,8 @@ static void prepare_qemu_exit(CPUState* cpu, target_ulong next_pc)
qemu_system_debug_request(); qemu_system_debug_request();
cpu->stopped = true; // TODO check if still needed cpu->stopped = true; // TODO check if still needed
#endif #endif
// in usermode, this may be called from the syscall hook, thus already out of the cpu_exec but still in the cpu_loop // 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) { if (cpu->running) {
cpu->exception_index = EXCP_LIBAFL_EXIT; cpu->exception_index = EXCP_LIBAFL_EXIT;
cpu_loop_exit(cpu); cpu_loop_exit(cpu);
@ -97,7 +97,8 @@ CPUState* libafl_last_exit_cpu(void)
return NULL; return NULL;
} }
void libafl_exit_request_internal(CPUState* cpu, uint64_t pc, ShutdownCause cause, int signal) void libafl_exit_request_internal(CPUState* cpu, uint64_t pc,
ShutdownCause cause, int signal)
{ {
last_exit_reason.kind = INTERNAL; last_exit_reason.kind = INTERNAL;
last_exit_reason.data.internal.cause = cause; last_exit_reason.data.internal.cause = cause;
@ -143,3 +144,16 @@ struct libafl_exit_reason* libafl_get_exit_reason(void)
return NULL; return NULL;
} }
void libafl_qemu_breakpoint_run(vaddr pc_next)
{
struct libafl_breakpoint* bp = libafl_qemu_breakpoints;
while (bp) {
if (bp->addr == pc_next) {
TCGv_i64 tmp0 = tcg_constant_i64((uint64_t)pc_next);
gen_helper_libafl_qemu_handle_breakpoint(tcg_env, tmp0);
tcg_temp_free_i64(tmp0);
}
bp = bp->next;
}
}

View File

@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
@ -11,683 +12,6 @@
#error "TARGET_LONG_BITS not defined" #error "TARGET_LONG_BITS not defined"
#endif #endif
target_ulong libafl_gen_cur_pc;
struct libafl_hook* libafl_qemu_instruction_hooks[LIBAFL_TABLES_SIZE];
size_t libafl_qemu_hooks_num = 0;
size_t libafl_qemu_add_instruction_hooks(target_ulong pc, void (*callback)(uint64_t data, target_ulong pc),
uint64_t data, int invalidate)
{
CPUState *cpu;
if (invalidate) {
CPU_FOREACH(cpu) {
libafl_breakpoint_invalidate(cpu, pc);
}
}
size_t idx = LIBAFL_TABLES_HASH(pc);
struct libafl_hook* hk = calloc(sizeof(struct libafl_hook), 1);
hk->addr = pc;
// hk->callback = callback;
hk->data = data;
hk->helper_info.func = callback;
hk->helper_info.name = "libafl_hook";
hk->helper_info.flags = dh_callflag(void);
hk->helper_info.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(tl, 2);
// TODO check for overflow
hk->num = libafl_qemu_hooks_num++;
hk->next = libafl_qemu_instruction_hooks[idx];
libafl_qemu_instruction_hooks[idx] = hk;
return hk->num;
}
size_t libafl_qemu_remove_instruction_hooks_at(target_ulong addr, int invalidate)
{
CPUState *cpu;
size_t r = 0;
size_t idx = LIBAFL_TABLES_HASH(addr);
struct libafl_hook** hk = &libafl_qemu_instruction_hooks[idx];
while (*hk) {
if ((*hk)->addr == addr) {
if (invalidate) {
CPU_FOREACH(cpu) {
libafl_breakpoint_invalidate(cpu, addr);
}
}
void *tmp = *hk;
*hk = (*hk)->next;
free(tmp);
r++;
} else {
hk = &(*hk)->next;
}
}
return r;
}
int libafl_qemu_remove_instruction_hook(size_t num, int invalidate)
{
CPUState *cpu;
size_t idx;
for (idx = 0; idx < LIBAFL_TABLES_SIZE; ++idx) {
struct libafl_hook** hk = &libafl_qemu_instruction_hooks[idx];
while (*hk) {
if ((*hk)->num == num) {
if (invalidate) {
CPU_FOREACH(cpu) {
libafl_breakpoint_invalidate(cpu, (*hk)->addr);
}
}
void *tmp = *hk;
*hk = (*hk)->next;
free(tmp);
return 1;
} else {
hk = &(*hk)->next;
}
}
}
return 0;
}
struct libafl_hook* libafl_search_instruction_hook(target_ulong addr)
{
size_t idx = LIBAFL_TABLES_HASH(addr);
struct libafl_hook* hk = libafl_qemu_instruction_hooks[idx];
while (hk) {
if (hk->addr == addr) {
return hk;
}
hk = hk->next;
}
return NULL;
}
#define GEN_REMOVE_HOOK(name) \
int libafl_qemu_remove_##name##_hook(size_t num, int invalidate) \
{ \
CPUState *cpu; \
struct libafl_##name##_hook** hk = &libafl_##name##_hooks; \
\
while (*hk) { \
if ((*hk)->num == num) { \
if (invalidate) { \
CPU_FOREACH(cpu) { \
tb_flush(cpu); \
} \
} \
\
void *tmp = *hk; \
*hk = (*hk)->next; \
free(tmp); \
return 1; \
} else { \
hk = &(*hk)->next; \
} \
} \
\
return 0; \
}
#define GEN_REMOVE_HOOK1(name) \
int libafl_qemu_remove_##name##_hook(size_t num) \
{ \
struct libafl_##name##_hook** hk = &libafl_##name##_hooks; \
\
while (*hk) { \
if ((*hk)->num == num) { \
void *tmp = *hk; \
*hk = (*hk)->next; \
free(tmp); \
return 1; \
} else { \
hk = &(*hk)->next; \
} \
} \
\
return 0; \
}
static TCGHelperInfo libafl_exec_backdoor_hook_info = {
.func = NULL, .name = "libafl_exec_backdoor_hook",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0)
| dh_typemask(env, 1)
| dh_typemask(i64, 2)
| dh_typemask(tl, 3)
};
struct libafl_backdoor_hook* libafl_backdoor_hooks;
size_t libafl_backdoor_hooks_num = 0;
size_t libafl_add_backdoor_hook(void (*exec)(uint64_t data, CPUArchState* cpu, target_ulong pc),
uint64_t data)
{
struct libafl_backdoor_hook* hook = calloc(sizeof(struct libafl_backdoor_hook), 1);
// hook->exec = exec;
hook->data = data;
hook->num = libafl_backdoor_hooks_num++;
hook->next = libafl_backdoor_hooks;
libafl_backdoor_hooks = hook;
memcpy(&hook->helper_info, &libafl_exec_backdoor_hook_info, sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
return hook->num;
}
GEN_REMOVE_HOOK(backdoor)
static TCGHelperInfo libafl_exec_edge_hook_info = {
.func = NULL, .name = "libafl_exec_edge_hook",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)
};
struct libafl_edge_hook* libafl_edge_hooks;
size_t libafl_edge_hooks_num = 0;
size_t libafl_add_edge_hook(uint64_t (*gen)(uint64_t data, target_ulong src, target_ulong dst),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
tb_flush(cpu);
}
struct libafl_edge_hook* hook = calloc(sizeof(struct libafl_edge_hook), 1);
hook->gen = gen;
// hook->exec = exec;
hook->data = data;
hook->num = libafl_edge_hooks_num++;
hook->next = libafl_edge_hooks;
libafl_edge_hooks = hook;
if (exec) {
memcpy(&hook->helper_info, &libafl_exec_edge_hook_info, sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
}
return hook->num;
}
GEN_REMOVE_HOOK(edge)
bool libafl_qemu_edge_hook_set_jit(size_t num, size_t (*jit)(uint64_t data, uint64_t id)) {
struct libafl_edge_hook* hk = libafl_edge_hooks;
while (hk) {
if (hk->num == num) {
hk->jit = jit;
return true;
} else {
hk = hk->next;
}
}
return false;
}
static TCGHelperInfo libafl_exec_block_hook_info = {
.func = NULL, .name = "libafl_exec_block_hook", \
.flags = dh_callflag(void), \
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)
};
struct libafl_block_hook* libafl_block_hooks;
size_t libafl_block_hooks_num = 0;
size_t libafl_add_block_hook(uint64_t (*gen)(uint64_t data, target_ulong pc),
void (*post_gen)(uint64_t data, target_ulong pc, target_ulong block_length),
void (*exec)(uint64_t data, uint64_t id), uint64_t data)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
tb_flush(cpu);
}
struct libafl_block_hook* hook = calloc(sizeof(struct libafl_block_hook), 1);
hook->gen = gen;
hook->post_gen = post_gen;
// hook->exec = exec;
hook->data = data;
hook->num = libafl_block_hooks_num++;
hook->next = libafl_block_hooks;
libafl_block_hooks = hook;
if (exec) {
memcpy(&hook->helper_info, &libafl_exec_block_hook_info, sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
}
return hook->num;
}
GEN_REMOVE_HOOK(block)
bool libafl_qemu_block_hook_set_jit(size_t num, size_t (*jit)(uint64_t data, uint64_t id)) {
struct libafl_block_hook* hk = libafl_block_hooks;
while (hk) {
if (hk->num == num) {
hk->jit = jit;
return true;
} else {
hk = hk->next;
}
}
return false;
}
static TCGHelperInfo libafl_exec_read_hook1_info = {
.func = NULL, .name = "libafl_exec_read_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_read_hook2_info = {
.func = NULL, .name = "libafl_exec_read_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_read_hook4_info = {
.func = NULL, .name = "libafl_exec_read_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_read_hook8_info = {
.func = NULL, .name = "libafl_exec_read_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_read_hookN_info = {
.func = NULL, .name = "libafl_exec_read_hookN",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)
| dh_typemask(tl, 3) | dh_typemask(i64, 4)
};
static TCGHelperInfo libafl_exec_write_hook1_info = {
.func = NULL, .name = "libafl_exec_write_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_write_hook2_info = {
.func = NULL, .name = "libafl_exec_write_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_write_hook4_info = {
.func = NULL, .name = "libafl_exec_write_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_write_hook8_info = {
.func = NULL, .name = "libafl_exec_write_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2) | dh_typemask(tl, 3)
};
static TCGHelperInfo libafl_exec_write_hookN_info = {
.func = NULL, .name = "libafl_exec_write_hookN",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)
| dh_typemask(tl, 3) | dh_typemask(i64, 4)
};
struct libafl_rw_hook* libafl_read_hooks;
size_t libafl_read_hooks_num = 0;
size_t libafl_add_read_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp *addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
tb_flush(cpu);
}
struct libafl_rw_hook* hook = calloc(sizeof(struct libafl_rw_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;
hook->execN = execN;*/
hook->data = data;
hook->num = libafl_read_hooks_num++;
hook->next = libafl_read_hooks;
libafl_read_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_read_hook1_info, sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_read_hook2_info, sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_read_hook4_info, sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_read_hook8_info, sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
if (execN) {
memcpy(&hook->helper_infoN, &libafl_exec_read_hookN_info, sizeof(TCGHelperInfo));
hook->helper_infoN.func = execN;
}
return hook->num;
}
GEN_REMOVE_HOOK(read)
struct libafl_rw_hook* libafl_write_hooks;
size_t libafl_write_hooks_num = 0;
size_t libafl_add_write_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp *addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
tb_flush(cpu);
}
struct libafl_rw_hook* hook = calloc(sizeof(struct libafl_rw_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;
hook->execN = execN;*/
hook->data = data;
hook->num = libafl_write_hooks_num++;
hook->next = libafl_write_hooks;
libafl_write_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_write_hook1_info, sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_write_hook2_info, sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_write_hook4_info, sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_write_hook8_info, sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
if (execN) {
memcpy(&hook->helper_infoN, &libafl_exec_write_hookN_info, sizeof(TCGHelperInfo));
hook->helper_infoN.func = execN;
}
return hook->num;
}
GEN_REMOVE_HOOK(write)
static void libafl_gen_rw(TCGTemp *addr, MemOpIdx oi, struct libafl_rw_hook* hook)
{
size_t size = memop_size(get_memop(oi));
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, libafl_gen_cur_pc, addr, oi);
TCGHelperInfo* info = NULL;
if (size == 1 && hook->helper_info1.func) info = &hook->helper_info1;
else if (size == 2 && hook->helper_info2.func) info = &hook->helper_info2;
else if (size == 4 && hook->helper_info4.func) info = &hook->helper_info4;
else if (size == 8 && hook->helper_info8.func) info = &hook->helper_info8;
if (cur_id != (uint64_t)-1) {
if (info) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp *tmp2[3] = { tcgv_i64_temp(tmp0),
tcgv_i64_temp(tmp1),
addr };
tcg_gen_callN(info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
} else if (hook->helper_infoN.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGv tmp2 = tcg_constant_tl(size);
TCGTemp *tmp3[4] = { tcgv_i64_temp(tmp0),
tcgv_i64_temp(tmp1),
addr,
#if TARGET_LONG_BITS == 32
tcgv_i32_temp(tmp2) };
#else
tcgv_i64_temp(tmp2) };
#endif
tcg_gen_callN(&hook->helper_infoN, NULL, tmp3);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
#if TARGET_LONG_BITS == 32
tcg_temp_free_i32(tmp2);
#else
tcg_temp_free_i64(tmp2);
#endif
}
}
hook = hook->next;
}
}
void libafl_gen_read(TCGTemp *addr, MemOpIdx oi)
{
libafl_gen_rw(addr, oi, libafl_read_hooks);
}
void libafl_gen_write(TCGTemp *addr, MemOpIdx oi)
{
libafl_gen_rw(addr, oi, libafl_write_hooks);
}
static TCGHelperInfo libafl_exec_cmp_hook1_info = {
.func = NULL, .name = "libafl_exec_cmp_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1)
| dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)
};
static TCGHelperInfo libafl_exec_cmp_hook2_info = {
.func = NULL, .name = "libafl_exec_cmp_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1)
| dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)
};
static TCGHelperInfo libafl_exec_cmp_hook4_info = {
.func = NULL, .name = "libafl_exec_cmp_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1)
| dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)
};
static TCGHelperInfo libafl_exec_cmp_hook8_info = {
.func = NULL, .name = "libafl_exec_cmp_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1)
| dh_typemask(i64, 2) | dh_typemask(i64, 3) | dh_typemask(i64, 4)
};
struct libafl_cmp_hook* libafl_cmp_hooks;
size_t libafl_cmp_hooks_num = 0;
size_t libafl_add_cmp_hook(uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size),
void (*exec1)(uint64_t data, uint64_t id, uint8_t v0, uint8_t v1),
void (*exec2)(uint64_t data, uint64_t id, uint16_t v0, uint16_t v1),
void (*exec4)(uint64_t data, uint64_t id, uint32_t v0, uint32_t v1),
void (*exec8)(uint64_t data, uint64_t id, uint64_t v0, uint64_t v1),
uint64_t data)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
tb_flush(cpu);
}
struct libafl_cmp_hook* hook = calloc(sizeof(struct libafl_cmp_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;*/
hook->data = data;
hook->num = libafl_cmp_hooks_num++;
hook->next = libafl_cmp_hooks;
libafl_cmp_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_cmp_hook1_info, sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_cmp_hook2_info, sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_cmp_hook4_info, sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_cmp_hook8_info, sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
return hook->num;
}
GEN_REMOVE_HOOK(cmp)
void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot)
{
size_t size = 0;
switch (ot & MO_SIZE) {
case MO_64:
size = 8;
break;
case MO_32:
size = 4;
break;
case MO_16:
size = 2;
break;
case MO_8:
size = 1;
break;
default:
return;
}
struct libafl_cmp_hook* hook = libafl_cmp_hooks;
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, pc, size);
TCGHelperInfo* info = NULL;
if (size == 1 && hook->helper_info1.func) info = &hook->helper_info1;
else if (size == 2 && hook->helper_info2.func) info = &hook->helper_info2;
else if (size == 4 && hook->helper_info4.func) info = &hook->helper_info4;
else if (size == 8 && hook->helper_info8.func) info = &hook->helper_info8;
if (cur_id != (uint64_t)-1 && info) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp *tmp2[4] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1),
#if TARGET_LONG_BITS == 32
tcgv_i32_temp(op0), tcgv_i32_temp(op1) };
#else
tcgv_i64_temp(op0), tcgv_i64_temp(op1) };
#endif
tcg_gen_callN(info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
hook = hook->next;
}
}
struct libafl_pre_syscall_hook* libafl_pre_syscall_hooks;
struct libafl_post_syscall_hook* libafl_post_syscall_hooks;
size_t libafl_pre_syscall_hooks_num = 0;
size_t libafl_post_syscall_hooks_num = 0;
size_t libafl_add_pre_syscall_hook(struct syshook_ret (*callback)(
uint64_t data, int sys_num, target_ulong arg0,
target_ulong arg1, target_ulong arg2,
target_ulong arg3, target_ulong arg4,
target_ulong arg5, target_ulong arg6,
target_ulong arg7),
uint64_t data)
{
struct libafl_pre_syscall_hook* hook = calloc(sizeof(struct libafl_pre_syscall_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_pre_syscall_hooks_num++;
hook->next = libafl_pre_syscall_hooks;
libafl_pre_syscall_hooks = hook;
return hook->num;
}
size_t libafl_add_post_syscall_hook(target_ulong (*callback)(
uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data)
{
struct libafl_post_syscall_hook* hook = calloc(sizeof(struct libafl_post_syscall_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_post_syscall_hooks_num++;
hook->next = libafl_post_syscall_hooks;
libafl_post_syscall_hooks = hook;
return hook->num;
}
GEN_REMOVE_HOOK1(pre_syscall)
GEN_REMOVE_HOOK1(post_syscall)
struct libafl_new_thread_hook* libafl_new_thread_hooks;
size_t libafl_new_thread_hooks_num = 0;
size_t libafl_add_new_thread_hook(bool (*callback)(uint64_t data, uint32_t tid),
uint64_t data) {
struct libafl_new_thread_hook* hook = calloc(sizeof(struct libafl_new_thread_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_new_thread_hooks_num++;
hook->next = libafl_new_thread_hooks;
libafl_new_thread_hooks = hook;
return hook->num;
}
GEN_REMOVE_HOOK1(new_thread)
#if TARGET_LONG_BITS == 32 #if TARGET_LONG_BITS == 32
#define SHADOW_BASE (0x20000000) #define SHADOW_BASE (0x20000000)
#elif TARGET_LONG_BITS == 64 #elif TARGET_LONG_BITS == 64
@ -696,7 +20,7 @@ GEN_REMOVE_HOOK1(new_thread)
#error Unhandled TARGET_LONG_BITS value #error Unhandled TARGET_LONG_BITS value
#endif #endif
void libafl_tcg_gen_asan(TCGTemp * addr, size_t size) void libafl_tcg_gen_asan(TCGTemp* addr, size_t size)
{ {
if (size == 0) if (size == 0)
return; return;
@ -729,15 +53,12 @@ void libafl_tcg_gen_asan(TCGTemp * addr, size_t size)
* detects an invalid access, we will instead attempt to write the value * detects an invalid access, we will instead attempt to write the value
* at 0x0. * at 0x0.
*/ */
tcg_gen_movcond_tl(TCG_COND_EQ, test_addr, tcg_gen_movcond_tl(TCG_COND_EQ, test_addr, shadow_val, tcg_constant_tl(0),
shadow_val, tcg_constant_tl(0), shadow_addr, tcg_constant_tl(0));
shadow_addr, tcg_constant_tl(0));
if (size < 8) if (size < 8) {
{ tcg_gen_movcond_tl(TCG_COND_GE, test_addr, k, shadow_val, test_addr,
tcg_gen_movcond_tl(TCG_COND_GE, test_addr, shadow_addr);
k, shadow_val,
test_addr, shadow_addr);
} }
tcg_gen_tl_ptr(test_ptr, test_addr); tcg_gen_tl_ptr(test_ptr, test_addr);

41
libafl/hooks/cpu_run.c Normal file
View File

@ -0,0 +1,41 @@
#include "libafl/hooks/cpu_run.h"
static struct libafl_cpu_run_hook* libafl_cpu_run_hooks = NULL;
static size_t libafl_cpu_run_hooks_num = 0;
GEN_REMOVE_HOOK1(cpu_run)
size_t libafl_hook_cpu_run_add(libafl_cpu_run_fn pre_cpu_run,
libafl_cpu_run_fn post_cpu_run, uint64_t data)
{
struct libafl_cpu_run_hook* hook =
calloc(sizeof(struct libafl_cpu_run_hook), 1);
hook->pre_cpu_run = pre_cpu_run;
hook->post_cpu_run = post_cpu_run;
hook->data = data;
hook->num = libafl_cpu_run_hooks_num++;
hook->next = libafl_cpu_run_hooks;
libafl_cpu_run_hooks = hook;
return hook->num;
}
void libafl_hook_cpu_run_pre_exec(CPUState* cpu)
{
struct libafl_cpu_run_hook* h = libafl_cpu_run_hooks;
while (h) {
h->pre_cpu_run(h->data, cpu);
h = h->next;
}
}
void libafl_hook_cpu_run_post_exec(CPUState* cpu)
{
struct libafl_cpu_run_hook* h = libafl_cpu_run_hooks;
while (h) {
h->post_cpu_run(h->data, cpu);
h = h->next;
}
}

89
libafl/hooks/syscall.c Normal file
View File

@ -0,0 +1,89 @@
#include "libafl/hooks/syscall.h"
struct libafl_pre_syscall_hook* libafl_pre_syscall_hooks;
size_t libafl_pre_syscall_hooks_num = 0;
struct libafl_post_syscall_hook* libafl_post_syscall_hooks;
size_t libafl_post_syscall_hooks_num = 0;
GEN_REMOVE_HOOK1(pre_syscall)
GEN_REMOVE_HOOK1(post_syscall)
size_t libafl_add_pre_syscall_hook(
struct syshook_ret (*callback)(uint64_t data, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data)
{
struct libafl_pre_syscall_hook* hook =
calloc(sizeof(struct libafl_pre_syscall_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_pre_syscall_hooks_num++;
hook->next = libafl_pre_syscall_hooks;
libafl_pre_syscall_hooks = hook;
return hook->num;
}
size_t libafl_add_post_syscall_hook(
target_ulong (*callback)(uint64_t data, target_ulong ret, int sys_num,
target_ulong arg0, target_ulong arg1,
target_ulong arg2, target_ulong arg3,
target_ulong arg4, target_ulong arg5,
target_ulong arg6, target_ulong arg7),
uint64_t data)
{
struct libafl_post_syscall_hook* hook =
calloc(sizeof(struct libafl_post_syscall_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_post_syscall_hooks_num++;
hook->next = libafl_post_syscall_hooks;
libafl_post_syscall_hooks = hook;
return hook->num;
}
bool libafl_hook_syscall_pre_run(CPUArchState* env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8, abi_long* ret)
{
bool skip_syscall = false;
struct libafl_pre_syscall_hook* h = libafl_pre_syscall_hooks;
while (h) {
// no null check
struct syshook_ret hook_ret = h->callback(
h->data, num, (target_ulong)arg1, (target_ulong)arg2,
(target_ulong)arg3, (target_ulong)arg4, (target_ulong)arg5,
(target_ulong)arg6, (target_ulong)arg7, (target_ulong)arg8);
if (hook_ret.skip_syscall) {
skip_syscall = true;
*ret = (abi_ulong)hook_ret.retval;
}
h = h->next;
}
return skip_syscall;
}
void libafl_hook_syscall_post_run(int num, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5,
abi_long arg6, abi_long arg7, abi_long arg8,
abi_long* ret)
{
struct libafl_post_syscall_hook* p = libafl_post_syscall_hooks;
while (p) {
// no null check
*ret = (abi_ulong)p->callback(p->data, (target_ulong)*ret, num,
(target_ulong)arg1, (target_ulong)arg2,
(target_ulong)arg3, (target_ulong)arg4,
(target_ulong)arg5, (target_ulong)arg6,
(target_ulong)arg7, (target_ulong)arg8);
p = p->next;
}
}

View File

@ -0,0 +1,47 @@
#include "libafl/hooks/tcg/backdoor.h"
struct libafl_backdoor_hook* libafl_backdoor_hooks;
size_t libafl_backdoor_hooks_num = 0;
static TCGHelperInfo libafl_exec_backdoor_hook_info = {
.func = NULL,
.name = "libafl_exec_backdoor_hook",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(env, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
GEN_REMOVE_HOOK(backdoor)
size_t libafl_add_backdoor_hook(void (*exec)(uint64_t data, CPUArchState* cpu,
target_ulong pc),
uint64_t data)
{
struct libafl_backdoor_hook* hook =
calloc(sizeof(struct libafl_backdoor_hook), 1);
// hook->exec = exec;
hook->data = data;
hook->num = libafl_backdoor_hooks_num++;
hook->next = libafl_backdoor_hooks;
libafl_backdoor_hooks = hook;
memcpy(&hook->helper_info, &libafl_exec_backdoor_hook_info,
sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
return hook->num;
}
void libafl_qemu_hook_backdoor_run(vaddr pc_next)
{
struct libafl_backdoor_hook* bhk = libafl_backdoor_hooks;
while (bhk) {
TCGv_i64 tmp0 = tcg_constant_i64(bhk->data);
TCGv tmp2 = tcg_constant_tl(pc_next);
TCGTemp* args[3] = {tcgv_i64_temp(tmp0), tcgv_ptr_temp(tcg_env),
tcgv_tl_temp(tmp2)};
tcg_gen_callN(&bhk->helper_info, NULL, args);
bhk = bhk->next;
}
}

89
libafl/hooks/tcg/block.c Normal file
View File

@ -0,0 +1,89 @@
#include "libafl/hooks/tcg/block.h"
struct libafl_block_hook* libafl_block_hooks;
size_t libafl_block_hooks_num = 0;
static TCGHelperInfo libafl_exec_block_hook_info = {
.func = NULL,
.name = "libafl_exec_block_hook",
.flags = dh_callflag(void),
.typemask =
dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)};
GEN_REMOVE_HOOK(block)
size_t libafl_add_block_hook(uint64_t (*gen)(uint64_t data, target_ulong pc),
void (*post_gen)(uint64_t data, target_ulong pc,
target_ulong block_length),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data)
{
CPUState* cpu;
CPU_FOREACH(cpu) { tb_flush(cpu); }
struct libafl_block_hook* hook =
calloc(sizeof(struct libafl_block_hook), 1);
hook->gen = gen;
hook->post_gen = post_gen;
// hook->exec = exec;
hook->data = data;
hook->num = libafl_block_hooks_num++;
hook->next = libafl_block_hooks;
libafl_block_hooks = hook;
if (exec) {
memcpy(&hook->helper_info, &libafl_exec_block_hook_info,
sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
}
return hook->num;
}
bool libafl_qemu_block_hook_set_jit(size_t num,
size_t (*jit)(uint64_t data, uint64_t id))
{
struct libafl_block_hook* hk = libafl_block_hooks;
while (hk) {
if (hk->num == num) {
hk->jit = jit;
return true;
} else {
hk = hk->next;
}
}
return false;
}
void libafl_qemu_hook_block_post_gen(TranslationBlock* tb, vaddr pc)
{
struct libafl_block_hook* hook = libafl_block_hooks;
while (hook) {
if (hook->post_gen)
hook->post_gen(hook->data, pc, tb->size);
hook = hook->next;
}
}
void libafl_qemu_hook_block_run(target_ulong pc)
{
struct libafl_block_hook* hook = libafl_block_hooks;
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, pc);
if (cur_id != (uint64_t)-1 && hook->helper_info.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp* tmp2[2] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1)};
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
if (cur_id != (uint64_t)-1 && hook->jit) {
hook->jit(hook->data, cur_id);
}
hook = hook->next;
}
}

129
libafl/hooks/tcg/cmp.c Normal file
View File

@ -0,0 +1,129 @@
#include "libafl/hooks/tcg/cmp.h"
struct libafl_cmp_hook* libafl_cmp_hooks;
size_t libafl_cmp_hooks_num = 0;
static TCGHelperInfo libafl_exec_cmp_hook1_info = {
.func = NULL,
.name = "libafl_exec_cmp_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)};
static TCGHelperInfo libafl_exec_cmp_hook2_info = {
.func = NULL,
.name = "libafl_exec_cmp_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)};
static TCGHelperInfo libafl_exec_cmp_hook4_info = {
.func = NULL,
.name = "libafl_exec_cmp_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(tl, 4)};
static TCGHelperInfo libafl_exec_cmp_hook8_info = {
.func = NULL,
.name = "libafl_exec_cmp_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(i64, 3) |
dh_typemask(i64, 4)};
GEN_REMOVE_HOOK(cmp)
size_t libafl_add_cmp_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, size_t size),
void (*exec1)(uint64_t data, uint64_t id, uint8_t v0, uint8_t v1),
void (*exec2)(uint64_t data, uint64_t id, uint16_t v0, uint16_t v1),
void (*exec4)(uint64_t data, uint64_t id, uint32_t v0, uint32_t v1),
void (*exec8)(uint64_t data, uint64_t id, uint64_t v0, uint64_t v1),
uint64_t data)
{
CPUState* cpu;
CPU_FOREACH(cpu) { tb_flush(cpu); }
struct libafl_cmp_hook* hook = calloc(sizeof(struct libafl_cmp_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;*/
hook->data = data;
hook->num = libafl_cmp_hooks_num++;
hook->next = libafl_cmp_hooks;
libafl_cmp_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_cmp_hook1_info,
sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_cmp_hook2_info,
sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_cmp_hook4_info,
sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_cmp_hook8_info,
sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
return hook->num;
}
void libafl_gen_cmp(target_ulong pc, TCGv op0, TCGv op1, MemOp ot)
{
size_t size = 0;
switch (ot & MO_SIZE) {
case MO_64:
size = 8;
break;
case MO_32:
size = 4;
break;
case MO_16:
size = 2;
break;
case MO_8:
size = 1;
break;
default:
return;
}
struct libafl_cmp_hook* hook = libafl_cmp_hooks;
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, pc, size);
TCGHelperInfo* info = NULL;
if (size == 1 && hook->helper_info1.func)
info = &hook->helper_info1;
else if (size == 2 && hook->helper_info2.func)
info = &hook->helper_info2;
else if (size == 4 && hook->helper_info4.func)
info = &hook->helper_info4;
else if (size == 8 && hook->helper_info8.func)
info = &hook->helper_info8;
if (cur_id != (uint64_t)-1 && info) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp* tmp2[4] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1),
#if TARGET_LONG_BITS == 32
tcgv_i32_temp(op0), tcgv_i32_temp(op1)};
#else
tcgv_i64_temp(op0), tcgv_i64_temp(op1)};
#endif
tcg_gen_callN(info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
hook = hook->next;
}
}

89
libafl/hooks/tcg/edge.c Normal file
View File

@ -0,0 +1,89 @@
#include "libafl/hooks/tcg/edge.h"
struct libafl_edge_hook* libafl_edge_hooks;
size_t libafl_edge_hooks_num = 0;
static TCGHelperInfo libafl_exec_edge_hook_info = {
.func = NULL,
.name = "libafl_exec_edge_hook",
.flags = dh_callflag(void),
.typemask =
dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(i64, 2)};
GEN_REMOVE_HOOK(edge)
size_t libafl_add_edge_hook(uint64_t (*gen)(uint64_t data, target_ulong src,
target_ulong dst),
void (*exec)(uint64_t data, uint64_t id),
uint64_t data)
{
CPUState* cpu;
CPU_FOREACH(cpu) { tb_flush(cpu); }
struct libafl_edge_hook* hook = calloc(sizeof(struct libafl_edge_hook), 1);
hook->gen = gen;
// hook->exec = exec;
hook->data = data;
hook->num = libafl_edge_hooks_num++;
hook->next = libafl_edge_hooks;
libafl_edge_hooks = hook;
if (exec) {
memcpy(&hook->helper_info, &libafl_exec_edge_hook_info,
sizeof(TCGHelperInfo));
hook->helper_info.func = exec;
}
return hook->num;
}
bool libafl_qemu_edge_hook_set_jit(size_t num,
size_t (*jit)(uint64_t data, uint64_t id))
{
struct libafl_edge_hook* hk = libafl_edge_hooks;
while (hk) {
if (hk->num == num) {
hk->jit = jit;
return true;
} else {
hk = hk->next;
}
}
return false;
}
bool libafl_qemu_hook_edge_gen(target_ulong src_block, target_ulong dst_block)
{
struct libafl_edge_hook* hook = libafl_edge_hooks;
bool no_exec_hook = true;
while (hook) {
hook->cur_id = 0;
if (hook->gen)
hook->cur_id = hook->gen(hook->data, src_block, dst_block);
if (hook->cur_id != (uint64_t)-1 &&
(hook->helper_info.func || hook->jit))
no_exec_hook = false;
hook = hook->next;
}
return no_exec_hook;
}
void libafl_qemu_hook_edge_run(void)
{
struct libafl_edge_hook* hook = libafl_edge_hooks;
while (hook) {
if (hook->cur_id != (uint64_t)-1 && hook->helper_info.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(hook->cur_id);
TCGTemp* tmp2[2] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1)};
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
}
if (hook->cur_id != (uint64_t)-1 && hook->jit) {
hook->jit(hook->data, hook->cur_id);
}
hook = hook->next;
}
}

View File

@ -0,0 +1,133 @@
#include "libafl/hooks/tcg/instruction.h"
target_ulong libafl_gen_cur_pc;
struct libafl_instruction_hook*
libafl_qemu_instruction_hooks[LIBAFL_TABLES_SIZE];
size_t libafl_qemu_hooks_num = 0;
size_t libafl_qemu_add_instruction_hooks(target_ulong pc,
void (*callback)(uint64_t data,
target_ulong pc),
uint64_t
data,
int invalidate)
{
CPUState* cpu;
if (invalidate) {
CPU_FOREACH(cpu) { libafl_breakpoint_invalidate(cpu, pc); }
}
size_t idx = LIBAFL_TABLES_HASH(pc);
struct libafl_instruction_hook* hk =
calloc(sizeof(struct libafl_instruction_hook), 1);
hk->addr = pc;
// hk->callback = callback;
hk->data = data;
hk->helper_info.func = callback;
hk->helper_info.name = "libafl_instruction_hook";
hk->helper_info.flags = dh_callflag(void);
hk->helper_info.typemask =
dh_typemask(void, 0) | dh_typemask(i64, 1) | dh_typemask(tl, 2);
// TODO check for overflow
hk->num = libafl_qemu_hooks_num++;
hk->next = libafl_qemu_instruction_hooks[idx];
libafl_qemu_instruction_hooks[idx] = hk;
return hk->num;
}
size_t libafl_qemu_remove_instruction_hooks_at(target_ulong addr,
int invalidate)
{
CPUState* cpu;
size_t r = 0;
size_t idx = LIBAFL_TABLES_HASH(addr);
struct libafl_instruction_hook** hk = &libafl_qemu_instruction_hooks[idx];
while (*hk) {
if ((*hk)->addr == addr) {
if (invalidate) {
CPU_FOREACH(cpu) { libafl_breakpoint_invalidate(cpu, addr); }
}
void* tmp = *hk;
*hk = (*hk)->next;
free(tmp);
r++;
} else {
hk = &(*hk)->next;
}
}
return r;
}
int libafl_qemu_remove_instruction_hook(size_t num, int invalidate)
{
CPUState* cpu;
size_t idx;
for (idx = 0; idx < LIBAFL_TABLES_SIZE; ++idx) {
struct libafl_instruction_hook** hk =
&libafl_qemu_instruction_hooks[idx];
while (*hk) {
if ((*hk)->num == num) {
if (invalidate) {
CPU_FOREACH(cpu)
{
libafl_breakpoint_invalidate(cpu, (*hk)->addr);
}
}
void* tmp = *hk;
*hk = (*hk)->next;
free(tmp);
return 1;
} else {
hk = &(*hk)->next;
}
}
}
return 0;
}
struct libafl_instruction_hook*
libafl_search_instruction_hook(target_ulong addr)
{
size_t idx = LIBAFL_TABLES_HASH(addr);
struct libafl_instruction_hook* hk = libafl_qemu_instruction_hooks[idx];
while (hk) {
if (hk->addr == addr) {
return hk;
}
hk = hk->next;
}
return NULL;
}
void libafl_qemu_hook_instruction_run(vaddr pc_next)
{
struct libafl_instruction_hook* hk =
libafl_search_instruction_hook(pc_next);
if (hk) {
TCGv_i64 tmp0 = tcg_constant_i64(hk->data);
#if TARGET_LONG_BITS == 32
TCGv_i32 tmp1 = tcg_constant_i32(pc_next);
TCGTemp* tmp2[2] = {tcgv_i64_temp(tmp0), tcgv_i32_temp(tmp1)};
#else
TCGv_i64 tmp1 = tcg_constant_i64(pc_next);
TCGTemp* tmp2[2] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1)};
#endif
// tcg_gen_callN(hk->callback, NULL, 2, tmp2);
tcg_gen_callN(&hk->helper_info, NULL, tmp2);
#if TARGET_LONG_BITS == 32
tcg_temp_free_i32(tmp1);
#else
tcg_temp_free_i64(tmp1);
#endif
tcg_temp_free_i64(tmp0);
}
}

View File

@ -0,0 +1,240 @@
#include "libafl/hooks/tcg/read_write.h"
struct libafl_rw_hook* libafl_read_hooks;
size_t libafl_read_hooks_num = 0;
struct libafl_rw_hook* libafl_write_hooks;
size_t libafl_write_hooks_num = 0;
static TCGHelperInfo libafl_exec_read_hook1_info = {
.func = NULL,
.name = "libafl_exec_read_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_read_hook2_info = {
.func = NULL,
.name = "libafl_exec_read_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_read_hook4_info = {
.func = NULL,
.name = "libafl_exec_read_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_read_hook8_info = {
.func = NULL,
.name = "libafl_exec_read_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_read_hookN_info = {
.func = NULL,
.name = "libafl_exec_read_hookN",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(i64, 4)};
static TCGHelperInfo libafl_exec_write_hook1_info = {
.func = NULL,
.name = "libafl_exec_write_hook1",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_write_hook2_info = {
.func = NULL,
.name = "libafl_exec_write_hook2",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_write_hook4_info = {
.func = NULL,
.name = "libafl_exec_write_hook4",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_write_hook8_info = {
.func = NULL,
.name = "libafl_exec_write_hook8",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3)};
static TCGHelperInfo libafl_exec_write_hookN_info = {
.func = NULL,
.name = "libafl_exec_write_hookN",
.flags = dh_callflag(void),
.typemask = dh_typemask(void, 0) | dh_typemask(i64, 1) |
dh_typemask(i64, 2) | dh_typemask(tl, 3) | dh_typemask(i64, 4)};
GEN_REMOVE_HOOK(read)
GEN_REMOVE_HOOK(write)
size_t libafl_add_read_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data)
{
CPUState* cpu;
CPU_FOREACH(cpu) { tb_flush(cpu); }
struct libafl_rw_hook* hook = calloc(sizeof(struct libafl_rw_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;
hook->execN = execN;*/
hook->data = data;
hook->num = libafl_read_hooks_num++;
hook->next = libafl_read_hooks;
libafl_read_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_read_hook1_info,
sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_read_hook2_info,
sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_read_hook4_info,
sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_read_hook8_info,
sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
if (execN) {
memcpy(&hook->helper_infoN, &libafl_exec_read_hookN_info,
sizeof(TCGHelperInfo));
hook->helper_infoN.func = execN;
}
return hook->num;
}
size_t libafl_add_write_hook(
uint64_t (*gen)(uint64_t data, target_ulong pc, TCGTemp* addr, MemOpIdx oi),
void (*exec1)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec2)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec4)(uint64_t data, uint64_t id, target_ulong addr),
void (*exec8)(uint64_t data, uint64_t id, target_ulong addr),
void (*execN)(uint64_t data, uint64_t id, target_ulong addr, size_t size),
uint64_t data)
{
CPUState* cpu;
CPU_FOREACH(cpu) { tb_flush(cpu); }
struct libafl_rw_hook* hook = calloc(sizeof(struct libafl_rw_hook), 1);
hook->gen = gen;
/*hook->exec1 = exec1;
hook->exec2 = exec2;
hook->exec4 = exec4;
hook->exec8 = exec8;
hook->execN = execN;*/
hook->data = data;
hook->num = libafl_write_hooks_num++;
hook->next = libafl_write_hooks;
libafl_write_hooks = hook;
if (exec1) {
memcpy(&hook->helper_info1, &libafl_exec_write_hook1_info,
sizeof(TCGHelperInfo));
hook->helper_info1.func = exec1;
}
if (exec2) {
memcpy(&hook->helper_info2, &libafl_exec_write_hook2_info,
sizeof(TCGHelperInfo));
hook->helper_info2.func = exec2;
}
if (exec4) {
memcpy(&hook->helper_info4, &libafl_exec_write_hook4_info,
sizeof(TCGHelperInfo));
hook->helper_info4.func = exec4;
}
if (exec8) {
memcpy(&hook->helper_info8, &libafl_exec_write_hook8_info,
sizeof(TCGHelperInfo));
hook->helper_info8.func = exec8;
}
if (execN) {
memcpy(&hook->helper_infoN, &libafl_exec_write_hookN_info,
sizeof(TCGHelperInfo));
hook->helper_infoN.func = execN;
}
return hook->num;
}
static void libafl_gen_rw(TCGTemp* addr, MemOpIdx oi,
struct libafl_rw_hook* hook)
{
size_t size = memop_size(get_memop(oi));
while (hook) {
uint64_t cur_id = 0;
if (hook->gen)
cur_id = hook->gen(hook->data, libafl_gen_cur_pc, addr, oi);
TCGHelperInfo* info = NULL;
if (size == 1 && hook->helper_info1.func)
info = &hook->helper_info1;
else if (size == 2 && hook->helper_info2.func)
info = &hook->helper_info2;
else if (size == 4 && hook->helper_info4.func)
info = &hook->helper_info4;
else if (size == 8 && hook->helper_info8.func)
info = &hook->helper_info8;
if (cur_id != (uint64_t)-1) {
if (info) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGTemp* tmp2[3] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1),
addr};
tcg_gen_callN(info, NULL, tmp2);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
} else if (hook->helper_infoN.func) {
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
TCGv_i64 tmp1 = tcg_constant_i64(cur_id);
TCGv tmp2 = tcg_constant_tl(size);
TCGTemp* tmp3[4] = {tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1),
addr,
#if TARGET_LONG_BITS == 32
tcgv_i32_temp(tmp2)};
#else
tcgv_i64_temp(tmp2)};
#endif
tcg_gen_callN(&hook->helper_infoN, NULL, tmp3);
tcg_temp_free_i64(tmp0);
tcg_temp_free_i64(tmp1);
#if TARGET_LONG_BITS == 32
tcg_temp_free_i32(tmp2);
#else
tcg_temp_free_i64(tmp2);
#endif
}
}
hook = hook->next;
}
}
void libafl_gen_read(TCGTemp* addr, MemOpIdx oi)
{
libafl_gen_rw(addr, oi, libafl_read_hooks);
}
void libafl_gen_write(TCGTemp* addr, MemOpIdx oi)
{
libafl_gen_rw(addr, oi, libafl_write_hooks);
}

47
libafl/hooks/thread.c Normal file
View File

@ -0,0 +1,47 @@
#include "libafl/hooks/thread.h"
#include <linux/unistd.h>
extern __thread CPUArchState* libafl_qemu_env;
struct libafl_new_thread_hook* libafl_new_thread_hooks;
size_t libafl_new_thread_hooks_num = 0;
GEN_REMOVE_HOOK1(new_thread)
size_t libafl_add_new_thread_hook(bool (*callback)(uint64_t data,
CPUArchState* env,
uint32_t tid),
uint64_t data)
{
struct libafl_new_thread_hook* hook =
calloc(sizeof(struct libafl_new_thread_hook), 1);
hook->callback = callback;
hook->data = data;
hook->num = libafl_new_thread_hooks_num++;
hook->next = libafl_new_thread_hooks;
libafl_new_thread_hooks = hook;
return hook->num;
}
bool libafl_hook_new_thread_run(CPUArchState* env)
{
libafl_qemu_env = env;
if (libafl_new_thread_hooks) {
bool continue_execution = true;
int tid = gettid();
struct libafl_new_thread_hook* h = libafl_new_thread_hooks;
while (h) {
continue_execution =
h->callback(h->data, env, tid) && continue_execution;
h = h->next;
}
return continue_execution;
}
return true;
}

View File

@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
@ -26,9 +27,10 @@ pub extern "C" fn trace_edge_single(id: u64, _data: u64) {
// from libafl_targets coverage.rs // from libafl_targets coverage.rs
// correct size doesn't matter here // correct size doesn't matter here
uint8_t __afl_area_ptr_local[65536] __attribute__((weak)); uint8_t __afl_area_ptr_local[65536] __attribute__((weak));
size_t __afl_map_size __attribute__((weak)); size_t __afl_map_size __attribute__((weak));
size_t libafl_jit_trace_edge_hitcount(uint64_t data, uint64_t id) { size_t libafl_jit_trace_edge_hitcount(uint64_t data, uint64_t id)
{
TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local); TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local);
TCGv_i32 counter = tcg_temp_new_i32(); TCGv_i32 counter = tcg_temp_new_i32();
tcg_gen_ld8u_i32(counter, map_ptr, (tcg_target_long)id); tcg_gen_ld8u_i32(counter, map_ptr, (tcg_target_long)id);
@ -37,7 +39,8 @@ size_t libafl_jit_trace_edge_hitcount(uint64_t data, uint64_t id) {
return 3; // # instructions return 3; // # instructions
} }
size_t libafl_jit_trace_edge_single(uint64_t data, uint64_t id) { size_t libafl_jit_trace_edge_single(uint64_t data, uint64_t id)
{
TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local); TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local);
TCGv_i32 counter = tcg_temp_new_i32(); TCGv_i32 counter = tcg_temp_new_i32();
tcg_gen_movi_i32(counter, 1); tcg_gen_movi_i32(counter, 1);
@ -47,7 +50,8 @@ size_t libafl_jit_trace_edge_single(uint64_t data, uint64_t id) {
uint64_t __prev_loc = 0; uint64_t __prev_loc = 0;
size_t libafl_jit_trace_block_hitcount(uint64_t data, uint64_t id) { size_t libafl_jit_trace_block_hitcount(uint64_t data, uint64_t id)
{
TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local); TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local);
TCGv_ptr prev_loc_ptr = tcg_constant_ptr(&__prev_loc); TCGv_ptr prev_loc_ptr = tcg_constant_ptr(&__prev_loc);
@ -58,8 +62,8 @@ size_t libafl_jit_trace_block_hitcount(uint64_t data, uint64_t id) {
// Compute location => 5 insn // Compute location => 5 insn
tcg_gen_ld_i64(prev_loc, prev_loc_ptr, 0); tcg_gen_ld_i64(prev_loc, prev_loc_ptr, 0);
tcg_gen_xori_i64(prev_loc, prev_loc, (int64_t) id); tcg_gen_xori_i64(prev_loc, prev_loc, (int64_t)id);
tcg_gen_andi_i64(prev_loc, prev_loc, (int64_t) (__afl_map_size - 1)); tcg_gen_andi_i64(prev_loc, prev_loc, (int64_t)(__afl_map_size - 1));
tcg_gen_trunc_i64_ptr(prev_loc2, prev_loc); tcg_gen_trunc_i64_ptr(prev_loc2, prev_loc);
tcg_gen_add_ptr(prev_loc2, map_ptr, prev_loc2); tcg_gen_add_ptr(prev_loc2, map_ptr, prev_loc2);
@ -69,13 +73,14 @@ size_t libafl_jit_trace_block_hitcount(uint64_t data, uint64_t id) {
tcg_gen_st8_i32(counter, prev_loc2, 0); tcg_gen_st8_i32(counter, prev_loc2, 0);
// Update prev_loc => 3 insn // Update prev_loc => 3 insn
tcg_gen_movi_i64(id_r, (int64_t) id); tcg_gen_movi_i64(id_r, (int64_t)id);
tcg_gen_shri_i64(id_r, id_r, 1); tcg_gen_shri_i64(id_r, id_r, 1);
tcg_gen_st_i64(id_r, prev_loc_ptr, 0); tcg_gen_st_i64(id_r, prev_loc_ptr, 0);
return 11; // # instructions return 11; // # instructions
} }
size_t libafl_jit_trace_block_single(uint64_t data, uint64_t id) { size_t libafl_jit_trace_block_single(uint64_t data, uint64_t id)
{
TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local); TCGv_ptr map_ptr = tcg_constant_ptr(__afl_area_ptr_local);
TCGv_ptr prev_loc_ptr = tcg_constant_ptr(&__prev_loc); TCGv_ptr prev_loc_ptr = tcg_constant_ptr(&__prev_loc);
@ -86,8 +91,8 @@ size_t libafl_jit_trace_block_single(uint64_t data, uint64_t id) {
// Compute location => 5 insn // Compute location => 5 insn
tcg_gen_ld_i64(prev_loc, prev_loc_ptr, 0); tcg_gen_ld_i64(prev_loc, prev_loc_ptr, 0);
tcg_gen_xori_i64(prev_loc, prev_loc, (int64_t) id); tcg_gen_xori_i64(prev_loc, prev_loc, (int64_t)id);
tcg_gen_andi_i64(prev_loc, prev_loc, (int64_t) (__afl_map_size - 1)); tcg_gen_andi_i64(prev_loc, prev_loc, (int64_t)(__afl_map_size - 1));
tcg_gen_trunc_i64_ptr(prev_loc2, prev_loc); tcg_gen_trunc_i64_ptr(prev_loc2, prev_loc);
tcg_gen_add_ptr(map_ptr, map_ptr, prev_loc2); tcg_gen_add_ptr(map_ptr, map_ptr, prev_loc2);
@ -96,7 +101,7 @@ size_t libafl_jit_trace_block_single(uint64_t data, uint64_t id) {
tcg_gen_st8_i32(counter, map_ptr, 0); tcg_gen_st8_i32(counter, map_ptr, 0);
// Update prev_loc => 3 insn // Update prev_loc => 3 insn
tcg_gen_movi_i64(id_r, (int64_t) id); tcg_gen_movi_i64(id_r, (int64_t)id);
tcg_gen_shri_i64(id_r, id_r, 1); tcg_gen_shri_i64(id_r, id_r, 1);
tcg_gen_st_i64(id_r, prev_loc_ptr, 0); tcg_gen_st_i64(id_r, prev_loc_ptr, 0);
return 10; // # instructions return 10; // # instructions

View File

@ -1,14 +1,30 @@
specific_ss.add(files( specific_ss.add(files(
'exit.c', 'exit.c',
'hook.c', 'hook.c',
'jit.c', 'jit.c',
'utils.c', 'utils.c',
))
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( # TCG-related hooks
'syx-snapshot/device-save.c', 'hooks/tcg/backdoor.c',
'syx-snapshot/syx-snapshot.c', 'hooks/tcg/block.c',
'syx-snapshot/syx-cow-cache.c', 'hooks/tcg/cmp.c',
'syx-snapshot/channel-buffer-writeback.c', 'hooks/tcg/edge.c',
)]) 'hooks/tcg/instruction.c',
'hooks/tcg/read_write.c',
# General hooks
'hooks/cpu_run.c',
))
specific_ss.add(when : 'CONFIG_SOFTMMU', if_true : [files(
'syx-snapshot/device-save.c',
'syx-snapshot/syx-snapshot.c',
'syx-snapshot/syx-cow-cache.c',
'syx-snapshot/channel-buffer-writeback.c',
)])
specific_ss.add(when : 'CONFIG_USER_ONLY', if_true : [files(
'hooks/syscall.c',
'hooks/thread.c',
)])

View File

@ -4,13 +4,18 @@
#include "libafl/syx-misc.h" #include "libafl/syx-misc.h"
#include "libafl/syx-snapshot/channel-buffer-writeback.h" #include "libafl/syx-snapshot/channel-buffer-writeback.h"
QIOChannelBufferWriteback* qio_channel_buffer_writeback_new(size_t capacity, uint8_t* writeback_buf, size_t writeback_buf_capacity, size_t* writeback_buf_usage) { QIOChannelBufferWriteback*
qio_channel_buffer_writeback_new(size_t capacity, uint8_t* writeback_buf,
size_t writeback_buf_capacity,
size_t* writeback_buf_usage)
{
assert(writeback_buf != NULL); assert(writeback_buf != NULL);
assert(writeback_buf_usage != NULL); assert(writeback_buf_usage != NULL);
QIOChannelBufferWriteback *ioc; QIOChannelBufferWriteback* ioc;
ioc = QIO_CHANNEL_BUFFER_WRITEBACK(object_new(TYPE_QIO_CHANNEL_BUFFER_WRITEBACK)); ioc = QIO_CHANNEL_BUFFER_WRITEBACK(
object_new(TYPE_QIO_CHANNEL_BUFFER_WRITEBACK));
assert(writeback_buf != NULL); assert(writeback_buf != NULL);
@ -27,16 +32,19 @@ QIOChannelBufferWriteback* qio_channel_buffer_writeback_new(size_t capacity, uin
return ioc; return ioc;
} }
QIOChannelBufferWriteback* QIOChannelBufferWriteback* qio_channel_buffer_writeback_new_external(
qio_channel_buffer_writeback_new_external(uint8_t* buf, size_t capacity, size_t usage, uint8_t* writeback_buf, size_t writeback_buf_capacity, size_t* writeback_buf_usage) { uint8_t* buf, size_t capacity, size_t usage, uint8_t* writeback_buf,
size_t writeback_buf_capacity, size_t* writeback_buf_usage)
{
assert(buf != NULL); assert(buf != NULL);
assert(usage <= capacity); assert(usage <= capacity);
assert(writeback_buf != NULL); assert(writeback_buf != NULL);
assert(writeback_buf_usage != NULL); assert(writeback_buf_usage != NULL);
QIOChannelBufferWriteback *ioc; QIOChannelBufferWriteback* ioc;
ioc = QIO_CHANNEL_BUFFER_WRITEBACK(object_new(TYPE_QIO_CHANNEL_BUFFER_WRITEBACK)); ioc = QIO_CHANNEL_BUFFER_WRITEBACK(
object_new(TYPE_QIO_CHANNEL_BUFFER_WRITEBACK));
assert(writeback_buf != NULL); assert(writeback_buf != NULL);
@ -52,8 +60,8 @@ qio_channel_buffer_writeback_new_external(uint8_t* buf, size_t capacity, size_t
return ioc; return ioc;
} }
static void qio_channel_buffer_writeback_finalize(Object* obj)
static void qio_channel_buffer_writeback_finalize(Object *obj) { {
QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(obj); QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(obj);
assert(bwioc->writeback_buf_capacity >= bwioc->usage); assert(bwioc->writeback_buf_capacity >= bwioc->usage);
@ -70,14 +78,11 @@ static void qio_channel_buffer_writeback_finalize(Object *obj) {
} }
} }
static ssize_t qio_channel_buffer_writeback_readv(QIOChannel* ioc,
static ssize_t qio_channel_buffer_writeback_readv(QIOChannel *ioc, const struct iovec* iov,
const struct iovec *iov, size_t niov, int** fds,
size_t niov, size_t* nfds, int flags,
int **fds, Error** errp)
size_t *nfds,
int flags,
Error **errp)
{ {
QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc); QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc);
ssize_t ret = 0; ssize_t ret = 0;
@ -90,7 +95,7 @@ static ssize_t qio_channel_buffer_writeback_readv(QIOChannel *ioc,
break; break;
} }
if ((bwioc->offset + want) > bwioc->usage) { if ((bwioc->offset + want) > bwioc->usage) {
want = bwioc->usage - bwioc->offset; want = bwioc->usage - bwioc->offset;
} }
@ -103,12 +108,10 @@ static ssize_t qio_channel_buffer_writeback_readv(QIOChannel *ioc,
} }
static ssize_t qio_channel_buffer_writeback_writev(QIOChannel* ioc, static ssize_t qio_channel_buffer_writeback_writev(QIOChannel* ioc,
const struct iovec *iov, const struct iovec* iov,
size_t niov, size_t niov, int* fds,
int *fds, size_t nfds, int flags,
size_t nfds, Error** errp)
int flags,
Error **errp)
{ {
QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc); QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc);
ssize_t ret = 0; ssize_t ret = 0;
@ -123,9 +126,7 @@ static ssize_t qio_channel_buffer_writeback_writev(QIOChannel* ioc,
assert(bwioc->offset <= bwioc->usage); assert(bwioc->offset <= bwioc->usage);
for (i = 0; i < niov; i++) { for (i = 0; i < niov; i++) {
memcpy(bwioc->data + bwioc->offset, memcpy(bwioc->data + bwioc->offset, iov[i].iov_base, iov[i].iov_len);
iov[i].iov_base,
iov[i].iov_len);
bwioc->offset += iov[i].iov_len; bwioc->offset += iov[i].iov_len;
bwioc->usage += iov[i].iov_len; bwioc->usage += iov[i].iov_len;
ret += iov[i].iov_len; ret += iov[i].iov_len;
@ -134,33 +135,32 @@ static ssize_t qio_channel_buffer_writeback_writev(QIOChannel* ioc,
return ret; return ret;
} }
static int qio_channel_buffer_writeback_set_blocking(QIOChannel *ioc G_GNUC_UNUSED, static int
bool enabled G_GNUC_UNUSED, qio_channel_buffer_writeback_set_blocking(QIOChannel* ioc G_GNUC_UNUSED,
Error **errp G_GNUC_UNUSED) bool enabled G_GNUC_UNUSED,
Error** errp G_GNUC_UNUSED)
{ {
return 0; return 0;
} }
static off_t qio_channel_buffer_writeback_seek(QIOChannel *ioc, static off_t qio_channel_buffer_writeback_seek(QIOChannel* ioc, off_t offset,
off_t offset, int whence, Error** errp)
int whence,
Error **errp)
{ {
QIOChannelBufferWriteback *bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc); QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc);
off_t new_pos; off_t new_pos;
switch(whence) { switch (whence) {
case SEEK_SET: case SEEK_SET:
new_pos = offset; new_pos = offset;
break; break;
case SEEK_CUR: case SEEK_CUR:
new_pos = (off_t) bwioc->offset + offset; new_pos = (off_t)bwioc->offset + offset;
break; break;
case SEEK_END: case SEEK_END:
new_pos = (off_t) bwioc->usage + offset; new_pos = (off_t)bwioc->usage + offset;
break; break;
default: default:
assert(false); assert(false);
} }
assert(new_pos >= 0 && new_pos <= bwioc->usage); assert(new_pos >= 0 && new_pos <= bwioc->usage);
@ -170,8 +170,7 @@ static off_t qio_channel_buffer_writeback_seek(QIOChannel *ioc,
return new_pos; return new_pos;
} }
static int qio_channel_buffer_writeback_close(QIOChannel *ioc, static int qio_channel_buffer_writeback_close(QIOChannel* ioc, Error** errp)
Error **errp)
{ {
QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc); QIOChannelBufferWriteback* bwioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc);
@ -194,67 +193,65 @@ static int qio_channel_buffer_writeback_close(QIOChannel *ioc,
typedef struct QIOChannelBufferWritebackSource QIOChannelBufferWritebackSource; typedef struct QIOChannelBufferWritebackSource QIOChannelBufferWritebackSource;
struct QIOChannelBufferWritebackSource { struct QIOChannelBufferWritebackSource {
GSource parent; GSource parent;
QIOChannelBufferWriteback *bioc; QIOChannelBufferWriteback* bioc;
GIOCondition condition; GIOCondition condition;
}; };
static gboolean static gboolean qio_channel_buffer_writeback_source_prepare(GSource* source,
qio_channel_buffer_writeback_source_prepare(GSource *source, gint* timeout)
gint *timeout)
{ {
QIOChannelBufferWritebackSource *bsource = (QIOChannelBufferWritebackSource *)source; QIOChannelBufferWritebackSource* bsource =
(QIOChannelBufferWritebackSource*)source;
*timeout = -1; *timeout = -1;
return (G_IO_IN | G_IO_OUT) & bsource->condition; return (G_IO_IN | G_IO_OUT) & bsource->condition;
} }
static gboolean static gboolean qio_channel_buffer_writeback_source_check(GSource* source)
qio_channel_buffer_writeback_source_check(GSource *source)
{ {
QIOChannelBufferWritebackSource *bsource = (QIOChannelBufferWritebackSource *)source; QIOChannelBufferWritebackSource* bsource =
(QIOChannelBufferWritebackSource*)source;
return (G_IO_IN | G_IO_OUT) & bsource->condition; return (G_IO_IN | G_IO_OUT) & bsource->condition;
} }
static gboolean static gboolean qio_channel_buffer_writeback_source_dispatch(
qio_channel_buffer_writeback_source_dispatch(GSource *source, GSource* source, GSourceFunc callback, gpointer user_data)
GSourceFunc callback,
gpointer user_data)
{ {
QIOChannelFunc func = (QIOChannelFunc)callback; QIOChannelFunc func = (QIOChannelFunc)callback;
QIOChannelBufferWritebackSource *bsource = (QIOChannelBufferWritebackSource *)source; QIOChannelBufferWritebackSource* bsource =
(QIOChannelBufferWritebackSource*)source;
return (*func)(QIO_CHANNEL(bsource->bioc), return (*func)(QIO_CHANNEL(bsource->bioc),
((G_IO_IN | G_IO_OUT) & bsource->condition), ((G_IO_IN | G_IO_OUT) & bsource->condition), user_data);
user_data);
} }
static void static void qio_channel_buffer_writeback_source_finalize(GSource* source)
qio_channel_buffer_writeback_source_finalize(GSource *source)
{ {
QIOChannelBufferWritebackSource *ssource = (QIOChannelBufferWritebackSource *)source; QIOChannelBufferWritebackSource* ssource =
(QIOChannelBufferWritebackSource*)source;
object_unref(OBJECT(ssource->bioc)); object_unref(OBJECT(ssource->bioc));
} }
GSourceFuncs qio_channel_buffer_writeback_source_funcs = { GSourceFuncs qio_channel_buffer_writeback_source_funcs = {
qio_channel_buffer_writeback_source_prepare, qio_channel_buffer_writeback_source_prepare,
qio_channel_buffer_writeback_source_check, qio_channel_buffer_writeback_source_check,
qio_channel_buffer_writeback_source_dispatch, qio_channel_buffer_writeback_source_dispatch,
qio_channel_buffer_writeback_source_finalize qio_channel_buffer_writeback_source_finalize};
};
static GSource *qio_channel_buffer_writeback_create_watch(QIOChannel *ioc, static GSource*
GIOCondition condition) qio_channel_buffer_writeback_create_watch(QIOChannel* ioc,
GIOCondition condition)
{ {
QIOChannelBufferWriteback *bioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc); QIOChannelBufferWriteback* bioc = QIO_CHANNEL_BUFFER_WRITEBACK(ioc);
QIOChannelBufferWritebackSource *ssource; QIOChannelBufferWritebackSource* ssource;
GSource *source; GSource* source;
source = g_source_new(&qio_channel_buffer_writeback_source_funcs, source = g_source_new(&qio_channel_buffer_writeback_source_funcs,
sizeof(QIOChannelBufferWritebackSource)); sizeof(QIOChannelBufferWritebackSource));
ssource = (QIOChannelBufferWritebackSource *)source; ssource = (QIOChannelBufferWritebackSource*)source;
ssource->bioc = bioc; ssource->bioc = bioc;
object_ref(OBJECT(bioc)); object_ref(OBJECT(bioc));
@ -264,10 +261,11 @@ static GSource *qio_channel_buffer_writeback_create_watch(QIOChannel *ioc,
return source; return source;
} }
static void qio_channel_buffer_writeback_class_init(ObjectClass *klass, static void
void *class_data G_GNUC_UNUSED) qio_channel_buffer_writeback_class_init(ObjectClass* klass,
void* class_data G_GNUC_UNUSED)
{ {
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); QIOChannelClass* ioc_klass = QIO_CHANNEL_CLASS(klass);
ioc_klass->io_writev = qio_channel_buffer_writeback_writev; ioc_klass->io_writev = qio_channel_buffer_writeback_writev;
ioc_klass->io_readv = qio_channel_buffer_writeback_readv; ioc_klass->io_readv = qio_channel_buffer_writeback_readv;
@ -278,11 +276,11 @@ static void qio_channel_buffer_writeback_class_init(ObjectClass *klass,
} }
static const TypeInfo qio_channel_buffer_writeback_info = { static const TypeInfo qio_channel_buffer_writeback_info = {
.parent = TYPE_QIO_CHANNEL, .parent = TYPE_QIO_CHANNEL,
.name = TYPE_QIO_CHANNEL_BUFFER_WRITEBACK, .name = TYPE_QIO_CHANNEL_BUFFER_WRITEBACK,
.instance_size = sizeof(QIOChannelBufferWriteback), .instance_size = sizeof(QIOChannelBufferWriteback),
.instance_finalize = qio_channel_buffer_writeback_finalize, .instance_finalize = qio_channel_buffer_writeback_finalize,
.class_init = qio_channel_buffer_writeback_class_init, .class_init = qio_channel_buffer_writeback_class_init,
}; };
static void qio_channel_buffer_writeback_register_types(void) static void qio_channel_buffer_writeback_register_types(void)

View File

@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "migration/qemu-file.h" #include "migration/qemu-file.h"
#include "io/channel-buffer.h" #include "io/channel-buffer.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
@ -14,14 +15,16 @@ int libafl_restoring_devices;
extern SaveState savevm_state; extern SaveState savevm_state;
extern int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc); extern int vmstate_save(QEMUFile* f, SaveStateEntry* se, JSONWriter* vmdesc);
// iothread must be locked // iothread must be locked
DeviceSaveState* device_save_all(void) { DeviceSaveState* device_save_all(void)
{
return device_save_kind(DEVICE_SNAPSHOT_ALL, NULL); return device_save_kind(DEVICE_SNAPSHOT_ALL, NULL);
} }
static int is_in_list(char* str, char** list) { static int is_in_list(char* str, char** list)
{
while (*list) { while (*list) {
if (!strcmp(str, *list)) { if (!strcmp(str, *list)) {
return 1; return 1;
@ -31,19 +34,23 @@ static int is_in_list(char* str, char** list) {
return 0; return 0;
} }
DeviceSaveState* device_save_kind(DeviceSnapshotKind kind, char** names) { DeviceSaveState* device_save_kind(DeviceSnapshotKind kind, char** names)
{
DeviceSaveState* dss = g_new0(DeviceSaveState, 1); DeviceSaveState* dss = g_new0(DeviceSaveState, 1);
SaveStateEntry *se; SaveStateEntry* se;
dss->kind = DEVICE_SAVE_KIND_FULL; dss->kind = DEVICE_SAVE_KIND_FULL;
dss->save_buffer = g_new(uint8_t, QEMU_FILE_RAM_LIMIT); dss->save_buffer = g_new(uint8_t, QEMU_FILE_RAM_LIMIT);
QIOChannelBufferWriteback* wbioc = qio_channel_buffer_writeback_new(QEMU_FILE_RAM_LIMIT, dss->save_buffer, QEMU_FILE_RAM_LIMIT, &dss->save_buffer_size); QIOChannelBufferWriteback* wbioc = qio_channel_buffer_writeback_new(
QEMU_FILE_RAM_LIMIT, dss->save_buffer, QEMU_FILE_RAM_LIMIT,
&dss->save_buffer_size);
QIOChannel* ioc = QIO_CHANNEL(wbioc); QIOChannel* ioc = QIO_CHANNEL(wbioc);
QEMUFile* f = qemu_file_new_output(ioc); QEMUFile* f = qemu_file_new_output(ioc);
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { QTAILQ_FOREACH(se, &savevm_state.handlers, entry)
{
int ret; int ret;
if (se->is_ram) { if (se->is_ram) {
@ -53,18 +60,18 @@ DeviceSaveState* device_save_kind(DeviceSnapshotKind kind, char** names) {
continue; continue;
} }
switch (kind) { switch (kind) {
case DEVICE_SNAPSHOT_ALLOWLIST: case DEVICE_SNAPSHOT_ALLOWLIST:
if (!is_in_list(se->idstr, names)) { if (!is_in_list(se->idstr, names)) {
continue; continue;
} }
break; break;
case DEVICE_SNAPSHOT_DENYLIST: case DEVICE_SNAPSHOT_DENYLIST:
if (is_in_list(se->idstr, names)) { if (is_in_list(se->idstr, names)) {
continue; continue;
} }
break; break;
default: default:
break; break;
} }
// SYX_PRINTF("Saving section %s...\n", se->idstr); // SYX_PRINTF("Saving section %s...\n", se->idstr);
@ -84,16 +91,18 @@ DeviceSaveState* device_save_kind(DeviceSnapshotKind kind, char** names) {
return dss; return dss;
} }
void device_restore_all(DeviceSaveState* dss) { void device_restore_all(DeviceSaveState* dss)
{
assert(dss->save_buffer != NULL); assert(dss->save_buffer != NULL);
QIOChannelBuffer* bioc = qio_channel_buffer_new_external(dss->save_buffer, QEMU_FILE_RAM_LIMIT, dss->save_buffer_size); QIOChannelBuffer* bioc = qio_channel_buffer_new_external(
dss->save_buffer, QEMU_FILE_RAM_LIMIT, dss->save_buffer_size);
QIOChannel* ioc = QIO_CHANNEL(bioc); QIOChannel* ioc = QIO_CHANNEL(bioc);
QEMUFile* f = qemu_file_new_input(ioc); QEMUFile* f = qemu_file_new_input(ioc);
int save_libafl_restoring_devices = libafl_restoring_devices; int save_libafl_restoring_devices = libafl_restoring_devices;
libafl_restoring_devices = 1; libafl_restoring_devices = 1;
qemu_load_device_state(f); qemu_load_device_state(f);
@ -103,21 +112,19 @@ void device_restore_all(DeviceSaveState* dss) {
qemu_fclose(f); qemu_fclose(f);
} }
void device_free_all(DeviceSaveState* dss) { void device_free_all(DeviceSaveState* dss) { g_free(dss->save_buffer); }
g_free(dss->save_buffer);
}
char** device_list_all(void) { char** device_list_all(void)
SaveStateEntry *se; {
SaveStateEntry* se;
size_t size = 1; size_t size = 1;
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { size++; }
size++;
}
char** list = malloc(size * sizeof(char*)); char** list = malloc(size * sizeof(char*));
size_t i = 0; size_t i = 0;
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { QTAILQ_FOREACH(se, &savevm_state.handlers, entry)
{
if (se->is_ram) { if (se->is_ram) {
continue; continue;
} }

View File

@ -19,11 +19,13 @@ static gchar* g_array_element_ptr(GArray* array, guint position)
return array->data + position * g_array_get_element_size(array); return array->data + position * g_array_get_element_size(array);
} }
void syx_cow_cache_push_layer(SyxCowCache* scc, uint64_t chunk_size, uint64_t max_size) void syx_cow_cache_push_layer(SyxCowCache* scc, uint64_t chunk_size,
uint64_t max_size)
{ {
SyxCowCacheLayer* new_layer = g_new0(SyxCowCacheLayer, 1); SyxCowCacheLayer* new_layer = g_new0(SyxCowCacheLayer, 1);
new_layer->cow_cache_devices = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); new_layer->cow_cache_devices =
g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
new_layer->chunk_size = chunk_size; new_layer->chunk_size = chunk_size;
new_layer->max_nb_chunks = max_size; new_layer->max_nb_chunks = max_size;
@ -38,9 +40,10 @@ void syx_cow_cache_pop_layer(SyxCowCache* scc)
// TODO // TODO
} }
static void flush_device_layer(gpointer _blk_name_hash, gpointer cache_device, gpointer _user_data) static void flush_device_layer(gpointer _blk_name_hash, gpointer cache_device,
gpointer _user_data)
{ {
SyxCowCacheDevice* sccd = (SyxCowCacheDevice*) cache_device; SyxCowCacheDevice* sccd = (SyxCowCacheDevice*)cache_device;
g_hash_table_remove_all(sccd->positions); g_hash_table_remove_all(sccd->positions);
g_array_set_size(sccd->data, 0); g_array_set_size(sccd->data, 0);
@ -51,7 +54,8 @@ void syx_cow_cache_flush_highest_layer(SyxCowCache* scc)
SyxCowCacheLayer* highest_layer = QTAILQ_FIRST(&scc->layers); SyxCowCacheLayer* highest_layer = QTAILQ_FIRST(&scc->layers);
// highest_layer->cow_cache_devices // highest_layer->cow_cache_devices
g_hash_table_foreach(highest_layer->cow_cache_devices, flush_device_layer, NULL); g_hash_table_foreach(highest_layer->cow_cache_devices, flush_device_layer,
NULL);
} }
void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs) void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs)
@ -61,22 +65,30 @@ void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs)
*rhs = NULL; *rhs = NULL;
} }
static bool read_chunk_from_cache_layer_device(SyxCowCacheDevice* sccd, QEMUIOVector* qiov, size_t qiov_offset, uint64_t blk_offset) static bool read_chunk_from_cache_layer_device(SyxCowCacheDevice* sccd,
QEMUIOVector* qiov,
size_t qiov_offset,
uint64_t blk_offset)
{ {
gpointer data_position = NULL; gpointer data_position = NULL;
bool found = g_hash_table_lookup_extended(sccd->positions, GUINT_TO_POINTER(blk_offset), NULL, &data_position); bool found = g_hash_table_lookup_extended(
sccd->positions, GUINT_TO_POINTER(blk_offset), NULL, &data_position);
// cache hit // cache hit
if (found) { if (found) {
void* data_position_ptr = g_array_element_ptr(sccd->data, GPOINTER_TO_UINT(data_position)); void* data_position_ptr =
assert(qemu_iovec_from_buf(qiov, qiov_offset, data_position_ptr, g_array_get_element_size(sccd->data)) == g_array_get_element_size(sccd->data)); g_array_element_ptr(sccd->data, GPOINTER_TO_UINT(data_position));
assert(qemu_iovec_from_buf(qiov, qiov_offset, data_position_ptr,
g_array_get_element_size(sccd->data)) ==
g_array_get_element_size(sccd->data));
} }
return found; return found;
} }
// len must be smaller than nb bytes to next aligned to chunk of blk_offset. // len must be smaller than nb bytes to next aligned to chunk of blk_offset.
// static void write_to_cache_layer_device_unaligned(SyxCowCacheDevice* sccd, QEMUIOVector* qiov, size_t qiov_offset, uint64_t blk_offset, uint64_t len) // static void write_to_cache_layer_device_unaligned(SyxCowCacheDevice* sccd,
// QEMUIOVector* qiov, size_t qiov_offset, uint64_t blk_offset, uint64_t len)
// { // {
// const uint64_t chunk_size = g_array_get_element_size(sccd->data); // const uint64_t chunk_size = g_array_get_element_size(sccd->data);
// //
@ -86,45 +98,58 @@ static bool read_chunk_from_cache_layer_device(SyxCowCacheDevice* sccd, QEMUIOVe
// uint64_t blk_offset_aligned = ROUND_DOWN(blk_offset, chunk_size); // uint64_t blk_offset_aligned = ROUND_DOWN(blk_offset, chunk_size);
// //
// gpointer data_position = NULL; // gpointer data_position = NULL;
// bool found = g_hash_table_lookup_extended(sccd->positions, GUINT_TO_POINTER(blk_offset_aligned), NULL, &data_position); // bool found = g_hash_table_lookup_extended(sccd->positions,
// GUINT_TO_POINTER(blk_offset_aligned), NULL, &data_position);
// //
// if (!found) { // if (!found) {
// data_position = GUINT_TO_POINTER(sccd->data->len); // data_position = GUINT_TO_POINTER(sccd->data->len);
// sccd->data = g_array_set_size(sccd->data, sccd->data->len + 1); // sccd->data = g_array_set_size(sccd->data, sccd->data->len + 1);
// g_hash_table_insert(sccd->positions, GUINT_TO_POINTER(blk_offset), data_position); // g_hash_table_insert(sccd->positions, GUINT_TO_POINTER(blk_offset),
// data_position);
// } // }
// //
// void* data_position_ptr = g_array_element_ptr(sccd->data, GPOINTER_TO_UINT(data_position)); // void* data_position_ptr = g_array_element_ptr(sccd->data,
// GPOINTER_TO_UINT(data_position));
// //
// assert(qemu_iovec_to_buf(qiov, qiov_offset, data_position_ptr, g_array_get_element_size(sccd->data)) == // assert(qemu_iovec_to_buf(qiov, qiov_offset, data_position_ptr,
// g_array_get_element_size(sccd->data)) ==
// g_array_get_element_size(sccd->data)); // g_array_get_element_size(sccd->data));
// } // }
// cache layer is allocated and all the basic checks are already done. // cache layer is allocated and all the basic checks are already done.
static void write_chunk_to_cache_layer_device(SyxCowCacheDevice* sccd, QEMUIOVector* qiov, size_t qiov_offset, uint64_t blk_offset) static void write_chunk_to_cache_layer_device(SyxCowCacheDevice* sccd,
QEMUIOVector* qiov,
size_t qiov_offset,
uint64_t blk_offset)
{ {
const uint64_t chunk_size = g_array_get_element_size(sccd->data); const uint64_t chunk_size = g_array_get_element_size(sccd->data);
gpointer data_position = NULL; gpointer data_position = NULL;
bool found = g_hash_table_lookup_extended(sccd->positions, GUINT_TO_POINTER(blk_offset), NULL, &data_position); bool found = g_hash_table_lookup_extended(
sccd->positions, GUINT_TO_POINTER(blk_offset), NULL, &data_position);
if (!found) { if (!found) {
data_position = GUINT_TO_POINTER(sccd->data->len); data_position = GUINT_TO_POINTER(sccd->data->len);
sccd->data = g_array_set_size(sccd->data, sccd->data->len + 1); sccd->data = g_array_set_size(sccd->data, sccd->data->len + 1);
g_hash_table_insert(sccd->positions, GUINT_TO_POINTER(blk_offset), data_position); g_hash_table_insert(sccd->positions, GUINT_TO_POINTER(blk_offset),
data_position);
} }
void* data_position_ptr = g_array_element_ptr(sccd->data, GPOINTER_TO_UINT(data_position)); void* data_position_ptr =
g_array_element_ptr(sccd->data, GPOINTER_TO_UINT(data_position));
assert(qemu_iovec_to_buf(qiov, qiov_offset, data_position_ptr, chunk_size) == assert(qemu_iovec_to_buf(qiov, qiov_offset, data_position_ptr,
chunk_size); chunk_size) == chunk_size);
} }
static bool read_chunk_from_cache_layer(SyxCowCacheLayer* sccl, BlockBackend* blk, QEMUIOVector* qiov, size_t qiov_offset, uint64_t blk_offset) static bool read_chunk_from_cache_layer(SyxCowCacheLayer* sccl,
BlockBackend* blk, QEMUIOVector* qiov,
size_t qiov_offset, uint64_t blk_offset)
{ {
assert(!(qiov->size % sccl->chunk_size)); assert(!(qiov->size % sccl->chunk_size));
SyxCowCacheDevice* cache_entry = g_hash_table_lookup(sccl->cow_cache_devices, GINT_TO_POINTER(blk_name_hash(blk))); SyxCowCacheDevice* cache_entry = g_hash_table_lookup(
sccl->cow_cache_devices, GINT_TO_POINTER(blk_name_hash(blk)));
// return early if nothing is registered // return early if nothing is registered
if (!cache_entry) { if (!cache_entry) {
@ -134,62 +159,79 @@ static bool read_chunk_from_cache_layer(SyxCowCacheLayer* sccl, BlockBackend* bl
assert(cache_entry && cache_entry->data); assert(cache_entry && cache_entry->data);
// try to read cached pages in current layer if something is registered. // try to read cached pages in current layer if something is registered.
return read_chunk_from_cache_layer_device(cache_entry, qiov, qiov_offset, blk_offset); return read_chunk_from_cache_layer_device(cache_entry, qiov, qiov_offset,
blk_offset);
} }
// Returns false if could not write to current layer. // Returns false if could not write to current layer.
static bool write_to_cache_layer(SyxCowCacheLayer* sccl, BlockBackend* blk, int64_t offset, int64_t bytes, QEMUIOVector* qiov) static bool write_to_cache_layer(SyxCowCacheLayer* sccl, BlockBackend* blk,
int64_t offset, int64_t bytes,
QEMUIOVector* qiov)
{ {
if (qiov->size % sccl->chunk_size) { if (qiov->size % sccl->chunk_size) {
// todo: determine if it is worth developing an unaligned access version. // todo: determine if it is worth developing an unaligned access
printf("error: 0x%zx %% 0x%lx == 0x%lx\n", qiov->size, sccl->chunk_size, qiov->size % sccl->chunk_size); // version.
printf("error: 0x%zx %% 0x%lx == 0x%lx\n", qiov->size, sccl->chunk_size,
qiov->size % sccl->chunk_size);
exit(1); exit(1);
} }
SyxCowCacheDevice* cache_entry = g_hash_table_lookup(sccl->cow_cache_devices, GINT_TO_POINTER(blk_name_hash(blk))); SyxCowCacheDevice* cache_entry = g_hash_table_lookup(
sccl->cow_cache_devices, GINT_TO_POINTER(blk_name_hash(blk)));
if (unlikely(!cache_entry)) { if (unlikely(!cache_entry)) {
cache_entry = g_new0(SyxCowCacheDevice, 1); cache_entry = g_new0(SyxCowCacheDevice, 1);
cache_entry->data = g_array_sized_new(false, false, sccl->chunk_size, INITIAL_NB_CHUNKS_PER_DEVICE); cache_entry->data = g_array_sized_new(false, false, sccl->chunk_size,
cache_entry->positions = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); INITIAL_NB_CHUNKS_PER_DEVICE);
g_hash_table_insert(sccl->cow_cache_devices, GINT_TO_POINTER(blk_name_hash(blk)), cache_entry); cache_entry->positions =
g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
g_hash_table_insert(sccl->cow_cache_devices,
GINT_TO_POINTER(blk_name_hash(blk)), cache_entry);
} }
assert(cache_entry && cache_entry->data); assert(cache_entry && cache_entry->data);
if (cache_entry->data->len + (qiov->size / sccl->chunk_size) > sccl->max_nb_chunks) { if (cache_entry->data->len + (qiov->size / sccl->chunk_size) >
sccl->max_nb_chunks) {
return false; return false;
} }
// write cached page // write cached page
uint64_t blk_offset = offset; uint64_t blk_offset = offset;
size_t qiov_offset = 0; size_t qiov_offset = 0;
for (; qiov_offset < qiov->size; blk_offset += sccl->chunk_size, qiov_offset += sccl->chunk_size) { for (; qiov_offset < qiov->size;
write_chunk_to_cache_layer_device(cache_entry, qiov, qiov_offset, blk_offset); blk_offset += sccl->chunk_size, qiov_offset += sccl->chunk_size) {
write_chunk_to_cache_layer_device(cache_entry, qiov, qiov_offset,
blk_offset);
} }
return true; return true;
} }
void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t _qiov_offset, void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend* blk,
BdrvRequestFlags flags) int64_t offset, int64_t bytes, QEMUIOVector* qiov,
size_t _qiov_offset, BdrvRequestFlags flags)
{ {
SyxCowCacheLayer* layer; SyxCowCacheLayer* layer;
uint64_t blk_offset = offset; uint64_t blk_offset = offset;
size_t qiov_offset = 0; size_t qiov_offset = 0;
uint64_t chunk_size = 0; uint64_t chunk_size = 0;
// printf("[%s] Read 0x%zx bytes @addr %lx\n", blk_name(blk), qiov->size, offset); // printf("[%s] Read 0x%zx bytes @addr %lx\n", blk_name(blk), qiov->size,
// offset);
// First read the backing block device normally. // First read the backing block device normally.
assert(blk_co_preadv(blk, offset, bytes, qiov, flags) >= 0); assert(blk_co_preadv(blk, offset, bytes, qiov, flags) >= 0);
// Then fix the chunks that have been read from before. // Then fix the chunks that have been read from before.
if (!QTAILQ_EMPTY(&scc->layers)) { if (!QTAILQ_EMPTY(&scc->layers)) {
for (;qiov_offset < qiov->size; blk_offset += chunk_size, qiov_offset += chunk_size) { for (; qiov_offset < qiov->size;
QTAILQ_FOREACH(layer, &scc->layers, next) { blk_offset += chunk_size, qiov_offset += chunk_size) {
QTAILQ_FOREACH(layer, &scc->layers, next)
{
chunk_size = layer->chunk_size; chunk_size = layer->chunk_size;
if (read_chunk_from_cache_layer(layer, blk, qiov, qiov_offset, blk_offset)) { if (read_chunk_from_cache_layer(layer, blk, qiov, qiov_offset,
blk_offset)) {
break; break;
} }
} }
@ -197,12 +239,15 @@ void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend *blk, int64_t offse
} }
} }
bool syx_cow_cache_write_entry(SyxCowCache* scc, BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, bool syx_cow_cache_write_entry(SyxCowCache* scc, BlockBackend* blk,
BdrvRequestFlags flags) int64_t offset, int64_t bytes,
QEMUIOVector* qiov, size_t qiov_offset,
BdrvRequestFlags flags)
{ {
SyxCowCacheLayer* layer; SyxCowCacheLayer* layer;
// printf("[%s] Write 0x%zx bytes @addr %lx\n", blk_name(blk), qiov->size, offset); // printf("[%s] Write 0x%zx bytes @addr %lx\n", blk_name(blk), qiov->size,
// offset);
layer = QTAILQ_FIRST(&scc->layers); layer = QTAILQ_FIRST(&scc->layers);
if (layer) { if (layer) {

View File

@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
@ -11,16 +12,16 @@
#include "libafl/syx-snapshot/syx-snapshot.h" #include "libafl/syx-snapshot/syx-snapshot.h"
#include "libafl/syx-snapshot/device-save.h" #include "libafl/syx-snapshot/device-save.h"
#define SYX_SNAPSHOT_LIST_INIT_SIZE 4096 #define SYX_SNAPSHOT_LIST_INIT_SIZE 4096
#define SYX_SNAPSHOT_LIST_GROW_FACTOR 2 #define SYX_SNAPSHOT_LIST_GROW_FACTOR 2
#define TARGET_NEXT_PAGE_ADDR(p) \ #define TARGET_NEXT_PAGE_ADDR(p) \
((typeof(p))(((uintptr_t) p + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK)) ((typeof(p))(((uintptr_t)p + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK))
/** /**
* Saved ramblock * Saved ramblock
*/ */
typedef struct SyxSnapshotRAMBlock { typedef struct SyxSnapshotRAMBlock {
uint8_t *ram; // RAM block uint8_t* ram; // RAM block
uint64_t used_length; // Length of the ram block uint64_t used_length; // Length of the ram block
} SyxSnapshotRAMBlock; } SyxSnapshotRAMBlock;
@ -37,11 +38,11 @@ typedef struct SyxSnapshotRoot {
*/ */
typedef struct SyxSnapshotDirtyPage { typedef struct SyxSnapshotDirtyPage {
ram_addr_t offset_within_rb; ram_addr_t offset_within_rb;
uint8_t *data; uint8_t* data;
} SyxSnapshotDirtyPage; } SyxSnapshotDirtyPage;
typedef struct SyxSnapshotDirtyPageList { typedef struct SyxSnapshotDirtyPageList {
SyxSnapshotDirtyPage *dirty_pages; SyxSnapshotDirtyPage* dirty_pages;
uint64_t length; uint64_t length;
} SyxSnapshotDirtyPageList; } SyxSnapshotDirtyPageList;
@ -51,36 +52,57 @@ typedef struct SyxSnapshotDirtyPageList {
*/ */
typedef struct SyxSnapshotIncrement { typedef struct SyxSnapshotIncrement {
// Back to root snapshot if NULL // Back to root snapshot if NULL
struct SyxSnapshotIncrement *parent; struct SyxSnapshotIncrement* parent;
DeviceSaveState *dss; DeviceSaveState* dss;
GHashTable *rbs_dirty_pages; // hash map: H(rb) -> SyxSnapshotDirtyPageList GHashTable* rbs_dirty_pages; // hash map: H(rb) -> SyxSnapshotDirtyPageList
} SyxSnapshotIncrement; } SyxSnapshotIncrement;
SyxSnapshotState syx_snapshot_state = {0}; SyxSnapshotState syx_snapshot_state = {0};
static MemoryRegion* mr_to_enable = NULL; static MemoryRegion* mr_to_enable = NULL;
static void destroy_ramblock_snapshot(gpointer root_snapshot); static void destroy_ramblock_snapshot(gpointer root_snapshot);
static void syx_snapshot_dirty_list_flush(SyxSnapshot* snapshot); static void syx_snapshot_dirty_list_flush(SyxSnapshot* snapshot);
static void rb_save_dirty_addr_to_table(gpointer offset_within_rb, gpointer unused, gpointer rb_dirty_list_to_page_args_ptr); static void
static void rb_dirty_list_to_dirty_pages(gpointer rb_idstr_hash, gpointer rb_dirty_list_hash_table_ptr, gpointer rbs_dirty_pages_ptr); rb_save_dirty_addr_to_table(gpointer offset_within_rb, gpointer unused,
static inline void syx_snapshot_dirty_list_add_internal(RAMBlock* rb, ram_addr_t offset); gpointer rb_dirty_list_to_page_args_ptr);
static void empty_rb_dirty_list(gpointer rb_idstr_hash, gpointer rb_dirty_list_hash_table_ptr, gpointer user_data);
static void destroy_snapshot_dirty_page_list(gpointer snapshot_dirty_page_list_ptr);
static void root_restore_rb_page(gpointer offset_within_rb, gpointer unused, gpointer root_restore_args_ptr); static void rb_dirty_list_to_dirty_pages(gpointer rb_idstr_hash,
static void root_restore_rb(gpointer rb_idstr_hash, gpointer rb_dirty_pages_hash_table_ptr, gpointer snapshot_ptr); gpointer rb_dirty_list_hash_table_ptr,
static void root_restore_check_memory_rb(gpointer rb_idstr_hash, gpointer rb_dirty_pages_hash_table_ptr, gpointer snapshot_ptr); gpointer rbs_dirty_pages_ptr);
static SyxSnapshotIncrement* syx_snapshot_increment_free(SyxSnapshotIncrement* increment); static inline void syx_snapshot_dirty_list_add_internal(RAMBlock* rb,
ram_addr_t offset);
static void empty_rb_dirty_list(gpointer rb_idstr_hash,
gpointer rb_dirty_list_hash_table_ptr,
gpointer user_data);
static void
destroy_snapshot_dirty_page_list(gpointer snapshot_dirty_page_list_ptr);
static void root_restore_rb_page(gpointer offset_within_rb, gpointer unused,
gpointer root_restore_args_ptr);
static void root_restore_rb(gpointer rb_idstr_hash,
gpointer rb_dirty_pages_hash_table_ptr,
gpointer snapshot_ptr);
static void root_restore_check_memory_rb(gpointer rb_idstr_hash,
gpointer rb_dirty_pages_hash_table_ptr,
gpointer snapshot_ptr);
static SyxSnapshotIncrement*
syx_snapshot_increment_free(SyxSnapshotIncrement* increment);
static RAMBlock* ramblock_lookup(gpointer rb_idstr_hash) static RAMBlock* ramblock_lookup(gpointer rb_idstr_hash)
{ {
RAMBlock* block; RAMBlock* block;
RAMBLOCK_FOREACH(block) { RAMBLOCK_FOREACH(block)
{
if (rb_idstr_hash == GINT_TO_POINTER(block->idstr_hash)) { if (rb_idstr_hash == GINT_TO_POINTER(block->idstr_hash)) {
return block; return block;
} }
@ -90,7 +112,9 @@ static RAMBlock* ramblock_lookup(gpointer rb_idstr_hash)
} }
// Root snapshot API // Root snapshot API
static SyxSnapshotRoot* syx_snapshot_root_new(DeviceSnapshotKind kind, char** devices); static SyxSnapshotRoot* syx_snapshot_root_new(DeviceSnapshotKind kind,
char** devices);
static void syx_snapshot_root_free(SyxSnapshotRoot* root); static void syx_snapshot_root_free(SyxSnapshotRoot* root);
struct rb_dirty_list_to_page_args { struct rb_dirty_list_to_page_args {
@ -116,52 +140,55 @@ struct rb_page_increment_restore_args {
}; };
struct rb_check_memory_args { struct rb_check_memory_args {
SyxSnapshot* snapshot; // IN SyxSnapshot* snapshot; // IN
uint64_t nb_inconsistent_pages; // OUT uint64_t nb_inconsistent_pages; // OUT
}; };
void syx_snapshot_init(bool cached_bdrvs) { void syx_snapshot_init(bool cached_bdrvs)
{
uint64_t page_size = TARGET_PAGE_SIZE; uint64_t page_size = TARGET_PAGE_SIZE;
syx_snapshot_state.page_size = page_size; syx_snapshot_state.page_size = page_size;
syx_snapshot_state.page_mask = ((uint64_t) -1) << __builtin_ctz(page_size); syx_snapshot_state.page_mask = ((uint64_t)-1) << __builtin_ctz(page_size);
syx_snapshot_state.tracked_snapshots = syx_snapshot_tracker_init(); syx_snapshot_state.tracked_snapshots = syx_snapshot_tracker_init();
if (cached_bdrvs) { if (cached_bdrvs) {
syx_snapshot_state.before_fuzz_cache = syx_cow_cache_new(); syx_snapshot_state.before_fuzz_cache = syx_cow_cache_new();
syx_cow_cache_push_layer(syx_snapshot_state.before_fuzz_cache, SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE, SYX_SNAPSHOT_COW_CACHE_DEFAULT_MAX_BLOCKS); syx_cow_cache_push_layer(syx_snapshot_state.before_fuzz_cache,
SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE,
SYX_SNAPSHOT_COW_CACHE_DEFAULT_MAX_BLOCKS);
} }
syx_snapshot_state.is_enabled = false; syx_snapshot_state.is_enabled = false;
} }
SyxSnapshot *syx_snapshot_new(bool track, bool is_active_bdrv_cache, DeviceSnapshotKind kind, char **devices) { SyxSnapshot* syx_snapshot_new(bool track, bool is_active_bdrv_cache,
SyxSnapshot *snapshot = g_new0(SyxSnapshot, 1); DeviceSnapshotKind kind, char** devices)
{
SyxSnapshot* snapshot = g_new0(SyxSnapshot, 1);
snapshot->root_snapshot = syx_snapshot_root_new(kind, devices); snapshot->root_snapshot = syx_snapshot_root_new(kind, devices);
snapshot->last_incremental_snapshot = NULL; snapshot->last_incremental_snapshot = NULL;
snapshot->rbs_dirty_list = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, snapshot->rbs_dirty_list =
(GDestroyNotify) g_hash_table_remove_all); g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_hash_table_remove_all);
snapshot->bdrvs_cow_cache = syx_cow_cache_new(); snapshot->bdrvs_cow_cache = syx_cow_cache_new();
if (is_active_bdrv_cache) { if (is_active_bdrv_cache) {
syx_cow_cache_move(snapshot->bdrvs_cow_cache, &syx_snapshot_state.before_fuzz_cache); syx_cow_cache_move(snapshot->bdrvs_cow_cache,
&syx_snapshot_state.before_fuzz_cache);
syx_snapshot_state.active_bdrv_cache_snapshot = snapshot; syx_snapshot_state.active_bdrv_cache_snapshot = snapshot;
} else { } else {
syx_cow_cache_push_layer(snapshot->bdrvs_cow_cache, SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE, SYX_SNAPSHOT_COW_CACHE_DEFAULT_MAX_BLOCKS); syx_cow_cache_push_layer(snapshot->bdrvs_cow_cache,
SYX_SNAPSHOT_COW_CACHE_DEFAULT_CHUNK_SIZE,
SYX_SNAPSHOT_COW_CACHE_DEFAULT_MAX_BLOCKS);
} }
if (track) { if (track) {
syx_snapshot_track(&syx_snapshot_state.tracked_snapshots, snapshot); syx_snapshot_track(&syx_snapshot_state.tracked_snapshots, snapshot);
} }
#ifdef CONFIG_DEBUG_TCG
SYX_PRINTF("[Snapshot Creation] Checking snapshot memory consistency\n");
g_hash_table_foreach(snapshot->rbs_dirty_list, root_restore_check_memory_rb, snapshot);
SYX_PRINTF("[Snapshot Creation] Memory is consistent.\n");
#endif
syx_snapshot_state.is_enabled = true; syx_snapshot_state.is_enabled = true;
return snapshot; return snapshot;
@ -190,55 +217,67 @@ static void destroy_ramblock_snapshot(gpointer root_snapshot)
g_free(snapshot_rb); g_free(snapshot_rb);
} }
static SyxSnapshotRoot* syx_snapshot_root_new(DeviceSnapshotKind kind, char **devices) { static SyxSnapshotRoot* syx_snapshot_root_new(DeviceSnapshotKind kind,
char** devices)
{
SyxSnapshotRoot* root = g_new0(SyxSnapshotRoot, 1); SyxSnapshotRoot* root = g_new0(SyxSnapshotRoot, 1);
RAMBlock *block; RAMBlock* block;
RAMBlock *inner_block; RAMBlock* inner_block;
DeviceSaveState *dss = device_save_kind(kind, devices); DeviceSaveState* dss = device_save_kind(kind, devices);
root->rbs_snapshot = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_ramblock_snapshot); root->rbs_snapshot = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, destroy_ramblock_snapshot);
root->dss = dss; root->dss = dss;
RAMBLOCK_FOREACH(block) { RAMBLOCK_FOREACH(block)
RAMBLOCK_FOREACH(inner_block) { {
if (block != inner_block && inner_block->idstr_hash == block->idstr_hash) { RAMBLOCK_FOREACH(inner_block)
SYX_ERROR("Hash collision detected on RAMBlocks %s and %s, snapshotting will not work correctly.", {
if (block != inner_block &&
inner_block->idstr_hash == block->idstr_hash) {
SYX_ERROR("Hash collision detected on RAMBlocks %s and %s, "
"snapshotting will not work correctly.",
inner_block->idstr, block->idstr); inner_block->idstr, block->idstr);
exit(1); exit(1);
} }
} }
SyxSnapshotRAMBlock *snapshot_rb = g_new(SyxSnapshotRAMBlock, 1); SyxSnapshotRAMBlock* snapshot_rb = g_new(SyxSnapshotRAMBlock, 1);
snapshot_rb->used_length = block->used_length; snapshot_rb->used_length = block->used_length;
snapshot_rb->ram = g_new(uint8_t, block->used_length); snapshot_rb->ram = g_new(uint8_t, block->used_length);
memcpy(snapshot_rb->ram, block->host, block->used_length); memcpy(snapshot_rb->ram, block->host, block->used_length);
g_hash_table_insert(root->rbs_snapshot, GINT_TO_POINTER(block->idstr_hash), snapshot_rb); g_hash_table_insert(root->rbs_snapshot,
GINT_TO_POINTER(block->idstr_hash), snapshot_rb);
} }
return root; return root;
} }
static void syx_snapshot_root_free(SyxSnapshotRoot *root) { static void syx_snapshot_root_free(SyxSnapshotRoot* root)
{
g_hash_table_destroy(root->rbs_snapshot); g_hash_table_destroy(root->rbs_snapshot);
g_free(root); g_free(root);
} }
SyxSnapshotTracker syx_snapshot_tracker_init(void) { SyxSnapshotTracker syx_snapshot_tracker_init(void)
{
SyxSnapshotTracker tracker = { SyxSnapshotTracker tracker = {
.length = 0, .length = 0,
.capacity = SYX_SNAPSHOT_LIST_INIT_SIZE, .capacity = SYX_SNAPSHOT_LIST_INIT_SIZE,
.tracked_snapshots = g_new(SyxSnapshot*, SYX_SNAPSHOT_LIST_INIT_SIZE) .tracked_snapshots = g_new(SyxSnapshot*, SYX_SNAPSHOT_LIST_INIT_SIZE)};
};
return tracker; return tracker;
} }
void syx_snapshot_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot) { void syx_snapshot_track(SyxSnapshotTracker* tracker, SyxSnapshot* snapshot)
{
if (tracker->length == tracker->capacity) { if (tracker->length == tracker->capacity) {
tracker->capacity *= SYX_SNAPSHOT_LIST_GROW_FACTOR; tracker->capacity *= SYX_SNAPSHOT_LIST_GROW_FACTOR;
tracker->tracked_snapshots = g_realloc(tracker->tracked_snapshots, tracker->capacity * sizeof(SyxSnapshot *)); tracker->tracked_snapshots =
g_realloc(tracker->tracked_snapshots,
tracker->capacity * sizeof(SyxSnapshot*));
} }
assert(tracker->length < tracker->capacity); assert(tracker->length < tracker->capacity);
@ -247,11 +286,13 @@ void syx_snapshot_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot) {
tracker->length++; tracker->length++;
} }
void syx_snapshot_stop_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot) { void syx_snapshot_stop_track(SyxSnapshotTracker* tracker, SyxSnapshot* snapshot)
{
for (uint64_t i = 0; i < tracker->length; ++i) { for (uint64_t i = 0; i < tracker->length; ++i) {
if (tracker->tracked_snapshots[i] == snapshot) { if (tracker->tracked_snapshots[i] == snapshot) {
for (uint64_t j = i + i; j < tracker->length; ++j) { for (uint64_t j = i + i; j < tracker->length; ++j) {
tracker->tracked_snapshots[j - 1] = tracker->tracked_snapshots[j]; tracker->tracked_snapshots[j - 1] =
tracker->tracked_snapshots[j];
} }
tracker->length--; tracker->length--;
return; return;
@ -262,39 +303,45 @@ void syx_snapshot_stop_track(SyxSnapshotTracker *tracker, SyxSnapshot *snapshot)
abort(); abort();
} }
static void static void rb_save_dirty_addr_to_table(gpointer offset_within_rb,
rb_save_dirty_addr_to_table(gpointer offset_within_rb, gpointer unused, gpointer rb_dirty_list_to_page_args_ptr) { gpointer unused,
struct rb_dirty_list_to_page_args *args = rb_dirty_list_to_page_args_ptr; gpointer rb_dirty_list_to_page_args_ptr)
RAMBlock *rb = args->rb; {
SyxSnapshotDirtyPage *dirty_page = &args->dirty_page_list->dirty_pages[*args->table_idx]; struct rb_dirty_list_to_page_args* args = rb_dirty_list_to_page_args_ptr;
dirty_page->offset_within_rb = (ram_addr_t) offset_within_rb; RAMBlock* rb = args->rb;
SyxSnapshotDirtyPage* dirty_page =
&args->dirty_page_list->dirty_pages[*args->table_idx];
dirty_page->offset_within_rb = (ram_addr_t)offset_within_rb;
memcpy((gpointer) dirty_page->data, rb->host + (ram_addr_t) offset_within_rb, syx_snapshot_state.page_size); memcpy((gpointer)dirty_page->data, rb->host + (ram_addr_t)offset_within_rb,
syx_snapshot_state.page_size);
*args->table_idx += 1; *args->table_idx += 1;
} }
static void rb_dirty_list_to_dirty_pages(gpointer rb_idstr_hash, gpointer rb_dirty_list_hash_table_ptr, static void rb_dirty_list_to_dirty_pages(gpointer rb_idstr_hash,
gpointer rbs_dirty_pages_ptr) { gpointer rb_dirty_list_hash_table_ptr,
GHashTable *rbs_dirty_pages = rbs_dirty_pages_ptr; gpointer rbs_dirty_pages_ptr)
GHashTable *rb_dirty_list = rb_dirty_list_hash_table_ptr; {
GHashTable* rbs_dirty_pages = rbs_dirty_pages_ptr;
GHashTable* rb_dirty_list = rb_dirty_list_hash_table_ptr;
RAMBlock *rb = ramblock_lookup(rb_idstr_hash); RAMBlock* rb = ramblock_lookup(rb_idstr_hash);
if (rb) { if (rb) {
SyxSnapshotDirtyPageList *dirty_page_list = g_new(SyxSnapshotDirtyPageList, 1); SyxSnapshotDirtyPageList* dirty_page_list =
g_new(SyxSnapshotDirtyPageList, 1);
dirty_page_list->length = g_hash_table_size(rb_dirty_list); dirty_page_list->length = g_hash_table_size(rb_dirty_list);
dirty_page_list->dirty_pages = g_new(SyxSnapshotDirtyPage, dirty_page_list->length); dirty_page_list->dirty_pages =
g_new(SyxSnapshotDirtyPage, dirty_page_list->length);
uint64_t *ctr = g_new0(uint64_t, 1); uint64_t* ctr = g_new0(uint64_t, 1);
struct rb_dirty_list_to_page_args dirty_list_to_page_args = { struct rb_dirty_list_to_page_args dirty_list_to_page_args = {
.rb = rb, .rb = rb, .table_idx = ctr, .dirty_page_list = dirty_page_list};
.table_idx = ctr,
.dirty_page_list = dirty_page_list
};
g_hash_table_foreach(rbs_dirty_pages, rb_save_dirty_addr_to_table, &dirty_list_to_page_args); g_hash_table_foreach(rbs_dirty_pages, rb_save_dirty_addr_to_table,
&dirty_list_to_page_args);
g_free(dirty_list_to_page_args.table_idx); g_free(dirty_list_to_page_args.table_idx);
} else { } else {
@ -302,8 +349,11 @@ static void rb_dirty_list_to_dirty_pages(gpointer rb_idstr_hash, gpointer rb_dir
} }
} }
static void destroy_snapshot_dirty_page_list(gpointer snapshot_dirty_page_list_ptr) { static void
SyxSnapshotDirtyPageList *snapshot_dirty_page_list = snapshot_dirty_page_list_ptr; destroy_snapshot_dirty_page_list(gpointer snapshot_dirty_page_list_ptr)
{
SyxSnapshotDirtyPageList* snapshot_dirty_page_list =
snapshot_dirty_page_list_ptr;
for (uint64_t i = 0; i < snapshot_dirty_page_list->length; ++i) { for (uint64_t i = 0; i < snapshot_dirty_page_list->length; ++i) {
g_free(snapshot_dirty_page_list->dirty_pages[i].data); g_free(snapshot_dirty_page_list->dirty_pages[i].data);
@ -313,26 +363,32 @@ static void destroy_snapshot_dirty_page_list(gpointer snapshot_dirty_page_list_p
g_free(snapshot_dirty_page_list); g_free(snapshot_dirty_page_list);
} }
void syx_snapshot_increment_push(SyxSnapshot *snapshot, DeviceSnapshotKind kind, char **devices) { void syx_snapshot_increment_push(SyxSnapshot* snapshot, DeviceSnapshotKind kind,
SyxSnapshotIncrement *increment = g_new0(SyxSnapshotIncrement, 1); char** devices)
{
SyxSnapshotIncrement* increment = g_new0(SyxSnapshotIncrement, 1);
increment->parent = snapshot->last_incremental_snapshot; increment->parent = snapshot->last_incremental_snapshot;
snapshot->last_incremental_snapshot = increment; snapshot->last_incremental_snapshot = increment;
increment->rbs_dirty_pages = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, increment->rbs_dirty_pages = g_hash_table_new_full(
destroy_snapshot_dirty_page_list); g_direct_hash, g_direct_equal, NULL, destroy_snapshot_dirty_page_list);
g_hash_table_foreach(snapshot->rbs_dirty_list, rb_dirty_list_to_dirty_pages, increment->rbs_dirty_pages); g_hash_table_foreach(snapshot->rbs_dirty_list, rb_dirty_list_to_dirty_pages,
increment->rbs_dirty_pages);
increment->dss = device_save_kind(kind, devices); increment->dss = device_save_kind(kind, devices);
g_hash_table_remove_all(snapshot->rbs_dirty_list); g_hash_table_remove_all(snapshot->rbs_dirty_list);
} }
static SyxSnapshotDirtyPage * static SyxSnapshotDirtyPage*
get_dirty_page_from_addr_rec(SyxSnapshotIncrement *increment, RAMBlock *rb, ram_addr_t offset_within_rb) { get_dirty_page_from_addr_rec(SyxSnapshotIncrement* increment, RAMBlock* rb,
ram_addr_t offset_within_rb)
{
if (increment == NULL) { if (increment == NULL) {
return NULL; return NULL;
} }
SyxSnapshotDirtyPageList *dpl = g_hash_table_lookup(increment->rbs_dirty_pages, GINT_TO_POINTER(rb->idstr_hash)); SyxSnapshotDirtyPageList* dpl = g_hash_table_lookup(
increment->rbs_dirty_pages, GINT_TO_POINTER(rb->idstr_hash));
if (dpl) { if (dpl) {
for (uint64_t i = 0; i < dpl->length; ++i) { for (uint64_t i = 0; i < dpl->length; ++i) {
@ -342,55 +398,62 @@ get_dirty_page_from_addr_rec(SyxSnapshotIncrement *increment, RAMBlock *rb, ram_
} }
} }
return get_dirty_page_from_addr_rec(increment->parent, rb, offset_within_rb); return get_dirty_page_from_addr_rec(increment->parent, rb,
offset_within_rb);
} }
static void restore_dirty_page_to_increment(gpointer offset_within_rb,
gpointer _unused, gpointer args_ptr)
{
struct rb_page_increment_restore_args* args = args_ptr;
RAMBlock* rb = args->rb;
SyxSnapshot* snapshot = args->snapshot;
SyxSnapshotIncrement* increment = args->increment;
ram_addr_t offset = (ram_addr_t)offset_within_rb;
static void restore_dirty_page_to_increment(gpointer offset_within_rb, gpointer _unused, gpointer args_ptr) { SyxSnapshotDirtyPage* dp =
struct rb_page_increment_restore_args *args = args_ptr; get_dirty_page_from_addr_rec(increment, rb, offset);
RAMBlock *rb = args->rb;
SyxSnapshot *snapshot = args->snapshot;
SyxSnapshotIncrement *increment = args->increment;
ram_addr_t offset = (ram_addr_t) offset_within_rb;
SyxSnapshotDirtyPage *dp = get_dirty_page_from_addr_rec(increment, rb, offset);
if (dp) { if (dp) {
memcpy(rb->host + offset, dp->data, syx_snapshot_state.page_size); memcpy(rb->host + offset, dp->data, syx_snapshot_state.page_size);
} else { } else {
SyxSnapshotRAMBlock *rrb = g_hash_table_lookup(snapshot->root_snapshot->rbs_snapshot, SyxSnapshotRAMBlock* rrb =
GINT_TO_POINTER(rb->idstr_hash)); g_hash_table_lookup(snapshot->root_snapshot->rbs_snapshot,
GINT_TO_POINTER(rb->idstr_hash));
assert(rrb); assert(rrb);
memcpy(rb->host + offset, rrb->ram, syx_snapshot_state.page_size); memcpy(rb->host + offset, rrb->ram, syx_snapshot_state.page_size);
} }
} }
static void restore_rb_to_increment(gpointer rb_idstr_hash, gpointer rb_dirty_pages_hash_table_ptr, gpointer args_ptr) { static void restore_rb_to_increment(gpointer rb_idstr_hash,
struct rb_increment_restore_args *args = args_ptr; gpointer rb_dirty_pages_hash_table_ptr,
GHashTable *rb_dirty_pages_hash_table = rb_dirty_pages_hash_table_ptr; gpointer args_ptr)
{
struct rb_increment_restore_args* args = args_ptr;
GHashTable* rb_dirty_pages_hash_table = rb_dirty_pages_hash_table_ptr;
RAMBlock *rb = ramblock_lookup(rb_idstr_hash); RAMBlock* rb = ramblock_lookup(rb_idstr_hash);
struct rb_page_increment_restore_args page_args = { struct rb_page_increment_restore_args page_args = {
.snapshot = args->snapshot, .snapshot = args->snapshot, .increment = args->increment, .rb = rb};
.increment = args->increment,
.rb = rb
};
g_hash_table_foreach(rb_dirty_pages_hash_table, restore_dirty_page_to_increment, &page_args); g_hash_table_foreach(rb_dirty_pages_hash_table,
restore_dirty_page_to_increment, &page_args);
} }
static void restore_to_increment(SyxSnapshot *snapshot, SyxSnapshotIncrement *increment) { static void restore_to_increment(SyxSnapshot* snapshot,
struct rb_increment_restore_args args = { SyxSnapshotIncrement* increment)
.snapshot = snapshot, {
.increment = increment struct rb_increment_restore_args args = {.snapshot = snapshot,
}; .increment = increment};
g_hash_table_foreach(snapshot->rbs_dirty_list, restore_rb_to_increment, &args); g_hash_table_foreach(snapshot->rbs_dirty_list, restore_rb_to_increment,
&args);
} }
void syx_snapshot_increment_pop(SyxSnapshot *snapshot) { void syx_snapshot_increment_pop(SyxSnapshot* snapshot)
SyxSnapshotIncrement *last_increment = snapshot->last_incremental_snapshot; {
SyxSnapshotIncrement* last_increment = snapshot->last_incremental_snapshot;
device_restore_all(last_increment->dss); device_restore_all(last_increment->dss);
restore_to_increment(snapshot, last_increment); restore_to_increment(snapshot, last_increment);
@ -401,8 +464,9 @@ void syx_snapshot_increment_pop(SyxSnapshot *snapshot) {
syx_snapshot_dirty_list_flush(snapshot); syx_snapshot_dirty_list_flush(snapshot);
} }
void syx_snapshot_increment_restore_last(SyxSnapshot *snapshot) { void syx_snapshot_increment_restore_last(SyxSnapshot* snapshot)
SyxSnapshotIncrement *last_increment = snapshot->last_incremental_snapshot; {
SyxSnapshotIncrement* last_increment = snapshot->last_incremental_snapshot;
device_restore_all(last_increment->dss); device_restore_all(last_increment->dss);
restore_to_increment(snapshot, last_increment); restore_to_increment(snapshot, last_increment);
@ -410,51 +474,62 @@ void syx_snapshot_increment_restore_last(SyxSnapshot *snapshot) {
syx_snapshot_dirty_list_flush(snapshot); syx_snapshot_dirty_list_flush(snapshot);
} }
static SyxSnapshotIncrement *syx_snapshot_increment_free(SyxSnapshotIncrement *increment) { static SyxSnapshotIncrement*
SyxSnapshotIncrement *parent_increment = increment->parent; syx_snapshot_increment_free(SyxSnapshotIncrement* increment)
{
SyxSnapshotIncrement* parent_increment = increment->parent;
g_hash_table_destroy(increment->rbs_dirty_pages); g_hash_table_destroy(increment->rbs_dirty_pages);
device_free_all(increment->dss); device_free_all(increment->dss);
g_free(increment); g_free(increment);
return parent_increment; return parent_increment;
} }
static void syx_snapshot_dirty_list_flush(SyxSnapshot *snapshot) { static void syx_snapshot_dirty_list_flush(SyxSnapshot* snapshot)
g_hash_table_foreach(snapshot->rbs_dirty_list, empty_rb_dirty_list, (gpointer) snapshot); {
g_hash_table_foreach(snapshot->rbs_dirty_list, empty_rb_dirty_list,
(gpointer)snapshot);
} }
static inline void syx_snapshot_dirty_list_add_internal(RAMBlock *rb, ram_addr_t offset) { static inline void syx_snapshot_dirty_list_add_internal(RAMBlock* rb,
assert((offset & syx_snapshot_state.page_mask) == offset); // offsets should always be page-aligned. ram_addr_t offset)
{
assert((offset & syx_snapshot_state.page_mask) ==
offset); // offsets should always be page-aligned.
for (uint64_t i = 0; i < syx_snapshot_state.tracked_snapshots.length; ++i) { for (uint64_t i = 0; i < syx_snapshot_state.tracked_snapshots.length; ++i) {
SyxSnapshot *snapshot = syx_snapshot_state.tracked_snapshots.tracked_snapshots[i]; SyxSnapshot* snapshot =
syx_snapshot_state.tracked_snapshots.tracked_snapshots[i];
GHashTable *rb_dirty_list = g_hash_table_lookup(snapshot->rbs_dirty_list, GINT_TO_POINTER(rb->idstr_hash)); GHashTable* rb_dirty_list = g_hash_table_lookup(
snapshot->rbs_dirty_list, GINT_TO_POINTER(rb->idstr_hash));
if (unlikely(!rb_dirty_list)) { if (unlikely(!rb_dirty_list)) {
#ifdef SYX_SNAPSHOT_DEBUG #ifdef SYX_SNAPSHOT_DEBUG
printf("rb_dirty_list did not exit, creating...\n"); printf("rb_dirty_list did not exit, creating...\n");
#endif #endif
rb_dirty_list = g_hash_table_new(g_direct_hash, g_direct_equal); rb_dirty_list = g_hash_table_new(g_direct_hash, g_direct_equal);
g_hash_table_insert(snapshot->rbs_dirty_list, GINT_TO_POINTER(rb->idstr_hash), rb_dirty_list); g_hash_table_insert(snapshot->rbs_dirty_list,
GINT_TO_POINTER(rb->idstr_hash), rb_dirty_list);
} }
if (g_hash_table_add(rb_dirty_list, GINT_TO_POINTER(offset))) { if (g_hash_table_add(rb_dirty_list, GINT_TO_POINTER(offset))) {
#ifdef SYX_SNAPSHOT_DEBUG #ifdef SYX_SNAPSHOT_DEBUG
SYX_PRINTF("[%s] Marking offset 0x%lx as dirty\n", rb->idstr, offset); SYX_PRINTF("[%s] Marking offset 0x%lx as dirty\n", rb->idstr,
offset);
#endif #endif
} }
} }
} }
bool syx_snapshot_is_enabled(void) { bool syx_snapshot_is_enabled(void) { return syx_snapshot_state.is_enabled; }
return syx_snapshot_state.is_enabled;
}
/* /*
// TODO: Check if using this method is better for performances. // TODO: Check if using this method is better for performances.
// The implementation is pretty bad, it would be nice to store host addr directly for // The implementation is pretty bad, it would be nice to store host addr
directly for
// the memcopy happening later on. // the memcopy happening later on.
__attribute__((target("no-3dnow,no-sse,no-mmx"),no_caller_saved_registers)) void syx_snapshot_dirty_list_add_tcg_target(uint64_t dummy, void* host_addr) { __attribute__((target("no-3dnow,no-sse,no-mmx"),no_caller_saved_registers)) void
syx_snapshot_dirty_list_add_tcg_target(uint64_t dummy, void* host_addr) {
// early check to know whether we should log the page access or not // early check to know whether we should log the page access or not
if (!syx_snapshot_is_enabled()) { if (!syx_snapshot_is_enabled()) {
return; return;
@ -472,14 +547,15 @@ __attribute__((target("no-3dnow,no-sse,no-mmx"),no_caller_saved_registers)) void
*/ */
// host_addr should be page-aligned. // host_addr should be page-aligned.
void syx_snapshot_dirty_list_add_hostaddr(void *host_addr) { void syx_snapshot_dirty_list_add_hostaddr(void* host_addr)
{
// early check to know whether we should log the page access or not // early check to know whether we should log the page access or not
if (!syx_snapshot_is_enabled()) { if (!syx_snapshot_is_enabled()) {
return; return;
} }
ram_addr_t offset; ram_addr_t offset;
RAMBlock *rb = qemu_ram_block_from_host((void *) host_addr, true, &offset); RAMBlock* rb = qemu_ram_block_from_host((void*)host_addr, true, &offset);
#ifdef SYX_SNAPSHOT_DEBUG #ifdef SYX_SNAPSHOT_DEBUG
SYX_PRINTF("Should mark offset 0x%lx as dirty\n", offset); SYX_PRINTF("Should mark offset 0x%lx as dirty\n", offset);
@ -492,17 +568,19 @@ void syx_snapshot_dirty_list_add_hostaddr(void *host_addr) {
syx_snapshot_dirty_list_add_internal(rb, offset); syx_snapshot_dirty_list_add_internal(rb, offset);
} }
void syx_snapshot_dirty_list_add_hostaddr_range(void *host_addr, uint64_t len) { void syx_snapshot_dirty_list_add_hostaddr_range(void* host_addr, uint64_t len)
{
// early check to know whether we should log the page access or not // early check to know whether we should log the page access or not
if (!syx_snapshot_is_enabled()) { if (!syx_snapshot_is_enabled()) {
return; return;
} }
assert(len < INT64_MAX); assert(len < INT64_MAX);
int64_t len_signed = (int64_t) len; int64_t len_signed = (int64_t)len;
syx_snapshot_dirty_list_add_hostaddr(QEMU_ALIGN_PTR_DOWN(host_addr, syx_snapshot_state.page_size)); syx_snapshot_dirty_list_add_hostaddr(
void *next_page_addr = TARGET_NEXT_PAGE_ADDR(host_addr); QEMU_ALIGN_PTR_DOWN(host_addr, syx_snapshot_state.page_size));
void* next_page_addr = TARGET_NEXT_PAGE_ADDR(host_addr);
assert(next_page_addr > host_addr); assert(next_page_addr > host_addr);
assert(QEMU_PTR_IS_ALIGNED(next_page_addr, TARGET_PAGE_SIZE)); assert(QEMU_PTR_IS_ALIGNED(next_page_addr, TARGET_PAGE_SIZE));
@ -519,73 +597,79 @@ void syx_snapshot_dirty_list_add_hostaddr_range(void *host_addr, uint64_t len) {
} }
} }
static void empty_rb_dirty_list(gpointer _rb_idstr_hash, gpointer rb_dirty_list_hash_table_ptr, gpointer _user_data) { static void empty_rb_dirty_list(gpointer _rb_idstr_hash,
GHashTable *rb_dirty_hash_table = rb_dirty_list_hash_table_ptr; gpointer rb_dirty_list_hash_table_ptr,
gpointer _user_data)
{
GHashTable* rb_dirty_hash_table = rb_dirty_list_hash_table_ptr;
g_hash_table_remove_all(rb_dirty_hash_table); g_hash_table_remove_all(rb_dirty_hash_table);
} }
static void root_restore_rb_page(gpointer offset_within_rb, gpointer _unused, gpointer root_restore_args_ptr) { static void root_restore_rb_page(gpointer offset_within_rb, gpointer _unused,
struct rb_page_root_restore_args *args = root_restore_args_ptr; gpointer root_restore_args_ptr)
RAMBlock *rb = args->rb; {
SyxSnapshotRAMBlock *snapshot_rb = args->snapshot_rb; struct rb_page_root_restore_args* args = root_restore_args_ptr;
RAMBlock* rb = args->rb;
SyxSnapshotRAMBlock* snapshot_rb = args->snapshot_rb;
// safe cast because ram_addr_t is also an alias to void* // safe cast because ram_addr_t is also an alias to void*
void *host_rb_restore = rb->host + (ram_addr_t) offset_within_rb; void* host_rb_restore = rb->host + (ram_addr_t)offset_within_rb;
void *host_snapshot_rb_restore = (gpointer) snapshot_rb->ram + (ram_addr_t) offset_within_rb; void* host_snapshot_rb_restore =
(gpointer)snapshot_rb->ram + (ram_addr_t)offset_within_rb;
#ifdef SYX_SNAPSHOT_DEBUG #ifdef SYX_SNAPSHOT_DEBUG
SYX_PRINTF("\t[%s] Restore at offset 0x%lx of size %lu...\n", rb->idstr, (uint64_t) offset_within_rb, syx_snapshot_state.page_size); SYX_PRINTF("\t[%s] Restore at offset 0x%lx of size %lu...\n", rb->idstr,
(uint64_t)offset_within_rb, syx_snapshot_state.page_size);
#endif #endif
memcpy(host_rb_restore, host_snapshot_rb_restore, syx_snapshot_state.page_size); memcpy(host_rb_restore, host_snapshot_rb_restore,
//TODO: manage special case of TSEG. syx_snapshot_state.page_size);
// TODO: manage special case of TSEG.
} }
static void root_restore_rb(gpointer rb_idstr_hash, gpointer rb_dirty_pages_hash_table_ptr, gpointer snapshot_ptr) { static void root_restore_rb(gpointer rb_idstr_hash,
SyxSnapshot *snapshot = snapshot_ptr; gpointer rb_dirty_pages_hash_table_ptr,
GHashTable *rb_dirty_pages_hash_table = rb_dirty_pages_hash_table_ptr; gpointer snapshot_ptr)
RAMBlock *rb = ramblock_lookup(rb_idstr_hash); {
SyxSnapshot* snapshot = snapshot_ptr;
GHashTable* rb_dirty_pages_hash_table = rb_dirty_pages_hash_table_ptr;
RAMBlock* rb = ramblock_lookup(rb_idstr_hash);
if (rb) { if (rb) {
SyxSnapshotRAMBlock *snapshot_ramblock = g_hash_table_lookup(snapshot->root_snapshot->rbs_snapshot, SyxSnapshotRAMBlock* snapshot_ramblock = g_hash_table_lookup(
rb_idstr_hash); snapshot->root_snapshot->rbs_snapshot, rb_idstr_hash);
struct rb_page_root_restore_args root_restore_args = { struct rb_page_root_restore_args root_restore_args = {
.rb = rb, .rb = rb, .snapshot_rb = snapshot_ramblock};
.snapshot_rb = snapshot_ramblock
};
#ifdef CONFIG_DEBUG_TCG g_hash_table_foreach(rb_dirty_pages_hash_table, root_restore_rb_page,
SYX_PRINTF("Restoring RB %s...\n", rb->idstr); &root_restore_args);
#endif
g_hash_table_foreach(rb_dirty_pages_hash_table, root_restore_rb_page, &root_restore_args);
#ifdef CONFIG_DEBUG_TCG
SYX_PRINTF("Finished to restore RB %s\n", rb->idstr);
#endif
} else { } else {
SYX_ERROR("Saved RAMBlock not found."); SYX_ERROR("Saved RAMBlock not found.");
exit(1); exit(1);
} }
} }
static void root_restore_check_memory_rb(gpointer rb_idstr_hash, gpointer rb_dirty_pages_hash_table_ptr, static void root_restore_check_memory_rb(gpointer rb_idstr_hash,
gpointer check_memory_args_ptr) { gpointer rb_dirty_pages_hash_table_ptr,
struct rb_check_memory_args *args = check_memory_args_ptr; gpointer check_memory_args_ptr)
SyxSnapshot *snapshot = args->snapshot; {
RAMBlock *rb = ramblock_lookup(rb_idstr_hash); struct rb_check_memory_args* args = check_memory_args_ptr;
SyxSnapshot* snapshot = args->snapshot;
RAMBlock* rb = ramblock_lookup(rb_idstr_hash);
if (rb) { if (rb) {
SYX_PRINTF("Checking memory consistency of %s... ", rb->idstr); SYX_PRINTF("Checking memory consistency of %s... ", rb->idstr);
SyxSnapshotRAMBlock *rb_snapshot = g_hash_table_lookup(snapshot->root_snapshot->rbs_snapshot, rb_idstr_hash); SyxSnapshotRAMBlock* rb_snapshot = g_hash_table_lookup(
snapshot->root_snapshot->rbs_snapshot, rb_idstr_hash);
assert(rb_snapshot); assert(rb_snapshot);
assert(rb->used_length == rb_snapshot->used_length); assert(rb->used_length == rb_snapshot->used_length);
for (uint64_t i = 0; i < rb->used_length; i += syx_snapshot_state.page_size) { for (uint64_t i = 0; i < rb->used_length;
if (memcmp(rb->host + i, rb_snapshot->ram + i, syx_snapshot_state.page_size) != 0) { i += syx_snapshot_state.page_size) {
if (memcmp(rb->host + i, rb_snapshot->ram + i,
syx_snapshot_state.page_size) != 0) {
SYX_ERROR("\nFound incorrect page at offset 0x%lx\n", i); SYX_ERROR("\nFound incorrect page at offset 0x%lx\n", i);
for (uint64_t j = 0; j < syx_snapshot_state.page_size; j++) { for (uint64_t j = 0; j < syx_snapshot_state.page_size; j++) {
if (*(rb->host + i + j) != *(rb_snapshot->ram + i + j)) { if (*(rb->host + i + j) != *(rb_snapshot->ram + i + j)) {
@ -594,12 +678,13 @@ static void root_restore_check_memory_rb(gpointer rb_idstr_hash, gpointer rb_dir
} }
args->nb_inconsistent_pages++; args->nb_inconsistent_pages++;
} }
} }
if (args->nb_inconsistent_pages > 0) { if (args->nb_inconsistent_pages > 0) {
SYX_ERROR("[%s] Found %lu page %s.\n", rb->idstr, args->nb_inconsistent_pages, SYX_ERROR("[%s] Found %lu page %s.\n", rb->idstr,
args->nb_inconsistent_pages > 1 ? "inconsistencies" : "inconsistency"); args->nb_inconsistent_pages,
args->nb_inconsistent_pages > 1 ? "inconsistencies"
: "inconsistency");
} else { } else {
SYX_PRINTF("OK.\n"); SYX_PRINTF("OK.\n");
} }
@ -609,27 +694,27 @@ static void root_restore_check_memory_rb(gpointer rb_idstr_hash, gpointer rb_dir
} }
} }
SyxSnapshotCheckResult syx_snapshot_check(SyxSnapshot* ref_snapshot) { SyxSnapshotCheckResult syx_snapshot_check(SyxSnapshot* ref_snapshot)
{
struct rb_check_memory_args args = { struct rb_check_memory_args args = {
.snapshot = ref_snapshot, .snapshot = ref_snapshot,
.nb_inconsistent_pages = 0, .nb_inconsistent_pages = 0,
}; };
g_hash_table_foreach(ref_snapshot->rbs_dirty_list, root_restore_check_memory_rb, &args); g_hash_table_foreach(ref_snapshot->rbs_dirty_list,
root_restore_check_memory_rb, &args);
struct SyxSnapshotCheckResult res = { struct SyxSnapshotCheckResult res = {.nb_inconsistencies =
.nb_inconsistencies = args.nb_inconsistent_pages args.nb_inconsistent_pages};
};
return res; return res;
} }
void syx_snapshot_root_restore(SyxSnapshot *snapshot) { void syx_snapshot_root_restore(SyxSnapshot* snapshot)
{
// health check. // health check.
CPUState *cpu; CPUState* cpu;
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) { assert(cpu->stopped); }
assert(cpu->stopped);
}
bool must_unlock_bql = false; bool must_unlock_bql = false;
@ -638,7 +723,8 @@ void syx_snapshot_root_restore(SyxSnapshot *snapshot) {
must_unlock_bql = true; must_unlock_bql = true;
} }
// In case, we first restore devices if there is a modification of memory layout // In case, we first restore devices if there is a modification of memory
// layout
device_restore_all(snapshot->root_snapshot->dss); device_restore_all(snapshot->root_snapshot->dss);
g_hash_table_foreach(snapshot->rbs_dirty_list, root_restore_rb, snapshot); g_hash_table_foreach(snapshot->rbs_dirty_list, root_restore_rb, snapshot);
@ -656,34 +742,46 @@ void syx_snapshot_root_restore(SyxSnapshot *snapshot) {
bql_unlock(); bql_unlock();
} }
} }
bool syx_snapshot_cow_cache_read_entry(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset,
bool syx_snapshot_cow_cache_read_entry(BlockBackend* blk, int64_t offset,
int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset,
BdrvRequestFlags flags) BdrvRequestFlags flags)
{ {
if (!syx_snapshot_state.active_bdrv_cache_snapshot) { if (!syx_snapshot_state.active_bdrv_cache_snapshot) {
if (syx_snapshot_state.before_fuzz_cache) { if (syx_snapshot_state.before_fuzz_cache) {
syx_cow_cache_read_entry(syx_snapshot_state.before_fuzz_cache, blk, offset, bytes, qiov, qiov_offset, flags); syx_cow_cache_read_entry(syx_snapshot_state.before_fuzz_cache, blk,
offset, bytes, qiov, qiov_offset, flags);
return true; return true;
} }
return false; return false;
} else { } else {
syx_cow_cache_read_entry(syx_snapshot_state.active_bdrv_cache_snapshot->bdrvs_cow_cache, blk, offset, bytes, qiov, qiov_offset, flags); syx_cow_cache_read_entry(
syx_snapshot_state.active_bdrv_cache_snapshot->bdrvs_cow_cache, blk,
offset, bytes, qiov, qiov_offset, flags);
return true; return true;
} }
} }
bool syx_snapshot_cow_cache_write_entry(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, bool syx_snapshot_cow_cache_write_entry(BlockBackend* blk, int64_t offset,
int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset,
BdrvRequestFlags flags) BdrvRequestFlags flags)
{ {
if (!syx_snapshot_state.active_bdrv_cache_snapshot) { if (!syx_snapshot_state.active_bdrv_cache_snapshot) {
if (syx_snapshot_state.before_fuzz_cache) { if (syx_snapshot_state.before_fuzz_cache) {
assert(syx_cow_cache_write_entry(syx_snapshot_state.before_fuzz_cache, blk, offset, bytes, qiov, qiov_offset, flags)); assert(syx_cow_cache_write_entry(
syx_snapshot_state.before_fuzz_cache, blk, offset, bytes, qiov,
qiov_offset, flags));
return true; return true;
} }
return false; return false;
} else { } else {
assert(syx_cow_cache_write_entry(syx_snapshot_state.active_bdrv_cache_snapshot->bdrvs_cow_cache, blk, offset, bytes, qiov, qiov_offset, flags)); assert(syx_cow_cache_write_entry(
syx_snapshot_state.active_bdrv_cache_snapshot->bdrvs_cow_cache, blk,
offset, bytes, qiov, qiov_offset, flags));
return true; return true;
} }
} }

View File

@ -7,6 +7,7 @@ uintptr_t libafl_qemu_host_page_size(void)
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
uint8_t* libafl_paddr2host(CPUState* cpu, hwaddr addr, bool is_write) uint8_t* libafl_paddr2host(CPUState* cpu, hwaddr addr, bool is_write)
{ {
if (addr == -1) { if (addr == -1) {
@ -15,10 +16,13 @@ uint8_t* libafl_paddr2host(CPUState* cpu, hwaddr addr, bool is_write)
hwaddr xlat; hwaddr xlat;
MemoryRegion* mr; MemoryRegion* mr;
WITH_RCU_READ_LOCK_GUARD() { WITH_RCU_READ_LOCK_GUARD()
mr = address_space_translate(cpu->as, addr, &xlat, NULL, is_write, MEMTXATTRS_UNSPECIFIED); {
mr = address_space_translate(cpu->as, addr, &xlat, NULL, is_write,
MEMTXATTRS_UNSPECIFIED);
} }
return qemu_map_ram_ptr(mr->ram_block, xlat); return qemu_map_ram_ptr(mr->ram_block, xlat);
} }
#endif #endif

View File

@ -977,7 +977,6 @@ static uintptr_t host_sigbus_handler(CPUState *cpu, siginfo_t *info,
// int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info, // int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info,
// host_sigcontext *uc); // host_sigcontext *uc);
void libafl_qemu_handle_crash(int host_sig, siginfo_t *info, void *puc);
/* int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info, /* int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info,
host_sigcontext *uc) host_sigcontext *uc)
@ -998,6 +997,8 @@ void libafl_qemu_handle_crash(int host_sig, siginfo_t *info, void *puc);
pc, guest_addr); pc, guest_addr);
} */ } */
#include "libafl/user.h"
void libafl_qemu_handle_crash(int host_sig, siginfo_t *info, void *puc) { void libafl_qemu_handle_crash(int host_sig, siginfo_t *info, void *puc) {
host_signal_handler(host_sig, info, puc); host_signal_handler(host_sig, info, puc);
} }

View File

@ -54,6 +54,11 @@
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
//#include <sys/user.h> //#include <sys/user.h>
//// --- Begin LibAFL code ---
#include "libafl/hooks/syscall.h"
//// --- End LibAFL code ---
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
@ -6529,9 +6534,7 @@ typedef struct {
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
#include "libafl/hook.h" #include "libafl/hooks/thread.h"
extern __thread CPUArchState *libafl_qemu_env;
//// --- End LibAFL code --- //// --- End LibAFL code ---
@ -6567,17 +6570,7 @@ static void *clone_func(void *arg)
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
libafl_qemu_env = env; if (libafl_hook_new_thread_run(env)) {
if (libafl_new_thread_hooks) {
bool continue_execution = true;
int tid = sys_gettid();
struct libafl_new_thread_hook* h = libafl_new_thread_hooks;
while (h) {
continue_execution = h->callback(h->data, tid) && continue_execution;
h = h->next;
}
if (continue_execution) cpu_loop(env);
} else {
cpu_loop(env); cpu_loop(env);
} }
@ -13886,25 +13879,7 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
bool skip_syscall = false; bool skip_syscall = libafl_hook_syscall_pre_run(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, &ret);
struct libafl_pre_syscall_hook* h = libafl_pre_syscall_hooks;
while (h) {
// no null check
struct syshook_ret hook_ret = h->callback(h->data, num,
(target_ulong)arg1,
(target_ulong)arg2,
(target_ulong)arg3,
(target_ulong)arg4,
(target_ulong)arg5,
(target_ulong)arg6,
(target_ulong)arg7,
(target_ulong)arg8);
if (hook_ret.skip_syscall) {
skip_syscall = true;
ret = (abi_ulong)hook_ret.retval;
}
h = h->next;
}
if (skip_syscall) goto after_syscall; if (skip_syscall) goto after_syscall;
//// --- End LibAFL code --- //// --- End LibAFL code ---
@ -13915,21 +13890,8 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
//// --- Begin LibAFL code --- //// --- Begin LibAFL code ---
after_syscall:; after_syscall:;
struct libafl_post_syscall_hook* p = libafl_post_syscall_hooks; libafl_hook_syscall_post_run(num, arg1, arg2, arg3, arg4,
while (p) { arg5, arg6, arg7, arg8, &ret);
// no null check
ret = (abi_ulong)p->callback(p->data, (target_ulong)ret, num,
(target_ulong)arg1,
(target_ulong)arg2,
(target_ulong)arg3,
(target_ulong)arg4,
(target_ulong)arg5,
(target_ulong)arg6,
(target_ulong)arg7,
(target_ulong)arg8);
p = p->next;
}
//// --- End LibAFL code --- //// --- End LibAFL code ---
if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { if (unlikely(qemu_loglevel_mask(LOG_STRACE))) {

15
scripts/libafl-format.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
ROOT_DIR="$SCRIPT_DIR/.."
if [ "$1" = "check" ]; then
CHECK="--dry-run --Werror"
elif [ "$1" != "" ]; then
echo "Unknown option: $1. Type 'check' to check without modifying any file; do not provide any argument to format automatically."
exit 1
fi
cd "$SCRIPT_DIR" || exit 1
find "$ROOT_DIR/libafl" -name "*.c" -exec clang-format $CHECK -style="file:$ROOT_DIR/libafl/.clang-format" -i {} \;
find "$ROOT_DIR/include/libafl" -name "*.h" -exec clang-format $CHECK -style=file:"$ROOT_DIR/libafl/.clang-format" -i {} \;

View File

@ -647,6 +647,12 @@ const AccelOpsClass *cpus_get_accel(void)
return cpus_accel; return cpus_accel;
} }
//// --- Begin LibAFL code ---
// #include "libafl/hooks/thread.h"
//// --- End LibAFL code ---
void qemu_init_vcpu(CPUState *cpu) void qemu_init_vcpu(CPUState *cpu)
{ {
MachineState *ms = MACHINE(qdev_get_machine()); MachineState *ms = MACHINE(qdev_get_machine());
@ -671,6 +677,23 @@ void qemu_init_vcpu(CPUState *cpu)
while (!cpu->created) { while (!cpu->created) {
qemu_cond_wait(&qemu_cpu_cond, &bql); qemu_cond_wait(&qemu_cpu_cond, &bql);
} }
//// --- Begin LibAFL code ---
// if (libafl_new_thread_hooks) {
// bool continue_execution = true;
// struct libafl_new_thread_hook* h = libafl_new_thread_hooks;
// while (h) {
// continue_execution &= h->callback(h->data, cpu, 0); // TODO: should we keep TID in systemmode? could be useful for identification
// h = h->next;
// }
// if (!continue_execution) {
// // TODO: check if this is correct
// qemu_cpu_stop(cpu, true);
// }
// }
//// --- End LibAFL code ---
} }
void cpu_stop_current(void) void cpu_stop_current(void)

View File

@ -741,10 +741,14 @@ static int cpu_pre_save(void *opaque)
} }
cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len;
memcpy(cpu->cpreg_vmstate_indexes, cpu->cpreg_indexes,
cpu->cpreg_array_len * sizeof(uint64_t)); // Some ARM cpus like Cortex M do not have coprocessors
memcpy(cpu->cpreg_vmstate_values, cpu->cpreg_values, if (cpu->cpreg_array_len > 0) {
cpu->cpreg_array_len * sizeof(uint64_t)); memcpy(cpu->cpreg_vmstate_indexes, cpu->cpreg_indexes,
cpu->cpreg_array_len * sizeof(uint64_t));
memcpy(cpu->cpreg_vmstate_values, cpu->cpreg_values,
cpu->cpreg_array_len * sizeof(uint64_t));
}
return 0; return 0;
} }