]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[experiments/fuzz] Initiall fuzzing code added for Kea6
authorTomek Mrugalski <tomasz@isc.org>
Mon, 21 Nov 2016 10:53:04 +0000 (11:53 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Wed, 24 Apr 2019 10:43:05 +0000 (12:43 +0200)
configure.ac
src/bin/dhcp6/Makefile.am
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/fuzz.cc [new file with mode: 0644]
src/bin/dhcp6/fuzz.h [new file with mode: 0644]

index 1d054feb614c680763a0d3a4ca77cb0fdc26cf56..7c75edd04f298ceb90aa78aa1d156f61ac94bffb 100644 (file)
@@ -1498,6 +1498,17 @@ if test "x$VALGRIND" != "xno"; then
    found_valgrind="found"
 fi
 
+AC_ARG_ENABLE(fuzz, [AC_HELP_STRING([--enable-fuzz],
+  [indicates that the code will be built with AFL (American Fuzzy Lop) support.
+   Code built this way is unusable as a regular server. [default=no]])],
+   enable_fuzz=$enableval, enable_fuzz=no)
+AM_CONDITIONAL(FUZZ, test x$enable_fuzz != xno)
+
+if test "x$enable_fuzz" != "xno" ; then
+    AC_DEFINE([FUZZ], [1], [AFL fuzzing was enabled.])
+fi
+
+
 # Check for optreset in unistd.h. On BSD systems the optreset is
 # used to reset the state of getopt() function. Resetting its state
 # is required if command line arguments are parsed multiple times
@@ -1983,6 +1994,7 @@ Developer:
   Generate Messages Files:   $enable_generate_messages
   Perfdhcp:                  $enable_perfdhcp
   Kea-shell:                 $enable_shell
+  Enable fuzz:               $enable_fuzz
 
 END
 
index 4730182bf2eb88731a43533a5a151acf765ae2bf..ee3585411e2bb3155b02b448f8a6cc5af31e370c 100644 (file)
@@ -55,7 +55,7 @@ libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh
 libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
 libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
 libdhcp6_la_SOURCES += dhcp6_messages.h dhcp6_messages.cc
-EXTRA_DIST += dhcp6_messages.mes
+libdhcp6_la_SOURCES += fuzz.cc
 
 sbin_PROGRAMS = kea-dhcp6
 
index fba6cfbbe51604b2c0d609a98103d9682e971e84..93cb45669a05a0bb8d55bcdee5f77f0b9887098a 100644 (file)
@@ -65,6 +65,8 @@
 #endif
 #include <dhcpsrv/memfile_lease_mgr.h>
 
+#include <dhcp6/fuzz.h>
+
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
 #include <boost/tokenizer.hpp>
@@ -405,6 +407,19 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
 }
 
 bool Dhcpv6Srv::run() {
+
+#ifdef FUZZ
+    // AFL fuzzing setup initiated here. At this stage, Kea has loaded its
+    // config, opened sockets, established DB connections, etc. It is truly
+    // ready to process packets. Now it's time to initialize AFL. It will
+    // set up a separate thread that will receive data from fuzzing engine
+    // and will send it as packets to Kea. Kea is supposed to process them
+    // and hopefully not crash in the process. Once the packet processing
+    // is done, Kea should let the AFL know that it's ready for the next
+    // packet. This is done further down in this loop (see kea_fuzz_notify()).
+    kea_fuzz_setup();
+#endif /* FUZZ */
+
     while (!shutdown_) {
         try {
             run_one();
@@ -414,11 +429,18 @@ bool Dhcpv6Srv::run() {
             // specific catches.
             LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION)
                 .arg(e.what());
+
         } catch (...) {
             // General catch-all non-standard exception that are not caught
             // by more specific catches.
             LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION);
         }
+
+#ifdef FUZZ
+        // Ok, this particular packet processing is done.
+        // Let the AFL know about it.
+        kea_fuzz_notify();
+#endif
     }
 
     return (true);
