diff mbox series

[ovs-dev] ossfuzz: Add ofctl parse target

Message ID 20181015092333.14074-1-bshastry@sect.tu-berlin.de
State Accepted
Headers show
Series [ovs-dev] ossfuzz: Add ofctl parse target | expand

Commit Message

Bhargava Shastry Oct. 15, 2018, 9:23 a.m. UTC
From: Bhargava Shastry <bshastry@sect.tu-berlin.de>

This patch adds a new target called ofctl_parse_target to
 ossfuzz. The main idea is to begin to fuzz APIs from the ofctl utility
 program. At a later point, these may be added. For the moment, this patch
 only fuzzes APIs that parse flow mod commands.

This target is demonstrably capable of finding memory corruption defects
 in the parsing path. To aid the fuzzing process, a dictionary file
 containing tokens specific to this parsing path have been added.

Signed-off-by: Bhargava Shastry <bshastry@sect.tu-berlin.de>
---
 tests/oss-fuzz/automake.mk                    |  13 ++-
 .../config/ofctl_parse_target.options         |   3 +
 tests/oss-fuzz/config/ofp-flow.dict           |  45 ++++++++
 tests/oss-fuzz/ofctl_parse_target.c           | 106 ++++++++++++++++++
 4 files changed, 165 insertions(+), 2 deletions(-)
 create mode 100644 tests/oss-fuzz/config/ofctl_parse_target.options
 create mode 100644 tests/oss-fuzz/config/ofp-flow.dict
 create mode 100644 tests/oss-fuzz/ofctl_parse_target.c

Comments

Ben Pfaff Oct. 23, 2018, 5:15 p.m. UTC | #1
On Mon, Oct 15, 2018 at 11:23:33AM +0200, bshastry@sect.tu-berlin.de wrote:
> From: Bhargava Shastry <bshastry@sect.tu-berlin.de>
> 
> This patch adds a new target called ofctl_parse_target to
>  ossfuzz. The main idea is to begin to fuzz APIs from the ofctl utility
>  program. At a later point, these may be added. For the moment, this patch
>  only fuzzes APIs that parse flow mod commands.
> 
> This target is demonstrably capable of finding memory corruption defects
>  in the parsing path. To aid the fuzzing process, a dictionary file
>  containing tokens specific to this parsing path have been added.
> 
> Signed-off-by: Bhargava Shastry <bshastry@sect.tu-berlin.de>

Thanks.  I applied this to master.
diff mbox series

Patch

diff --git a/tests/oss-fuzz/automake.mk b/tests/oss-fuzz/automake.mk
index 2c506be3d..5bf7d0d7c 100644
--- a/tests/oss-fuzz/automake.mk
+++ b/tests/oss-fuzz/automake.mk
@@ -4,7 +4,8 @@  OSS_FUZZ_TARGETS = \
 	tests/oss-fuzz/ofp_print_target \
 	tests/oss-fuzz/expr_parse_target \
 	tests/oss-fuzz/odp_target \
-	tests/oss-fuzz/miniflow_target
+	tests/oss-fuzz/miniflow_target \
+	tests/oss-fuzz/ofctl_parse_target
 EXTRA_PROGRAMS += $(OSS_FUZZ_TARGETS)
 oss-fuzz-targets: $(OSS_FUZZ_TARGETS)
 
@@ -45,6 +46,12 @@  tests_oss_fuzz_miniflow_target_SOURCES = \
 tests_oss_fuzz_miniflow_target_LDADD = lib/libopenvswitch.la
 tests_oss_fuzz_miniflow_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
 
+tests_oss_fuzz_ofctl_parse_target_SOURCES = \
+        tests/oss-fuzz/ofctl_parse_target.c \
+        tests/oss-fuzz/fuzzer.h
+tests_oss_fuzz_ofctl_parse_target_LDADD = lib/libopenvswitch.la
+tests_oss_fuzz_ofctl_parse_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
+
 EXTRA_DIST += \
 	tests/oss-fuzz/config/flow_extract_target.options \
 	tests/oss-fuzz/config/json_parser_target.options \
@@ -52,6 +59,8 @@  EXTRA_DIST += \
 	tests/oss-fuzz/config/expr_parse_target.options \
 	tests/oss-fuzz/config/odp_target.options \
 	tests/oss-fuzz/config/miniflow_target.options \
+        tests/oss-fuzz/config/ofctl_parse_target.options \
 	tests/oss-fuzz/config/ovs.dict \
 	tests/oss-fuzz/config/expr.dict \
