diff options
Diffstat (limited to 'src/tests/fuzz')
-rw-r--r-- | src/tests/fuzz/fuzz_applayerparserparse.c | 213 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_applayerprotodetectgetproto.c | 99 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_confyamlloadstring.c | 31 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_decodepcapfile.c | 101 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_mimedecparseline.c | 66 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_predefpcap_aware.c | 165 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_siginit.c | 59 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_sigpcap.c | 208 | ||||
-rw-r--r-- | src/tests/fuzz/fuzz_sigpcap_aware.c | 203 | ||||
-rw-r--r-- | src/tests/fuzz/onefile.c | 88 |
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; +} |