From patchwork Mon Oct 15 09:23:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bhargava Shastry X-Patchwork-Id: 984006 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=sect.tu-berlin.de Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42YY0N58NYz9s5c for ; Mon, 15 Oct 2018 20:23:48 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 9B172191A; Mon, 15 Oct 2018 09:23:44 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id B144C190F for ; Mon, 15 Oct 2018 09:23:43 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mail.sec.t-labs.tu-berlin.de (mail.sec.t-labs.tu-berlin.de [130.149.230.43]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id ADE236E0 for ; Mon, 15 Oct 2018 09:23:42 +0000 (UTC) From: bshastry@sect.tu-berlin.de To: dev@openvswitch.org Date: Mon, 15 Oct 2018 11:23:33 +0200 Message-Id: <20181015092333.14074-1-bshastry@sect.tu-berlin.de> X-Mailer: git-send-email 2.17.1 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: Bhargava Shastry Subject: [ovs-dev] [PATCH] ossfuzz: Add ofctl parse target X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Bhargava Shastry 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 --- 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 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 +#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; +}