]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
fuzz: adds eight fuzz targets
authorPhilippe Antoine <contact@catenacyber.fr>
Mon, 27 May 2019 15:02:22 +0000 (17:02 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 20 Mar 2020 12:21:52 +0000 (13:21 +0100)
And ways to compile them with enable-fuzztargets at configure time
Adds utility function in util-unittest-helper

14 files changed:
configure.ac
src/Makefile.am
src/tests/fuzz/README [new file with mode: 0644]
src/tests/fuzz/fuzz_applayerparserparse.c [new file with mode: 0644]
src/tests/fuzz/fuzz_applayerprotodetectgetproto.c [new file with mode: 0644]
src/tests/fuzz/fuzz_confyamlloadstring.c [new file with mode: 0644]
src/tests/fuzz/fuzz_decodeder.c [new file with mode: 0644]
src/tests/fuzz/fuzz_decodepcapfile.c [new file with mode: 0644]
src/tests/fuzz/fuzz_mimedecparseline.c [new file with mode: 0644]
src/tests/fuzz/fuzz_siginit.c [new file with mode: 0644]
src/tests/fuzz/fuzz_sigpcap.c [new file with mode: 0644]
src/tests/fuzz/onefile.c [new file with mode: 0644]
src/util-unittest-helper.c
src/util-unittest-helper.h

index 187b73db41474943130ee66e63d914617dc28cf8..0d73ef77415f939a5b55900da189d3676b265f57 100644 (file)
@@ -3,7 +3,7 @@
     AC_CONFIG_HEADERS([config.h])
     AC_CONFIG_SRCDIR([src/suricata.c])
     AC_CONFIG_MACRO_DIR(m4)
-    AM_INIT_AUTOMAKE([tar-ustar])
+    AM_INIT_AUTOMAKE([tar-ustar subdir-objects])
 
     AC_LANG([C])
     AC_PROG_CC_C99
 
 # options
 
+
+    AC_ARG_ENABLE(fuzztargets,
+        AS_HELP_STRING([--enable-fuzztargets], [Enable fuzz targets]),[enable_fuzztargets=$enableval],[enable_fuzztargets=no])
+    AM_CONDITIONAL([BUILD_FUZZTARGETS], [test "x$enable_fuzztargets" = "xyes"])
+    AC_PROG_CXX
+    AS_IF([test "x$enable_fuzztargets" = "xyes"], [
+        AC_DEFINE([AFLFUZZ_NO_RANDOM], [1], [Disable all use of random functions])
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[while (__AFL_LOOP(1000))]])],
+                [AC_DEFINE([AFLFUZZ_PERSISTANT_MODE], [1], [Enable AFL PERSISTANT_MODE])],
+                [])
+        AC_LANG_PUSH(C++)
+        tmp_saved_flags=$[]_AC_LANG_PREFIX[]FLAGS
+        AS_IF([test "x$LIB_FUZZING_ENGINE" = "x"], [
+            LIB_FUZZING_ENGINE=-fsanitize=fuzzer
+            AC_SUBST(LIB_FUZZING_ENGINE)
+        ])
+        _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $LIB_FUZZING_ENGINE"
+        AC_MSG_CHECKING([whether $CXX accepts $LIB_FUZZING_ENGINE])
+        AC_LINK_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+extern "C" int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
+extern "C" int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size) {
+(void)Data;
+(void)Size;
+return 0;
+}
+            ]])],
+            [ AC_MSG_RESULT(yes)
+              has_sanitizefuzzer=yes],
+            [ AC_MSG_RESULT(no) ]
+        )
+        _AC_LANG_PREFIX[]FLAGS=$tmp_saved_flags
+        AC_LANG_POP()
+    ])
+
+    AM_CONDITIONAL([HAS_FUZZLDFLAGS], [test "x$has_sanitizefuzzer" = "xyes"])
+
   # enable the running of unit tests
     AC_ARG_ENABLE(unittests,
            AS_HELP_STRING([--enable-unittests], [Enable compilation of the unit tests]),[enable_unittests=$enableval],[enable_unittests=no])
