diff --git a/Makefile b/Makefile index 51e9f98e..6ad1ba56 100644 --- a/Makefile +++ b/Makefile @@ -59,12 +59,28 @@ BENCH_PERF := $(BENCH_RUNNER:%=%.perf) BENCH_TRACE := $(BENCH_RUNNER:%=%.trace) BENCH_CSV := $(BENCH_RUNNER:%=%.csv) -CFLAGS += -fcallgraph-info=su CFLAGS += -g3 CFLAGS += -I. CFLAGS += -std=c99 -Wall -Wextra -pedantic CFLAGS += -Wmissing-prototypes + +ifneq ($(CC),clang) +CFLAGS += -fcallgraph-info=su CFLAGS += -ftrack-macro-expansion=0 +endif + +ifdef FUZZ +CFLAGS += -fsanitize=fuzzer-no-link +endif + +ifdef ASAN +CFLAGS += -fsanitize=address +endif + +ifdef UBSAN +CFLAGS += -fsanitize=undefined +endif + ifdef DEBUG CFLAGS += -O0 else @@ -472,6 +488,14 @@ benchmarks-diff: $(BENCH_CSV) $(BUILDDIR)/lfs: $(OBJ) $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ +# Libfuzzer is only supported on clang +ifeq ($(CC),clang) +ifdef FUZZ +$(BUILDDIR)/fuzz_mount: $(OBJ) fuzz/fuzz_mount.c + $(CC) $(CFLAGS) $^ -fsanitize=fuzzer -DCUSTOM_MUTATOR $(LFLAGS) -o $@ +endif +endif + $(BUILDDIR)/liblfs.a: $(OBJ) $(AR) rcs $@ $^ @@ -583,3 +607,4 @@ clean: rm -f $(BENCH_PERF) rm -f $(BENCH_TRACE) rm -f $(BENCH_CSV) + rm -f fuzz_mount diff --git a/fuzz/fuzz_mount.c b/fuzz/fuzz_mount.c new file mode 100644 index 00000000..1ea74379 --- /dev/null +++ b/fuzz/fuzz_mount.c @@ -0,0 +1,111 @@ +#include +#include "lfs.h" + +#define STORAGE_SIZE 1024*1024 + +static uint8_t disk_buffer[STORAGE_SIZE]; + +int min(int a, int b) { + if(a < b) { + return a; + } + return b; +} + +static int read(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + memcpy(buffer, &disk_buffer[block*c->read_size + off], size); + return 0; +} + +static int prog(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + memcpy(&disk_buffer[block*cfg->read_size + off], buffer, size); + return 0; +} + +static int erase(const struct lfs_config *cfg, lfs_block_t block) { + (void)cfg; + (void)block; + return 0; +} + +static int sync(const struct lfs_config *cfg) { + // sync is a noop + (void)cfg; + return 0; +} + +// configuration of the filesystem is provided by this struct +const struct lfs_config filesystem_cfg = { + // block device operations + .read = read, + .prog = prog, + .erase = erase, + .sync = sync, + + // block device configuration + .read_size = 1024, + .prog_size = 1024, + .block_size = 1024, + .block_count = 1024, + .cache_size = 1024, + .lookahead_size = 1024, + .block_cycles = 500, +}; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if(size == 0) { + return -1; + } + memset(disk_buffer, 0, sizeof(disk_buffer)); + + // variables used by the filesystem + lfs_t lfs; + + // Copy fuzzed data into fake storage device. + memcpy(disk_buffer, data, min(size, STORAGE_SIZE)); + + // mount the filesystem + int err = lfs_mount(&lfs, &filesystem_cfg); + + if (err) { + return 0; + } + + // release any resources we were using + lfs_unmount(&lfs); + + return 0; +} + +#ifdef CUSTOM_MUTATOR + +// Forward-declare the libFuzzer's mutator callback. +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, + size_t max_size, unsigned int seed) { + (void)seed; + memset(disk_buffer, 0, sizeof(disk_buffer)); + // variables used by the filesystem + lfs_t lfs; + + // Copy fuzzed data into fake storage device. + memcpy(disk_buffer, data, min(size, STORAGE_SIZE)); + + // Mount the filesystem + int err = lfs_mount(&lfs, &filesystem_cfg); + + // Reformat if we can't mount the filesystem + if (err) { + lfs_format(&lfs, &filesystem_cfg); + lfs_mount(&lfs, &filesystem_cfg); + } + lfs_unmount(&lfs); + memcpy(data, disk_buffer, min(STORAGE_SIZE, max_size)); + + return LLVMFuzzerMutate(data, size, max_size); +} + +#endif // CUSTOM_MUTATOR