cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

fuzz.c (7539B)


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