From: Philippe Antoine Date: Mon, 27 May 2019 15:02:22 +0000 (+0200) Subject: fuzz: adds eight fuzz targets X-Git-Tag: suricata-6.0.0-beta1~615 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=600b0d7c5590b62f7efe27792332783101439562;p=thirdparty%2Fsuricata.git fuzz: adds eight fuzz targets And ways to compile them with enable-fuzztargets at configure time Adds utility function in util-unittest-helper --- diff --git a/configure.ac b/configure.ac index 187b73db41..0d73ef7741 100644 --- a/configure.ac +++ b/configure.ac @@ -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 @@ -459,9 +459,49 @@ # 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 +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]) ]) diff --git a/src/Makefile.am b/src/Makefile.am index 783ec2bc78..6788ee2dc0 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 0000000000..316b25ed60 --- /dev/null +++ b/src/tests/fuzz/README @@ -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// +- 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 index 0000000000..4216006277 --- /dev/null +++ b/src/tests/fuzz/fuzz_applayerparserparse.c @@ -0,0 +1,137 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..71202ce3df --- /dev/null +++ b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c @@ -0,0 +1,57 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..2f897ccc38 --- /dev/null +++ b/src/tests/fuzz/fuzz_confyamlloadstring.c @@ -0,0 +1,30 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..7fc4fb70e9 --- /dev/null +++ b/src/tests/fuzz/fuzz_decodeder.c @@ -0,0 +1,32 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..28cf661f6e --- /dev/null +++ b/src/tests/fuzz/fuzz_decodepcapfile.c @@ -0,0 +1,97 @@ +/** + * @file + * @author Philippe Antoine + * fuzz target for AppLayerProtoDetectGetProto + */ + + +#include + +#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 index 0000000000..2230d5891d --- /dev/null +++ b/src/tests/fuzz/fuzz_mimedecparseline.c @@ -0,0 +1,64 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..8567839061 --- /dev/null +++ b/src/tests/fuzz/fuzz_siginit.c @@ -0,0 +1,45 @@ +/** + * @file + * @author Philippe Antoine + * 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 index 0000000000..c4e05d73ba --- /dev/null +++ b/src/tests/fuzz/fuzz_sigpcap.c @@ -0,0 +1,179 @@ +/** + * @file + * @author Philippe Antoine + * fuzz target for AppLayerProtoDetectGetProto + */ + + +#include + +#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 index 0000000000..8ea7ac907d --- /dev/null +++ b/src/tests/fuzz/onefile.c @@ -0,0 +1,50 @@ +#include +#include +#include + +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; +} diff --git a/src/util-unittest-helper.c b/src/util-unittest-helper.c index a76ee804d9..0078df7e9f 100644 --- a/src/util-unittest-helper.c +++ b/src/util-unittest-helper.c @@ -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 diff --git a/src/util-unittest-helper.h b/src/util-unittest-helper.h index d0a0972711..05e7963ffd 100644 --- a/src/util-unittest-helper.h +++ b/src/util-unittest-helper.h @@ -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);