1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 #include <inttypes.h>
15 #include <stdbool.h>
16
17 #include <named/fuzz.h>
18
19 #ifdef ENABLE_AFL
20 #include <arpa/inet.h>
21 #include <errno.h>
22 #include <pthread.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include <isc/app.h>
29 #include <isc/condition.h>
30 #include <isc/mutex.h>
31 #include <isc/thread.h>
32 #include <isc/util.h>
33
34 #include <dns/log.h>
35
36 #include <named/globals.h>
37 #include <named/log.h>
38 #include <named/server.h>
39
40 /*
41 * We are using pthreads directly because we might be using it with
42 * unthreaded version of BIND, where all thread functions are
43 * mocks. Since AFL for now only works on Linux it's not a problem.
44 */
45 static pthread_cond_t cond;
46 static pthread_mutex_t mutex;
47 static bool ready;
48
49 /*
50 * In "client:" mode, this thread reads fuzzed query messages from AFL
51 * from standard input and sends it to named's listening port (DNS) that
52 * is passed in the -A client:<address>:<port> option. It can be used to
53 * test named from the client side.
54 */
55 static void *
fuzz_thread_client(void * arg)56 fuzz_thread_client(void *arg) {
57 char *host;
58 char *port;
59 struct sockaddr_in servaddr;
60 int sockfd;
61 void *buf;
62
63 UNUSED(arg);
64
65 /*
66 * Parse named -A argument in the "address:port" syntax. Due to
67 * the syntax used, this only supports IPv4 addresses.
68 */
69 host = strdup(named_g_fuzz_addr);
70 RUNTIME_CHECK(host != NULL);
71
72 port = strchr(host, ':');
73 RUNTIME_CHECK(port != NULL);
74 *port = 0;
75 ++port;
76
77 memset(&servaddr, 0, sizeof(servaddr));
78 servaddr.sin_family = AF_INET;
79 RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
80 servaddr.sin_port = htons(atoi(port));
81
82 free(host);
83
84 /*
85 * Wait for named to start. This is set in run_server() in the
86 * named thread.
87 */
88 while (!named_g_run_done) {
89 usleep(10000);
90 }
91
92 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
93 RUNTIME_CHECK(sockfd != -1);
94
95 buf = malloc(65536);
96 RUNTIME_CHECK(buf != NULL);
97
98 /*
99 * Processing fuzzed packets 100,000 times before shutting down
100 * the app.
101 */
102 #ifdef __AFL_LOOP
103 for (int loop = 0; loop < 100000; loop++) {
104 #else /* ifdef __AFL_LOOP */
105 {
106 #endif /* ifdef __AFL_LOOP */
107 ssize_t length;
108 ssize_t sent;
109
110 length = read(0, buf, 65536);
111 if (length <= 0) {
112 usleep(1000000);
113 goto next;
114 }
115
116 /*
117 * Ignore packets that are larger than 4096 bytes.
118 */
119 if (length > 4096) {
120 /*
121 * AFL_CMIN doesn't support persistent mode, so
122 * shutdown the server.
123 */
124 if (getenv("AFL_CMIN")) {
125 free(buf);
126 close(sockfd);
127 named_server_flushonshutdown(named_g_server,
128 false);
129 isc_app_shutdown();
130 return (NULL);
131 }
132 raise(SIGSTOP);
133 goto next;
134 }
135
136 RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
137
138 ready = false;
139
140 sent = sendto(sockfd, buf, length, 0,
141 (struct sockaddr *)&servaddr, sizeof(servaddr));
142 RUNTIME_CHECK(sent == length);
143
144 /*
145 * Read the reply message from named to unclog it. Don't
146 * bother if there isn't a reply.
147 */
148 (void)recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL);
149
150 while (!ready) {
151 pthread_cond_wait(&cond, &mutex);
152 }
153
154 RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
155 next:;
156 }
157
158 free(buf);
159 close(sockfd);
160
161 named_server_flushonshutdown(named_g_server, false);
162 isc_app_shutdown();
163
164 return (NULL);
165 }
166
167 /*
168 * In "resolver:" mode, this thread reads fuzzed reply messages from AFL
169 * from standard input. It also sets up a listener as a remote
170 * authoritative server and sends a driver query to the client side of
171 * named(resolver). When named(resolver) connects to this authoritative
172 * server, this thread writes the fuzzed reply message from AFL to it.
173 *
174 * -A resolver:<saddress>:<sport>:<raddress>:<rport>
175 *
176 * Here, <saddress>:<sport> is where named(resolver) is listening on.
177 * <raddress>:<rport> is where the thread is supposed to setup the
178 * authoritative server. This address should be configured via the root
179 * zone to be the authoritiative server for aaaaaaaaaa.example.
180 *
181 * named(resolver) when being fuzzed will not cache answers.
182 */
183 static void *
184 fuzz_thread_resolver(void *arg) {
185 char *sqtype, *shost, *sport, *rhost, *rport;
186 struct sockaddr_in servaddr, recaddr, recvaddr;
187 /*
188 * Query for aaaaaaaaaa.example./A in wire format with RD=1,
189 * EDNS and DO=1. 0x88, 0x0c at the start is the ID field which
190 * will be updated for each query.
191 */
192 char respacket[] = { 0x88, 0x0c, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x01, 0x0a, 0x61, 0x61, 0x61,
194 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x07,
195 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
196 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10,
197 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 };
198 /*
199 * Response for example./DNSKEY in wire format. Note that RRSIGs
200 * were generated with this DNSKEY that are used as seeds for
201 * AFL in the DNSSEC fuzzing job. So the DNSKEY content of this
202 * message must not change, or the corresponding RRSIGs will
203 * have to be updated. 0x8d, 0xf6 at the start is the ID field
204 * which will be made to match the query.
205 */
206 const uint8_t dnskey_wf[] = {
207 0x8d, 0xf6, 0x84, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
208 0x00, 0x01, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
209 0x00, 0x00, 0x30, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x30, 0x00,
210 0x01, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x08, 0x01, 0x00, 0x03,
211 0x08, 0x03, 0x01, 0x00, 0x01, 0xbd, 0x81, 0xdc, 0x7f, 0x16,
212 0xd4, 0x81, 0x7c, 0x1f, 0x9f, 0x6a, 0x68, 0xdd, 0xd4, 0xda,
213 0x48, 0xd9, 0x1c, 0xbd, 0xa6, 0x46, 0x1a, 0xf0, 0xb4, 0xb9,
214 0xec, 0x3d, 0x6c, 0x0b, 0x57, 0xc7, 0xd6, 0x54, 0x66, 0xe6,
215 0x6c, 0xd5, 0x90, 0x3a, 0x78, 0x7d, 0x7f, 0x78, 0x80, 0xa2,
216 0x89, 0x61, 0x6d, 0x8a, 0x2b, 0xcd, 0x0a, 0x77, 0x7a, 0xad,
217 0xc9, 0x61, 0x53, 0x53, 0x8c, 0x99, 0x72, 0x86, 0x14, 0x74,
218 0x9c, 0x49, 0x2a, 0x47, 0x23, 0xf7, 0x02, 0x07, 0x73, 0x1c,
219 0x5c, 0x2e, 0xb4, 0x9a, 0xa4, 0xd7, 0x98, 0x42, 0xc3, 0xd2,
220 0xfe, 0xbf, 0xf3, 0xb3, 0x6a, 0x52, 0x92, 0xd5, 0xfa, 0x47,
221 0x00, 0xe3, 0xd9, 0x59, 0x31, 0x95, 0x48, 0x40, 0xfc, 0x06,
222 0x73, 0x90, 0xc6, 0x73, 0x96, 0xba, 0x29, 0x91, 0xe2, 0xac,
223 0xa3, 0xa5, 0x6d, 0x91, 0x6d, 0x52, 0xb9, 0x34, 0xba, 0x68,
224 0x4f, 0xad, 0xf0, 0xc3, 0xf3, 0x1d, 0x6d, 0x61, 0x76, 0xe5,
225 0x3d, 0xa3, 0x9b, 0x2a, 0x0c, 0x92, 0xb3, 0x78, 0x6b, 0xf1,
226 0x20, 0xd6, 0x90, 0xb7, 0xac, 0xe2, 0xf8, 0x2b, 0x94, 0x10,
227 0x79, 0xce, 0xa8, 0x60, 0x42, 0xea, 0x6a, 0x18, 0x2f, 0xc0,
228 0xd8, 0x05, 0x0a, 0x3b, 0x06, 0x0f, 0x02, 0x7e, 0xff, 0x33,
229 0x46, 0xee, 0xb6, 0x21, 0x25, 0x90, 0x63, 0x4b, 0x3b, 0x5e,
230 0xb2, 0x72, 0x3a, 0xcb, 0x91, 0x41, 0xf4, 0x20, 0x50, 0x78,
231 0x1c, 0x93, 0x95, 0xda, 0xfa, 0xae, 0x85, 0xc5, 0xd7, 0x6b,
232 0x92, 0x0c, 0x70, 0x6b, 0xe4, 0xb7, 0x29, 0x3a, 0x2e, 0x18,
233 0x88, 0x82, 0x33, 0x7c, 0xa8, 0xea, 0xb8, 0x31, 0x8f, 0xaf,
234 0x50, 0xc5, 0x9c, 0x08, 0x56, 0x8f, 0x09, 0x76, 0x4e, 0xdf,
235 0x97, 0x75, 0x9d, 0x00, 0x52, 0x7f, 0xdb, 0xec, 0x30, 0xcb,
236 0x1c, 0x4c, 0x2a, 0x21, 0x93, 0xc4, 0x6d, 0x85, 0xa9, 0x40,
237 0x3b, 0xc0, 0x0c, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x01,
238 0x2c, 0x01, 0x1b, 0x00, 0x30, 0x08, 0x01, 0x00, 0x00, 0x01,
239 0x2c, 0x67, 0x74, 0x85, 0x80, 0x58, 0xb3, 0xc5, 0x17, 0x36,
240 0x90, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
241 0x45, 0xac, 0xd3, 0x82, 0x69, 0xf3, 0x10, 0x3a, 0x97, 0x2c,
242 0x6a, 0xa9, 0x78, 0x99, 0xea, 0xb0, 0xcc, 0xf7, 0xaf, 0x33,
243 0x51, 0x5b, 0xdf, 0x77, 0x04, 0x18, 0x14, 0x99, 0x61, 0xeb,
244 0x8d, 0x76, 0x3f, 0xd1, 0x71, 0x14, 0x43, 0x80, 0x53, 0xc2,
245 0x3b, 0x9f, 0x09, 0x4f, 0xb3, 0x51, 0x04, 0x89, 0x0e, 0xc8,
246 0x54, 0x12, 0xcd, 0x07, 0x20, 0xbe, 0x94, 0xc2, 0xda, 0x99,
247 0xdd, 0x1e, 0xf8, 0xb0, 0x84, 0x2e, 0xf9, 0x19, 0x35, 0x36,
248 0xf5, 0xd0, 0x5d, 0x82, 0x18, 0x74, 0xa0, 0x00, 0xb6, 0x15,
249 0x57, 0x40, 0x5f, 0x78, 0x2d, 0x27, 0xac, 0xc7, 0x8a, 0x29,
250 0x55, 0xa9, 0xcd, 0xbc, 0xf7, 0x3e, 0xff, 0xae, 0x1a, 0x5a,
251 0x1d, 0xac, 0x0d, 0x78, 0x0e, 0x08, 0x33, 0x6c, 0x59, 0x70,
252 0x40, 0xb9, 0x65, 0xbd, 0x35, 0xbb, 0x9a, 0x70, 0xdc, 0x93,
253 0x66, 0xb0, 0xef, 0xfe, 0xf0, 0x32, 0xa6, 0xee, 0xb7, 0x03,
254 0x89, 0xa2, 0x4d, 0xe0, 0xf1, 0x20, 0xdf, 0x39, 0xe8, 0xe3,
255 0xcc, 0x95, 0xe9, 0x9a, 0xad, 0xbf, 0xbd, 0x7c, 0xf7, 0xd7,
256 0xde, 0x47, 0x9e, 0xf6, 0x17, 0xbb, 0x84, 0xa9, 0xed, 0xf2,
257 0x45, 0x61, 0x6d, 0x13, 0x0b, 0x06, 0x29, 0x50, 0xde, 0xfd,
258 0x42, 0xb0, 0x66, 0x2c, 0x1c, 0x2b, 0x63, 0xcb, 0x4e, 0xb9,
259 0x31, 0xc4, 0xea, 0xd2, 0x07, 0x3a, 0x08, 0x79, 0x19, 0x4b,
260 0x4c, 0x50, 0x97, 0x02, 0xd7, 0x26, 0x41, 0x2f, 0xdd, 0x57,
261 0xaa, 0xb0, 0xa0, 0x21, 0x4e, 0x74, 0xb6, 0x97, 0x4b, 0x8b,
262 0x09, 0x9c, 0x3d, 0x29, 0xfb, 0x12, 0x27, 0x47, 0x8f, 0xb8,
263 0xc5, 0x8e, 0x65, 0xcd, 0xca, 0x2f, 0xba, 0xf5, 0x3e, 0xec,
264 0x56, 0xc3, 0xc9, 0xa1, 0x62, 0x7d, 0xf2, 0x9f, 0x90, 0x16,
265 0x1d, 0xbf, 0x97, 0x28, 0xe1, 0x92, 0xb1, 0x53, 0xab, 0xc4,
266 0xe0, 0x99, 0xbb, 0x19, 0x90, 0x7c, 0x00, 0x00, 0x29, 0x10,
267 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00
268 };
269
270 int sockfd;
271 int listenfd;
272 int loop;
273 uint16_t qtype;
274 char *buf, *rbuf;
275 char *nameptr;
276 unsigned int i;
277 uint8_t llen;
278 uint64_t seed;
279
280 UNUSED(arg);
281
282 /*
283 * Parse named -A argument in the "qtype:saddress:sport:raddress:rport"
284 * syntax. Due to the syntax used, this only supports IPv4 addresses.
285 */
286 sqtype = strdup(named_g_fuzz_addr);
287 RUNTIME_CHECK(sqtype != NULL);
288
289 shost = strchr(sqtype, ':');
290 RUNTIME_CHECK(shost != NULL);
291 *shost = 0;
292 shost++;
293
294 sport = strchr(shost, ':');
295 RUNTIME_CHECK(sport != NULL);
296 *sport = 0;
297 sport++;
298
299 rhost = strchr(sport, ':');
300 RUNTIME_CHECK(rhost != NULL);
301 *rhost = 0;
302 rhost++;
303
304 rport = strchr(rhost, ':');
305 RUNTIME_CHECK(rport != NULL);
306 *rport = 0;
307 rport++;
308
309 /*
310 * Patch in the qtype into the question section of respacket.
311 */
312 qtype = atoi(sqtype);
313 respacket[32] = (qtype >> 8) & 0xff;
314 respacket[33] = qtype & 0xff;
315
316 memset(&servaddr, 0, sizeof(servaddr));
317 servaddr.sin_family = AF_INET;
318 RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1);
319 servaddr.sin_port = htons(atoi(sport));
320
321 memset(&recaddr, 0, sizeof(recaddr));
322 recaddr.sin_family = AF_INET;
323 RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1);
324 recaddr.sin_port = htons(atoi(rport));
325
326 free(sqtype);
327
328 /*
329 * Wait for named to start. This is set in run_server() in the
330 * named thread.
331 */
332 while (!named_g_run_done) {
333 usleep(10000);
334 }
335
336 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
337 RUNTIME_CHECK(sockfd != -1);
338
339 listenfd = socket(AF_INET, SOCK_DGRAM, 0);
340 RUNTIME_CHECK(listenfd != -1);
341
342 RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr,
343 sizeof(struct sockaddr_in)) == 0);
344
345 buf = malloc(65536);
346 rbuf = malloc(65536);
347 RUNTIME_CHECK(buf != NULL);
348 RUNTIME_CHECK(rbuf != NULL);
349
350 seed = 42;
351
352 /*
353 * Processing fuzzed packets 100,000 times before shutting down
354 * the app.
355 */
356 for (loop = 0; loop < 100000; loop++) {
357 ssize_t length;
358 ssize_t sent;
359 unsigned short id;
360 socklen_t socklen;
361
362 memset(buf, 0, 12);
363 length = read(0, buf, 65536);
364 if (length <= 0) {
365 usleep(1000000);
366 continue;
367 }
368
369 if (length > 4096) {
370 if (getenv("AFL_CMIN")) {
371 free(buf);
372 free(rbuf);
373 close(sockfd);
374 close(listenfd);
375 named_server_flushonshutdown(named_g_server,
376 false);
377 isc_app_shutdown();
378 return (NULL);
379 }
380 raise(SIGSTOP);
381 continue;
382 }
383
384 if (length < 12) {
385 length = 12;
386 }
387
388 RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
389
390 ready = false;
391
392 /* Use a unique query ID. */
393 seed = 1664525 * seed + 1013904223;
394 id = seed & 0xffff;
395 respacket[0] = (id >> 8) & 0xff;
396 respacket[1] = id & 0xff;
397
398 /*
399 * Flush any pending data on the authoritative server.
400 */
401 socklen = sizeof(recvaddr);
402 (void)recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT,
403 (struct sockaddr *)&recvaddr, &socklen);
404
405 /*
406 * Send a fixed client query to named(resolver) of
407 * aaaaaaaaaa.example./A. This is the starting query
408 * driver.
409 */
410 sent = sendto(sockfd, respacket, sizeof(respacket), 0,
411 (struct sockaddr *)&servaddr, sizeof(servaddr));
412 RUNTIME_CHECK(sent == sizeof(respacket));
413
414 /*
415 * named(resolver) will process the query above and send
416 * an upstream query to the authoritative server. We
417 * handle that here as the upstream authoritative server
418 * on listenfd.
419 */
420 socklen = sizeof(recvaddr);
421 sent = recvfrom(listenfd, rbuf, 65536, 0,
422 (struct sockaddr *)&recvaddr, &socklen);
423 RUNTIME_CHECK(sent > 0);
424
425 /*
426 * Copy QID and set QR so that response is always
427 * accepted by named(resolver).
428 */
429 buf[0] = rbuf[0];
430 buf[1] = rbuf[1];
431 buf[2] |= 0x80;
432
433 /*
434 * NOTE: We are not copying the QNAME or setting
435 * rcode=NOERROR each time. So the resolver may fail the
436 * client query (driver) / wander due to this. AA flag
437 * may also not be set based on the contents of the AFL
438 * fuzzed packet.
439 */
440
441 /*
442 * A hack - set QTYPE to the one from query so that we
443 * can easily share packets between instances. If we
444 * write over something else we'll get FORMERR anyway.
445 */
446
447 /* Skip DNS header to get to the name */
448 nameptr = buf + 12;
449
450 /* Skip the name to get to the qtype */
451 i = 0;
452 while (((llen = nameptr[i]) != 0) && (i < 255) &&
453 (((nameptr + i + 1 + llen) - buf) < length))
454 {
455 i += 1 + llen;
456 }
457
458 if (i <= 255) {
459 nameptr += 1 + i;
460 /* Patch the qtype */
461 if ((nameptr - buf) < (length - 2)) {
462 *nameptr++ = (qtype >> 8) & 0xff;
463 *nameptr++ = qtype & 0xff;
464 }
465 /* Patch the qclass */
466 if ((nameptr - buf) < (length - 2)) {
467 *nameptr++ = 0;
468 *nameptr++ = 1;
469 }
470 }
471
472 /*
473 * Send the reply to named(resolver).
474 */
475 sent = sendto(listenfd, buf, length, 0,
476 (struct sockaddr *)&recvaddr, sizeof(recvaddr));
477 RUNTIME_CHECK(sent == length);
478
479 /* We might get additional questions here (e.g. for CNAME). */
480 for (;;) {
481 fd_set fds;
482 struct timeval tv;
483 int rv;
484 int max;
485
486 FD_ZERO(&fds);
487 FD_SET(listenfd, &fds);
488 FD_SET(sockfd, &fds);
489 tv.tv_sec = 10;
490 tv.tv_usec = 0;
491 max = (listenfd > sockfd ? listenfd : sockfd) + 1;
492
493 rv = select(max, &fds, NULL, NULL, &tv);
494 RUNTIME_CHECK(rv > 0);
495
496 if (FD_ISSET(sockfd, &fds)) {
497 /*
498 * It's the reply from named(resolver)
499 * to the client(query driver), so we're
500 * done.
501 */
502 (void)recvfrom(sockfd, buf, 65536, 0, NULL,
503 NULL);
504 break;
505 }
506
507 /*
508 * We've got additional question (eg. due to
509 * CNAME). Bounce it - setting QR flag and
510 * NOERROR rcode and sending it back.
511 */
512 length = recvfrom(listenfd, buf, 65536, 0,
513 (struct sockaddr *)&recvaddr,
514 &socklen);
515
516 /*
517 * If this is a DNSKEY query, send the DNSKEY,
518 * otherwise, bounce the query.
519 */
520
521 /* Skip DNS header to get to the name */
522 nameptr = buf + 12;
523
524 /* Skip the name to get to the qtype */
525 i = 0;
526 while (((llen = nameptr[i]) != 0) && (i < 255) &&
527 (((nameptr + i + 1 + llen) - buf) < length))
528 {
529 i += 1 + llen;
530 }
531
532 if (i <= 255) {
533 nameptr += 1 + i;
534 /*
535 * Patch in the DNSKEY reply without
536 * touching the ID field. Note that we
537 * don't compare the name in the
538 * question section in the query, but we
539 * don't expect to receive any query for
540 * type DNSKEY but for the name
541 * "example."
542 */
543 if ((nameptr - buf) < (length - 2)) {
544 uint8_t hb, lb;
545 hb = *nameptr++;
546 lb = *nameptr++;
547 qtype = (hb << 8) | lb;
548
549 if (qtype == 48) {
550 memmove(buf + 2, dnskey_wf + 2,
551 sizeof(dnskey_wf) - 2);
552 length = sizeof(dnskey_wf);
553 }
554 }
555 }
556
557 buf[2] |= 0x80;
558 buf[3] &= 0xF0;
559 sent = sendto(listenfd, buf, length, 0,
560 (struct sockaddr *)&recvaddr,
561 sizeof(recvaddr));
562 RUNTIME_CHECK(sent == length);
563 }
564
565 while (!ready) {
566 pthread_cond_wait(&cond, &mutex);
567 }
568
569 RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
570 }
571
572 free(buf);
573 free(rbuf);
574 close(sockfd);
575 close(listenfd);
576 named_server_flushonshutdown(named_g_server, false);
577 isc_app_shutdown();
578
579 #ifdef __AFL_LOOP
580 /*
581 * This is here just for the signature, that's how AFL detects
582 * if it's a 'persistent mode' binary. It has to occur somewhere
583 * in the file, that's all. < wpk_> AFL checks the binary for
584 * this signature ("##SIG_AFL_PERSISTENT##") and runs the binary
585 * in persistent mode if it's present.
586 */
587 __AFL_LOOP(0);
588 #endif /* ifdef __AFL_LOOP */
589
590 return (NULL);
591 }
592
593 /*
594 * In "tcp:", "http:" and "rndc:" modes, this thread reads fuzzed query
595 * blobs from AFL from standard input and sends it to the corresponding
596 * TCP listening port of named (port 53 DNS, or the HTTP statistics
597 * channels listener or the rndc port) that is passed in the -A
598 * <mode>:<address>:<port> option. It can be used to test named from the
599 * client side.
600 */
601 static void *
602 fuzz_thread_tcp(void *arg) {
603 char *host;
604 char *port;
605 struct sockaddr_in servaddr;
606 int sockfd;
607 char *buf;
608 int loop;
609
610 UNUSED(arg);
611
612 /*
613 * Parse named -A argument in the "address:port" syntax. Due to
614 * the syntax used, this only supports IPv4 addresses.
615 */
616 host = strdup(named_g_fuzz_addr);
617 RUNTIME_CHECK(host != NULL);
618
619 port = strchr(host, ':');
620 RUNTIME_CHECK(port != NULL);
621 *port = 0;
622 ++port;
623
624 memset(&servaddr, 0, sizeof(servaddr));
625 servaddr.sin_family = AF_INET;
626 RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
627 servaddr.sin_port = htons(atoi(port));
628
629 free(host);
630
631 /*
632 * Wait for named to start. This is set in run_server() in the
633 * named thread.
634 */
635 while (!named_g_run_done) {
636 usleep(10000);
637 }
638
639 buf = malloc(65539);
640 RUNTIME_CHECK(buf != NULL);
641
642 /*
643 * Processing fuzzed packets 100,000 times before shutting down
644 * the app.
645 */
646 for (loop = 0; loop < 100000; loop++) {
647 ssize_t length;
648 ssize_t sent;
649 int yes;
650 int r;
651
652 if (named_g_fuzz_type == isc_fuzz_tcpclient) {
653 /*
654 * To fuzz DNS TCP client we have to put 16-bit
655 * message length preceding the start of packet.
656 */
657 length = read(0, buf + 2, 65535);
658 buf[0] = (length >> 8) & 0xff;
659 buf[1] = length & 0xff;
660 length += 2;
661 } else {
662 /*
663 * Other types of TCP clients such as HTTP, etc.
664 */
665 length = read(0, buf, 65535);
666 }
667 if (length <= 0) {
668 usleep(1000000);
669 continue;
670 }
671 if (named_g_fuzz_type == isc_fuzz_http) {
672 /*
673 * This guarantees that the request will be
674 * processed.
675 */
676 INSIST(length <= 65535);
677 buf[length++] = '\r';
678 buf[length++] = '\n';
679 buf[length++] = '\r';
680 buf[length++] = '\n';
681 }
682
683 RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
684
685 ready = false;
686 yes = 1;
687 sockfd = socket(AF_INET, SOCK_STREAM, 0);
688
689 RUNTIME_CHECK(sockfd != -1);
690 RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
691 sizeof(int)) == 0);
692
693 do {
694 r = connect(sockfd, (struct sockaddr *)&servaddr,
695 sizeof(servaddr));
696 if (r != 0) {
697 usleep(10000);
698 }
699 } while (r != 0);
700
701 /*
702 * Send the fuzzed query blob to the target server.
703 */
704 sent = write(sockfd, buf, length);
705 RUNTIME_CHECK(sent == length);
706
707 close(sockfd);
708
709 while (!ready) {
710 pthread_cond_wait(&cond, &mutex);
711 }
712
713 RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
714 }
715
716 free(buf);
717 close(sockfd);
718 named_server_flushonshutdown(named_g_server, false);
719 isc_app_shutdown();
720
721 return (NULL);
722 }
723
724 #endif /* ENABLE_AFL */
725
726 /*
727 * named has finished processing a message and has sent the
728 * reply. Signal the fuzz thread using the condition variable, to read
729 * and process the next item from AFL.
730 */
731 void
732 named_fuzz_notify(void) {
733 #ifdef ENABLE_AFL
734 if (getenv("AFL_CMIN")) {
735 named_server_flushonshutdown(named_g_server, false);
736 isc_app_shutdown();
737 return;
738 }
739
740 raise(SIGSTOP);
741
742 RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
743
744 ready = true;
745
746 RUNTIME_CHECK(pthread_cond_signal(&cond) == 0);
747 RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
748 #endif /* ENABLE_AFL */
749 }
750
751 void
752 named_fuzz_setup(void) {
753 #ifdef ENABLE_AFL
754 if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) {
755 pthread_t thread;
756 void *(fn) = NULL;
757
758 switch (named_g_fuzz_type) {
759 case isc_fuzz_client:
760 fn = fuzz_thread_client;
761 break;
762
763 case isc_fuzz_http:
764 case isc_fuzz_tcpclient:
765 case isc_fuzz_rndc:
766 fn = fuzz_thread_tcp;
767 break;
768
769 case isc_fuzz_resolver:
770 fn = fuzz_thread_resolver;
771 break;
772
773 default:
774 RUNTIME_CHECK(fn != NULL);
775 }
776
777 RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0);
778 RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0);
779 RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0);
780 }
781 #endif /* ENABLE_AFL */
782 }
783