Commit 08381e67 authored by Stephen Morris's avatar Stephen Morris

[#640] Simplification of fuzzing structure

Instead of using a separate thread to read input from the fuzzer,
the input is now read in the main thread and transferred to the
interface on which Kea is expecting it to appear.
parent 57e98c22
...@@ -773,21 +773,18 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) { ...@@ -773,21 +773,18 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
bool bool
Dhcpv4Srv::run() { Dhcpv4Srv::run() {
#ifdef ENABLE_AFL #ifdef ENABLE_AFL
// AFL fuzzing setup initiated here. At this stage, Kea has loaded its // Set up structures needed for fuzzing.
// config, opened sockets, established DB connections, etc. It is truly Fuzz fuzzer(4, server_port_);
// 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 // 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. // running persistent fuzzing. This has to be in the main image file.
__AFL_LOOP(0); while (__AFL_LOOP(fuzzer.maxLoopCount())) {
#endif // ENABLE_AFL // Read from stdin and put the data read into an address/port on which
// Kea is listening, read for Kea to read it via asynchronous I/O.
fuzzer.transfer();
#else
while (!shutdown_) { while (!shutdown_) {
#endif // ENABLE_AFL
try { try {
run_one(); run_one();
getIOService()->poll(); getIOService()->poll();
...@@ -802,12 +799,6 @@ Dhcpv4Srv::run() { ...@@ -802,12 +799,6 @@ Dhcpv4Srv::run() {
// std::exception. // std::exception.
LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_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); return (true);
......
...@@ -444,22 +444,18 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt, ...@@ -444,22 +444,18 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
bool Dhcpv6Srv::run() { bool Dhcpv6Srv::run() {
#ifdef ENABLE_AFL #ifdef ENABLE_AFL
// AFL fuzzing setup initiated here. At this stage, Kea has loaded its // Set up structures needed for fuzzing.
// config, opened sockets, established DB connections, etc. It is truly Fuzz fuzzer(6, server_port_);
// 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 // 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. // running persistent fuzzing. This has to be in the main image file.
__AFL_LOOP(0); while (__AFL_LOOP(fuzzer.maxLoopCount())) {
#endif // ENABLE_AFL // Read from stdin and put the data read into an address/port on which
// Kea is listening, read for Kea to read it via asynchronous I/O.
fuzzer.transfer();
#else
while (!shutdown_) { while (!shutdown_) {
#endif // ENABLE_AFL
try { try {
run_one(); run_one();
getIOService()->poll(); getIOService()->poll();
...@@ -474,12 +470,6 @@ bool Dhcpv6Srv::run() { ...@@ -474,12 +470,6 @@ bool Dhcpv6Srv::run() {
// by more specific catches. // by more specific catches.
LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION); LOG_ERROR(packet6_logger, DHCP6_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); return (true);
......
# This is an example configuration file for DHCPv6 server in Kea.
# It's a basic scenario with one IPv6 subnet configured. It is
# assumed that one subnet (2001:db8:1::/64 is available directly
# over ethX interface.
{ "Dhcp6":
{
# Kea is told to listen on ethX interface only.
"interfaces-config": {
"interfaces": [ "eth0" ]
},
# We need to specify the the database used to store leases. As of
# September 2016, four database backends are supported: MySQL,
# PostgreSQL, Cassandra, and the in-memory database, Memfile.
# We'll use memfile because it doesn't require any prior set up.
"lease-database": {
"type": "memfile"
},
# Addresses will be assigned with preferred and valid lifetimes
# being 3000 and 4000, respectively. Client is told to start
# renewing after 1000 seconds. If the server does not respond
# after 2000 seconds since the lease was granted, client is supposed
# to start REBIND procedure (emergency renewal that allows switching
# to a different server).
"preferred-lifetime": 3000,
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
# The following list defines subnets. Each subnet consists of at
# least subnet and pool entries.
"subnet6": [
{
"pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64",
"interface": "eth0"
}
]
},
# The following configures logging. It assumes that messages with at least
# informational level (info, warn, error and fatal) should be logged to stdout.
"Logging": {
"loggers": [
{
"name": "kea-dhcp6",
"output_options": [
{
"output": "/tmp/kea-fuzz.log"
}
],
"debuglevel": 0,
"severity": "DEBUG"
}
]
}
}
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
...@@ -35,61 +35,31 @@ using namespace std; ...@@ -35,61 +35,31 @@ using namespace std;
// Constants defined in the Fuzz class definition. // Constants defined in the Fuzz class definition.
constexpr size_t Fuzz::BUFFER_SIZE; constexpr size_t Fuzz::BUFFER_SIZE;
constexpr size_t Fuzz::MAX_SEND_SIZE; constexpr size_t Fuzz::MAX_SEND_SIZE;
constexpr useconds_t Fuzz::SLEEP_INTERVAL; constexpr long Fuzz::MAX_LOOP_COUNT;
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 // Constructor
Fuzz::Fuzz(int ipversion, volatile bool* shutdown) : Fuzz::Fuzz(int ipversion, uint16_t port) :
fuzz_sync_("fuzz_sync"), main_sync_("main_sync"), address_(nullptr), address_(nullptr), interface_(nullptr), loop_max_(MAX_LOOP_COUNT),
interface_(nullptr), loop_max_(LOOP_COUNT), port_(0), running_(false), port_(port), sockaddr_len_(0), sockaddr_ptr_(nullptr), sockfd_(-1) {
sockaddr_ptr_(nullptr), sockaddr_len_(0), shutdown_ptr_(nullptr) {
try { try {
stringstream reason; // Used to construct exception messages 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. // Set up address structures.
setAddress(ipversion); setAddress(ipversion);
// Create the socket through which packets read from stdin will be sent
// to the port on which Kea is listening. This is closed in the
// destructor.
sockfd_ = socket((ipversion == 4) ? AF_INET : AF_INET6, SOCK_DGRAM, 0);
if (sockfd_ < 0) {
LOG_FATAL(fuzz_logger, FUZZ_SOCKET_CREATE_FAIL)
.arg(strerror(errno));
return;
}
// Check if the hard-coded maximum loop count is being overridden // Check if the hard-coded maximum loop count is being overridden
const char *loop_max_ptr = getenv("FUZZ_AFL_LOOP_MAX"); const char *loop_max_ptr = getenv("KEA_AFL_LOOP_MAX");
if (loop_max_ptr != 0) { if (loop_max_ptr != 0) {
try { try {
loop_max_ = boost::lexical_cast<long>(loop_max_ptr); loop_max_ = boost::lexical_cast<long>(loop_max_ptr);
...@@ -100,20 +70,12 @@ Fuzz::Fuzz(int ipversion, volatile bool* shutdown) : ...@@ -100,20 +70,12 @@ Fuzz::Fuzz(int ipversion, volatile bool* shutdown) :
} }
if (loop_max_ <= 0) { if (loop_max_ <= 0) {
reason << "FUZZ_AFL_LOOP_MAX is " << loop_max_ << ". " reason << "KEA_AFL_LOOP_MAX is " << loop_max_ << ". "
<< "It must be an integer greater than zero."; << "It must be an integer greater than zero.";
isc_throw(FuzzInitFail, reason.str()); 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) { } catch (const FuzzInitFail& e) {
// AFL tends to make it difficult to find out what exactly has failed: // AFL tends to make it difficult to find out what exactly has failed:
// make sure that the error is logged. // make sure that the error is logged.
...@@ -127,11 +89,7 @@ Fuzz::Fuzz(int ipversion, volatile bool* shutdown) : ...@@ -127,11 +89,7 @@ Fuzz::Fuzz(int ipversion, volatile bool* shutdown) :
// Destructor // Destructor
Fuzz::~Fuzz() { Fuzz::~Fuzz() {
// The fuzzing thread should not be running when the fuzzing object static_cast<void>(close(sockfd_));
// goes out of scope.
if (running_) {
LOG_ERROR(fuzz_logger, FUZZ_THREAD_NOT_TERMINATED);
}
} }
// Parse IP address/port/interface and set up address structures. // Parse IP address/port/interface and set up address structures.
...@@ -140,49 +98,22 @@ Fuzz::setAddress(int ipversion) { ...@@ -140,49 +98,22 @@ Fuzz::setAddress(int ipversion) {
stringstream reason; // Used in error messages stringstream reason; // Used in error messages
// Get the environment for the fuzzing: interface, address and port. // Get the environment for the fuzzing: interface, address and port.
interface_ = getenv("FUZZ_AFL_INTERFACE"); interface_ = getenv("KEA_AFL_INTERFACE");
if (! interface_) { if (! interface_) {
isc_throw(FuzzInitFail, "no fuzzing interface has been set"); isc_throw(FuzzInitFail, "no fuzzing interface has been set");
} }
// Now the address. // Now the address. (The port is specified via the "-p" command-line
address_ = getenv("FUZZ_AFL_ADDRESS"); // switch and passed to this object through the constructor.)
address_ = getenv("KEA_AFL_ADDRESS");
if (address_ == 0) { if (address_ == 0) {
isc_throw(FuzzInitFail, "no fuzzing address has been set"); isc_throw(FuzzInitFail, "no fuzzing address has been set");
} }
// ... and the port. // Set up the appropriate data structure depending on the address given.
const char *port_ptr = getenv("FUZZ_AFL_PORT"); if ((strstr(address_, ":") != NULL) && (ipversion == 6)) {
if (port_ptr == 0) { // Expecting IPv6 and the address contains a colon, so assume it is an
isc_throw(FuzzInitFail, "no fuzzing port has been set"); // an IPv6 address.
}
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_)); memset(&servaddr6_, 0, sizeof (servaddr6_));
servaddr6_.sin6_family = AF_INET6; servaddr6_.sin6_family = AF_INET6;
...@@ -203,6 +134,24 @@ Fuzz::setAddress(int ipversion) { ...@@ -203,6 +134,24 @@ Fuzz::setAddress(int ipversion) {
sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr6_); sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr6_);
sockaddr_len_ = sizeof(servaddr6_); sockaddr_len_ = sizeof(servaddr6_);
} else if ((strstr(address_, ".") != NULL) && (ipversion == 4)) {
// Expecting an IPv4 address and it contains a dot, so assume it is.
// This check is done after the IPv6 check, as it is possible for an
// IPv4 address to be emnbedded in an IPv6 one.
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 { } else {
reason << "Expected IP version (" << ipversion << ") is not " reason << "Expected IP version (" << ipversion << ") is not "
<< "4 or 6, or the given address " << address_ << " does not " << "4 or 6, or the given address " << address_ << " does not "
...@@ -213,127 +162,44 @@ Fuzz::setAddress(int ipversion) { ...@@ -213,127 +162,44 @@ Fuzz::setAddress(int ipversion) {
} }
// This is the main fuzzing function. It receives data from fuzzing engine. // This is the main fuzzing function. It receives data from fuzzing engine over
// That data is received to stdin and then sent over the configured UDP socket. // stdin and then sends it to 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 void
Fuzz::run(void) { Fuzz::transfer(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 // Read from stdin. Just return if nothing is read (or there is an error)
// Kea will be terminated and AFL will restart it. The counting of loop // and hope that this does not cause a hang.
// iterations is done here with a separate variable (instead of inside char buf[BUFFER_SIZE];
// inside the read loop in the server process using __AFL_LOOP) to ensure ssize_t length = read(0, buf, sizeof(buf));
// 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. // Save the errno in case there was an error because if debugging is
// // enabled, the following LOG_DEBUG call may destroy its value.
// The condition variables synchronize the operation: this thread int errnum = errno;
// will read from stdin and write to the socket. It then blocks until LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_DATA_READ)
// the main thread has processed the packet, at which point it can read .arg(length);
// 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.
if (length > 0) {
// Now send the data to the UDP port on which Kea is listening.
// Send the data to the main Kea thread. Limit the size of the // Send the data to the main Kea thread. Limit the size of the
// packets that can be sent. // packets that can be sent.
size_t send_len = (length < MAX_SEND_SIZE) ? length : MAX_SEND_SIZE; size_t send_len = (length < MAX_SEND_SIZE) ? length : MAX_SEND_SIZE;
ssize_t sent = sendto(sockfd, buf, send_len, 0, sockaddr_ptr_, ssize_t sent = sendto(sockfd_, buf, send_len, 0, sockaddr_ptr_,
sockaddr_len_); sockaddr_len_);
if (sent < 0) { if (sent > 0) {
// TODO: If we get here, we may well hang: AFL has sent us a LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SEND).arg(sent);
// 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) { } else if (sent != length) {
LOG_WARN(fuzz_logger, FUZZ_SHORT_SEND).arg(length).arg(sent); LOG_WARN(fuzz_logger, FUZZ_SHORT_SEND).arg(length).arg(sent);
} else { } else {
LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SEND).arg(sent); LOG_ERROR(fuzz_logger, FUZZ_SEND_ERROR).arg(strerror(errno));
} }
} else {
if (loop <= 0) { // Read did not get any bytes. A zero-length read (EOF) may have been
// If this is the last loop iteration, close everything down. // generated by AFL, so don't log that. But otherwise log an error.
// This is done before giving permission for the main thread if (length != 0) {
// to run to avoid a race condition. LOG_ERROR(fuzz_logger, FUZZ_READ_FAIL).arg(strerror(errnum));
*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 #endif // ENABLE_AFL
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") // Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
// //
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
...@@ -22,112 +22,87 @@ ...@@ -22,112 +22,87 @@
#include <thread> #include <thread>
namespace isc { namespace isc {
/// @brief Helper class to manage synchronization between fuzzing threads /// @brief AFL Fuzzing
///
/// Persistent-mode AFL fuzzing has the AFL fuzzer send packets of data to
/// stdin of the program being tested. The program processes the data and
/// signals to AFL that it is complete.
///
/// To reduce the code changes required, the scheme adopted for Kea is that
/// the AFL data read from stdin is written to an address/port on which Kea
/// is listening. Kea then reads the data from that port and processes it
/// in the usual way.
/// ///
/// This contains the variables and encapsulates the primitives required /// The Fuzz class handles the transfer of data between AFL and Kea. After
/// to manage the condition variables between the two threads. /// suitable initialization, its transfer() method is called in the main
/// processing loop, right before Kea waits for input. The method handles the
/// read from stdin and the write to the selected address port.
class FuzzSync { class Fuzz {
public: public:
/// @brief Constructor /// @brief size of the buffer used to transfer data between AFL and Kea.
///
/// Just set the name of the variable for debug message purposes.
/// ///
/// @param name The name of the object, output in debug messages. /// This is much larger than the data that will be sent to Kea (so AFL
FuzzSync(const char* name); /// data may be trimmed). However, it does allow for AFL to send quite
/// large packets without resulting in AFL synchronization problems because
/// Kea has not read all the data sent.
static constexpr size_t BUFFER_SIZE = 256 * 1024;
/// @brief Waits for condition notification /// @brief maximum size of packets fuzzing thread will send to Kea
///
/// 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 -