AFLplusplus / qemuafl

This fork of QEMU enables fuzzing userspace ELF binaries under AFL++.

Home Page:https://aflplus.plus

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

drcov plugin won't work if program doesn't exit normally

bruce30262 opened this issue · comments

First of all thanks @JRomainG and @vanhauser-thc for PR #56. The drcov plugin is very useful.

However I found that when a program doesn't exit normally, the plugin won't dump the coverage info into the file. So for example a simple program like:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int *p = NULL;
    *p = 123; // crash
    return 0;
}

Will results in an empty drcov file.

I'm not familiar with QEMU TCG plugin but I think it's because it only calls plugin_exit in the atexit callback :

qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);

So when program exit abnormally ( crash / hang / timeout / ctrl-c ...etc ) it won't dump the coverage info.

Is it possible to register plugin_exit in other signal handlers, so when the program receive those signals and terminate itself it will also dump the coverage info ? Here are some signals I can think of :

  • SIGTERM
  • SIGINT
  • SIGALRM
  • SIGKILL
  • SIGSEGV

Unfortunately, it doesn't seem to be that easy, as QEMU registers its own signal handlers. Ideally, a callback would be added to the dump_core_and_abort function, but I do not believe that this is natively supported. You can however add it with a simple patch:

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 8b23f67821..21bf7fcc60 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -632,6 +632,9 @@ static void QEMU_NORETURN dump_core_and_abort(int target_sig)
     trace_user_force_sig(env, target_sig, host_sig);
     gdb_signalled(env, target_sig);
 
+    /* notify plugins that we are about to exit */
+    qemu_plugin_atexit_cb();
+
     /* dump core if supported by target binary format */
     if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) {
         stop_all_tasks();

I see, thanks for the reply !