From c834bdaf81272457e5c61d8e512b5e4d43c047ab Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 12 Aug 2025 15:05:51 +0200 Subject: [PATCH 1/3] breakpoint working with gdb and libafl qemu in parallel --- accel/tcg/cpu-exec.c | 6 ++---- include/libafl/system.h | 5 +++++ include/libafl/user.h | 6 +++--- include/system/runstate.h | 1 + libafl/exit.c | 4 ++-- libafl/system.c | 22 +++++++++++++++++++++- linux-user/arm/cpu_loop.c | 6 ------ linux-user/ppc/cpu_loop.c | 6 ------ linux-user/syscall.c | 6 +++--- qapi/run-state.json | 5 ++++- system/cpus.c | 6 ------ system/runstate.c | 30 +++++++++++++++++++++++++----- 12 files changed, 66 insertions(+), 37 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ff7aeecc1e..23b02a8a8a 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -714,8 +714,7 @@ static inline void cpu_handle_debug_exception(CPUState *cpu) static inline bool cpu_handle_exception(CPUState *cpu, int *ret) { - //// --- Begin LibAFL code --- - +//// --- Begin LibAFL code --- if (cpu->exception_index == EXCP_LIBAFL_EXIT) { *ret = cpu->exception_index; cpu->exception_index = -1; @@ -723,8 +722,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) libafl_sync_exit_cpu(); return true; } - - //// --- End LibAFL code --- +//// --- End LibAFL code --- if (cpu->exception_index < 0) { #ifndef CONFIG_USER_ONLY diff --git a/include/libafl/system.h b/include/libafl/system.h index 6e69ebf430..6f62e63f16 100644 --- a/include/libafl/system.h +++ b/include/libafl/system.h @@ -6,3 +6,8 @@ int libafl_qemu_set_hw_breakpoint(vaddr addr); int libafl_qemu_remove_hw_breakpoint(vaddr addr); void libafl_qemu_init(int argc, char** argv); +int libafl_qemu_run(void); + +size_t libafl_target_page_size(void); +int libafl_target_page_mask(void); +int libafl_target_page_offset_mask(void); diff --git a/include/libafl/user.h b/include/libafl/user.h index 8c67dbebe0..5f95ea4d46 100644 --- a/include/libafl/user.h +++ b/include/libafl/user.h @@ -7,9 +7,9 @@ #include "exec/cpu-defs.h" struct libafl_mapinfo { - target_ulong start; - target_ulong end; - target_ulong offset; + uint64_t start; + uint64_t end; + uint64_t offset; const char* path; int flags; int is_priv; diff --git a/include/system/runstate.h b/include/system/runstate.h index bffc3719d4..e7276037e0 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -95,6 +95,7 @@ void qemu_system_shutdown_request(ShutdownCause reason); void qemu_system_powerdown_request(void); void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_register_shutdown_notifier(Notifier *notifier); +void qemu_system_return_request(void); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); diff --git a/libafl/exit.c b/libafl/exit.c index 7dfbcf9712..6f4a575427 100644 --- a/libafl/exit.c +++ b/libafl/exit.c @@ -80,7 +80,7 @@ static void prepare_qemu_exit(CPUState* cpu, target_ulong next_pc) last_exit_reason.next_pc = next_pc; #ifndef CONFIG_USER_ONLY - qemu_system_debug_request(); + qemu_system_return_request(); #endif // in usermode, this may be called from the syscall hook, thus already out @@ -146,7 +146,7 @@ void libafl_exit_request_timeout(void) last_exit_reason.kind = TIMEOUT; last_exit_reason.cpu = current_cpu; - qemu_system_debug_request(); + qemu_system_return_request(); } #endif diff --git a/libafl/system.c b/libafl/system.c index 09a9a2d383..b04f1349f7 100644 --- a/libafl/system.c +++ b/libafl/system.c @@ -4,12 +4,32 @@ #include "system/accel-ops.h" #include "system/cpus.h" #include "gdbstub/enums.h" +#include "exec/target_page.h" +#include "qemu/main-loop.h" +#include "system/replay.h" +#include "system/runstate.h" #include "libafl/system.h" int libafl_qemu_toggle_hw_breakpoint(vaddr addr, bool set); -void libafl_qemu_init(int argc, char** argv) { qemu_init(argc, argv); } +void libafl_qemu_init(int argc, char** argv) +{ + qemu_init(argc, argv); +} + +int libafl_qemu_run(void) +{ + if (runstate_check(RUN_STATE_RET)) { + // we are resuming from a return to libafl + // transition to RUN_STATE_RUNNING + vm_start(); + } + + int status = qemu_main_loop(); + + return status; +} int libafl_qemu_set_hw_breakpoint(vaddr addr) { diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 2506e5edd7..2b2ad8b077 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -297,17 +297,13 @@ void cpu_loop(CPUARMState *env) abi_ulong ret; //// --- Begin LibAFL code --- - libafl_exit_signal_vm_start(); - //// --- End LibAFL code --- for(;;) { //// --- Begin LibAFL code --- - if (libafl_exit_asap()) return; - //// --- End LibAFL code --- cpu_exec_start(cs); @@ -318,10 +314,8 @@ void cpu_loop(CPUARMState *env) switch(trapnr) { //// --- Begin LibAFL code --- - case EXCP_LIBAFL_EXIT: return; - //// --- End LibAFL code --- case EXCP_UDEF: diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 0ad95b68a9..4e4a8d18a7 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -78,18 +78,14 @@ void cpu_loop(CPUPPCState *env) target_ulong ret; //// --- Begin LibAFL code --- - libafl_exit_signal_vm_start(); - //// --- End LibAFL code --- for(;;) { bool arch_interrupt; //// --- Begin LibAFL code --- - if (libafl_exit_asap()) return; - //// --- End LibAFL code --- cpu_exec_start(cs); @@ -101,10 +97,8 @@ void cpu_loop(CPUPPCState *env) switch (trapnr) { //// --- Begin LibAFL code --- - case EXCP_LIBAFL_EXIT: return; - //// --- End LibAFL code --- case POWERPC_EXCP_NONE: diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c564ff8c48..0d8208da3d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -13953,9 +13953,9 @@ IntervalTreeNode * libafl_maps_next(IntervalTreeNode *pageflags_maps_node, Inter if (flags & PAGE_EXEC) libafl_flags |= PROT_EXEC; ret->is_valid = true; - ret->start = (target_ulong)h2g_nocheck(min); - ret->end = (target_ulong)h2g_nocheck(max); - ret->offset = (target_ulong)e->offset; + ret->start = (uint64_t) h2g_nocheck(min); + ret->end = (uint64_t) max; + ret->offset = (uint64_t) e->offset; ret->path = e->path; ret->flags = libafl_flags; ret->is_priv = e->is_priv; diff --git a/qapi/run-state.json b/qapi/run-state.json index ce95cfa46b..e2e4430c2b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -52,12 +52,15 @@ # @colo: guest is paused to save/restore VM state under colo # checkpoint, VM can not get into this state unless colo # capability is enabled for migration. (since 2.8) +# +# @ret: guest stopped to return to the library caller. +# this is only valid if QEMU is compiled as a library (with AS_LIB). ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm', 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', - 'guest-panicked', 'colo' ] } + 'guest-panicked', 'colo', 'ret' ] } ## # @ShutdownCause: diff --git a/system/cpus.c b/system/cpus.c index 11ce379d47..33847ce91d 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -802,19 +802,13 @@ int vm_prepare_start(bool step_pending) } //// --- Begin LibAFL code --- - void libafl_exit_signal_vm_start(void); - - //// --- End LibAFL code --- void vm_start(void) { - //// --- Begin LibAFL code --- - libafl_exit_signal_vm_start(); - //// --- End LibAFL code --- if (!vm_prepare_start(false)) { diff --git a/system/runstate.c b/system/runstate.c index b12af1c29a..62b7403d45 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -153,7 +153,8 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_RUNNING, RUN_STATE_SHUTDOWN }, { RUN_STATE_RUNNING, RUN_STATE_WATCHDOG }, { RUN_STATE_RUNNING, RUN_STATE_GUEST_PANICKED }, - { RUN_STATE_RUNNING, RUN_STATE_COLO}, + { RUN_STATE_RUNNING, RUN_STATE_COLO }, + { RUN_STATE_RUNNING, RUN_STATE_RET }, { RUN_STATE_SAVE_VM, RUN_STATE_RUNNING }, { RUN_STATE_SAVE_VM, RUN_STATE_SUSPENDED }, @@ -183,6 +184,8 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_PRELAUNCH }, + { RUN_STATE_RET, RUN_STATE_RUNNING }, + { RUN_STATE__MAX, RUN_STATE__MAX }, }; @@ -418,6 +421,7 @@ static int shutdown_exit_code = EXIT_SUCCESS; static int shutdown_signal; static pid_t shutdown_pid; static int powerdown_requested; +static int return_requested; static int debug_requested; static int suspend_requested; static WakeupReason wakeup_reason; @@ -499,6 +503,13 @@ static int qemu_powerdown_requested(void) return r; } +static int qemu_return_requested(void) +{ + int r = return_requested; + return_requested = 0; + return r; +} + static int qemu_debug_requested(void) { int r = debug_requested; @@ -796,6 +807,12 @@ void qemu_register_shutdown_notifier(Notifier *notifier) notifier_list_add(&shutdown_notifiers, notifier); } +void qemu_system_return_request(void) +{ + return_requested = 1; + qemu_notify_event(); +} + void qemu_system_debug_request(void) { debug_requested = 1; @@ -807,15 +824,18 @@ static bool main_loop_should_exit(int *status) RunState r; ShutdownCause request; - if (qemu_debug_requested()) { - vm_stop(RUN_STATE_DEBUG); - //// --- Begin LibAFL code --- #ifdef AS_LIB - return true; // exit back to fuzzing harness + if (qemu_return_requested()) { + // exit back to libafl qemu harness + vm_stop(RUN_STATE_RET); + return true; + } #endif //// --- End LibAFL code --- + if (qemu_debug_requested()) { + vm_stop(RUN_STATE_DEBUG); } if (qemu_suspend_requested()) { qemu_system_suspend(); From f4f85db0e69df08b5dce208a8d4c58cfe68f1a80 Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 12 Aug 2025 17:14:49 +0200 Subject: [PATCH 2/3] as lib --- include/system/runstate.h | 7 ++++++- system/runstate.c | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/system/runstate.h b/include/system/runstate.h index e7276037e0..11e325ed3c 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -95,7 +95,6 @@ void qemu_system_shutdown_request(ShutdownCause reason); void qemu_system_powerdown_request(void); void qemu_register_powerdown_notifier(Notifier *notifier); void qemu_register_shutdown_notifier(Notifier *notifier); -void qemu_system_return_request(void); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); @@ -109,5 +108,11 @@ void qemu_system_guest_crashloaded(GuestPanicInformation *info); void qemu_system_guest_pvshutdown(void); bool qemu_system_dump_in_progress(void); +//// --- Begin LibAFL code --- +#ifdef AS_LIB +void qemu_system_return_request(void); +#endif +//// --- End LibAFL code --- + #endif diff --git a/system/runstate.c b/system/runstate.c index 62b7403d45..c6fecc32b8 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -503,12 +503,16 @@ static int qemu_powerdown_requested(void) return r; } +//// --- Begin LibAFL code --- +#ifdef AS_LIB static int qemu_return_requested(void) { int r = return_requested; return_requested = 0; return r; } +#endif +//// --- End LibAFL code --- static int qemu_debug_requested(void) { @@ -807,11 +811,15 @@ void qemu_register_shutdown_notifier(Notifier *notifier) notifier_list_add(&shutdown_notifiers, notifier); } +//// --- Begin LibAFL code --- +#ifdef AS_LIB void qemu_system_return_request(void) { return_requested = 1; qemu_notify_event(); } +#endif +//// --- End LibAFL code --- void qemu_system_debug_request(void) { From 6c22e9ef6db2c018c4843038ec67a9e85638ee3e Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Tue, 12 Aug 2025 17:48:10 +0200 Subject: [PATCH 3/3] as lib --- system/runstate.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/system/runstate.c b/system/runstate.c index c6fecc32b8..36ebdcfe9e 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -421,7 +421,6 @@ static int shutdown_exit_code = EXIT_SUCCESS; static int shutdown_signal; static pid_t shutdown_pid; static int powerdown_requested; -static int return_requested; static int debug_requested; static int suspend_requested; static WakeupReason wakeup_reason; @@ -435,6 +434,12 @@ static NotifierList shutdown_notifiers = NOTIFIER_LIST_INITIALIZER(shutdown_notifiers); static uint32_t wakeup_reason_mask = ~(1 << QEMU_WAKEUP_REASON_NONE); +//// --- Begin LibAFL code --- +#ifdef AS_LIB +static int return_requested; +#endif +//// --- End LibAFL code --- + ShutdownCause qemu_shutdown_requested_get(void) { return shutdown_requested;