]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4358. [test] Added American Fuzzy Lop harness that allows
authorWitold Krecicki <wpk@isc.org>
Thu, 5 May 2016 09:46:11 +0000 (11:46 +0200)
committerWitold Krecicki <wpk@isc.org>
Thu, 5 May 2016 09:49:38 +0000 (11:49 +0200)
feeding fuzzed packets into BIND.
[RT #41723]

18 files changed:
CHANGES
bin/named/Makefile.in
bin/named/client.c
bin/named/controlconf.c
bin/named/fuzz.c [new file with mode: 0644]
bin/named/include/named/fuzz.h [new file with mode: 0644]
bin/named/include/named/globals.h
bin/named/include/named/main.h
bin/named/main.c
bin/named/query.c
bin/named/server.c
config.h.in
configure
configure.in
lib/dns/include/dns/resolver.h
lib/dns/resolver.c
lib/isc/httpd.c
lib/isc/include/isc/httpd.h

diff --git a/CHANGES b/CHANGES
index 544efb48f24c887f7b7fbee17eeeba34df9bd07e..1eb602f54ab579bc07836d26287c82fec79ebf05 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+4358.  [test]          Added American Fuzzy Lop harness that allows
+                       feeding fuzzed packets into BIND.
+                       [RT #41723]
+
 4357.  [func]          Add the python RNDC module. [RT #42093]
 
 4356.  [func]          Add the ability to specify whether to wait for
index 05810bcac82e00ae361ce08cace47a4a8dec9376..69d189dff6995b687c403505353b140501331886 100644 (file)
@@ -86,7 +86,7 @@ TARGETS =     named@EXEEXT@ lwresd@EXEEXT@
 GEOIPLINKOBJS = geoip.@O@
 
 OBJS =         builtin.@O@ client.@O@ config.@O@ control.@O@ \
-               controlconf.@O@ @GEOIPLINKOBJS@ interfacemgr.@O@ \
+               controlconf.@O@ fuzz.@O@ @GEOIPLINKOBJS@ interfacemgr.@O@ \
                listenlist.@O@ log.@O@ logconf.@O@ main.@O@ notify.@O@ \
                query.@O@ server.@O@ sortlist.@O@ statschannel.@O@ \
                tkeyconf.@O@ tsigconf.@O@ update.@O@ xfrout.@O@ \
@@ -102,7 +102,7 @@ SYMOBJS =   symtbl.@O@
 GEOIPLINKSRCS = geoip.c
 
 SRCS =         builtin.c client.c config.c control.c \
-               controlconf.c @GEOIPLINKSRCS@ interfacemgr.c \
+               controlconf.c fuzz.c @GEOIPLINKSRCS@ interfacemgr.c \
                listenlist.c log.c logconf.c main.c notify.c \
                query.c server.c sortlist.c statschannel.c \
                tkeyconf.c tsigconf.c update.c xfrout.c \
index 76cfcd385e3a6c5745341907f6c63d20fe263985..be2592537d8bb29874ee55b28c333ff12e8b4160 100644 (file)
 #include <isc/timer.h>
 #include <isc/util.h>
 
+#include <dns/adb.h>
 #include <dns/badcache.h>
 #include <dns/db.h>
 #include <dns/dispatch.h>
 #include <dns/dnstap.h>
+#include <dns/cache.h>
 #include <dns/edns.h>
 #include <dns/events.h>
 #include <dns/message.h>
@@ -54,6 +56,7 @@
 #include <dns/view.h>
 #include <dns/zone.h>
 
+#include <named/fuzz.h>
 #include <named/interfacemgr.h>
 #include <named/log.h>
 #include <named/notify.h>
@@ -702,8 +705,15 @@ ns_client_endrequest(ns_client_t *client) {
                client->next = NULL;
        }
 
-       if (client->view != NULL)
+       if (client->view != NULL) {
+#ifdef ENABLE_AFL
+               if (ns_g_fuzz_type == ns_fuzz_resolver) {
+                       dns_cache_clean(client->view->cache, INT_MAX);
+                       dns_adb_flush(client->view->adb);
+               }
+#endif
                dns_view_detach(&client->view);
+       }
        if (client->opt != NULL) {
                INSIST(dns_rdataset_isassociated(client->opt));
                dns_rdataset_disassociate(client->opt);
@@ -727,6 +737,14 @@ ns_client_endrequest(ns_client_t *client) {
         * the request; that's all except the TCP flag.
         */
        client->attributes &= NS_CLIENTATTR_TCP;
+#ifdef ENABLE_AFL
+       if (ns_g_fuzz_type == ns_fuzz_client ||
+           ns_g_fuzz_type == ns_fuzz_tcpclient ||
+           ns_g_fuzz_type == ns_fuzz_resolver) {
+               named_fuzz_notify();
+       }
+#endif /* ENABLE_AFL */
+
 }
 
 void
index 0440609986d48576eee67eaa3c8a816ceebd5f88..45fc0e364569c84e817203ce4eac91f78afbed78 100644 (file)
@@ -183,6 +183,11 @@ maybe_free_connection(controlconnection_t *conn) {
        }
 
        ISC_LIST_UNLINK(listener->connections, conn, link);
+#ifdef ENABLE_AFL
+       if (ns_g_fuzz_type == ns_fuzz_rndc) {
+               named_fuzz_notify();
+       }
+#endif
        isc_mem_put(listener->mctx, conn, sizeof(*conn));
 }
 
@@ -600,6 +605,11 @@ newconnection(controllistener_t *listener, isc_socket_t *sock) {
        if (conn->timer != NULL)
                isc_timer_detach(&conn->timer);
        isc_mem_put(listener->mctx, conn, sizeof(*conn));
+#ifdef ENABLE_AFL
+       if (ns_g_fuzz_type == ns_fuzz_rndc) {
+               named_fuzz_notify();
+       }
+#endif
        return (result);
 }
 
diff --git a/bin/named/fuzz.c b/bin/named/fuzz.c
new file mode 100644 (file)
index 0000000..ba06a51
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2015,2016  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <named/fuzz.h>
+
+#ifdef ENABLE_AFL
+#include <named/globals.h>
+#include <named/server.h>
+#include <sys/errno.h>
+
+#include <isc/app.h>
+#include <isc/condition.h>
+#include <isc/mutex.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+#include <named/log.h>
+#include <dns/log.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/inet.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
+
+/*
+ * 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 isc_boolean_t ready;
+
+
+static void *
+fuzz_main_client(void *arg) {
+       char *host;
+       char *port;
+       struct sockaddr_in servaddr;
+       int sockfd;
+       int loop;
+       void *buf;
+
+       UNUSED(arg);
+
+       /*
+        * Parse named -A argument in the "address:port" syntax. Due to
+        * the syntax used, this only supports IPv4 addresses.
+        */
+
+       host = strdup(ns_g_fuzz_named_addr);
+       RUNTIME_CHECK(host != NULL);
+       port = strchr(host, ':');
+       RUNTIME_CHECK(port != NULL);
+       *port = 0;
+       ++port;
+
+       memset(&servaddr, 0, sizeof (servaddr));
+       servaddr.sin_family = AF_INET;
+       RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+       servaddr.sin_port = htons(atoi(port));
+
+       free(host);
+
+       /* Wait for named to start. */
+       while (!ns_g_run_done) {
+               usleep(10000);
+       }
+
+       sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       RUNTIME_CHECK(sockfd != -1);
+
+       buf = malloc(65536);
+       RUNTIME_CHECK(buf != NULL);
+
+       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;
+               }
+
+               RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+               ready = ISC_FALSE;
+
+               ssize_t sent;
+
+               sent = sendto(sockfd, buf, length, 0,
+                             (struct sockaddr *) &servaddr, sizeof(servaddr));
+               RUNTIME_CHECK(sent == length);
+
+               /* unclog */
+               recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL);
+
+               while (!ready)
+                       pthread_cond_wait(&cond, &mutex);
+
+               RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == ISC_R_SUCCESS);
+       }
+
+       free(buf);
+       close(sockfd);
+
+       ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+       isc_app_shutdown();
+
+       return (NULL);
+}
+
+static void *
+fuzz_main_resolver(void *arg) {
+       char *shost, *sport, *rhost, *rport;
+       /* Query for A? aaaaaaaaaa.example. */
+       char respacket[] =
+                "\0\0\1 \0\1\0\0\0\0\0\0\naaaaaaaaaa\7example\0\0\1\0\1";
+       struct sockaddr_in servaddr, recaddr, recvaddr;
+       int sockfd;
+       int listenfd;
+       int loop;
+       char *buf, *rbuf;
+
+       UNUSED(arg);
+
+       /*
+        * Parse named -A argument in the "laddress:sport:raddress:rport"
+        * syntax.  Due to the syntax used, this only supports IPv4 addresses.
+        */
+
+       shost = strdup(ns_g_fuzz_named_addr);
+       RUNTIME_CHECK(shost != NULL);
+       sport = strchr(shost, ':');
+       RUNTIME_CHECK(sport != NULL);
+       *sport = 0;
+       sport++;
+       rhost = strchr(sport, ':');
+       RUNTIME_CHECK(rhost != NULL);
+       *rhost = 0;
+       rhost++;
+       rport = strchr(rhost, ':');
+       RUNTIME_CHECK(rport != NULL);
+       *rport = 0;
+       rport++;
+
+       memset(&servaddr, 0, sizeof (servaddr));
+       servaddr.sin_family = AF_INET;
+       RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1);
+       servaddr.sin_port = htons(atoi(sport));
+
+       memset(&recaddr, 0, sizeof (recaddr));
+       recaddr.sin_family = AF_INET;
+       RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1);
+       recaddr.sin_port = htons(atoi(rport));
+
+       free(shost);
+
+       /* Wait for named to start */
+       while (!ns_g_run_done) {
+               usleep(10000);
+       }
+
+       sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       RUNTIME_CHECK(sockfd != -1);
+
+       listenfd = socket(AF_INET, SOCK_DGRAM, 0);
+       RUNTIME_CHECK(listenfd != -1);
+       RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr,
+                          sizeof(struct sockaddr_in)) == 0);
+
+       buf = malloc(65536);
+       rbuf = malloc(65536);
+       RUNTIME_CHECK(buf != NULL);
+       RUNTIME_CHECK(rbuf != NULL);
+
+       loop = 100000;
+       while (loop--) {
+               ssize_t length;
+               memset(buf, 0, 16);
+               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 (length < 16) {
+                   length = 16;
+               }
+
+               RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+               ready = ISC_FALSE;
+
+               ssize_t sent;
+               /* Randomize query ID. */
+               int id = random();
+               respacket[0] = id >> 8;
+               respacket[1] = id & 0xff;
+
+               /* flush */
+               socklen_t socklen = sizeof(recvaddr);
+               sent = recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT,
+                       (struct sockaddr *) &recvaddr, &socklen);
+
+               sent = sendto(sockfd, respacket, sizeof(respacket), 0,
+                      (struct sockaddr *) &servaddr, sizeof(servaddr));
+               RUNTIME_CHECK(sent == sizeof(respacket));
+
+               socklen = sizeof(recvaddr);
+               sent = recvfrom(listenfd, rbuf, 65536, 0,
+                               (struct sockaddr *) &recvaddr, &socklen);
+               RUNTIME_CHECK(sent > 0);
+
+               /* Copy QID and set QR so that response is always processed. */
+               buf[0] = rbuf[0];
+               buf[1] = rbuf[1];
+               buf[2] |= 0x80;
+
+               sent = sendto(listenfd, buf, length, 0,
+                             (struct sockaddr *) &recvaddr, sizeof(recvaddr));
+               RUNTIME_CHECK(sent == length);
+
+               /* We might get additional questions here (e.g. for CNAME). */
+               for (;;) {
+                       fd_set fds;
+                       struct timeval tv;
+                       int rv;
+                       int max;
+
+                       FD_ZERO(&fds);
+                       FD_SET(listenfd, &fds);
+                       FD_SET(sockfd, &fds);
+                       tv.tv_sec = 10;
+                       tv.tv_usec = 0;
+                       max = (listenfd > sockfd ? listenfd : sockfd)+1;
+
+                       rv = select(max, &fds, NULL, NULL, &tv);
+                       RUNTIME_CHECK(rv > 0);
+
+                       if (FD_ISSET(sockfd, &fds)) {
+                               /* It's the reply, we're done. */
+                               recvfrom(sockfd, buf, 65536, 0, NULL, NULL);
+                               break;
+                       }
+
+                       /*
+                        * We've got additional question (eg. cname chain)
+                        * We are bouncing it - setting QR flag and NOERROR
+                        * rcode and sending it back.
+                        */
+
+                       length = recvfrom(listenfd, buf, 65536, 0,
+                                  (struct sockaddr *) &recvaddr, &socklen);
+                       buf[2] |= 0x80;
+                       buf[3] &= 0xF0;
+                       sent = sendto(listenfd, buf, length, 0,
+                                     (struct sockaddr *) &recvaddr,
+                                     sizeof(recvaddr));
+                       RUNTIME_CHECK(sent == length);
+               }
+
+               while (!ready)
+                       pthread_cond_wait(&cond, &mutex);
+
+               RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+       }
+
+       free(buf);
+       close(sockfd);
+       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);
+}
+
+static void *
+fuzz_main_tcp(void *arg) {
+       char *host;
+       char *port;
+       struct sockaddr_in servaddr;
+       int sockfd;
+       char *buf;
+       int loop;
+
+       UNUSED(arg);
+
+       /*
+        * Parse named -A argument in the "address:port" syntax. Due to
+        * the syntax used, this only supports IPv4 addresses.
+        */
+
+       host = strdup(ns_g_fuzz_named_addr);
+       RUNTIME_CHECK(host != NULL);
+       port = strchr(host, ':');
+       RUNTIME_CHECK(port != NULL);
+       *port = 0;
+       ++port;
+
+       memset(&servaddr, 0, sizeof (servaddr));
+       servaddr.sin_family = AF_INET;
+       RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+       servaddr.sin_port = htons(atoi(port));
+
+       free(host);
+
+       /* Wait for named to start */
+       while (!ns_g_run_done) {
+               usleep(10000);
+       }
+
+       buf = malloc(65539);
+       RUNTIME_CHECK(buf != NULL);
+
+       loop = 100000;
+       while (loop--) {
+               ssize_t length;
+
+               if (ns_g_fuzz_type == ns_fuzz_tcpclient) {
+                       /*
+                        * To fuzz TCP client we have to put length at
+                        * the start of packet.
+                        */
+                       length = read(0, buf+2, 65535);
+                       buf[0] = length >> 8;
+                       buf[1] = length & 0xff;
+                       length += 2;
+               } else {
+                       length = read(0, buf, 65535);
+               }
+               if (length <= 0) {
+                       usleep(1000000);
+                       continue;
+               }
+               if (ns_g_fuzz_type == ns_fuzz_http) {
+                       /*
+                        * This guarantees that the request will be processed.
+                        */
+                       buf[length++]='\r';
+                       buf[length++]='\n';
+                       buf[length++]='\r';
+                       buf[length++]='\n';
+               }
+
+               RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+               ready = ISC_FALSE;
+
+               ssize_t sent;
+               int yes = 1;
+               int r;
+               sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+               RUNTIME_CHECK(sockfd != -1);
+               RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+                                        &yes, sizeof(int)) == 0);
+
+               do {
+                       r = connect(sockfd, (struct sockaddr*)&servaddr,
+                                   sizeof(servaddr));
+               } while (r != 0);
+
+               sent = write(sockfd, buf, length);
+               RUNTIME_CHECK(sent == length);
+               close(sockfd);
+
+               /* unclog */
+               recvfrom(sockfd, buf, 65537, MSG_DONTWAIT, NULL, NULL);
+
+               while (!ready)
+                       pthread_cond_wait(&cond, &mutex);
+
+               RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == ISC_R_SUCCESS);
+       }
+
+       free(buf);
+       close(sockfd);
+       ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+       isc_app_shutdown();
+
+       return (NULL);
+}
+
+#endif /* ENABLE_AFL */
+
+void
+named_fuzz_notify(void) {
+#ifdef ENABLE_AFL
+       if (getenv("AFL_CMIN")) {
+               ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+               isc_app_shutdown();
+               return;
+       }
+
+       raise(SIGSTOP);
+
+       RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+       ready = ISC_TRUE;
+
+       RUNTIME_CHECK(pthread_cond_signal(&cond) == 0);
+       RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+#endif /* ENABLE_AFL */
+}
+
+void
+named_fuzz_setup(void) {
+#ifdef ENABLE_AFL
+       if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) {
+               pthread_t thread;
+               void *(fn) = NULL;
+
+               switch (ns_g_fuzz_type) {
+               case ns_fuzz_client:
+                       fn = fuzz_main_client;
+                       break;
+
+               case ns_fuzz_http:
+               case ns_fuzz_tcpclient:
+               case ns_fuzz_rndc:
+                       fn = fuzz_main_tcp;
+                       break;
+               case ns_fuzz_resolver:
+                       fn = fuzz_main_resolver;
+                       break;
+               default:
+                       RUNTIME_CHECK(fn != NULL);
+               }
+
+               RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0);
+               RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0);
+               RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0);
+       }
+#endif /* ENABLE_AFL */
+}
diff --git a/bin/named/include/named/fuzz.h b/bin/named/include/named/fuzz.h
new file mode 100644 (file)
index 0000000..c723611
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef NAMED_FUZZ_H
+#define NAMED_FUZZ_H
+
+void
+named_fuzz_notify(void);
+
+void
+named_fuzz_setup(void);
+
+typedef enum {
+       ns_fuzz_none,
+       ns_fuzz_client,
+       ns_fuzz_tcpclient,
+       ns_fuzz_resolver,
+       ns_fuzz_http,
+       ns_fuzz_rndc
+} ns_fuzz_t;
+
+#endif /* NAMED_FUZZ_H */
index df46adcd23dbf8d92395947b024e3d66e70ab1f4..ae5c30f511f2dc95ffd9a21374ebf02811a79936 100644 (file)
@@ -33,6 +33,7 @@
 #include <dst/dst.h>
 
 #include <named/types.h>