-	tests/oss-fuzz/config/odp.dict
\ No newline at end of file
+	tests/oss-fuzz/config/odp.dict \
+	tests/oss-fuzz/config/ofp-flow.dict
diff --git a/tests/oss-fuzz/config/ofctl_parse_target.options b/tests/oss-fuzz/config/ofctl_parse_target.options
new file mode 100644
index 000000000..6d67dd6ad
--- /dev/null
+++ b/tests/oss-fuzz/config/ofctl_parse_target.options
@@ -0,0 +1,3 @@ 
+[libfuzzer]
+close_fd_mask = 3
+dict = ofp-flow.dict
diff --git a/tests/oss-fuzz/config/ofp-flow.dict b/tests/oss-fuzz/config/ofp-flow.dict
new file mode 100644
index 000000000..01175e5eb
--- /dev/null
+++ b/tests/oss-fuzz/config/ofp-flow.dict
@@ -0,0 +1,45 @@ 
+")"
+"("
+"["
+"]"
+","
+"-"
+"0"
+":"
+"="
+"ADD"
+"DEL"
+"DEL_STRICT"
+"MOD"
+"MOD_STRICT"
+"\x00"
+"\x20"
+"\x3F"
+"actions"
+"add"
+"allow_hidden_fields"
+"cc"
+"check_overlap"
+"cookie"
+"delete"
+"delete_strict"
+"duration"
+"eth"
+"hard_age"
+"hard_timeout"
+"idle_age"
+"idle_timeout"
+"importance"
+"modify"
+"modify_strict"
+"n_bytes"
+"n_packets"
+"no_byte_counts"
+"no_packet_counts"
+"no_readonly_table"
+"out_group"
+"out_port"
+"priority"
+"reset_counts"
+"send_flow_rem"
+"table"
diff --git a/tests/oss-fuzz/ofctl_parse_target.c b/tests/oss-fuzz/ofctl_parse_target.c
new file mode 100644
index 000000000..29f89f9b5
--- /dev/null
+++ b/tests/oss-fuzz/ofctl_parse_target.c
@@ -0,0 +1,106 @@ 
+#include <config.h>
+#include "fuzzer.h"
+#include "openvswitch/ofp-flow.h"
+#include "ofp-version-opt.h"
+#include "ofproto/ofproto.h"
+#include "openflow/openflow.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
+#include "util.h"
+
+static void
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
+                    enum ofputil_protocol usable_protocols)
+{
+    enum ofputil_protocol protocol = 0;
+    char *usable_s;
+    size_t i;
+
+    usable_s = ofputil_protocols_to_string(usable_protocols);
+    printf("usable protocols: %s\n", usable_s);
+    free(usable_s);
+
+    if (!(usable_protocols & OFPUTIL_P_ANY)) {
+        printf("no usable protocol\n");
+    }
+    for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+        protocol = 1 << i;
+        if (protocol & usable_protocols & OFPUTIL_P_ANY) {
+            break;
+        }
+    }
+    ovs_assert(is_pow2(protocol));
+
+    printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
+
+    for (i = 0; i < n_fms; i++) {
+        struct ofputil_flow_mod *fm = &fms[i];
+        struct ofpbuf *msg;
+
+        msg = ofputil_encode_flow_mod(fm, protocol);
+        ofpbuf_delete(msg);
+
+        free(CONST_CAST(struct ofpact *, fm->ofpacts));
+        minimatch_destroy(&fm->match);
+    }
+}
+
+/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
+ * it back to stdout.  */
+static void
+ofctl_parse_flow(const char *input, int command)
+{
+    enum ofputil_protocol usable_protocols;
+    struct ofputil_flow_mod fm;
+    char *error;
+
+    error = parse_ofp_flow_mod_str(&fm, input, NULL, NULL,
+                                   command, &usable_protocols);
+    if (error) {
+        printf("Error encountered: %s\n", error);
+    }
+    ofctl_parse_flows__(&fm, 1, usable_protocols);
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+    /* Bail out if we cannot construct at least a 1 char string.
+     * Reserve 1 byte to decide flow mod command.
+     *
+     * Here's the structure of data we expect
+     * |--Byte 1--|--Byte 2--|...|--Byte (size-1)--|
+     *
+     * where,
+     *
+     * Byte 1: Used to decide which ofp flow mod command to test
+     * Bytes 2--(size-1): The C string that is actually passed to
+     *                    ofctl_parse_flow() test API.
+     *
+     * This means that the fuzzed input is actually a C string of
+     * length = (size -2) with the terminal byte being the NUL
+     * character. Moreover, this string is expected to not contain
+     * a new-line character.
+     */
+    const char *stream = (const char *) data;
+    if (size < 3 || stream[size - 1] != '\0' || strchr(&stream[1], '\n') ||
+        strlen(&stream[1]) != size - 2) {
+        return 0;
+    }
+
+    /* Disable logging to avoid write to disk. */
+    static bool isInit = false;
+    if (!isInit) {
+        vlog_set_verbosity("off");
+        isInit = true;
+    }
+
+    /* Decide test parameters using first byte of fuzzed input. */
+    int command = (stream[0] % OFPFC_DELETE_STRICT) + 1;
+
+    /* Fuzz extended match parsing. */
+    const char *input = &stream[1];
+    ofctl_parse_flow(input, command);
+
+    return 0;
+}