From: Witold Krecicki Date: Thu, 5 May 2016 09:46:11 +0000 (+0200) Subject: 4358. [test] Added American Fuzzy Lop harness that allows X-Git-Tag: v9.11.0a2~47 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=19d80ce5844e00a021643759adcbe27c11b485a0;p=thirdparty%2Fbind9.git 4358. [test] Added American Fuzzy Lop harness that allows feeding fuzzed packets into BIND. [RT #41723] --- diff --git a/CHANGES b/CHANGES index 544efb48f24..1eb602f54ab 100644 --- 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 diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in index 05810bcac82..69d189dff69 100644 --- a/bin/named/Makefile.in +++ b/bin/named/Makefile.in @@ -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 \ diff --git a/bin/named/client.c b/bin/named/client.c index 76cfcd385e3..be2592537d8 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -35,10 +35,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -54,6 +56,7 @@ #include #include +#include #include #include #include @@ -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 diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 0440609986d..45fc0e36456 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -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 index 00000000000..ba06a519104 --- /dev/null +++ b/bin/named/fuzz.c @@ -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 + +#ifdef ENABLE_AFL +#include +#include +#include + +#include +#include +#include +#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 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 index 00000000000..c7236112c86 --- /dev/null +++ b/bin/named/include/named/fuzz.h @@ -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 */ diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index df46adcd23d..ae5c30f511f 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -33,6 +33,7 @@ #include #include +#include #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 diff --git a/bin/named/include/named/main.h b/bin/named/include/named/main.h index e838b6302a7..aa1fcdb5b5a 100644 --- a/bin/named/include/named/main.h +++ b/bin/named/include/named/main.h @@ -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, ...) diff --git a/bin/named/main.c b/bin/named/main.c index 2b4e73e57c1..b6cb3d4e11f 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include #include @@ -68,6 +70,7 @@ #include #include +#include #include /* Explicit, though named/log.h includes it. */ #include #include @@ -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. */ diff --git a/bin/named/query.c b/bin/named/query.c index 4ce6bf000e9..07d3a3e4918 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -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))) diff --git a/bin/named/server.c b/bin/named/server.c index 4c3ceeb999c..2c6d47ca014 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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 diff --git a/config.h.in b/config.h.in index 2fbdba73de4..0f885b13294 100644 --- a/config.h.in +++ b/config.h.in @@ -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 diff --git a/configure b/configure index 0f05532263c..f3b689ddce8 100755 --- 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 : diff --git a/configure.in b/configure.in index 394bc174486..15ef86547d9 100644 --- a/configure.in +++ b/configure.in @@ -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]])) diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 4f19ff47394..cf3fcb0c4c8 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -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 */ diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 64abe98765c..7bd0d133325 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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 diff --git a/lib/isc/httpd.c b/lib/isc/httpd.c index 58e34a4208f..d52527284eb 100644 --- a/lib/isc/httpd.c +++ b/lib/isc/httpd.c @@ -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; +} diff --git a/lib/isc/include/isc/httpd.h b/lib/isc/include/isc/httpd.h index 06ce90f3232..f3f765109dc 100644 --- a/lib/isc/include/isc/httpd.h +++ b/lib/isc/include/isc/httpd.h @@ -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 */