From 6911290dbb577fb295e556da6fd7e4f5f7c81a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Mon, 20 Feb 2017 11:37:43 +0100 Subject: test/fuzz: add fuzzing infrastructure For now we use the american fuzzy lop in the future we might also add libFuzzer support. --- fuzz/.gitignore | 2 + fuzz/Makefile | 28 +++++++ fuzz/README.md | 30 +++++++ fuzz/dictionaries/text-fuzzer.dict | 20 +++++ fuzz/fuzzer.h | 18 ++++ fuzz/inputs/text-fuzzer/text-fuzzer.in | 13 +++ fuzz/text-fuzzer.c | 149 +++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Makefile create mode 100644 fuzz/README.md create mode 100644 fuzz/dictionaries/text-fuzzer.dict create mode 100644 fuzz/fuzzer.h create mode 100644 fuzz/inputs/text-fuzzer/text-fuzzer.in create mode 100644 fuzz/text-fuzzer.c diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..af19e48 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,2 @@ +/results +/text-fuzzer diff --git a/fuzz/Makefile b/fuzz/Makefile new file mode 100644 index 0000000..66037a2 --- /dev/null +++ b/fuzz/Makefile @@ -0,0 +1,28 @@ +-include ../../config.mk + +ALL = text-fuzzer +CC = afl-gcc +CFLAGS += -I. -I../.. -DBUFFER_SIZE=4 -DBLOCK_SIZE=4 + +test: $(ALL) + +text-fuzzer: text-fuzzer.c fuzzer.h ../../text.c ../../text-util.c ../../text-motions.c ../../text-objects.c ../../text-regex.c + @echo Compiling $@ binary + ${CC} ${CFLAGS} ${CFLAGS_STD} ${CFLAGS_EXTRA} ${filter %.c, $^} ${LDFLAGS} -o $@ + +debug: clean + $(MAKE) CFLAGS_EXTRA='${CFLAGS_EXTRA} ${CFLAGS_DEBUG}' + +afl-fuzz-text: text-fuzzer + @mkdir -p "results/$<" + @afl-fuzz -i - -x "dictionaries/$<.dict" -o "results/$<" -- "./$<" || \ + afl-fuzz -i "inputs/$<" -x "dictionaries/$<.dict" -o "results/$<" -- "./$<" + +clean: + @echo cleaning + @rm -f $(ALL) + +distclean: clean + @rm -rf results/ + +.PHONY: clean distclean debug afl-fuzz-text diff --git a/fuzz/README.md b/fuzz/README.md new file mode 100644 index 0000000..3eabb7a --- /dev/null +++ b/fuzz/README.md @@ -0,0 +1,30 @@ +Fuzzing infrastructure for low level code used by vis +----------------------------------------------------- + +This directory contains some simple command line applications +which expose core library interfaces through the standard I/O +streams. They are intended to be used as test drivers for +fuzzers like [AFL](http://lcamtuf.coredump.cx/afl/). + +Run one of the `make afl-fuzz-*` targets to start fuzzing a +specific instrumented binary using `afl-fuzz(1)`. By default +it will try to resume a previous fuzzing session, before +starting a new one if that fails. + +The following files are used: + + * `$APP-fuzzer.c` application exposing a simple text interface + * `fuzzer.h` common code used among different fuzzing drivers + * `./input/$APP/` intial test input, one file per test + * `./dictionaries/$APP.dict` a dictionary with valid syntax tokens + * `./results/$APP/` the fuzzing results are stored here + +See the AFL documentation for further information. + +In the future we might also use [libFuzzer](http://llvm.org/docs/LibFuzzer.html) +for further fuzzing. + +Quick start example: + + $ make afl-fuzz-text + diff --git a/fuzz/dictionaries/text-fuzzer.dict b/fuzz/dictionaries/text-fuzzer.dict new file mode 100644 index 0000000..389141b --- /dev/null +++ b/fuzz/dictionaries/text-fuzzer.dict @@ -0,0 +1,20 @@ +# AFL dictionary for text-fuzzer +# +# Not sure whether it makes sense to specify a dictionary, +# the syntax is quite simple? +# +cmd_earlier="-" +cmd_later="+" +cmd_mark_get="?" +# cmd_mark_set="= 0" +cmd_mark_set="=" +cmd_size="#" +# cmd_delete="d 0 1" +cmd_delete="d" +# cmd_insert="i 0 text" +cmd_insert="i" +cmd_print="p" +cmd_quit="q" +cmd_redo="r" +cmd_snapshot="s" +cmd_undo="u" diff --git a/fuzz/fuzzer.h b/fuzz/fuzzer.h new file mode 100644 index 0000000..3265532 --- /dev/null +++ b/fuzz/fuzzer.h @@ -0,0 +1,18 @@ +#ifndef FUZZER_H +#define FUZZER_H + +enum CmdStatus { + CMD_FAIL = false, + CMD_OK = true, + CMD_ERR, /* syntax error */ + CMD_QUIT, /* quit, accept no further commands */ +}; + +static const char *cmd_status_msg[] = { + [CMD_FAIL] = "Fail\n", + [CMD_OK] = "", + [CMD_ERR] = "Syntax error\n", + [CMD_QUIT] = "Bye\n", +}; + +#endif diff --git a/fuzz/inputs/text-fuzzer/text-fuzzer.in b/fuzz/inputs/text-fuzzer/text-fuzzer.in new file mode 100644 index 0000000..11e910c --- /dev/null +++ b/fuzz/inputs/text-fuzzer/text-fuzzer.in @@ -0,0 +1,13 @@ +i 0 text +d 1 2 +s +i 1 ex +u +r +- ++ +p +# += 2 +? +q diff --git a/fuzz/text-fuzzer.c b/fuzz/text-fuzzer.c new file mode 100644 index 0000000..449e0dd --- /dev/null +++ b/fuzz/text-fuzzer.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include "fuzzer.h" +#include "text.h" +#include "text-util.h" +#include "util.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +typedef enum CmdStatus (*Cmd)(Text *txt, const char *cmd); + +static Mark mark = EMARK; + +static enum CmdStatus cmd_insert(Text *txt, const char *cmd) { + char data[BUFSIZ]; + size_t pos; + if (sscanf(cmd, "%zu %s\n", &pos, data) != 2) + return CMD_ERR; + size_t len = strlen(data); + return text_insert(txt, pos, data, len); +} + +static enum CmdStatus cmd_delete(Text *txt, const char *cmd) { + size_t pos, len; + if (sscanf(cmd, "%zu %zu", &pos, &len) != 2) + return CMD_ERR; + return text_delete(txt, pos, len); +} + +static enum CmdStatus cmd_size(Text *txt, const char *cmd) { + printf("%zu bytes\n", text_size(txt)); + return CMD_OK; +} + +static enum CmdStatus cmd_snapshot(Text *txt, const char *cmd) { + text_snapshot(txt); + return CMD_OK; +} + +static enum CmdStatus cmd_undo(Text *txt, const char *cmd) { + return text_undo(txt) != EPOS; +} + +static enum CmdStatus cmd_redo(Text *txt, const char *cmd) { + return text_redo(txt) != EPOS; +} + +static enum CmdStatus cmd_earlier(Text *txt, const char *cmd) { + return text_earlier(txt, 1) != EPOS; +} + +static enum CmdStatus cmd_later(Text *txt, const char *cmd) { + return text_later(txt, 1) != EPOS; +} + +static enum CmdStatus cmd_mark_set(Text *txt, const char *cmd) { + size_t pos; + if (sscanf(cmd, "%zu\n", &pos) != 1) + return CMD_ERR; + Mark m = text_mark_set(txt, pos); + if (m != EMARK) + mark = m; + return m != EMARK; +} + +static enum CmdStatus cmd_mark_get(Text *txt, const char *cmd) { + size_t pos = text_mark_get(txt, mark); + if (pos != EPOS) + printf("%zu\n", pos); + return pos != EPOS; +} + +static enum CmdStatus cmd_print(Text *txt, const char *cmd) { + size_t start = 0, size = text_size(txt), rem = size; + for (Iterator it = text_iterator_get(txt, start); + rem > 0 && text_iterator_valid(&it); + text_iterator_next(&it)) { + size_t prem = it.end - it.text; + if (prem > rem) + prem = rem; + if (fwrite(it.text, prem, 1, stdout) != 1) + return CMD_ERR; + rem -= prem; + } + if (rem != size) + puts(""); + return rem == 0; +} + +static enum CmdStatus cmd_quit(Text *txt, const char *cmd) { + return CMD_QUIT; +} + +static Cmd commands[] = { + ['-'] = cmd_earlier, + ['+'] = cmd_later, + ['?'] = cmd_mark_get, + ['='] = cmd_mark_set, + ['#'] = cmd_size, + ['d'] = cmd_delete, + ['i'] = cmd_insert, + ['p'] = cmd_print, + ['q'] = cmd_quit, + ['r'] = cmd_redo, + ['s'] = cmd_snapshot, + ['u'] = cmd_undo, +}; + +int main(int argc, char *argv[]) { + char line[BUFSIZ], *name = (argc == 1) ? NULL : argv[1]; + Text *txt = text_load(name); + if (!name) + name = "-"; + if (!txt) { + fprintf(stderr, "Failed to load text from `%s'\n", name); + return 1; + } + + printf("Loaded %zu bytes from `%s'\n", text_size(txt), name); + + for (;;) { + printf("> "); + if (!fgets(line, sizeof(line), stdin)) + break; + if (!isatty(0)) + printf("%s", line); + if (line[0] == '\n') + continue; + size_t idx = line[0]; + if (idx < LENGTH(commands) && commands[idx]) { + enum CmdStatus ret = commands[idx](txt, line+1); + printf("%s", cmd_status_msg[ret]); + if (ret == CMD_QUIT) + break; + } else { + puts("Invalid command"); + } + } + + text_free(txt); + + return 0; +} -- cgit v1.2.3