]> git.puffer.fish Git - matthieu/frr.git/commitdiff
enable libfuzzer for bgpd
authorQuentin Young <qlyoung@cumulusnetworks.com>
Sat, 11 Jan 2020 03:20:33 +0000 (22:20 -0500)
committerQuentin Young <qlyoung@nvidia.com>
Mon, 15 Nov 2021 19:52:42 +0000 (14:52 -0500)
Wow that was painful

libFuzzer replaces main(), so while we can compile with
-fsanitize=fuzzer, we can't link with it unless we have a way to
undefine main(). So I've added a #define, FUZZING_LIBFUZZER, that
daemons who want to support libfuzzer need to guard their main() with.
This also means we can't use the SAN_FLAGS automake variable, since that
is included in both AM_CFLAGS and AM_LDFLAGS, to add -fsanitize=fuzzer
to. We need new daemon specific flags. Actually, we can add
-fsanitize=fuzzer-no-link to SAN_FLAGS, but we need daemon specific
LDFLAGS so we can control who links with -fsanitize=fuzzer.

Also, compiling with libfuzzer also requires that you define a function
named LLVMFuzzerTestOneInput(). So I defined a stub version in libfrr.c
and added a macro to undefine it for daemons who actually implement it.
Now that I write it down this probably isn't necessary at all given the
previous paragraph. I think that function is only checked for at link
time.

For bgpd, because libfuzzer is in-process, we now need to actuall clean
up after ourselves each fuzz run to avoid leaking memory.  We also can't
touch global state. This also means we run slower because we have to
create and destroy a peer struct every iteration.

Finally I've almost certainly broken afl for now, will fix later.

bgpd/bgp_io.c
bgpd/bgp_keepalives.c
bgpd/bgp_main.c
bgpd/subdir.am
configure.ac
lib/libfrr.c

index f866ad2c385023045436bfa9426964dbbd506e38..b624d4d75cd3bc6d9054e26bc2884893cdc4aef9 100644 (file)
@@ -57,6 +57,9 @@ 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);
 
@@ -75,6 +78,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);
 
@@ -86,6 +92,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);
 
@@ -106,6 +115,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);
 
index 3a5adae8748091c6ba940497e23788934147fbb2..c00a379ab06c3df859b0ac070c71829c0e4be524 100644 (file)
@@ -238,6 +238,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;
 
@@ -266,6 +269,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;
 
index 53137d350ad2a0a337a78961a4ee1ffb144ddf5d..0284086dddaa795f34cbb20178014496eb5ad3d6 100644 (file)
@@ -17,8 +17,7 @@
  * with this program; see the file COPYING; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  */
-
-#define FUZZING 1
+#define FUZZING_OVERRIDE_LLVMFuzzerTestOneInput
 
 #include <zebra.h>
 
@@ -401,13 +400,9 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT,
 
 #include "lib/ringbuf.h"
 
-/* Main routine of bgpd. Treatment of argument and start bgp finite
-   state machine is handled at here. */
-int main(int argc, char **argv)
-{
-       int opt;
-       int tmp_port;
+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;
@@ -415,19 +410,18 @@ int main(int argc, char **argv)
        int skip_runas = 0;
        int instance = 0;
        int buffer_size = BGP_SOCKET_SNDBUF_SIZE;
-       char *address;
        struct listnode *node;
 
        addresses->cmp = (int (*)(void *, void *))strcmp;
 
-       frr_preinit(&bgpd_di, argc, argv);
+       const char *name[] = { "bgpd" };
+
+       frr_preinit(&bgpd_di, 1, (char **) name);
 
-#ifdef FUZZING
        /* Initialize basic BGP datastructures */
-       bgp_master_init(frr_init_fast(), buffer_size);
+       bgp_master_init(frr_init_fast(), buffer_size, addresses);
        bm->port = bgp_port;
        bgp_option_set(BGP_OPT_NO_LISTEN);
-       bm->address = bgp_address;
 
 
        bgp_option_set(BGP_OPT_NO_FIB);
@@ -442,6 +436,11 @@ int main(int argc, char **argv)
        as_t as = 65001;
        bgp_get(&b, &as, "default", BGP_INSTANCE_TYPE_DEFAULT);
 
+       return true;
+}
+
+static struct peer *FuzzingCreatePeer()
+{
        union sockunion su;
        sockunion_init(&su);
        inet_pton(AF_INET, "10.1.1.1", &su.sin.sin_addr);
@@ -461,18 +460,32 @@ int main(int argc, char **argv)
                SET_FLAG(p->af_cap[afi][safi], 0x3FFF);
        }
 
