afl: improve packet fuzz testing

Due to the use of AFL_LOOP and initialization/deinit outside of it,
part of the fuzzing relied on the global 'state' in flow and defrag.
Because of this crashes that were found could not be reproduced. The
saved crash input was only the last in the series.

This patch addresses that. It requires a new output directory 'dump'
where the packet fuzzers will store all their input. If the AFL_LOOP
fails the files will not be removed and this 'serie' can be read
again for reproducing the issue.

e.g.: AFL would work with:
--afl-decoder-ppp=@@

and after a crash is found the produced serie can be read with:
--afl-decoder-ppp-serie=1486656919-514163

The series have a timestamp as name and a suffix that controls the
order in which the files will be 'replayed' in Suricata.
pull/2552/head
Victor Julien 9 years ago
parent 923d93f314
commit fbd69729aa

@ -46,6 +46,7 @@ conf-yaml-loader.c conf-yaml-loader.h \
counters.c counters.h \
data-queue.c data-queue.h \
decode.c decode.h \
decode-afl.c \
decode-erspan.c decode-erspan.h \
decode-ethernet.c decode-ethernet.h \
decode-events.c decode-events.h \

@ -0,0 +1,149 @@
/* Copyright (C) 2007-2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "suricata-common.h"
#include "suricata.h"
#include "conf.h"
#include "decode.h"
#include "util-debug.h"
#include "util-mem.h"
#include "app-layer-detect-proto.h"
#include "app-layer.h"
#include "tm-threads.h"
#include "util-error.h"
#include "util-print.h"
#include "tmqh-packetpool.h"
#include "util-profiling.h"
#include "pkt-var.h"
#include "util-mpm-ac.h"
#include "output.h"
#include "output-flow.h"
#include "defrag.h"
#include "flow.h"
#ifdef AFLFUZZ_DECODER
/* stateful processing of data as packets. Because AFL in case of a
* crash will only safe the last input, we dump all the inputs to a
* directory 'dump' with a unique timestamp for the serie and an
* incrementing 'id' so that we can 'replay' it in
* DecoderParseDataFromFileSerie().
*/
int DecoderParseDataFromFile(char *filename, DecoderFunc Decoder) {
uint8_t buffer[65536];
struct timeval ts;
memset(&ts, 0, sizeof(ts));
gettimeofday(&ts, NULL);
uint32_t cnt = 0;
DefragInit();
FlowInitConfig(FLOW_QUIET);
ThreadVars tv;
memset(&tv, 0, sizeof(tv));
DecodeThreadVars *dtv = DecodeThreadVarsAlloc(&tv);
DecodeRegisterPerfCounters(dtv, &tv);
StatsSetupPrivate(&tv);
#ifdef AFLFUZZ_PERSISTANT_MODE
while (__AFL_LOOP(1000)) {
/* reset state */
memset(buffer, 0, sizeof(buffer));
#endif /* AFLFUZZ_PERSISTANT_MODE */
FILE *fp = fopen(filename, "r");
BUG_ON(fp == NULL);
size_t size = fread(&buffer, 1, sizeof(buffer), fp);
char outfilename[256];
snprintf(outfilename, sizeof(outfilename), "dump/%u-%u.%u", (uint)ts.tv_sec, (uint)ts.tv_usec, cnt);
FILE *out_fp = fopen(outfilename, "w");
BUG_ON(out_fp == NULL);
(void)fwrite(buffer, size, 1, out_fp);
fclose(out_fp);
Packet *p = PacketGetFromAlloc();
if (p != NULL) {
(void) Decoder (&tv, dtv, p, buffer, size, NULL);
PacketFree(p);
}
fclose(fp);
cnt++;
#ifdef AFLFUZZ_PERSISTANT_MODE
}
#endif /* AFLFUZZ_PERSISTANT_MODE */
/* if we get here there was no crash, so we can remove our files */
uint32_t x = 0;
for (x = 0; x < cnt; x++) {
char rmfilename[256];
snprintf(rmfilename, sizeof(rmfilename), "dump/%u-%u.%u", (uint)ts.tv_sec, (uint)ts.tv_usec, x);
unlink(rmfilename);
}
DecodeThreadVarsFree(&tv, dtv);
FlowShutdown();
DefragDestroy();
return 0;
}
/* load a serie of files generated by DecoderParseDataFromFile() in
* the same order as it was produced. */
int DecoderParseDataFromFileSerie(char *fileprefix, DecoderFunc Decoder)
{
uint8_t buffer[65536];
uint32_t cnt = 0;
DefragInit();
FlowInitConfig(FLOW_QUIET);
ThreadVars tv;
memset(&tv, 0, sizeof(tv));
DecodeThreadVars *dtv = DecodeThreadVarsAlloc(&tv);
DecodeRegisterPerfCounters(dtv, &tv);
StatsSetupPrivate(&tv);
char filename[256];
snprintf(filename, sizeof(filename), "dump/%s.%u", fileprefix, cnt);
FILE *fp;
while ((fp = fopen(filename, "r")) != NULL)
{
memset(buffer, 0, sizeof(buffer));
size_t size = fread(&buffer, 1, sizeof(buffer), fp);
Packet *p = PacketGetFromAlloc();
if (p != NULL) {
(void) Decoder (&tv, dtv, p, buffer, size, NULL);
PacketFree(p);
}
fclose(fp);
cnt++;
snprintf(filename, sizeof(filename), "dump/%s.%u", fileprefix, cnt);
}
DecodeThreadVarsFree(&tv, dtv);
FlowShutdown();
DefragDestroy();
return 0;
}
#endif /* AFLFUZZ_DECODER */

