diff options
author | Stephen Morris <stephen@isc.org> | 2019-05-29 10:54:07 +0200 |
---|---|---|
committer | Stephen Morris <stephen@isc.org> | 2019-12-20 18:55:45 +0100 |
commit | 57e98c22f856986969dae82f80343fefe367148a (patch) | |
tree | f458e1586d7f39b137882cd90c7de1dae58869c4 | |
parent | [#640] Initial fuzzing code added for Kea6 (diff) | |
download | kea-57e98c22f856986969dae82f80343fefe367148a.tar.xz kea-57e98c22f856986969dae82f80343fefe367148a.zip |
[#640] Major refactoring of fuzzing code
1. Convert to C++ class.
2. Extend framework to support DHCPv4 fuzzing.
3. Restrict size of data that can be accepted from AFL. (Kea will
only accept up to about 64k or data, (set by the size of a UDP
datagram). However, AFL can send much larger data packets,
which may cause problems in synchronization between the two
threads used to implement fuzzing in Kea.
-rwxr-xr-x | configure.ac | 4 | ||||
-rw-r--r-- | src/bin/dhcp4/dhcp4_srv.cc | 22 | ||||
-rw-r--r-- | src/bin/dhcp6/Makefile.am | 1 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_srv.cc | 36 | ||||
-rw-r--r-- | src/bin/dhcp6/fuzz.cc | 280 | ||||
-rw-r--r-- | src/bin/dhcp6/fuzz.h | 29 | ||||
-rw-r--r-- | src/lib/dhcpsrv/Makefile.am | 17 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz.cc | 339 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz.h | 199 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz_log.cc | 23 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz_log.h | 43 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz_messages.cc | 63 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz_messages.h | 35 | ||||
-rw-r--r-- | src/lib/dhcpsrv/fuzz_messages.mes | 105 |
14 files changed, 865 insertions, 331 deletions
diff --git a/configure.ac b/configure.ac index 0a8075fe00..6f292d6508 100755 --- a/configure.ac +++ b/configure.ac @@ -1464,10 +1464,10 @@ 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) +AM_CONDITIONAL(ENABLE_AFL, test x$enable_fuzz != xno) if test "x$enable_fuzz" != "xno" ; then - AC_DEFINE([FUZZ], [1], [AFL fuzzing was enabled.]) + AC_DEFINE([ENABLE_AFL], [1], [AFL fuzzing was enabled.]) fi diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 2ba43def3c..d0a45aa7d1 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -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> @@ -771,6 +772,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 by a call to the packetProcessed() method. + Fuzz fuzz_controller(4, &shutdown_); + // + // The next line is needed as a signature for AFL to recognise that we are + // running persistent fuzzing. This has to be in the main image file. + __AFL_LOOP(0); +#endif // ENABLE_AFL while (!shutdown_) { try { run_one(); @@ -786,6 +802,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_controller.packetProcessed(); +#endif // ENABLE_AFL } return (true); diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 8ad0cfb519..6ab6b5b684 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -38,7 +38,6 @@ 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 -libdhcp6_la_SOURCES += fuzz.cc sbin_PROGRAMS = kea-dhcp6 diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 27b46277b2..4d28f364c1 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -13,6 +13,7 @@ #include <dhcp/docsis3_option_defs.h> #include <dhcp/duid.h> #include <dhcp/duid_factory.h> +#include <dhcpsrv/fuzz.h> #include <dhcp/iface_mgr.h> #include <dhcp/libdhcp++.h> #include <dhcp/option6_addrlst.h> @@ -65,8 +66,6 @@ #endif #include <dhcpsrv/memfile_lease_mgr.h> -#include <dhcp6/fuzz.h> - #include <boost/bind.hpp> #include <boost/foreach.hpp> #include <boost/tokenizer.hpp> @@ -444,18 +443,21 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt, } bool Dhcpv6Srv::run() { - -#ifdef FUZZ +#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 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(&shutdown_); -#endif /* FUZZ */ + // 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 by a call to the packetProcessed() method. + Fuzz fuzz_controller(6, &shutdown_); + // + // The next line is needed as a signature for AFL to recognise that we are + // running persistent fuzzing. This has to be in the main image file. + __AFL_LOOP(0); +#endif // ENABLE_AFL while (!shutdown_) { try { @@ -473,11 +475,11 @@ bool Dhcpv6Srv::run() { 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 +#ifdef ENABLE_AFL + // Ok, this particular packet processing is done. If we are fuzzing, + // let AFL know about it. + fuzz_controller.packetProcessed(); +#endif // ENABLE_AFL } return (true); diff --git a/src/bin/dhcp6/fuzz.cc b/src/bin/dhcp6/fuzz.cc deleted file mode 100644 index a2f50ad32b..0000000000 --- a/src/bin/dhcp6/fuzz.cc +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 <fstream> -#include <ctime> - -#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 - -/// This is how many packets Kea will process until shutting itself down. -/// AFL should restart it. This safety switch is here for eliminating cases -/// where Kea goes into a weird state and stops processing packets properly. -const unsigned int LOOP_COUNT = 100000; - -/// This mechanism limits down the number of logs this harness prints. -/// E.g. when set to 100, it will print a message every 100 packets. -const unsigned int PRINT_EVERY = 5; - -/// This is the place where the harness log message will be printed. -const std::string PRINT_LOG("/tmp/kea-fuzz-harness.txt"); - -/* - * 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; - -static volatile bool * shutdown_reference = NULL; - -void kea_shutdown(void) { - if (shutdown_reference) { - // do we have the reference to shutdown flag from Dhcp6Srv? - // If yes, then let's set it to true. Kea will shutdown on - // its own. - *shutdown_reference = true; - } else { - // We don't have the pointer yet. Let's terminate abruptly. - exit(EXIT_SUCCESS); - } -} - - -// 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 *) { - 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"); - - ofstream f(PRINT_LOG.c_str(), ios::ate); - - 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()); - - f << "Kea AFL setup:" << endl; - f << "Interface: " << iface << endl; - f << "Interface index: " << iface_id << endl; - f << "UDP destination addr: " << dst << endl; - f << "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) { - f << "Error: inet_pton() failed: can't convert " << dst - << " to address." << endl; - exit(EXIT_FAILURE); - } - servaddr.sin6_port = htons(atoi(port.c_str())); - servaddr.sin6_scope_id = iface_id; - - sockfd = socket(AF_INET6, SOCK_DGRAM, 0); - if (sockfd < 0) { - f << "Failed to create UDP6 socket" << endl; - exit(EXIT_FAILURE); - } - - buf = malloc(65536); - if (!buf) { - f << "Failed to allocate a buffer" << endl; - exit(EXIT_FAILURE); - } - - time_t t; - - loop = LOOP_COUNT; - 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) { - f << "#### Failed to lock mutex" << endl; - abort(); - } - - ready = false; - - ssize_t sent; - - t = time(0); - struct tm * now = localtime(&t); - - if (! (loop%PRINT_EVERY)) { - f << (now->tm_year + 1900) << "-" << (now->tm_mon + 1) << "-" << (now->tm_mday) - << " " << (now->tm_hour) << ":" << (now->tm_min) << ":" << (now->tm_sec) - << " Sending " << length << " bytes to " << dst << "/" << port - << " over " << iface << "/" << iface_id << ", loop iteration << " - << loop << endl; - } - - sent = sendto(sockfd, buf, length, 0, - (struct sockaddr *) &servaddr, sizeof(servaddr)); - if (sent != length) { - f << "#### Error: expected to send " << length - << ", but really sent " << sent << endl; - f << "#### errno=" << errno << endl; - } - - /* unclog */ - recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL); - - while (!ready) - pthread_cond_wait(&cond, &mutex); - - if (pthread_mutex_unlock(&mutex) != 0) { - f << "#### Failed to unlock mutex" << endl; - abort(); - } - } - - f << LOOP_COUNT << " packets processed, terminating." << endl; - f.close(); - - free(buf); - close(sockfd); - - // @todo: shutdown kea - // ns_server_flushonshutdown(ns_g_server, ISC_FALSE); - // isc_app_shutdown(); - kea_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 - if (getenv("AFL_CMIN")) { - kea_shutdown(); - return; - } - - raise(SIGSTOP); - - if (pthread_mutex_lock(&mutex) != 0) { - cerr << "#### unable to lock mutex" << endl; - abort(); - } - - ready = true; - - if (pthread_cond_signal(&cond) != 0) { - cerr << "#### unable to cond signal" << endl; - abort(); - } - - if (pthread_mutex_unlock(&mutex) != 0) { - cerr << "Unable to unlock mutex" << endl; - abort(); - } -#endif /* ENABLE_AFL */ -} - -void -kea_fuzz_setup(volatile bool* shutdown) { -#ifdef ENABLE_AFL - - shutdown_reference = shutdown; - - /// @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) { - cerr << "#### unable to init mutex" << endl; - abort(); - } - - if (pthread_cond_init(&cond, NULL) != 0) { - cerr << "#### unable to init condition variable" << endl; - abort(); - } - - if (pthread_create(&thread, NULL, kea_main_client, NULL) != 0) { - cerr << "#### unable to create fuzz thread" << endl; - abort(); - } - } - -#endif /* ENABLE_AFL */ -} diff --git a/src/bin/dhcp6/fuzz.h b/src/bin/dhcp6/fuzz.h deleted file mode 100644 index 98d9cdf047..0000000000 --- a/src/bin/dhcp6/fuzz.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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); - -/// @brief Sets up Kea fuzzing -/// -/// @param shutdown pointer to boolean flag that will be set to true to -/// trigger shutdown procedure -/// -/// This takes one parameter, which is a pointer to shutdown flag, -/// which should point to instance of Dhcp6Srv::shutdown_. Kea runs -/// until something sets this flag to true, which is an indication to -/// start shutdown procedure. -void kea_fuzz_setup(volatile bool * shutdown); - -}; - -#endif /* KEA_FUZZ_H */ diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 8657a6d0aa..427dd3a2b5 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -180,6 +180,12 @@ libkea_dhcpsrv_la_SOURCES += parsers/simple_parser4.h libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.cc libkea_dhcpsrv_la_SOURCES += parsers/simple_parser6.h +if ENABLE_AFL +libkea_dhcpsrv_la_SOURCES += fuzz.cc fuzz.h +libkea_dhcpsrv_la_SOURCES += fuzz_log.cc fuzz_log.h +libkea_dhcpsrv_la_SOURCES += fuzz_messages.cc fuzz_messages.h +endif + libkea_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS) libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) libkea_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/eval/libkea-eval.la @@ -226,6 +232,7 @@ endif EXTRA_DIST += alloc_engine_messages.mes EXTRA_DIST += dhcpsrv_messages.mes EXTRA_DIST += hosts_messages.mes +EXTRA_DIST += fuzz_messages.mes # If we want to get rid of all generated messages files, we need to use # make maintainer-clean. The proper way to introduce custom commands for @@ -237,6 +244,7 @@ maintainer-clean-local: rm -f alloc_engine_messages.h alloc_engine_messages.cc rm -f dhcpsrv_messages.h dhcpsrv_messages.cc rm -f hosts_messages.h hosts_messages.cc + rm -f fuzz_messages.h fuzz_messages.cc # To regenerate messages files, one can do: # @@ -251,7 +259,8 @@ if GENERATE_MESSAGES # Define rule to build logging source files from message file messages: alloc_engine_messages.h alloc_engine_messages.cc \ dhcpsrv_messages.h dhcpsrv_messages.cc \ - hosts_messages.h hosts_messages.cc + hosts_messages.h hosts_messages.cc \ + fuzz_messages.h fuzz_messages.cc @echo Message files regenerated alloc_engine_messages.h alloc_engine_messages.cc: alloc_engine_messages.mes @@ -263,11 +272,15 @@ dhcpsrv_messages.h dhcpsrv_messages.cc: dhcpsrv_messages.mes hosts_messages.h hosts_messages.cc: hosts_messages.mes $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes +fuzz_messages.h fuzz_messages.cc: fuzz_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/dhcpsrv/fuzz_messages.mes + else messages: alloc_engine_messages.h alloc_engine_messages.cc \ dhcpsrv_messages.h dhcpsrv_messages.cc \ - hosts_messages.h hosts_messages.cc + hosts_messages.h hosts_messages.cc \ + fuzz_messages.h fuzz_messages.cc @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. endif diff --git a/src/lib/dhcpsrv/fuzz.cc b/src/lib/dhcpsrv/fuzz.cc new file mode 100644 index 0000000000..cb28389de4 --- /dev/null +++ b/src/lib/dhcpsrv/fuzz.cc @@ -0,0 +1,339 @@ +// 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> + +#ifdef ENABLE_AFL + +#ifndef __AFL_LOOP +#error To use American Fuzzy Lop you have to set CXX to afl-clang-fast++ +#endif + +#include <dhcp/dhcp6.h> +#include <dhcpsrv/fuzz.h> +#include <dhcpsrv/fuzz_log.h> + +#include <boost/lexical_cast.hpp> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include <iostream> +#include <sstream> +#include <fstream> +#include <ctime> + +using namespace isc; +using namespace isc::dhcp; +using namespace std; + +// Constants defined in the Fuzz class definition. +constexpr size_t Fuzz::BUFFER_SIZE; +constexpr size_t Fuzz::MAX_SEND_SIZE; +constexpr useconds_t Fuzz::SLEEP_INTERVAL; +constexpr long Fuzz::LOOP_COUNT; + +// FuzzSync methods. FuzSynch is the class that encapsulates the +// synchronization process between the main and fuzzing threads. + +// Constructor +FuzzSync::FuzzSync(const char* name) : ready_(false), name_(name) { +} + +// Wait to be notified when the predicate is true +void +FuzzSync::wait(void) { + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_WAITING).arg(name_); + unique_lock<mutex> lock(mutex_); + cond_.wait(lock, [=]() { return this->ready_; }); + ready_ = false; + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_WAITED).arg(name_); +} + +// Set predicate and notify the waiting thread to continue +void +FuzzSync::notify(void) { + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SETTING).arg(name_); + unique_lock<mutex> lock(mutex_); + ready_ = true; + cond_.notify_all(); + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SET).arg(name_); +} + +// Fuzz methods. + +// Constructor +Fuzz::Fuzz(int ipversion, volatile bool* shutdown) : + fuzz_sync_("fuzz_sync"), main_sync_("main_sync"), address_(nullptr), + interface_(nullptr), loop_max_(LOOP_COUNT), port_(0), running_(false), + sockaddr_ptr_(nullptr), sockaddr_len_(0), shutdown_ptr_(nullptr) { + + try { + stringstream reason; // Used to construct 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) { + shutdown_ptr_ = shutdown; + } else { + isc_throw(FuzzInitFail, "must pass shutdown flag to kea_fuzz_init"); + } + + // Set up address structures. + setAddress(ipversion); + + // Check if the hard-coded maximum loop count is being overridden + const char *loop_max_ptr = getenv("FUZZ_AFL_LOOP_MAX"); + if (loop_max_ptr != 0) { + try { + loop_max_ = boost::lexical_cast<long>(loop_max_ptr); + } catch (const boost::bad_lexical_cast&) { + reason << "cannot convert port number specification " + << loop_max_ptr << " to an integer"; + isc_throw(FuzzInitFail, reason.str()); + } + + if (loop_max_ <= 0) { + reason << "FUZZ_AFL_LOOP_MAX is " << loop_max_ << ". " + << "It must be an integer greater than zero."; + isc_throw(FuzzInitFail, reason.str()); + } + } + + // Start the thread that reads the packets sent by AFL from stdin and + // passes them to the port on which Kea is listening. + fuzzing_thread_ = std::thread(&Fuzz::run, this); + + // Wait for the fuzzing thread to read its first packet from AFL and + // send it to the port on which Kea is listening. + fuzz_sync_.wait(); + + } catch (const FuzzInitFail& e) { + // AFL tends to make it difficult to find out what exactly has failed: + // make sure that the error is logged. + LOG_FATAL(fuzz_logger, FUZZ_INIT_FAIL).arg(e.what()); + throw; + } + + LOG_INFO(fuzz_logger, FUZZ_INIT_COMPLETE).arg(interface_).arg(address_) + .arg(port_).arg(loop_max_); +} + +// Destructor +Fuzz::~Fuzz() { + // The fuzzing thread should not be running when the fuzzing object + // goes out of scope. + if (running_) { + LOG_ERROR(fuzz_logger, FUZZ_THREAD_NOT_TERMINATED); + } +} + +// Parse IP address/port/interface and set up address structures. +void +Fuzz::setAddress(int ipversion) { + stringstream reason; // Used in error messages + + // Get the environment for the fuzzing: interface, address and port. + interface_ = getenv("FUZZ_AFL_INTERFACE"); + if (! interface_) { + isc_throw(FuzzInitFail, "no fuzzing interface has been set"); + } + + // Now the address. + address_ = getenv("FUZZ_AFL_ADDRESS"); + if (address_ == 0) { + isc_throw(FuzzInitFail, "no fuzzing address has been set"); + } + + // ... and the port. + const char *port_ptr = getenv("FUZZ_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()); + } + + // 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_)); + + 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_); + + sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr4_); + sockaddr_len_ = sizeof(servaddr4_); + + } else if ((strstr(address_, ":") != NULL) && (ipversion == 6)) { + + // Set up the IPv6 address structure. + 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_ << " 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(interface_); + if (servaddr6_.sin6_scope_id == 0) { + reason << "error retrieving interface ID for " + << interface_ << ": " << strerror(errno); + isc_throw(FuzzInitFail, reason.str()); + } + + 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()); + } + +} + + +// 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. +// It then waits for the main thread to process the packet, the completion of +// that task being signalled by the main thread calling Fuzz::packetProcessed(). +void +Fuzz::run(void) { + running_ = true; + + // Create the socket throw which packets read from stdin will be send + // to the port on which Kea is listening. + int sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sockfd < 0) { + LOG_FATAL(fuzz_logger, FUZZ_SOCKET_CREATE_FAIL).arg(strerror(errno)); + return; + } + + // Main loop. This runs for a fixed number of iterations, after which + // Kea will be terminated and AFL will restart it. The counting of loop + // iterations is done here with a separate variable (instead of inside + // inside the read loop in the server process using __AFL_LOOP) to ensure + // that thread running this function shuts down properly between each + // restart of Kea. + auto loop = loop_max_; + while (loop-- > 0) { + // Read from stdin and continue reading (albeit after a pause) even + // if there is an error. Do the same if an EOF is received. + char buf[BUFFER_SIZE]; + ssize_t length = read(0, buf, sizeof(buf)); + if (length <= 0) { + // Don't log EOFs received - they may be generated by AFL + if (length != 0) { + LOG_ERROR(fuzz_logger, FUZZ_READ_FAIL).arg(strerror(errno)); + } + usleep(SLEEP_INTERVAL); + continue; + } + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_DATA_READ) + .arg(length); + + // Now send the data to the UDP port on which Kea is listening. + // + // The condition variables synchronize the operation: this thread + // will read from stdin and write to the socket. It then blocks until + // the main thread has processed the packet, at which point it can read + // more data from stdin. + // + // Synchronization is required because although the read from stdin is + // blocking, there is no blocking on the sending of data to the port + // from which Kea is reading. It is quite possible to lose packets, + // and AFL seems to get confused in this case. At any rate, without + // some form of synchronization, this approach does not work. + + // Send the data to the main Kea thread. Limit the size of the + // packets that can be sent. + size_t send_len = (length < MAX_SEND_SIZE) ? length : MAX_SEND_SIZE; + ssize_t sent = sendto(sockfd, buf, send_len, 0, sockaddr_ptr_, + sockaddr_len_); + if (sent < 0) { + // TODO: If we get here, we may well hang: AFL has sent us a + // packet but by continuing, we are not letting Kea process it + // and trigger AFL to send another. For the time being, we + // are restricting the size of packets Kea can send us. + LOG_ERROR(fuzz_logger, FUZZ_SEND_ERROR).arg(strerror(errno)); + continue; + } else if (sent != length) { + LOG_WARN(fuzz_logger, FUZZ_SHORT_SEND).arg(length).arg(sent); + } else { + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SEND).arg(sent); + } + + if (loop <= 0) { + // If this is the last loop iteration, close everything down. + // This is done before giving permission for the main thread + // to run to avoid a race condition. + *shutdown_ptr_ = true; + close(sockfd); + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_SHUTDOWN_INITIATED); + } + + // Tell the main thread to run. + fuzz_sync_.notify(); + + // We now need to synchronize with the main thread. In particular, + // we suspend processing until we know that the processing of the + // packet by Kea has finished and that the completion function has + // raised a SIGSTOP. + main_sync_.wait(); + } + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_LOOP_EXIT); + + // If the main thread is waiting, let it terminate as well. + fuzz_sync_.notify(); + + running_ = false; + + return; +} + +// Called by the main thread, this notifies AFL that processing for the +// last packet has finished. +void +Fuzz::packetProcessed(void) { + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_PACKET_PROCESSED_CALLED); + + // Tell AFL that the processing for this packet has finished. + raise(SIGSTOP); + + // Tell the fuzzing loop that it can continue and wait until it tells + // us that the main thread can continue. + main_sync_.notify(); + fuzz_sync_.wait(); + + // If the fuzzing thread is shutting down, wait for it to terminate. + if (*shutdown_ptr_) { + // We shouldn't need to notify it to continue (the previous call in + // this method should have done that), but it does no harm to be sure. + main_sync_.notify(); + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_THREAD_WAIT); + fuzzing_thread_.join(); + LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE, FUZZ_THREAD_TERMINATED); + } +} + +#endif // ENABLE_AFL diff --git a/src/lib/dhcpsrv/fuzz.h b/src/lib/dhcpsrv/fuzz.h new file mode 100644 index 0000000000..bdcbaf0a09 --- /dev/null +++ b/src/lib/dhcpsrv/fuzz.h @@ -0,0 +1,199 @@ +// 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 FUZZ_H +#define FUZZ_H + +#ifdef ENABLE_AFL + +#include <exceptions/exceptions.h> + +#include <arpa/inet.h> +#include <net/if.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <condition_variable> +#include <mutex> +#include <string> +#include <thread> + +namespace isc { + +/// @brief Helper class to manage synchronization between fuzzing threads +/// +/// This contains the variables and encapsulates the primitives required +/// to manage the condition variables between the two threads. + +class FuzzSync { +public: + /// @brief Constructor + /// + /// Just set the name of the variable for debug message purposes. + /// + /// @param name The name of the object, output in debug messages. + FuzzSync(const char* name); + + /// @brief Waits for condition notification + /// + /// Called by a thread, this function will wait for another thread to + /// notify it that it can proceed: in other words, this function calls + /// <condition_variable>.wait() and waits for the other to call + /// <condition_variable>.notify(). + /// + /// As it is possible to miss a notification - if one thread reaches the + /// notification point before the other thread reaches the wait point - + /// the operation is mediated by a predicate (in this case, a boolean + /// variable). If this is set when the waiting thread reaches the wait + /// point, the thread does not wait. If it is not set, the thread will + /// wait until it is notified through the condition variable. At this + /// point, if the variable is still not set, the thread will re-enter the + /// wait state. + /// + /// In both cases, the predicate variable is cleared on exit. + void wait(void); + + /// @brief Notifies other thread to continue + /// + /// Called by a thread, this function will notify another thread that is + /// waiting on the condition variable that it can continue. As noted + /// in the documentation for wait(), the operation is mediated by a + /// predicate variable; in this case, the variable is explicitly set + /// before the notification is sent. + void notify(void); + +private: + std::condition_variable cond_; + std::mutex mutex_; + volatile bool ready_; + std::string name_; +}; + + +/// @brief AFL Fuzzing Functions + +class Fuzz { +public: + /// @brief Constructor + /// + /// Initializes member variables. + Fuzz(); + + /// @brief Constructor + /// + /// Initializes fuzzing object and starts the fuzzing thread. + /// + /// @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. + Fuzz(int ipversion, volatile bool* shutdown); + + /// @brief Destructor + /// + /// Does some basic checks when going out of scope. The purpose of these + /// checks is to aid diagnosis in the event of problems with the fuzzing. + ~Fuzz(); + + /// @brief Main Kea Fuzzing Function + /// + /// This is the main Kea fuzzing method. It is the entry point for the + /// thread that handles the interface between AFL and Kea. The method + /// receives data from the fuzzing engine via stdin, and then sends it to + /// the configured UDP socket. The main thread of Kea reads it from there, + /// processes it and when processing is complete, calls the + /// packetProcessed() method to notify the fuzzing thread that processing + /// of the packet is complete. + /// + /// After a given number of packets, this method will set the flag shut + /// down Kea. This is recommended by the AFL documentation as it avoids + /// any resource leaks (which are not caught by AFL) from getting too large + /// and interfering with the fuzzing. AFL will automatically restart the + /// program to continue fuzzing. + void run(void); + + /// @brief Notify fuzzing thread that processing is complete + /// + /// This function is called by the Kea processing loop running in the main + /// thread when it has finished processing a packet. It raises a SIGSTOP + /// signal, which tells the AFL fuzzer that processing for the data it has + /// just sent has finished; this causes it to send another fuzzed packet + /// to stdin. It also sets a condition variable, so releasing the fuzzing + /// thread to read the next data from AFL. + /// + /// If a shutdown has been initiated, this method waits for the fuzzing + /// thread to exit before allowing the shutdown to continue. + 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. + void setAddress(int ipversion); + + /// @brief size of the buffer used to transfer data between AFL and Kea. + /// + /// This is much larger than the data that will be sent to Kea (so AFL + /// data being trimmed). However, it does allow for AFL to send quite + /// large packets without resulting in timeouts or synchronization + /// problems with the fuzzing thread. + static constexpr size_t BUFFER_SIZE = 128000; + + /// @brief maximum size of packets fuzzing thread will send to Kea + /// + /// This is below the maximum size of data that can be put into a + /// single UDP datagram. + static constexpr size_t MAX_SEND_SIZE = 64000; + + /// @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 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/mutex variables. The fuzz_XX_ variables are set by the + // fuzzing thread and waited on by the main thread. The main_XX_ variables + // are set by the main thread and waited on by the fuzzing thread. + FuzzSync fuzz_sync_; // Set by fuzzing thread + FuzzSync main_sync_; // Set by main thread + + // Other member variables. + const char* address_; //< Pointer to address string + std::thread fuzzing_thread_;//< Holds the thread ID + const char* interface_; //< Pointer to interface string + long loop_max_; //< Maximum number of loop iterations + uint16_t port_; //< Port number to use + volatile bool running_; //< Set if the thread is running + struct sockaddr* sockaddr_ptr_; //< Pointer to structure used + size_t sockaddr_len_; //< Length of the structure + struct sockaddr_in servaddr4_; //< IPv6 address information + struct sockaddr_in6 servaddr6_; //< IPv6 address information + volatile bool* shutdown_ptr_; //< Pointer to shutdown flag +}; + + +/// @brief Exception thrown if fuzzing initialization fails. +class FuzzInitFail : public Exception { +public: + FuzzInitFail(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +} + +#endif // ENABLE_AFL + +#endif // FUZZ_H diff --git a/src/lib/dhcpsrv/fuzz_log.cc b/src/lib/dhcpsrv/fuzz_log.cc new file mode 100644 index 0000000000..e214aa0a2d --- /dev/null +++ b/src/lib/dhcpsrv/fuzz_log.cc @@ -0,0 +1,23 @@ +// Copyright (C) 2015-2018 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/. + +/// @brief Defines the logger used by the @c isc::dhcp::HostMgr + +#include <config.h> + +#include "dhcpsrv/fuzz_log.h" + +namespace isc { +namespace dhcp { + +extern const int FUZZ_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +extern const int FUZZ_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; + +isc::log::Logger fuzz_logger("fuzz"); + +} // namespace dhcp +} // namespace isc + diff --git a/src/lib/dhcpsrv/fuzz_log.h b/src/lib/dhcpsrv/fuzz_log.h new file mode 100644 index 0000000000..89a3820d2d --- /dev/null +++ b/src/lib/dhcpsrv/fuzz_log.h @@ -0,0 +1,43 @@ +// Copyright (C) 2015-2018 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 FUZZ_LOG_H +#define FUZZ_LOG_H + +#include <dhcpsrv/fuzz_messages.h> +#include <log/macros.h> + +namespace isc { +namespace dhcp { + +///@{ +/// \brief Logging levels for fuzzing output +/// +/// Defines the levels used to output debug messages during fuzzing. + +/// @brief Traces normal operations +/// +/// An example of the normal operation is a report of a packet being received +/// from the fuzzer. +extern const int FUZZ_DBG_TRACE; + +/// @brief Record detailed traces +/// +/// Messages logged at this level will log detailed tracing information. +extern const int FUZZ_DBG_TRACE_DETAIL; + +///@} + +/// @brief Logger for the @c HostMgr and the code it calls. +/// +/// Define the logger used to log messages in @c HostMgr and the code it +/// calls to manage host reservations. +extern isc::log::Logger fuzz_logger; + +} // namespace dhcp +} // namespace isc + +#endif // FUZZ_LOG_H diff --git a/src/lib/dhcpsrv/fuzz_messages.cc b/src/lib/dhcpsrv/fuzz_messages.cc new file mode 100644 index 0000000000..65dfe4de2d --- /dev/null +++ b/src/lib/dhcpsrv/fuzz_messages.cc @@ -0,0 +1,63 @@ +// File created from ../../../src/lib/dhcpsrv/fuzz_messages.mes on Sun Jun 16 2019 18:13 + +#include <cstddef> +#include <log/message_types.h> +#include <log/message_initializer.h> + +namespace isc { +namespace dhcp { + +extern const isc::log::MessageID FUZZ_DATA_READ = "FUZZ_DATA_READ"; +extern const isc::log::MessageID FUZZ_INIT_COMPLETE = "FUZZ_INIT_COMPLETE"; +extern const isc::log::MessageID FUZZ_INIT_FAIL = "FUZZ_INIT_FAIL"; +extern const isc::log::MessageID FUZZ_INTERFACE = "FUZZ_INTERFACE"; +extern const isc::log::MessageID FUZZ_LOOP_EXIT = "FUZZ_LOOP_EXIT"; +extern const isc::log::MessageID FUZZ_LOOP_MAX = "FUZZ_LOOP_MAX"; +extern const isc::log::MessageID FUZZ_PACKET_PROCESSED_CALLED = "FUZZ_PACKET_PROCESSED_CALLED"; +extern const isc::log::MessageID FUZZ_READ_FAIL = "FUZZ_READ_FAIL"; +extern const isc::log::MessageID FUZZ_SEND = "FUZZ_SEND"; +extern const isc::log::MessageID FUZZ_SEND_ERROR = "FUZZ_SEND_ERROR"; +extern const isc::log::MessageID FUZZ_SET = "FUZZ_SET"; +extern const isc::log::MessageID FUZZ_SETTING = "FUZZ_SETTING"; +extern const isc::log::MessageID FUZZ_SHORT_SEND = "FUZZ_SHORT_SEND"; +extern const isc::log::MessageID FUZZ_SHUTDOWN_INITIATED = "FUZZ_SHUTDOWN_INITIATED"; +extern const isc::log::MessageID FUZZ_SOCKET_CREATE_FAIL = "FUZZ_SOCKET_CREATE_FAIL"; +extern const isc::log::MessageID FUZZ_THREAD_NOT_TERMINATED = "FUZZ_THREAD_NOT_TERMINATED"; +extern const isc::log::MessageID FUZZ_THREAD_TERMINATED = "FUZZ_THREAD_TERMINATED"; +extern const isc::log::MessageID FUZZ_THREAD_WAIT = "FUZZ_THREAD_WAIT"; +extern const isc::log::MessageID FUZZ_WAITED = "FUZZ_WAITED"; +extern const isc::log::MessageID FUZZ_WAITING = "FUZZ_WAITING"; + +} // namespace dhcp +} // namespace isc + +namespace { + +const char* values[] = { + "FUZZ_DATA_READ", "read %1 byte(s) from AFL via stdin", + "FUZZ_INIT_COMPLETE", "fuzz initialization complete: interface %1, address %2, port %3, max loops %4", + "FUZZ_INIT_FAIL", "fuzz initialization failure, reason: %1", + "FUZZ_INTERFACE", "fuzzing will use interface %1 (address %2, port %3)", + "FUZZ_LOOP_EXIT", "fuzzing loop has exited, shutting down Kea", + "FUZZ_LOOP_MAX", "fuzzing loop will run %1 time(s) before exiting", + "FUZZ_PACKET_PROCESSED_CALLED", "packetProcessed has been called", + "FUZZ_READ_FAIL", "error reading input from fuzzer: %1", + "FUZZ_SEND", "sent %1 byte(s) to the socket connected to the Kea interface", + "FUZZ_SEND_ERROR", "failed to send data to Kea input socket: %1", + "FUZZ_SET", "successfully set %1 condition variable", + "FUZZ_SETTING", "setting %1 condition variable", + "FUZZ_SHORT_SEND", "expected to send %d bytes to Kea input socket but only sent %2", + "FUZZ_SHUTDOWN_INITIATED", "shutdown initated, shutdown flag is set", + "FUZZ_SOCKET_CREATE_FAIL", "failed to crease socket for use by fuzzing thread: %1", + "FUZZ_THREAD_NOT_TERMINATED", "fuzzing thread has not terminated", + "FUZZ_THREAD_TERMINATED", "fuzzing thread has terminated", + "FUZZ_THREAD_WAIT", "waiting for fuzzing thread to terminate", + "FUZZ_WAITED", "successfully waited for for %1 condition variable to be set", + "FUZZ_WAITING", "waiting for %1 condition variable to be set", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/lib/dhcpsrv/fuzz_messages.h b/src/lib/dhcpsrv/fuzz_messages.h new file mode 100644 index 0000000000..6dea5b1871 --- /dev/null +++ b/src/lib/dhcpsrv/fuzz_messages.h @@ -0,0 +1,35 @@ +// File created from ../../../src/lib/dhcpsrv/fuzz_messages.mes on Sun Jun 16 2019 18:13 + +#ifndef FUZZ_MESSAGES_H +#define FUZZ_MESSAGES_H + +#include <log/message_types.h> + +namespace isc { +namespace dhcp { + +extern const isc::log::MessageID FUZZ_DATA_READ; +extern const isc::log::MessageID FUZZ_INIT_COMPLETE; +extern const isc::log::MessageID FUZZ_INIT_FAIL; +extern const isc::log::MessageID FUZZ_INTERFACE; +extern const isc::log::MessageID FUZZ_LOOP_EXIT; +extern const isc::log::MessageID FUZZ_LOOP_MAX; +extern const isc::log::MessageID FUZZ_PACKET_PROCESSED_CALLED; +extern const isc::log::MessageID FUZZ_READ_FAIL; +extern const isc::log::MessageID FUZZ_SEND; +extern const isc::log::MessageID FUZZ_SEND_ERROR; +extern const isc::log::MessageID FUZZ_SET; +extern const isc::log::MessageID FUZZ_SETTING; +extern const isc::log::MessageID FUZZ_SHORT_SEND; +extern const isc::log::MessageID FUZZ_SHUTDOWN_INITIATED; +extern const isc::log::MessageID FUZZ_SOCKET_CREATE_FAIL; +extern const isc::log::MessageID FUZZ_THREAD_NOT_TERMINATED; +extern const isc::log::MessageID FUZZ_THREAD_TERMINATED; +extern const isc::log::MessageID FUZZ_THREAD_WAIT; +extern const isc::log::MessageID FUZZ_WAITED; +extern const isc::log::MessageID FUZZ_WAITING; + +} // namespace dhcp +} // namespace isc + +#endif // FUZZ_MESSAGES_H diff --git a/src/lib/dhcpsrv/fuzz_messages.mes b/src/lib/dhcpsrv/fuzz_messages.mes new file mode 100644 index 0000000000..7e011f74f9 --- /dev/null +++ b/src/lib/dhcpsrv/fuzz_messages.mes @@ -0,0 +1,105 @@ +# Copyright (C) 2015-2019 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/. + +$NAMESPACE isc::dhcp + +% FUZZ_DATA_READ read %1 byte(s) from AFL via stdin +A debug message output to indicate how much data has been received from +the fuzzer via stdin + +% FUZZ_INTERFACE fuzzing will use interface %1 (address %2, port %3) +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: interface %1, address %2, port %3, max loops %4 +An informational message output when the fuzzing initialization function has +completed successfully. The parameters listed are those which must be/can be +set via environment variables. + +% 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 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 +documentation as a way of avoiding issues if Kea gets itself into a funny +state after running for a long time. + +% FUZZ_LOOP_MAX fuzzing loop will run %1 time(s) before exiting +A debug message noting how many loops (i.e. packets from the fuzzer) the code +will process before Kea exits and the fuzzer restarts it. The hard-coded +value can be overridden by setting the environment variable FUZZ_AFL_LOOP_MAX. + +% FUZZ_PACKET_PROCESSED_CALLED packetProcessed has been called +A debug message indicating that the processing of a packet by Kea has +finished and that the Fuzz::packetProcessed() method has been called. This +raises a SIGSTOP informing AFL that Kea is ready to receive another packet. + +% FUZZ_READ_FAIL error reading input from fuzzer: %1 +This error is reported if the read of data from the fuzzer (which is +received over stdin) fails, or if a read returns zero bytes. If this +occurs, the thread will sleep for a short period before retrying the read. +The message includes the reason for the failure. + +% FUZZ_SEND sent %1 byte(s) to the socket connected to the Kea interface +A debug message stating that the sendto() call in the main fuzzing function +has successfully completed and reporting the number of bytes sent. This +call sends data received from AFL to the port on which Kea is listening. + +% FUZZ_SEND_ERROR failed to send data to Kea input socket: %1 +This error will be reported if the sendto() call in the fuzzing thread (which +sends data received from AFL to the socket on which Kea is listening) fails. +The reason for the failure is given in the message. The fuzzing code will +attempt to continue from this, but it may cause the fuzzing process to fail. + +% FUZZ_SET successfully set %1 condition variable +A debug message stating the named condition variable has been be set. + +% FUZZ_SETTING setting %1 condition variable +A debug message stating the named condition variable is to be set. + +% FUZZ_SHORT_SEND expected to send %d bytes to Kea input socket but only sent %2 +A warning message that is output if the sendto() call (used to send data +from the fuzzing thread to the main Kea processing) did not send as much +data as that read from AFL. This may indicate a problem in the underlying +communications between the fuzzing thread and the main Kea processing. + +% FUZZ_SHUTDOWN_INITIATED shutdown initated, shutdown flag is set +A debug message output when the fuzzing thread has reached the maximum number +of iterations. At this point, the shutdown flag is set, Kea will exit and +the fuzzer will restart it. Periodic shutdowns of the program being fuzzed +are recommended in the AFL documentation as a way of overcoming memory leaks +of odd conditions that a program can get into after an extended period of +running. + +% FUZZ_SOCKET_CREATE_FAIL failed to crease socket for use by fuzzing thread: %1 +An error message output when the fuzzing code has failed to create a socket +through which is will copy data received on stdin from the AFL fuzzer to +the port on which Kea is listening. The program will most likely hang if +this occurs. + +% FUZZ_THREAD_NOT_TERMINATED fuzzing thread has not terminated +An error message output in the destructor of the fuzzing object should the +object go out of scope with the fuzzing thread running. This message is +for diagnosing problems in the fuzzing code. + +% FUZZ_THREAD_TERMINATED fuzzing thread has terminated +A debug message, output when the main thread has detected that the fuzzing +thread has terminated. + +% FUZZ_THREAD_WAIT waiting for fuzzing thread to terminate +A debug message, output when the main thread is waiting for the fuzzing thread +to terminate. + +% FUZZ_WAITED successfully waited for for %1 condition variable to be set +A debug message stating the the condition variable for which the originating +thread was waiting has been set and that the wait has completed. + +% FUZZ_WAITING waiting for %1 condition variable to be set +A debug message stating the condition variable for which the originating +thread is waiting. |