+    AS_IF([test "x$enable_fuzztargets" = "xyes"], [
+        export enable_unittests="yes"
+    ])
     AS_IF([test "x$enable_unittests" = "xyes"], [
         AC_DEFINE([UNITTESTS],[1],[Enable built-in unittests])
     ])
index 783ec2bc7858ee170857d2a6d681b7452c4403a5..6788ee2dc082651e7c6ce1816ecbe0349f5e2772 100755 (executable)
@@ -6,8 +6,14 @@ noinst_HEADERS = action-globals.h \
        suricata-common.h threadvars.h tree.h \
     util-validate.h
 bin_PROGRAMS = suricata
+if BUILD_FUZZTARGETS
+    bin_PROGRAMS += fuzz_applayerprotodetectgetproto \
+    fuzz_applayerparserparse fuzz_siginit \
+    fuzz_confyamlloadstring fuzz_decodepcapfile \
+    fuzz_sigpcap fuzz_mimedecparseline fuzz_decodeder
+endif
 
-suricata_SOURCES = \
+COMMON_SOURCES = \
 alert-debuglog.c alert-debuglog.h \
 alert-fastlog.c alert-fastlog.h \
 alert-prelude.c alert-prelude.h \
@@ -324,7 +330,6 @@ log-stats.c log-stats.h \
 log-tcp-data.c log-tcp-data.h \
 log-tlslog.c log-tlslog.h \
 log-tlsstore.c log-tlsstore.h \
-main.c \
 output.c output.h \
 output-file.c output-file.h \
 output-filedata.c output-filedata.h \
@@ -542,11 +547,116 @@ EXTRA_DIST = tests
 # set the include path found by configure
 AM_CPPFLAGS = $(all_includes)
 
+suricata_SOURCES = main.c $(COMMON_SOURCES)
+
 # the library search path.
 suricata_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
 suricata_LDADD = $(HTP_LDADD) $(RUST_LDADD)
 suricata_DEPENDENCIES = $(RUST_SURICATA_LIB)
 
