Skip to content

Commit

Permalink
Refactor the way that the fuzzer is passing the data in
Browse files Browse the repository at this point in the history
  • Loading branch information
henrybear327 committed Nov 19, 2023
1 parent ad20c72 commit 11a5502
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 46 additions & 3 deletions src/fuzz_target.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#include <stdio.h>

Check warning

Code scanning / Cppcheck (reported by Codacy)

Include file: <stdio.h> not found. Please note: Cppcheck does not need standard library headers to get proper results. Warning

Include file: <stdio.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.6 rule Note

MISRA 21.6 rule
#include <stdlib.h>

Check warning

Code scanning / Cppcheck (reported by Codacy)

Include file: <stdlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results. Warning

Include file: <stdlib.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string.h>

Check warning

Code scanning / Cppcheck (reported by Codacy)

Include file: <string.h> not found. Please note: Cppcheck does not need standard library headers to get proper results. Warning

Include file: <string.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include "riscv.h"

const int max_instructions = 5000;
const int max_cycles = 5000;

Check warning

Code scanning / Cppcheck (reported by Codacy)

misra violation 804 with no text in the supplied rule-texts-file Warning

misra violation 804 with no text in the supplied rule-texts-file
const char* fake_rv32emu_name = "./fake_rv32emu";

Check warning

Code scanning / Cppcheck (reported by Codacy)

misra violation 804 with no text in the supplied rule-texts-file Warning

misra violation 804 with no text in the supplied rule-texts-file
const char* fake_elf_name = "fake_elf";

Check warning

Code scanning / Cppcheck (reported by Codacy)

misra violation 804 with no text in the supplied rule-texts-file Warning

misra violation 804 with no text in the supplied rule-texts-file

/* In order to be able to inspect a coredump we want to crash on every ASAN error. */
extern "C" void __asan_on_error()

Check warning

Code scanning / Cppcheck (reported by Codacy)

misra violation 802 with no text in the supplied rule-texts-file Warning

misra violation 802 with no text in the supplied rule-texts-file
{
Expand All @@ -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;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
char** args = (char**) malloc(sizeof(char*) * argc);

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule

char* arg0 = (char*) malloc(strlen(fake_rv32emu_name) + 1);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126). Note

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126).

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
strncpy(arg0, fake_rv32emu_name, strlen(fake_rv32emu_name) + 1);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126). Note

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126).

Check notice

Code scanning / Flawfinder (reported by Codacy)

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Note

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120).

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
args[0] = arg0;

char* arg1 = (char*) malloc(3);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting
strncpy(arg1, "-s", 3);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule

Check notice

Code scanning / Flawfinder (reported by Codacy)

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string. Note

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string.
args[1] = arg1;
args[2] = (char*) data;

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 11.8 rule Note

MISRA 11.8 rule

char* arg3 = (char*) malloc(3);

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
strncpy(arg3, "-l", 3);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string. Note

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string.

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
args[3] = arg3;
char* len_str = (char*)malloc(20 + 1); // LLONG_MIN base 10 has 20 chars

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting
sprintf(len_str, "%zu", len);

Check failure

Code scanning / Flawfinder (reported by Codacy)

Does not check for buffer overflows (CWE-120). Use sprintf_s, snprintf, or vsnprintf. Risk is low because the source has a constant maximum length. Error

Does not check for buffer overflows (CWE-120). Use sprintf_s, snprintf, or vsnprintf. Risk is low because the source has a constant maximum length.

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
args[4] = len_str;

char* arg5 = (char*) malloc(3);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting
strncpy(arg5, "-k", 3);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string. Note

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Risk is low because the source is a constant string.

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
args[5] = arg5;
char* max_cycles_str = (char*)malloc(11 + 1); // INT_MIN base 10 has 11 chars

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting
sprintf(max_cycles_str, "%d", max_cycles);

Check failure

