From: Tomek Mrugalski Date: Mon, 21 Nov 2016 10:53:04 +0000 (+0100) Subject: [experiments/fuzz] Initiall fuzzing code added for Kea6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4bf351a7ca951265a725000545ca5269e554831f;p=thirdparty%2Fkea.git [experiments/fuzz] Initiall fuzzing code added for Kea6 --- diff --git a/configure.ac b/configure.ac index 1d054feb61..7c75edd04f 100644 --- a/configure.ac +++ b/configure.ac @@ -1498,6 +1498,17 @@ if test "x$VALGRIND" != "xno"; then found_valgrind="found" fi +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) + +if test "x$enable_fuzz" != "xno" ; then + AC_DEFINE([FUZZ], [1], [AFL fuzzing was enabled.]) +fi + + # Check for optreset in unistd.h. On BSD systems the optreset is # used to reset the state of getopt() function. Resetting its state # is required if command line arguments are parsed multiple times @@ -1983,6 +1994,7 @@ Developer: Generate Messages Files: $enable_generate_messages Perfdhcp: $enable_perfdhcp Kea-shell: $enable_shell + Enable fuzz: $enable_fuzz END diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 4730182bf2..ee3585411e 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -55,7 +55,7 @@ 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 -EXTRA_DIST += dhcp6_messages.mes +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 fba6cfbbe5..93cb45669a 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -65,6 +65,8 @@ #endif #include +#include + #include #include #include @@ -405,6 +407,19 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt, } bool Dhcpv6Srv::run() { + +#ifdef FUZZ + // 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(); +#endif /* FUZZ */ + while (!shutdown_) { try { run_one(); @@ -414,11 +429,18 @@ bool Dhcpv6Srv::run() { // specific catches. LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION) .arg(e.what()); + } catch (...) { // General catch-all non-standard exception that are not caught // by more specific catches. 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 } return (true); diff --git a/src/bin/dhcp6/fuzz.cc b/src/bin/dhcp6/fuzz.cc new file mode 100644 index 0000000000..57959e679e --- /dev/null +++ b/src/bin/dhcp6/fuzz.cc @@ -0,0 +1,223 @@ +/* + * 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 + +#define ENABLE_AFL + +#ifdef ENABLE_AFL +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __AFL_LOOP +#error To use American Fuzzy Lop you have to set CC to afl-clang-fast!!! +#endif + +/* + * 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; + +// 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 *arg) { + 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"); + + 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()); + + cout << "Kea AFL setup:" << endl; + cout << "Interface: " << iface << endl; + cout << "Interface index: " << iface_id << endl; + cout << "UDP destination addr: " << dst << endl; + cout << "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) { + cout << "Error: inet_pton() failed: can't convert " << dst + << " to address." << endl; + exit(EXIT_FAILURE); + } + servaddr.sin6_port = htons(547); + servaddr.sin6_scope_id = iface_id; + + sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sockfd < 0) { + cout << "Failed to create UDP6 socket" << endl; + exit(EXIT_FAILURE); + } + + buf = malloc(65536); + if (!buf) { + cout << "Failed to allocate a buffer" << endl; + exit(EXIT_FAILURE); + } + + loop = 100000; + 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) { + cout << "#### Failed to lock mutex" << endl; + } + + ready = false; + + ssize_t sent; + + sent = sendto(sockfd, buf, length, 0, + (struct sockaddr *) &servaddr, sizeof(servaddr)); + if (sent != length) { + cout << "#### Error: expected to send " << length + << ", but really sent " << sent << endl; + } + + /* unclog */ + recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL); + + while (!ready) + pthread_cond_wait(&cond, &mutex); + + if (pthread_mutex_unlock(&mutex) != 0) { + cout << "#### Failed to unlock mutex" << endl; + } + } + + free(buf); + close(sockfd); + + // @todo: shutdown kea + // ns_server_flushonshutdown(ns_g_server, ISC_FALSE); + // isc_app_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 + /// @todo: What does this piece of code do? + /* if (getenv("AFL_CMIN")) { + ns_server_flushonshutdown(ns_g_server, ISC_FALSE); + isc_app_shutdown(); + return; + } */ + + raise(SIGSTOP); + + if (pthread_mutex_lock(&mutex) != 0) { + cout << "#### unable to lock mutex" << endl; + } + + ready = true; + + if (pthread_cond_signal(&cond) != 0) { + + } + + if (pthread_mutex_unlock(&mutex) != 0) { + cout << "Unable to unlock mutex" << endl; + } +#endif /* ENABLE_AFL */ +} + +void +kea_fuzz_setup(void) { +#ifdef ENABLE_AFL + + /// @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) { + + } + + if (pthread_cond_init(&cond, NULL) == 0) { + + } + + + if (pthread_create(&thread, NULL, kea_main_client, NULL) != 0) { + + } + } + +#endif /* ENABLE_AFL */ +} diff --git a/src/bin/dhcp6/fuzz.h b/src/bin/dhcp6/fuzz.h new file mode 100644 index 0000000000..dcce8b2728 --- /dev/null +++ b/src/bin/dhcp6/fuzz.h @@ -0,0 +1,20 @@ +/* + * 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); + +void kea_fuzz_setup(void); + +}; + +#endif /* KEA_FUZZ_H */