]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#640,!351] Extended fuzzing to kea-dhcp4.
authorStephen Morris <stephen@isc.org>
Thu, 13 Jun 2019 09:56:57 +0000 (10:56 +0100)
committerStephen Morris <stephen@isc.org>
Tue, 1 Oct 2019 16:00:21 +0000 (17:00 +0100)
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp6/dhcp6_srv.cc
src/lib/dhcpsrv/fuzz.cc
src/lib/dhcpsrv/fuzz.h
src/lib/dhcpsrv/fuzz_messages.mes

index f658489a568cbc9ccc27ee31cbb912c1d76b9f59..132e4981473177fb2eb2cbd520bcf1bb40edac3a 100644 (file)
@@ -31,6 +31,7 @@
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_shared_networks.h>
 #include <dhcpsrv/cfg_subnets4.h>
+#include <dhcpsrv/fuzz.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/ncr_generator.h>
@@ -767,6 +768,21 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
 
 bool
 Dhcpv4Srv::run() {
+#ifdef ENABLE_AFL
+    // 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 know that it's ready for the next packet. This is done further
+    // down in this loop (see Fuzz::packetProcessed()).
+    Fuzz::init(4, &shutdown_);
+    //
+    // The next line is needed as a signature for AFL to recognise that we are
+    // running persistent fuzzing.
+    __AFL_LOOP(0);
+#endif // ENABLE_AFL
     while (!shutdown_) {
         try {
             run_one();
@@ -782,6 +798,12 @@ Dhcpv4Srv::run() {
             // std::exception.
             LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION);
         }
+
+#ifdef ENABLE_AFL
+        // Ok, this particular packet processing is done.  If we are fuzzing,
+        // let AFL know about it.
+        Fuzz::packetProcessed();
+#endif // ENABLE_AFL
     }
 
     return (true);
index fb3fe6b43eed4d53d81dfb07cb2d1e9476fdf2d8..760a39944fbbd4af432ffee16d6e064cae153176 100644 (file)
@@ -452,7 +452,7 @@ bool Dhcpv6Srv::run() {
     // not crash in the process. Once the packet processing is done, Kea should
     // let the know that it's ready for the next packet. This is done further
     // down in this loop (see Fuzz::packetProcessed()).
-    Fuzz::init(&shutdown_);
+    Fuzz::init(6, &shutdown_);
     //
     // The next line is needed as a signature for AFL to recognise that we are
     // running persistent fuzzing.
index a03a8fcc7aaad915b84f6cf1f68f7dd67fdd37c4..1be2394b5f8dce0cce0b4f9adb3edd9011fe4d01 100644 (file)
@@ -47,7 +47,10 @@ std::thread             Fuzz::fuzzing_thread_;
 
 // Address structure used to define the address/port used to send
 // fuzzing data to the Kea server.
-struct sockaddr_in6     Fuzz::servaddr_;
+struct sockaddr_in6     Fuzz::servaddr6_;
+struct sockaddr_in      Fuzz::servaddr4_;
+sockaddr*               Fuzz::sockaddr_ptr = NULL;
+size_t                  Fuzz::sockaddr_len = 0;
 
 // Pointer to the shutdown flag used by Kea.  The fuzzing code will set this
 // after the appropriate number of packets have been fuzzed.
@@ -84,72 +87,106 @@ FuzzSynch::notify(void) {
 
 // Fuzz methods.
 
-// Initialization: create the thread artifacts and start the fuzzing thread.
 void
-Fuzz::init(volatile bool* shutdown_flag) {
+Fuzz::setAddress(int ipversion) {
+    stringstream reason;    // Used in error messages
+
+    // Get the environment for the fuzzing: interface, address and port.
+    const char *iface = getenv("KEA_AFL_INTERFACE");
+    if (! iface) {
+        isc_throw(FuzzInitFail, "no fuzzing interface has been set");
+    }
+
+    // Now the address.
+    const char *address = getenv("KEA_AFL_ADDRESS");
+    if (address == 0) {
+        isc_throw(FuzzInitFail, "no fuzzing address has been set");
+    }
+
+    // ... and the port.
+    unsigned short port = 0;
+    const char *port_ptr = getenv("KEA_AFL_PORT");
+    if (port_ptr == 0) {
+        isc_throw(FuzzInitFail, "no fuzzing port has been set");
+    }
     try {
-        stringstream reason;    // Used for exception messages
+        port = boost::lexical_cast<uint16_t>(port_ptr);
+    } catch (const boost::bad_lexical_cast&) {
+        reason << "cannot convert port number specification "
+               << port_ptr << " to an integer";
+        isc_throw(FuzzInitFail, reason.str());
+    }
 
-        // Store reference to shutdown flag.  When the fuzzing loop has read
-        // the set number of packets from AFL, it will set this flag to trigger
-        // a Kea shutdown.
-        if (shutdown_flag) {
-            shutdown_ptr_ = shutdown_flag;
-        } else {
-            isc_throw(FuzzInitFail, "must pass shutdown flag to kea_fuzz_init");
-        }
+    // Decide if the address is an IPv4 or IPv6 address.
+    if ((strstr(address, ".") != NULL) && (ipversion == 4)) {
+        // Assume an IPv4 address
+        memset(&servaddr4_, 0, sizeof(servaddr4_));
 
-        // Get the environment for the fuzzing: interface, address and port.
-        const char *iface_ptr = getenv("KEA_AFL_INTERFACE");
-        if (! iface_ptr) {
-            isc_throw(FuzzInitFail, "no fuzzing interface has been set");
-        }
-        // Interface obtained, convert to ID.
-        unsigned int iface_id = if_nametoindex(iface_ptr);
-        if (iface_id == 0) {
-            reason << "error retrieving interface ID for "
-                   << iface_ptr << ": " << strerror(errno);
+        servaddr4_.sin_family = AF_INET;
+        if (inet_pton(AF_INET, address, &servaddr4_.sin_addr) != 1) {
+            reason << "inet_pton() failed: can't convert "
+                   << address << " to an IPv6 address" << endl;
             isc_throw(FuzzInitFail, reason.str());
         }
+        servaddr4_.sin_port = htons(port);
 
-        // Now the address.
-        const char *address_ptr = getenv("KEA_AFL_ADDRESS");
-        if (address_ptr == 0) {
-            isc_throw(FuzzInitFail, "no fuzzing address has been set");
-        }
+        sockaddr_ptr = reinterpret_cast<sockaddr*>(&servaddr4_);
+        sockaddr_len = sizeof(servaddr4_);
 
-        // ... and the port.
-        unsigned short port = 0;
-        const char *port_ptr = getenv("KEA_AFL_PORT");
-        if (port_ptr == 0) {
-            isc_throw(FuzzInitFail, "no fuzzing port has been set");
-        }
-        try {
-            port = boost::lexical_cast<uint16_t>(port_ptr);
-        } catch (const boost::bad_lexical_cast&) {
-            reason << "cannot convert port number specification "
-                   << port_ptr << " to an integer";
-            isc_throw(FuzzInitFail, reason.str());
-        }
+    } else if ((strstr(address, ":") != NULL) && (ipversion == 6)) {
 
         // Set up the IPv6 address structure.
-        memset(&servaddr_, 0, sizeof (servaddr_));
-        servaddr_.sin6_family = AF_INET6;
-        if (inet_pton(AF_INET6, address_ptr, &servaddr_.sin6_addr) != 1) {
+        memset(&servaddr6_, 0, sizeof (servaddr6_));
+
+        servaddr6_.sin6_family = AF_INET6;
+        if (inet_pton(AF_INET6, address, &servaddr6_.sin6_addr) != 1) {
             reason << "inet_pton() failed: can't convert "
-                   << address_ptr << " to an IPv6 address" << endl;
+                   << address << " to an IPv6 address" << endl;
+            isc_throw(FuzzInitFail, reason.str());
+        }
+        servaddr6_.sin6_port = htons(port);
+
+        // Interface ID is needed for IPv6 address structures.
+        servaddr6_.sin6_scope_id = if_nametoindex(iface);
+        if (servaddr6_.sin6_scope_id == 0) {
+            reason << "error retrieving interface ID for "
+                   << iface << ": " << strerror(errno);
             isc_throw(FuzzInitFail, reason.str());
         }
-        servaddr_.sin6_port = htons(port);
-        servaddr_.sin6_scope_id = iface_id;
+
+        sockaddr_ptr = reinterpret_cast<sockaddr*>(&servaddr6_);
+        sockaddr_len = sizeof(servaddr6_);
+    } else {
+        reason << "Expected IP version (" << ipversion << ") is not "
+               << "4 or 6, or the given address " << address << " does not "
+               << "match the IP version expected";
+        isc_throw(FuzzInitFail, reason.str());
+    }
+
+    LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_INTERFACE).arg(iface).arg(address).arg(port);
+}
+
+// Initialization: create the thread artifacts and start the fuzzing thread.
+void
+Fuzz::init(int ipversion, volatile bool* shutdown_flag) {
+    try {
+        stringstream reason;    //yy Used for exception messages
+
+        // Store reference to shutdown flag.  When the fuzzing loop has read
+        // the set number of packets from AFL, it will set this flag to trigger
+        // a Kea shutdown.
+        if (shutdown_flag) {
+            shutdown_ptr_ = shutdown_flag;
+        } else {
+            isc_throw(FuzzInitFail, "must pass shutdown flag to kea_fuzz_init");
+        }
 
         // Initialize synchronization variables.
         fuzz_sync_.init("fuzz_synch");
         main_sync_.init("main_synch");
 
-        // Initialization complete.
-        LOG_INFO(fuzz_logger, FUZZ_INTERFACE)
-                 .arg(iface_ptr).arg(address_ptr).arg(port);
+        // Set up address structures.
+        setAddress(ipversion);
 
         // Start the thread that reads the packets sent by AFL from stdin and
         // passes them to the port on which Kea is listening.
@@ -165,6 +202,8 @@ Fuzz::init(volatile bool* shutdown_flag) {
         LOG_FATAL(fuzz_logger, FUZZ_INIT_FAIL).arg(e.what());
         throw;
     }
+
+    LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_INIT_COMPLETE);
 }
 
 // This is the main fuzzing function. It receives data from fuzzing engine.
@@ -215,8 +254,8 @@ Fuzz::run(void) {
         // some form of synchronization, this approach does not work.
 
         // Send the data to the main Kea thread.
-        ssize_t sent = sendto(sockfd, buf, length, 0,
-                          (struct sockaddr *) &servaddr_, sizeof(servaddr_));
+        ssize_t sent = sendto(sockfd, buf, length, 0, sockaddr_ptr,
+                              sockaddr_len);
         if (sent < 0) {
             LOG_ERROR(fuzz_logger, FUZZ_SEND_ERROR).arg(strerror(errno));
             continue;
@@ -256,7 +295,7 @@ Fuzz::run(void) {
 // last packet has finished.
 void
 Fuzz::packetProcessed(void) {
-    LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_PACKET_PROCESSED_CALLED);
+    LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_PACKET_PROCESSED_CALLED);
 
     // Tell AFL that the processing for this packet has finished.
     raise(SIGSTOP);
index bc91298f8b7e63a101b795c5125cdf3dbd25b8f1..b74d2eb65f9b96f8be4c2e05995e9b91e0cbe2e7 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef FUZZ_H
 #define FUZZ_H
 
+#ifdef ENABLE_AFL
+
 #include <exceptions/exceptions.h>
 
 #include <arpa/inet.h>
@@ -86,9 +88,11 @@ public:
     /// packets has been received from the fuzzer.  After Kea exits, the fuzzer
     /// will restart it.
     ///
+    /// @param ipversion Either 4 or 6 depending on what IP version the
+    ///                  server responds to.
     /// @param shutdown Pointer to boolean flag that will be set to true to
     ///        trigger the shutdown procedure.
-    static void init(volatile bool* shutdown);
+    static void init(int ipversion, volatile bool* shutdown);
 
     /// @brief Main Kea Fuzzing Function
     ///
@@ -120,17 +124,29 @@ public:
     /// thread to exit before allowing the shutdown to continue.
     static void packetProcessed(void);
 
+    /// @brief Populate address structures
+    ///
+    /// Decodes the environment variables used to pass address/port information
+    /// to the program and sets up the appropriate address structures.
+    ///
+    /// @param ipversion Either 4 or 6 depending on which IP version address
+    ///                  is expected.
+    ///
+    /// @throws FuzzInitFail Thrown if the address is not in the expected
+    ///                      format.
+    static void setAddress(int ipversion);
+
     /// @brief size of the buffer used to transfer data between AFL and Kea.
     static constexpr size_t BUFFER_SIZE = 65536;
 
-    // @brief Delay before rereading if read from stdin returns an error (us)
+    /// @brief Delay before rereading if read from stdin returns an error (us)
     static constexpr  useconds_t SLEEP_INTERVAL = 50000;
 
     /// @brief Number of many packets Kea will process until shutting down.
     ///
-    /// After the shutdown, AFL ill restart it. This safety switch is here for
-    ///  eliminating cases where Kea goes into a weird state and stops
-    ///  processing packets properly.
+    /// After the shutdown, AFL will restart it. This safety switch is here for
+    /// eliminating cases where Kea goes into a weird state and stops
+    /// processing packets properly.
     static constexpr long LOOP_COUNT = 1000;
 
     // Condition/mutext variables.  The fuzz_XX_ variables are set by the
@@ -141,7 +157,10 @@ public:
 
     // Other member variables.
     static std::thread          fuzzing_thread_;//< Holds the thread ID
-    static struct sockaddr_in6  servaddr_;      //< Address information
+    static struct sockaddr*     sockaddr_ptr;   //< Pointer to structure used
+    static size_t               sockaddr_len;   //< Length of the structure
+    static struct sockaddr_in   servaddr4_;     //< IPv6 address information
+    static struct sockaddr_in6  servaddr6_;     //< IPv6 address information
     static volatile bool*       shutdown_ptr_;  //< Pointer to shutdown flag
 };
 
@@ -155,4 +174,6 @@ public:
 
 }
 
+#endif // ENABLE_AFL
+
 #endif // FUZZ_H
index 218f60e79d59eabb581933991a90a436dfb02e1e..ff4bbd6d40aa27e10457d62a3a7deba0ffcca9e9 100644 (file)
@@ -14,11 +14,15 @@ the fuzzer via stdin
 An informational message output during fuzzing initialization, this reports
 the details of the interface to be used for fuzzing.
 
+% FUZZ_INIT_COMPLETE fuzz initialization complete
+A debug message output when the fuzzing initialization function has completed
+successfully.
+
 % FUZZ_INIT_FAIL fuzz initialization failure, reason: %1
 An error message reported if the fuzzing initialization failed.  The reason
 for the failure is given in the message.
 
-% FUZZ_LOOP_EXIT main loop has exited, shutting down Kea
+% FUZZ_LOOP_EXIT fuzzing loop has exited, shutting down Kea
 This debug message is output when Kea has processed the number of packets
 given by the hard-coded variable Fuzz::LOOP_COUNT.  Kea is shutting
 itself down and will be restarted by AFL.  This is recommended by the AFL