+#include <named/fuzz.h>
 
 #undef EXTERN
 #undef INIT
@@ -57,6 +58,9 @@ EXTERN isc_entropy_t *                ns_g_entropy            INIT(NULL);
 EXTERN isc_entropy_t *         ns_g_fallbackentropy    INIT(NULL);
 EXTERN unsigned int            ns_g_cpus_detected      INIT(1);
 
+#ifdef ENABLE_AFL
+EXTERN isc_boolean_t           ns_g_run_done           INIT(ISC_FALSE);
+#endif
 /*
  * XXXRTH  We're going to want multiple timer managers eventually.  One
  *         for really short timers, another for client timers, and one
@@ -186,6 +190,9 @@ EXTERN isc_boolean_t                ns_g_disable4           INIT(ISC_FALSE);
 EXTERN dns_geoip_databases_t   *ns_g_geoip             INIT(NULL);
 #endif
 
+EXTERN const char *            ns_g_fuzz_named_addr    INIT(NULL);
+EXTERN ns_fuzz_t               ns_g_fuzz_type          INIT(ns_fuzz_none);
+
 #undef EXTERN
 #undef INIT
 
index e838b6302a7757c423c82e5a077e71fc17aedb1e..aa1fcdb5b5a9012cd85b12efb36880dd24e27686 100644 (file)
@@ -27,7 +27,7 @@
 /*
  * Commandline arguments for named; also referenced in win32/ntservice.c
  */