+nodist_fuzz_applayerprotodetectgetproto_SOURCES = tests/fuzz/fuzz_applayerprotodetectgetproto.c $(COMMON_SOURCES)
+fuzz_applayerprotodetectgetproto_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_applayerprotodetectgetproto_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_applayerprotodetectgetproto_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_applayerprotodetectgetproto_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_applayerprotodetectgetproto_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_applayerprotodetectgetproto_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_applayerparserparse_SOURCES = tests/fuzz/fuzz_applayerparserparse.c $(COMMON_SOURCES)
+fuzz_applayerparserparse_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_applayerparserparse_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_applayerparserparse_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_applayerparserparse_SOURCES += tests/fuzz/onefile.c
+endif
+fuzz_applayerparserparse_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_applayerparserparse_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_siginit_SOURCES = tests/fuzz/fuzz_siginit.c $(COMMON_SOURCES)
+fuzz_siginit_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_siginit_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_siginit_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_siginit_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_siginit_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_siginit_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_confyamlloadstring_SOURCES = tests/fuzz/fuzz_confyamlloadstring.c $(COMMON_SOURCES)
+fuzz_confyamlloadstring_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_confyamlloadstring_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_confyamlloadstring_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_confyamlloadstring_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_confyamlloadstring_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_confyamlloadstring_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_decodepcapfile_SOURCES = tests/fuzz/fuzz_decodepcapfile.c $(COMMON_SOURCES)
+fuzz_decodepcapfile_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_decodepcapfile_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_decodepcapfile_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_decodepcapfile_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_decodepcapfile_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_decodepcapfile_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_sigpcap_SOURCES = tests/fuzz/fuzz_sigpcap.c $(COMMON_SOURCES)
+fuzz_sigpcap_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_sigpcap_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_sigpcap_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_sigpcap_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_sigpcap_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_sigpcap_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_mimedecparseline_SOURCES = tests/fuzz/fuzz_mimedecparseline.c $(COMMON_SOURCES)
+fuzz_mimedecparseline_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_mimedecparseline_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_mimedecparseline_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_mimedecparseline_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_mimedecparseline_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_mimedecparseline_LDFLAGS) $(LDFLAGS) -o $@
+
+nodist_fuzz_decodeder_SOURCES = tests/fuzz/fuzz_decodeder.c $(COMMON_SOURCES)
+fuzz_decodeder_LDFLAGS = $(all_libraries) ${SECLDFLAGS}
+fuzz_decodeder_LDADD = $(RUST_SURICATA_LIB) $(HTP_LDADD) $(RUST_LDADD)
+if HAS_FUZZLDFLAGS
+    fuzz_decodeder_LDFLAGS += $(LIB_FUZZING_ENGINE)
+else
+    nodist_fuzz_decodeder_SOURCES += tests/fuzz/onefile.c
+endif
+# force usage of CXX for linker
+fuzz_decodeder_LINK=$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+    $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) $(CXXFLAGS) \
+    $(fuzz_decodeder_LDFLAGS) $(LDFLAGS) -o $@
+
 # default CFLAGS
 AM_CFLAGS = ${OPTIMIZATION_CFLAGS} ${GCC_CFLAGS} ${CLANG_CFLAGS}            \
     ${SECCFLAGS} ${PCAP_CFLAGS} -DLOCAL_STATE_DIR=\"$(localstatedir)\"      \
diff --git a/src/tests/fuzz/README b/src/tests/fuzz/README
new file mode 100644 (file)
index 0000000..316b25e
--- /dev/null
@@ -0,0 +1,57 @@
+How to run fuzzing ?
+
+1) With oss-fuzz
+- install docker
+- run git clone --branch suricata --depth 1 https://github.com/catenacyber/oss-fuzz
+(we will use the original google repo once we merge this)
+- change directory into cloned repository : cd oss-fuzz
+- run python infra/helper.py build_image suricata
+- run python infra/helper.py build_fuzzers --sanitizer address suricata
+You can use undefined sanitizer (memory sanitizer does not work yet see https://github.com/google/oss-fuzz/issues/2145#issuecomment-485781098
+- run python infra/helper.py run_fuzzer suricata fuzz_siginit
+(or another fuzz target, try ls build/out/suricata/fuzz_*)
+
+To generate coverage :
+- run python infra/helper.py build_fuzzers --sanitizer=coverage suricata
+- get a corpus cf https://github.com/google/oss-fuzz/issues/2490
+- put your corpus in build/corpus/suricata/<fuzz_target_name>/
+- run python infra/helper.py coverage --no-corpus-download suricata
+
+2) With libfuzzer
+
+To compile the fuzz targets, you should do the following :
+```
+export CFLAGS="-g -fsanitize=address,fuzzer-no-link"
+export LDFLAGS="-g -fsanitize=address"
+export CC=clang
+./configure --enable-fuzztargets
+make
+```
+
+You can specify other sanitizers here such as undefined and memory
+
+Then you can run a target :
+./src/.libs/fuzz_target_x your_libfuzzer_options
+Where target_x is on file in `ls ./src/.libs/fuzz_*`
+
+If your clang does not support the compile flag "-fsanitize=fuzzer" (MacOS), you can run these same commands but you need first to install libfuzzer as libFuzzingEngine and you need to add `export LIB_FUZZING_ENGINE=/path/to/libFuzzer.a` before calling configure command
+
+To compile libFuzzer, you can do the following
+```
+svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer
+cd fuzzer
+./build.sh
+```
+
+
+3) With afl
+
+To compile the fuzz targets, you simply need to run
+```
+CC=afl-gcc ./configure --enable-fuzztargets
+CC=afl-gcc make
+```
+You can rather use afl-clang if needed.
+
+Then you can run afl as usual with each of the fuzz targets in ./src/.libs/
+afl-fuzz your_afl_options -- ./src/.libs/fuzz_target_x @@
diff --git a/src/tests/fuzz/fuzz_applayerparserparse.c b/src/tests/fuzz/fuzz_applayerparserparse.c
new file mode 100644 (file)
index 0000000..4216006
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+
+#include "suricata-common.h"
+#include "app-layer-detect-proto.h"
+#include "flow-util.h"
+#include "app-layer-parser.h"
+#include "util-unittest-helper.h"
+
+#define HEADER_LEN 6
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+AppLayerParserThreadCtx *alp_tctx = NULL;
+
+/* input buffer is structured this way :
+ * 6 bytes header,
+ * then sequence of buffers separated by magic bytes 01 D5 CA 7A */
+
+/* The 6 bytes header is
+ * alproto
+ * proto
+ * source port (uint16_t)
+ * destination port (uint16_t) */
+
+const uint8_t separator[] = {0x01, 0xD5, 0xCA, 0x7A};
+SCInstance suricata;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    Flow * f;
+    TcpSession ssn;
+    const uint8_t * albuffer;
+    uint8_t * alnext;
+    size_t alsize;
+    // used to find under and overflows
+    // otherwise overflows do not fail as they read the next packet
+    uint8_t * isolatedBuffer;
+
+    if (size < HEADER_LEN) {
+        return 0;
+    }
+
+    if (alp_tctx == NULL) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+
+        InitGlobal();
+        run_mode = RUNMODE_PCAP_FILE;
+        GlobalsInitPreConfig();
+
+        //redirect logs to /tmp
+        ConfigSetLogDirectory("/tmp/");
+
+        PostConfLoadedSetup(&suricata);
+        alp_tctx = AppLayerParserThreadCtxAlloc();
+    }
+
+    if (data[0] >= ALPROTO_MAX) {
+        return 0;
+    }
+    //no UTHBuildFlow to have storage
+    f = FlowAlloc();
+    if (f == NULL) {
+        return 0;
+    }
+    f->flags |= FLOW_IPV4;
+    f->src.addr_data32[0] = 0x01020304;
+    f->dst.addr_data32[0] = 0x05060708;
+    f->sp = (data[2] << 8) | data[3];
+    f->dp = (data[4] << 8) | data[5];
+    f->proto = data[1];
+    memset(&ssn, 0, sizeof(TcpSession));
+    f->protoctx = &ssn;
+    f->protomap = FlowGetProtoMapping(f->proto);
+    f->alproto = data[0];
+
+    /*
+     * We want to fuzz multiple calls to AppLayerParserParse
+     * because some parts of the code are only reached after
+     * multiple packets (in SMTP for example).
+     * So we treat our input as a list of buffers with magic separator.
+     */
+    albuffer = data + HEADER_LEN;
+    alsize = size - HEADER_LEN;
+    uint8_t flags = STREAM_START;
+    int flip = 0;
+    alnext = memmem(albuffer, alsize, separator, 4);
+    while (alnext) {
+        if (flip) {
+            flags |= STREAM_TOCLIENT;
+            flags &= ~(STREAM_TOSERVER);
+            flip = 0;
+        } else {
+            flags |= STREAM_TOSERVER;
+            flags &= ~(STREAM_TOCLIENT);
+            flip = 1;
+        }
+
+        if (alnext != albuffer) {
+            // only if we have some data
+            isolatedBuffer = malloc(alnext - albuffer);
+            if (isolatedBuffer == NULL) {
+                return 0;
+            }
+            memcpy(isolatedBuffer, albuffer, alnext - albuffer);
+            (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alnext - albuffer);
+            free(isolatedBuffer);
+            flags &= ~(STREAM_START);
+        }
+        alsize -= alnext - albuffer + 4;
+        albuffer = alnext + 4;
+        if (alsize == 0) {
+            break;
+        }
+        alnext = memmem(albuffer, alsize, separator, 4);
+    }
+    if (alsize > 0 ) {
+        flags |= STREAM_EOF;
+        isolatedBuffer = malloc(alsize);
+        if (isolatedBuffer == NULL) {
+            return 0;
+        }
+        memcpy(isolatedBuffer, albuffer, alsize);
+        (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alsize);
+        free(isolatedBuffer);
+    }
+
+    FlowFree(f);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c
new file mode 100644 (file)
index 0000000..71202ce
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+
+#include "suricata-common.h"
+#include "app-layer-detect-proto.h"
+#include "flow-util.h"
+#include "app-layer-parser.h"
+#include "util-unittest-helper.h"
+
+
+#define HEADER_LEN 6
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+AppLayerProtoDetectThreadCtx *alpd_tctx = NULL;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    Flow *f;
+    TcpSession ssn;
+    bool reverse;
+
+    if (size < HEADER_LEN) {
+        return 0;
+    }
+
+    if (alpd_tctx == NULL) {
+        //global init
+        InitGlobal();
+        run_mode = RUNMODE_UNITTEST;
+        MpmTableSetup();
+        SpmTableSetup();
+        AppLayerProtoDetectSetup();
+        AppLayerParserSetup();
+        AppLayerParserRegisterProtocolParsers();
+        alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    }
+
+    f = UTHBuildFlow(AF_INET, "1.2.3.4", "5.6.7.8", (data[2] << 8) | data[3], (data[4] << 8) | data[5]);
+    if (f == NULL) {
+        return 0;
+    }
+    f->proto = data[1];
+    memset(&ssn, 0, sizeof(TcpSession));
+    f->protoctx = &ssn;
+    f->protomap = FlowGetProtoMapping(f->proto);
+
+    AppLayerProtoDetectGetProto(alpd_tctx, f, data+HEADER_LEN, size-HEADER_LEN, f->proto, data[0], &reverse);
+    UTHFreeFlow(f);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_confyamlloadstring.c b/src/tests/fuzz/fuzz_confyamlloadstring.c
new file mode 100644 (file)
index 0000000..2f897cc
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for ConfYamlLoadString
+ */
+
+
+#include "suricata-common.h"
+#include "conf-yaml-loader.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    if (initialized == 0) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+        //global init
+        InitGlobal();
+        run_mode = RUNMODE_UNITTEST;
+        initialized = 1;
+    }
+
+    ConfYamlLoadString((const char *) data, size);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_decodeder.c b/src/tests/fuzz/fuzz_decodeder.c
new file mode 100644 (file)
index 0000000..7fc4fb7
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for DecodeDer
+ */
+
+
+#include "suricata-common.h"
+#include "util-decode-der.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    if (initialized == 0) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+        //global init
+        InitGlobal();
+        run_mode = RUNMODE_UNITTEST;
+        initialized = 1;
+    }
+    uint32_t errcode = 0;
+
+    Asn1Generic *a = DecodeDer(data, size, &errcode);
+    DerFree(a);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_decodepcapfile.c b/src/tests/fuzz/fuzz_decodepcapfile.c
new file mode 100644 (file)
index 0000000..28cf661
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+
+#include <pcap/pcap.h>
+
+#include "suricata-common.h"
+#include "app-layer-detect-proto.h"
+#include "defrag.h"
+#include "tm-modules.h"
+#include "tm-threads.h"
+#include "source-pcap-file.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+SCInstance suricata;
+
+const char configNoChecksum[] = "\
+%YAML 1.1\n\
+---\n\
+pcap-file:\n\
+\n\
+  checksum-checks: no\n\
+";
+
+ThreadVars *tv;
+DecodeThreadVars *dtv;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    void *ptv = NULL;
+
+    if (initialized == 0) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+
+        InitGlobal();
+        run_mode = RUNMODE_PCAP_FILE;
+
+        //redirect logs to /tmp
+        ConfigSetLogDirectory("/tmp/");
+        //disables checksums validation for fuzzing
+        if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+            abort();
+        }
+
+        PostConfLoadedSetup(&suricata);
+
+        RunModeInitialize();
+        TimeModeSetOffline();
+        PcapFileGlobalInit();
+
+        tv = TmThreadCreatePacketHandler("fuzz",
+                                         "packetpool", "packetpool",
+                                         "packetpool", "packetpool",
+                                         "pktacqloop");
+        if (tv == NULL) {
+            return 0;
+        }
+        TmModule *tm_module = TmModuleGetByName("ReceivePcapFile");
+        if (tm_module == NULL) {
+            return 0;
+        }
+        TmSlotSetFuncAppend(tv, tm_module, "/tmp/fuzz.pcap");
+        tm_module = TmModuleGetByName("DecodePcapFile");
+        if (tm_module == NULL) {
+            return 0;
+        }
+        TmSlotSetFuncAppend(tv, tm_module, NULL);
+        tmm_modules[TMM_DECODEPCAPFILE].ThreadInit(tv, NULL, (void **) &dtv);
+        (void)SC_ATOMIC_SET(tv->tm_slots->slot_next->slot_data, dtv);
+
+        PacketPoolInit();
+
+        initialized = 1;
+    }
+
+    //rewrite buffer to a file as libpcap does not have buffer inputs
+    if (UTHbufferToFile("/tmp/fuzz.pcap", data, size) < 0) {
+        return 0;
+    }
+
+    if (tmm_modules[TMM_RECEIVEPCAPFILE].ThreadInit(tv, "/tmp/fuzz.pcap", &ptv) == TM_ECODE_OK && ptv != NULL) {
+        suricata_ctl_flags = 0;
+        tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqLoop(tv, ptv, tv->tm_slots);
+        tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit(tv, ptv);
+    }
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_mimedecparseline.c b/src/tests/fuzz/fuzz_mimedecparseline.c
new file mode 100644 (file)
index 0000000..2230d58
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for ConfYamlLoadString
+ */
+
+
+#include "suricata-common.h"
+#include "util-decode-mime.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+static int dummy = 0;
+
+static int MimeParserDataFromFileCB(const uint8_t *chunk, uint32_t len,
+                                    MimeDecParseState *state)
+{
+    if (len > 0 && chunk[len-1] == 0) {
+        // do not get optimizd away
+        dummy++;
+    }
+    return MIME_DEC_OK;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    if (initialized == 0) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+        //global init
+        InitGlobal();
+        run_mode = RUNMODE_UNITTEST;
+        initialized = 1;
+    }
+
+    uint32_t line_count = 0;
+
+    MimeDecParseState *state = MimeDecInitParser(&line_count, MimeParserDataFromFileCB);
+    MimeDecEntity *msg_head = state->msg;
+    const uint8_t * buffer = data;
+    while (1) {
+        uint8_t * next = memchr(buffer, '\n', size);
+        if (next == NULL) {
+            (void) MimeDecParseLine(buffer, size, 1, state);
+            break;
+        } else {
+            (void) MimeDecParseLine(buffer, next - buffer, 1, state);
+            if (buffer + size < next + 1) {
+                break;
+            }
+            size -= next - buffer + 1;
+            buffer = next + 1;
+        }
+    }
+    /* Completed */
+    (void)MimeDecParseComplete(state);
+    /* De Init parser */
+    MimeDecDeInitParser(state);
+    MimeDecFreeEntity(msg_head);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_siginit.c b/src/tests/fuzz/fuzz_siginit.c
new file mode 100644 (file)
index 0000000..8567839
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for SigInit
+ */
+
+
+#include "suricata-common.h"
+#include "util-reference-config.h"
+#include "util-classification-config.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+DetectEngineCtx *de_ctx = NULL;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    if (de_ctx == NULL) {
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+        //global init
+        InitGlobal();
+        run_mode = RUNMODE_UNITTEST;
+        MpmTableSetup();
+        SpmTableSetup();
+        SigTableSetup();
+        SCReferenceConfInit();
+        SCClassConfInit();
+        de_ctx = DetectEngineCtxInit();
+    }
+
+    char * buffer = malloc(size+1);
+    if (buffer) {
+        memcpy(buffer, data, size);
+        //null terminate string
+        buffer[size] = 0;
+        Signature *s = SigInit(de_ctx, buffer);
+        free(buffer);
+        SigFree(s);
+    }
+
+    return 0;
+}
diff --git a/src/tests/fuzz/fuzz_sigpcap.c b/src/tests/fuzz/fuzz_sigpcap.c
new file mode 100644 (file)
index 0000000..c4e05d7
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+
+#include <pcap/pcap.h>
+
+#include "suricata-common.h"
+#include "source-pcap-file.h"
+#include "detect-engine.h"
+#include "util-classification-config.h"
+#include "util-reference-config.h"
+#include "app-layer.h"
+#include "tm-queuehandlers.h"
+#include "util-cidr.h"
+#include "util-proto-name.h"
+#include "detect-engine-tag.h"
+#include "detect-engine-threshold.h"
+#include "host-bit.h"
+#include "ippair-bit.h"
+#include "app-layer-htp.h"
+#include "util-decode-asn1.h"
+#include "detect-fast-pattern.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+
+static int initialized = 0;
+ThreadVars tv;
+DecodeThreadVars *dtv;
+//FlowWorkerThreadData
+void *fwd;
+SCInstance suricata;
+
+const char configNoChecksum[] = "\
+%YAML 1.1\n\
+---\n\
+pcap-file:\n\
+\n\
+  checksum-checks: no\n\
+\n\
+stream:\n\
+\n\
+  checksum-validation: no\n\
+";
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    pcap_t * pkts;
+    char errbuf[PCAP_ERRBUF_SIZE];
+    const u_char *pkt;
+    struct pcap_pkthdr *header;
+    int r;
+    Packet *p;
+    size_t pos;
+
+    if (initialized == 0) {
+        //Redirects logs to /dev/null
+        setenv("SC_LOG_OP_IFACE", "file", 0);
+        setenv("SC_LOG_FILE", "/dev/null", 0);
+
+        InitGlobal();
+
+        run_mode = RUNMODE_PCAP_FILE;
+        //redirect logs to /tmp
+        ConfigSetLogDirectory("/tmp/");
+        //disables checksums validation for fuzzing
+        if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+            abort();
+        }
+        suricata.sig_file = strdup("/tmp/fuzz.rules");
+        suricata.sig_file_exclusive = 1;
+        //loads rules after init
+        suricata.delayed_detect = 1;
+
+        SupportFastPatternForSigMatchTypes();
+        PostConfLoadedSetup(&suricata);
+
+        //dummy init before DetectEngineReload
+        DetectEngineCtx * de_ctx = DetectEngineCtxInit();
+        de_ctx->flags |= DE_QUIET;
+        DetectEngineAddToMaster(de_ctx);
+
+        memset(&tv, 0, sizeof(tv));
+        dtv = DecodeThreadVarsAlloc(&tv);
+        DecodeRegisterPerfCounters(dtv, &tv);
+        tmm_modules[TMM_FLOWWORKER].ThreadInit(&tv, NULL, &fwd);
+        StatsSetupPrivate(&tv);
+
+        initialized = 1;
+    }
+
+    /* TODO add yaml config
+     for (pos = 0; pos < size; pos++) {
+        if (data[pos] == 0) {
+            break;
+        }
+    }
+    if (ConfYamlLoadString(data, pos) != 0) {
+        return 0;
+    }
+    if (pos < size) {
+        //skip zero
+        pos++;
+    }
+    data += pos;
+    size -= pos;*/
+
+    for (pos=0; pos < size; pos++) {
+        if (data[pos] == 0) {
+            break;
+        }
+    }
+    if (pos > 0 && pos < size) {
+        // dump signatures to a file so as to reuse SigLoadSignatures
+        if (UTHbufferToFile(suricata.sig_file, data, pos-1) < 0) {
+            return 0;
+        }
+    } else {
+        if (UTHbufferToFile(suricata.sig_file, data, pos) < 0) {
+            return 0;
+        }
+    }
+
+    if (DetectEngineReload(&suricata) < 0) {
+        return 0;
+    }
+    if (pos < size) {
+        //skip zero
+        pos++;
+    }
+    data += pos;
+    size -= pos;
+
+    //rewrite buffer to a file as libpcap does not have buffer inputs
+    if (UTHbufferToFile("/tmp/fuzz.pcap", data, size) < 0) {
+        return 0;
+    }
+
+    //initialize structure
+    pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf);
+    if (pkts == NULL) {
+        return 0;
+    }
+
+    //loop over packets
+    r = pcap_next_ex(pkts, &header, &pkt);
+    p = PacketGetFromAlloc();
+    p->datalink = pcap_datalink(pkts);
+    while (r > 0) {
+        PacketCopyData(p, pkt, header->caplen);
+        //DecodePcapFile
+        TmEcode ecode = tmm_modules[TMM_DECODEPCAPFILE].Func(&tv, p, dtv);
+        if (ecode == TM_ECODE_FAILED) {
+            break;
+        }
+        Packet *extra_p = PacketDequeueNoLock(&tv.decode_pq);
+        while (extra_p != NULL) {
+            PacketFree(extra_p);
+            extra_p = PacketDequeueNoLock(&tv.decode_pq);
+        }
+        tmm_modules[TMM_FLOWWORKER].Func(&tv, p, fwd);
+        extra_p = PacketDequeueNoLock(&tv.decode_pq);
+        while (extra_p != NULL) {
+            PacketFree(extra_p);
+            extra_p = PacketDequeueNoLock(&tv.decode_pq);
+        }
+        r = pcap_next_ex(pkts, &header, &pkt);
+    }
+    //close structure
+    pcap_close(pkts);
+    PacketFree(p);
+
+    return 0;
+}
diff --git a/src/tests/fuzz/onefile.c b/src/tests/fuzz/onefile.c
new file mode 100644 (file)
index 0000000..8ea7ac9
--- /dev/null
@@ -0,0 +1,50 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+int main(int argc, char** argv)
+{
+    FILE * fp;
+    uint8_t *data;
+    size_t size;
+
+    if (argc != 2) {
+        return 1;
+    }
+    //opens the file, get its size, and reads it into a buffer
+    fp = fopen(argv[1], "rb");
+    if (fp == NULL) {
+        return 2;
+    }
+    if (fseek(fp, 0L, SEEK_END) != 0) {
+        fclose(fp);
+        return 2;
+    }
+    size = ftell(fp);
+    if (size == (size_t) -1) {
+        fclose(fp);
+        return 2;
+    }
+    if (fseek(fp, 0L, SEEK_SET) != 0) {
+        fclose(fp);
+        return 2;
+    }
+    data = malloc(size);
+    if (data == NULL) {
+        fclose(fp);
+        return 2;
+    }
+    if (fread(data, size, 1, fp) != 1) {
+        fclose(fp);
+        free(data);
+        return 2;
+    }
+
+    //lauch fuzzer
+    LLVMFuzzerTestOneInput(data, size);
+    free(data);
+    fclose(fp);
+    return 0;
+}
index a76ee804d965967f3ff07f9e9b4b703b15fb7028..0078df7e9f1f541e31d70335f41ee7176daea43c 100644 (file)
@@ -942,6 +942,27 @@ int UTHParseSignature(const char *str, bool expect)
     PASS;
 }
 
