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