Code scanning / Flawfinder (reported by Codacy)

Does not check for buffer overflows (CWE-120). Use sprintf_s, snprintf, or vsnprintf. Risk is low because the source has a constant maximum length. Error

Does not check for buffer overflows (CWE-120). Use sprintf_s, snprintf, or vsnprintf. Risk is low because the source has a constant maximum length.

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
args[6] = max_cycles_str;

char* arg7 = (char*) malloc(strlen(fake_elf_name) + 1);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126). Note

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126).

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule

Check warning

Code scanning / Cppcheck (reported by Codacy)

C-style pointer casting Warning

C-style pointer casting

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
strncpy(arg7, fake_elf_name, strlen(fake_elf_name) + 1);

Check notice

Code scanning / Flawfinder (reported by Codacy)

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126). Note

Does not handle strings that are not \0-terminated; if given one it may perform an over-read (it could cause a crash if unprotected) (CWE-126).

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule

Check notice

Code scanning / Flawfinder (reported by Codacy)

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120). Note

Easily used incorrectly; doesn't always \0-terminate or check for invalid pointers [MS-banned] (CWE-120).

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 10.4 rule Note

MISRA 10.4 rule
args[7] = arg7;

int ret = rv_init_and_execute_elf(argc, args);
if (ret == 0) {
fprintf(stderr, "Executed successfully\n");

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
} else {
fprintf(stderr, "Executed with failure\n");

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.7 rule Note

MISRA 17.7 rule
}

free(arg0);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(arg1);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(arg3);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(len_str);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(arg5);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(max_cycles_str);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(arg7);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
free(args);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 21.3 rule Note

MISRA 21.3 rule
}

extern "C" void LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
Expand Down
133 changes: 62 additions & 71 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Check warning

Code scanning / Semgrep (reported by Codacy)

The atoi family of functions can potentially overflow or underflow integer values. Warning

The atoi family of functions can potentially overflow or underflow integer values.

Check notice

Code scanning / Flawfinder (reported by Codacy)

Unless checked, the resulting number can exceed the expected range (CWE-190). If source untrusted, check both minimum and maximum, even if the input had no minus sign (large numbers can roll over into negative number; consider saving to an unsigned value if that is intended). Note

Unless checked, the resulting number can exceed the expected range (CWE-190). If source untrusted, check both minimum and maximum, even if the input had no minus sign (large numbers can roll over into negative number; consider saving to an unsigned value if that is intended).
emu_argc++;
break;
case 'k': // max execution cycle
opt_max_execution_cycles = true;
max_execution_cycles = atoi(optarg);

Check warning

Code scanning / Semgrep (reported by Codacy)

The atoi family of functions can potentially overflow or underflow integer values. Warning

The atoi family of functions can potentially overflow or underflow integer values.

Check notice

Code scanning / Flawfinder (reported by Codacy)

Unless checked, the resulting number can exceed the expected range (CWE-190). If source untrusted, check both minimum and maximum, even if the input had no minus sign (large numbers can roll over into negative number; consider saving to an unsigned value if that is intended). Note

Unless checked, the resulting number can exceed the expected range (CWE-190). If source untrusted, check both minimum and maximum, even if the input had no minus sign (large numbers can roll over into negative number; consider saving to an unsigned value if that is intended).
emu_argc++;
break;
default:
return false;
}

idx += 2;
}
#else
while ((opt = getopt(argc, args, optstr)) != -1) {
emu_argc++;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]);
Expand All @@ -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)) {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This argument to a file access function is derived from
user input (a command-line argument)
and then passed to elf_open(path), which calls open(__path).
#endif
fprintf(stderr, "Unable to open ELF file '%s'\n", opt_prog_name);
elf_delete(elf);
return 1;
Expand Down Expand Up @@ -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 */
Expand All @@ -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
10 changes: 2 additions & 8 deletions src/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

0 comments on commit 11a5502

Please sign in to comment.