From ea43b024a8a03f6648d0e06589137a4fbfea9f5a Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 3 Oct 2018 01:15:58 +0200 Subject: [PATCH] ossfuzz: Additions for new ODP parser target This patch adds a new oss-fuzz target for the ODP parser. The target harness has been adapted from test-odp.c. Prominently, it leaves out "parse_filter" due to an unresolvable bug in that code at the time of writing. It also includes the following: - a fuzzing dictionary - fuzzing config - some automake additions for the new target Signed-off-by: Bhargava Shastry Signed-off-by: Ben Pfaff --- tests/oss-fuzz/automake.mk | 13 +- tests/oss-fuzz/config/odp.dict | 170 +++++++++++++++++++++++ tests/oss-fuzz/config/odp_target.options | 3 + tests/oss-fuzz/odp_target.c | 147 ++++++++++++++++++++ 4 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 tests/oss-fuzz/config/odp.dict create mode 100644 tests/oss-fuzz/config/odp_target.options create mode 100644 tests/oss-fuzz/odp_target.c diff --git a/tests/oss-fuzz/automake.mk b/tests/oss-fuzz/automake.mk index 3e3ac2f9c5d..4fbdb4c2b87 100644 --- a/tests/oss-fuzz/automake.mk +++ b/tests/oss-fuzz/automake.mk @@ -2,7 +2,8 @@ OSS_FUZZ_TARGETS = \ tests/oss-fuzz/flow_extract_target \ tests/oss-fuzz/json_parser_target \ tests/oss-fuzz/ofp_print_target \ - tests/oss-fuzz/expr_parse_target + tests/oss-fuzz/expr_parse_target \ + tests/oss-fuzz/odp_target EXTRA_PROGRAMS += $(OSS_FUZZ_TARGETS) oss-fuzz-targets: $(OSS_FUZZ_TARGETS) @@ -31,10 +32,18 @@ tests_oss_fuzz_expr_parse_target_LDADD = lib/libopenvswitch.la \ ovn/lib/libovn.la tests_oss_fuzz_expr_parse_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ +tests_oss_fuzz_odp_target_SOURCES = \ + tests/oss-fuzz/odp_target.c \ + tests/oss-fuzz/fuzzer.h +tests_oss_fuzz_odp_target_LDADD = lib/libopenvswitch.la +tests_oss_fuzz_odp_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++ + EXTRA_DIST += \ tests/oss-fuzz/config/flow_extract_target.options \ tests/oss-fuzz/config/json_parser_target.options \ tests/oss-fuzz/config/ofp_print_target.options \ tests/oss-fuzz/config/expr_parse_target.options \ + tests/oss-fuzz/config/odp_target.options \ tests/oss-fuzz/config/ovs.dict \ - tests/oss-fuzz/config/expr.dict + tests/oss-fuzz/config/expr.dict \ + tests/oss-fuzz/config/odp.dict diff --git a/tests/oss-fuzz/config/odp.dict b/tests/oss-fuzz/config/odp.dict new file mode 100644 index 00000000000..a9bdd3e9973 --- /dev/null +++ b/tests/oss-fuzz/config/odp.dict @@ -0,0 +1,170 @@ +"(" +")" +"," +"/0x" +"0x" +": " +"=" +"=r" +"action" +"arp" +"bfd" +"bos" +"c1" +"c2" +"c3" +"c4" +"cc" +"cfi" +"cfm" +"class" +"clone" +"code" +"commit," +"crit," +"csum" +"ct" +"ct(error)" +"ct_clear" +"ct_label" +"ct_mark" +"ct_state" +"ct_tuple4" +"ct_tuple6" +"ct_zone" +"dir" +"dl_type" +"dnat" +"dp_hash" +"drop" +"dst" +"dst_port" +"egress" +"encap" +"error" +"erspan" +"erspan(ver=1,sid=0x,dir=,idx=0x)" +"est" +"eth" +"eth_type" +"eth_type(0/)" +"first" +"flags" +"force_commit," +"frag" +"gbp(" +"geneve" +"gre((flags=0x,proto=0x)" +"hash," +"helper" +"hlimit" +"hwid" +"icmp" +"icmpv6" +"id" +"idx=" +"in_port" +"ingress" +"inv" +"ip6erspan" +"ipfix(output_port=" +"ipv4" +"ipv6" +"ipv6_dst" +"ipv6_src" +"l4()" +"label" +"lacp" +"later" +"len" +"lldp" +"mark=/," +"match" +"md2" +"mdtype" +"meter()" +"mpls" +"nat" +"nd" +"new" +"no" +"np" +"ns" +"nsh" +"oam," +"op" +"options(" +"out_port(" +"packet_type" +"pcp=" +"persistent," +"pop_eth" +"pop_mpls(eth_type=0x)" +"pop_nsh()" +"pop_vlan" +"proto" +"push_eth(src=:::::,dst=:::::,type=)" +"push_mpls(" +"push_nsh(" +"push_vlan(" +"push_vlan(tpid=,vid=,pcp=,cfi=)" +"random," +"recirc()" +"recirc_id" +"rel" +"rpl" +"sFlow(vid=,pcp=,output=)" +"sample" +"sctp" +"seq=0x" +"set(" +"set(nsh(" +"sha" +"si" +"sip" +"skb_mark" +"skb_priority" +"sll" +"slow_path" +"snat" +"spi" +"src" +"src_port" +"stp" +"sym_l4()" +"target" +"tc=" +"tclass" +"tcp" +"tcp_flags" +"tha" +"tip" +"tll" +"tnl_pop(" +"tnl_push(tnl_port(" +"too_little" +"too_much" +"tos" +"tp_dst" +"tp_src" +"tpid=0x" +"trk" +"trunc()" +"ttl" +"tun_id" +"tunnel" +"tunnel_out_port" +"type" +"udp" +"unspec" +"userdata" +"userspace(" +"userspace(error)" +"userspace(pid" +"ver" +"vid" +"vlan" +"vxlan" +"vxlan(flags=0x,vni=0x)" +"vxlan(gbp(" +"zone" diff --git a/tests/oss-fuzz/config/odp_target.options b/tests/oss-fuzz/config/odp_target.options new file mode 100644 index 00000000000..41821b8c196 --- /dev/null +++ b/tests/oss-fuzz/config/odp_target.options @@ -0,0 +1,3 @@ +[libfuzzer] +close_fd_mask = 3 +dict = odp.dict diff --git a/tests/oss-fuzz/odp_target.c b/tests/oss-fuzz/odp_target.c new file mode 100644 index 00000000000..93231bde335 --- /dev/null +++ b/tests/oss-fuzz/odp_target.c @@ -0,0 +1,147 @@ +#include +#include "fuzzer.h" +#undef NDEBUG +#include "odp-util.h" +#include +#include "openvswitch/dynamic-string.h" +#include "flow.h" +#include "openvswitch/match.h" +#include "openvswitch/ofpbuf.h" +#include "util.h" +#include "openvswitch/ofp-flow.h" +#include "openvswitch/vlog.h" + +static int +parse_keys(bool wc_keys, const char *in) +{ + int exit_code = 0; + + enum odp_key_fitness fitness; + struct ofpbuf odp_key; + struct ofpbuf odp_mask; + struct flow flow; + struct ds out; + int error; + + /* Convert string to OVS DP key. */ + ofpbuf_init(&odp_key, 0); + ofpbuf_init(&odp_mask, 0); + error = odp_flow_from_string(in, NULL, + &odp_key, &odp_mask); + if (error) { + printf("odp_flow_from_string: error\n"); + goto next; + } + + if (!wc_keys) { + struct odp_flow_key_parms odp_parms = { + .flow = &flow, + .support = { + .recirc = true, + .ct_state = true, + .ct_zone = true, + .ct_mark = true, + .ct_label = true, + .max_vlan_headers = SIZE_MAX, + }, + }; + + /* Convert odp_key to flow. */ + fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); + switch (fitness) { + case ODP_FIT_PERFECT: + break; + + case ODP_FIT_TOO_LITTLE: + printf("ODP_FIT_TOO_LITTLE: "); + break; + + case ODP_FIT_TOO_MUCH: + printf("ODP_FIT_TOO_MUCH: "); + break; + + case ODP_FIT_ERROR: + printf("odp_flow_key_to_flow: error\n"); + goto next; + } + /* Convert cls_rule back to odp_key. */ + ofpbuf_uninit(&odp_key); + ofpbuf_init(&odp_key, 0); + odp_flow_key_from_flow(&odp_parms, &odp_key); + + if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) { + printf ("too long: %"PRIu32" > %d\n", + odp_key.size, ODPUTIL_FLOW_KEY_BYTES); + exit_code = 1; + } + } + + /* Convert odp_key to string. */ + ds_init(&out); + if (wc_keys) { + odp_flow_format(odp_key.data, odp_key.size, + odp_mask.data, odp_mask.size, NULL, &out, false); + } else { + odp_flow_key_format(odp_key.data, odp_key.size, &out); + } + puts(ds_cstr(&out)); + ds_destroy(&out); + +next: + ofpbuf_uninit(&odp_key); + ofpbuf_uninit(&odp_mask); + + return exit_code; +} + +static int +parse_actions(const char *in) +{ + struct ofpbuf odp_actions; + struct ds out; + int error; + + /* Convert string to OVS DP actions. */ + ofpbuf_init(&odp_actions, 0); + error = odp_actions_from_string(in, NULL, &odp_actions); + if (error) { + printf("odp_actions_from_string: error\n"); + goto next; + } + + /* Convert odp_actions back to string. */ + ds_init(&out); + format_odp_actions(&out, odp_actions.data, odp_actions.size, NULL); + puts(ds_cstr(&out)); + ds_destroy(&out); + +next: + ofpbuf_uninit(&odp_actions); + return 0; +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + /* Bail out if we cannot construct at least a 1 char string. */ + const char *input = (const char *) data; + if (size < 2 || input[size - 1] != '\0' || strchr(input, '\n')) { + return 0; + } + + /* Disable logging to avoid write to disk. */ + static bool isInit = false; + if (!isInit) { + vlog_set_verbosity("off"); + isInit = true; + } + + /* Parse keys and wc keys. */ + parse_keys(false, input); + parse_keys(true, input); + + /* Parse actions. */ + parse_actions(input); + + return 0; +}