diff mbox series

[RFC,08/19] fuzz: add shims to intercept libfuzzer init

Message ID 20190725032321.12721-9-alxndr@bu.edu (mailing list archive)
State New, archived
Headers show
Series Add virtual device fuzzing support | expand

Commit Message

Alexander Bulekov July 25, 2019, 3:23 a.m. UTC
Intercept coverage buffer registration calls and use this information to
copy them to shared memory, if using fork() to avoid resetting device
state.

Signed-off-by: Alexander Oleinik <alxndr@bu.edu>
---
 tests/fuzz/fuzzer_hooks.c | 106 ++++++++++++++++++++++++++++++++++++++
 tests/fuzz/fuzzer_hooks.h |   9 ++++
 2 files changed, 115 insertions(+)
 create mode 100644 tests/fuzz/fuzzer_hooks.c
 create mode 100644 tests/fuzz/fuzzer_hooks.h

Comments

Paolo Bonzini July 25, 2019, 8:21 a.m. UTC | #1
On 25/07/19 05:23, Oleinik, Alexander wrote:
> Intercept coverage buffer registration calls and use this information to
> copy them to shared memory, if using fork() to avoid resetting device
> state.
> 
> Signed-off-by: Alexander Oleinik <alxndr@bu.edu>
> ---
>  tests/fuzz/fuzzer_hooks.c | 106 ++++++++++++++++++++++++++++++++++++++
>  tests/fuzz/fuzzer_hooks.h |   9 ++++
>  2 files changed, 115 insertions(+)
>  create mode 100644 tests/fuzz/fuzzer_hooks.c
>  create mode 100644 tests/fuzz/fuzzer_hooks.h
> 
> diff --git a/tests/fuzz/fuzzer_hooks.c b/tests/fuzz/fuzzer_hooks.c
> new file mode 100644
> index 0000000000..5a0bbec413
> --- /dev/null
> +++ b/tests/fuzz/fuzzer_hooks.c
> @@ -0,0 +1,106 @@
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qapi/error.h"
> +#include "qemu-common.h"
> +#include "fuzzer_hooks.h"
> +
> +#include <dlfcn.h>
> +#include <elf.h>
> +
> +
> +extern void* _ZN6fuzzer3TPCE;

Would it make sense to make this a C++ source, so that you can avoid
using the mangled names (in this case, "namespace fuzzer { extern void
*TPC; }" and then using fuzzer::TPC)?  Even if it's just a single symbol.

