Commit 57e98c22 authored by Stephen Morris's avatar Stephen Morris

[#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.
parent 4939b823
......@@ -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
......
......@@ -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);
......
......@@ -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
......
......@@ -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);
......
/*
* 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 */
}
/*
* 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 */
......@@ -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
......
// 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);