+/** \brief writes the contents of a buffer into a file */
+int UTHbufferToFile(const char * name, const uint8_t *data, size_t size) {
+    FILE * fd;
+    if (remove(name) != 0) {
+        if (errno != ENOENT) {
+            printf("failed remove, errno=%d\n", errno);
+            return -1;
+        }
+    }
+    fd = fopen(name, "wb");
+    if (fd == NULL) {
+        printf("failed open, errno=%d\n", errno);
+        return -2;
+    }
+    if (fwrite (data, 1, size, fd) != size) {
+        fclose(fd);
+        return -3;
+    }
+    fclose(fd);
+    return 0;
+}
 
 /*
  * unittests for the unittest helpers
index d0a0972711b6a8a835914b20c6b1f13f0e5e011e..05e7963ffd5cb72329d863f4d7a8692789c912d8 100644 (file)
@@ -63,6 +63,7 @@ Packet *UTHBuildPacketIPV6Real(uint8_t *, uint16_t , uint8_t ipproto, const char
 
 void * UTHmemsearch(const void *big, size_t big_len, const void *little, size_t little_len);
 int UTHParseSignature(const char *str, bool expect);
+int UTHbufferToFile(const char * name, const uint8_t *data, size_t size);
 #endif
 
 void UTHRegisterTests(void);