-#define NS_MAIN_ARGS "46c:C:d:D:E:fFgi:lL:M:m:n:N:p:P:sS:t:T:U:u:vVx:X:"
+#define NS_MAIN_ARGS "46A:c:C:d:D:E:fFgi:lL:M:m:n:N:p:P:sS:t:T:U:u:vVx:X:"
 
 ISC_PLATFORM_NORETURN_PRE void
 ns_main_earlyfatal(const char *format, ...)
index 2b4e73e57c13df458e206a25f551b4fb138b6a57..b6cb3d4e11f0ee7379ae6824c61198653fd7227c 100644 (file)
@@ -30,6 +30,7 @@
 #include <isc/entropy.h>
 #include <isc/file.h>
 #include <isc/hash.h>
+#include <isc/httpd.h>
 #include <isc/os.h>
 #include <isc/platform.h>
 #include <isc/print.h>
@@ -46,6 +47,7 @@
 #include <dns/dyndb.h>
 #include <dns/name.h>
 #include <dns/result.h>
+#include <dns/resolver.h>
 #include <dns/view.h>
 
 #include <dst/result.h>
@@ -68,6 +70,7 @@
 
 #include <named/builtin.h>
 #include <named/control.h>
+#include <named/fuzz.h>
 #include <named/globals.h>     /* Explicit, though named/log.h includes it. */
 #include <named/interfacemgr.h>
 #include <named/log.h>
