From b2e05fb61858d7ed3a388f07fbb3d400ec986c51 Mon Sep 17 00:00:00 2001 From: Iggy Frankovic Date: Fri, 25 Aug 2023 08:38:49 -0700 Subject: [PATCH] *: enable fuzzing This change introduces support for fuzzing daemons. There are two types of changes included here: * Code that implements shims, harnesses, entry points and other required constructs to enable AFL and libFuzzer to attach to and run the target daemons * Code that tweaks daemon behavior to allow fuzzers to find more bugs, cover more code, and work around certain fuzzer constraints; these are things like disabling packet checksums, disabling the event loop, removing i/o calls, etc. Both types are usually wrapped by #ifdef FUZZER blocks just to mark them for readers of the code. Support for both AFL and libFuzzer is included. Signed-off-by: Quentin Young Signed-off-by: Iggy Frankovic --- bgpd/bgp_fsm.c | 4 + bgpd/bgp_io.c | 16 +++- bgpd/bgp_io.h | 2 + bgpd/bgp_keepalives.c | 6 ++ bgpd/bgp_main.c | 185 ++++++++++++++++++++++++++++++++++++++++- bgpd/bgp_packet.c | 15 +++- bgpd/bgpd.c | 27 ++++-- bgpd/subdir.am | 4 + configure.ac | 23 ++++- lib/checksum.c | 6 +- lib/command.c | 4 + lib/frrevent.h | 14 ++++ lib/fuzz.h | 26 ++++++ lib/graph.c | 6 ++ lib/if.c | 5 +- lib/if.h | 4 + lib/jhash.c | 3 + lib/libfrr.c | 133 +++++++++++++++++++++++++++++ lib/libfrr.h | 3 + lib/memory.c | 4 + lib/memory.h | 6 ++ lib/prefix.c | 3 +- lib/printf/vfprintf.c | 8 +- lib/privs.c | 23 ++++- lib/zclient.c | 67 +++++++++++++++ lib/zclient.h | 5 ++ lib/zebra.h | 11 +++ lib/zlog.h | 16 ++++ ospfd/ospf_lsa.c | 9 +- ospfd/ospf_main.c | 133 +++++++++++++++++++++++++++++ ospfd/ospf_neighbor.c | 7 +- ospfd/ospf_network.c | 3 + ospfd/ospf_nsm.h | 5 ++ ospfd/ospf_packet.c | 40 +++++++-- ospfd/ospf_packet.h | 9 ++ ospfd/ospfd.h | 4 + ospfd/subdir.am | 1 + pimd/pim_iface.c | 22 +++-- pimd/pim_iface.h | 7 ++ pimd/pim_ifchannel.h | 25 ++++-- pimd/pim_igmp.c | 4 + pimd/pim_igmpv3.c | 21 ++++- pimd/pim_instance.c | 7 +- pimd/pim_instance.h | 2 +- pimd/pim_main.c | 147 ++++++++++++++++++++++++++++++++ pimd/pim_mlag.c | 7 +- pimd/pim_mroute.c | 15 ++++ pimd/pim_mroute.h | 3 +- pimd/pim_neighbor.c | 6 +- pimd/pim_pim.c | 19 ++++- pimd/pim_register.c | 5 ++ pimd/pim_rp.c | 3 + pimd/pim_tlv.h | 6 +- pimd/pim_upstream.h | 135 ++++++++++++++++++++---------- pimd/pimd.c | 2 +- pimd/subdir.am | 2 + vrrpd/subdir.am | 1 + vrrpd/vrrp.c | 13 ++- vrrpd/vrrp.h | 8 ++ vrrpd/vrrp_main.c | 101 ++++++++++++++++++++++ vrrpd/vrrp_packet.c | 3 +- vrrpd/vrrp_zebra.c | 8 ++ vrrpd/vrrp_zebra.h | 5 ++ zebra/kernel_netlink.c | 101 +++++++++++++++------- zebra/kernel_netlink.h | 6 ++ zebra/main.c | 124 +++++++++++++++++++++++++++ zebra/subdir.am | 1 + zebra/zapi_msg.c | 32 ++++++- zebra/zebra_gr.c | 20 ++++- zebra/zebra_mlag.c | 6 +- zebra/zserv.c | 24 +++++- zebra/zserv.h | 6 ++ 72 files changed, 1587 insertions(+), 150 deletions(-) create mode 100644 lib/fuzz.h diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index cdb3ead160..efec391513 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -3,6 +3,7 @@ * From RFC1771 [A Border Gateway Protocol 4 (BGP-4)] * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro */ +#define FUZZING 1 #include @@ -354,6 +355,9 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) structure. */ void bgp_timer_set(struct peer *peer) { +#ifdef FUZZING + return; +#endif afi_t afi; safi_t safi; diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index a375bd6005..90bf7f115d 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -33,7 +33,7 @@ static uint16_t bgp_write(struct peer *); static uint16_t bgp_read(struct peer *peer, int *code_p); static void bgp_process_writes(struct event *event); static void bgp_process_reads(struct event *event); -static bool validate_header(struct peer *); +bool validate_header(struct peer *); /* generic i/o status codes */ #define BGP_IO_TRANS_ERR (1 << 0) /* EAGAIN or similar occurred */ @@ -44,6 +44,9 @@ static bool validate_header(struct peer *); void bgp_writes_on(struct peer *peer) { +#ifdef FUZZING + return; +#endif struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); @@ -62,6 +65,9 @@ void bgp_writes_on(struct peer *peer) void bgp_writes_off(struct peer *peer) { +#ifdef FUZZING + return; +#endif struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); @@ -73,6 +79,9 @@ void bgp_writes_off(struct peer *peer) void bgp_reads_on(struct peer *peer) { +#ifdef FUZZING + return; +#endif struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); @@ -93,6 +102,9 @@ void bgp_reads_on(struct peer *peer) void bgp_reads_off(struct peer *peer) { +#ifdef FUZZING + return; +#endif struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); @@ -546,7 +558,7 @@ static uint16_t bgp_read(struct peer *peer, int *code_p) * Assumes that there are at least BGP_HEADER_SIZE readable bytes in the input * buffer. */ -static bool validate_header(struct peer *peer) +bool validate_header(struct peer *peer) { uint16_t size; uint8_t type; diff --git a/bgpd/bgp_io.h b/bgpd/bgp_io.h index 4c92373c2d..ce398ddf68 100644 --- a/bgpd/bgp_io.h +++ b/bgpd/bgp_io.h @@ -14,6 +14,8 @@ #include "bgpd/bgpd.h" #include "frr_pthread.h" +bool validate_header(struct peer *p); + /** * Start function for write thread. * diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index 48bde1220d..ed5eda61d9 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -231,6 +231,9 @@ void *bgp_keepalives_start(void *arg) void bgp_keepalives_on(struct peer *peer) { +#ifdef FUZZING + return; +#endif if (CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON)) return; @@ -260,6 +263,9 @@ void bgp_keepalives_on(struct peer *peer) void bgp_keepalives_off(struct peer *peer) { +#ifdef FUZZING + return; +#endif if (!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON)) return; diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 074059c146..72722b19da 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -28,6 +28,7 @@ #include "ns.h" #include "bgpd/bgpd.h" +#include "bgp_io.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_mplsvpn.h" @@ -367,6 +368,168 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, #define DEPRECATED_OPTIONS "" +#ifdef FUZZING +#include "lib/ringbuf.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static bool FuzzingInit(void) { + int bgp_port = BGP_PORT_DEFAULT; + struct list *addresses = list_new(); + int no_fib_flag = 0; + int no_zebra_flag = 0; + int skip_runas = 0; + int instance = 0; + int buffer_size = BGP_SOCKET_SNDBUF_SIZE; + struct listnode *node; + + addresses->cmp = (int (*)(void *, void *))strcmp; + + const char *name[] = { "bgpd" }; + + frr_preinit(&bgpd_di, 1, (char **) name); + + /* Initialize basic BGP datastructures */ + bgp_master_init(frr_init_fast(), buffer_size, addresses); + bm->port = bgp_port; + bgp_option_set(BGP_OPT_NO_LISTEN); + + bgp_option_set(BGP_OPT_NO_FIB); + bgp_option_set(BGP_OPT_NO_ZEBRA); + bgp_error_init(); + // bgp_vrf_init(); + bgp_init((unsigned short)instance); + + + /* Create a default instance and peer */ + struct bgp *b; + as_t as = 65001; + bgp_get(&b, &as, "default", BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_UNDEFINED); + + return true; +} + +/* + * Create peer structure that we'll use as the receiver for fuzzed packets. + * + * state + * BGP FSM state to create the peer in + */ +static struct peer *FuzzingCreatePeer(int state) +{ + union sockunion su; + sockunion_init(&su); + inet_pton(AF_INET, "10.1.1.1", &su.sin.sin_addr); + su.sin.sin_family = AF_INET; + su.sin.sin_port = 2001; + struct peer *p = peer_create(&su, NULL, bgp_get_default(), 65000, 65001, + AS_UNSPECIFIED, NULL, 1, NULL); + p->bgp->rpkt_quanta = 1; + p->status = state; + p->as_type = AS_EXTERNAL; + + /* set all flags */ + afi_t afi; + safi_t safi; + p->cap |= 0xFFFF; + FOREACH_AFI_SAFI(afi, safi) { + SET_FLAG(p->af_cap[afi][safi], 0x3FFF); + } + + peer_activate(p, AFI_L2VPN, SAFI_EVPN); + peer_activate(p, AFI_IP, SAFI_MPLS_VPN); + + return p; +} + +static struct peer *FuzzingPeer; + +static bool FuzzingInitialized; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (!FuzzingInitialized) { + FuzzingInit(); + FuzzingInitialized = true; + /* See comment below */ + FuzzingPeer = FuzzingCreatePeer(Established); + } + //make_route_maps(); + /* + * In the AFL standalone case, the peer will already be created for us + * before __AFL_INIT() is called to speed things up. We can't pass it + * as an argument because the function signature must match libFuzzer's + * expectations, so it's saved in a global variable. Even though we'll + * be exiting the program after each run, we still destroy the peer + * because that increases our coverage and is likely to find memory + * leaks when pointers are nulled that would otherwise be "in-use at + * exit". + * + * In the libFuzzer case, we can either create and destroy it each time + * to fuzz single packet rx, or we can keep the peer around, which will + * fuzz a long lived session. Of course, as state accumulates over + * time, memory usage will grow, which imposes some resource + * constraints on the fuzzing host. In practice a reasonable server + * machine with 64gb of memory or so should be able to fuzz + * indefinitely; if bgpd consumes this much memory over time, that + * behavior should probably be considered a bug. + */ + struct peer *p; +#ifdef FUZZING_LIBFUZZER + /* For non-persistent mode */ + // p = FuzzingCreatePeer(); + /* For persistent mode */ + p = FuzzingPeer; +#else + p = FuzzingPeer; +#endif /* FUZZING_LIBFUZZER */ + + ringbuf_reset(p->ibuf_work); + ringbuf_put(p->ibuf_work, data, size); + + int result = 0; + unsigned char pktbuf[BGP_MAX_PACKET_SIZE]; + uint16_t pktsize = 0; + + /* + * Simulate the read process done by bgp_process_reads(). + * + * The actual packet processing code assumes that the size field in the + * BGP message is correct, and this check is performed by the i/o code, + * so we need to make sure that remains true for fuzzed input. + * + * Also, validate_header() assumes that there is at least + * BGP_HEADER_SIZE bytes in ibuf_work. + */ + if ((size < BGP_HEADER_SIZE) || !validate_header(p)) { + goto done; + } + + ringbuf_peek(p->ibuf_work, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize)); + pktsize = ntohs(pktsize); + + assert(pktsize <= p->max_packet_size); + + if (ringbuf_remain(p->ibuf_work) >= pktsize) { + struct stream *pkt = stream_new(pktsize); + ringbuf_get(p->ibuf_work, pktbuf, pktsize); + stream_put(pkt, pktbuf, pktsize); + p->curr = pkt; + + struct event t = {}; + t.arg = p; + bgp_process_packet(&t); + } + +done: + //peer_delete(p); + + return 0; +}; +#endif /* FUZZING */ + +#ifndef FUZZING_LIBFUZZER /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ int main(int argc, char **argv) @@ -383,10 +546,29 @@ int main(int argc, char **argv) int buffer_size = BGP_SOCKET_SNDBUF_SIZE; char *address; struct listnode *node; + //char *bgp_address = NULL; - addresses->cmp = (int (*)(void *, void *))strcmp; + //addresses->cmp = (int (*)(void *, void *))strcmp; frr_preinit(&bgpd_di, argc, argv); + +#ifdef FUZZING + FuzzingInit(); + FuzzingPeer = FuzzingCreatePeer(Established); + FuzzingInitialized = true; + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif /* __AFL_HAVE_MANUAL_CONTROL */ + uint8_t *input; + int r = frrfuzz_read_input(&input); + + if (!input) + return 0; + + return LLVMFuzzerTestOneInput(input, r); +#endif /* FUZZING */ + frr_opt_add( "p:l:SnZe:I:s:" DEPRECATED_OPTIONS, longopts, " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n" @@ -507,3 +689,4 @@ int main(int argc, char **argv) /* Not reached. */ return 0; } +#endif /* FUZZING_LIBFUZZER */ \ No newline at end of file diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ec692277b2..8fd262fd97 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -4,6 +4,7 @@ * Copyright (C) 2017 Cumulus Networks * Copyright (C) 1999 Kunihiro Ishiguro */ +#define FUZZING 1 #include #include @@ -721,6 +722,7 @@ void bgp_open_send(struct peer *peer) * @param peer * @return 0 */ +#ifndef FUZZING static void bgp_write_notify(struct peer *peer) { int ret, val; @@ -780,6 +782,7 @@ static void bgp_write_notify(struct peer *peer) stream_free(s); } +#endif /* * Encapsulate an original BGP CEASE Notification into Hard Reset @@ -1034,7 +1037,9 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, peer->bgp->peer); +#ifndef FUZZING bgp_write_notify(peer); +#endif } /* @@ -1608,12 +1613,14 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) return BGP_Stop; /* Get sockname. */ +#ifndef FUZZING if (bgp_getsockname(peer) < 0) { flog_err_sys(EC_LIB_SOCKET, "%s: bgp_getsockname() failed for peer: %s", __func__, peer->host); return BGP_Stop; } +#endif /* Set remote router-id */ peer->remote_id = remote_id; @@ -1741,7 +1748,9 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) #endif } } +#ifndef FUZZING peer->rtt = sockopt_tcp_rtt(peer->fd); +#endif return Receive_OPEN_message; } @@ -2893,10 +2902,11 @@ void bgp_process_packet(struct event *thread) bgp_size_t size; char notify_data_length[2]; +#ifndef FUZZING frr_with_mutex (&peer->io_mtx) { peer->curr = stream_fifo_pop(peer->ibuf); } - +#endif if (peer->curr == NULL) // no packets to process, hmm... return; @@ -3001,6 +3011,9 @@ void bgp_process_packet(struct event *thread) /* delete processed packet */ stream_free(peer->curr); peer->curr = NULL; +#ifdef FUZZING + return; +#endif processed++; /* Update FSM */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 0c72b2fb30..c4225cd550 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1419,7 +1419,11 @@ struct peer *peer_new(struct bgp *bgp) ringbuf_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE/2); /* Get service port number. */ +#ifndef FUZZING sp = getservbyname("bgp", "tcp"); +#else + sp = NULL; +#endif peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs(sp->s_port); QOBJ_REG(peer, peer); @@ -8248,35 +8252,42 @@ void bgp_init(unsigned short instance) /* allocates some vital data structures used by peer commands in * vty_init */ - +#ifndef FUZZING /* pre-init pthreads */ bgp_pthreads_init(); /* Init zebra. */ bgp_zebra_init(bm->master, instance); - +#endif #ifdef ENABLE_BGP_VNC vnc_zebra_init(bm->master); #endif /* BGP VTY commands installation. */ +#ifndef FUZZING bgp_vty_init(); +#endif /* BGP inits. */ bgp_attr_init(); - bgp_debug_init(); bgp_community_alias_init(); +#ifndef FUZZING + bgp_debug_init(); bgp_dump_init(); +#endif bgp_route_init(); bgp_route_map_init(); +#ifndef FUZZING bgp_scan_vty_init(); +#endif bgp_mplsvpn_init(); +#ifndef FUZZING #ifdef ENABLE_BGP_VNC rfapi_init(); #endif bgp_ethernetvpn_init(); bgp_flowspec_vty_init(); - +#endif /* Access list initialize. */ access_list_init(); access_list_add_hook(peer_distribute_update); @@ -8295,14 +8306,14 @@ void bgp_init(unsigned short instance) /* Community list initialize. */ bgp_clist = community_list_init(); + bgp_label_per_nexthop_init(); + +#ifndef FUZZING /* BFD init */ bgp_bfd_init(bm->master); - bgp_lp_vty_init(); - - bgp_label_per_nexthop_init(); - cmd_variable_handler_register(bgp_viewvrf_var_handlers); +#endif } void bgp_terminate(void) diff --git a/bgpd/subdir.am b/bgpd/subdir.am index c2dd207a49..1e166943da 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -186,6 +186,10 @@ noinst_HEADERS += \ bgpd_bgpd_SOURCES = bgpd/bgp_main.c bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c +bgpd_bgpd_LDFLAGS = $(AM_LDFLAGS) $(BGPD_SAN_FLAGS) +bgpd_bgp_btoa_CFLAGS = $(AM_CFLAGS) +bgpd_bgp_btoa_LDLAGS = $(AM_LDLAGS) + # RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS) bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS) diff --git a/configure.ac b/configure.ac index a0819a1f22..7cbfa65fc1 100644 --- a/configure.ac +++ b/configure.ac @@ -353,7 +353,7 @@ AC_C_FLAG([-fms-extensions], [ ]) AC_C_FLAG([-fno-omit-frame-pointer]) AC_C_FLAG([-funwind-tables]) -AC_C_FLAG([-Wall]) +#AC_C_FLAG([-Wall]) AC_C_FLAG([-Wextra]) AC_C_FLAG([-Wformat-nonliteral]) AC_C_FLAG([-Wformat-security]) @@ -423,7 +423,26 @@ if test "$enable_undefined_sanitizer" = "yes"; then SAN_FLAGS="$SAN_FLAGS -fsanitize=undefined" ]) fi +if test "$enable_libfuzzer" = "yes"; then + AC_C_FLAG([-fsanitize=address,fuzzer], [ + AC_MSG_ERROR([$CC does not support ASAN / libFuzzer.]) + ], [ + AC_MSG_NOTICE([WARNING - libFuzzer only enabled on supported daemons]) + SAN_FLAGS="$SAN_FLAGS -fsanitize=fuzzer-no-link" + BGPD_SAN_FLAGS="-fsanitize=fuzzer" + ZEBRA_SAN_FLAGS="-fsanitize=fuzzer" + OSPFD_SAN_FLAGS="-fsanitize=fuzzer" + VRRPD_SAN_FLAGS="-fsanitize=fuzzer" + PIMD_SAN_FLAGS="-fsanitize=fuzzer" + AC_DEFINE([FUZZING_LIBFUZZER], [1], [Compiling and linking with libFuzzer]) + ]) +fi AC_SUBST([SAN_FLAGS]) +AC_SUBST([BGPD_SAN_FLAGS]) +AC_SUBST([ZEBRA_SAN_FLAGS]) +AC_SUBST([OSPFD_SAN_FLAGS]) +AC_SUBST([VRRPD_SAN_FLAGS]) +AC_SUBST([PIMD_SAN_FLAGS]) dnl frr-format.so if test "$with_frr_format" != "no" -a "$with_frr_format" != "yes" -a -n "$with_frr_format"; then @@ -763,6 +782,8 @@ AC_ARG_ENABLE([memory-sanitizer], AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads])) AC_ARG_ENABLE([undefined-sanitizer], AS_HELP_STRING([--enable-undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior])) +AC_ARG_ENABLE([libfuzzer], + AS_HELP_STRING([--libfuzzer], [enable libFuzzer])) AC_ARG_WITH([crypto], AS_HELP_STRING([--with-crypto=], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)])) AC_ARG_WITH([frr-format], diff --git a/lib/checksum.c b/lib/checksum.c index 6c5f06de45..bead799969 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -21,6 +21,7 @@ uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) { const struct iovec *iov_end; uint32_t sum = 0; + register unsigned short answer; /* assumes unsigned short == 16 bits */ union { uint8_t bytes[2]; @@ -78,7 +79,9 @@ uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ sum += (sum >> 16); /* add carry */ - return ~sum; + /* ones-complement, then truncate to 16 bits */ + answer = (unsigned short)~sum; + return (answer); } /* Fletcher Checksum -- Refer to RFC1008. */ @@ -90,6 +93,7 @@ uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) without modifying the buffer; a valid checksum returns 0 */ uint16_t fletcher_checksum(uint8_t *buffer, const size_t len, const uint16_t offset) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { uint8_t *p; int x, y, c0, c1; diff --git a/lib/command.c b/lib/command.c index 8025ab534f..edc964b9d5 100644 --- a/lib/command.c +++ b/lib/command.c @@ -8,6 +8,7 @@ * Copyright (C) 2013 by Open Source Routing. * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") */ +#define FUZZING 1 #include #include @@ -284,6 +285,9 @@ void cmd_defer_tree(bool val) /* Install a command into a node. */ void _install_element(enum node_type ntype, const struct cmd_element *cmd) { +#ifdef FUZZING + return; +#endif struct cmd_node *cnode; /* cmd_init hasn't been called */ diff --git a/lib/frrevent.h b/lib/frrevent.h index 2b0c52bb51..16ed12038f 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -185,6 +185,8 @@ struct cpu_event_history { _event_add_##addfn(&_xref, m, f, a, v, t); \ }) /* end */ +#ifndef FUZZING + #define event_add_read(m, f, a, v, t) _xref_t_a(read_write, READ, m, f, a, v, t) #define event_add_write(m, f, a, v, t) \ _xref_t_a(read_write, WRITE, m, f, a, v, t) @@ -207,6 +209,18 @@ struct cpu_event_history { XREF_LINK(_xref.xref); \ _event_execute(&_xref, m, f, a, v); \ }) /* end */ +#else + +#define event_add_read(m, f, a, v, t) 0 +#define event_add_write(m, f, a, v, t) 0 +#define event_add_timer(m, f, a, v, t) 0 +#define event_add_timer_msec(m, f, a, v, t) 0 +#define event_add_timer_tv(m, f, a, v, t) 0 +#define event_add_event(m, f, a, v, t) 0 +#define event_execute(m, f, a, v) 0 +#define event_execute_name(m, f, a, v, n) 0 + +#endif /* Prototypes. */ extern struct event_loop *event_master_create(const char *name); diff --git a/lib/fuzz.h b/lib/fuzz.h new file mode 100644 index 0000000000..5a80430407 --- /dev/null +++ b/lib/fuzz.h @@ -0,0 +1,26 @@ +/* +* Utilities for fuzzing frr. +*/ +#ifndef __FUZZ_H__ +#define __FUZZ_H__ + +#include +#include +#include + +static inline int frrfuzz_read_input(uint8_t **input) +{ + fseek(stdin, 0, SEEK_END); + long fsize = ftell(stdin); + if (fsize < 0) + return 0; + + *input = (uint8_t *)malloc(fsize); + + fseek(stdin, 0, SEEK_SET); + int r = fread(*input, 1, fsize, stdin); + + return r; +} + +#endif /* __FUZZ_H__ */ \ No newline at end of file diff --git a/lib/graph.c b/lib/graph.c index e6c2386d75..fc1c5853a0 100644 --- a/lib/graph.c +++ b/lib/graph.c @@ -21,6 +21,7 @@ struct graph *graph_new(void) } void graph_delete_graph(struct graph *graph) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { for (unsigned int i = vector_active(graph->nodes); i--; /**/) graph_delete_node(graph, vector_slot(graph->nodes, i)); @@ -60,6 +61,7 @@ static void graph_vector_remove(vector v, unsigned int ix) } void graph_delete_node(struct graph *graph, struct graph_node *node) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { if (!node) return; @@ -107,6 +109,7 @@ struct graph_node *graph_add_edge(struct graph_node *from, } void graph_remove_edge(struct graph_node *from, struct graph_node *to) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { // remove from from to->from for (unsigned int i = vector_active(to->from); i--; /**/) @@ -123,6 +126,7 @@ void graph_remove_edge(struct graph_node *from, struct graph_node *to) } struct graph_node *graph_find_node(struct graph *graph, void *data) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { struct graph_node *g; @@ -136,6 +140,7 @@ struct graph_node *graph_find_node(struct graph *graph, void *data) } bool graph_has_edge(struct graph_node *from, struct graph_node *to) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { for (unsigned int i = vector_active(from->to); i--; /**/) if (vector_slot(from->to, i) == to) @@ -147,6 +152,7 @@ bool graph_has_edge(struct graph_node *from, struct graph_node *to) static void _graph_dfs(struct graph *graph, struct graph_node *start, vector visited, void (*dfs_cb)(struct graph_node *, void *), void *arg) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { /* check that we have not visited this node */ for (unsigned int i = 0; i < vector_active(visited); i++) { diff --git a/lib/if.c b/lib/if.c index 6f567861d1..aad2da744a 100644 --- a/lib/if.c +++ b/lib/if.c @@ -208,7 +208,10 @@ void if_down_via_zapi(struct interface *ifp) (*ifp_master.down_hook)(ifp); } -static struct interface *if_create_name(const char *name, struct vrf *vrf) +#ifndef FUZZING +static +#endif +struct interface *if_create_name(const char *name, struct vrf *vrf) { struct interface *ifp; diff --git a/lib/if.h b/lib/if.h index c6b4fd216a..c74d450f2a 100644 --- a/lib/if.h +++ b/lib/if.h @@ -619,6 +619,10 @@ extern void if_destroy_via_zapi(struct interface *ifp); extern const struct frr_yang_module_info frr_interface_info; +#ifdef FUZZING +struct interface *if_create_name(const char *name, struct vrf *vrf); +#endif + #ifdef __cplusplus } #endif diff --git a/lib/jhash.c b/lib/jhash.c index 0d561ef3a4..2627d6bfa9 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -60,6 +60,7 @@ * the input key. */ uint32_t jhash(const void *key, uint32_t length, uint32_t initval) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { uint32_t a, b, c, len; const uint8_t *k = key; @@ -127,6 +128,7 @@ uint32_t jhash(const void *key, uint32_t length, uint32_t initval) * The length parameter here is the number of uint32_ts in the key. */ uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { uint32_t a, b, c, len; @@ -166,6 +168,7 @@ uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval) * done at the end is not done here. */ uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; diff --git a/lib/libfrr.c b/lib/libfrr.c index 33237df5fc..34216cb281 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -33,6 +33,14 @@ #include "frrscript.h" #include "systemd.h" +#if defined(FUZZING) && defined(FUZZING_LIBFUZZER) && !defined(FUZZING_OVERRIDE_LLVMFuzzerTestOneInput) +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return 0; +} +#endif /* lol */ + DEFINE_HOOK(frr_early_init, (struct event_loop * tm), (tm)); DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm)); DEFINE_HOOK(frr_config_pre, (struct event_loop * tm), (tm)); @@ -813,6 +821,131 @@ struct event_loop *frr_init(void) return master; } +#ifdef FUZZING +static struct event_loop *master; +struct event_loop *frr_init_fast(void) +{ + struct log_arg *log_arg; +#if 0 + struct option_chain *oc; + struct frrmod_runtime *module; + char moderr[256]; + const char *dir; + dir = di->module_path ? di->module_path : frr_moduledir; +#endif +#if 0 +#ifdef HAVE_SQLITE3 + snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", + frr_dbdir, p_pathspace, di->name, p_instance); +#endif +#endif + struct zprivs_ids_t ids; + + zprivs_preinit(di->privs); + zprivs_get_ids(&ids); + + zlog_init(di->progname, di->logname, di->instance, + ids.uid_normal, ids.gid_normal); + zlog_tls_buffer_init(); + + while ((log_arg = log_args_pop(di->early_logging))) { + command_setup_early_logging(log_arg->target, + di->early_loglevel); + /* this is a bit of a hack, + but need to notice when + the target is stdout */ + if (strcmp(log_arg->target, "stdout") == 0) + logging_to_stdout = true; + XFREE(MTYPE_TMP, log_arg); + } + +#if 0 + if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len, + frr_zclientpath)) { + fprintf(stderr, "Invalid zserv socket path: %s\n", + frr_zclientpath); + exit(1); + } + + /* don't mkdir these as root... */ + if (!(di->flags & FRR_NO_PRIVSEP)) { + if (!di->pid_file || !di->vty_path) + frr_mkdir(frr_vtydir, false); + if (di->pid_file) + frr_mkdir(di->pid_file, true); + if (di->vty_path) + frr_mkdir(di->vty_path, true); + } +#endif + +#if 0 + frrmod_init(di->module); + while (modules) { + modules = (oc = modules)->next; + module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr)); + if (!module) { + fprintf(stderr, "%s\n", moderr); + exit(1); + } + XFREE(MTYPE_TMP, oc); + } + +#endif + + zprivs_init(di->privs); + master = event_master_create(NULL); + +/* We don't want signal handlers for fuzzing, libFuzzer uses signals for + * process control */ +#if 0 + signal_init(master, di->n_signals, di->signals); +#endif + +#if 0 +#ifdef HAVE_SQLITE3 + if (!di->db_file) + di->db_file = dbfile_default; + db_init(di->db_file); +#endif + +#endif + if (di->flags & FRR_LIMITED_CLI) + cmd_init(-1); + else + cmd_init(1); + + vty_init(master, di->log_always); + +#if 0 + log_filter_cmd_init(); +#endif + +#if 0 + frr_pthread_init(); +#endif + + log_ref_init(); +#if 0 + log_ref_vty_init(); +#endif + lib_error_init(); + +#if 0 + yang_init(); + + debug_init_cli(); + + nb_init(master, di->yang_modules, di->n_yang_modules); + if (nb_db_init() != NB_OK) + flog_warn(EC_LIB_NB_DATABASE, + "%s: failed to initialize northbound database", + __func__); +#endif + + return master; +} +#endif + const char *frr_get_progname(void) { return di ? di->progname : NULL; diff --git a/lib/libfrr.h b/lib/libfrr.h index b260a54dfe..a881d7bd05 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -138,6 +138,9 @@ extern int frr_getopt(int argc, char *const argv[], int *longindex); extern __attribute__((__noreturn__)) void frr_help_exit(int status); +/* FOR FUZZING */ +extern struct event_loop *frr_init_fast(void); + extern struct event_loop *frr_init(void); extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); diff --git a/lib/memory.c b/lib/memory.c index 8fbe5c4093..4fb7746b6d 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -27,6 +27,10 @@ DEFINE_MGROUP(LIB, "libfrr"); DEFINE_MTYPE(LIB, TMP, "Temporary memory"); DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory"); +#ifdef FUZZING +#undef HAVE_MALLOC_USABLE_SIZE +#endif + static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr) { size_t current; diff --git a/lib/memory.h b/lib/memory.h index ba437ebd0e..87d04eede3 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -16,6 +16,12 @@ extern "C" { #endif +#define FUZZING 1 +#ifdef FUZZING +#undef HAVE_MALLOC_USABLE_SIZE +#undef HAVE_MALLOC_SIZE +#endif + #if defined(HAVE_MALLOC_SIZE) && !defined(HAVE_MALLOC_USABLE_SIZE) #define malloc_usable_size(x) malloc_size(x) #define HAVE_MALLOC_USABLE_SIZE diff --git a/lib/prefix.c b/lib/prefix.c index b8cad910f4..4ecf0e9344 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -677,7 +677,8 @@ void masklen2ip(const int masklen, struct in_addr *netmask) * has defined behaviour for << 32 (or has a 64-bit left shift) */ if (sizeof(unsigned long long) > 4) - netmask->s_addr = htonl(0xffffffffULL << (32 - masklen)); + netmask->s_addr = + htonl((uint32_t)(0xffffffffULL << (32 - masklen))); else netmask->s_addr = htonl(masklen ? 0xffffffffU << (32 - masklen) : 0); diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index cc886834fa..40bad6b00f 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -146,8 +146,8 @@ __wcsconv(wchar_t *wcsarg, int prec) /* * Non-MT-safe version */ -ssize_t -vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap) +ssize_t vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap) + __attribute__((no_sanitize("unsigned-integer-overflow"))) { const char *fmt; /* format string */ int ch; /* character from fmt */ @@ -453,7 +453,7 @@ reswitch: switch (ch) { if (flags & INTMAX_SIZE) ujval = SJARG(); else - ulval = SARG(); + ulval = (u_long)SARG(); if (printfrr_ext_char(fmt[0])) { struct printfrr_eargs ea = { @@ -483,7 +483,7 @@ reswitch: switch (ch) { } } else { if ((long)ulval < 0) { - ulval = -ulval; + ulval = (~ulval) + 1; sign = '-'; } } diff --git a/lib/privs.c b/lib/privs.c index accd9895ff..ccba48e5bd 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -288,6 +288,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) /* we have caps, we have no need to ever change back the original user */ /* only change uid if we don't have the correct one */ +#ifndef FUZZING if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { fprintf(stderr, @@ -296,7 +297,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) exit(1); } } - +#endif if (!(zprivs_state.caps = cap_init())) { fprintf(stderr, "privs_init: failed to cap_init, %s\n", safe_strerror(errno)); @@ -326,6 +327,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as * and when, and only when, they are needed. */ +#ifndef FUZZING if (cap_set_proc(zprivs_state.caps)) { cap_t current_caps; char *current_caps_text = NULL; @@ -352,7 +354,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) exit(1); } - +#endif /* set methods for the caller to use */ zprivs->change = zprivs_change_caps; zprivs->current_state = zprivs_state_caps; @@ -478,6 +480,9 @@ static struct zebra_privs_refs_t *get_privs_refs(struct zebra_privs_t *privs) struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, const char *funcname) { +#ifdef FUZZING + return NULL; +#endif int save_errno = errno; struct zebra_privs_refs_t *refs; @@ -508,6 +513,9 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, void _zprivs_lower(struct zebra_privs_t **privs) { +#ifdef FUZZING + return; +#endif int save_errno = errno; struct zebra_privs_refs_t *refs; @@ -550,6 +558,12 @@ void zprivs_preinit(struct zebra_privs_t *zprivs) zprivs->process_refs.raised_in_funcname = NULL; STAILQ_INIT(&zprivs->thread_refs); +#ifdef FUZZING + zprivs->user = NULL; + zprivs->group = NULL; + zprivs->vty_group = NULL; +#endif + if (zprivs->vty_group) { /* in a "NULL" setup, this is allowed to fail too, but still * try. */ @@ -652,6 +666,7 @@ void zprivs_init(struct zebra_privs_t *zprivs) zprivs_state.zsuid = geteuid(); /* initial uid */ /* add groups only if we changed uid - otherwise skip */ +#ifndef FUZZING if ((ngroups) && (zprivs_state.zsuid != zprivs_state.zuid)) { if (setgroups(ngroups, groups)) { fprintf(stderr, "privs_init: could not setgroups, %s\n", @@ -669,6 +684,7 @@ void zprivs_init(struct zebra_privs_t *zprivs) exit(1); } } +#endif #ifdef HAVE_CAPABILITIES zprivs_caps_init(zprivs); @@ -702,9 +718,10 @@ void zprivs_init(struct zebra_privs_t *zprivs) exit(1); } } - +#ifndef FUZZING zprivs->change = zprivs_change_uid; zprivs->current_state = zprivs_state_uid; +#endif #endif /* HAVE_CAPABILITIES */ } diff --git a/lib/zclient.c b/lib/zclient.c index 8526cbfaa1..34595b5e38 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -4030,6 +4030,73 @@ static zclient_handler *const lib_handlers[] = { [ZEBRA_INTERFACE_BFD_DEST_UPDATE] = zclient_bfd_session_update, }; +#ifdef FUZZING +int zclient_read_fuzz(struct zclient *zclient, const uint8_t *data, size_t len) +{ + uint16_t length, command; + uint8_t marker, version; + vrf_id_t vrf_id; + + /* Length check. */ + if (len > STREAM_SIZE(zclient->ibuf)) { + struct stream *ns; + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: message size %zu exceeds buffer size %lu, expanding...", + __func__, len, + (unsigned long)STREAM_SIZE(zclient->ibuf)); + ns = stream_new(len); + stream_free(zclient->ibuf); + zclient->ibuf = ns; + } + + if (len < ZEBRA_HEADER_SIZE) { + flog_err(EC_LIB_ZAPI_MISSMATCH, + "%s: socket %d message length %zu is less than %d ", + __func__, zclient->sock, len, ZEBRA_HEADER_SIZE); + return -1; + } + + stream_reset(zclient->ibuf); + stream_put(zclient->ibuf, data, len); + + length = stream_getw(zclient->ibuf); + marker = stream_getc(zclient->ibuf); + version = stream_getc(zclient->ibuf); + vrf_id = stream_getl(zclient->ibuf); + command = stream_getw(zclient->ibuf); + + if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) { + flog_err( + EC_LIB_ZAPI_MISSMATCH, + "%s: socket %d version mismatch, marker %d, version %d", + __func__, zclient->sock, marker, version); + return -1; + } + + length -= ZEBRA_HEADER_SIZE; + + if (zclient_debug) + zlog_debug("zclient %p command %s VRF %u", zclient, + zserv_command_string(command), vrf_id); + + if (command < array_size(lib_handlers) && lib_handlers[command]) + lib_handlers[command](command, zclient, length, vrf_id); + if (command < zclient->n_handlers && zclient->handlers[command]) + zclient->handlers[command](command, zclient, length, vrf_id); + + if (zclient->sock < 0) + /* Connection was closed during packet processing. */ + return -1; + + /* Register read thread. */ + stream_reset(zclient->ibuf); + zclient_event(ZCLIENT_READ, zclient); + + return 0; +} +#endif + /* Zebra client message read function. */ static void zclient_read(struct event *thread) { diff --git a/lib/zclient.h b/lib/zclient.h index e43393fd70..fdd61831dd 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -7,6 +7,7 @@ #define _ZEBRA_ZCLIENT_H struct zclient; +#define FUZZING 1 /* For struct zapi_route. */ #include "prefix.h" @@ -1251,6 +1252,10 @@ struct zapi_client_close_info { uint32_t session_id; }; +#ifdef FUZZING +int zclient_read_fuzz(struct zclient *zclient, const uint8_t *data, size_t len); +#endif + /* Decode incoming client close notify */ extern int zapi_client_close_notify_decode(struct stream *s, struct zapi_client_close_info *info); diff --git a/lib/zebra.h b/lib/zebra.h index ecc87f58f1..1b2b3c8a92 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -6,6 +6,13 @@ #ifndef _ZEBRA_H #define _ZEBRA_H +#define FUZZING 1 + +#define ZAPI_FUZZING 1 +#define NETLINK_FUZZING 2 + +#define FUZZING_MODE ZAPI_FUZZING + #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -60,6 +67,10 @@ #include #endif +#ifdef FUZZING +#include "fuzz.h" +#endif + /* misc include group */ #include diff --git a/lib/zlog.h b/lib/zlog.h index a207b29a3b..3a1c4329e0 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -109,6 +109,8 @@ static inline void zlog_ref(const struct xref_logmsg *xref, zlog_ref(&_xref, (msg), ##__VA_ARGS__); \ } while (0) +#ifndef FUZZING + #define zlog_err(...) _zlog_ecref(0, LOG_ERR, __VA_ARGS__) #define zlog_warn(...) _zlog_ecref(0, LOG_WARNING, __VA_ARGS__) #define zlog_info(...) _zlog_ecref(0, LOG_INFO, __VA_ARGS__) @@ -123,6 +125,20 @@ static inline void zlog_ref(const struct xref_logmsg *xref, #define flog_err_sys(ferr_id, format, ...) \ _zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__) +#else + +#define zlog_err(...) 0 +#define zlog_warn(...) 0 +#define zlog_info(...) 0 +#define zlog_notice(...) 0 +#define zlog_debug(...) 0 + +#define flog_err(ferr_id, format, ...) 0 +#define flog_warn(ferr_id, format, ...) 0 +#define flog_err_sys(ferr_id, format, ...) 0 + +#endif + extern void zlog_sigsafe(const char *text, size_t len); /* extra priority value to disable a target without deleting it */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 67f1faf8a9..8fb271d16c 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -3003,7 +3003,8 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, can be originated. " */ - if (ntohl(lsa->data->ls_seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) { + if (ntohl(lsa->data->ls_seqnum) != 0 + && ntohl(lsa->data->ls_seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) { if (ospf_lsa_is_self_originated(ospf, lsa)) { lsa->data->ls_seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER); @@ -3969,6 +3970,9 @@ void ospf_schedule_lsa_flood_area(struct ospf_area *area, struct ospf_lsa *lsa) data->lsa = ospf_lsa_lock(lsa); /* Message / Flood area */ event_add_event(master, ospf_lsa_action, data, 0, NULL); +#ifdef FUZZING + XFREE(MTYPE_OSPF_MESSAGE, data); +#endif } void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa) @@ -3981,6 +3985,9 @@ void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa) data->lsa = ospf_lsa_lock(lsa); /* Message / Flush area */ event_add_event(master, ospf_lsa_action, data, 0, NULL); +#ifdef FUZZING + XFREE(MTYPE_OSPF_MESSAGE, data); +#endif } diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 1f476a7e3d..9d57007ca6 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -28,6 +28,11 @@ #include "libfrr.h" #include "routemap.h" +#ifdef FUZZING +#include "sockopt.h" +#include +#endif + #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_asbr.h" @@ -134,9 +139,136 @@ FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, .n_yang_modules = array_size(ospfd_yang_modules), ); +#ifdef FUZZING + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static bool FuzzingInit(void) +{ + unsigned short instance = 0; + bool created = false; + + const char *name[] = { "ospfd" }; + + frr_preinit(&ospfd_di, 1, (char **) &name); + + + /* INIT */ + ospf_master_init(frr_init_fast()); + ospf_debug_init(); + ospf_vrf_init(); + access_list_init(); + prefix_list_init(); + ospf_if_init(); + ospf_zebra_init(master, instance); + ospf_bfd_init(master); + ospf_route_map_init(); + ospf_opaque_init(); + ospf_error_init(); + + return true; +} + +static struct ospf *FuzzingCreateOspf(void) +{ + struct prefix p; + struct interface *ifp = if_get_by_name("fuzziface", 0, "default"); + ifp->mtu = 68; + str2prefix("11.0.2.0/24", &p); + + bool created; + struct ospf *o = ospf_get(0, "omgwtfbbq", &created); + o->fd = 69; + + struct in_addr in; + inet_pton(AF_INET, "0.0.0.0", &in); + struct ospf_area *a = ospf_area_new(o, in); + + struct connected *c = connected_add_by_prefix(ifp, &p, NULL); + add_ospf_interface(c, a); + + struct ospf_interface *oi = listhead(a->oiflist)->data; + oi->state = 7; // ISM_DR + + o->fuzzing_packet_ifp = ifp; + + return o; +} + +static struct ospf *FuzzingOspf; +static bool FuzzingInitialized; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (!FuzzingInitialized) { + FuzzingInit(); + FuzzingInitialized = true; + FuzzingOspf = FuzzingCreateOspf(); + } + + struct ospf *o; + +#ifdef FUZZING_LIBFUZZER + o = FuzzingOspf; +#else + o = FuzzingOspf; +#endif + + /* Simulate the read process done by ospf_recv_packet */ + stream_free(o->ibuf); + o->ibuf = stream_new(MAX(1, size)); + + stream_put(o->ibuf, data, size); + { + struct ip *iph; + unsigned short ip_len = 0; + + if (size < sizeof(struct ip)) + goto done; + + iph = (struct ip *)STREAM_DATA(o->ibuf); + sockopt_iphdrincl_swab_systoh(iph); + ip_len = iph->ip_len; + + // skipping platform #ifdefs as I test on linux right now + // skipping ifindex lookup as it will fail anyway + + if (size != ip_len) + goto done; + } + + ospf_read_helper(o); + +done: + return 0; +} +#endif + +#ifndef FUZZING_LIBFUZZER /* OSPFd main routine. */ int main(int argc, char **argv) { +#ifdef FUZZING + + FuzzingInitialized = FuzzingInit(); + FuzzingOspf = FuzzingCreateOspf(); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + uint8_t *input = NULL; + int r = frrfuzz_read_input(&input); + + if (r < 0 || !input) + goto done; + + LLVMFuzzerTestOneInput(input, r); + + free(input); +done: + return 0; +#endif + #ifdef SUPPORT_OSPF_API /* OSPF apiserver is disabled by default. */ ospf_apiserver_enable = 0; @@ -222,3 +354,4 @@ int main(int argc, char **argv) /* Not reached. */ return 0; } +#endif \ No newline at end of file diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index c238f051df..61faedc5ad 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -439,19 +439,20 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, if (IPV4_ADDR_SAME(&nbr_nbma->addr, &nbr->src)) { nbr_nbma->nbr = nbr; nbr->nbr_nbma = nbr_nbma; - +#ifndef FUZZING if (nbr_nbma->t_poll) EVENT_OFF(nbr_nbma->t_poll); - +#endif nbr->state_change = nbr_nbma->state_change + 1; } } } /* New nbr, save the crypto sequence number if necessary */ +#ifndef FUZZING if (ntohs(ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC) nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; - +#endif /* Configure BFD if interface has it. */ ospf_neighbor_bfd_apply(nbr); diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index aff8ed05c7..157f45b069 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -238,6 +238,9 @@ void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, int ospf_sock_init(struct ospf *ospf) { +#ifdef FUZZING + return 0; +#endif int ret; /* silently ignore. already done */ diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index 9973b4870d..d2d49a6daf 100644 --- a/ospfd/ospf_nsm.h +++ b/ospfd/ospf_nsm.h @@ -47,6 +47,11 @@ #define OSPF_NSM_EVENT_SCHEDULE(N, E) \ event_add_event(master, ospf_nsm_event, (N), (E), NULL) +#ifdef FUZZING +#undef OSPF_NSM_EVENT_SCHEDULE +#define OSPF_NSM_EVENT_SCHEDULE(N, E) +#endif + /* Macro for OSPF NSM execute event. */ #define OSPF_NSM_EVENT_EXECUTE(N, E) \ event_execute(master, ospf_nsm_event, (N), (E)) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index d010b8b6e6..6097bffc8f 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1644,6 +1644,7 @@ static void ospf_ls_req(struct ip *iph, struct ospf_header *ospfh, /* Search proper LSA in LSDB. */ find = ospf_lsa_lookup(oi->ospf, oi->area, ls_type, ls_id, adv_router); +#ifndef FUZZING if (find == NULL) { OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_BadLSReq); list_delete(&ls_upd); @@ -1668,9 +1669,13 @@ static void ospf_ls_req(struct ip *iph, struct ospf_header *ospfh, /* Append LSA to update list. */ listnode_add(ls_upd, find); length += ntohs(find->data->length); - +#endif size -= OSPF_LSA_KEY_SIZE; } +#ifdef FUZZING + list_delete(&ls_upd); + return; +#endif /* Send rest of Link State Update. */ if (listcount(ls_upd) > 0) { @@ -2316,8 +2321,11 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, msgh.msg_control = (caddr_t)buff; msgh.msg_controllen = sizeof(buff); +#ifndef FUZZING ret = stream_recvmsg(ibuf, fd, &msgh, MSG_DONTWAIT, OSPF_MAX_PACKET_SIZE + 1); +#endif + if (ret < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) flog_warn(EC_OSPF_PACKET, "stream_recvmsg failed: %s", @@ -2493,7 +2501,9 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) lookup_msg(ospf_auth_type_str, iface_auth_type, NULL), &ospfh->router_id); +#ifndef FUZZING return 0; +#endif } if (!ospf_check_sum(ospfh)) { if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) @@ -2502,7 +2512,9 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) "interface %s: Null auth OK, but checksum error, Router-ID %pI4", IF_NAME(oi), &ospfh->router_id); +#ifndef FUZZING return 0; +#endif } return 1; case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */ @@ -2956,12 +2968,7 @@ static int ospf_verify_header(struct stream *ibuf, struct ospf_interface *oi, return 0; } -enum ospf_read_return_enum { - OSPF_READ_ERROR, - OSPF_READ_CONTINUE, -}; - -static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) +enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) { int ret; struct stream *ibuf; @@ -2972,8 +2979,13 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) struct connected *c; struct interface *ifp = NULL; +#ifndef FUZZING stream_reset(ospf->ibuf); ibuf = ospf_recv_packet(ospf, ospf->fd, &ifp, ospf->ibuf); +#else + ibuf = ospf->ibuf; + ifp = ospf->fuzzing_packet_ifp; +#endif if (ibuf == NULL) return OSPF_READ_ERROR; @@ -3182,6 +3194,20 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) /* Adjust size to message length. */ length = ntohs(ospfh->length) - OSPF_HEADER_SIZE; +#ifdef FUZZING + /* + * Everything except hellos returns early with no neighbor found, so we + * need to make a neighbor + */ + struct prefix p; + p.family = AF_INET; + p.prefixlen = 24; + p.u.prefix4 = iph->ip_src; + + struct ospf_neighbor *n = ospf_nbr_get(oi, ospfh, iph, &p); + n->state = NSM_Exchange; +#endif + /* Read rest of the packet and call each sort of packet routine. */ switch (ospfh->type) { diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h index 234738979e..6044c93d86 100644 --- a/ospfd/ospf_packet.h +++ b/ospfd/ospf_packet.h @@ -150,4 +150,13 @@ extern const size_t ospf_packet_type_str_max; extern void ospf_proactively_arp(struct ospf_neighbor *); +#ifdef FUZZING +enum ospf_read_return_enum { + OSPF_READ_ERROR, + OSPF_READ_CONTINUE, +}; + +enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf); +#endif + #endif /* _ZEBRA_OSPF_PACKET_H */ diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 36936b16f4..c16f649faa 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -159,6 +159,10 @@ struct ospf { * config. */ uint8_t oi_running; +#ifdef FUZZING + struct interface *fuzzing_packet_ifp; +#endif + /* OSPF instance ID */ unsigned short instance; diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 44ee3b0f13..976e4b2529 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -108,6 +108,7 @@ noinst_HEADERS += \ ospfd/ospf_zebra.h \ # end +ospfd_ospfd_LDFLAGS = $(AM_LDFLAGS) $(OSPFD_SAN_FLAGS) ospfd_ospfd_LDADD = ospfd/libfrrospf.a ospfd/libfrrospfclient.a lib/libfrr.la $(LIBCAP) $(LIBM) ospfd_ospfd_SOURCES = ospfd/ospf_main.c diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index cc1ca77d65..2f5e2efe37 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1549,8 +1549,10 @@ int pim_if_ifchannel_count(struct pim_interface *pim_ifp) return count; } - -static int pim_ifp_create(struct interface *ifp) +#ifndef FUZZING +static +#endif +int pim_ifp_create(struct interface *ifp) { struct pim_instance *pim; @@ -1620,7 +1622,10 @@ static int pim_ifp_create(struct interface *ifp) return 0; } -static int pim_ifp_up(struct interface *ifp) +#ifndef FUZZING +static +#endif +int pim_ifp_up(struct interface *ifp) { uint32_t table_id; struct pim_interface *pim_ifp; @@ -1676,7 +1681,10 @@ static int pim_ifp_up(struct interface *ifp) return 0; } -static int pim_ifp_down(struct interface *ifp) +#ifndef FUZZING +static +#endif +int pim_ifp_down(struct interface *ifp) { if (PIM_DEBUG_ZEBRA) { zlog_debug( @@ -1711,8 +1719,10 @@ static int pim_ifp_down(struct interface *ifp) return 0; } - -static int pim_ifp_destroy(struct interface *ifp) +#ifndef FUZZING +static +#endif +int pim_ifp_destroy(struct interface *ifp) { if (PIM_DEBUG_ZEBRA) { zlog_debug( diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 973840a753..58888eb17a 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -244,4 +244,11 @@ int pim_if_ifchannel_count(struct pim_interface *pim_ifp); void pim_iface_init(void); +#ifdef FUZZING +int pim_ifp_create(struct interface *ifp); +int pim_ifp_up(struct interface *ifp); +int pim_ifp_down(struct interface *ifp); +int pim_ifp_destroy(struct interface *ifp); +#endif + #endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index 4b0ff955f9..10520410bf 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -33,30 +33,37 @@ enum pim_ifjoin_state { */ #define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0) #define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT) -#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT) -#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) \ + ((flags) |= (typeof((flags)))PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) \ + ((flags) &= (typeof((flags))) ~PIM_IF_FLAG_MASK_COULD_ASSERT) /* Flag to detect change in AssertTrackingDesired(S,G,I) */ #define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1) #define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) -#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) -#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) \ + ((flags) |= (typeof((flags)))PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) \ + ((flags) &= (typeof((flags))) ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) /* * Flag to tell us if the ifchannel is (S,G,rpt) */ #define PIM_IF_FLAG_MASK_S_G_RPT (1 << 2) #define PIM_IF_FLAG_TEST_S_G_RPT(flags) ((flags) & PIM_IF_FLAG_MASK_S_G_RPT) -#define PIM_IF_FLAG_SET_S_G_RPT(flags) ((flags) |= PIM_IF_FLAG_MASK_S_G_RPT) -#define PIM_IF_FLAG_UNSET_S_G_RPT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_S_G_RPT) +#define PIM_IF_FLAG_SET_S_G_RPT(flags) \ + ((flags) |= (typeof((flags)))PIM_IF_FLAG_MASK_S_G_RPT) +#define PIM_IF_FLAG_UNSET_S_G_RPT(flags) \ + ((flags) &= (typeof((flags))) ~PIM_IF_FLAG_MASK_S_G_RPT) /* * Flag to tell us if the ifchannel is proto PIM */ #define PIM_IF_FLAG_MASK_PROTO_PIM (1 << 3) #define PIM_IF_FLAG_TEST_PROTO_PIM(flags) ((flags)&PIM_IF_FLAG_MASK_PROTO_PIM) -#define PIM_IF_FLAG_SET_PROTO_PIM(flags) ((flags) |= PIM_IF_FLAG_MASK_PROTO_PIM) +#define PIM_IF_FLAG_SET_PROTO_PIM(flags) \ + ((flags) |= (typeof((flags)))PIM_IF_FLAG_MASK_PROTO_PIM) #define PIM_IF_FLAG_UNSET_PROTO_PIM(flags) \ ((flags) &= ~PIM_IF_FLAG_MASK_PROTO_PIM) /* @@ -65,9 +72,9 @@ enum pim_ifjoin_state { #define PIM_IF_FLAG_MASK_PROTO_IGMP (1 << 4) #define PIM_IF_FLAG_TEST_PROTO_IGMP(flags) ((flags)&PIM_IF_FLAG_MASK_PROTO_IGMP) #define PIM_IF_FLAG_SET_PROTO_IGMP(flags) \ - ((flags) |= PIM_IF_FLAG_MASK_PROTO_IGMP) + ((flags) |= (typeof((flags)))PIM_IF_FLAG_MASK_PROTO_IGMP) #define PIM_IF_FLAG_UNSET_PROTO_IGMP(flags) \ - ((flags) &= ~PIM_IF_FLAG_MASK_PROTO_IGMP) + ((flags) &= (typeof((flags))) ~PIM_IF_FLAG_MASK_PROTO_IGMP) /* Per-interface (S,G) state */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 063ba6edd2..6d66a7944a 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -1255,12 +1255,16 @@ struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list, struct sockaddr_in sin; int fd; +#ifndef FUZZING fd = igmp_sock_open(ifaddr, ifp); if (fd < 0) { zlog_warn("Could not open IGMP socket for %pI4 on %s", &ifaddr, ifp->name); return NULL; } +#else + fd = 69; +#endif sin.sin_family = AF_INET; sin.sin_addr = ifaddr; diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 15078dd1ec..92e2767502 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -314,8 +314,9 @@ static void group_exclude_fwd_anysrc_ifempty(struct gm_group *group) void igmp_source_free(struct gm_source *source) { /* make sure there is no source timer running */ +#ifndef FUZZING assert(!source->t_source_timer); - +#endif XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); } @@ -580,7 +581,9 @@ static void isex_excl(struct gm_group *group, int num_sources, * (A-X-Y) */ assert(!source->t_source_timer); /* timer == 0 */ igmp_source_reset_gmi(group, source); +#ifndef FUZZING assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ +#endif } } /* scan received sources */ @@ -633,7 +636,9 @@ static void isex_incl(struct gm_group *group, int num_sources, } else { /* I.4: if not found, create source with timer=0 (B-A) */ +#ifndef FUZZING assert(!source->t_source_timer); /* (B-A) timer=0 */ +#endif } } /* scan received sources */ @@ -831,6 +836,9 @@ static void toex_incl(struct gm_group *group, int num_sources, /* and set SEND flag (A*B) */ IGMP_SOURCE_DO_SEND(source->source_flags); ++num_sources_tosend; +#ifndef FUZZING + assert(!source->t_source_timer); /* (B-A) timer=0 */ +#endif } } /* Scan received sources (B) */ @@ -896,8 +904,9 @@ static void toex_excl(struct gm_group *group, int num_sources, assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); igmp_source_timer_on(group, source, group_timer_msec); +#ifndef FUZZING assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ - +#endif /* make sure source is created with DELETE flag unset */ assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); } @@ -1399,7 +1408,9 @@ static void block_excl(struct gm_group *group, int num_sources, assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); igmp_source_timer_on(group, source, group_timer_msec); +#ifndef FUZZING assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ +#endif } if (source->t_source_timer) { @@ -1892,9 +1903,11 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from, if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) { zlog_warn( - "Recv IGMPv3 report from %s on %s with invalid checksum", - from_str, ifp->name); + "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); +#ifndef FUZZING return -1; +#endif } /* Collecting IGMP Rx stats */ diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 6f33af0601..12cb950637 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -116,9 +116,11 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim->last_route_change_time = -1; +#ifndef FUZZING pim->reg_sock = pim_reg_sock(); if (pim->reg_sock < 0) assert(0); +#endif /* MSDP global timer defaults. */ pim->msdp.hold_time = PIM_MSDP_PEER_HOLD_TIME; @@ -197,7 +199,7 @@ static int pim_vrf_disable(struct vrf *vrf) return 0; } -static int pim_vrf_config_write(struct vty *vty) +static int __attribute__((unused)) pim_vrf_config_write(struct vty *vty) { struct vrf *vrf; struct pim_instance *pim; @@ -223,8 +225,9 @@ static int pim_vrf_config_write(struct vty *vty) void pim_vrf_init(void) { vrf_init(pim_vrf_new, pim_vrf_enable, pim_vrf_disable, pim_vrf_delete); - +#ifndef FUZZING vrf_cmd_init(pim_vrf_config_write); +#endif } void pim_vrf_terminate(void) diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 11577ae46d..e114ecd26a 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -96,7 +96,7 @@ struct pim_router { struct in_addr anycast_vtep_ip; struct in_addr local_vtep_ip; struct pim_mlag_stats mlag_stats; - enum pim_mlag_flags mlag_flags; + uint8_t mlag_flags; char peerlink_rif[INTERFACE_NAMSIZ]; struct interface *peerlink_rif_p; }; diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 7db0a7676b..228e96e937 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -37,6 +37,15 @@ #include "pim_errors.h" #include "pim_nb.h" +#define FUZZING 1 +#ifdef FUZZING +#include "fuzz.h" +#include "pim_pim.h" +#include "pim_mroute.h" +#include "pim_tlv.h" +#include "pim_neighbor.h" +#endif + extern struct host host; struct option longopts[] = {{0}}; @@ -81,10 +90,146 @@ FRR_DAEMON_INFO(pimd, PIM, .vty_port = PIMD_VTY_PORT, .n_yang_modules = array_size(pimd_yang_modules), ); +#ifdef FUZZING + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static struct interface *FuzzingIfp; +static struct in_addr FuzzingSrc = { .s_addr = 0x0900001b }; + +static bool FuzzingInit(void) +{ + vrf_configure_backend(VRF_BACKEND_VRF_LITE); + + const char *name[] = {"pimd"}; + + frr_preinit(&pimd_di, 1, (char **)name); + pim_router_init(); + + /* + * Initializations + */ + pim_error_init(); + pim_vrf_init(); + access_list_init(); + prefix_list_init(); + prefix_list_add_hook(pim_prefix_list_update); + prefix_list_delete_hook(pim_prefix_list_update); + + pim_route_map_init(); + pim_init(); + + /* + * Initialize zclient "update" and "lookup" sockets + */ + if_zapi_callbacks(pim_ifp_create, pim_ifp_up, + pim_ifp_down, pim_ifp_destroy); + pim_zebra_init(); + pim_bfd_init(); + pim_mlag_init(); + + /* Create some fake interface */ + + /* Source address stuff */ + struct prefix p; + str2prefix("27.0.0.9/24", &p); + + /* Create system interface */ + FuzzingIfp = if_get_by_name("fuzziface", VRF_DEFAULT, "default"); + if_set_index(FuzzingIfp, 69); + connected_add_by_prefix(FuzzingIfp, &p, NULL); + + return true; +} + +static struct pim_instance *FuzzingCreatePimInstance(void) +{ + /* Create pim stuff */ + fprintf(stderr, ">>>>>>>>> %p\n", FuzzingIfp); + struct pim_interface *pim_ifp = pim_if_new(FuzzingIfp, true, true, false, false); + pim_igmp_sock_add(pim_ifp->gm_socket_list, FuzzingSrc, FuzzingIfp, false); + + struct pim_instance *pim = vrf_lookup_by_id(VRF_DEFAULT)->info; + pim_if_create_pimreg(pim); + pim_hello_options ho = 0; + pim_neighbor_add(FuzzingIfp, FuzzingSrc, ho, 210, 1, 1, 30, 20, NULL, 0); + + return pim; +} + +static bool FuzzingInitialized; + +static struct pim_instance *FuzzingPim; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (!FuzzingInitialized) { + FuzzingInit(); + FuzzingInitialized = true; + FuzzingPim = FuzzingCreatePimInstance(); + } + + struct pim_instance *pim; +#ifdef FUZZING_LIBFUZZER + pim = FuzzingPim; +#else + pim = FuzzingPim; +#endif + + int result; + + uint8_t *input = malloc(size); + memcpy(input, data, size); + +#ifdef KERNEL_IFACE + result = pim_mroute_msg(pim, (const char *) input, size, 69); +#else + int retval; + struct in_addr src; + struct in_addr grp; + + memset(&src, '\0', sizeof(src)); + memset(&grp, '\0', sizeof(grp)); + + retval = inet_aton("10.1.1.1", &src); + retval = inet_aton("10.1.1.2", &grp); + + pim_sgaddr sg; + sg.src = src; + sg.grp = grp; + result = pim_pim_packet(FuzzingIfp, input, size, sg); + +#endif /* KERNEL_IFACE */ + + free(input); + + return result; +} +#endif /* FUZZING */ + +#ifndef FUZZING_LIBFUZZER int main(int argc, char **argv, char **envp) { frr_preinit(&pimd_di, argc, argv); + +#ifdef FUZZING + FuzzingInit(); + FuzzingInitialized = true; + FuzzingPim = FuzzingCreatePimInstance(); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif /* __AFL_HAVE_MANUAL_CONTROL */ + + uint8_t *input = NULL; + int r = frrfuzz_read_input(&input); + + int ret = LLVMFuzzerTestOneInput(input, r); + + return ret; +#endif /* FUZZING */ + frr_opt_add("", longopts, ""); /* this while just reads the options */ @@ -101,6 +246,7 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); + break; } } @@ -164,3 +310,4 @@ int main(int argc, char **argv, char **envp) /* never reached */ return 0; } +#endif /* FUZZING_LIBFUZZER */ \ No newline at end of file diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c index 5d72eb6581..a557f1137a 100644 --- a/pimd/pim_mlag.c +++ b/pimd/pim_mlag.c @@ -887,10 +887,9 @@ int pim_zebra_mlag_process_up(ZAPI_CALLBACK_ARGS) static void pim_mlag_param_reset(void) { /* reset the cached params and stats */ - router->mlag_flags &= ~(PIM_MLAGF_STATUS_RXED | - PIM_MLAGF_LOCAL_CONN_UP | - PIM_MLAGF_PEER_CONN_UP | - PIM_MLAGF_PEER_ZEBRA_UP); + router->mlag_flags &= + (uint8_t) ~(PIM_MLAGF_STATUS_RXED | PIM_MLAGF_LOCAL_CONN_UP + | PIM_MLAGF_PEER_CONN_UP | PIM_MLAGF_PEER_ZEBRA_UP); router->local_vtep_ip.s_addr = INADDR_ANY; router->anycast_vtep_ip.s_addr = INADDR_ANY; router->mlag_role = MLAG_ROLE_NONE; diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index b64fcdeb87..73a7d8de54 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -824,6 +824,7 @@ int pim_mroute_socket_enable(struct pim_instance *pim) frr_with_privs(&pimd_privs) { +#ifndef FUZZING #if PIM_IPV == 4 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); #else @@ -835,7 +836,11 @@ int pim_mroute_socket_enable(struct pim_instance *pim) safe_strerror(errno)); return -2; } +#else + fd = 69; +#endif +#ifndef FUZZING #if PIM_IPV == 6 struct icmp6_filter filter[1]; int ret; @@ -856,6 +861,9 @@ int pim_mroute_socket_enable(struct pim_instance *pim) "(VRF %s) failed to set mroute control filter: %m", pim->vrf->name); #endif +#else + fd = 69; +#endif #ifdef SO_BINDTODEVICE if (pim->vrf->vrf_id != VRF_DEFAULT @@ -871,6 +879,7 @@ int pim_mroute_socket_enable(struct pim_instance *pim) } pim->mroute_socket = fd; +#ifndef FUZZING if (pim_mroute_set(pim, 1)) { zlog_warn( "Could not enable mroute on socket fd=%d: errno=%d: %s", @@ -883,6 +892,7 @@ int pim_mroute_socket_enable(struct pim_instance *pim) pim->mroute_socket_creation = pim_time_monotonic_sec(); mroute_read_on(pim); +#endif return 0; } @@ -955,8 +965,13 @@ int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr, #endif #endif +#ifndef FUZZING err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF, (void *)&vc, sizeof(vc)); +#else + err = 0; +#endif + if (err) { zlog_warn( "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s", diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index 8706f42206..124a15d985 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -100,6 +100,8 @@ typedef struct sioc_sg_req pim_sioc_sg_req; #define MRT6MSG_WRMIFWHOLE 4 /* For PIM processing */ #endif +#include "lib/if.h" + #ifndef GMMSG_NOCACHE #define GMMSG_NOCACHE MRT6MSG_NOCACHE /* For PIM processing */ #define GMMSG_WHOLEPKT MRT6MSG_WHOLEPKT /* For PIM processing */ @@ -134,7 +136,6 @@ typedef struct sioc_sg_req6 pim_sioc_sg_req; /* Above: from */ - struct channel_oil; struct pim_instance; diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 1cd7cce086..5e9689b6b9 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -589,14 +589,16 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { /* update num. of neighbors without hello option lan_delay */ - --pim_ifp->pim_number_of_nonlandelay_neighbors; + pim_ifp->pim_number_of_nonlandelay_neighbors = MAX( + pim_ifp->pim_number_of_nonlandelay_neighbors - 1, 0); } if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)) { /* update num. of neighbors without dr_pri */ - --pim_ifp->pim_dr_num_nondrpri_neighbors; + pim_ifp->pim_dr_num_nondrpri_neighbors = + MAX(pim_ifp->pim_dr_num_nondrpri_neighbors - 1, 0); } assert(neigh->propagation_delay_msec diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index a4c9178bb9..e3ce8be1a8 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -164,6 +164,19 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, } ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ +#ifdef FUZZING + /* + * Ensure that the header length is 20 or 24 bytes as is appropriate + * The kernel typically ensures that the passed up ip header meets + * standards, and would actually drop the packet if it didn't meet + * them. So since we are fuzzing and faking the header bits/bobs + * then let's ensure that we don't get into a weird situation + * where a bad ip_hlen here causes pim_msg_len to wrap around into + * the high billions and then we have an issue later + */ + if (ip_hlen != 20 && ip_hlen != 24) + return -1; +#endif sg = pim_sgaddr_from_iphdr(ip_hdr); pim_msg = buf + ip_hlen; @@ -241,8 +254,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", ifp->name, pim_checksum, checksum); - +#ifndef FUZZING return -1; +#endif } } } else { @@ -252,8 +266,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, zlog_debug( "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", ifp->name, pim_checksum, checksum); - +#ifndef FUZZING return -1; +#endif } } diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 9696a76fc5..6b058beeda 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -505,6 +505,11 @@ int pim_register_recv(struct interface *ifp, pim_addr dest_addr, } #define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 + + if (tlv_buf_size + < (int)(PIM_MSG_REGISTER_BIT_RESERVED_LEN + sizeof(struct ip))) { + return 0; + } ip_hdr = (tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); if (!if_address_is_local(&dest_addr, PIM_AF, pim->vrf->vrf_id)) { diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index c7516242f5..27b7044a04 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -804,6 +804,9 @@ int pim_rp_del(struct pim_instance *pim, pim_addr rp_addr, struct prefix group, pim_addr_to_prefix(&grp, up->sg.grp); trp_info = pim_rp_find_match_group(pim, &grp); + if (!trp_info) + continue; + /* RP not found for the group grp */ if (pim_rpf_addr_is_inaddr_any(&trp_info->rp)) { pim_upstream_rpf_clear(pim, up); diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index ea2af6457d..28c802f3c0 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -31,8 +31,10 @@ typedef uint32_t pim_hello_options; #define PIM_RPT_BIT_MASK (1 << 0) #define PIM_WILDCARD_BIT_MASK (1 << 1) -#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask)) -#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask)) +#define PIM_OPTION_SET(options, option_mask) \ + ((options) |= (typeof((options)))(option_mask)) +#define PIM_OPTION_UNSET(options, option_mask) \ + ((options) &= (typeof((options))) ~(option_mask)) #define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask)) #define PIM_TLV_GET_UINT16(buf) \ diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 4e0926e294..4debad1be9 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -109,50 +109,97 @@ #define PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(flags) ((flags) & (PIM_UPSTREAM_FLAG_MASK_SRC_IGMP | PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)) #define PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(flags) ((flags)&PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) -#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) -#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) -#define PIM_UPSTREAM_FLAG_SET_FHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FHR) -#define PIM_UPSTREAM_FLAG_SET_SRC_IGMP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) -#define PIM_UPSTREAM_FLAG_SET_SRC_PIM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_PIM) -#define PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) -#define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) -#define PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) -#define PIM_UPSTREAM_FLAG_SET_SRC_LHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_LHR) -#define PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) -#define PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) -#define PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) -#define PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) -#define PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) -#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) -#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) -#define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) -#define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) -#define PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) -#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT) -#define PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) - -#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) -#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) -#define PIM_UPSTREAM_FLAG_UNSET_FHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FHR) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_PIM) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) -#define PIM_UPSTREAM_FLAG_UNSET_SEND_SG_RPT_PRUNE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_LHR) -#define PIM_UPSTREAM_FLAG_UNSET_DISABLE_KAT_EXPIRY(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) -#define PIM_UPSTREAM_FLAG_UNSET_STATIC_IIF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) -#define PIM_UPSTREAM_FLAG_UNSET_ALLOW_IIF_IN_OIL(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) -#define PIM_UPSTREAM_FLAG_UNSET_NO_PIMREG_DATA(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) -#define PIM_UPSTREAM_FLAG_UNSET_FORCE_PIMREG(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_ORIG(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_TERM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) -#define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) -#define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) -#define PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) -#define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) -#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT) -#define PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) \ + ((flags) |= \ + (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) +#define PIM_UPSTREAM_FLAG_SET_FHR(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_FHR) +#define PIM_UPSTREAM_FLAG_SET_SRC_IGMP(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) +#define PIM_UPSTREAM_FLAG_SET_SRC_PIM(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_PIM) +#define PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) +#define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) +#define PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) +#define PIM_UPSTREAM_FLAG_SET_SRC_LHR(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_LHR) +#define PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) +#define PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) +#define PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) +#define PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) +#define PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) +#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) +#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) +#define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) +#define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) +#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_USE_RPT) +#define PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(flags) \ + ((flags) |= (typeof((flags)))PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) + +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) \ + ((flags) &= \ + (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) +#define PIM_UPSTREAM_FLAG_UNSET_FHR(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_FHR) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_PIM) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) +#define PIM_UPSTREAM_FLAG_UNSET_SEND_SG_RPT_PRUNE(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_LHR) +#define PIM_UPSTREAM_FLAG_UNSET_DISABLE_KAT_EXPIRY(flags) \ + ((flags) &= \ + (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) +#define PIM_UPSTREAM_FLAG_UNSET_STATIC_IIF(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) +#define PIM_UPSTREAM_FLAG_UNSET_ALLOW_IIF_IN_OIL(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) +#define PIM_UPSTREAM_FLAG_UNSET_NO_PIMREG_DATA(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) +#define PIM_UPSTREAM_FLAG_UNSET_FORCE_PIMREG(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_ORIG(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_TERM(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_USE_RPT) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(flags) \ + ((flags) &= (typeof((flags))) ~PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) + /* The RPF cost is incremented by 10 if the RPF interface is the peerlink-rif. * This is used to force the MLAG switch with the lowest cost to the RPF diff --git a/pimd/pimd.c b/pimd/pimd.c index db61974800..977c8a9c64 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -85,7 +85,7 @@ void pim_router_init(void) router = XCALLOC(MTYPE_ROUTER, sizeof(*router)); router->debugs = 0; - router->master = frr_init(); + router->master = frr_init_fast(); router->t_periodic = PIM_DEFAULT_T_PERIODIC; router->multipath = MULTIPATH_NUM; diff --git a/pimd/subdir.am b/pimd/subdir.am index 9a7901ec3f..c50c81d0e7 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -162,11 +162,13 @@ clippy_scan += \ pimd_pimd_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4 pimd_pimd_LDADD = lib/libfrr.la $(LIBCAP) +pimd_pimd_LDFLAGS = $(AM_LDFLAGS) $(PIMD_SAN_FLAGS) if PIM6D sbin_PROGRAMS += pimd/pim6d pimd_pim6d_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=6 pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP) +pimd_pimd6d_LDFLAGS = $(AM_LDFLAGS) $(PIMD_SAN_FLAGS) endif pimd_test_igmpv3_join_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4 diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 03b4042611..2303e4d956 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -35,6 +35,7 @@ clippy_scan += \ # end vrrpd_vrrpd_LDADD = lib/libfrr.la @LIBCAP@ +vrrpd_vrrpd_LDFLAGS = $(AM_CFLAGS) $(VRRPD_SAN_FLAGS) nodist_vrrpd_vrrpd_SOURCES = \ yang/frr-vrrpd.yang.c \ # end diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 7a779307d9..d2369c85d7 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -976,7 +976,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* * Read and process next IPvX datagram. */ -static void vrrp_read(struct event *thread) +#ifndef FUZZING +static +#endif +void vrrp_read(struct event *thread) { struct vrrp_router *r = EVENT_ARG(thread); @@ -1001,6 +1004,7 @@ static void vrrp_read(struct event *thread) m.msg_control = control; m.msg_controllen = sizeof(control); +#ifndef FUZZING nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT); if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) { @@ -1011,6 +1015,10 @@ static void vrrp_read(struct event *thread) resched = false; goto done; } +#else + nbytes = r->fuzzing_input_size; + m.msg_name = &r->fuzzing_sa; +#endif if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) { DEBUGD(&vrrp_dbg_pkt, @@ -1037,9 +1045,10 @@ static void vrrp_read(struct event *thread) done: memset(r->ibuf, 0x00, sizeof(r->ibuf)); - +#ifndef FUZZING if (resched) event_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); +#endif } /* diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 0ac9b1f49c..8d9190743c 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -100,6 +100,10 @@ struct vrrp_router { struct ipaddr src; /* Socket read buffer */ +#ifdef FUZZING + size_t fuzzing_input_size; + struct sockaddr_in fuzzing_sa; +#endif uint8_t ibuf[IP_MAXPACKET]; /* @@ -461,6 +465,10 @@ int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); */ int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); +#ifdef FUZZING +void vrrp_read(struct event *thread); +#endif + /* State machine ----------------------------------------------------------- */ #define VRRP_STATE_INITIALIZE 0 diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 5245c74689..06a33adc3a 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -118,8 +118,108 @@ FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, .n_yang_modules = array_size(vrrp_yang_modules), ); +#ifdef FUZZING + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size); + +static bool FuzzingInit(void) +{ + const char *name[] = { "vrrpd" }; + + frr_preinit(&vrrpd_di, 1, (char **) name); + + master = frr_init(); + + access_list_init(); + vrrp_debug_init(); + vrrp_zebra_init(); + vrrp_vty_init(); + vrrp_init(); + + + return true; +} + +static struct vrrp_vrouter *FuzzingCreateVr(void) +{ + struct interface *ifp; + struct prefix p; + + ifp = if_get_by_name("fuzziface", VRF_DEFAULT, "default"); + ifp->mtu = 68; + str2prefix("11.0.2.1/24", &p); + connected_add_by_prefix(ifp, &p, NULL); + + struct vrrp_vrouter *vr = vrrp_vrouter_create(ifp, 10, 3); + vr->v4->fsm.state = VRRP_STATE_MASTER; + vr->v6->fsm.state = VRRP_STATE_MASTER; + + vrrp_debug_set(NULL, 0, CONFIG_NODE, 1, 1, 1, 1, 1, 1, 1, 1); + + return vr; +} + +bool FuzzingInitialized; +struct vrrp_vrouter *FuzzingVr; + +int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) +{ + if (!FuzzingInitialized) { + FuzzingInit(); + FuzzingInitialized = true; + FuzzingVr = FuzzingCreateVr(); + } + + struct event t; + struct vrrp_vrouter *vr; + +#ifdef FUZZING_LIBFUZZER + vr = FuzzingVr; +#else + vr = FuzzingVr; +#endif + +#define FUZZING_ZAPI 0 + +#if !FUZZING_ZAPI + /* set input size */ + vr->v4->fuzzing_input_size = size; + /* some info to fake msghdr with */ + memcpy(vr->v4->ibuf, data, MIN(size, sizeof(vr->v4->ibuf))); + vr->v4->fuzzing_sa.sin_family = AF_INET; + inet_pton(AF_INET, "11.0.2.3", &vr->v4->fuzzing_sa.sin_addr); + + t.arg = vr->v4; + + vrrp_read(&t); +#else + zclient_read_fuzz(zclient, data, size); +#endif + + return 0; +} + +#endif + +#ifndef FUZZING_LIBFUZZER int main(int argc, char **argv, char **envp) { +#ifdef FUZZING + FuzzingInit(); + FuzzingInitialized = true; + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif /* AFL_HAVE_MANUAL_CONTROL */ + + uint8_t *input; + int r = frrfuzz_read_input(&input); + + if (r < 0) + return 0; + + return LLVMFuzzerTestOneInput(input, r); +#endif frr_preinit(&vrrpd_di, argc, argv); frr_opt_add("", longopts, ""); @@ -157,3 +257,4 @@ int main(int argc, char **argv, char **envp) /* Not reached. */ return 0; } +#endif \ No newline at end of file diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 36494c7df8..e1e203b007 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -281,10 +281,11 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, bool ipv4_ph, /* Checksum check */ uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src, ipv4_ph); +#ifndef FUZZING VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, "Bad VRRP checksum %hx; should be %hx", (*pkt)->hdr.chksum, chksum); - +#endif /* Type check */ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", (*pkt)->hdr.vertype & 0x0f); diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 6d753d2e47..2bf6ddefe0 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -19,7 +19,11 @@ #define VRRP_LOGPFX "[ZEBRA] " +#ifndef FUZZING static struct zclient *zclient; +#else +struct zclient *zclient; +#endif static void vrrp_zebra_debug_if_state(struct interface *ifp, const char *func) { @@ -157,6 +161,10 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) { +#ifdef FUZZING + if (!r) + return; +#endif DEBUGD(&vrrp_dbg_zebra, VRRP_LOGPFX VRRP_LOGPFX_VRID "Requesting Zebra to turn router advertisements %s for %s", diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h index 5e59256e1d..e4537d3f85 100644 --- a/vrrpd/vrrp_zebra.h +++ b/vrrpd/vrrp_zebra.h @@ -11,6 +11,11 @@ #include "lib/if.h" +#ifdef FUZZING +#include "lib/zclient.h" +extern struct zclient *zclient; +#endif + extern void vrrp_zebra_init(void); extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); extern void vrrp_zclient_send_interface_protodown(struct interface *ifp, diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 78b1dfe276..8864685a02 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -5,6 +5,12 @@ #include +#if defined(HANDLE_NETLINK_FUZZING) +#include +#include +#include "libfrr.h" +#endif /* HANDLE_NETLINK_FUZZING */ + #ifdef HAVE_NETLINK #include "linklist.h" @@ -482,6 +488,36 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; } +#if defined(HANDLE_NETLINK_FUZZING) +/* Using globals here to avoid adding function parameters */ + +/* Keep distinct filenames for netlink fuzzy collection */ +static unsigned int netlink_file_counter = 1; + +/** + * netlink_write_incoming() - Writes all data received from netlink to a file + * @buf: Data from netlink. + * @size: Size of data. + * @counter: Counter for keeping filenames distinct. + */ +static void netlink_write_incoming(const char *buf, const unsigned int size, + unsigned int counter) +{ + char fname[MAXPATHLEN]; + FILE *f; + + snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); + frr_with_privs(&zserv_privs) { + f = fopen(fname, "w"); + } + if (f) { + fwrite(buf, 1, size, f); + fclose(f); + } +} + +#endif /* HANDLE_NETLINK_FUZZING */ + static void kernel_read(struct event *thread) { struct zebra_ns *zns = (struct zebra_ns *)EVENT_ARG(thread); @@ -998,6 +1034,11 @@ static int netlink_recv_msg(struct nlsock *nl, struct msghdr *msg) #endif /* NETLINK_DEBUG */ } +#if defined(HANDLE_NETLINK_FUZZING) + zlog_debug("Writing incoming netlink message"); + netlink_write_incoming(buf, status, netlink_file_counter++); +#endif /* HANDLE_NETLINK_FUZZING */ + return status; } @@ -1304,33 +1345,14 @@ static int nl_batch_read_resp(struct nl_batch *bth) msg.msg_name = (void *)&snl; msg.msg_namelen = sizeof(snl); - /* - * The responses are not batched, so we need to read and process one - * message at a time. - */ - while (true) { - status = netlink_recv_msg(nl, &msg); - /* - * status == -1 is a full on failure somewhere - * since we don't know where the problem happened - * we must mark all as failed - * - * Else we mark everything as worked - * - */ - if (status == -1 || status == 0) { - while ((ctx = dplane_ctx_dequeue(&(bth->ctx_list))) != - NULL) { - if (status == -1) - dplane_ctx_set_status( - ctx, - ZEBRA_DPLANE_REQUEST_FAILURE); - dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); - } - return status; - } + status = netlink_recv_msg(nl, &msg); + if (status == -1 || status == 0) + return status; + + for (h = (struct nlmsghdr *)nl->buf; + (status >= 0 && NLMSG_OK(h, (unsigned int)status)); + h = NLMSG_NEXT(h, status)) { - h = (struct nlmsghdr *)nl->buf; ignore_msg = false; seq = h->nlmsg_seq; /* @@ -1802,6 +1824,7 @@ void kernel_init(struct zebra_ns *zns) snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); zns->netlink.sock = -1; +#ifndef FUZZING if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", @@ -1810,10 +1833,11 @@ void kernel_init(struct zebra_ns *zns) } kernel_netlink_nlsock_insert(&zns->netlink); - +#endif snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name), "netlink-cmd (NS %u)", zns->ns_id); zns->netlink_cmd.sock = -1; +#ifndef FUZZING if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", zns->netlink_cmd.name); @@ -1821,12 +1845,13 @@ void kernel_init(struct zebra_ns *zns) } kernel_netlink_nlsock_insert(&zns->netlink_cmd); - +#endif /* Outbound socket for dplane programming of the host OS. */ snprintf(zns->netlink_dplane_out.name, sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)", zns->ns_id); zns->netlink_dplane_out.sock = -1; +#ifndef FUZZING if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", zns->netlink_dplane_out.name); @@ -1848,7 +1873,8 @@ void kernel_init(struct zebra_ns *zns) } kernel_netlink_nlsock_insert(&zns->netlink_dplane_in); - +#endif +#ifndef FUZZING /* * SOL_NETLINK is not available on all platforms yet * apparently. It's in bits/socket.h which I am not @@ -1922,9 +1948,11 @@ void kernel_init(struct zebra_ns *zns) /* Set receive buffer size if it's set from command line */ if (rcvbufsize) { netlink_recvbuf(&zns->netlink, rcvbufsize); +#ifndef FUZZING netlink_recvbuf(&zns->netlink_cmd, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_out, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_in, rcvbufsize); +#endif } /* Set filter for inbound sockets, to exclude events we've generated @@ -1937,6 +1965,7 @@ void kernel_init(struct zebra_ns *zns) zns->netlink_cmd.snl.nl_pid, zns->netlink_dplane_out.snl.nl_pid); +#endif /* FUZZING */ zns->t_netlink = NULL; event_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, @@ -1997,4 +2026,18 @@ void kernel_router_terminate(void) nlsock_hash = NULL; } +#ifdef FUZZING +void netlink_fuzz(const uint8_t *data, size_t size) +{ + struct nlmsghdr *h = (struct nlmsghdr *)data; + + if (!NLMSG_OK(h, size)) + return; + + netlink_information_fetch(h, NS_DEFAULT, 0); +} +#endif /* FUZZING */ + + + #endif /* HAVE_NETLINK */ diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index 446c860327..74110cad8c 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -171,6 +171,12 @@ extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set); extern struct nlsock *kernel_netlink_nlsock_lookup(int sock); + +#ifdef FUZZING +void netlink_fuzz(const uint8_t *data, size_t size); +#endif + + #endif /* HAVE_NETLINK */ #ifdef __cplusplus diff --git a/zebra/main.c b/zebra/main.c index bd4623be55..e62614751c 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -22,6 +22,9 @@ #include "routemap.h" #include "routing_nb.h" +#include "fuzz.h" +#include "frr_pthread.h" + #include "zebra/zebra_router.h" #include "zebra/zebra_errors.h" #include "zebra/rib.h" @@ -45,6 +48,11 @@ #include "zebra/zebra_srte.h" #include "zebra/zebra_srv6.h" #include "zebra/zebra_srv6_vty.h" +#include "zebra/zapi_msg.h" + +#ifdef FUZZING +#include "zebra/kernel_netlink.h" +#endif /* FUZZING */ #define ZEBRA_PTM_SUPPORT @@ -279,6 +287,101 @@ FRR_DAEMON_INFO( .n_yang_modules = array_size(zebra_yang_modules), ); +#ifdef FUZZING + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static bool FuzzingInit(void) +{ + graceful_restart = 0; + vrf_configure_backend(VRF_BACKEND_VRF_LITE); + + const char *name[] = { "zebra" }; + + frr_preinit(&zebra_di, 1, (char **) name); + + /* Zebra related initialize. */ + zrouter.master = frr_init_fast(); + + zebra_router_init(false, true); + zserv_init(); + rib_init(); + zebra_if_init(); + zebra_debug_init(); + router_id_cmd_init(); + zebra_ns_init(); + zebra_vty_init(); + access_list_init(); + prefix_list_init(); + zebra_mpls_init(); + zebra_mpls_vty_init(); + zebra_pw_vty_init(); + zebra_pbr_init(); + zrouter.startup_time = monotime(NULL); + label_manager_init(); + zebra_rnh_init(); + zebra_evpn_init(); + zebra_error_init(); + frr_pthread_init(); + + return true; +} + +#ifndef FUZZING_LIBFUZZER +static struct zserv *FuzzingZc; +#endif /* FUZZING_LIBFUZZER */ + +static struct stream_fifo *fifo; + +static bool FuzzingInitialized; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (!FuzzingInitialized) { + FuzzingInit(); + FuzzingInitialized = true; + fifo = stream_fifo_new(); + } + + /* + * In the AFL standalone case, the client will already be created for + * us before __AFL_INIT() is called to speed things up. We can't pass + * it as an argument because the function signature must match + * libFuzzer's expectations. + * + * In the libFuzzer case, we need to create it each time. + * + * In both cases the client must be destroyed before we return.. + */ + struct zserv *zc; +#ifdef FUZZING_LIBFUZZER + zc = zserv_client_create(69); +#else + zc = FuzzingZc; +#endif /* FUZZING_LIBFUZZER */ + + +#if (FUZZING_MODE == ZAPI_FUZZING) + struct stream *s = stream_new(size + 1); + stream_put(s, data, size); + stream_fifo_push(fifo, s); + + zserv_handle_commands(zc, fifo); +#elif (FUZZING_MODE == NETLINK_FUZZING) + netlink_fuzz(data, size); +#endif + +done: + zserv_close_client(zc); + + return 0; +} +#endif /* FUZZING */ + +#ifndef FUZZING_LIBFUZZER + +CPP_NOTICE("Not using LibFuzzer, compiling in main symbol!") + /* Main startup routine. */ int main(int argc, char **argv) { @@ -289,6 +392,26 @@ int main(int argc, char **argv) bool asic_offload = false; bool notify_on_ack = true; +#ifdef FUZZING + FuzzingInit(); + FuzzingZc = zserv_client_create(69); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif /* __AFL_HAVE_MANUAL_CONTROL */ + + uint8_t *input = NULL; + int r = frrfuzz_read_input(&input); + + int ret = LLVMFuzzerTestOneInput(input, r); + + if (r > 0 && input) { + free(input); + } + + return ret; +#endif /* FUZZING */ + graceful_restart = 0; vrf_configure_backend(VRF_BACKEND_VRF_LITE); @@ -474,3 +597,4 @@ int main(int argc, char **argv) /* Not reached... */ return 0; } +#endif /* FUZZING_LIBFUZZER */ \ No newline at end of file diff --git a/zebra/subdir.am b/zebra/subdir.am index 1060e38785..d19aa65fd9 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -28,6 +28,7 @@ man8 += $(MANBUILD)/frr-zebra.8 ## endif ZEBRA endif +zebra_zebra_LDFLAGS = $(AM_LDFLAGS) $(ZEBRA_SAN_FLAGS) zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS) if HAVE_PROTOBUF3 zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 1d09843239..60a331f531 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2806,7 +2806,9 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, STREAM_GETL(s, size); STREAM_GETL(s, base); +#ifndef FUZZING assert(proto == client->proto && instance == client->instance); +#endif /* call hook to get a chunk using wrapper */ lm_get_chunk_call(&lmc, client, keep, size, base, vrf_id); @@ -2831,8 +2833,9 @@ static void zread_release_label_chunk(struct zserv *client, struct stream *msg) STREAM_GETL(s, start); STREAM_GETL(s, end); +#ifndef FUZZING assert(proto == client->proto && instance == client->instance); - +#endif /* call hook to release a chunk using wrapper */ lm_release_chunk_call(client, start, end); @@ -3961,8 +3964,33 @@ void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo) ZEBRA_MAX_PACKET_SIZ); goto continue_loop; } - +#ifdef FUZZING + /* + * The stream read over in zserv_read + * already guarantees this conditional + * when we read actual packets from clients + * but since we are cheating there is no + * point in allowing a crash in the fuzzing + * here. So let's prevent it. + */ + if (STREAM_READABLE(msg) < ZEBRA_HEADER_SIZE) + goto continue_loop; +#endif zapi_parse_header(msg, &hdr); +#ifdef FUZZING + /* + * The stream read over in zserv_read + * already guarantees the sizing of the packet + * before it can even be enqueued but FUZZING + * is cheating and calling this function directly + * Let's cut to the chase and prevent a crash + * because we have a funny header size -vs- + * what we can read. + */ + if (STREAM_SIZE(msg) != hdr.length) + goto continue_loop; +#endif + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV && IS_ZEBRA_DEBUG_DETAIL) diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c index cf2056b7ac..032541d8b7 100644 --- a/zebra/zebra_gr.c +++ b/zebra/zebra_gr.c @@ -148,12 +148,17 @@ int32_t zebra_gr_client_disconnect(struct zserv *client) if (stale_client) { LOG_GR("%s: Stale client %s exist, we should not be here!", __func__, zebra_route_string(client->proto)); +#ifndef FUZZING assert(0); +#endif } client->restart_time = monotime(&tv); /* For all the GR instance start the stale removal timer. */ +#ifdef FUZZING + struct client_gr_info dupinfo = {}; +#endif TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities) && (info->t_stale_removal == NULL)) { @@ -170,11 +175,22 @@ int32_t zebra_gr_client_disconnect(struct zserv *client) __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id, info->stale_removal_time); + + dupinfo = *info; +#ifdef FUZZING + // yeah, that thread will never execute...clean it up now + //struct thread t = {}; + struct event t = {}; + t.arg = info; + info->t_stale_removal = &t; + zebra_gr_route_stale_delete_timer_expiry(&t); + info = &dupinfo; +#endif } } - +#ifndef FUZZING listnode_add(zrouter.stale_client_list, client); - +#endif return 0; } diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 7715eab0a8..3b7cf0eb7d 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -353,6 +353,9 @@ stream_failure: static void zebra_mlag_spawn_pthread(void) { /* Start MLAG write pthread */ +#ifdef FUZZING + return; +#endif struct frr_pthread_attr pattr = {.start = frr_pthread_attr_default.start, @@ -453,8 +456,9 @@ void zebra_mlag_client_register(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_MLAG) zlog_debug( "First client, opening the channel with MLAG"); - +#ifndef FUZZING zebra_mlag_spawn_pthread(); +#endif rc = hook_call(zebra_mlag_private_open_channel); if (rc < 0) { /* diff --git a/zebra/zserv.c b/zebra/zserv.c index d2367007cf..22a19e3a3b 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -460,6 +460,9 @@ zread_fail: static void zserv_client_event(struct zserv *client, enum zserv_client_event event) { +#ifdef FUZZING + return; +#endif switch (event) { case ZSERV_CLIENT_READ: event_add_read(client->pthread->master, zserv_read, client, @@ -527,6 +530,10 @@ static void zserv_process_messages(struct event *thread) int zserv_send_message(struct zserv *client, struct stream *msg) { +#ifdef FUZZING + stream_free(msg); + return 0; +#endif frr_with_mutex (&client->obuf_mtx) { stream_fifo_push(client->obuf_fifo, msg); } @@ -584,7 +591,9 @@ static void zserv_client_free(struct zserv *client) unsigned long nroutes; unsigned long nnhgs; +#ifndef FUZZING close(client->sock); +#endif if (DYNAMIC_CLIENT_GR_DISABLED(client)) { zebra_mpls_client_cleanup_vrf_label(client->proto); @@ -618,10 +627,12 @@ static void zserv_client_free(struct zserv *client) if (client->wb) buffer_free(client->wb); +#ifndef FUZZING /* Free buffer mutexes */ pthread_mutex_destroy(&client->stats_mtx); pthread_mutex_destroy(&client->obuf_mtx); pthread_mutex_destroy(&client->ibuf_mtx); +#endif /* Free bitmaps. */ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { @@ -661,6 +672,7 @@ void zserv_close_client(struct zserv *client) bool free_p = true; if (client->pthread) { +#ifndef FUZZING /* synchronously stop and join pthread */ frr_pthread_stop(client->pthread, NULL); @@ -675,6 +687,7 @@ void zserv_close_client(struct zserv *client) /* destroy pthread */ frr_pthread_destroy(client->pthread); client->pthread = NULL; +#endif } /* @@ -724,7 +737,7 @@ static void zserv_handle_client_fail(struct event *thread) * sock * client's socket file descriptor */ -static struct zserv *zserv_client_create(int sock) +struct zserv *zserv_client_create(int sock) { struct zserv *client; size_t stream_size = @@ -741,9 +754,11 @@ static struct zserv *zserv_client_create(int sock) client->ibuf_work = stream_new(stream_size); client->obuf_work = stream_new(stream_size); client->connect_time = monotime(NULL); +#ifndef FUZZING pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); pthread_mutex_init(&client->stats_mtx, NULL); +#endif client->wb = buffer_new(0); TAILQ_INIT(&(client->gr_info_queue)); @@ -761,6 +776,7 @@ static struct zserv *zserv_client_create(int sock) listnode_add(zrouter.client_list, client); } +#ifndef FUZZING struct frr_pthread_attr zclient_pthr_attrs = { .start = frr_pthread_attr_default.start, .stop = frr_pthread_attr_default.stop @@ -771,12 +787,15 @@ static struct zserv *zserv_client_create(int sock) /* start read loop */ zserv_client_event(client, ZSERV_CLIENT_READ); +#endif /* call callbacks */ hook_call(zserv_client_connect, client); /* start pthread */ +#ifndef FUZZING frr_pthread_run(client->pthread, NULL); +#endif return client; } @@ -949,6 +968,9 @@ void zserv_start(char *path) void zserv_event(struct zserv *client, enum zserv_event event) { +#ifdef FUZZING + return; +#endif switch (event) { case ZSERV_ACCEPT: event_add_read(zrouter.master, zserv_accept, NULL, zsock, NULL); diff --git a/zebra/zserv.h b/zebra/zserv.h index 90aa4d53f4..2c610f25ba 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -386,6 +386,12 @@ extern void zread_client_capabilities(struct zserv *client, struct zmsghdr *hdr, struct stream *msg, struct zebra_vrf *zvrf); +#ifdef FUZZING +struct zserv *zserv_client_create(int sock); +#endif + + + #ifdef __cplusplus } #endif -- 2.39.5