Skip to content

Commit 38522ce

Browse files
Adds basic libfuzzer harness
This harness simply mounts and unmounts a ramdisk that is populated by fuzzed data. This fuzz harness tests that littlefs is able to safely attempt to mount a corrupted storage device.
1 parent b78afe2 commit 38522ce

File tree

2 files changed

+137
-1
lines changed

2 files changed

+137
-1
lines changed

Makefile

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,28 @@ BENCH_PERF := $(BENCH_RUNNER:%=%.perf)
5959
BENCH_TRACE := $(BENCH_RUNNER:%=%.trace)
6060
BENCH_CSV := $(BENCH_RUNNER:%=%.csv)
6161

62-
CFLAGS += -fcallgraph-info=su
6362
CFLAGS += -g3
6463
CFLAGS += -I.
6564
CFLAGS += -std=c99 -Wall -Wextra -pedantic
6665
CFLAGS += -Wmissing-prototypes
66+
67+
ifneq ($(CC),clang)
68+
CFLAGS += -fcallgraph-info=su
6769
CFLAGS += -ftrack-macro-expansion=0
70+
endif
71+
72+
ifdef FUZZ
73+
CFLAGS += -fsanitize=fuzzer-no-link
74+
endif
75+
76+
ifdef ASAN
77+
CFLAGS += -fsanitize=address
78+
endif
79+
80+
ifdef UBSAN
81+
CFLAGS += -fsanitize=undefined
82+
endif
83+
6884
ifdef DEBUG
6985
CFLAGS += -O0
7086
else
@@ -472,6 +488,14 @@ benchmarks-diff: $(BENCH_CSV)
472488
$(BUILDDIR)/lfs: $(OBJ)
473489
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
474490

491+
# Libfuzzer is only supported on clang
492+
ifeq ($(CC),clang)
493+
ifdef FUZZ
494+
$(BUILDDIR)/fuzz_mount: $(OBJ) fuzz/fuzz_mount.c
495+
$(CC) $(CFLAGS) $^ -fsanitize=fuzzer -DCUSTOM_MUTATOR $(LFLAGS) -o $@
496+
endif
497+
endif
498+
475499
$(BUILDDIR)/liblfs.a: $(OBJ)
476500
$(AR) rcs $@ $^
477501

@@ -583,3 +607,4 @@ clean:
583607
rm -f $(BENCH_PERF)
584608
rm -f $(BENCH_TRACE)
585609
rm -f $(BENCH_CSV)
610+
rm -f fuzz_mount

fuzz/fuzz_mount.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include <string.h>
2+
#include "lfs.h"
3+
4+
#define STORAGE_SIZE 1024*1024
5+
6+
static uint8_t disk_buffer[STORAGE_SIZE];
7+
8+
int min(int a, int b) {
9+
if(a < b) {
10+
return a;
11+
}
12+
return b;
13+
}
14+
15+
static int read(const struct lfs_config *c, lfs_block_t block,
16+
lfs_off_t off, void *buffer, lfs_size_t size) {
17+
memcpy(buffer, &disk_buffer[block*c->read_size + off], size);
18+
return 0;
19+
}
20+
21+
static int prog(const struct lfs_config *cfg, lfs_block_t block,
22+
lfs_off_t off, const void *buffer, lfs_size_t size) {
23+
memcpy(&disk_buffer[block*cfg->read_size + off], buffer, size);
24+
return 0;
25+
}
26+
27+
static int erase(const struct lfs_config *cfg, lfs_block_t block) {
28+
(void)cfg;
29+
(void)block;
30+
return 0;
31+
}
32+
33+
static int sync(const struct lfs_config *cfg) {
34+
// sync is a noop
35+
(void)cfg;
36+
return 0;
37+
}
38+
39+
// configuration of the filesystem is provided by this struct
40+
const struct lfs_config filesystem_cfg = {
41+
// block device operations
42+
.read = read,
43+
.prog = prog,
44+
.erase = erase,
45+
.sync = sync,
46+
47+
// block device configuration
48+
.read_size = 1024,
49+
.prog_size = 1024,
50+
.block_size = 1024,
51+
.block_count = 1024,
52+
.cache_size = 1024,
53+
.lookahead_size = 1024,
54+
.block_cycles = 500,
55+
};
56+
57+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
58+
if(size == 0) {
59+
return -1;
60+
}
61+
memset(disk_buffer, 0, sizeof(disk_buffer));
62+
63+
// variables used by the filesystem
64+
lfs_t lfs;
65+
66+
// Copy fuzzed data into fake storage device.
67+
memcpy(disk_buffer, data, min(size, STORAGE_SIZE));
68+
69+
// mount the filesystem
70+
int err = lfs_mount(&lfs, &filesystem_cfg);
71+
72+
if (err) {
73+
return 0;
74+
}
75+
76+
// release any resources we were using
77+
lfs_unmount(&lfs);
78+
79+
return 0;
80+
}
81+
82+
#ifdef CUSTOM_MUTATOR
83+
84+
// Forward-declare the libFuzzer's mutator callback.
85+
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
86+
87+
size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
88+
size_t max_size, unsigned int seed) {
89+
(void)seed;
90+
memset(disk_buffer, 0, sizeof(disk_buffer));
91+
// variables used by the filesystem
92+
lfs_t lfs;
93+
94+
// Copy fuzzed data into fake storage device.
95+
memcpy(disk_buffer, data, min(size, STORAGE_SIZE));
96+
97+
// Mount the filesystem
98+
int err = lfs_mount(&lfs, &filesystem_cfg);
99+
100+
// Reformat if we can't mount the filesystem
101+
if (err) {
102+
lfs_format(&lfs, &filesystem_cfg);
103+
lfs_mount(&lfs, &filesystem_cfg);
104+
}
105+
lfs_unmount(&lfs);
106+
memcpy(data, disk_buffer, min(STORAGE_SIZE, max_size));
107+
108+
return LLVMFuzzerMutate(data, size, max_size);
109+
}
110+
111+
#endif // CUSTOM_MUTATOR

0 commit comments

Comments
 (0)