From: Stephen Morris Date: Thu, 13 Jun 2019 09:56:57 +0000 (+0100) Subject: [#640,!351] Extended fuzzing to kea-dhcp4. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7338b4511ff14590e3f9017815c07a44f1e8da4;p=thirdparty%2Fkea.git [#640,!351] Extended fuzzing to kea-dhcp4. --- diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index f658489a56..132e498147 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index fb3fe6b43e..760a39944f 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -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. diff --git a/src/lib/dhcpsrv/fuzz.cc b/src/lib/dhcpsrv/fuzz.cc index a03a8fcc7a..1be2394b5f 100644 --- a/src/lib/dhcpsrv/fuzz.cc +++ b/src/lib/dhcpsrv/fuzz.cc @@ -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(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(&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(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(&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); diff --git a/src/lib/dhcpsrv/fuzz.h b/src/lib/dhcpsrv/fuzz.h index bc91298f8b..b74d2eb65f 100644 --- a/src/lib/dhcpsrv/fuzz.h +++ b/src/lib/dhcpsrv/fuzz.h @@ -7,6 +7,8 @@ #ifndef FUZZ_H #define FUZZ_H +#ifdef ENABLE_AFL + #include #include @@ -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 diff --git a/src/lib/dhcpsrv/fuzz_messages.mes b/src/lib/dhcpsrv/fuzz_messages.mes index 218f60e79d..ff4bbd6d40 100644 --- a/src/lib/dhcpsrv/fuzz_messages.mes +++ b/src/lib/dhcpsrv/fuzz_messages.mes @@ -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