qemu/tests/qtest/fuzz/fuzz.c
<<
>>
Prefs
   1/*
   2 * fuzzing driver
   3 *
   4 * Copyright Red Hat Inc., 2019
   5 *
   6 * Authors:
   7 *  Alexander Bulekov   <alxndr@bu.edu>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 *
  12 */
  13
  14#include "qemu/osdep.h"
  15
  16#include <wordexp.h>
  17
  18#include "qemu/cutils.h"
  19#include "qemu/datadir.h"
  20#include "sysemu/sysemu.h"
  21#include "sysemu/qtest.h"
  22#include "sysemu/runstate.h"
  23#include "qemu/main-loop.h"
  24#include "qemu/rcu.h"
  25#include "tests/qtest/libqtest.h"
  26#include "tests/qtest/libqos/qgraph.h"
  27#include "fuzz.h"
  28
  29#define MAX_EVENT_LOOPS 10
  30
  31typedef struct FuzzTargetState {
  32        FuzzTarget *target;
  33        QSLIST_ENTRY(FuzzTargetState) target_list;
  34} FuzzTargetState;
  35
  36typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList;
  37
  38static const char *fuzz_arch = TARGET_NAME;
  39
  40static FuzzTargetList *fuzz_target_list;
  41static FuzzTarget *fuzz_target;
  42static QTestState *fuzz_qts;
  43
  44
  45
  46void flush_events(QTestState *s)
  47{
  48    int i = MAX_EVENT_LOOPS;
  49    while (g_main_context_pending(NULL) && i-- > 0) {
  50        main_loop_wait(false);
  51    }
  52}
  53
  54static QTestState *qtest_setup(void)
  55{
  56    qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts);
  57    return qtest_inproc_init(&fuzz_qts, false, fuzz_arch,
  58            &qtest_server_inproc_recv);
  59}
  60
  61void fuzz_add_target(const FuzzTarget *target)
  62{
  63    FuzzTargetState *tmp;
  64    FuzzTargetState *target_state;
  65    if (!fuzz_target_list) {
  66        fuzz_target_list = g_new0(FuzzTargetList, 1);
  67    }
  68
  69    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
  70        if (g_strcmp0(tmp->target->name, target->name) == 0) {
  71            fprintf(stderr, "Error: Fuzz target name %s already in use\n",
  72                    target->name);
  73            abort();
  74        }
  75    }
  76    target_state = g_new0(FuzzTargetState, 1);
  77    target_state->target = g_new0(FuzzTarget, 1);
  78    *(target_state->target) = *target;
  79    QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list);
  80}
  81
  82
  83
  84static void usage(char *path)
  85{
  86    printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path);
  87    printf("where FUZZ_TARGET is one of:\n");
  88    FuzzTargetState *tmp;
  89    if (!fuzz_target_list) {
  90        fprintf(stderr, "Fuzz target list not initialized\n");
  91        abort();
  92    }
  93    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
  94        printf(" * %s  : %s\n", tmp->target->name,
  95                tmp->target->description);
  96    }
  97    printf("Alternatively, add -target-FUZZ_TARGET to the executable name\n\n"
  98           "Set the environment variable FUZZ_SERIALIZE_QTEST=1 to serialize\n"
  99           "QTest commands into an ASCII protocol. Useful for building crash\n"
 100           "reproducers, but slows down execution.\n\n"
 101           "Set the environment variable QTEST_LOG=1 to log all qtest commands"
 102           "\n");
 103    exit(0);
 104}
 105
 106static FuzzTarget *fuzz_get_target(char* name)
 107{
 108    FuzzTargetState *tmp;
 109    if (!fuzz_target_list) {
 110        fprintf(stderr, "Fuzz target list not initialized\n");
 111        abort();
 112    }
 113
 114    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
 115        if (strcmp(tmp->target->name, name) == 0) {
 116            return tmp->target;
 117        }
 118    }
 119    return NULL;
 120}
 121
 122
 123/* Sometimes called by libfuzzer to mutate two inputs into one */
 124size_t LLVMFuzzerCustomCrossOver(const uint8_t *data1, size_t size1,
 125                                 const uint8_t *data2, size_t size2,
 126                                 uint8_t *out, size_t max_out_size,
 127                                 unsigned int seed)
 128{
 129    if (fuzz_target->crossover) {
 130        return fuzz_target->crossover(data1, size1, data2, size2, out,
 131                                      max_out_size, seed);
 132    }
 133    return 0;
 134}
 135
 136/* Executed for each fuzzing-input */
 137int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
 138{
 139    /*
 140     * Do the pre-fuzz-initialization before the first fuzzing iteration,
 141     * instead of before the actual fuzz loop. This is needed since libfuzzer
 142     * may fork off additional workers, prior to the fuzzing loop, and if
 143     * pre_fuzz() sets up e.g. shared memory, this should be done for the
 144     * individual worker processes
 145     */
 146    static int pre_fuzz_done;
 147    if (!pre_fuzz_done && fuzz_target->pre_fuzz) {
 148        fuzz_target->pre_fuzz(fuzz_qts);
 149        pre_fuzz_done = true;
 150    }
 151
 152    fuzz_target->fuzz(fuzz_qts, Data, Size);
 153    return 0;
 154}
 155
 156/* Executed once, prior to fuzzing */
 157int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
 158{
 159
 160    char *target_name;
 161    GString *cmd_line;
 162    gchar *pretty_cmd_line;
 163    bool serialize = false;
 164
 165    /* Initialize qgraph and modules */
 166    qos_graph_init();
 167    module_call_init(MODULE_INIT_FUZZ_TARGET);
 168    module_call_init(MODULE_INIT_QOM);
 169    module_call_init(MODULE_INIT_LIBQOS);
 170
 171    qemu_init_exec_dir(**argv);
 172    target_name = strstr(**argv, "-target-");
 173    if (target_name) {        /* The binary name specifies the target */
 174        target_name += strlen("-target-");
 175    } else if (*argc > 1) {  /* The target is specified as an argument */
 176        target_name = (*argv)[1];
 177        if (!strstr(target_name, "--fuzz-target=")) {
 178            usage(**argv);
 179        }
 180        target_name += strlen("--fuzz-target=");
 181    } else {
 182        usage(**argv);
 183    }
 184
 185    /* Should we always serialize qtest commands? */
 186    if (getenv("FUZZ_SERIALIZE_QTEST")) {
 187        serialize = true;
 188    }
 189
 190    fuzz_qtest_set_serialize(serialize);
 191
 192    /* Identify the fuzz target */
 193    fuzz_target = fuzz_get_target(target_name);
 194    if (!fuzz_target) {
 195        usage(**argv);
 196    }
 197
 198    fuzz_qts = qtest_setup();
 199
 200    if (fuzz_target->pre_vm_init) {
 201        fuzz_target->pre_vm_init();
 202    }
 203
 204    /* Run QEMU's softmmu main with the fuzz-target dependent arguments */
 205    cmd_line = fuzz_target->get_init_cmdline(fuzz_target);
 206    g_string_append_printf(cmd_line, " %s -qtest /dev/null ",
 207                           getenv("QTEST_LOG") ? "" : "-qtest-log none");
 208
 209    /* Split the runcmd into an argv and argc */
 210    wordexp_t result;
 211    wordexp(cmd_line->str, &result, 0);
 212    g_string_free(cmd_line, true);
 213
 214    if (getenv("QTEST_LOG")) {
 215        pretty_cmd_line  = g_strjoinv(" ", result.we_wordv + 1);
 216        printf("Starting %s with Arguments: %s\n",
 217                result.we_wordv[0], pretty_cmd_line);
 218        g_free(pretty_cmd_line);
 219    }
 220
 221    qemu_init(result.we_wordc, result.we_wordv, NULL);
 222
 223    /* re-enable the rcu atfork, which was previously disabled in qemu_init */
 224    rcu_enable_atfork();
 225
 226    /*
 227     * Disable QEMU's signal handlers, since we manually control the main_loop,
 228     * and don't check for main_loop_should_exit
 229     */
 230    signal(SIGINT, SIG_DFL);
 231    signal(SIGHUP, SIG_DFL);
 232    signal(SIGTERM, SIG_DFL);
 233
 234    return 0;
 235}
 236