@ -600,55 +600,6 @@ void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s)
s->counter_ips_replaced = StatsRegisterCounter("ips.replaced", tv);
}
#ifdef AFLFUZZ_DECODER
int DecoderParseDataFromFile(char *filename, DecoderFunc Decoder) {
uint8_t buffer[65536];
int result = 1;
#ifdef AFLFUZZ_PERSISTANT_MODE
while (__AFL_LOOP(1000)) {
/* reset state */
memset(buffer, 0, sizeof(buffer));
#endif /* AFLFUZZ_PERSISTANT_MODE */
FILE *fp = fopen(filename, "r");
BUG_ON(fp == NULL);
ThreadVars tv;
memset(&tv, 0, sizeof(tv));
DecodeThreadVars *dtv = DecodeThreadVarsAlloc(&tv);
DecodeRegisterPerfCounters(dtv, &tv);
StatsSetupPrivate(&tv);
while (1) {
int done = 0;
size_t size = fread(&buffer, 1, sizeof(buffer), fp);
if (size < sizeof(buffer))
done = 1;
Packet *p = PacketGetFromAlloc();
if (p != NULL) {
(void) Decoder (&tv, dtv, p, buffer, size, NULL);
PacketFree(p);
}
if (done)
break;
}
DecodeThreadVarsFree(&tv, dtv);
fclose(fp);
#ifdef AFLFUZZ_PERSISTANT_MODE
}
#endif /* AFLFUZZ_PERSISTANT_MODE */
result = 0;
return result;
}
#endif /* AFLFUZZ_DECODER */
/**
* @}
*/

@ -958,6 +958,7 @@ typedef int (*DecoderFunc)(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
uint8_t *pkt, uint16_t len, PacketQueue *pq);
int DecoderParseDataFromFile(char *filename, DecoderFunc Decoder);
int DecoderParseDataFromFileSerie(char *fileprefix, DecoderFunc Decoder);
#endif
/** \brief Set the No payload inspection Flag for the packet.

@ -1261,7 +1261,11 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
{"afl-rules", required_argument, 0 , 0},
{"afl-mime", required_argument, 0 , 0},
{"afl-decoder-ppp", required_argument, 0 , 0},
{"afl-decoder-ppp-serie", required_argument, 0 , 0},
{"afl-decoder-ipv4", required_argument, 0 , 0},
{"afl-decoder-ipv4-serie", required_argument, 0 , 0},
{"afl-decoder-ipv6", required_argument, 0 , 0},
{"afl-decoder-ipv6-serie", required_argument, 0 , 0},
{"afl-der", required_argument, 0, 0},
#ifdef BUILD_UNIX_SOCKET
@ -1565,24 +1569,33 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
exit(MimeParserDataFromFile(optarg));
#endif
#ifdef AFLFUZZ_DECODER
} else if(strcmp((long_opts[option_index]).name, "afl-decoder-ppp") == 0) {
} else if(strstr((long_opts[option_index]).name, "afl-decoder-ppp") != NULL) {
StatsInit();
MpmTableSetup();
SpmTableSetup();
AppLayerProtoDetectSetup();
DefragInit();
FlowInitConfig(FLOW_QUIET);
//printf("arg: //%s\n", optarg);
exit(DecoderParseDataFromFile(optarg, DecodePPP));
} else if(strcmp((long_opts[option_index]).name, "afl-decoder-ipv4") == 0) {
if (strcmp((long_opts[option_index]).name, "afl-decoder-ppp") == 0)
exit(DecoderParseDataFromFile(optarg, DecodePPP));
else
exit(DecoderParseDataFromFileSerie(optarg, DecodePPP));
} else if(strstr((long_opts[option_index]).name, "afl-decoder-ipv4") != NULL) {
StatsInit();
MpmTableSetup();
SpmTableSetup();
AppLayerProtoDetectSetup();
DefragInit();
FlowInitConfig(FLOW_QUIET);
//printf("arg: //%s\n", optarg);
exit(DecoderParseDataFromFile(optarg, DecodeIPV4));
if (strcmp((long_opts[option_index]).name, "afl-decoder-ipv4") == 0)
exit(DecoderParseDataFromFile(optarg, DecodeIPV4));
else
exit(DecoderParseDataFromFileSerie(optarg, DecodeIPV4));
} else if(strstr((long_opts[option_index]).name, "afl-decoder-ipv6") != NULL) {
StatsInit();
MpmTableSetup();
SpmTableSetup();
AppLayerProtoDetectSetup();
if (strcmp((long_opts[option_index]).name, "afl-decoder-ipv6") == 0)
exit(DecoderParseDataFromFile(optarg, DecodeIPV6));
else
exit(DecoderParseDataFromFileSerie(optarg, DecodeIPV6));
#endif
#ifdef AFLFUZZ_DER
} else if(strcmp((long_opts[option_index]).name, "afl-der") == 0) {

Loading…
Cancel
Save