mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
7.0 KiB
C
230 lines
7.0 KiB
C
/**
|
|
* @file
|
|
* @author Philippe Antoine <contact@catenacyber.fr>
|
|
* fuzz target for AppLayerParserParse
|
|
*/
|
|
|
|
#include "suricata-common.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"
|
|
|
|
#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;
|
|
|
|
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));
|
|
}
|
|
}
|
|
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 (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/");
|
|
// disables checksums validation for fuzzing
|
|
if (ConfYamlLoadString(configNoChecksum, strlen(configNoChecksum)) != 0) {
|
|
abort();
|
|
}
|
|
|
|
PostConfLoadedSetup(&surifuzz);
|
|
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 = (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) {
|
|
return 0;
|
|
}
|
|
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);
|
|
|
|
if (f->alstate && f->alparser) {
|
|
// check if we have too many open transactions
|
|
const uint64_t total_txs = AppLayerParserGetTxCnt(f, f->alstate);
|
|
uint64_t min = 0;
|
|
AppLayerGetTxIterState state;
|
|
memset(&state, 0, sizeof(state));
|
|
uint64_t nbtx = 0;
|
|
AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(f->proto, f->alproto);
|
|
while (1) {
|
|
AppLayerGetTxIterTuple ires =
|
|
IterFunc(f->proto, f->alproto, f->alstate, min, total_txs, &state);
|
|
if (ires.tx_ptr == NULL)
|
|
break;
|
|
min = ires.tx_id + 1;
|
|
nbtx++;
|
|
if (nbtx > ALPROTO_MAXTX) {
|
|
printf("Too many open transactions for protocol %s\n",
|
|
AppProtoToString(f->alproto));
|
|
printf("Assertion failure: %s\n", AppProtoToString(f->alproto));
|
|
fflush(stdout);
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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) {
|
|
return 0;
|
|
}
|
|
memcpy(isolatedBuffer, albuffer, alsize);
|
|
(void) AppLayerParserParse(NULL, alp_tctx, f, f->alproto, flags, isolatedBuffer, alsize);
|
|
free(isolatedBuffer);
|
|
}
|
|
|
|
FLOWLOCK_UNLOCK(f);
|
|
FlowFree(f);
|
|
|
|
return 0;
|
|
}
|