1 /*
2 american fuzzy lop++ - test case minimizer
3 ------------------------------------------
4
5 Originally written by Michal Zalewski
6
7 Forkserver design by Jann Horn <[email protected]>
8
9 Now maintained by Marc Heuse <[email protected]>,
10 Heiko Eißfeldt <[email protected]> and
11 Andrea Fioraldi <[email protected]> and
12 Dominik Maier <[email protected]>
13
14 Copyright 2016, 2017 Google Inc. All rights reserved.
15 Copyright 2019-2024 AFLplusplus Project. All rights reserved.
16
17 Licensed under the Apache License, Version 2.0 (the "License");
18 you may not use this file except in compliance with the License.
19 You may obtain a copy of the License at:
20
21 https://www.apache.org/licenses/LICENSE-2.0
22
23 A simple test case minimizer that takes an input file and tries to remove
24 as much data as possible while keeping the binary in a crashing state
25 *or* producing consistent instrumentation output (the mode is auto-selected
26 based on the initially observed behavior).
27
28 */
29
30 #define AFL_MAIN
31
32 #include "config.h"
33 #include "types.h"
34 #include "debug.h"
35 #include "alloc-inl.h"
36 #include "hash.h"
37 #include "forkserver.h"
38 #include "sharedmem.h"
39 #include "common.h"
40
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <errno.h>
47 #include <signal.h>
48 #include <dirent.h>
49 #include <fcntl.h>
50 #include <limits.h>
51
52 #include <sys/wait.h>
53 #include <sys/time.h>
54 #ifndef USEMMAP
55 #include <sys/shm.h>
56 #endif
57 #include <sys/stat.h>
58 #include <sys/types.h>
59 #include <sys/resource.h>
60
61 static u8 *mask_bitmap; /* Mask for trace bits (-B) */
62
63 static u8 *in_file, /* Minimizer input test case */
64 *out_file, *output_file; /* Minimizer output file */
65
66 static u8 *in_data; /* Input data for trimming */
67
68 static u32 in_len, /* Input data length */
69 missed_hangs, /* Misses due to hangs */
70 missed_crashes, /* Misses due to crashes */
71 missed_paths, /* Misses due to exec path diffs */
72 map_size = MAP_SIZE;
73
74 static u64 orig_cksum; /* Original checksum */
75
76 static u8 crash_mode, /* Crash-centric mode? */
77 hang_mode, /* Minimize as long as it hangs */
78 exit_crash, /* Treat non-zero exit as crash? */
79 edges_only, /* Ignore hit counts? */
80 exact_mode, /* Require path match for crashes? */
81 remove_out_file, /* remove out_file on exit? */
82 remove_shm = 1, /* remove shmem on exit? */
83 debug; /* debug mode */
84
85 static volatile u8 stop_soon; /* Ctrl-C pressed? */
86
87 static afl_forkserver_t *fsrv;
88 static sharedmem_t shm;
89 static sharedmem_t *shm_fuzz;
90
91 /*
92 * forkserver section
93 */
94
95 /* Classify tuple counts. This is a slow & naive version, but good enough here.
96 */
97
98 static const u8 count_class_lookup[256] = {
99
100 [0] = 0,
101 [1] = 1,
102 [2] = 2,
103 [3] = 4,
104 [4 ... 7] = 8,
105 [8 ... 15] = 16,
106 [16 ... 31] = 32,
107 [32 ... 127] = 64,
108 [128 ... 255] = 128
109
110 };
111
kill_child()112 static void kill_child() {
113
114 if (fsrv->child_pid > 0) {
115
116 kill(fsrv->child_pid, fsrv->child_kill_signal);
117 fsrv->child_pid = -1;
118
119 }
120
121 }
122
deinit_shmem(afl_forkserver_t * fsrv,sharedmem_t * shm_fuzz)123 static sharedmem_t *deinit_shmem(afl_forkserver_t *fsrv,
124 sharedmem_t *shm_fuzz) {
125
126 afl_shm_deinit(shm_fuzz);
127 fsrv->support_shmem_fuzz = 0;
128 fsrv->shmem_fuzz_len = NULL;
129 fsrv->shmem_fuzz = NULL;
130 ck_free(shm_fuzz);
131 return NULL;
132
133 }
134
135 /* Apply mask to classified bitmap (if set). */
136
apply_mask(u32 * mem,u32 * mask)137 static void apply_mask(u32 *mem, u32 *mask) {
138
139 u32 i = (map_size >> 2);
140
141 if (!mask) { return; }
142
143 while (i--) {
144
145 *mem &= ~*mask;
146 mem++;
147 mask++;
148
149 }
150
151 }
152
classify_counts(afl_forkserver_t * fsrv)153 static void classify_counts(afl_forkserver_t *fsrv) {
154
155 u8 *mem = fsrv->trace_bits;
156 u32 i = map_size;
157
158 if (edges_only) {
159
160 while (i--) {
161
162 if (*mem) { *mem = 1; }
163 mem++;
164
165 }
166
167 } else {
168
169 while (i--) {
170
171 *mem = count_class_lookup[*mem];
172 mem++;
173
174 }
175
176 }
177
178 }
179
180 /* See if any bytes are set in the bitmap. */
181
anything_set(afl_forkserver_t * fsrv)182 static inline u8 anything_set(afl_forkserver_t *fsrv) {
183
184 u32 *ptr = (u32 *)fsrv->trace_bits;
185 u32 i = (map_size >> 2);
186
187 while (i--) {
188
189 if (*(ptr++)) { return 1; }
190
191 }
192
193 return 0;
194
195 }
196
at_exit_handler(void)197 static void at_exit_handler(void) {
198
199 if (remove_shm) {
200
201 if (shm.map) afl_shm_deinit(&shm);
202 if (fsrv->use_shmem_fuzz) deinit_shmem(fsrv, shm_fuzz);
203
204 }
205
206 afl_fsrv_killall();
207 if (remove_out_file) unlink(out_file);
208
209 }
210
211 /* Read initial file. */
212
read_initial_file(void)213 static void read_initial_file(void) {
214
215 struct stat st;
216 s32 fd = open(in_file, O_RDONLY);
217
218 if (fd < 0) { PFATAL("Unable to open '%s'", in_file); }
219
220 if (fstat(fd, &st) || !st.st_size) { FATAL("Zero-sized input file."); }
221
222 if (st.st_size >= TMIN_MAX_FILE) {
223
224 FATAL("Input file is too large (%ld MB max)", TMIN_MAX_FILE / 1024 / 1024);
225
226 }
227
228 in_len = st.st_size;
229 in_data = ck_alloc_nozero(in_len);
230
231 ck_read(fd, in_data, in_len, in_file);
232
233 close(fd);
234
235 OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file);
236
237 }
238
239 /* Write output file. */
240
write_to_file(u8 * path,u8 * mem,u32 len)241 static s32 write_to_file(u8 *path, u8 *mem, u32 len) {
242
243 s32 ret;
244
245 unlink(path); /* Ignore errors */
246
247 ret = open(path, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
248
249 if (ret < 0) { PFATAL("Unable to create '%s'", path); }
250
251 ck_write(ret, mem, len, path);
252
253 lseek(ret, 0, SEEK_SET);
254
255 return ret;
256
257 }
258
259 /* Execute target application. Returns 0 if the changes are a dud, or
260 1 if they should be kept. */
261
tmin_run_target(afl_forkserver_t * fsrv,u8 * mem,u32 len,u8 first_run)262 static u8 tmin_run_target(afl_forkserver_t *fsrv, u8 *mem, u32 len,
263 u8 first_run) {
264
265 afl_fsrv_write_to_testcase(fsrv, mem, len);
266
267 fsrv_run_result_t ret =
268 afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon);
269
270 if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); }
271
272 if (stop_soon) {
273
274 SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST);
275 close(write_to_file(output_file, in_data, in_len));
276 exit(1);
277
278 }
279
280 /* Always discard inputs that time out, unless we are in hang mode */
281
282 if (hang_mode) {
283
284 switch (ret) {
285
286 case FSRV_RUN_TMOUT:
287 return 1;
288 case FSRV_RUN_CRASH:
289 missed_crashes++;
290 return 0;
291 default:
292 missed_hangs++;
293 return 0;
294
295 }
296
297 }
298
299 classify_counts(fsrv);
300 apply_mask((u32 *)fsrv->trace_bits, (u32 *)mask_bitmap);
301
302 if (ret == FSRV_RUN_TMOUT) {
303
304 missed_hangs++;
305 return 0;
306
307 }
308
309 /* Handle crashing inputs depending on current mode. */
310
311 if (ret == FSRV_RUN_CRASH) {
312
313 if (first_run) { crash_mode = 1; }
314
315 if (crash_mode) {
316
317 if (!exact_mode) { return 1; }
318
319 } else {
320
321 missed_crashes++;
322 return 0;
323
324 }
325
326 } else {
327
328 /* Handle non-crashing inputs appropriately. */
329
330 if (crash_mode) {
331
332 missed_paths++;
333 return 0;
334
335 }
336
337 }
338
339 if (ret == FSRV_RUN_NOINST) { FATAL("Binary not instrumented?"); }
340
341 u64 cksum = hash64(fsrv->trace_bits, fsrv->map_size, HASH_CONST);
342
343 if (first_run) { orig_cksum = cksum; }
344
345 if (orig_cksum == cksum) { return 1; }
346
347 missed_paths++;
348 return 0;
349
350 }
351
352 /* Actually minimize! */
353
minimize(afl_forkserver_t * fsrv)354 static void minimize(afl_forkserver_t *fsrv) {
355
356 static u32 alpha_map[256];
357
358 u8 *tmp_buf = ck_alloc_nozero(in_len);
359 u32 orig_len = in_len, stage_o_len;
360
361 u32 del_len, set_len, del_pos, set_pos, i, alpha_size, cur_pass = 0;
362 u32 syms_removed, alpha_del0 = 0, alpha_del1, alpha_del2, alpha_d_total = 0;
363 u8 changed_any, prev_del;
364
365 /***********************
366 * BLOCK NORMALIZATION *
367 ***********************/
368
369 set_len = next_pow2(in_len / TMIN_SET_STEPS);
370 set_pos = 0;
371
372 if (set_len < TMIN_SET_MIN_SIZE) { set_len = TMIN_SET_MIN_SIZE; }
373
374 ACTF(cBRI "Stage #0: " cRST "One-time block normalization...");
375
376 while (set_pos < in_len) {
377
378 u32 use_len = MIN(set_len, in_len - set_pos);
379
380 for (i = 0; i < use_len; i++) {
381
382 if (in_data[set_pos + i] != '0') { break; }
383
384 }
385
386 if (i != use_len) {
387
388 memcpy(tmp_buf, in_data, in_len);
389 memset(tmp_buf + set_pos, '0', use_len);
390
391 u8 res;
392 res = tmin_run_target(fsrv, tmp_buf, in_len, 0);
393
394 if (res) {
395
396 memset(in_data + set_pos, '0', use_len);
397 /* changed_any = 1; value is not used */
398 alpha_del0 += use_len;
399
400 }
401
402 }
403
404 set_pos += set_len;
405
406 }
407
408 alpha_d_total += alpha_del0;
409
410 OKF("Block normalization complete, %u byte%s replaced.", alpha_del0,
411 alpha_del0 == 1 ? "" : "s");
412
413 next_pass:
414
415 ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass);
416 changed_any = 0;
417
418 /******************
419 * BLOCK DELETION *
420 ******************/
421
422 del_len = next_pow2(in_len / TRIM_START_STEPS);
423 stage_o_len = in_len;
424
425 ACTF(cBRI "Stage #1: " cRST "Removing blocks of data...");
426
427 next_del_blksize:
428
429 if (!del_len) { del_len = 1; }
430 del_pos = 0;
431 prev_del = 1;
432
433 SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, del_len,
434 in_len);
435
436 while (del_pos < in_len) {
437
438 u8 res;
439 s32 tail_len;
440
441 tail_len = in_len - del_pos - del_len;
442 if (tail_len < 0) { tail_len = 0; }
443
444 /* If we have processed at least one full block (initially, prev_del == 1),
445 and we did so without deleting the previous one, and we aren't at the
446 very end of the buffer (tail_len > 0), and the current block is the same
447 as the previous one... skip this step as a no-op. */
448
449 if (!prev_del && tail_len &&
450 !memcmp(in_data + del_pos - del_len, in_data + del_pos, del_len)) {
451
452 del_pos += del_len;
453 continue;
454
455 }
456
457 prev_del = 0;
458
459 /* Head */
460 memcpy(tmp_buf, in_data, del_pos);
461
462 /* Tail */
463 memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len);
464
465 res = tmin_run_target(fsrv, tmp_buf, del_pos + tail_len, 0);
466
467 if (res) {
468
469 memcpy(in_data, tmp_buf, del_pos + tail_len);
470 prev_del = 1;
471 in_len = del_pos + tail_len;
472
473 changed_any = 1;
474
475 } else {
476
477 del_pos += del_len;
478
479 }
480
481 }
482
483 if (del_len > 1 && in_len >= 1) {
484
485 del_len /= 2;
486 goto next_del_blksize;
487
488 }
489
490 OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);
491
492 if (!in_len && changed_any) {
493
494 WARNF(cLRD
495 "Down to zero bytes - check the command line and mem limit!" cRST);
496
497 }
498
499 if (cur_pass > 1 && !changed_any) { goto finalize_all; }
500
501 /*************************
502 * ALPHABET MINIMIZATION *
503 *************************/
504
505 alpha_size = 0;
506 alpha_del1 = 0;
507 syms_removed = 0;
508
509 memset(alpha_map, 0, sizeof(alpha_map));
510
511 for (i = 0; i < in_len; i++) {
512
513 if (!alpha_map[in_data[i]]) { alpha_size++; }
514 alpha_map[in_data[i]]++;
515
516 }
517
518 ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
519 alpha_size, alpha_size == 1 ? "" : "s");
520
521 for (i = 0; i < 256; i++) {
522
523 u32 r;
524 u8 res;
525
526 if (i == '0' || !alpha_map[i]) { continue; }
527
528 memcpy(tmp_buf, in_data, in_len);
529
530 for (r = 0; r < in_len; r++) {
531
532 if (tmp_buf[r] == i) { tmp_buf[r] = '0'; }
533
534 }
535
536 res = tmin_run_target(fsrv, tmp_buf, in_len, 0);
537
538 if (res) {
539
540 memcpy(in_data, tmp_buf, in_len);
541 syms_removed++;
542 alpha_del1 += alpha_map[i];
543 changed_any = 1;
544
545 }
546
547 }
548
549 alpha_d_total += alpha_del1;
550
551 OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
552 syms_removed, syms_removed == 1 ? "" : "s", alpha_del1,
553 alpha_del1 == 1 ? "" : "s");
554
555 /**************************
556 * CHARACTER MINIMIZATION *
557 **************************/
558
559 alpha_del2 = 0;
560
561 ACTF(cBRI "Stage #3: " cRST "Character minimization...");
562
563 memcpy(tmp_buf, in_data, in_len);
564
565 for (i = 0; i < in_len; i++) {
566
567 u8 res, orig = tmp_buf[i];
568
569 if (orig == '0') { continue; }
570 tmp_buf[i] = '0';
571
572 res = tmin_run_target(fsrv, tmp_buf, in_len, 0);
573
574 if (res) {
575
576 in_data[i] = '0';
577 alpha_del2++;
578 changed_any = 1;
579
580 } else {
581
582 tmp_buf[i] = orig;
583
584 }
585
586 }
587
588 alpha_d_total += alpha_del2;
589
590 OKF("Character minimization done, %u byte%s replaced.", alpha_del2,
591 alpha_del2 == 1 ? "" : "s");
592
593 if (changed_any) { goto next_pass; }
594
595 finalize_all:
596
597 if (tmp_buf) { ck_free(tmp_buf); }
598
599 if (hang_mode) {
600
601 SAYF("\n" cGRA " File size reduced by : " cRST
602 "%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST
603 "%0.02f%%\n" cGRA " Number of execs done : " cRST "%llu\n" cGRA
604 " Fruitless execs : " cRST "termination=%u crash=%u\n\n",
605 100 - ((double)in_len) * 100 / orig_len, in_len,
606 in_len == 1 ? "" : "s",
607 ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1),
608 fsrv->total_execs, missed_paths, missed_crashes);
609 return;
610
611 }
612
613 SAYF("\n" cGRA " File size reduced by : " cRST
614 "%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST
615 "%0.02f%%\n" cGRA " Number of execs done : " cRST "%llu\n" cGRA
616 " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n",
617 100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
618 ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1),
619 fsrv->total_execs, missed_paths, missed_crashes,
620 missed_hangs ? cLRD : "", missed_hangs);
621
622 if (fsrv->total_execs > 50 && missed_hangs * 10 > fsrv->total_execs &&
623 !hang_mode) {
624
625 WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
626
627 }
628
629 }
630
631 /* Handle Ctrl-C and the like. */
632
handle_stop_sig(int sig)633 static void handle_stop_sig(int sig) {
634
635 (void)sig;
636 stop_soon = 1;
637 afl_fsrv_killall();
638
639 }
640
641 /* Do basic preparations - persistent fds, filenames, etc. */
642
set_up_environment(afl_forkserver_t * fsrv,char ** argv)643 static void set_up_environment(afl_forkserver_t *fsrv, char **argv) {
644
645 u8 *x;
646 char *afl_preload;
647 char *frida_afl_preload = NULL;
648
649 fsrv->dev_null_fd = open("/dev/null", O_RDWR);
650 if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
651
652 if (!out_file) {
653
654 u8 *use_dir = ".";
655
656 if (access(use_dir, R_OK | W_OK | X_OK)) {
657
658 use_dir = get_afl_env("TMPDIR");
659 if (!use_dir) { use_dir = "/tmp"; }
660
661 }
662
663 out_file = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, (u32)getpid());
664 remove_out_file = 1;
665
666 }
667
668 unlink(out_file);
669
670 fsrv->out_file = out_file;
671 fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
672
673 if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); }
674
675 /* Set sane defaults... */
676
677 x = get_afl_env("MSAN_OPTIONS");
678
679 if (x) {
680
681 if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) {
682
683 FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
684 MSAN_ERROR) " - please fix!");
685
686 }
687
688 }
689
690 set_sanitizer_defaults();
691
692 if (get_afl_env("AFL_PRELOAD")) {
693
694 if (fsrv->qemu_mode) {
695
696 /* afl-qemu-trace takes care of converting AFL_PRELOAD. */
697
698 } else if (fsrv->frida_mode) {
699
700 afl_preload = getenv("AFL_PRELOAD");
701 u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
702 if (afl_preload) {
703
704 frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
705
706 } else {
707
708 frida_afl_preload = alloc_printf("%s", frida_binary);
709
710 }
711
712 ck_free(frida_binary);
713
714 setenv("LD_PRELOAD", frida_afl_preload, 1);
715 setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
716
717 } else {
718
719 /* CoreSight mode uses the default behavior. */
720
721 setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
722 setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
723
724 }
725
726 } else if (fsrv->frida_mode) {
727
728 u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
729 setenv("LD_PRELOAD", frida_binary, 1);
730 setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
731 ck_free(frida_binary);
732
733 }
734
735 if (frida_afl_preload) { ck_free(frida_afl_preload); }
736
737 }
738
739 /* Setup signal handlers, duh. */
740
setup_signal_handlers(void)741 static void setup_signal_handlers(void) {
742
743 struct sigaction sa;
744
745 sa.sa_handler = NULL;
746 #ifdef SA_RESTART
747 sa.sa_flags = SA_RESTART;
748 #else
749 sa.sa_flags = 0;
750 #endif
751 sa.sa_sigaction = NULL;
752
753 sigemptyset(&sa.sa_mask);
754
755 /* Various ways of saying "stop". */
756
757 sa.sa_handler = handle_stop_sig;
758 sigaction(SIGHUP, &sa, NULL);
759 sigaction(SIGINT, &sa, NULL);
760 sigaction(SIGTERM, &sa, NULL);
761
762 }
763
764 /* Display usage hints. */
765
usage(u8 * argv0)766 static void usage(u8 *argv0) {
767
768 SAYF(
769 "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
770
771 "Required parameters:\n"
772
773 " -i file - input test case to be shrunk by the tool\n"
774 " -o file - final output location for the minimized data\n\n"
775
776 "Execution control settings:\n"
777
778 " -f file - input file read by the tested program (stdin)\n"
779 " -t msec - timeout for each run (%u ms)\n"
780 " -m megs - memory limit for child process (%u MB)\n"
781 #if defined(__linux__) && defined(__aarch64__)
782 " -A - use binary-only instrumentation (ARM CoreSight mode)\n"
783 #endif
784 " -O - use binary-only instrumentation (FRIDA mode)\n"
785 #if defined(__linux__)
786 " -Q - use binary-only instrumentation (QEMU mode)\n"
787 " -U - use unicorn-based instrumentation (Unicorn mode)\n"
788 " -W - use qemu-based instrumentation with Wine (Wine "
789 "mode)\n"
790 " (Not necessary, here for consistency with other afl-* "
791 "tools)\n"
792 " -X - use Nyx mode\n"
793 #endif
794 "\n"
795
796 "Minimization settings:\n"
797
798 " -e - solve for edge coverage only, ignore hit counts\n"
799 " -x - treat non-zero exit codes as crashes\n\n"
800 " -H - minimize a hang (hang mode)\n"
801
802 "For additional tips, please consult %s/README.md.\n\n"
803
804 "Environment variables used:\n"
805 "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n"
806 "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in ms)\n"
807 "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n"
808 " (default: SIGKILL)\n"
809 "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n"
810 " (default: SIGTERM). If unset and AFL_KILL_SIGNAL is\n"
811 " set, that value will be used.\n"
812 "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
813 " the target was compiled for\n"
814 "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
815 "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n"
816 "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
817 "ASAN_OPTIONS: custom settings for ASAN\n"
818 " (must contain abort_on_error=1 and symbolize=0)\n"
819 "MSAN_OPTIONS: custom settings for MSAN\n"
820 " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n"
821 "TMPDIR: directory to use for temporary input files\n",
822 argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
823
824 exit(1);
825
826 }
827
828 /* Main entry point */
829
main(int argc,char ** argv_orig,char ** envp)830 int main(int argc, char **argv_orig, char **envp) {
831
832 s32 opt;
833 u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
834 char **use_argv;
835
836 char **argv = argv_cpy_dup(argc, argv_orig);
837
838 afl_forkserver_t fsrv_var = {0};
839 if (getenv("AFL_DEBUG")) { debug = 1; }
840 fsrv = &fsrv_var;
841 afl_fsrv_init(fsrv);
842 map_size = get_map_size();
843 fsrv->map_size = map_size;
844
845 doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
846
847 SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n");
848
849 while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeAOQUWXYHh")) > 0) {
850
851 switch (opt) {
852
853 case 'i':
854
855 if (in_file) { FATAL("Multiple -i options not supported"); }
856 in_file = optarg;
857 break;
858
859 case 'o':
860
861 if (output_file) { FATAL("Multiple -o options not supported"); }
862 output_file = optarg;
863 break;
864
865 case 'f':
866
867 if (out_file) { FATAL("Multiple -f options not supported"); }
868 fsrv->use_stdin = 0;
869 out_file = ck_strdup(optarg);
870 break;
871
872 case 'e':
873
874 if (edges_only) { FATAL("Multiple -e options not supported"); }
875 if (hang_mode) {
876
877 FATAL("Edges only and hang mode are mutually exclusive.");
878
879 }
880
881 edges_only = 1;
882 break;
883
884 case 'x':
885
886 if (exit_crash) { FATAL("Multiple -x options not supported"); }
887 exit_crash = 1;
888 break;
889
890 case 'm': {
891
892 u8 suffix = 'M';
893
894 if (mem_limit_given) { FATAL("Multiple -m options not supported"); }
895 mem_limit_given = 1;
896
897 if (!optarg) { FATAL("Wrong usage of -m"); }
898
899 if (!strcmp(optarg, "none")) {
900
901 fsrv->mem_limit = 0;
902 break;
903
904 }
905
906 if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 ||
907 optarg[0] == '-') {
908
909 FATAL("Bad syntax used for -m");
910
911 }
912
913 switch (suffix) {
914
915 case 'T':
916 fsrv->mem_limit *= 1024 * 1024;
917 break;
918 case 'G':
919 fsrv->mem_limit *= 1024;
920 break;
921 case 'k':
922 fsrv->mem_limit /= 1024;
923 break;
924 case 'M':
925 break;
926
927 default:
928 FATAL("Unsupported suffix or bad syntax for -m");
929
930 }
931
932 if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); }
933
934 if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) {
935
936 FATAL("Value of -m out of range on 32-bit systems");
937
938 }
939
940 }
941
942 break;
943
944 case 't':
945
946 if (timeout_given) { FATAL("Multiple -t options not supported"); }
947 timeout_given = 1;
948
949 if (!optarg) { FATAL("Wrong usage of -t"); }
950
951 fsrv->exec_tmout = atoi(optarg);
952
953 if (fsrv->exec_tmout < 10 || optarg[0] == '-') {
954
955 FATAL("Dangerously low value of -t");
956
957 }
958
959 break;
960
961 case 'A': /* CoreSight mode */
962
963 #if !defined(__aarch64__) || !defined(__linux__)
964 FATAL("-A option is not supported on this platform");
965 #endif
966
967 if (fsrv->cs_mode) { FATAL("Multiple -A options not supported"); }
968
969 fsrv->cs_mode = 1;
970 break;
971
972 case 'O': /* FRIDA mode */
973
974 if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
975
976 fsrv->frida_mode = 1;
977 setenv("AFL_FRIDA_INST_SEED", "1", 1);
978
979 break;
980
981 case 'Q':
982
983 if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
984 if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; }
985
986 fsrv->qemu_mode = 1;
987 break;
988
989 case 'U':
990
991 if (unicorn_mode) { FATAL("Multiple -Q options not supported"); }
992 if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; }
993
994 unicorn_mode = 1;
995 break;
996
997 case 'W': /* Wine+QEMU mode */
998
999 if (use_wine) { FATAL("Multiple -W options not supported"); }
1000 fsrv->qemu_mode = 1;
1001 use_wine = 1;
1002
1003 if (!mem_limit_given) { fsrv->mem_limit = 0; }
1004
1005 break;
1006
1007 case 'Y': // fallthough
1008 #ifdef __linux__
1009 case 'X': /* NYX mode */
1010
1011 if (fsrv->nyx_mode) { FATAL("Multiple -X options not supported"); }
1012
1013 fsrv->nyx_mode = 1;
1014 fsrv->nyx_parent = true;
1015 fsrv->nyx_standalone = true;
1016
1017 break;
1018 #else
1019 case 'X':
1020 FATAL("Nyx mode is only availabe on linux...");
1021 break;
1022 #endif
1023
1024 case 'H': /* Hang Mode */
1025
1026 /* Minimizes a testcase to the minimum that still times out */
1027
1028 if (hang_mode) { FATAL("Multipe -H options not supported"); }
1029 if (edges_only) {
1030
1031 FATAL("Edges only and hang mode are mutually exclusive.");
1032
1033 }
1034
1035 hang_mode = 1;
1036 break;
1037
1038 case 'B': /* load bitmap */
1039
1040 /* This is a secret undocumented option! It is speculated to be useful
1041 if you have a baseline "boring" input file and another "interesting"
1042 file you want to minimize.
1043
1044 You can dump a binary bitmap for the boring file using
1045 afl-showmap -b, and then load it into afl-tmin via -B. The minimizer
1046 will then minimize to preserve only the edges that are unique to
1047 the interesting input file, but ignoring everything from the
1048 original map.
1049
1050 The option may be extended and made more official if it proves
1051 to be useful. */
1052
1053 if (mask_bitmap) { FATAL("Multiple -B options not supported"); }
1054 mask_bitmap = ck_alloc(map_size);
1055 read_bitmap(optarg, mask_bitmap, map_size);
1056 break;
1057
1058 case 'h':
1059 usage(argv[0]);
1060 return -1;
1061 break;
1062
1063 default:
1064 usage(argv[0]);
1065
1066 }
1067
1068 }
1069
1070 if (optind == argc || !in_file || !output_file) { usage(argv[0]); }
1071
1072 check_environment_vars(envp);
1073
1074 if (getenv("AFL_NO_FORKSRV")) { /* if set, use the fauxserver */
1075 fsrv->use_fauxsrv = true;
1076
1077 }
1078
1079 setenv("AFL_NO_AUTODICT", "1", 1);
1080
1081 /* initialize cmplog_mode */
1082 shm.cmplog_mode = 0;
1083
1084 atexit(at_exit_handler);
1085 setup_signal_handlers();
1086
1087 set_up_environment(fsrv, argv);
1088
1089 #ifdef __linux__
1090 if (!fsrv->nyx_mode) {
1091
1092 fsrv->target_path = find_binary(argv[optind]);
1093
1094 } else {
1095
1096 fsrv->target_path = ck_strdup(argv[optind]);
1097
1098 }
1099
1100 #else
1101 fsrv->target_path = find_binary(argv[optind]);
1102 #endif
1103
1104 fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);
1105 detect_file_args(argv + optind, out_file, &fsrv->use_stdin);
1106 signal(SIGALRM, kill_child);
1107
1108 if (fsrv->qemu_mode) {
1109
1110 if (use_wine) {
1111
1112 use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind,
1113 argv + optind);
1114
1115 } else {
1116
1117 use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind,
1118 argv + optind);
1119
1120 }
1121
1122 } else if (fsrv->cs_mode) {
1123
1124 use_argv =
1125 get_cs_argv(argv[0], &fsrv->target_path, argc - optind, argv + optind);
1126
1127 #ifdef __linux__
1128
1129 } else if (fsrv->nyx_mode) {
1130
1131 fsrv->nyx_id = 0;
1132
1133 u8 *libnyx_binary = find_afl_binary(argv[0], "libnyx.so");
1134 fsrv->nyx_handlers = afl_load_libnyx_plugin(libnyx_binary);
1135 if (fsrv->nyx_handlers == NULL) {
1136
1137 FATAL("failed to initialize libnyx.so...");
1138
1139 }
1140
1141 fsrv->nyx_use_tmp_workdir = true;
1142 fsrv->nyx_bind_cpu_id = 0;
1143
1144 use_argv = argv + optind;
1145 #endif
1146
1147 } else {
1148
1149 use_argv = argv + optind;
1150
1151 }
1152
1153 exact_mode = !!get_afl_env("AFL_TMIN_EXACT");
1154
1155 if (hang_mode && exact_mode) {
1156
1157 SAYF("AFL_TMIN_EXACT won't work for loops in hang mode, ignoring.");
1158 exact_mode = 0;
1159
1160 }
1161
1162 SAYF("\n");
1163
1164 if (getenv("AFL_FORKSRV_INIT_TMOUT")) {
1165
1166 s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT"));
1167 if (forksrv_init_tmout < 1) {
1168
1169 FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT");
1170
1171 }
1172
1173 fsrv->init_tmout = (u32)forksrv_init_tmout;
1174
1175 }
1176
1177 configure_afl_kill_signals(
1178 fsrv, NULL, NULL, (fsrv->qemu_mode || unicorn_mode) ? SIGKILL : SIGTERM);
1179
1180 if (getenv("AFL_CRASH_EXITCODE")) {
1181
1182 long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10);
1183 if ((!exitcode && (errno == EINVAL || errno == ERANGE)) ||
1184 exitcode < -127 || exitcode > 128) {
1185
1186 FATAL("Invalid crash exitcode, expected -127 to 128, but got %s",
1187 getenv("AFL_CRASH_EXITCODE"));
1188
1189 }
1190
1191 fsrv->uses_crash_exitcode = true;
1192 // WEXITSTATUS is 8 bit unsigned
1193 fsrv->crash_exitcode = (u8)exitcode;
1194
1195 }
1196
1197 shm_fuzz = ck_alloc(sizeof(sharedmem_t));
1198
1199 /* initialize cmplog_mode */
1200 shm_fuzz->cmplog_mode = 0;
1201 u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1);
1202 shm_fuzz->shmemfuzz_mode = 1;
1203 if (!map) { FATAL("BUG: Zero return from afl_shm_init."); }
1204 #ifdef USEMMAP
1205 setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1);
1206 #else
1207 u8 *shm_str = alloc_printf("%d", shm_fuzz->shm_id);
1208 setenv(SHM_FUZZ_ENV_VAR, shm_str, 1);
1209 ck_free(shm_str);
1210 #endif
1211 fsrv->support_shmem_fuzz = 1;
1212 fsrv->shmem_fuzz_len = (u32 *)map;
1213 fsrv->shmem_fuzz = map + sizeof(u32);
1214
1215 read_initial_file();
1216
1217 #ifdef __linux__
1218 if (!fsrv->nyx_mode) { (void)check_binary_signatures(fsrv->target_path); }
1219 #else
1220 (void)check_binary_signatures(fsrv->target_path);
1221 #endif
1222
1223 if (!fsrv->qemu_mode && !unicorn_mode) {
1224
1225 fsrv->map_size = 4194304; // dummy temporary value
1226 u32 new_map_size =
1227 afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon,
1228 (get_afl_env("AFL_DEBUG_CHILD") ||
1229 get_afl_env("AFL_DEBUG_CHILD_OUTPUT"))
1230 ? 1
1231 : 0);
1232
1233 if (new_map_size) {
1234
1235 if (map_size < new_map_size ||
1236 (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) {
1237
1238 if (!be_quiet)
1239 ACTF("Acquired new map size for target: %u bytes\n", new_map_size);
1240
1241 afl_shm_deinit(&shm);
1242 afl_fsrv_kill(fsrv);
1243 fsrv->map_size = new_map_size;
1244 fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0);
1245 afl_fsrv_start(fsrv, use_argv, &stop_soon,
1246 (get_afl_env("AFL_DEBUG_CHILD") ||
1247 get_afl_env("AFL_DEBUG_CHILD_OUTPUT"))
1248 ? 1
1249 : 0);
1250
1251 }
1252
1253 map_size = new_map_size;
1254
1255 }
1256
1257 fsrv->map_size = map_size;
1258
1259 } else {
1260
1261 afl_fsrv_start(fsrv, use_argv, &stop_soon,
1262 (get_afl_env("AFL_DEBUG_CHILD") ||
1263 get_afl_env("AFL_DEBUG_CHILD_OUTPUT"))
1264 ? 1
1265 : 0);
1266
1267 }
1268
1269 if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
1270 shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
1271
1272 ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
1273 fsrv->mem_limit, fsrv->exec_tmout, edges_only ? ", edges only" : "");
1274
1275 tmin_run_target(fsrv, in_data, in_len, 1);
1276
1277 if (hang_mode && !fsrv->last_run_timed_out) {
1278
1279 FATAL(
1280 "Target binary did not time out but hang minimization mode "
1281 "(-H) was set (-t %u).",
1282 fsrv->exec_tmout);
1283
1284 }
1285
1286 if (fsrv->last_run_timed_out && !hang_mode) {
1287
1288 FATAL(
1289 "Target binary times out (adjusting -t may help). Use -H to minimize a "
1290 "hang.");
1291
1292 }
1293
1294 if (hang_mode) {
1295
1296 OKF("Program hangs as expected, minimizing in " cCYA "hang" cRST " mode.");
1297
1298 } else if (!crash_mode) {
1299
1300 OKF("Program terminates normally, minimizing in " cCYA "instrumented" cRST
1301 " mode.");
1302
1303 if (!anything_set(fsrv)) { FATAL("No instrumentation detected."); }
1304
1305 } else {
1306
1307 OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST
1308 " mode.",
1309 exact_mode ? "EXACT " : "");
1310
1311 }
1312
1313 minimize(fsrv);
1314
1315 ACTF("Writing output to '%s'...", output_file);
1316
1317 unlink(out_file);
1318 if (out_file) { ck_free(out_file); }
1319 out_file = NULL;
1320
1321 close(write_to_file(output_file, in_data, in_len));
1322
1323 OKF("We're done here. Have a nice day!\n");
1324
1325 remove_shm = 0;
1326 afl_shm_deinit(&shm);
1327 if (fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
1328 afl_fsrv_deinit(fsrv);
1329 if (fsrv->target_path) { ck_free(fsrv->target_path); }
1330 if (mask_bitmap) { ck_free(mask_bitmap); }
1331 if (in_data) { ck_free(in_data); }
1332
1333 argv_cpy_free(argv);
1334
1335 exit(0);
1336
1337 }
1338
1339