]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-test: Add fuzzing framework
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 23 Mar 2020 16:29:16 +0000 (18:29 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 8 Jan 2021 14:53:44 +0000 (14:53 +0000)
src/lib-test/Makefile.am
src/lib-test/fuzzer.c [new file with mode: 0644]
src/lib-test/fuzzer.h [new file with mode: 0644]

index 6c472fcbf1c73c9f20b74b8520c61811185989c0..ce7417d6428546880593a75a9553bbd900ca7cb9 100644 (file)
@@ -5,12 +5,14 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-charset
 
 libtest_la_SOURCES = \
+       fuzzer.c \
        test-common.c \
        test-istream.c \
        test-ostream.c \
        test-subprocess.c
 
 headers = \
+       fuzzer.h \
        test-common.h \
        test-subprocess.h
 
diff --git a/src/lib-test/fuzzer.c b/src/lib-test/fuzzer.c
new file mode 100644 (file)
index 0000000..5279017
--- /dev/null
@@ -0,0 +1,79 @@
+/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "lib-signals.h"
+#include "net.h"
+#include "istream.h"
+#include "ostream.h"
+#include "iostream-pump.h"
+#include "fuzzer.h"
+
+#include <sys/socket.h>
+#include <unistd.h>
+
+void fuzzer_init(struct fuzzer_context *fuzz_ctx)
+{
+       i_zero(fuzz_ctx);
+       if (!lib_is_initialized()) {
+               lib_init();
+               lib_signals_init();
+               lib_signals_ignore(SIGPIPE, TRUE);
+       }
+       fuzz_ctx->fd = -1;
+}
+
+void fuzzer_deinit(struct fuzzer_context *fuzz_ctx)
+{
+       iostream_pump_destroy(&fuzz_ctx->pump);
+       /* ensure fd gets closed, we don't care
+          if this fails. */
+       if (fuzz_ctx->fd > -1)
+               (void)close(fuzz_ctx->fd);
+       io_loop_destroy(&fuzz_ctx->ioloop);
+}
+
+static void pump_finished(enum iostream_pump_status status ATTR_UNUSED,
+                         struct fuzzer_context *fuzz_ctx)
+{
+       struct istream *input = iostream_pump_get_input(fuzz_ctx->pump);
+       struct ostream *output = iostream_pump_get_output(fuzz_ctx->pump);
+
+       switch (status) {
+       case IOSTREAM_PUMP_STATUS_INPUT_EOF:
+               break;
+       case IOSTREAM_PUMP_STATUS_INPUT_ERROR:
+               i_error("read(%s) failed: %s", i_stream_get_name(input),
+                       i_stream_get_error(input));
+               break;
+       case IOSTREAM_PUMP_STATUS_OUTPUT_ERROR:
+               i_error("write(%s) failed: %s", o_stream_get_name(output),
+                       o_stream_get_error(output));
+               break;
+       };
+
+       if (shutdown(o_stream_get_fd(output), SHUT_RDWR) < 0)
+               i_fatal("shutdown() failed: %m");
+       iostream_pump_destroy(&fuzz_ctx->pump);
+}
+
+int fuzzer_io_as_fd(struct fuzzer_context *fuzz_ctx,
+                  const uint8_t *data, size_t size)
+{
+       int sfd[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0)
+               i_fatal("socketpair() failed: %m");
+       net_set_nonblock(sfd[0], TRUE);
+       net_set_nonblock(sfd[1], TRUE);
+
+       struct istream *input = i_stream_create_from_data(data, size);
+       struct ostream *output = o_stream_create_fd_autoclose(&sfd[0], IO_BLOCK_SIZE);
+       fuzz_ctx->pump = iostream_pump_create(input, output);
+       fuzz_ctx->fd = sfd[1];
+       iostream_pump_set_completion_callback(fuzz_ctx->pump, pump_finished,
+                                             fuzz_ctx);
+       i_stream_unref(&input);
+       o_stream_unref(&output);
+       iostream_pump_start(fuzz_ctx->pump);
+       return sfd[1];
+}
diff --git a/src/lib-test/fuzzer.h b/src/lib-test/fuzzer.h
new file mode 100644 (file)
index 0000000..85d83c4
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef FUZZER_H
+#define FUZZER_H
+
+struct iostream_pump;
+struct ioloop;
+
+struct fuzzer_context {
+       int fd;
+       struct iostream_pump *pump;
+       struct ioloop *ioloop;
+};
+
+#define FUZZ_BEGIN_DATA(data_arg, size_arg) \
+       int LLVMFuzzerTestOneInput(data_arg, size_arg); \
+       int LLVMFuzzerTestOneInput(data_arg, size_arg) { \
+               struct fuzzer_context fuzz_ctx; \
+               fuzzer_init(&fuzz_ctx); T_BEGIN {
+
+#define FUZZ_BEGIN_STR(str_arg) \
+       FUZZ_BEGIN_DATA(const uint8_t *_param_data, size_t _param_size) \
+       str_arg = t_strndup(_param_data, _param_size);
+
+#define FUZZ_BEGIN_FD \
+       FUZZ_BEGIN_DATA(const uint8_t *_param_data, size_t _param_size) \
+       fuzz_ctx.ioloop = io_loop_create(); \
+       (void)fuzzer_io_as_fd(&fuzz_ctx, _param_data, _param_size);
+
+#define FUZZ_END \
+       } T_END; fuzzer_deinit(&fuzz_ctx); return 0; }
+
+void fuzzer_init(struct fuzzer_context *fuzz_ctx);
+void fuzzer_deinit(struct fuzzer_context *fuzz_ctx);
+
+int fuzzer_io_as_fd(struct fuzzer_context *fuzz_ctx,
+                   const uint8_t *data, size_t size);
+
+#endif