]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
afl: improve packet fuzz testing
authorVictor Julien <victor@inliniac.net>
Thu, 9 Feb 2017 17:22:18 +0000 (18:22 +0100)
committerVictor Julien <victor@inliniac.net>
Fri, 10 Feb 2017 15:02:20 +0000 (16:02 +0100)
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.

src/Makefile.am
src/decode-afl.c [new file with mode: 0644]
src/decode.c
src/decode.h
src/suricata.c

index 48f5dd0ee0bce395bc6be79d10fa616b9ed21f17..ada1afc6a4d8d3c1701f884472bb592c2934f4fb 100644 (file)
@@ -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 \
diff --git a/src/decode-afl.c b/src/decode-afl.c
new file mode 100644 (file)
index 0000000..59fd6fe
--- /dev/null
@@ -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 */
+
index 58fa61b33879c41320c3b206bc6d7b3e794a86db..4b064c8300d632e4df92ca17bc6f39e4df8e2f6e 100644 (file)
@@ -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 */
-
 /**
  * @}
  */
index 85b4faf5601c312743d3d3d7a403a4991979861c..dcf7e2e120f5e2a9736255015ebad3dc75b3709b 100644 (file)
@@ -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.
index 7f16679525ee524baeba617ed1bda4e95c8112f0..92d7280880978131c1c07b23f0c2408e139e9d47 100644 (file)
@@ -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) {