Kea  1.9.9-git
fuzz.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #ifdef ENABLE_AFL
10 
11 #ifndef __AFL_LOOP
12 #error To use American Fuzzy Lop you have to set CXX to afl-clang-fast++
13 #endif
14 
15 #include <dhcp/dhcp6.h>
16 #include <dhcpsrv/fuzz.h>
17 #include <dhcpsrv/fuzz_log.h>
18 
19 #include <boost/lexical_cast.hpp>
20 
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25 
26 #include <iostream>
27 #include <sstream>
28 #include <fstream>
29 #include <ctime>
30 
31 using namespace isc;
32 using namespace isc::dhcp;
33 using namespace std;
34 
35 // Constants defined in the Fuzz class definition.
36 constexpr size_t Fuzz::BUFFER_SIZE;
37 constexpr size_t Fuzz::MAX_SEND_SIZE;
38 constexpr long Fuzz::MAX_LOOP_COUNT;
39 
40 // Constructor
41 Fuzz::Fuzz(int ipversion, uint16_t port) :
42  loop_max_(MAX_LOOP_COUNT), sockaddr_len_(0), sockaddr_ptr_(nullptr),
43  sockfd_(-1) {
44 
45  try {
46  stringstream reason; // Used to construct exception messages
47 
48  // Get the values of the environment variables used to control the
49  // fuzzing.
50 
51  // Specfies the interface to be used to pass packets from AFL to Kea.
52  const char* interface = getenv("KEA_AFL_INTERFACE");
53  if (! interface) {
54  isc_throw(FuzzInitFail, "no fuzzing interface has been set");
55  }
56 
57  // The address on the interface to be used.
58  const char* address = getenv("KEA_AFL_ADDRESS");
59  if (address == 0) {
60  isc_throw(FuzzInitFail, "no fuzzing address has been set");
61  }
62 
63  // Number of Kea packet-read loops before Kea exits and AFL starts a
64  // new instance. This is optional: the default is set by the constant
65  // MAX_LOOP_COUNT.
66  const char *loop_max_ptr = getenv("KEA_AFL_LOOP_MAX");
67  if (loop_max_ptr != 0) {
68  try {
69  loop_max_ = boost::lexical_cast<long>(loop_max_ptr);
70  } catch (const boost::bad_lexical_cast&) {
71  reason << "cannot convert loop count " << loop_max_ptr
72  << " to an integer";
73  isc_throw(FuzzInitFail, reason.str());
74  }
75 
76  if (loop_max_ <= 0) {
77  reason << "KEA_AFL_LOOP_MAX is " << loop_max_ << ". "
78  << "It must be an integer greater than zero.";
79  isc_throw(FuzzInitFail, reason.str());
80  }
81  }
82 
83  // Set up address structures used to route the packets from AFL to Kea.
84  createAddressStructures(ipversion, interface, address, port);
85 
86  // Create the socket through which packets read from stdin will be sent
87  // to the port on which Kea is listening. This is closed in the
88  // destructor.
89  sockfd_ = socket((ipversion == 4) ? AF_INET : AF_INET6, SOCK_DGRAM, 0);
90  if (sockfd_ < 0) {
92  .arg(strerror(errno));
93  return;
94  }
95 
96  LOG_INFO(fuzz_logger, FUZZ_INIT_COMPLETE).arg(interface).arg(address)
97  .arg(port).arg(loop_max_);
98 
99  } catch (const FuzzInitFail& e) {
100  // AFL tends to make it difficult to find out what exactly has failed:
101  // make sure that the error is logged.
102  LOG_FATAL(fuzz_logger, FUZZ_INIT_FAIL).arg(e.what());
103  throw;
104  }
105 }
106 
107 // Destructor
108 Fuzz::~Fuzz() {
109  static_cast<void>(close(sockfd_));
110 }
111 
112 // Set up address structures.
113 void
114 Fuzz::createAddressStructures(int ipversion, const char* interface,
115  const char* address, uint16_t port) {
116  stringstream reason; // Used in error messages
117 
118  // Set up the appropriate data structure depending on the address given.
119  if ((ipversion == 6) && (strstr(address, ":") != NULL)) {
120  // Expecting IPv6 and the address contains a colon, so assume it is an
121  // an IPv6 address.
122  memset(&servaddr6_, 0, sizeof (servaddr6_));
123 
124  servaddr6_.sin6_family = AF_INET6;
125  if (inet_pton(AF_INET6, address, &servaddr6_.sin6_addr) != 1) {
126  reason << "inet_pton() failed: can't convert "
127  << address << " to an IPv6 address" << endl;
128  isc_throw(FuzzInitFail, reason.str());
129  }
130  servaddr6_.sin6_port = htons(port);
131 
132  // Interface ID is needed for IPv6 address structures.
133  servaddr6_.sin6_scope_id = if_nametoindex(interface);
134  if (servaddr6_.sin6_scope_id == 0) {
135  reason << "error retrieving interface ID for "
136  << interface << ": " << strerror(errno);
137  isc_throw(FuzzInitFail, reason.str());
138  }
139 
140  sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr6_);
141  sockaddr_len_ = sizeof(servaddr6_);
142 
143  } else if ((ipversion == 4) && (strstr(address, ".") != NULL)) {
144  // Expecting an IPv4 address and it contains a dot, so assume it is.
145  // This check is done after the IPv6 check, as it is possible for an
146  // IPv4 address to be embedded in an IPv6 one.
147  memset(&servaddr4_, 0, sizeof(servaddr4_));
148 
149  servaddr4_.sin_family = AF_INET;
150  if (inet_pton(AF_INET, address, &servaddr4_.sin_addr) != 1) {
151  reason << "inet_pton() failed: can't convert "
152  << address << " to an IPv6 address" << endl;
153  isc_throw(FuzzInitFail, reason.str());
154  }
155  servaddr4_.sin_port = htons(port);
156 
157  sockaddr_ptr_ = reinterpret_cast<sockaddr*>(&servaddr4_);
158  sockaddr_len_ = sizeof(servaddr4_);
159 
160  } else {
161  reason << "Expected IP version (" << ipversion << ") is not "
162  << "4 or 6, or the given address " << address << " does not "
163  << "match the IP version expected";
164  isc_throw(FuzzInitFail, reason.str());
165  }
166 
167 }
168 
169 
170 // This is the main fuzzing function. It receives data from fuzzing engine over
171 // stdin and then sends it to the configured UDP socket.
172 void
173 Fuzz::transfer(void) const {
174 
175  // Read from stdin. Just return if nothing is read (or there is an error)
176  // and hope that this does not cause a hang.
177  char buf[BUFFER_SIZE];
178  ssize_t length = read(0, buf, sizeof(buf));
179 
180  // Save the errno in case there was an error because if debugging is
181  // enabled, the following LOG_DEBUG call may destroy its value.
182  int errnum = errno;
183  LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_DATA_READ).arg(length);
184 
185  if (length > 0) {
186  // Now send the data to the UDP port on which Kea is listening.
187  // Send the data to the main Kea thread. Limit the size of the
188  // packets that can be sent.
189  size_t send_len = (length < MAX_SEND_SIZE) ? length : MAX_SEND_SIZE;
190  ssize_t sent = sendto(sockfd_, buf, send_len, 0, sockaddr_ptr_,
191  sockaddr_len_);
192  if (sent > 0) {
193  LOG_DEBUG(fuzz_logger, FUZZ_DBG_TRACE_DETAIL, FUZZ_SEND).arg(sent);
194  } else if (sent != length) {
195  LOG_WARN(fuzz_logger, FUZZ_SHORT_SEND).arg(length).arg(sent);
196  } else {
197  LOG_ERROR(fuzz_logger, FUZZ_SEND_ERROR).arg(strerror(errno));
198  }
199  } else {
200  // Read did not get any bytes. A zero-length read (EOF) may have been
201  // generated by AFL, so don't log that. But otherwise log an error.
202  if (length != 0) {
203  LOG_ERROR(fuzz_logger, FUZZ_READ_FAIL).arg(strerror(errnum));
204  }
205  }
206 
207 }
208 
209 #endif // ENABLE_AFL
const isc::log::MessageID FUZZ_INIT_COMPLETE
Definition: fuzz_messages.h:12
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
STL namespace.
const isc::log::MessageID FUZZ_INIT_FAIL
Definition: fuzz_messages.h:13
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const isc::log::MessageID FUZZ_SOCKET_CREATE_FAIL
Definition: fuzz_messages.h:18
Defines the logger used by the top-level component of kea-dhcp-ddns.
isc::log::Logger fuzz_logger("fuzz")
Logger for the HostMgr and the code it calls.
Definition: fuzz_log.h:38
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition: macros.h:38