summaryrefslogtreecommitdiffstats
path: root/src/tests/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/fuzz')
-rw-r--r--src/tests/fuzz/fuzz_applayerparserparse.c213
-rw-r--r--src/tests/fuzz/fuzz_applayerprotodetectgetproto.c99
-rw-r--r--src/tests/fuzz/fuzz_confyamlloadstring.c31
-rw-r--r--src/tests/fuzz/fuzz_decodepcapfile.c101
-rw-r--r--src/tests/fuzz/fuzz_mimedecparseline.c66
-rw-r--r--src/tests/fuzz/fuzz_predefpcap_aware.c165
-rw-r--r--src/tests/fuzz/fuzz_siginit.c59
-rw-r--r--src/tests/fuzz/fuzz_sigpcap.c208
-rw-r--r--src/tests/fuzz/fuzz_sigpcap_aware.c203
-rw-r--r--src/tests/fuzz/onefile.c88
10 files changed, 1233 insertions, 0 deletions
diff --git a/src/tests/fuzz/fuzz_applayerparserparse.c b/src/tests/fuzz/fuzz_applayerparserparse.c
new file mode 100644
index 0000000..0ee263f
--- /dev/null
+++ b/src/tests/fuzz/fuzz_applayerparserparse.c
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerParserParse
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "app-layer-detect-proto.h"
+#include "flow-util.h"
+#include "app-layer-parser.h"
+#include "util-unittest-helper.h"
+#include "util-byte.h"
+#include "conf-yaml-loader.h"
+#include "util-conf.h"
+
+#define HEADER_LEN 6
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+AppLayerParserThreadCtx *alp_tctx = NULL;
+
+#include "confyaml.c"
+
+/* 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 surifuzz;
+AppProto forceLayer = 0;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ char *target_suffix = strrchr((*argv)[0], '_');
+ if (target_suffix != NULL) {
+ AppProto applayer = StringToAppProto(target_suffix + 1);
+ if (applayer != ALPROTO_UNKNOWN) {
+ forceLayer = applayer;
+ printf("Forcing %s=%" PRIu16 "\n", AppProtoToString(forceLayer), forceLayer);
+ return 0;
+ }
+ }
+ // else
+ const char *forceLayerStr = getenv("FUZZ_APPLAYER");
+ if (forceLayerStr) {
+ if (ByteExtractStringUint16(&forceLayer, 10, 0, forceLayerStr) < 0) {
+ forceLayer = 0;
+ printf("Invalid numeric value for FUZZ_APPLAYER environment variable");
+ } else {
+ printf("Forcing %s\n", AppProtoToString(forceLayer));
+ }
+ }
+ // http is the output name, but we want to fuzz HTTP1
+ if (forceLayer == ALPROTO_HTTP) {
+ forceLayer = ALPROTO_HTTP1;
+ }
+ return 0;
+}
+
+// arbitrary value
+#define ALPROTO_MAXTX 4096
+
+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 (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/");
+ // disables checksums validation for fuzzing
+ if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+ abort();
+ }
+
+ PostConfLoadedSetup(&surifuzz);
+ alp_tctx = AppLayerParserThreadCtxAlloc();
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+ }
+
+ if (size < HEADER_LEN) {
+ return 0;
+ }
+
+ 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 = (uint16_t)((data[2] << 8) | data[3]);
+ f->dp = (uint16_t)((data[4] << 8) | data[5]);
+ f->proto = data[1];
+ memset(&ssn, 0, sizeof(TcpSession));
+ f->protoctx = &ssn;
+ f->protomap = FlowGetProtoMapping(f->proto);
+ if (forceLayer > 0) {
+ f->alproto = forceLayer;
+ } else {
+ f->alproto = data[0];
+ }
+
+ FLOWLOCK_WRLOCK(f);
+ /*
+ * 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) {
+ goto bail;
+ }
+ memcpy(isolatedBuffer, albuffer, alnext - albuffer);
+ (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alnext - albuffer);
+ free(isolatedBuffer);
+ if (FlowChangeProto(f)) {
+ // exits if a protocol change is requested
+ alsize = 0;
+ break;
+ }
+ flags &= ~(STREAM_START);
+ if (f->alparser &&
+ (((flags & STREAM_TOSERVER) != 0 &&
+ AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_EOF_TS)) ||
+ ((flags & STREAM_TOCLIENT) != 0 &&
+ AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_EOF_TC)))) {
+ //no final chunk
+ alsize = 0;
+ break;
+ }
+
+ AppLayerParserTransactionsCleanup(f, flags & (STREAM_TOSERVER | STREAM_TOCLIENT));
+ }
+ alsize -= alnext - albuffer + 4;
+ albuffer = alnext + 4;
+ if (alsize == 0) {
+ break;
+ }
+ alnext = memmem(albuffer, alsize, separator, 4);
+ }
+ if (alsize > 0 ) {
+ if (flip) {
+ flags |= STREAM_TOCLIENT;
+ flags &= ~(STREAM_TOSERVER);
+ flip = 0;
+ } else {
+ flags |= STREAM_TOSERVER;
+ flags &= ~(STREAM_TOCLIENT);
+ flip = 1;
+ }
+ flags |= STREAM_EOF;
+ isolatedBuffer = malloc(alsize);
+ if (isolatedBuffer == NULL) {
+ goto bail;
+ }
+ memcpy(isolatedBuffer, albuffer, alsize);
+ (void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alsize);
+ free(isolatedBuffer);
+ }
+
+bail:
+ FLOWLOCK_UNLOCK(f);
+ 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 0000000..598e7cc
--- /dev/null
+++ b/src/tests/fuzz/fuzz_applayerprotodetectgetproto.c
@@ -0,0 +1,99 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "app-layer-detect-proto.h"
+#include "flow-util.h"
+#include "app-layer-parser.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+
+#define HEADER_LEN 6
+
+//rule of thumb constant, so as not to timeout target
+#define PROTO_DETECT_MAX_LEN 1024
+
+#include "confyaml.c"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+AppLayerProtoDetectThreadCtx *alpd_tctx = NULL;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ Flow *f;
+ TcpSession ssn;
+ bool reverse;
+ AppProto alproto;
+ AppProto alproto2;
+
+ if (alpd_tctx == NULL) {
+ //global init
+ InitGlobal();
+ run_mode = RUNMODE_UNITTEST;
+ if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+ abort();
+ }
+ MpmTableSetup();
+ SpmTableSetup();
+ EngineModeSetIDS();
+ AppLayerProtoDetectSetup();
+ AppLayerParserSetup();
+ AppLayerParserRegisterProtocolParsers();
+ alpd_tctx = AppLayerProtoDetectGetCtxThread();
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+ }
+
+ if (size < HEADER_LEN) {
+ return 0;
+ }
+
+ f = TestHelperBuildFlow(AF_INET, "1.2.3.4", "5.6.7.8", (uint16_t)((data[2] << 8) | data[3]),
+ (uint16_t)((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);
+
+ uint8_t flags = STREAM_TOCLIENT;
+ if (data[0] & STREAM_TOSERVER) {
+ flags = STREAM_TOSERVER;
+ }
+ alproto = AppLayerProtoDetectGetProto(
+ alpd_tctx, f, data + HEADER_LEN, size - HEADER_LEN, f->proto, flags, &reverse);
+ if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED && f->proto == IPPROTO_TCP) {
+ /* If we find a valid protocol at the start of a stream :
+ * check that with smaller input
+ * we find the same protocol or ALPROTO_UNKNOWN.
+ * Otherwise, we have evasion with TCP splitting
+ */
+ for (size_t i = 0; i < size-HEADER_LEN && i < PROTO_DETECT_MAX_LEN; i++) {
+ // reset detection at each try cf probing_parser_toserver_alproto_masks
+ AppLayerProtoDetectReset(f);
+ alproto2 = AppLayerProtoDetectGetProto(
+ alpd_tctx, f, data + HEADER_LEN, i, f->proto, flags, &reverse);
+ if (alproto2 != ALPROTO_UNKNOWN && alproto2 != alproto) {
+ printf("Failed with input length %" PRIuMAX " versus %" PRIuMAX
+ ", found %s instead of %s\n",
+ (uintmax_t)i, (uintmax_t)size - HEADER_LEN, AppProtoToString(alproto2),
+ AppProtoToString(alproto));
+ printf("Assertion failure: %s-%s\n", AppProtoToString(alproto2),
+ AppProtoToString(alproto));
+ fflush(stdout);
+ abort();
+ }
+ }
+ }
+ FlowFree(f);
+
+ return 0;
+}
diff --git a/src/tests/fuzz/fuzz_confyamlloadstring.c b/src/tests/fuzz/fuzz_confyamlloadstring.c
new file mode 100644
index 0000000..f5f9ed3
--- /dev/null
+++ b/src/tests/fuzz/fuzz_confyamlloadstring.c
@@ -0,0 +1,31 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for ConfYamlLoadString
+ */
+
+
+#include "suricata-common.h"
+#include "suricata.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_decodepcapfile.c b/src/tests/fuzz/fuzz_decodepcapfile.c
new file mode 100644
index 0000000..cf50869
--- /dev/null
+++ b/src/tests/fuzz/fuzz_decodepcapfile.c
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for TMM_DECODEPCAPFILE
+ */
+
+#include "suricata-common.h"
+#include "suricata.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"
+#include "util-time.h"
+#include "util-conf.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+SCInstance surifuzz;
+
+const char configNoChecksum[] = "\
+%YAML 1.1\n\
+---\n\
+pcap-file:\n\
+\n\
+ checksum-checks: no\n\
+";
+
+ThreadVars *tv;
+DecodeThreadVars *dtv;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+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(&surifuzz);
+
+ RunModeInitializeThreadSettings();
+ 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);
+
+ extern uint16_t max_pending_packets;
+ max_pending_packets = 128;
+ PacketPoolInit();
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+
+ initialized = 1;
+ }
+
+ //rewrite buffer to a file as libpcap does not have buffer inputs
+ if (TestHelperBufferToFile("/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 0000000..432ce7d
--- /dev/null
+++ b/src/tests/fuzz/fuzz_mimedecparseline.c
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for ConfYamlLoadString
+ */
+
+
+#include "suricata-common.h"
+#include "suricata.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 optimized 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) {
+ if (state->state_flag >= BODY_STARTED)
+ (void)MimeDecParseLine(buffer, size, 0, 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_predefpcap_aware.c b/src/tests/fuzz/fuzz_predefpcap_aware.c
new file mode 100644
index 0000000..c20e3d3
--- /dev/null
+++ b/src/tests/fuzz/fuzz_predefpcap_aware.c
@@ -0,0 +1,165 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for predefined signatures and pcap (aware)
+ */
+
+#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-profiling.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 "detect-fast-pattern.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+#include "pkt-var.h"
+#include "flow-util.h"
+#include "tm-modules.h"
+#include "tmqh-packetpool.h"
+#include "util-conf.h"
+#include "packet.h"
+
+#include <fuzz_pcap.h>
+
+int LLVMFuzzerInitialize(const int *argc, char ***argv);
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+ThreadVars tv;
+DecodeThreadVars *dtv;
+// FlowWorkerThreadData
+void *fwd;
+SCInstance surifuzz;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+#include "confyaml.c"
+
+char *filepath = NULL;
+
+int LLVMFuzzerInitialize(const int *argc, char ***argv)
+{
+ filepath = dirname(strdup((*argv)[0]));
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ FPC_buffer_t pkts;
+ const u_char *pkt;
+ struct pcap_pkthdr header;
+ int r;
+ Packet *p;
+ size_t pcap_cnt = 0;
+
+ if (initialized == 0) {
+ // Redirects logs to /dev/null
+ setenv("SC_LOG_OP_IFACE", "file", 0);
+ setenv("SC_LOG_FILE", "/dev/null", 0);
+
+ InitGlobal();
+
+ GlobalsInitPreConfig();
+ run_mode = RUNMODE_PCAP_FILE;
+ // redirect logs to /tmp
+ ConfigSetLogDirectory("/tmp/");
+ // disables checksums validation for fuzzing
+ if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+ abort();
+ }
+ surifuzz.sig_file = malloc(strlen(filepath) + strlen("/fuzz.rules") + 1);
+ memcpy(surifuzz.sig_file, filepath, strlen(filepath));
+ memcpy(surifuzz.sig_file + strlen(filepath), "/fuzz.rules", strlen("/fuzz.rules"));
+ surifuzz.sig_file[strlen(filepath) + strlen("/fuzz.rules")] = 0;
+ surifuzz.sig_file_exclusive = 1;
+ // loads rules after init
+ surifuzz.delayed_detect = 1;
+
+ PostConfLoadedSetup(&surifuzz);
+ PreRunPostPrivsDropInit(run_mode);
+ PostConfLoadedDetectSetup(&surifuzz);
+
+ memset(&tv, 0, sizeof(tv));
+ tv.flow_queue = FlowQueueNew();
+ if (tv.flow_queue == NULL)
+ abort();
+ dtv = DecodeThreadVarsAlloc(&tv);
+ DecodeRegisterPerfCounters(dtv, &tv);
+ tmm_modules[TMM_FLOWWORKER].ThreadInit(&tv, NULL, &fwd);
+ StatsSetupPrivate(&tv);
+
+ extern uint16_t max_pending_packets;
+ max_pending_packets = 128;
+ PacketPoolInit();
+ if (DetectEngineReload(&surifuzz) < 0) {
+ return 0;
+ }
+
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+ initialized = 1;
+ }
+
+ if (size < FPC0_HEADER_LEN) {
+ return 0;
+ }
+ // initialize FPC with the buffer
+ if (FPC_init(&pkts, data, size) < 0) {
+ return 0;
+ }
+
+ // loop over packets
+ r = FPC_next(&pkts, &header, &pkt);
+ p = PacketGetFromAlloc();
+ if (p == NULL || r <= 0 || header.ts.tv_sec >= INT_MAX - 3600) {
+ goto bail;
+ }
+ p->ts = SCTIME_FROM_TIMEVAL(&header.ts);
+ p->datalink = pkts.datalink;
+ p->pkt_src = PKT_SRC_WIRE;
+ while (r > 0) {
+ if (PacketCopyData(p, pkt, header.caplen) == 0) {
+ // 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) {
+ PacketFreeOrRelease(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) {
+ PacketFreeOrRelease(extra_p);
+ extra_p = PacketDequeueNoLock(&tv.decode_pq);
+ }
+ }
+ r = FPC_next(&pkts, &header, &pkt);
+ if (r <= 0 || header.ts.tv_sec >= INT_MAX - 3600) {
+ goto bail;
+ }
+ PacketRecycle(p);
+ p->ts = SCTIME_FROM_TIMEVAL(&header.ts);
+ p->datalink = pkts.datalink;
+ pcap_cnt++;
+ p->pcap_cnt = pcap_cnt;
+ p->pkt_src = PKT_SRC_WIRE;
+ }
+bail:
+ if (p != NULL) {
+ PacketFree(p);
+ }
+ FlowReset();
+
+ return 0;
+}
diff --git a/src/tests/fuzz/fuzz_siginit.c b/src/tests/fuzz/fuzz_siginit.c
new file mode 100644
index 0000000..80514b2
--- /dev/null
+++ b/src/tests/fuzz/fuzz_siginit.c
@@ -0,0 +1,59 @@
+/**
+ * @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);
+
+static uint32_t cnt = 0;
+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();
+ EngineModeSetIDS();
+ SigTableSetup();
+ }
+ if (cnt++ == 1024) {
+ DetectEngineCtxFree(de_ctx);
+ de_ctx = NULL;
+ cnt = 0;
+ }
+ if (de_ctx == NULL) {
+ de_ctx = DetectEngineCtxInit();
+ BUG_ON(de_ctx == NULL);
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->rule_file = (char *)"fuzzer";
+ }
+
+ 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);
+ if (s && s->next) {
+ SigFree(de_ctx, s->next);
+ s->next = NULL;
+ }
+ SigFree(de_ctx, s);
+ }
+
+ return 0;
+}
diff --git a/src/tests/fuzz/fuzz_sigpcap.c b/src/tests/fuzz/fuzz_sigpcap.c
new file mode 100644
index 0000000..e5bd56d
--- /dev/null
+++ b/src/tests/fuzz/fuzz_sigpcap.c
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for signature file and pcap file
+ */
+
+#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-profiling.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 "detect-fast-pattern.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+#include "pkt-var.h"
+#include "flow-util.h"
+#include "flow-worker.h"
+#include "tm-modules.h"
+#include "tmqh-packetpool.h"
+#include "util-file.h"
+#include "util-conf.h"
+#include "packet.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+
+static int initialized = 0;
+ThreadVars tv;
+DecodeThreadVars *dtv;
+//FlowWorkerThreadData
+void *fwd;
+SCInstance surifuzz;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+#include "confyaml.c"
+
+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;
+ size_t pcap_cnt = 0;
+
+ if (initialized == 0) {
+ //Redirects logs to /dev/null
+ setenv("SC_LOG_OP_IFACE", "file", 0);
+ setenv("SC_LOG_FILE", "/dev/null", 0);
+
+ InitGlobal();
+
+ GlobalsInitPreConfig();
+ run_mode = RUNMODE_PCAP_FILE;
+ //redirect logs to /tmp
+ ConfigSetLogDirectory("/tmp/");
+ //disables checksums validation for fuzzing
+ if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+ abort();
+ }
+ // do not load rules before reproducible DetectEngineReload
+ remove("/tmp/fuzz.rules");
+ surifuzz.sig_file = strdup("/tmp/fuzz.rules");
+ surifuzz.sig_file_exclusive = 1;
+ //loads rules after init
+ surifuzz.delayed_detect = 1;
+
+ PostConfLoadedSetup(&surifuzz);
+ PreRunPostPrivsDropInit(run_mode);
+ PostConfLoadedDetectSetup(&surifuzz);
+
+ memset(&tv, 0, sizeof(tv));
+ tv.flow_queue = FlowQueueNew();
+ if (tv.flow_queue == NULL)
+ abort();
+ dtv = DecodeThreadVarsAlloc(&tv);
+ DecodeRegisterPerfCounters(dtv, &tv);
+ tmm_modules[TMM_FLOWWORKER].ThreadInit(&tv, NULL, &fwd);
+ StatsSetupPrivate(&tv);
+
+ extern uint16_t max_pending_packets;
+ max_pending_packets = 128;
+ PacketPoolInit();
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+ 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 (TestHelperBufferToFile(surifuzz.sig_file, data, pos-1) < 0) {
+ return 0;
+ }
+ } else {
+ if (TestHelperBufferToFile(surifuzz.sig_file, data, pos) < 0) {
+ return 0;
+ }
+ }
+
+ if (DetectEngineReload(&surifuzz) < 0) {
+ return 0;
+ }
+ DetectEngineThreadCtx *old_det_ctx = FlowWorkerGetDetectCtxPtr(fwd);
+
+ DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
+ de_ctx->ref_cnt--;
+ DetectEngineThreadCtx *new_det_ctx = DetectEngineThreadCtxInitForReload(&tv, de_ctx, 1);
+ FlowWorkerReplaceDetectCtx(fwd, new_det_ctx);
+
+ DetectEngineThreadCtxDeinit(NULL, old_det_ctx);
+
+ if (pos < size) {
+ //skip zero
+ pos++;
+ }
+ data += pos;
+ size -= pos;
+
+ //rewrite buffer to a file as libpcap does not have buffer inputs
+ if (TestHelperBufferToFile("/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();
+ if (r <= 0 || header->ts.tv_sec >= INT_MAX - 3600 || header->ts.tv_usec < 0) {
+ goto bail;
+ }
+ p->ts = SCTIME_FROM_TIMEVAL(&header->ts);
+ p->datalink = pcap_datalink(pkts);
+ p->pkt_src = PKT_SRC_WIRE;
+ while (r > 0) {
+ if (PacketCopyData(p, pkt, header->caplen) == 0) {
+ // 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) {
+ PacketFreeOrRelease(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) {
+ PacketFreeOrRelease(extra_p);
+ extra_p = PacketDequeueNoLock(&tv.decode_pq);
+ }
+ }
+ r = pcap_next_ex(pkts, &header, &pkt);
+ if (r <= 0 || header->ts.tv_sec >= INT_MAX - 3600 || header->ts.tv_usec < 0) {
+ goto bail;
+ }
+ PacketRecycle(p);
+ p->ts = SCTIME_FROM_TIMEVAL(&header->ts);
+ p->datalink = pcap_datalink(pkts);
+ p->pkt_src = PKT_SRC_WIRE;
+ pcap_cnt++;
+ p->pcap_cnt = pcap_cnt;
+ }
+bail:
+ //close structure
+ pcap_close(pkts);
+ PacketFree(p);
+ FlowReset();
+
+ return 0;
+}
diff --git a/src/tests/fuzz/fuzz_sigpcap_aware.c b/src/tests/fuzz/fuzz_sigpcap_aware.c
new file mode 100644
index 0000000..d245476
--- /dev/null
+++ b/src/tests/fuzz/fuzz_sigpcap_aware.c
@@ -0,0 +1,203 @@
+/**
+ * @file
+ * @author Philippe Antoine <contact@catenacyber.fr>
+ * fuzz target for AppLayerProtoDetectGetProto
+ */
+
+#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-profiling.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 "detect-fast-pattern.h"
+#include "util-unittest-helper.h"
+#include "conf-yaml-loader.h"
+#include "pkt-var.h"
+#include "flow-util.h"
+#include "flow-worker.h"
+#include "tm-modules.h"
+#include "tmqh-packetpool.h"
+#include "util-conf.h"
+#include "packet.h"
+
+#include <fuzz_pcap.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int initialized = 0;
+ThreadVars tv;
+DecodeThreadVars *dtv;
+// FlowWorkerThreadData
+void *fwd;
+SCInstance surifuzz;
+SC_ATOMIC_EXTERN(unsigned int, engine_stage);
+
+#include "confyaml.c"
+
+static void SigGenerateAware(const uint8_t *data, size_t size, char *r, size_t *len)
+{
+ *len = snprintf(r, 511, "alert ip any any -> any any (");
+ for (size_t i = 0; i + 1 < size && *len < 511; i++) {
+ if (data[i] & 0x80) {
+ size_t off = (data[i] & 0x7F + ((data[i + 1] & 0xF) << 7)) %
+ (sizeof(sigmatch_table) / sizeof(SigTableElmt));
+ if (sigmatch_table[off].flags & SIGMATCH_NOOPT ||
+ ((data[i + 1] & 0x80) && sigmatch_table[off].flags & SIGMATCH_OPTIONAL_OPT)) {
+ *len += snprintf(r + *len, 511 - *len, "; %s;", sigmatch_table[off].name);
+ } else {
+ *len += snprintf(r + *len, 511 - *len, "; %s:", sigmatch_table[off].name);
+ }
+ i++;
+ } else {
+ r[*len] = data[i];
+ *len = *len + 1;
+ }
+ }
+ if (*len < 511) {
+ *len += snprintf(r + *len, 511 - *len, ")");
+ } else {
+ r[511] = 0;
+ *len = 511;
+ }
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ FPC_buffer_t pkts;
+ const u_char *pkt;
+ struct pcap_pkthdr header;
+ int r;
+ Packet *p;
+ size_t pos;
+ size_t pcap_cnt = 0;
+
+ if (initialized == 0) {
+ // Redirects logs to /dev/null
+ setenv("SC_LOG_OP_IFACE", "file", 0);
+ setenv("SC_LOG_FILE", "/dev/null", 0);
+
+ InitGlobal();
+
+ GlobalsInitPreConfig();
+ run_mode = RUNMODE_PCAP_FILE;
+ // redirect logs to /tmp
+ ConfigSetLogDirectory("/tmp/");
+ // disables checksums validation for fuzzing
+ if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
+ abort();
+ }
+ // do not load rules before reproducible DetectEngineReload
+ remove("/tmp/fuzz.rules");
+ surifuzz.sig_file = strdup("/tmp/fuzz.rules");
+ surifuzz.sig_file_exclusive = 1;
+ // loads rules after init
+ surifuzz.delayed_detect = 1;
+
+ PostConfLoadedSetup(&surifuzz);
+ PreRunPostPrivsDropInit(run_mode);
+ PostConfLoadedDetectSetup(&surifuzz);
+
+ memset(&tv, 0, sizeof(tv));
+ tv.flow_queue = FlowQueueNew();
+ if (tv.flow_queue == NULL)
+ abort();
+ dtv = DecodeThreadVarsAlloc(&tv);
+ DecodeRegisterPerfCounters(dtv, &tv);
+ tmm_modules[TMM_FLOWWORKER].ThreadInit(&tv, NULL, &fwd);
+ StatsSetupPrivate(&tv);
+
+ extern uint16_t max_pending_packets;
+ max_pending_packets = 128;
+ PacketPoolInit();
+ SC_ATOMIC_SET(engine_stage, SURICATA_RUNTIME);
+ initialized = 1;
+ }
+
+ if (size < 1 + FPC0_HEADER_LEN) {
+ return 0;
+ }
+ for (pos = 0; pos < size - FPC0_HEADER_LEN; pos++) {
+ if (data[pos] == 0) {
+ break;
+ }
+ }
+ // initialize FPC with the buffer
+ if (FPC_init(&pkts, data + pos + 1, size - pos - 1) < 0) {
+ return 0;
+ }
+
+ // dump signatures to a file so as to reuse SigLoadSignatures
+ char sigaware[512];
+ size_t len;
+ SigGenerateAware(data, pos + 1, sigaware, &len);
+ if (TestHelperBufferToFile(surifuzz.sig_file, (uint8_t *)sigaware, len) < 0) {
+ return 0;
+ }
+
+ if (DetectEngineReload(&surifuzz) < 0) {
+ return 0;
+ }
+ DetectEngineThreadCtx *old_det_ctx = FlowWorkerGetDetectCtxPtr(fwd);
+
+ DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
+ de_ctx->ref_cnt--;
+ DetectEngineThreadCtx *new_det_ctx = DetectEngineThreadCtxInitForReload(&tv, de_ctx, 1);
+ FlowWorkerReplaceDetectCtx(fwd, new_det_ctx);
+
+ DetectEngineThreadCtxDeinit(NULL, old_det_ctx);
+
+ // loop over packets
+ r = FPC_next(&pkts, &header, &pkt);
+ p = PacketGetFromAlloc();
+ if (r <= 0 || header.ts.tv_sec >= INT_MAX - 3600) {
+ goto bail;
+ }
+ p->pkt_src = PKT_SRC_WIRE;
+ p->ts = SCTIME_FROM_TIMEVAL(&header.ts);
+ p->datalink = pkts.datalink;
+ while (r > 0) {
+ if (PacketCopyData(p, pkt, header.caplen) == 0) {
+ // 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) {
+ PacketFreeOrRelease(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) {
+ PacketFreeOrRelease(extra_p);
+ extra_p = PacketDequeueNoLock(&tv.decode_pq);
+ }
+ }
+ r = FPC_next(&pkts, &header, &pkt);
+ if (r <= 0 || header.ts.tv_sec >= INT_MAX - 3600) {
+ goto bail;
+ }
+ PacketRecycle(p);
+ p->pkt_src = PKT_SRC_WIRE;
+ p->ts = SCTIME_FROM_TIMEVAL(&header.ts);
+ p->datalink = pkts.datalink;
+ pcap_cnt++;
+ p->pcap_cnt = pcap_cnt;
+ }
+bail:
+ PacketFree(p);
+ FlowReset();
+
+ return 0;
+}
diff --git a/src/tests/fuzz/onefile.c b/src/tests/fuzz/onefile.c
new file mode 100644
index 0000000..344ef8e
--- /dev/null
+++ b/src/tests/fuzz/onefile.c
@@ -0,0 +1,88 @@
+#include "suricata-common.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+static int runOneFile(const char *fname)
+{
+ // opens the file, get its size, and reads it into a buffer
+ uint8_t *data;
+ size_t size;
+ FILE *fp = fopen(fname, "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;
+ }
+
+ // launch fuzzer
+ LLVMFuzzerTestOneInput(data, size);
+ free(data);
+ fclose(fp);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ DIR *d;
+ struct dirent *dir;
+ int r;
+
+ if (argc != 2) {
+ return 1;
+ }
+#ifdef AFLFUZZ_PERSISTENT_MODE
+ while (__AFL_LOOP(1000)) {
+#endif /* AFLFUZZ_PERSISTENT_MODE */
+
+ d = opendir(argv[1]);
+ if (d == NULL) {
+ // run one file
+ r = runOneFile(argv[1]);
+ if (r != 0) {
+ return r;
+ }
+ } else {
+ // run every file in one directory
+ if (chdir(argv[1]) != 0) {
+ closedir(d);
+ printf("Invalid directory\n");
+ return 2;
+ }
+ while ((dir = readdir(d)) != NULL) {
+ if (dir->d_type != DT_REG) {
+ continue;
+ }
+ r = runOneFile(dir->d_name);
+ if (r != 0) {
+ return r;
+ }
+ }
+ closedir(d);
+ }
+#ifdef AFLFUZZ_PERSISTENT_MODE
+ }
+#endif /* AFLFUZZ_PERSISTENT_MODE */
+
+ return 0;
+}