> +// The libfuzzer handlers
> +void __real___sanitizer_cov_8bit_counters_init(uint8_t*, uint8_t*);
> +void __real___sanitizer_cov_trace_pc_guard_init(uint8_t*, uint8_t*);
> +
> +void __wrap___sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop);
> +void __wrap___sanitizer_cov_trace_pc_guard_init(uint8_t *Start, uint8_t *Stop);
> +
> +
> +void* counter_shm;
> +
> +typedef struct CoverageRegion {
> +    uint8_t* start;
> +    size_t length;
> +    bool store; /* Set this if it needs to be copied to the forked process */
> +} CoverageRegion;
> +
> +CoverageRegion regions[10];
> +int region_index = 0;
> +
> +void __wrap___sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop)
> +{
> +    regions[region_index].start = Start;
> +    regions[region_index].length = Stop-Start;
> +    regions[region_index].store = true;
> +    region_index++;
> +    __real___sanitizer_cov_8bit_counters_init(Start, Stop);
> +}
> +
> +void __wrap___sanitizer_cov_trace_pc_guard_init(uint8_t *Start, uint8_t *Stop)
> +{
> +    regions[region_index].start = Start;
> +    regions[region_index++].length = Stop-Start;
> +    regions[region_index].store = true;
> +    region_index++;
> +    __real___sanitizer_cov_trace_pc_guard_init(Start, Stop);
> +}
> +
> +static void add_tpc_region(void)
> +{
> +    /* Got symbol and length from readelf. Horrible way to do this! */
> +    regions[region_index].start = (uint8_t*)(&_ZN6fuzzer3TPCE);
> +    regions[region_index].length = 0x443c00; 
> +    regions[region_index].store = true;
> +    region_index++;
> +}
> +
> +void counter_shm_init(void)
> +{
> +    /*
> +     * Add the  internal libfuzzer object that gets modified by cmp, etc
> +     * callbacks
> +     */
> +    add_tpc_region(); 
> +
> +    size_t length = 0;
> +    for(int i=0; i<region_index; i++){
> +        printf("%d %lx\n", i, length);
> +        length += regions[i].length;
> +    }
> +
> +    /* 
> +     * Map some shared memory. When we use a fork-server we can copy the
> +     * libfuzzer-related counters
> +     * */
> +    counter_shm = mmap(NULL, length, PROT_READ | PROT_WRITE, 
> +            MAP_SHARED | MAP_ANONYMOUS, -1, 0);
> +    if(counter_shm == MAP_FAILED) {
> +        printf("mmap() failed\n");
> +        do { perror("error:"); exit(EXIT_FAILURE); } while (0);
> +        exit(-1);
> +    }
> +}
> +
> +void counter_shm_store(void)
> +{
> +    size_t offset = 0;
> +    for(int i=0; i<region_index; i++) {
> +        if(regions[i].store) {
> +            memcpy(counter_shm + offset, regions[i].start, regions[i].length);
> +        }
> +        offset+=regions[i].length;
> +    }
> +}
> +
> +void counter_shm_load(void)
> +{
> +    size_t offset = 0;
> +    for(int i=0; i<region_index; i++) {
> +        if(regions[i].store) {
> +            memcpy(regions[i].start, counter_shm + offset, regions[i].length);
> +        }
> +        offset+=regions[i].length;
> +    }
> +}
> +
> diff --git a/tests/fuzz/fuzzer_hooks.h b/tests/fuzz/fuzzer_hooks.h
> new file mode 100644
> index 0000000000..90dca254d4
> --- /dev/null
> +++ b/tests/fuzz/fuzzer_hooks.h
> @@ -0,0 +1,9 @@
> +#ifndef FUZZER_HOOKS_H
> +#define FUZZER_HOOKS_H
> +
> +void counter_shm_init(void);
> +void counter_shm_store(void);
> +void counter_shm_load(void);
> +
> +#endif
> +
>
Stefan Hajnoczi July 26, 2019, 12:59 p.m. UTC | #2
On Thu, Jul 25, 2019 at 10:21:18AM +0200, Paolo Bonzini wrote:
> On 25/07/19 05:23, Oleinik, Alexander wrote:
> > Intercept coverage buffer registration calls and use this information to
> > copy them to shared memory, if using fork() to avoid resetting device
> > state.
> > 
> > Signed-off-by: Alexander Oleinik <alxndr@bu.edu>
> > ---
> >  tests/fuzz/fuzzer_hooks.c | 106 ++++++++++++++++++++++++++++++++++++++
> >  tests/fuzz/fuzzer_hooks.h |   9 ++++
> >  2 files changed, 115 insertions(+)
> >  create mode 100644 tests/fuzz/fuzzer_hooks.c
> >  create mode 100644 tests/fuzz/fuzzer_hooks.h
> > 
> > diff --git a/tests/fuzz/fuzzer_hooks.c b/tests/fuzz/fuzzer_hooks.c
> > new file mode 100644
> > index 0000000000..5a0bbec413
> > --- /dev/null
> > +++ b/tests/fuzz/fuzzer_hooks.c
> > @@ -0,0 +1,106 @@
> > +#include "qemu/osdep.h"
> > +#include "qemu/units.h"
> > +#include "qapi/error.h"
> > +#include "qemu-common.h"
> > +#include "fuzzer_hooks.h"
> > +
> > +#include <dlfcn.h>
> > +#include <elf.h>
> > +
> > +
> > +extern void* _ZN6fuzzer3TPCE;
> 
> Would it make sense to make this a C++ source, so that you can avoid
> using the mangled names (in this case, "namespace fuzzer { extern void
> *TPC; }" and then using fuzzer::TPC)?  Even if it's just a single symbol.

A proper libfuzzer API is nicest in the long term.

