From 11a550240307c5525825eb1cba8f3f603cf8ac50 Mon Sep 17 00:00:00 2001 From: Chun-Hung Tseng Date: Sun, 19 Nov 2023 01:07:53 +0100 Subject: [PATCH] Refactor the way that the fuzzer is passing the data in --- src/elf.c | 2 +- src/elf.h | 2 +- src/fuzz_target.cc | 49 ++++++++++++++++- src/main.c | 133 +++++++++++++++++++++------------------------ src/riscv.h | 10 +--- 5 files changed, 112 insertions(+), 84 deletions(-) diff --git a/src/elf.c b/src/elf.c index e828c5b31..0c7b7b470 100644 --- a/src/elf.c +++ b/src/elf.c @@ -291,7 +291,7 @@ bool elf_load(elf_t *e, riscv_t *rv, memory_t *mem) } #ifdef FUZZER -bool elf_open(elf_t *e, const char *path, const uint8_t *data, size_t len) +bool elf_open(elf_t *e, uint8_t *data, size_t len) #else bool elf_open(elf_t *e, const char *path) #endif diff --git a/src/elf.h b/src/elf.h index d1913d8b7..9677d1754 100644 --- a/src/elf.h +++ b/src/elf.h @@ -134,7 +134,7 @@ void elf_delete(elf_t *e); /* Open an ELF file from specified path */ #ifdef FUZZER -bool elf_open(elf_t *e, const char *path, const uint8_t *data, size_t len); +bool elf_open(elf_t *e, uint8_t *data, size_t len); #else bool elf_open(elf_t *e, const char *path); #endif diff --git a/src/fuzz_target.cc b/src/fuzz_target.cc index ccaf08746..573624d45 100644 --- a/src/fuzz_target.cc +++ b/src/fuzz_target.cc @@ -1,8 +1,12 @@ #include #include +#include #include "riscv.h" -const int max_instructions = 5000; +const int max_cycles = 5000; +const char* fake_rv32emu_name = "./fake_rv32emu"; +const char* fake_elf_name = "fake_elf"; + /* In order to be able to inspect a coredump we want to crash on every ASAN error. */ extern "C" void __asan_on_error() { @@ -14,13 +18,52 @@ extern "C" void __msan_on_error() } static void fuzz_elf_loader(const uint8_t *data, size_t len) -{ - int ret = rv_init_and_execute_binary(data, len, max_instructions); +{ + int argc = 1 + 2 * 3 + 1; + char** args = (char**) malloc(sizeof(char*) * argc); + + char* arg0 = (char*) malloc(strlen(fake_rv32emu_name) + 1); + strncpy(arg0, fake_rv32emu_name, strlen(fake_rv32emu_name) + 1); + args[0] = arg0; + + char* arg1 = (char*) malloc(3); + strncpy(arg1, "-s", 3); + args[1] = arg1; + args[2] = (char*) data; + + char* arg3 = (char*) malloc(3); + strncpy(arg3, "-l", 3); + args[3] = arg3; + char* len_str = (char*)malloc(20 + 1); // LLONG_MIN base 10 has 20 chars + sprintf(len_str, "%zu", len); + args[4] = len_str; + + char* arg5 = (char*) malloc(3); + strncpy(arg5, "-k", 3); + args[5] = arg5; + char* max_cycles_str = (char*)malloc(11 + 1); // INT_MIN base 10 has 11 chars + sprintf(max_cycles_str, "%d", max_cycles); + args[6] = max_cycles_str; + + char* arg7 = (char*) malloc(strlen(fake_elf_name) + 1); + strncpy(arg7, fake_elf_name, strlen(fake_elf_name) + 1); + args[7] = arg7; + + int ret = rv_init_and_execute_elf(argc, args); if (ret == 0) { fprintf(stderr, "Executed successfully\n"); } else { fprintf(stderr, "Executed with failure\n"); } + + free(arg0); + free(arg1); + free(arg3); + free(len_str); + free(arg5); + free(max_cycles_str); + free(arg7); + free(args); } extern "C" void LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) diff --git a/src/main.c b/src/main.c index df6982e2c..af3962dbb 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,18 @@ static bool opt_quiet_outputs = false; /* target executable */ static const char *opt_prog_name = "a.out"; +#ifdef FUZZER +/* ELF input as string (for fuzzing) */ +static bool opt_elf_string = false; +static uint8_t *elf_string = NULL; + +static bool opt_elf_strlen = NULL; +static int elf_strlen = 0; + +static bool opt_max_execution_cycles = NULL; +static int max_execution_cycles = 0; +#endif + /* target argc and argv */ static int prog_argc; static char **prog_args; @@ -83,10 +95,10 @@ static void run_and_trace(riscv_t *rv, elf_t *elf) } #ifdef FUZZER -static void run(riscv_t *rv, const int max_instructions) +static void run(riscv_t *rv, int max_cycles) { /* step instructions */ - rv_step(rv, max_instructions); + rv_step(rv, max_cycles); } #else static void run(riscv_t *rv) @@ -124,6 +136,38 @@ static bool parse_args(int argc, char **args) int opt; int emu_argc = 0; +#ifdef FUZZER + // getopt() won't work with binary data as control characters will screw the + // parsing up this part of the parsing code only work with the fuzzer + int idx = 1; + while (idx + 1 < argc) { + emu_argc++; + char opt = args[idx][1]; + char *optarg = args[idx + 1]; + + switch (opt) { + case 's': // binary string + opt_elf_string = true; + elf_string = (uint8_t *) optarg; + emu_argc++; + break; + case 'l': // binary string len + opt_elf_strlen = true; + elf_strlen = atoi(optarg); + emu_argc++; + break; + case 'k': // max execution cycle + opt_max_execution_cycles = true; + max_execution_cycles = atoi(optarg); + emu_argc++; + break; + default: + return false; + } + + idx += 2; + } +#else while ((opt = getopt(argc, args, optstr)) != -1) { emu_argc++; @@ -158,6 +202,7 @@ static bool parse_args(int argc, char **args) return false; } } +#endif prog_argc = argc - emu_argc - 1; /* optind points to the first non-option string, so it should indicate the @@ -194,75 +239,7 @@ static void dump_test_signature(elf_t *elf) fclose(f); } -#ifdef FUZZER -int rv_init_and_execute_binary(const uint8_t *data, - size_t len, - const int max_instructions) -{ - /* open the binary stream generated by the fuzzer as ELF */ - elf_t *elf = elf_new(); - if (!elf_open(elf, NULL, data, len)) { - fprintf(stderr, "Unable to open fuzzer data as file\n"); - elf_delete(elf); - return 1; - } - - /* install the I/O handlers for the RISC-V runtime */ - const riscv_io_t io = { - /* memory read interface */ - .mem_ifetch = MEMIO(ifetch), - .mem_read_w = MEMIO(read_w), - .mem_read_s = MEMIO(read_s), - .mem_read_b = MEMIO(read_b), - - /* memory write interface */ - .mem_write_w = MEMIO(write_w), - .mem_write_s = MEMIO(write_s), - .mem_write_b = MEMIO(write_b), - - /* system */ - .on_ecall = ecall_handler, - .on_ebreak = ebreak_handler, - .allow_misalign = opt_misaligned, - }; - - state_t *state = state_new(); - - /* find the start of the heap */ - const struct Elf32_Sym *end; - if ((end = elf_get_symbol(elf, "_end"))) - state->break_addr = end->st_value; - - /* create the RISC-V runtime */ - prog_argc = 0; - prog_args = NULL; - riscv_t *rv = - rv_create(&io, state, prog_argc, prog_args, !opt_quiet_outputs); - if (!rv) { - fprintf(stderr, "Unable to create riscv emulator\n"); - return 1; - } - - /* load the binary stream into the memory abstraction */ - if (!elf_load(elf, rv, state->mem)) { - fprintf(stderr, "Unable to load fuzzer data\n"); - return 1; - } - - /* execute the binary stream */ - run(rv, max_instructions); - - /* finalize the RISC-V runtime */ - elf_delete(elf); - rv_delete(rv); - state_delete(state); - - return 0; -} -#endif - -#ifndef FUZZER -int main(int argc, char **args) +int rv_init_and_execute_elf(int argc, char **args) { if (argc == 1 || !parse_args(argc, args)) { print_usage(args[0]); @@ -271,7 +248,11 @@ int main(int argc, char **args) /* open the ELF file from the file system */ elf_t *elf = elf_new(); +#ifdef FUZZER + if (!elf_open(elf, (uint8_t *) elf_string, elf_strlen)) { +#else if (!elf_open(elf, opt_prog_name)) { +#endif fprintf(stderr, "Unable to open ELF file '%s'\n", opt_prog_name); elf_delete(elf); return 1; @@ -327,7 +308,11 @@ int main(int argc, char **args) } #endif else { +#ifdef FUZZER + run(rv, max_execution_cycles); +#else run(rv); +#endif } /* dump registers as JSON */ @@ -345,4 +330,10 @@ int main(int argc, char **args) return 0; } + +#ifndef FUZZER +int main(int argc, char **args) +{ + return rv_init_and_execute_elf(argc, args); +} #endif diff --git a/src/riscv.h b/src/riscv.h index 80c398f9f..c8f2add54 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -140,14 +140,6 @@ typedef struct { bool allow_misalign; } riscv_io_t; -#ifdef FUZZER -// For fuzzing only: initialize the RISC-V emulator, and execute the given -// binary stream -int rv_init_and_execute_binary(const uint8_t *data, - size_t len, - const int max_instructions); -#endif - /* create a RISC-V emulator */ riscv_t *rv_create(const riscv_io_t *io, riscv_user_t user_data, @@ -205,6 +197,8 @@ bool rv_has_halted(riscv_t *rv); /* return the flag of outputting exit code */ bool rv_enables_to_output_exit_code(riscv_t *rv); +/* the init and execute logic shared by main and fuzzer */ +int rv_init_and_execute_elf(int argc, char **args); #ifdef __cplusplus }; #endif