xref: /aosp_15_r20/external/AFLplusplus/src/afl-tmin.c (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
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