Alexander: Could you send a patch to libfuzzer to see if they are
willing to support this via their API?

Stefan
diff mbox series

Patch

diff --git a/tests/fuzz/fuzzer_hooks.c b/tests/fuzz/fuzzer_hooks.c
new file mode 100644
index 0000000000..5a0bbec413
--- /dev/null
+++ b/tests/fuzz/fuzzer_hooks.c
@@ -0,0 +1,106 @@ 
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "fuzzer_hooks.h"
+
+#include <dlfcn.h>
+#include <elf.h>
+
+
+extern void* _ZN6fuzzer3TPCE;
+// The libfuzzer handlers
+void __real___sanitizer_cov_8bit_counters_init(uint8_t*, uint8_t*);
+void __real___sanitizer_cov_trace_pc_guard_init(uint8_t*, uint8_t*);
+
+void __wrap___sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop);
+void __wrap___sanitizer_cov_trace_pc_guard_init(uint8_t *Start, uint8_t *Stop);
+
+
+void* counter_shm;
+
+typedef struct CoverageRegion {
+    uint8_t* start;
+    size_t length;
+    bool store; /* Set this if it needs to be copied to the forked process */
+} CoverageRegion;
+
+CoverageRegion regions[10];
+int region_index = 0;
+
+void __wrap___sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop)
+{
+    regions[region_index].start = Start;
+    regions[region_index].length = Stop-Start;
+    regions[region_index].store = true;
+    region_index++;
+    __real___sanitizer_cov_8bit_counters_init(Start, Stop);
+}
+
+void __wrap___sanitizer_cov_trace_pc_guard_init(uint8_t *Start, uint8_t *Stop)
+{
+    regions[region_index].start = Start;
+    regions[region_index++].length = Stop-Start;
+    regions[region_index].store = true;
+    region_index++;
+    __real___sanitizer_cov_trace_pc_guard_init(Start, Stop);
+}
+
+static void add_tpc_region(void)
+{
+    /* Got symbol and length from readelf. Horrible way to do this! */
+    regions[region_index].start = (uint8_t*)(&_ZN6fuzzer3TPCE);
+    regions[region_index].length = 0x443c00; 
+    regions[region_index].store = true;
+    region_index++;
+}
+
+void counter_shm_init(void)
+{
+    /*
+     * Add the  internal libfuzzer object that gets modified by cmp, etc
+     * callbacks
+     */
+    add_tpc_region(); 
+
+    size_t length = 0;
+    for(int i=0; i<region_index; i++){
+        printf("%d %lx\n", i, length);
+        length += regions[i].length;
+    }
+
+    /* 
+     * Map some shared memory. When we use a fork-server we can copy the
+     * libfuzzer-related counters
+     * */
+    counter_shm = mmap(NULL, length, PROT_READ | PROT_WRITE, 
+            MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+    if(counter_shm == MAP_FAILED) {
+        printf("mmap() failed\n");
+        do { perror("error:"); exit(EXIT_FAILURE); } while (0);
+        exit(-1);
+    }
+}
+
+void counter_shm_store(void)
+{
+    size_t offset = 0;
+    for(int i=0; i<region_index; i++) {
+        if(regions[i].store) {
+            memcpy(counter_shm + offset, regions[i].start, regions[i].length);
+        }
+        offset+=regions[i].length;
+    }
+}
+
+void counter_shm_load(void)
+{
+    size_t offset = 0;
+    for(int i=0; i<region_index; i++) {
+        if(regions[i].store) {
+            memcpy(regions[i].start, counter_shm + offset, regions[i].length);
+        }
+        offset+=regions[i].length;
+    }
+}
+
diff --git a/tests/fuzz/fuzzer_hooks.h b/tests/fuzz/fuzzer_hooks.h
new file mode 100644
index 0000000000..90dca254d4
--- /dev/null
+++ b/tests/fuzz/fuzzer_hooks.h
@@ -0,0 +1,9 @@ 
+#ifndef FUZZER_HOOKS_H
+#define FUZZER_HOOKS_H
+
+void counter_shm_init(void);
+void counter_shm_store(void);
+void counter_shm_load(void);
+
+#endif
+