@@ -435,6 +438,29 @@ set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) {
                *ret = 0;
 }
 
+static void
+parse_fuzz_arg(void) {
+       if (!strncmp(isc_commandline_argument, "client:", 7)) {
+               ns_g_fuzz_named_addr = isc_commandline_argument + 7;
+               ns_g_fuzz_type = ns_fuzz_client;
+       } else if (!strncmp(isc_commandline_argument, "tcp:", 4)) {
+               ns_g_fuzz_named_addr = isc_commandline_argument + 4;
+               ns_g_fuzz_type = ns_fuzz_tcpclient;
+       } else if (!strncmp(isc_commandline_argument, "resolver:", 9)) {
+               ns_g_fuzz_named_addr = isc_commandline_argument + 9;
+               ns_g_fuzz_type = ns_fuzz_resolver;
+       } else if (!strncmp(isc_commandline_argument, "http:", 5)) {
+               ns_g_fuzz_named_addr = isc_commandline_argument + 5;
+               ns_g_fuzz_type = ns_fuzz_http;
+       } else if (!strncmp(isc_commandline_argument, "rndc:", 5)) {
+               ns_g_fuzz_named_addr = isc_commandline_argument + 5;
+               ns_g_fuzz_type = ns_fuzz_rndc;
+       } else {
+               ns_main_earlyfatal("unknown fuzzing type '%s'",
+                                  isc_commandline_argument);
+       }
+}
+
 static void
 parse_command_line(int argc, char *argv[]) {
        int ch;
@@ -466,6 +492,9 @@ parse_command_line(int argc, char *argv[]) {
                        isc_net_disableipv4();
                        ns_g_disable4 = ISC_TRUE;
                        break;
+               case 'A':
+                       parse_fuzz_arg();
+                       break;
                case 'c':
                        ns_g_conffile = isc_commandline_argument;
                        lwresd_g_conffile = isc_commandline_argument;
@@ -1315,6 +1344,17 @@ main(int argc, char *argv[]) {
 
        parse_command_line(argc, argv);
 
+#ifdef ENABLE_AFL
+       if (ns_g_fuzz_type != ns_fuzz_none) {
+               named_fuzz_setup();
+       }
+
+       if (ns_g_fuzz_type == ns_fuzz_resolver) {
+               dns_resolver_setfuzzing();
+       } else if (ns_g_fuzz_type == ns_fuzz_http) {
+               isc_httpd_setfinishhook(named_fuzz_notify);
+       }
+#endif
        /*
         * Warn about common configuration error.
         */
index 4ce6bf000e9b64230dfdb1d26511616aa1dba614..07d3a3e491862becafece83a7451b331da4138c5 100644 (file)
@@ -6771,9 +6771,19 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
         */
        if (RECURSIONOK(client)) {
                flags = 0;
+#ifdef ENABLE_AFL
+               if (ns_g_fuzz_type == ns_fuzz_resolver) {
+                       failcache = ISC_FALSE;
+               } else {
+                       failcache = dns_badcache_find(client->view->failcache,
+                                                     client->query.qname, qtype,
+                                                     &flags, &client->tnow);
+               }
+#else
                failcache = dns_badcache_find(client->view->failcache,
                                              client->query.qname, qtype,
                                              &flags, &client->tnow);
+#endif
                if (failcache &&
                    (((flags & NS_FAILCACHE_CD) != 0) ||
                     ((client->message->flags & DNS_MESSAGEFLAG_CD) == 0)))
index 4c3ceeb999c4bee97c4bc81727a576fb91c392d0..2c6d47ca014e7f959c608ec0064214602c40bc21 100644 (file)
@@ -7113,6 +7113,9 @@ run_server(isc_task_t *task, isc_event_t *event) {
        isc_hash_init();
 
        CHECKFATAL(load_zones(server, ISC_TRUE), "loading zones");
+#ifdef ENABLE_AFL
+       ns_g_run_done = ISC_TRUE;
+#endif
 }
 
 void
index 2fbdba73de4b07bf17f1448a349eaeb6e208c72e..0f885b13294b2a82fce688598ee0276a6c05cec0 100644 (file)
@@ -173,6 +173,9 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to enable "rrset-order fixed" syntax. */
 #undef DNS_RDATASET_FIXED
 
+/* Define to enable American Fuzzy Lop test harness */
+#undef ENABLE_AFL
+
 /* Define to enable rpz-nsdname rules. */
 #undef ENABLE_RPZ_NSDNAME
 
index 0f05532263c571de7286d7f90d9b726387476e1b..f3b689ddce895559bf86061363fcc059292c1ed3 100755 (executable)
--- a/configure
+++ b/configure
@@ -996,6 +996,7 @@ enable_libbind
 enable_warn_shadow
 enable_warn_error
 enable_developer
+enable_afl
 enable_seccomp
 with_python
 enable_kqueue
@@ -1693,6 +1694,7 @@ Optional Features:
   --enable-warn-shadow   turn on -Wshadow when compiling
   --enable-warn-error    turn on -Werror when compiling
   --enable-developer      enable developer build settings
+  --enable-afl           enable American Fuzzy Lop test harness [default=no]
   --enable-seccomp        enable support for libseccomp system call filtering
                           [default=no]
   --enable-kqueue         use BSD kqueue when available [default=yes]
@@ -11470,6 +11472,20 @@ yes)
        ;;
 esac
 
+# American Fuzzy Lop
+# Check whether --enable-afl was given.
+if test "${enable_afl+set}" = set; then :
+  enableval=$enable_afl;
+$as_echo "#define ENABLE_AFL 1" >>confdefs.h
+
+fi
+
+case "$enable_afl" in
+yes)
+       LIBS="$LIBS -lpthread"
+       ;;
+esac
+
 #libseccomp sandboxing
 # Check whether --enable-seccomp was given.
 if test "${enable_seccomp+set}" = set; then :
index 394bc1744867ea586774acdd43057f8a193b7371..15ef86547d91b2e7a696b166c0f3e6dc6ca6f872 100644 (file)
@@ -97,6 +97,15 @@ yes)
        ;;
 esac
 
+# American Fuzzy Lop
+AC_ARG_ENABLE(afl, [  --enable-afl               enable American Fuzzy Lop test harness [[default=no]]],
+            [AC_DEFINE([ENABLE_AFL], [1], [Define to enable American Fuzzy Lop test harness])])
+case "$enable_afl" in
+yes)
+       LIBS="$LIBS -lpthread"
+       ;;
+esac
+
 #libseccomp sandboxing
 AC_ARG_ENABLE(seccomp,
        AS_HELP_STRING([--enable-seccomp],[enable support for libseccomp system call filtering [default=no]]))
