Refactoring of signal handling (#100)
* refactor signal handling for qemu usermode
This commit is contained in:
parent
7e0dc68430
commit
695657e4f3
@ -16,10 +16,22 @@ struct libafl_mapinfo {
|
|||||||
bool is_valid;
|
bool is_valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void (*libafl_dump_core_hook)(int host_sig);
|
struct libafl_qemu_sig_ctx {
|
||||||
|
bool in_qemu_sig_hdlr; // we were inside qemu native signal handler
|
||||||
|
bool is_target_signal; // if we were in qemu signal handle, true -> is a
|
||||||
|
// propagated target signal; false -> is a host qemu
|
||||||
|
// signal.
|
||||||
|
};
|
||||||
|
|
||||||
extern int libafl_force_dfl;
|
extern int libafl_force_dfl;
|
||||||
|
|
||||||
void libafl_dump_core_exec(int signal);
|
void libafl_qemu_native_signal_handler(int host_sig, siginfo_t* info,
|
||||||
|
void* puc);
|
||||||
|
|
||||||
|
struct libafl_qemu_sig_ctx* libafl_qemu_signal_context(void);
|
||||||
|
void libafl_set_in_target_signal_ctx(void);
|
||||||
|
void libafl_set_in_host_signal_ctx(void);
|
||||||
|
void libafl_unset_in_signal_ctx(void);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -4,23 +4,39 @@
|
|||||||
|
|
||||||
#include "libafl/user.h"
|
#include "libafl/user.h"
|
||||||
|
|
||||||
void (*libafl_dump_core_hook)(int host_sig) = NULL;
|
|
||||||
static struct image_info libafl_image_info;
|
static struct image_info libafl_image_info;
|
||||||
|
|
||||||
|
struct libafl_qemu_sig_ctx libafl_qemu_sig_ctx = {0};
|
||||||
|
|
||||||
extern abi_ulong target_brk, initial_target_brk;
|
extern abi_ulong target_brk, initial_target_brk;
|
||||||
|
|
||||||
void host_signal_handler(int host_sig, siginfo_t* info, void* puc);
|
void host_signal_handler(int host_sig, siginfo_t* info, void* puc);
|
||||||
|
|
||||||
void libafl_qemu_handle_crash(int host_sig, siginfo_t* info, void* puc)
|
void libafl_qemu_native_signal_handler(int host_sig, siginfo_t* info, void* puc)
|
||||||
{
|
{
|
||||||
host_signal_handler(host_sig, info, puc);
|
host_signal_handler(host_sig, info, puc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void libafl_dump_core_exec(int signal)
|
void libafl_set_in_target_signal_ctx(void)
|
||||||
{
|
{
|
||||||
if (libafl_dump_core_hook) {
|
libafl_qemu_sig_ctx.in_qemu_sig_hdlr = true;
|
||||||
libafl_dump_core_hook(signal);
|
libafl_qemu_sig_ctx.is_target_signal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void libafl_set_in_host_signal_ctx(void)
|
||||||
|
{
|
||||||
|
libafl_qemu_sig_ctx.in_qemu_sig_hdlr = true;
|
||||||
|
libafl_qemu_sig_ctx.is_target_signal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void libafl_unset_in_signal_ctx(void)
|
||||||
|
{
|
||||||
|
libafl_qemu_sig_ctx.in_qemu_sig_hdlr = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libafl_qemu_sig_ctx* libafl_qemu_signal_context(void)
|
||||||
|
{
|
||||||
|
return &libafl_qemu_sig_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t libafl_load_addr(void) { return libafl_image_info.load_addr; }
|
uint64_t libafl_load_addr(void) { return libafl_image_info.load_addr; }
|
||||||
|
@ -679,6 +679,7 @@ void force_sigsegv(int oldsig)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// called when the signal is cause by the target, and is not because of the host
|
||||||
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
|
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
|
||||||
MMUAccessType access_type, bool maperr, uintptr_t ra)
|
MMUAccessType access_type, bool maperr, uintptr_t ra)
|
||||||
{
|
{
|
||||||
@ -709,13 +710,18 @@ void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
|
|||||||
cpu_loop_exit_restore(cpu, ra);
|
cpu_loop_exit_restore(cpu, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* abort execution with signal */
|
/* abort host execution with signal */
|
||||||
static G_NORETURN
|
static G_NORETURN
|
||||||
void die_with_signal(int host_sig)
|
void die_with_signal(int host_sig)
|
||||||
{
|
{
|
||||||
struct sigaction act = {
|
//// --- Start LibAFL code ---
|
||||||
.sa_handler = SIG_DFL,
|
// We don't want to give back the signal to default handler.
|
||||||
};
|
// Instead, LibAFL is gonna catch the signal if it has put a handler for it
|
||||||
|
// and decide what to do
|
||||||
|
|
||||||
|
// struct sigaction act = {
|
||||||
|
// .sa_handler = SIG_DFL,
|
||||||
|
// };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The proper exit code for dying from an uncaught signal is -<signal>.
|
* The proper exit code for dying from an uncaught signal is -<signal>.
|
||||||
@ -724,19 +730,32 @@ void die_with_signal(int host_sig)
|
|||||||
* signal. Here the default signal handler is installed, we send
|
* signal. Here the default signal handler is installed, we send
|
||||||
* the signal and we wait for it to arrive.
|
* the signal and we wait for it to arrive.
|
||||||
*/
|
*/
|
||||||
sigfillset(&act.sa_mask);
|
// sigfillset(&act.sa_mask);
|
||||||
sigaction(host_sig, &act, NULL);
|
// sigaction(host_sig, &act, NULL);
|
||||||
|
|
||||||
|
// make sure signal is not blocked
|
||||||
|
sigset_t host_sig_set;
|
||||||
|
sigemptyset(&host_sig_set);
|
||||||
|
sigaddset(&host_sig_set, host_sig);
|
||||||
|
|
||||||
|
sigprocmask(SIG_UNBLOCK, &host_sig_set, NULL);
|
||||||
|
//// --- End LibAFL code ---
|
||||||
|
|
||||||
kill(getpid(), host_sig);
|
kill(getpid(), host_sig);
|
||||||
|
|
||||||
/* Make sure the signal isn't masked (reusing the mask inside of act). */
|
/* Make sure the signal isn't masked (reusing the mask inside of act). */
|
||||||
sigdelset(&act.sa_mask, host_sig);
|
//// --- Start LibAFL code ---
|
||||||
sigsuspend(&act.sa_mask);
|
// Unused as of now
|
||||||
|
// sigdelset(&act.sa_mask, host_sig);
|
||||||
|
// sigsuspend(&act.sa_mask);
|
||||||
|
//// --- End LibAFL code ---
|
||||||
|
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// target code signal handling.
|
||||||
|
// transform target signal into host signal.
|
||||||
static G_NORETURN
|
static G_NORETURN
|
||||||
void dump_core_and_abort(CPUArchState *env, int target_sig)
|
void dump_core_and_abort(CPUArchState *env, int target_sig)
|
||||||
{
|
{
|
||||||
@ -771,15 +790,7 @@ void dump_core_and_abort(CPUArchState *env, int target_sig)
|
|||||||
}
|
}
|
||||||
|
|
||||||
preexit_cleanup(env, 128 + target_sig);
|
preexit_cleanup(env, 128 + target_sig);
|
||||||
|
|
||||||
//// --- Begin LibAFL code ---
|
|
||||||
|
|
||||||
libafl_dump_core_exec(host_sig);
|
|
||||||
|
|
||||||
// die_with_signal_nodfl(host_sig); // to trigger LibAFL sig handler
|
|
||||||
|
|
||||||
//// --- End LibAFL code ---
|
|
||||||
|
|
||||||
die_with_signal(host_sig);
|
die_with_signal(host_sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,6 +825,7 @@ static inline void rewind_if_in_safe_syscall(void *puc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QEMU handler called when a real host signal is received (and not caused by the target)
|
||||||
static G_NORETURN
|
static G_NORETURN
|
||||||
void die_from_signal(siginfo_t *info)
|
void die_from_signal(siginfo_t *info)
|
||||||
{
|
{
|
||||||
@ -893,12 +905,6 @@ void die_from_signal(siginfo_t *info)
|
|||||||
error_report("QEMU internal SIG%s {code=%s, addr=%p}",
|
error_report("QEMU internal SIG%s {code=%s, addr=%p}",
|
||||||
sig, code, info->si_addr);
|
sig, code, info->si_addr);
|
||||||
|
|
||||||
//// --- Begin LibAFL code ---
|
|
||||||
|
|
||||||
libafl_dump_core_exec(info->si_signo);
|
|
||||||
|
|
||||||
//// --- End LibAFL code ---
|
|
||||||
|
|
||||||
die_with_signal(info->si_signo);
|
die_with_signal(info->si_signo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,34 +983,10 @@ static uintptr_t host_sigbus_handler(CPUState *cpu, siginfo_t *info,
|
|||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// --- Begin LibAFL code ---
|
|
||||||
|
|
||||||
// int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info,
|
|
||||||
// host_sigcontext *uc);
|
|
||||||
|
|
||||||
/* int libafl_qemu_is_tb_protected_write(int host_sig, siginfo_t *info,
|
|
||||||
host_sigcontext *uc)
|
|
||||||
{
|
|
||||||
CPUState *cpu = thread_cpu;
|
|
||||||
uintptr_t host_addr = (uintptr_t)info->si_addr;
|
|
||||||
|
|
||||||
bool is_valid = h2g_valid(host_addr);
|
|
||||||
abi_ptr guest_addr = h2g_nocheck(host_addr);
|
|
||||||
uintptr_t pc = host_signal_pc(uc);
|
|
||||||
bool is_write = host_signal_write(info, uc);
|
|
||||||
MMUAccessType access_type = adjust_signal_pc(&pc, is_write);
|
|
||||||
|
|
||||||
return is_write
|
|
||||||
&& is_valid
|
|
||||||
&& info->si_code == SEGV_ACCERR
|
|
||||||
&& handle_sigsegv_accerr_write(cpu, host_signal_mask(uc),
|
|
||||||
pc, guest_addr);
|
|
||||||
} */
|
|
||||||
|
|
||||||
//// --- End LibAFL code ---
|
|
||||||
|
|
||||||
//// --- Begin LibAFL code ---
|
//// --- Begin LibAFL code ---
|
||||||
/* static */
|
/* static */
|
||||||
|
// QEMU entrypoint for signal handling.
|
||||||
|
// it will notably determine whether the incoming signal is caused by the host or the target.
|
||||||
//// --- End LibAFL code ---
|
//// --- End LibAFL code ---
|
||||||
void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||||
{
|
{
|
||||||
@ -1019,6 +1001,10 @@ void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
|||||||
bool sync_sig = false;
|
bool sync_sig = false;
|
||||||
void *sigmask;
|
void *sigmask;
|
||||||
|
|
||||||
|
//// --- Start LibAFL code ---
|
||||||
|
libafl_set_in_host_signal_ctx();
|
||||||
|
//// --- End LibAFL code ---
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
|
* Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
|
||||||
* handling wrt signal blocking and unwinding. Non-spoofed SIGILL,
|
* handling wrt signal blocking and unwinding. Non-spoofed SIGILL,
|
||||||
@ -1029,7 +1015,10 @@ void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
|||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
/* Only returns on handle_sigsegv_accerr_write success. */
|
/* Only returns on handle_sigsegv_accerr_write success. */
|
||||||
host_sigsegv_handler(cpu, info, uc);
|
host_sigsegv_handler(cpu, info, uc);
|
||||||
return;
|
//// --- Start LibAFL code ---
|
||||||
|
goto exit;
|
||||||
|
// return;
|
||||||
|
//// --- End LibAFL code ---
|
||||||
case SIGBUS:
|
case SIGBUS:
|
||||||
pc = host_sigbus_handler(cpu, info, uc);
|
pc = host_sigbus_handler(cpu, info, uc);
|
||||||
sync_sig = true;
|
sync_sig = true;
|
||||||
@ -1044,7 +1033,10 @@ void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
|||||||
/* get target signal number */
|
/* get target signal number */
|
||||||
guest_sig = host_to_target_signal(host_sig);
|
guest_sig = host_to_target_signal(host_sig);
|
||||||
if (guest_sig < 1 || guest_sig > TARGET_NSIG) {
|
if (guest_sig < 1 || guest_sig > TARGET_NSIG) {
|
||||||
return;
|
//// --- Start LibAFL code ---
|
||||||
|
goto exit;
|
||||||
|
// return;
|
||||||
|
//// --- EndLibAFL code ---
|
||||||
}
|
}
|
||||||
trace_user_host_signal(env, host_sig, guest_sig);
|
trace_user_host_signal(env, host_sig, guest_sig);
|
||||||
|
|
||||||
@ -1086,6 +1078,10 @@ void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
|||||||
|
|
||||||
/* interrupt the virtual CPU as soon as possible */
|
/* interrupt the virtual CPU as soon as possible */
|
||||||
cpu_exit(thread_cpu);
|
cpu_exit(thread_cpu);
|
||||||
|
//// --- Start LibAFL code ---
|
||||||
|
exit:
|
||||||
|
libafl_unset_in_signal_ctx();
|
||||||
|
//// --- End LibAFL code ---
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do_sigaltstack() returns target values and errnos. */
|
/* do_sigaltstack() returns target values and errnos. */
|
||||||
@ -1228,6 +1224,7 @@ int libafl_force_dfl = 0;
|
|||||||
|
|
||||||
//// --- End LibAFL code ---
|
//// --- End LibAFL code ---
|
||||||
|
|
||||||
|
// Pending signal during target execution
|
||||||
static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|
static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|
||||||
struct emulated_sigtable *k)
|
struct emulated_sigtable *k)
|
||||||
{
|
{
|
||||||
@ -1239,6 +1236,10 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|
|||||||
struct target_sigaction *sa;
|
struct target_sigaction *sa;
|
||||||
TaskState *ts = get_task_state(cpu);
|
TaskState *ts = get_task_state(cpu);
|
||||||
|
|
||||||
|
//// --- Start LibAFL code ---
|
||||||
|
libafl_set_in_target_signal_ctx();
|
||||||
|
//// --- End LibAFL code ---
|
||||||
|
|
||||||
trace_user_handle_signal(cpu_env, sig);
|
trace_user_handle_signal(cpu_env, sig);
|
||||||
/* dequeue signal */
|
/* dequeue signal */
|
||||||
k->pending = 0;
|
k->pending = 0;
|
||||||
@ -1269,8 +1270,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|
|||||||
|
|
||||||
//// --- Start LibAFL code ---
|
//// --- Start LibAFL code ---
|
||||||
|
|
||||||
if (libafl_force_dfl && (sig == SIGABRT || sig == SIGABRT|| sig == SIGSEGV
|
if (libafl_force_dfl && (sig == SIGABRT || sig == SIGSEGV || sig == SIGILL || sig == SIGBUS)) {
|
||||||
|| sig == SIGILL || sig == SIGBUS)) {
|
|
||||||
handler = TARGET_SIG_DFL;
|
handler = TARGET_SIG_DFL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1333,6 +1333,9 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|
|||||||
sa->_sa_handler = TARGET_SIG_DFL;
|
sa->_sa_handler = TARGET_SIG_DFL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//// --- Start LibAFL code ---
|
||||||
|
libafl_unset_in_signal_ctx();
|
||||||
|
//// --- End LibAFL code ---
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_pending_signals(CPUArchState *cpu_env)
|
void process_pending_signals(CPUArchState *cpu_env)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user