aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2020-10-11 19:59:31 +0200
committerPřemysl Eric Janouch <p@janouch.name>2020-10-11 20:04:34 +0200
commitdf3f53bd5c370dcd46074189f3ad545c6aab942b (patch)
treebe27ba0d62132173f9bd0f9a2d5779792e92df65
parente029aae1d3d1884ca868c3694bdec0456b3e8267 (diff)
downloadliberty-df3f53bd5c370dcd46074189f3ad545c6aab942b.tar.gz
liberty-df3f53bd5c370dcd46074189f3ad545c6aab942b.tar.xz
liberty-df3f53bd5c370dcd46074189f3ad545c6aab942b.zip
Add a basic fuzzing framework using libFuzzer
Updates #1
-rwxr-xr-xfuzz18
-rw-r--r--tests/fuzz.c210
-rw-r--r--tests/proto.c6
3 files changed, 232 insertions, 2 deletions
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 <p@janouch.name>
+ *
+ * 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, &parameters);
+ free (type);
+ free (subtype);
+ str_map_free (&parameters);
+
+ 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 (&parameters, "charset"), "utf-8"));
+ free (type);
+ free (subtype);
str_map_free (&parameters);
- 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"));