-#ifdef __AFL_HAVE_MANUAL_CONTROL
-       __AFL_INIT();
-#endif
+       return p;
+}
 
+static struct peer *FuzzingPeer;
 
-       uint8_t *input;
-       int r = frrfuzz_read_input(&input);
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       static bool initialized;
 
-       if (!input)
-               return 0;
+       if (!initialized)
+               initialized = FuzzingInit();
+
+       /*
+        * 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.
+        *
+        * In the libFuzzer case, we need to create it each time.
+        *
+        * In both cases the peer must be destroyed before we return..
+        */
+       struct peer *p = (FuzzingPeer) ? FuzzingPeer : FuzzingCreatePeer();
 
-       ringbuf_put(p->ibuf_work, input, r);
+
+       ringbuf_put(p->ibuf_work, data, size);
 
        /*
         * Simulate the read process done by bgp_process_reads().
@@ -482,10 +495,10 @@ int main(int argc, char **argv)
         * so we need to make sure that remains true for fuzzed input.
         * */
        if (!validate_header(p))
-               exit(9);
+               return 0;
 
        int result = 0;
-       static unsigned char pktbuf[BGP_MAX_PACKET_SIZE];
+       unsigned char pktbuf[BGP_MAX_PACKET_SIZE];
        uint16_t pktsize = 0;
 
        ringbuf_peek(p->ibuf_work, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize));
@@ -504,9 +517,46 @@ int main(int argc, char **argv)
                result = bgp_process_packet(&t);
        }
 
-       exit(result);
+       peer_delete(p);
+       FuzzingPeer = NULL;
+
+       return 0;
+};
+
+/* Fucking libFuzzer... */
+#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)
+{
+       int opt;
+       int tmp_port;
+
+       int bgp_port = BGP_PORT_DEFAULT;
+       char *bgp_address = NULL;
+       int no_fib_flag = 0;
+       int no_zebra_flag = 0;
+       int skip_runas = 0;
+       int instance = 0;
+       int buffer_size = BGP_SOCKET_SNDBUF_SIZE;
+
+       frr_preinit(&bgpd_di, argc, argv);
+
+#ifdef FUZZING
+       FuzzingInit();
+       FuzzingPeer = FuzzingCreatePeer();
+
+#ifdef __AFL_HAVE_MANUAL_CONTROL
+       __AFL_INIT();
 #endif
+       uint8_t *input;
+       int r = frrfuzz_read_input(&input);
 
+       if (!input)
+               return 0;
+
+       return LLVMFuzzerTestOneInput(input, r);
+#endif
 
        frr_opt_add(
                "p:l:SnZe:I:s:" DEPRECATED_OPTIONS, longopts,
@@ -627,3 +677,4 @@ int main(int argc, char **argv)
        /* Not reached. */
        return 0;
 }
+#endif /* FUZZING_LIBFUZZER */
index 9e3a095529e6f99eae6706a501b72c61345b5662..58993ca6e41de8db3458f2b06bf33fc19968cb1c 100644 (file)
@@ -210,6 +210,10 @@ noinst_HEADERS += \
 bgpd_bgpd_SOURCES = bgpd/bgp_main.c
 bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c
 
+bgpd_bgpd_CFLAGS = $(AM_CFLAGS) $(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)
index 9aa6efbf5586933862614757b6cdc0d0cbd0fe93..e369aef7f683388f16fddf2a154a84c0a09b4625 100644 (file)
@@ -397,7 +397,19 @@ 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])
+    AC_MSG_NOTICE([WARNING - libFuzzer implies ASAN for all daemons])
+    SAN_FLAGS="$SAN_FLAGS -fsanitize=address"
+    BGPD_SAN_FLAGS="-fsanitize=fuzzer"
+    AC_DEFINE([FUZZING_LIBFUZZER], [1], [Compiling and linking with libFuzzer])
+  ])
+fi
 AC_SUBST([SAN_FLAGS])
+AC_SUBST([BGPD_SAN_FLAGS])
 
 dnl frr-format.so
 if test "$with_frr_format" != "no" -a "$with_frr_format" != "yes" -a -n "$with_frr_format"; then
@@ -727,6 +739,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=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)]))
 AC_ARG_WITH([frr-format],
index 9409fac9f24136cfecf11e530fdfe855f42bb7fc..e513ee2ed74c5fb14f4c242b2321fcfc4319da32 100644 (file)
 #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_late_init, (struct thread_master * tm), (tm));
 DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm));
 DEFINE_HOOK(frr_config_post, (struct thread_master * tm), (tm));