1 /*
2 american fuzzy lop++ - test case minimizer
3 ------------------------------------------
4
5 Originally written by Michal Zalewski
6
7 Forkserver design by Jann Horn <jannhorn@googlemail.com>
8
9 Now maintained by Marc Heuse <mh@mh-sec.de>,
10 Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
11 Andrea Fioraldi <andreafioraldi@gmail.com> and
12 Dominik Maier <mail@dmnk.co>
13
14 Copyright 2016, 2017 Google Inc. All rights reserved.
15 Copyright 2019-2022 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->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("ASAN_OPTIONS");
678
679 if (x) {
680
681 if (!strstr(x, "abort_on_error=1")) {
682
683 FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");
684
685 }
686
687 #ifndef ASAN_BUILD
688 if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) {
689
690 FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");
691
692 }
693
694 #endif
695
696 }
697
698 x = get_afl_env("MSAN_OPTIONS");
699
700 if (x) {
701
702 if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) {
703
704 FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
705 MSAN_ERROR) " - please fix!");
706
707 }
708
709 if (!strstr(x, "symbolize=0")) {
710
711 FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
712
713 }
714
715 }
716
717 x = get_afl_env("LSAN_OPTIONS");
718
719 if (x) {
720
721 if (!strstr(x, "symbolize=0")) {
722
723 FATAL("Custom LSAN_OPTIONS set without symbolize=0 - please fix!");
724
725 }
726
727 }
728
729 setenv("ASAN_OPTIONS",
730 "abort_on_error=1:"
731 "detect_leaks=0:"
732 "allocator_may_return_null=1:"
733 "symbolize=0:"
734 "detect_odr_violation=0:"
735 "handle_segv=0:"
736 "handle_sigbus=0:"
737 "handle_abort=0:"
738 "handle_sigfpe=0:"
739 "handle_sigill=0",
740 0);
741
742 setenv("UBSAN_OPTIONS",
743 "halt_on_error=1:"
744 "abort_on_error=1:"
745 "malloc_context_size=0:"
746 "allocator_may_return_null=1:"
747 "symbolize=0:"
748 "handle_segv=0:"
749 "handle_sigbus=0:"
750 "handle_abort=0:"
751 "handle_sigfpe=0:"
752 "handle_sigill=0",
753 0);
754
755 setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
756 "abort_on_error=1:"
757 "msan_track_origins=0"
758 "allocator_may_return_null=1:"
759 "symbolize=0:"
760 "handle_segv=0:"
761 "handle_sigbus=0:"
762 "handle_abort=0:"
763 "handle_sigfpe=0:"
764 "handle_sigill=0", 0);
765
766 setenv("LSAN_OPTIONS",
767 "exitcode=" STRINGIFY(LSAN_ERROR) ":"
768 "fast_unwind_on_malloc=0:"
769 "symbolize=0:"
770 "print_suppressions=0",
771 0);
772
773 if (get_afl_env("AFL_PRELOAD")) {
774
775 if (fsrv->qemu_mode) {
776
777 /* afl-qemu-trace takes care of converting AFL_PRELOAD. */
778
779 } else if (fsrv->frida_mode) {
780
781 afl_preload = getenv("AFL_PRELOAD");
782 u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
783 if (afl_preload) {
784
785 frida_afl_preload = alloc_printf("%s:%s", afl_preload, frida_binary);
786
787 } else {
788
789 frida_afl_preload = alloc_printf("%s", frida_binary);
790
791 }
792
793 ck_free(frida_binary);
794
795 setenv("LD_PRELOAD", frida_afl_preload, 1);
796 setenv("DYLD_INSERT_LIBRARIES", frida_afl_preload, 1);
797
798 } else {
799
800 /* CoreSight mode uses the default behavior. */
801
802 setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
803 setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
804
805 }
806
807 } else if (fsrv->frida_mode) {
808
809 u8 *frida_binary = find_afl_binary(argv[0], "afl-frida-trace.so");
810 setenv("LD_PRELOAD", frida_binary, 1);
811 setenv("DYLD_INSERT_LIBRARIES", frida_binary, 1);
812 ck_free(frida_binary);
813
814 }
815
816 if (frida_afl_preload) { ck_free(frida_afl_preload); }
817
818 }
819
820 /* Setup signal handlers, duh. */
821
setup_signal_handlers(void)822 static void setup_signal_handlers(void) {
823
824 struct sigaction sa;
825
826 sa.sa_handler = NULL;
827 sa.sa_flags = SA_RESTART;
828 sa.sa_sigaction = NULL;
829
830 sigemptyset(&sa.sa_mask);
831
832 /* Various ways of saying "stop". */
833
834 sa.sa_handler = handle_stop_sig;
835 sigaction(SIGHUP, &sa, NULL);
836 sigaction(SIGINT, &sa, NULL);
837 sigaction(SIGTERM, &sa, NULL);
838
839 }
840
841 /* Display usage hints. */
842
usage(u8 * argv0)843 static void usage(u8 *argv0) {
844
845 SAYF(
846 "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
847
848 "Required parameters:\n"
849
850 " -i file - input test case to be shrunk by the tool\n"
851 " -o file - final output location for the minimized data\n\n"
852
853 "Execution control settings:\n"
854
855 " -f file - input file read by the tested program (stdin)\n"
856 " -t msec - timeout for each run (%u ms)\n"
857 " -m megs - memory limit for child process (%u MB)\n"
858 #if defined(__linux__) && defined(__aarch64__)
859 " -A - use binary-only instrumentation (ARM CoreSight mode)\n"
860 #endif
861 " -O - use binary-only instrumentation (FRIDA mode)\n"
862 #if defined(__linux__)
863 " -Q - use binary-only instrumentation (QEMU mode)\n"
864 " -U - use unicorn-based instrumentation (Unicorn mode)\n"
865 " -W - use qemu-based instrumentation with Wine (Wine "
866 "mode)\n"
867 " (Not necessary, here for consistency with other afl-* "
868 "tools)\n"
869 #endif
870 "\n"
871
872 "Minimization settings:\n"
873
874 " -e - solve for edge coverage only, ignore hit counts\n"
875 " -x - treat non-zero exit codes as crashes\n\n"
876 " -H - minimize a hang (hang mode)\n"
877
878 "For additional tips, please consult %s/README.md.\n\n"
879
880 "Environment variables used:\n"
881 "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n"
882 "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n"
883 "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
884 "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
885 " the target was compiled for\n"
886 "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
887 "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n"
888 "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
889 "ASAN_OPTIONS: custom settings for ASAN\n"
890 " (must contain abort_on_error=1 and symbolize=0)\n"
891 "MSAN_OPTIONS: custom settings for MSAN\n"
892 " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n"
893 "TMPDIR: directory to use for temporary input files\n",
894 argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
895
896 exit(1);
897
898 }
899
900 /* Main entry point */
901
main(int argc,char ** argv_orig,char ** envp)902 int main(int argc, char **argv_orig, char **envp) {
903
904 s32 opt;
905 u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
906 char **use_argv;
907
908 char **argv = argv_cpy_dup(argc, argv_orig);
909
910 afl_forkserver_t fsrv_var = {0};
911 if (getenv("AFL_DEBUG")) { debug = 1; }
912 fsrv = &fsrv_var;
913 afl_fsrv_init(fsrv);
914 map_size = get_map_size();
915 fsrv->map_size = map_size;
916
917 doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
918
919 SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n");
920
921 while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeAOQUWHh")) > 0) {
922
923 switch (opt) {
924
925 case 'i':
926
927 if (in_file) { FATAL("Multiple -i options not supported"); }
928 in_file = optarg;
929 break;
930
931 case 'o':
932
933 if (output_file) { FATAL("Multiple -o options not supported"); }
934 output_file = optarg;
935 break;
936
937 case 'f':
938
939 if (out_file) { FATAL("Multiple -f options not supported"); }
940 fsrv->use_stdin = 0;
941 out_file = ck_strdup(optarg);
942 break;
943
944 case 'e':
945
946 if (edges_only) { FATAL("Multiple -e options not supported"); }
947 if (hang_mode) {
948
949 FATAL("Edges only and hang mode are mutually exclusive.");
950
951 }
952
953 edges_only = 1;
954 break;
955
956 case 'x':
957
958 if (exit_crash) { FATAL("Multiple -x options not supported"); }
959 exit_crash = 1;
960 break;
961
962 case 'm': {
963
964 u8 suffix = 'M';
965
966 if (mem_limit_given) { FATAL("Multiple -m options not supported"); }
967 mem_limit_given = 1;
968
969 if (!optarg) { FATAL("Wrong usage of -m"); }
970
971 if (!strcmp(optarg, "none")) {
972
973 fsrv->mem_limit = 0;
974 break;
975
976 }
977
978 if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 ||
979 optarg[0] == '-') {
980
981 FATAL("Bad syntax used for -m");
982
983 }
984
985 switch (suffix) {
986
987 case 'T':
988 fsrv->mem_limit *= 1024 * 1024;
989 break;
990 case 'G':
991 fsrv->mem_limit *= 1024;
992 break;
993 case 'k':
994 fsrv->mem_limit /= 1024;
995 break;
996 case 'M':
997 break;
998
999 default:
1000 FATAL("Unsupported suffix or bad syntax for -m");
1001
1002 }
1003
1004 if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); }
1005
1006 if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) {
1007
1008 FATAL("Value of -m out of range on 32-bit systems");
1009
1010 }
1011
1012 }
1013
1014 break;
1015
1016 case 't':
1017
1018 if (timeout_given) { FATAL("Multiple -t options not supported"); }
1019 timeout_given = 1;
1020
1021 if (!optarg) { FATAL("Wrong usage of -t"); }
1022
1023 fsrv->exec_tmout = atoi(optarg);
1024
1025 if (fsrv->exec_tmout < 10 || optarg[0] == '-') {
1026
1027 FATAL("Dangerously low value of -t");
1028
1029 }
1030
1031 break;
1032
1033 case 'A': /* CoreSight mode */
1034
1035 #if !defined(__aarch64__) || !defined(__linux__)
1036 FATAL("-A option is not supported on this platform");
1037 #endif
1038
1039 if (fsrv->cs_mode) { FATAL("Multiple -A options not supported"); }
1040
1041 fsrv->cs_mode = 1;
1042 break;
1043
1044 case 'O': /* FRIDA mode */
1045
1046 if (fsrv->frida_mode) { FATAL("Multiple -O options not supported"); }
1047
1048 fsrv->frida_mode = 1;
1049 setenv("AFL_FRIDA_INST_SEED", "1", 1);
1050
1051 break;
1052
1053 case 'Q':
1054
1055 if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); }
1056 if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; }
1057
1058 fsrv->qemu_mode = 1;
1059 break;
1060
1061 case 'U':
1062
1063 if (unicorn_mode) { FATAL("Multiple -Q options not supported"); }
1064 if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; }
1065
1066 unicorn_mode = 1;
1067 break;
1068
1069 case 'W': /* Wine+QEMU mode */
1070
1071 if (use_wine) { FATAL("Multiple -W options not supported"); }
1072 fsrv->qemu_mode = 1;
1073 use_wine = 1;
1074
1075 if (!mem_limit_given) { fsrv->mem_limit = 0; }
1076
1077 break;
1078
1079 case 'H': /* Hang Mode */
1080
1081 /* Minimizes a testcase to the minimum that still times out */
1082
1083 if (hang_mode) { FATAL("Multipe -H options not supported"); }
1084 if (edges_only) {
1085
1086 FATAL("Edges only and hang mode are mutually exclusive.");
1087
1088 }
1089
1090 hang_mode = 1;
1091 break;
1092
1093 case 'B': /* load bitmap */
1094
1095 /* This is a secret undocumented option! It is speculated to be useful
1096 if you have a baseline "boring" input file and another "interesting"
1097 file you want to minimize.
1098
1099 You can dump a binary bitmap for the boring file using
1100 afl-showmap -b, and then load it into afl-tmin via -B. The minimizer
1101 will then minimize to preserve only the edges that are unique to
1102 the interesting input file, but ignoring everything from the
1103 original map.
1104
1105 The option may be extended and made more official if it proves
1106 to be useful. */
1107
1108 if (mask_bitmap) { FATAL("Multiple -B options not supported"); }
1109 mask_bitmap = ck_alloc(map_size);
1110 read_bitmap(optarg, mask_bitmap, map_size);
1111 break;
1112
1113 case 'h':
1114 usage(argv[0]);
1115 return -1;
1116 break;
1117
1118 default:
1119 usage(argv[0]);
1120
1121 }
1122
1123 }
1124
1125 if (optind == argc || !in_file || !output_file) { usage(argv[0]); }
1126
1127 check_environment_vars(envp);
1128
1129 if (getenv("AFL_NO_FORKSRV")) { /* if set, use the fauxserver */
1130 fsrv->use_fauxsrv = true;
1131
1132 }
1133
1134 setenv("AFL_NO_AUTODICT", "1", 1);
1135
1136 /* initialize cmplog_mode */
1137 shm.cmplog_mode = 0;
1138
1139 atexit(at_exit_handler);
1140 setup_signal_handlers();
1141
1142 set_up_environment(fsrv, argv);
1143
1144 fsrv->target_path = find_binary(argv[optind]);
1145 fsrv->trace_bits = afl_shm_init(&shm, map_size, 0);
1146 detect_file_args(argv + optind, out_file, &fsrv->use_stdin);
1147 signal(SIGALRM, kill_child);
1148
1149 if (fsrv->qemu_mode) {
1150
1151 if (use_wine) {
1152
1153 use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind,
1154 argv + optind);
1155
1156 } else {
1157
1158 use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind,
1159 argv + optind);
1160
1161 }
1162
1163 } else if (fsrv->cs_mode) {
1164
1165 use_argv =
1166 get_cs_argv(argv[0], &fsrv->target_path, argc - optind, argv + optind);
1167
1168 } else {
1169
1170 use_argv = argv + optind;
1171
1172 }
1173
1174 exact_mode = !!get_afl_env("AFL_TMIN_EXACT");
1175
1176 if (hang_mode && exact_mode) {
1177
1178 SAYF("AFL_TMIN_EXACT won't work for loops in hang mode, ignoring.");
1179 exact_mode = 0;
1180
1181 }
1182
1183 SAYF("\n");
1184
1185 if (getenv("AFL_FORKSRV_INIT_TMOUT")) {
1186
1187 s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT"));
1188 if (forksrv_init_tmout < 1) {
1189
1190 FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT");
1191
1192 }
1193
1194 fsrv->init_tmout = (u32)forksrv_init_tmout;
1195
1196 }
1197
1198 fsrv->kill_signal =
1199 parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL);
1200
1201 if (getenv("AFL_CRASH_EXITCODE")) {
1202
1203 long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10);
1204 if ((!exitcode && (errno == EINVAL || errno == ERANGE)) ||
1205 exitcode < -127 || exitcode > 128) {
1206
1207 FATAL("Invalid crash exitcode, expected -127 to 128, but got %s",
1208 getenv("AFL_CRASH_EXITCODE"));
1209
1210 }
1211
1212 fsrv->uses_crash_exitcode = true;
1213 // WEXITSTATUS is 8 bit unsigned
1214 fsrv->crash_exitcode = (u8)exitcode;
1215
1216 }
1217
1218 shm_fuzz = ck_alloc(sizeof(sharedmem_t));
1219
1220 /* initialize cmplog_mode */
1221 shm_fuzz->cmplog_mode = 0;
1222 u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1);
1223 shm_fuzz->shmemfuzz_mode = 1;
1224 if (!map) { FATAL("BUG: Zero return from afl_shm_init."); }
1225 #ifdef USEMMAP
1226 setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1);
1227 #else
1228 u8 *shm_str = alloc_printf("%d", shm_fuzz->shm_id);
1229 setenv(SHM_FUZZ_ENV_VAR, shm_str, 1);
1230 ck_free(shm_str);
1231 #endif
1232 fsrv->support_shmem_fuzz = 1;
1233 fsrv->shmem_fuzz_len = (u32 *)map;
1234 fsrv->shmem_fuzz = map + sizeof(u32);
1235
1236 read_initial_file();
1237 (void)check_binary_signatures(fsrv->target_path);
1238
1239 if (!fsrv->qemu_mode && !unicorn_mode) {
1240
1241 fsrv->map_size = 4194304; // dummy temporary value
1242 u32 new_map_size =
1243 afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon,
1244 (get_afl_env("AFL_DEBUG_CHILD") ||
1245 get_afl_env("AFL_DEBUG_CHILD_OUTPUT"))
1246 ? 1
1247 : 0);
1248
1249 if (new_map_size) {
1250
1251 if (map_size < new_map_size ||
1252 (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) {
1253
1254 if (!be_quiet)
1255 ACTF("Aquired new map size for target: %u bytes\n", new_map_size);
1256
1257 afl_shm_deinit(&shm);
1258 afl_fsrv_kill(fsrv);
1259 fsrv->map_size = new_map_size;
1260 fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0);
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 map_size = new_map_size;
1270
1271 }
1272
1273 fsrv->map_size = map_size;
1274
1275 } else {
1276
1277 afl_fsrv_start(fsrv, use_argv, &stop_soon,
1278 (get_afl_env("AFL_DEBUG_CHILD") ||
1279 get_afl_env("AFL_DEBUG_CHILD_OUTPUT"))
1280 ? 1
1281 : 0);
1282
1283 }
1284
1285 if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
1286 shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
1287
1288 ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
1289 fsrv->mem_limit, fsrv->exec_tmout, edges_only ? ", edges only" : "");
1290
1291 tmin_run_target(fsrv, in_data, in_len, 1);
1292
1293 if (hang_mode && !fsrv->last_run_timed_out) {
1294
1295 FATAL(
1296 "Target binary did not time out but hang minimization mode "
1297 "(-H) was set (-t %u).",
1298 fsrv->exec_tmout);
1299
1300 }
1301
1302 if (fsrv->last_run_timed_out && !hang_mode) {
1303
1304 FATAL(
1305 "Target binary times out (adjusting -t may help). Use -H to minimize a "
1306 "hang.");
1307
1308 }
1309
1310 if (hang_mode) {
1311
1312 OKF("Program hangs as expected, minimizing in " cCYA "hang" cRST " mode.");
1313
1314 } else if (!crash_mode) {
1315
1316 OKF("Program terminates normally, minimizing in " cCYA "instrumented" cRST
1317 " mode.");
1318
1319 if (!anything_set(fsrv)) { FATAL("No instrumentation detected."); }
1320
1321 } else {
1322
1323 OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST
1324 " mode.",
1325 exact_mode ? "EXACT " : "");
1326
1327 }
1328
1329 minimize(fsrv);
1330
1331 ACTF("Writing output to '%s'...", output_file);
1332
1333 unlink(out_file);
1334 if (out_file) { ck_free(out_file); }
1335 out_file = NULL;
1336
1337 close(write_to_file(output_file, in_data, in_len));
1338
1339 OKF("We're done here. Have a nice day!\n");
1340
1341 remove_shm = 0;
1342 afl_shm_deinit(&shm);
1343 if (fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
1344 afl_fsrv_deinit(fsrv);
1345 if (fsrv->target_path) { ck_free(fsrv->target_path); }
1346 if (mask_bitmap) { ck_free(mask_bitmap); }
1347 if (in_data) { ck_free(in_data); }
1348
1349 argv_cpy_free(argv);
1350
1351 exit(0);
1352
1353 }
1354
1355