index 4f19ff47394e2b62e8d853dd8b74d78e30012d47..cf3fcb0c4c8264567f51363be949dac4b7dee4ce 100644 (file)
@@ -699,6 +699,14 @@ void
 dns_resolver_dumpfetches(dns_resolver_t *resolver,
                         isc_statsformat_t format, FILE *fp);
 
+
+#ifdef ENABLE_AFL
+/*%
+ * Enable fuzzing of resolver, changes behaviour and eliminates retries
+ */
+void dns_resolver_setfuzzing(void);
+#endif
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RESOLVER_H */
index 64abe98765c89b870285abe9042f80d29e0ced30..7bd0d133325de9323c419088c95f07b418926b34 100644 (file)
@@ -548,6 +548,13 @@ struct dns_resolver {
 #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
 #define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
 
+#ifdef ENABLE_AFL
+static isc_boolean_t fuzzing_resolver = ISC_FALSE;
+void dns_resolver_setfuzzing() {
+       fuzzing_resolver = ISC_TRUE;
+}
+#endif
+
 static void destroy(dns_resolver_t *res);
 static void empty_bucket(dns_resolver_t *res);
 static isc_result_t resquery_send(resquery_t *query);
@@ -1890,6 +1897,10 @@ static void
 add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
        isc_sockaddr_t *sa;
 
+#ifdef ENABLE_AFL
+       if (fuzzing_resolver)
+               return;
+#endif
        if (bad_edns(fctx, address))
                return;
 
@@ -2836,6 +2847,11 @@ mark_bad(fetchctx_t *fctx) {
        dns_adbaddrinfo_t *addrinfo;
        isc_boolean_t all_bad = ISC_TRUE;
 
+#ifdef ENABLE_AFL
+       if (fuzzing_resolver)
+               return ISC_FALSE;
+#endif
+
        /*
         * Mark all known bad servers, so we don't try to talk to them
         * again.
@@ -2911,6 +2927,11 @@ add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
        const char *spc = "";
        isc_sockaddr_t *address = &addrinfo->sockaddr;
 
+#ifdef ENABLE_AFL
+       if (fuzzing_resolver)
+               return;
+#endif
+
        if (reason == DNS_R_LAME)
                fctx->lamecount++;
        else {
@@ -8478,6 +8499,12 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         */
        fctx_cancelquery(&query, &devent, finish, no_response, ISC_FALSE);
 
+#ifdef ENABLE_AFL
+       if (fuzzing_resolver && (keep_trying || resend)) {
+               fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+               return;
+       } else
+#endif
        if (keep_trying) {
                if (result == DNS_R_FORMERR)
                        broken_server = DNS_R_FORMERR;
@@ -9695,8 +9722,13 @@ void
 dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
                         dns_rdatatype_t type, isc_time_t *expire)
 {
-       (void) dns_badcache_add(resolver->badcache, name, type,
-                               ISC_FALSE, 0, expire);
+#ifdef ENABLE_AFL
+       if (!fuzzing_resolver)
+#endif
+       {
+               (void) dns_badcache_add(resolver->badcache, name, type,
+                                       ISC_FALSE, 0, expire);
+       }
 }
 
 isc_boolean_t
index 58e34a4208f0dee604e5b7663ee1fa0eff763637..d52527284ebba2572d287b83d7bf2be4de3b79ed 100644 (file)
@@ -218,6 +218,8 @@ static void reset_client(isc_httpd_t *httpd);
 static isc_httpdaction_t render_404;
 static isc_httpdaction_t render_500;
 
+static void (*finishhook)(void) = NULL;
+
 static void
 destroy_client(isc_httpd_t **httpdp) {
        isc_httpd_t *httpd = *httpdp;
@@ -245,6 +247,9 @@ destroy_client(isc_httpd_t **httpdp) {
 
        UNLOCK(&httpdmgr->lock);
 
+       if (finishhook != NULL)
+               finishhook();
+
        httpdmgr_destroy(httpdmgr);
 }
 
@@ -1258,3 +1263,9 @@ isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
 
        return (ISC_R_SUCCESS);
 }
+
+void
+isc_httpd_setfinishhook(void (*fn)(void))
+{
+       finishhook = fn;
+}
index 06ce90f323265b807c5d2715656953500a19c518..f3f765109dc580086bbdf634dd4f750a0be0d7a8 100644 (file)
@@ -83,4 +83,7 @@ isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val);
 
 isc_result_t isc_httpd_endheaders(isc_httpd_t *httpd);
 
+void
+isc_httpd_setfinishhook(void (*fn)(void));
+
 #endif /* ISC_HTTPD_H */