diff --git a/src/bin/dhcp6/fuzz.cc b/src/bin/dhcp6/fuzz.cc
new file mode 100644 (file)
index 0000000..57959e6
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "config.h"
+
+#include <dhcp6/fuzz.h>
+
+#define ENABLE_AFL
+
+#ifdef ENABLE_AFL
+#include <sys/errno.h>
+
+#include <dhcp/dhcp6.h>
+
+#include <iostream>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#ifndef __AFL_LOOP
+#error To use American Fuzzy Lop you have to set CC to afl-clang-fast!!!
+#endif
+
+/*
+ * We are using pthreads directly because we might be using it with unthreaded
+ * version of BIND, where all thread functions are mocks. Since AFL for now only
+ * works on Linux it's not a problem.
+ */
+static pthread_cond_t cond;
+static pthread_mutex_t mutex;
+
+static bool ready;
+
+using namespace std;
+
+// This is the main fuzzing function. It receives data from fuzzing engine.
+// That data is received to stdin and then sent over the configured UDP socket.
+// Then it wait for a conditional, which is called in kea_fuzz_notify() from
+// Kea main loop.
+static void *
+kea_main_client(void *arg) {
+    const char *host;
+    struct sockaddr_in6 servaddr;
+    int sockfd;
+    int loop;
+    void *buf;
+
+    string iface("eth0");
+    string dst(ALL_DHCP_RELAY_AGENTS_AND_SERVERS);
+    string port("547");
+
+    const char *iface_ptr = getenv("KEA_AFL_INTERFACE");
+    if (iface_ptr) {
+        iface = string(iface_ptr);
+    }
+
+    const char *dst_ptr = getenv("KEA_AFL_ADDR");
+    if (dst_ptr) {
+        dst = string(dst_ptr);
+    }
+
+    const char *port_ptr = getenv("KEA_AFL_PORT");
+    if (port_ptr) {
+        port = string(port_ptr);
+    }
+
+    unsigned int iface_id = if_nametoindex(iface.c_str());
+    
+    cout << "Kea AFL setup:" << endl;
+    cout << "Interface: " << iface << endl;
+    cout << "Interface index: " << iface_id << endl;
+    cout << "UDP destination addr: " << dst << endl;
+    cout << "UDP destination port: " << port << endl;
+
+    memset(&servaddr, 0, sizeof (servaddr));
+    servaddr.sin6_family = AF_INET6;
+    if (inet_pton(AF_INET6, dst.c_str(), &servaddr.sin6_addr) != 1) {
+        cout << "Error: inet_pton() failed: can't convert " << dst
+             << " to address." << endl;
+        exit(EXIT_FAILURE);
+    }
+    servaddr.sin6_port = htons(547);
+    servaddr.sin6_scope_id = iface_id;
+
+    sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (sockfd < 0) {
+        cout << "Failed to create UDP6 socket" << endl;
+        exit(EXIT_FAILURE);
+    }
+
+    buf = malloc(65536);
+    if (!buf) {
+        cout << "Failed to allocate a buffer" << endl;
+        exit(EXIT_FAILURE);
+    }
+
+    loop = 100000;
+    while (loop--) {
+        ssize_t length;
+
+        length = read(0, buf, 65536);
+        if (length <= 0) {
+            usleep(1000000);
+            continue;
+        }
+
+        /* if (length > 4096) {
+            if (getenv("AFL_CMIN")) {
+                ns_server_flushonshutdown(ns_g_server,
+                                          ISC_FALSE);
+                isc_app_shutdown();
+                return (NULL);
+            }
+            raise(SIGSTOP);
+            continue;
+            } */
+
+        if (pthread_mutex_lock(&mutex) != 0) {
+            cout << "#### Failed to lock mutex" << endl;
+        }
+
+        ready = false;
+
+        ssize_t sent;
+
+        sent = sendto(sockfd, buf, length, 0,
+                      (struct sockaddr *) &servaddr, sizeof(servaddr));
+        if (sent != length) {
+            cout << "#### Error: expected to send " << length
+                 << ", but really sent " << sent << endl;
+        }
+
+        /* unclog */
+        recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL);
+
+        while (!ready)
+            pthread_cond_wait(&cond, &mutex);
+
+        if (pthread_mutex_unlock(&mutex) != 0) {
+            cout << "#### Failed to unlock mutex" << endl;
+        }
+    }
+
+    free(buf);
+    close(sockfd);
+
+    // @todo: shutdown kea
+    // ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+    // isc_app_shutdown();
+
+    /*
+     * It's here just for the signature, that's how AFL detects if it's
+     * a 'persistent mode' binary.
+     */
+    __AFL_LOOP(0);
+
+    return (NULL);
+}
+
+#endif /* ENABLE_AFT */
+
+void
+kea_fuzz_notify(void) {
+#ifdef ENABLE_AFL
+    /// @todo: What does this piece of code do?
+    /* if (getenv("AFL_CMIN")) {
+        ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+        isc_app_shutdown();
+        return;
+        } */
+
+    raise(SIGSTOP);
+    
+    if (pthread_mutex_lock(&mutex) != 0) {
+        cout << "#### unable to lock mutex" << endl;
+    }
+    
+    ready = true;
+    
+    if (pthread_cond_signal(&cond) != 0) {
+
+    }
+    
+    if (pthread_mutex_unlock(&mutex) != 0) {
+        cout << "Unable to unlock mutex" << endl;
+    }
+#endif /* ENABLE_AFL */
+}
+
+void
+kea_fuzz_setup(void) {
+#ifdef ENABLE_AFL
+
+    /// @todo: What are those variables? What do they do?
+    if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) {
+        pthread_t thread;
+        
+        if (pthread_mutex_init(&mutex, NULL) != 0) {
+            
+        }
+        
+        if (pthread_cond_init(&cond, NULL) == 0) {
+
+        }
+
+        
+        if (pthread_create(&thread, NULL, kea_main_client, NULL) != 0) {
+            
+        }
+    }
+
+#endif /* ENABLE_AFL */
+}
diff --git a/src/bin/dhcp6/fuzz.h b/src/bin/dhcp6/fuzz.h
new file mode 100644 (file)
index 0000000..dcce8b2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef KEA_FUZZ_H
+#define KEA_FUZZ_H
+
+extern "C" {
+
+void kea_fuzz_notify(void);
+
+void kea_fuzz_setup(void);
+
+};
+
+#endif /* KEA_FUZZ_H */