• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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