From df3f53bd5c370dcd46074189f3ad545c6aab942b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sun, 11 Oct 2020 19:59:31 +0200 Subject: Add a basic fuzzing framework using libFuzzer Updates #1 --- fuzz | 18 +++++ tests/fuzz.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/proto.c | 6 +- 3 files changed, 232 insertions(+), 2 deletions(-) create mode 100755 fuzz create mode 100644 tests/fuzz.c diff --git a/fuzz b/fuzz new file mode 100755 index 0000000..0964712 --- /dev/null +++ b/fuzz @@ -0,0 +1,18 @@ +#!/bin/sh +# I'm not sure how to make maximum use of this invention +# Make sure to have llvm-symbolizer installed +clang -g -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all \ + tests/fuzz.c -o fuzz-executor + +fuzz () { + echo "`tput bold`-- Fuzzing $1`tput sgr0`" + mkdir -p /tmp/corpus-$1 + ./fuzz-executor -test=$1 -artifact_prefix=$1- \ + -max_len=32 -max_total_time=600 -timeout=1 /tmp/corpus-$1 +} + +if [ $# -gt 0 ]; then + for test in "$@"; do fuzz $test; done +else + for test in $(./fuzz-executor); do fuzz $test; done +fi diff --git a/tests/fuzz.c b/tests/fuzz.c new file mode 100644 index 0000000..abc8eb7 --- /dev/null +++ b/tests/fuzz.c @@ -0,0 +1,210 @@ +/* + * tests/fuzz.c + * + * Copyright (c) 2020, PÅ™emysl Eric Janouch + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#define PROGRAM_NAME "fuzz" +#define PROGRAM_VERSION "0" + +#define LIBERTY_WANT_SSL +// The MPD client is a full wrapper and needs the network +#define LIBERTY_WANT_POLLER +#define LIBERTY_WANT_ASYNC + +#define LIBERTY_WANT_PROTO_IRC +#define LIBERTY_WANT_PROTO_HTTP +#define LIBERTY_WANT_PROTO_SCGI +#define LIBERTY_WANT_PROTO_FASTCGI +#define LIBERTY_WANT_PROTO_WS +#define LIBERTY_WANT_PROTO_MPD + +#include "../liberty.c" + +// --- UTF-8 ------------------------------------------------------------------- + +static void +test_utf8_validate (const uint8_t *data, size_t size) +{ + utf8_validate ((const char *) data, size); +} + +// --- Base 64 ----------------------------------------------------------------- + +static void +test_base64_decode (const uint8_t *data, size_t size) +{ + struct str out = str_make (); + base64_decode ((const char *) data, size, &out); + str_free (&out); +} + +// --- IRC --------------------------------------------------------------------- + +static void +test_irc_parse_message (const uint8_t *data, size_t size) +{ + struct str wrap = str_make (); + str_append_data (&wrap, data, size); + + struct irc_message msg; + irc_parse_message (&msg, wrap.str); + irc_free_message (&msg); + + str_free (&wrap); +} + +// --- HTTP -------------------------------------------------------------------- + +static void +test_http_parse_media_type (const uint8_t *data, size_t size) +{ + struct str wrap = str_make (); + str_append_data (&wrap, data, size); + + char *type = NULL; + char *subtype = NULL; + struct str_map parameters = str_map_make (free); + http_parse_media_type (wrap.str, &type, &subtype, ¶meters); + free (type); + free (subtype); + str_map_free (¶meters); + + str_free (&wrap); +} + +static void +test_http_parse_upgrade (const uint8_t *data, size_t size) +{ + struct str wrap = str_make (); + str_append_data (&wrap, data, size); + + struct http_protocol *protocols = NULL; + http_parse_upgrade (wrap.str, &protocols); + LIST_FOR_EACH (struct http_protocol, iter, protocols) + http_protocol_destroy (iter); + + str_free (&wrap); +} + +// --- SCGI -------------------------------------------------------------------- + +static bool +test_scgi_parser_on_headers_read (void *user_data) +{ + (void) user_data; + return true; +} + +static bool +test_scgi_parser_on_content (void *user_data, const void *data, size_t len) +{ + (void) user_data; + (void) data; + (void) len; + return true; +} + +static void +test_scgi_parser_push (const uint8_t *data, size_t size) +{ + struct scgi_parser parser = scgi_parser_make (); + parser.on_headers_read = test_scgi_parser_on_headers_read; + parser.on_content = test_scgi_parser_on_content; + + scgi_parser_push (&parser, data, size, NULL); + scgi_parser_free (&parser); +} + +// --- WebSockets -------------------------------------------------------------- + +static bool +test_websockets_on_frame_header (void *user_data, const struct ws_parser *self) +{ + (void) user_data; + (void) self; + return true; +} + +static bool +test_websockets_on_frame (void *user_data, const struct ws_parser *self) +{ + (void) user_data; + (void) self; + return true; +} + +static void +test_ws_parser_push (const uint8_t *data, size_t size) +{ + struct ws_parser parser = ws_parser_make (); + parser.on_frame_header = test_websockets_on_frame_header; + parser.on_frame = test_websockets_on_frame; + + ws_parser_push (&parser, data, size); + ws_parser_free (&parser); +} + +// --- Main -------------------------------------------------------------------- + +typedef void (*fuzz_test_fn) (const uint8_t *data, size_t size); +static fuzz_test_fn generator = NULL; + +void +LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + generator (data, size); +} + +int +LLVMFuzzerInitialize (int *argcp, char ***argvp) +{ + struct str_map targets = str_map_make (NULL); +#define REGISTER(name) str_map_set (&targets, #name, test_ ## name); + REGISTER (utf8_validate) + REGISTER (base64_decode) + REGISTER (irc_parse_message) + REGISTER (http_parse_media_type) + REGISTER (http_parse_upgrade) + REGISTER (scgi_parser_push) + REGISTER (ws_parser_push) + // TODO: add more parsers/processors + + char **argv = *argvp, *option = "-test=", *name = NULL; + for (int i = 1; i < *argcp; i++) + if (!strncmp (argv[i], option, strlen (option))) + { + name = argv[i] + strlen (option); + memmove (argv + i, argv + i + 1, (*argcp - i) * sizeof *argv); + (*argcp)--; + } + + if (!name) + { + struct str_map_iter iter = str_map_iter_make (&targets); + while (str_map_iter_next (&iter)) + printf ("%s\n", iter.link->key); + exit (EXIT_FAILURE); + } + + if (!(generator = str_map_find (&targets, name))) + { + fprintf (stderr, "Unknown test: %s\n", name); + exit (EXIT_FAILURE); + } + + str_map_free (&targets); + return 0; +} diff --git a/tests/proto.c b/tests/proto.c index c0b6d49..27afe1c 100644 --- a/tests/proto.c +++ b/tests/proto.c @@ -77,7 +77,7 @@ test_irc (void) static void test_http_parser (void) { - struct str_map parameters = str_map_make (NULL); + struct str_map parameters = str_map_make (free); parameters.key_xfrm = tolower_ascii_strxfrm; char *type = NULL; @@ -88,9 +88,11 @@ test_http_parser (void) soft_assert (!strcasecmp_ascii (subtype, "html")); soft_assert (parameters.len == 1); soft_assert (!strcmp (str_map_find (¶meters, "charset"), "utf-8")); + free (type); + free (subtype); str_map_free (¶meters); - struct http_protocol *protocols; + struct http_protocol *protocols = NULL; soft_assert (http_parse_upgrade ("websocket, HTTP/2.0, , ", &protocols)); soft_assert (!strcmp (protocols->name, "websocket")); -- cgit v1.2.3