github.com/prysmaticlabs/prysm@v1.4.4/third_party/afl/afl-showmap.c (about)

     1  /*
     2     american fuzzy lop - map display utility
     3     ----------------------------------------
     4  
     5     Written and maintained by Michal Zalewski <lcamtuf@google.com>
     6  
     7     Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. All rights reserved.
     8  
     9     Licensed under the Apache License, Version 2.0 (the "License");
    10     you may not use this file except in compliance with the License.
    11     You may obtain a copy of the License at:
    12  
    13       http://www.apache.org/licenses/LICENSE-2.0
    14  
    15     A very simple tool that runs the targeted binary and displays
    16     the contents of the trace bitmap in a human-readable form. Useful in
    17     scripts to eliminate redundant inputs and perform other checks.
    18  
    19     Exit code is 2 if the target program crashes; 1 if it times out or
    20     there is a problem executing it; or 0 if execution is successful.
    21  
    22   */
    23  
    24  #define AFL_MAIN
    25  
    26  #include "config.h"
    27  #include "types.h"
    28  #include "debug.h"
    29  #include "alloc-inl.h"
    30  #include "hash.h"
    31  
    32  #include <stdio.h>
    33  #include <unistd.h>
    34  #include <stdlib.h>
    35  #include <string.h>
    36  #include <time.h>
    37  #include <errno.h>
    38  #include <signal.h>
    39  #include <dirent.h>
    40  #include <fcntl.h>
    41  
    42  #include <sys/wait.h>
    43  #include <sys/time.h>
    44  #include <sys/shm.h>
    45  #include <sys/stat.h>
    46  #include <sys/types.h>
    47  #include <sys/resource.h>
    48  
    49  static s32 child_pid;                 /* PID of the tested program         */
    50  
    51  static u8* trace_bits;                /* SHM with instrumentation bitmap   */
    52  
    53  static u8 *out_file,                  /* Trace output file                 */
    54            *doc_path,                  /* Path to docs                      */
    55            *target_path,               /* Path to target binary             */
    56            *at_file;                   /* Substitution string for @@        */
    57  
    58  static u32 exec_tmout;                /* Exec timeout (ms)                 */
    59  
    60  static u64 mem_limit = MEM_LIMIT;     /* Memory limit (MB)                 */
    61  
    62  static s32 shm_id;                    /* ID of the SHM region              */
    63  
    64  static u8  quiet_mode,                /* Hide non-essential messages?      */
    65             edges_only,                /* Ignore hit counts?                */
    66             cmin_mode,                 /* Generate output in afl-cmin mode? */
    67             binary_mode,               /* Write output as a binary map      */
    68             keep_cores;                /* Allow coredumps?                  */
    69  
    70  static volatile u8
    71             stop_soon,                 /* Ctrl-C pressed?                   */
    72             child_timed_out,           /* Child timed out?                  */
    73             child_crashed;             /* Child crashed?                    */
    74  
    75  /* Classify tuple counts. Instead of mapping to individual bits, as in
    76     afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */
    77  
    78  static const u8 count_class_human[256] = {
    79  
    80    [0]           = 0,
    81    [1]           = 1,
    82    [2]           = 2,
    83    [3]           = 3,
    84    [4 ... 7]     = 4,
    85    [8 ... 15]    = 5,
    86    [16 ... 31]   = 6,
    87    [32 ... 127]  = 7,
    88    [128 ... 255] = 8
    89  
    90  };
    91  
    92  static const u8 count_class_binary[256] = {
    93  
    94    [0]           = 0,
    95    [1]           = 1,
    96    [2]           = 2,
    97    [3]           = 4,
    98    [4 ... 7]     = 8,
    99    [8 ... 15]    = 16,
   100    [16 ... 31]   = 32,
   101    [32 ... 127]  = 64,
   102    [128 ... 255] = 128
   103  
   104  };
   105  
   106  static void classify_counts(u8* mem, const u8* map) {
   107  
   108    u32 i = MAP_SIZE;
   109  
   110    if (edges_only) {
   111  
   112      while (i--) {
   113        if (*mem) *mem = 1;
   114        mem++;
   115      }
   116  
   117    } else {
   118  
   119      while (i--) {
   120        *mem = map[*mem];
   121        mem++;
   122      }
   123  
   124    }
   125  
   126  }
   127  
   128  
   129  /* Get rid of shared memory (atexit handler). */
   130  
   131  static void remove_shm(void) {
   132  
   133    shmctl(shm_id, IPC_RMID, NULL);
   134  
   135  }
   136  
   137  
   138  /* Configure shared memory. */
   139  
   140  static void setup_shm(void) {
   141  
   142    u8* shm_str;
   143  
   144    shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
   145  
   146    if (shm_id < 0) PFATAL("shmget() failed");
   147  
   148    atexit(remove_shm);
   149  
   150    shm_str = alloc_printf("%d", shm_id);
   151  
   152    setenv(SHM_ENV_VAR, shm_str, 1);
   153  
   154    ck_free(shm_str);
   155  
   156    trace_bits = shmat(shm_id, NULL, 0);
   157    
   158    if (!trace_bits) PFATAL("shmat() failed");
   159  
   160  }
   161  
   162  /* Write results. */
   163  
   164  static u32 write_results(void) {
   165  
   166    s32 fd;
   167    u32 i, ret = 0;
   168  
   169    u8  cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
   170        caa = !!getenv("AFL_CMIN_ALLOW_ANY");
   171  
   172    if (!strncmp(out_file, "/dev/", 5)) {
   173  
   174      fd = open(out_file, O_WRONLY, 0600);
   175      if (fd < 0) PFATAL("Unable to open '%s'", out_file);
   176  
   177    } else if (!strcmp(out_file, "-")) {
   178  
   179      fd = dup(1);
   180      if (fd < 0) PFATAL("Unable to open stdout");
   181  
   182    } else {
   183  
   184      unlink(out_file); /* Ignore errors */
   185      fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
   186      if (fd < 0) PFATAL("Unable to create '%s'", out_file);
   187  
   188    }
   189  
   190  
   191    if (binary_mode) {
   192  
   193      for (i = 0; i < MAP_SIZE; i++)
   194        if (trace_bits[i]) ret++;
   195      
   196      ck_write(fd, trace_bits, MAP_SIZE, out_file);
   197      close(fd);
   198  
   199    } else {
   200  
   201      FILE* f = fdopen(fd, "w");
   202  
   203      if (!f) PFATAL("fdopen() failed");
   204  
   205      for (i = 0; i < MAP_SIZE; i++) {
   206  
   207        if (!trace_bits[i]) continue;
   208        ret++;
   209  
   210        if (cmin_mode) {
   211  
   212          if (child_timed_out) break;
   213          if (!caa && child_crashed != cco) break;
   214  
   215          fprintf(f, "%u%u\n", trace_bits[i], i);
   216  
   217        } else fprintf(f, "%06u:%u\n", i, trace_bits[i]);
   218  
   219      }
   220    
   221      fclose(f);
   222  
   223    }
   224  
   225    return ret;
   226  
   227  }
   228  
   229  
   230  /* Handle timeout signal. */
   231  
   232  static void handle_timeout(int sig) {
   233  
   234    child_timed_out = 1;
   235    if (child_pid > 0) kill(child_pid, SIGKILL);
   236  
   237  }
   238  
   239  
   240  /* Execute target application. */
   241  
   242  static void run_target(char** argv) {
   243  
   244    static struct itimerval it;
   245    int status = 0;
   246  
   247    if (!quiet_mode)
   248      SAYF("-- Program output begins --\n" cRST);
   249  
   250    MEM_BARRIER();
   251  
   252    child_pid = fork();
   253  
   254    if (child_pid < 0) PFATAL("fork() failed");
   255  
   256    if (!child_pid) {
   257  
   258      struct rlimit r;
   259  
   260      if (quiet_mode) {
   261  
   262        s32 fd = open("/dev/null", O_RDWR);
   263  
   264        if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
   265          *(u32*)trace_bits = EXEC_FAIL_SIG;
   266          PFATAL("Descriptor initialization failed");
   267        }
   268  
   269        close(fd);
   270  
   271      }
   272  
   273      if (mem_limit) {
   274  
   275        r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
   276  
   277  #ifdef RLIMIT_AS
   278  
   279        setrlimit(RLIMIT_AS, &r); /* Ignore errors */
   280  
   281  #else
   282  
   283        setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
   284  
   285  #endif /* ^RLIMIT_AS */
   286  
   287      }
   288  
   289      if (!keep_cores) r.rlim_max = r.rlim_cur = 0;
   290      else r.rlim_max = r.rlim_cur = RLIM_INFINITY;
   291  
   292      setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
   293  
   294      if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
   295  
   296      setsid();
   297  
   298      execv(target_path, argv);
   299  
   300      *(u32*)trace_bits = EXEC_FAIL_SIG;
   301      exit(0);
   302  
   303    }
   304  
   305    /* Configure timeout, wait for child, cancel timeout. */
   306  
   307    if (exec_tmout) {
   308  
   309      child_timed_out = 0;
   310      it.it_value.tv_sec = (exec_tmout / 1000);
   311      it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
   312  
   313    }
   314  
   315    setitimer(ITIMER_REAL, &it, NULL);
   316  
   317    if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed");
   318  
   319    child_pid = 0;
   320    it.it_value.tv_sec = 0;
   321    it.it_value.tv_usec = 0;
   322    setitimer(ITIMER_REAL, &it, NULL);
   323  
   324    MEM_BARRIER();
   325  
   326    /* Clean up bitmap, analyze exit condition, etc. */
   327  
   328    if (*(u32*)trace_bits == EXEC_FAIL_SIG)
   329      FATAL("Unable to execute '%s'", argv[0]);
   330  
   331    classify_counts(trace_bits, binary_mode ?
   332                    count_class_binary : count_class_human);
   333  
   334    if (!quiet_mode)
   335      SAYF(cRST "-- Program output ends --\n");
   336  
   337    if (!child_timed_out && !stop_soon && WIFSIGNALED(status))
   338      child_crashed = 1;
   339  
   340    if (!quiet_mode) {
   341  
   342      if (child_timed_out)
   343        SAYF(cLRD "\n+++ Program timed off +++\n" cRST);
   344      else if (stop_soon)
   345        SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
   346      else if (child_crashed)
   347        SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status));
   348  
   349    }
   350  
   351  
   352  }
   353  
   354  
   355  /* Handle Ctrl-C and the like. */
   356  
   357  static void handle_stop_sig(int sig) {
   358  
   359    stop_soon = 1;
   360  
   361    if (child_pid > 0) kill(child_pid, SIGKILL);
   362  
   363  }
   364  
   365  
   366  /* Do basic preparations - persistent fds, filenames, etc. */
   367  
   368  static void set_up_environment(void) {
   369  
   370    setenv("ASAN_OPTIONS", "abort_on_error=1:"
   371                           "detect_leaks=0:"
   372                           "symbolize=0:"
   373                           "allocator_may_return_null=1", 0);
   374  
   375    setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
   376                           "symbolize=0:"
   377                           "abort_on_error=1:"
   378                           "allocator_may_return_null=1:"
   379                           "msan_track_origins=0", 0);
   380  
   381    if (getenv("AFL_PRELOAD")) {
   382      setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
   383      setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
   384    }
   385  
   386  }
   387  
   388  
   389  /* Setup signal handlers, duh. */
   390  
   391  static void setup_signal_handlers(void) {
   392  
   393    struct sigaction sa;
   394  
   395    sa.sa_handler   = NULL;
   396    sa.sa_flags     = SA_RESTART;
   397    sa.sa_sigaction = NULL;
   398  
   399    sigemptyset(&sa.sa_mask);
   400  
   401    /* Various ways of saying "stop". */
   402  
   403    sa.sa_handler = handle_stop_sig;
   404    sigaction(SIGHUP, &sa, NULL);
   405    sigaction(SIGINT, &sa, NULL);
   406    sigaction(SIGTERM, &sa, NULL);
   407  
   408    /* Exec timeout notifications. */
   409  
   410    sa.sa_handler = handle_timeout;
   411    sigaction(SIGALRM, &sa, NULL);
   412  
   413  }
   414  
   415  
   416  /* Detect @@ in args. */
   417  
   418  static void detect_file_args(char** argv) {
   419  
   420    u32 i = 0;
   421    u8* cwd = getcwd(NULL, 0);
   422  
   423    if (!cwd) PFATAL("getcwd() failed");
   424  
   425    while (argv[i]) {
   426  
   427      u8* aa_loc = strstr(argv[i], "@@");
   428  
   429      if (aa_loc) {
   430  
   431        u8 *aa_subst, *n_arg;
   432  
   433        if (!at_file) FATAL("@@ syntax is not supported by this tool.");
   434  
   435        /* Be sure that we're always using fully-qualified paths. */
   436  
   437        if (at_file[0] == '/') aa_subst = at_file;
   438        else aa_subst = alloc_printf("%s/%s", cwd, at_file);
   439  
   440        /* Construct a replacement argv value. */
   441  
   442        *aa_loc = 0;
   443        n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
   444        argv[i] = n_arg;
   445        *aa_loc = '@';
   446  
   447        if (at_file[0] != '/') ck_free(aa_subst);
   448  
   449      }
   450  
   451      i++;
   452  
   453    }
   454  
   455    free(cwd); /* not tracked */
   456  
   457  }
   458  
   459  
   460  /* Show banner. */
   461  
   462  static void show_banner(void) {
   463  
   464    SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
   465  
   466  }
   467  
   468  /* Display usage hints. */
   469  
   470  static void usage(u8* argv0) {
   471  
   472    show_banner();
   473  
   474    SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
   475  
   476         "Required parameters:\n\n"
   477  
   478         "  -o file       - file to write the trace data to\n\n"
   479  
   480         "Execution control settings:\n\n"
   481  
   482         "  -t msec       - timeout for each run (none)\n"
   483         "  -m megs       - memory limit for child process (%u MB)\n"
   484         "  -Q            - use binary-only instrumentation (QEMU mode)\n\n"
   485  
   486         "Other settings:\n\n"
   487  
   488         "  -q            - sink program's output and don't show messages\n"
   489         "  -e            - show edge coverage only, ignore hit counts\n"
   490         "  -c            - allow core dumps\n\n"
   491  
   492         "This tool displays raw tuple data captured by AFL instrumentation.\n"
   493         "For additional help, consult %s/README.\n\n" cRST,
   494  
   495         argv0, MEM_LIMIT, doc_path);
   496  
   497    exit(1);
   498  
   499  }
   500  
   501  
   502  /* Find binary. */
   503  
   504  static void find_binary(u8* fname) {
   505  
   506    u8* env_path = 0;
   507    struct stat st;
   508  
   509    if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
   510  
   511      target_path = ck_strdup(fname);
   512  
   513      if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
   514          !(st.st_mode & 0111) || st.st_size < 4)
   515        FATAL("Program '%s' not found or not executable", fname);
   516  
   517    } else {
   518  
   519      while (env_path) {
   520  
   521        u8 *cur_elem, *delim = strchr(env_path, ':');
   522  
   523        if (delim) {
   524  
   525          cur_elem = ck_alloc(delim - env_path + 1);
   526          memcpy(cur_elem, env_path, delim - env_path);
   527          delim++;
   528  
   529        } else cur_elem = ck_strdup(env_path);
   530  
   531        env_path = delim;
   532  
   533        if (cur_elem[0])
   534          target_path = alloc_printf("%s/%s", cur_elem, fname);
   535        else
   536          target_path = ck_strdup(fname);
   537  
   538        ck_free(cur_elem);
   539  
   540        if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
   541            (st.st_mode & 0111) && st.st_size >= 4) break;
   542  
   543        ck_free(target_path);
   544        target_path = 0;
   545  
   546      }
   547  
   548      if (!target_path) FATAL("Program '%s' not found or not executable", fname);
   549  
   550    }
   551  
   552  }
   553  
   554  
   555  /* Fix up argv for QEMU. */
   556  
   557  static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
   558  
   559    char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
   560    u8 *tmp, *cp, *rsl, *own_copy;
   561  
   562    /* Workaround for a QEMU stability glitch. */
   563  
   564    setenv("QEMU_LOG", "nochain", 1);
   565  
   566    memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
   567  
   568    new_argv[2] = target_path;
   569    new_argv[1] = "--";
   570  
   571    /* Now we need to actually find qemu for argv[0]. */
   572  
   573    tmp = getenv("AFL_PATH");
   574  
   575    if (tmp) {
   576  
   577      cp = alloc_printf("%s/afl-qemu-trace", tmp);
   578  
   579      if (access(cp, X_OK))
   580        FATAL("Unable to find '%s'", tmp);
   581  
   582      target_path = new_argv[0] = cp;
   583      return new_argv;
   584  
   585    }
   586  
   587    own_copy = ck_strdup(own_loc);
   588    rsl = strrchr(own_copy, '/');
   589  
   590    if (rsl) {
   591  
   592      *rsl = 0;
   593  
   594      cp = alloc_printf("%s/afl-qemu-trace", own_copy);
   595      ck_free(own_copy);
   596  
   597      if (!access(cp, X_OK)) {
   598  
   599        target_path = new_argv[0] = cp;
   600        return new_argv;
   601  
   602      }
   603  
   604    } else ck_free(own_copy);
   605  
   606    if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
   607  
   608      target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace";
   609      return new_argv;
   610  
   611    }
   612  
   613    FATAL("Unable to find 'afl-qemu-trace'.");
   614  
   615  }
   616  
   617  
   618  /* Main entry point */
   619  
   620  int main(int argc, char** argv) {
   621  
   622    s32 opt;
   623    u8  mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
   624    u32 tcnt;
   625    char** use_argv;
   626  
   627    doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
   628  
   629    while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbc")) > 0)
   630  
   631      switch (opt) {
   632  
   633        case 'o':
   634  
   635          if (out_file) FATAL("Multiple -o options not supported");
   636          out_file = optarg;
   637          break;
   638  
   639        case 'm': {
   640  
   641            u8 suffix = 'M';
   642  
   643            if (mem_limit_given) FATAL("Multiple -m options not supported");
   644            mem_limit_given = 1;
   645  
   646            if (!strcmp(optarg, "none")) {
   647  
   648              mem_limit = 0;
   649              break;
   650  
   651            }
   652  
   653            if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
   654                optarg[0] == '-') FATAL("Bad syntax used for -m");
   655  
   656            switch (suffix) {
   657  
   658              case 'T': mem_limit *= 1024 * 1024; break;
   659              case 'G': mem_limit *= 1024; break;
   660              case 'k': mem_limit /= 1024; break;
   661              case 'M': break;
   662  
   663              default:  FATAL("Unsupported suffix or bad syntax for -m");
   664  
   665            }
   666  
   667            if (mem_limit < 5) FATAL("Dangerously low value of -m");
   668  
   669            if (sizeof(rlim_t) == 4 && mem_limit > 2000)
   670              FATAL("Value of -m out of range on 32-bit systems");
   671  
   672          }
   673  
   674          break;
   675  
   676        case 't':
   677  
   678          if (timeout_given) FATAL("Multiple -t options not supported");
   679          timeout_given = 1;
   680  
   681          if (strcmp(optarg, "none")) {
   682            exec_tmout = atoi(optarg);
   683  
   684            if (exec_tmout < 20 || optarg[0] == '-')
   685              FATAL("Dangerously low value of -t");
   686  
   687          }
   688  
   689          break;
   690  
   691        case 'e':
   692  
   693          if (edges_only) FATAL("Multiple -e options not supported");
   694          edges_only = 1;
   695          break;
   696  
   697        case 'q':
   698  
   699          if (quiet_mode) FATAL("Multiple -q options not supported");
   700          quiet_mode = 1;
   701          break;
   702  
   703        case 'Z':
   704  
   705          /* This is an undocumented option to write data in the syntax expected
   706             by afl-cmin. Nobody else should have any use for this. */
   707  
   708          cmin_mode  = 1;
   709          quiet_mode = 1;
   710          break;
   711  
   712        case 'A':
   713  
   714          /* Another afl-cmin specific feature. */
   715          at_file = optarg;
   716          break;
   717  
   718        case 'Q':
   719  
   720          if (qemu_mode) FATAL("Multiple -Q options not supported");
   721          if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
   722  
   723          qemu_mode = 1;
   724          break;
   725  
   726        case 'b':
   727  
   728          /* Secret undocumented mode. Writes output in raw binary format
   729             similar to that dumped by afl-fuzz in <out_dir/queue/fuzz_bitmap. */
   730  
   731          binary_mode = 1;
   732          break;
   733  
   734        case 'c':
   735  
   736          if (keep_cores) FATAL("Multiple -c options not supported");
   737          keep_cores = 1;
   738          break;
   739  
   740        default:
   741  
   742          usage(argv[0]);
   743  
   744      }
   745  
   746    if (optind == argc || !out_file) usage(argv[0]);
   747  
   748    setup_shm();
   749    setup_signal_handlers();
   750  
   751    set_up_environment();
   752  
   753    find_binary(argv[optind]);
   754  
   755    if (!quiet_mode) {
   756      show_banner();
   757      ACTF("Executing '%s'...\n", target_path);
   758    }
   759  
   760    detect_file_args(argv + optind);
   761  
   762    if (qemu_mode)
   763      use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
   764    else
   765      use_argv = argv + optind;
   766  
   767    run_target(use_argv);
   768  
   769    tcnt = write_results();
   770  
   771    if (!quiet_mode) {
   772  
   773      if (!tcnt) FATAL("No instrumentation detected" cRST);
   774      OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file);
   775  
   776    }
   777  
   778    exit(child_crashed * 2 + child_timed_out);
   779  
   780  }