AFL源码分析之afl-fuzz.c (fuzz执行流程)

到今天通过各位师傅的博客把fuzz的流程过了一遍,

感谢各位师傅对afl分析的分享,

---------------------------------------------------------------------------fuzz的执行流程(我的理解,如有错误希望可以提醒,感谢各位师傅了)----------------------------------------------------------------------------------------------------------------------------------------------------------

首先循环开始,会先精简队列

cull_queue();(细节我就不论述了)

然后刷新展示界面show_stats()

然后调用fuzz_one()进行对样本进行变异fuzz,返回skipped_fuzz,

判断skipped_fuzz的返回值,判断是否退出fuzz还是,fuzz队列的下一个样本

主循环结束后,摧毁内存空间,关闭描述符,输出和更新一些状态

这里就不得不提到fuzz_one()函数的一些细节了(变异的过程也在这部分源代码)

  首先初始化变量、根据概率及前期轮次结果判断返回节点,映射case等,然后进入下一个阶段

  * CALIBRATION (only if failed earlier on) *该阶段主要核实case的校验错误

  * TRIMMING *该阶段主要判断case是否已经被修剪,没有过就调用trim_case()修剪一次,更新修剪后case的大小

  * PERFORMANCE SCORE *判断该case是否已经经历过deterministic阶段,如果已经经历过,直接跳转至havoc阶段

经过以上的阶段后就进入了变异的流程了

  * SIMPLE BITFLIP (+dictionary construction) * 取反变异

     stage_short = "flip1";单位取反

     stage_name  = "bitflip 2/1"; // bitflip 2 / 1相邻两位取反变异策略

    stage_name  = "bitflip 4/1";//bitflip 4/1相邻四位取反变异策略

    stage_name  = "bitflip 8/8";//bitflip 8/8 字节翻转变异策略

    stage_name  = "bitflip 16/8";//连续两字节翻转

    stage_name  = "bitflip 32/8";//连续四字节翻转

   * ARITHMETIC INC/DEC *加减变异

    stage_name  = "arith 8/8";//arith 8/8变异策略,对每个byte(8 bit)进行加减运算

    stage_name  = "arith 16/8";//arith 16/8变异策略,对每个双字(16 bit)进行加减运算

    stage_name  = "arith 32/8";//arith 32/8变异策略,对每个四字(32 bit)进行加减运算

    stage_name  = "interest 8/8";//interest 8/8 按字节替换interest数据变异策略

    stage_name  = "interest 16/8";//interest 16/8 按双字节替换interest数据变异策略

    stage_name  = "interest 32/8";//interest 32/8 按四字节替换interest数据变异策略

  * DICTIONARY STUFF *根据用户输入的extra token进行替换插入变异策略

    stage_name  = "user extras (over)";//user extras (over) 以用户指定的token替换out_buf策略

    stage_name  = "user extras (insert)"; //user extras (insert) 以用户token插入out_buf策略

    stage_name = "auto extras (over)"; //auto extras (over)替换阶段2,这里替换的不是extras token,而是a_extras token

  * RANDOM HAVOC * 随机毁灭模式(注:我最喜欢的就是这个模式,充满着各种随机性)这里我就放上师傅们的格式注释  

    检测如果没有设置splice_cycle
    标记此阶段为"havoc"
    否则标记为"splice"
    设置目前为止的stage_max、orig_hit_cnt、havoc_queued
    开始循环stage
    产生随机数use_stacking,该随机数决定一次stage中变化的次数
    循环use_stacking次
    根据是否存在extras token来决定UR()函数参数(17或15),随机产生的值作为变换方式的case选项,随机变换方式如下:
      随机选中interesting_8[]中的随机byte替换out_buf中的随机byte
      随机选择大小端序后,选中interesting_16[]中的随机word替换out_buf中的随机word
      随机选择大小端序后,选中interesting_32[]中的随机dword替换out_buf中的随机dword
      选择out_buf[]中的随机byte减随机数值
      选择out_buf[]中的随机byte加随机数值
      随机选择大小端序后,选择out_buf[]中的随机word减随机数值
      随机选择大小端序后,选择out_buf[]中的随机word加随机数值
      随机选择大小端序后,选择out_buf[]中的随机dword减随机数值
      随机选择大小端序后,选择out_buf[]中的随机dword加随机数值
      选择out_buf[]中的随机byte做异或翻转
      选择out_buf[]中的随机byte进行删除
      选择out_buf[]中的随机位置插入随机长度的内容
      这段内容有75%的概率是out_buf[]中的内容
      有25%的概率是一段相同的随机数字
      这串数字有50%的概率是随机生成的数字
      有50%的概率从out_buf[]中随机选取一个字节
      选择out_buf[]中的随机位置覆盖随机长度的内容
      这段内容有75%的概率是out_buf[]中的内容
      有25%的概率是一段相同的随机数字
      这串数字有50%的概率是随机生成的数字
      有50%的概率从out_buf[]中随机选取一个字节
      随机选取一段内容覆盖成extra token,token来自a_extras[use_extra].data或者extras[use_extra].data
      随机选取一段内容插入extra token,token来自a_extras[use_extra].data或者extras[use_extra].data
      循环叠加变换use_stacking次后调用common_fuzz_stuff函数对变换后的结果进行fuzz
    如果fuzz后queued_paths与havoc_queued不一样了,说明发现了新路径,更新stage_max、perf_score、havoc_queued
   * SPLICING *在经历过RANDOM HAVOC阶段后没有什么效果,那么就会进入到SPLICING阶段,尝试拼接两个测试用例中的内容,拼接之后重新走一遍RANDOM HAVOC阶段

   以上就是代码中的变异,每次变异都会执行fuzz记录结果和是否出现新路径,,还有一些函数也非常有意思

  sync_fuzzers()

  主要查找其它fuzzer文件夹下的queue文件夹的case,然后依次执行,如果发现了新的path就保存到自己的queque文件下(偷偷男孩?)

  seve_if_interactive()

  检查这个case的执行结果是否是interesting的,决定是否保存或跳过

  trim_case()

  上面变异前提到过的case修剪程序

  calculate_score()

  根据queue entry的执行速度、覆盖到的path数和路径深度来评估出一个得分,这个得分perf_score在后面havoc的时候使用

  common_fuzz_stuff()(真正意义上的执行程序(我认为的))

  运行文件写入变异后的数据判断运行结果,返回其它值

至此fuzz我基础上是理解了,

因为我看到sakura师傅的博客说afl-as遍历汇编语言寻找分支和函数进行插桩的做法以及不推荐了,有更好的插桩方式afl-clang-fast,通过llvm pass来插桩,可能会在以后补上这部分的源代码注释

aff-fuzz.c的源代码完整如下:(如有错误感谢提醒)

/*
  Copyright 2013 Google LLC All rights reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at:

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

/*
   american fuzzy lop - fuzzer code
   --------------------------------

   Written and maintained by Michal Zalewski <lcamtuf@google.com>

   Forkserver design by Jann Horn <jannhorn@googlemail.com>

   This is the real deal: the program takes an instrumented binary and
   attempts a variety of basic fuzzing tricks, paying close attention to
   how they affect the execution path.

*/

#define AFL_MAIN
#include "android-ashmem.h"
#define MESSAGES_TO_STDOUT

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define _FILE_OFFSET_BITS 64

#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"
#include "hash.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <dirent.h>
#include <ctype.h>
#include <fcntl.h>
#include <termios.h>
#include <dlfcn.h>
#include <sched.h>

#include <sys/wait.h>
#include <sys/time.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/file.h>

#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__)
#  include <sys/sysctl.h>
#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */

/* For systems that have sched_setaffinity; right now just Linux, but one
   can hope... */

#ifdef __linux__
#  define HAVE_AFFINITY 1
#endif /* __linux__ */

/* A toggle to export some variables when building as a library. Not very
   useful for the general public. */

#ifdef AFL_LIB
#  define EXP_ST
#else
#  define EXP_ST static
#endif /* ^AFL_LIB */

/* Lots of globals, but mostly for the status UI and other things where it
   really makes no sense to haul them around as function parameters. */


EXP_ST u8 *in_dir,                    /* Input directory with test cases  */
          *out_file,                  /* File to fuzz, if any             */
          *out_dir,                   /* Working & output directory       */
          *sync_dir,                  /* Synchronization directory        */
          *sync_id,                   /* Fuzzer ID                        */
          *use_banner,                /* Display banner                   */
          *in_bitmap,                 /* Input bitmap                     */
          *doc_path,                  /* Path to documentation dir        */
          *target_path,               /* Path to target binary            */
          *orig_cmdline;              /* Original command line            */

EXP_ST u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms)   */
static u32 hang_tmout = EXEC_TIMEOUT; /* Timeout used for hang det (ms)   */

EXP_ST u64 mem_limit  = MEM_LIMIT;    /* Memory cap for child (MB)        */

static u32 stats_update_freq = 1;     /* Stats update frequency (execs)   */

EXP_ST u8  skip_deterministic,        /* Skip deterministic stages?       */
           force_deterministic,       /* Force deterministic stages?  力确定的阶段?    */
           use_splicing,              /* Recombine input files?  再结合输入文件吗?         */
           dumb_mode,                 /* Run in non-instrumented mode?    */
           score_changed,             /* Scoring for favorites changed?   */
           kill_signal,               /* Signal that killed the child     */
           resuming_fuzz,             /* Resuming an older fuzzing job?   */
           timeout_given,             /* Specific timeout given?          */
           not_on_tty,                /* stdout is not a tty              */
           term_too_small,            /* terminal dimensions too small    */
           uses_asan,                 /* Target uses ASAN?                */
           no_forkserver,             /* Disable forkserver?              */
           crash_mode,                /* Crash mode! Yeah!                */
           in_place_resume,           /* Attempt in-place resume?         */
           auto_changed,              /* Auto-generated tokens changed?   */
           no_cpu_meter_red,          /* Feng shui on the status screen   */
           no_arith,                  /* Skip most arithmetic ops         */
           shuffle_queue,             /* Shuffle input queue?             */
           bitmap_changed = 1,        /* Time to update bitmap?           */
           qemu_mode,                 /* Running in QEMU mode?            */
           skip_requested,            /* Skip request, via SIGUSR1        */
           run_over10m,               /* Run time over 10 minutes?        */
           persistent_mode,           /* Running in persistent mode?      */
           deferred_mode,             /* Deferred forkserver mode?        */
           fast_cal;                  /* Try to calibrate faster?         */

static s32 out_fd,                    /* Persistent fd for out_file       */
           dev_urandom_fd = -1,       /* Persistent fd for /dev/urandom   */
           dev_null_fd = -1,          /* Persistent fd for /dev/null      */
           fsrv_ctl_fd,               /* Fork server control pipe (write) */
           fsrv_st_fd;                /* Fork server status pipe (read)   */

static s32 forksrv_pid,               /* PID of the fork server           */
           child_pid = -1,            /* PID of the fuzzed program        */
           out_dir_fd = -1;           /* FD of the lock file              */

EXP_ST u8* trace_bits;                /* SHM with instrumentation bitmap  */

EXP_ST u8  virgin_bits[MAP_SIZE],     /* Regions yet untouched by fuzzing */
           virgin_tmout[MAP_SIZE],    /* Bits we haven't seen in tmouts   */
           virgin_crash[MAP_SIZE];    /* Bits we haven't seen in crashes  */

static u8  var_bytes[MAP_SIZE];       /* Bytes that appear to be variable */

static s32 shm_id;                    /* ID of the SHM region             */

static volatile u8 stop_soon,         /* Ctrl-C pressed?                  */
                   clear_screen = 1,  /* Window resized?                  */
                   child_timed_out;   /* Traced process timed out?        */

EXP_ST u32 queued_paths,              /* Total number of queued testcases */
           queued_variable,           /* Testcases with variable behavior */
           queued_at_start,           /* Total number of initial inputs   */
           queued_discovered,         /* Items discovered during this run */
           queued_imported,           /* Items imported via -S            */
           queued_favored,            /* Paths deemed favorable           */
           queued_with_cov,           /* Paths with new coverage bytes    */
           pending_not_fuzzed,        /* Queued but not done yet          */
           pending_favored,           /* Pending favored paths            */
           cur_skipped_paths,         /* Abandoned inputs in cur cycle    */
           cur_depth,                 /* Current path depth               */
           max_depth,                 /* Max path depth                   */
           useless_at_start,          /* Number of useless starting paths */
           var_byte_count,            /* Bitmap bytes with var behavior   */
           current_entry,             /* Current queue entry ID           */
           havoc_div = 1;             /* Cycle count divisor for havoc    */

EXP_ST u64 total_crashes,             /* Total number of crashes          */
           unique_crashes,            /* Crashes with unique signatures   */
           total_tmouts,              /* Total number of timeouts         */
           unique_tmouts,             /* Timeouts with unique signatures  */
           unique_hangs,              /* Hangs with unique signatures     */
           total_execs,               /* Total execve() calls             */
           slowest_exec_ms,           /* Slowest testcase non hang in ms  */
           start_time,                /* Unix start time (ms)             */
           last_path_time,            /* Time for most recent path (ms)   */
           last_crash_time,           /* Time for most recent crash (ms)  */
           last_hang_time,            /* Time for most recent hang (ms)   */
           last_crash_execs,          /* Exec counter at last crash       */
           queue_cycle,               /* Queue round counter              */
           cycles_wo_finds,           /* Cycles without any new paths     */
           trim_execs,                /* Execs done to trim input files   */
           bytes_trim_in,             /* Bytes coming into the trimmer    */
           bytes_trim_out,            /* Bytes coming outa the trimmer    */
           blocks_eff_total,          /* Blocks subject to effector maps  */
           blocks_eff_select;         /* Blocks selected as fuzzable      */

static u32 subseq_tmouts;             /* Number of timeouts in a row      */

static u8 *stage_name = "init",       /* Name of the current fuzz stage   */
          *stage_short,               /* Short stage name                 */
          *syncing_party;             /* Currently syncing with...        */

static s32 stage_cur, stage_max;      /* Stage progression                */
static s32 splicing_with = -1;        /* Splicing with which test case?   */

static u32 master_id, master_max;     /* Master instance job splitting    */

static u32 syncing_case;              /* Syncing with case #...           */

static s32 stage_cur_byte,            /* Byte offset of current stage op  */
           stage_cur_val;             /* Value used for stage op          */

static u8  stage_val_type;            /* Value type (STAGE_VAL_*)         */

static u64 stage_finds[32],           /* Patterns found per fuzz stage    */
           stage_cycles[32];          /* Execs per fuzz stage             */

static u32 rand_cnt;                  /* Random number counter            */

static u64 total_cal_us,              /* Total calibration time (us)      */
           total_cal_cycles;          /* Total calibration cycles         */

static u64 total_bitmap_size,         /* Total bit count for all bitmaps  */
           total_bitmap_entries;      /* Number of bitmaps counted        */

static s32 cpu_core_count;            /* CPU core count                   */

#ifdef HAVE_AFFINITY

static s32 cpu_aff = -1;       	      /* Selected CPU core                */

#endif /* HAVE_AFFINITY */

static FILE* plot_file;               /* Gnuplot output file              */

struct queue_entry {

  u8* fname;                          /* File name for the test case      */
  u32 len;                            /* Input length                     */

  u8  cal_failed,                     /* Calibration failed?              */
      trim_done,                      /* Trimmed?                         */
      was_fuzzed,                     /* Had any fuzzing done yet?        */
      passed_det,                     /* Deterministic stages passed?     */
      has_new_cov,                    /* Triggers new coverage?           */
      var_behavior,                   /* Variable behavior?               */
      favored,                        /* Currently favored?               */
      fs_redundant;                   /* Marked as redundant in the fs?   */

  u32 bitmap_size,                    /* Number of bits set in bitmap     */
      exec_cksum;                     /* Checksum of the execution trace  */

  u64 exec_us,                        /* Execution time (us)              */
      handicap,                       /* Number of queue cycles behind    */
      depth;                          /* Path depth                       */

  u8* trace_mini;                     /* Trace bytes, if kept             */
  u32 tc_ref;                         /* Trace bytes ref count            */

  struct queue_entry *next,           /* Next element, if any             */
                     *next_100;       /* 100 elements ahead               */

};

static struct queue_entry *queue,     /* Fuzzing queue (linked list)      */
                          *queue_cur, /* Current offset within the queue  */
                          *queue_top, /* Top of the list                  */
                          *q_prev100; /* Previous 100 marker              */

static struct queue_entry*
  top_rated[MAP_SIZE];                /* Top entries for bitmap bytes     */

struct extra_data {
  u8* data;                           /* Dictionary token data            */
  u32 len;                            /* Dictionary token length          */
  u32 hit_cnt;                        /* Use count in the corpus          */
};

static struct extra_data* extras;     /* Extra tokens to fuzz with        */
static u32 extras_cnt;                /* Total number of tokens read      */

static struct extra_data* a_extras;   /* Automatically selected extras    */
static u32 a_extras_cnt;              /* Total number of tokens available */

static u8* (*post_handler)(u8* buf, u32* len);

/* Interesting values, as per config.h */

static s8  interesting_8[]  = { INTERESTING_8 };
static s16 interesting_16[] = { INTERESTING_8, INTERESTING_16 };
static s32 interesting_32[] = { INTERESTING_8, INTERESTING_16, INTERESTING_32 };

/* Fuzzing stages */

enum {
  /* 00 */ STAGE_FLIP1,
  /* 01 */ STAGE_FLIP2,
  /* 02 */ STAGE_FLIP4,
  /* 03 */ STAGE_FLIP8,
  /* 04 */ STAGE_FLIP16,
  /* 05 */ STAGE_FLIP32,
  /* 06 */ STAGE_ARITH8,
  /* 07 */ STAGE_ARITH16,
  /* 08 */ STAGE_ARITH32,
  /* 09 */ STAGE_INTEREST8,
  /* 10 */ STAGE_INTEREST16,
  /* 11 */ STAGE_INTEREST32,
  /* 12 */ STAGE_EXTRAS_UO,
  /* 13 */ STAGE_EXTRAS_UI,
  /* 14 */ STAGE_EXTRAS_AO,
  /* 15 */ STAGE_HAVOC,
  /* 16 */ STAGE_SPLICE
};

/* Stage value types */

enum {
  /* 00 */ STAGE_VAL_NONE,
  /* 01 */ STAGE_VAL_LE,
  /* 02 */ STAGE_VAL_BE
};

/* Execution status fault codes */

enum {
  /* 00 */ FAULT_NONE,
  /* 01 */ FAULT_TMOUT,
  /* 02 */ FAULT_CRASH,
  /* 03 */ FAULT_ERROR,
  /* 04 */ FAULT_NOINST,
  /* 05 */ FAULT_NOBITS
};


/* Get unix time in milliseconds */

static u64 get_cur_time(void) { //以

  struct timeval tv;
  struct timezone tz;

  gettimeofday(&tv, &tz);

  return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);

}


/* Get unix time in microseconds */

static u64 get_cur_time_us(void) {

  struct timeval tv;
  struct timezone tz;

  gettimeofday(&tv, &tz);

  return (tv.tv_sec * 1000000ULL) + tv.tv_usec;

}


/* Generate a random number (from 0 to limit - 1). This may
   have slight bias. */

static inline u32 UR(u32 limit) {

  if (unlikely(!rand_cnt--)) {

    u32 seed[2];

    ck_read(dev_urandom_fd, &seed, sizeof(seed), "/dev/urandom");

    srandom(seed[0]);
    rand_cnt = (RESEED_RNG / 2) + (seed[1] % RESEED_RNG);

  }

  return random() % limit;

}


/* Shuffle an array of pointers. Might be slightly biased. */

static void shuffle_ptrs(void** ptrs, u32 cnt) {

  u32 i;

  for (i = 0; i < cnt - 2; i++) {

    u32 j = i + UR(cnt - i);
    void *s = ptrs[i];
    ptrs[i] = ptrs[j];
    ptrs[j] = s;

  }

}


#ifdef HAVE_AFFINITY

/* Build a list of processes bound to specific cores. Returns -1 if nothing
   can be found. Assumes an upper bound of 4k CPUs. */

static void bind_to_free_cpu(void) {

  DIR* d;
  struct dirent* de;
  cpu_set_t c;

  u8 cpu_used[4096] = { 0 };
  u32 i;

  if (cpu_core_count < 2) return;

  if (getenv("AFL_NO_AFFINITY")) {

    WARNF("Not binding to a CPU core (AFL_NO_AFFINITY set).");
    return;

  }

  d = opendir("/proc");

  if (!d) {

    WARNF("Unable to access /proc - can't scan for free CPU cores.");
    return;

  }

  ACTF("Checking CPU core loadout...");

  /* Introduce some jitter, in case multiple AFL tasks are doing the same
     thing at the same time... */

  usleep(R(1000) * 250);

  /* Scan all /proc/<pid>/status entries, checking for Cpus_allowed_list.
     Flag all processes bound to a specific CPU using cpu_used[]. This will
     fail for some exotic binding setups, but is likely good enough in almost
     all real-world use cases. */

  while ((de = readdir(d))) {

    u8* fn;
    FILE* f;
    u8 tmp[MAX_LINE];
    u8 has_vmsize = 0;

    if (!isdigit(de->d_name[0])) continue;

    fn = alloc_printf("/proc/%s/status", de->d_name);

    if (!(f = fopen(fn, "r"))) {
      ck_free(fn);
      continue;
    }

    while (fgets(tmp, MAX_LINE, f)) {

      u32 hval;

      /* Processes without VmSize are probably kernel tasks. */

      if (!strncmp(tmp, "VmSize:\t", 8)) has_vmsize = 1;

      if (!strncmp(tmp, "Cpus_allowed_list:\t", 19) &&
          !strchr(tmp, '-') && !strchr(tmp, ',') &&
          sscanf(tmp + 19, "%u", &hval) == 1 && hval < sizeof(cpu_used) &&
          has_vmsize) {

        cpu_used[hval] = 1;
        break;

      }

    }

    ck_free(fn);
    fclose(f);

  }

  closedir(d);

  for (i = 0; i < cpu_core_count; i++) if (!cpu_used[i]) break;

  if (i == cpu_core_count) {

    SAYF("\n" cLRD "[-] " cRST
         "Uh-oh, looks like all %u CPU cores on your system are allocated to\n"
         "    other instances of afl-fuzz (or similar CPU-locked tasks). Starting\n"
         "    another fuzzer on this machine is probably a bad plan, but if you are\n"
         "    absolutely sure, you can set AFL_NO_AFFINITY and try again.\n",
         cpu_core_count);

    FATAL("No more free CPU cores");

  }

  OKF("Found a free CPU core, binding to #%u.", i);

  cpu_aff = i;

  CPU_ZERO(&c);
  CPU_SET(i, &c);

  if (sched_setaffinity(0, sizeof(c), &c))
    PFATAL("sched_setaffinity failed");

}

#endif /* HAVE_AFFINITY */

#ifndef IGNORE_FINDS

/* Helper function to compare buffers; returns first and last differing offset. We
   use this to find reasonable locations for splicing two files. */

static void locate_diffs(u8* ptr1, u8* ptr2, u32 len, s32* first, s32* last) {

  s32 f_loc = -1;
  s32 l_loc = -1;
  u32 pos;

  for (pos = 0; pos < len; pos++) {

    if (*(ptr1++) != *(ptr2++)) {

      if (f_loc == -1) f_loc = pos;
      l_loc = pos;

    }

  }

  *first = f_loc;
  *last = l_loc;

  return;

}

#endif /* !IGNORE_FINDS */


/* Describe integer. Uses 12 cyclic static buffers for return values. The value
   returned should be five characters or less for all the integers we reasonably
   expect to see. */

static u8* DI(u64 val) {

  static u8 tmp[12][16];
  static u8 cur;

  cur = (cur + 1) % 12;

#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) do { \
    if (val < (_divisor) * (_limit_mult)) { \
      sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \
      return tmp[cur]; \
    } \
  } while (0)

  /* 0-9999 */
  CHK_FORMAT(1, 10000, "%llu", u64);

  /* 10.0k - 99.9k */
  CHK_FORMAT(1000, 99.95, "%0.01fk", double);

  /* 100k - 999k */
  CHK_FORMAT(1000, 1000, "%lluk", u64);

  /* 1.00M - 9.99M */
  CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double);

  /* 10.0M - 99.9M */
  CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double);

  /* 100M - 999M */
  CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64);

  /* 1.00G - 9.99G */
  CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double);

  /* 10.0G - 99.9G */
  CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double);

  /* 100G - 999G */
  CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64);

  /* 1.00T - 9.99G */
  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double);

  /* 10.0T - 99.9T */
  CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double);

  /* 100T+ */
  strcpy(tmp[cur], "infty");
  return tmp[cur];

}


/* Describe float. Similar to the above, except with a single 
   static buffer. */

static u8* DF(double val) {

  static u8 tmp[16];

  if (val < 99.995) {
    sprintf(tmp, "%0.02f", val);
    return tmp;
  }

  if (val < 999.95) {
    sprintf(tmp, "%0.01f", val);
    return tmp;
  }

  return DI((u64)val);

}


/* Describe integer as memory size. */

static u8* DMS(u64 val) {

  static u8 tmp[12][16];
  static u8 cur;

  cur = (cur + 1) % 12;

  /* 0-9999 */
  CHK_FORMAT(1, 10000, "%llu B", u64);

  /* 10.0k - 99.9k */
  CHK_FORMAT(1024, 99.95, "%0.01f kB", double);

  /* 100k - 999k */
  CHK_FORMAT(1024, 1000, "%llu kB", u64);

  /* 1.00M - 9.99M */
  CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double);

  /* 10.0M - 99.9M */
  CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double);

  /* 100M - 999M */
  CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64);

  /* 1.00G - 9.99G */
  CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double);

  /* 10.0G - 99.9G */
  CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double);

  /* 100G - 999G */
  CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64);

  /* 1.00T - 9.99G */
  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double);

  /* 10.0T - 99.9T */
  CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double);

#undef CHK_FORMAT

  /* 100T+ */
  strcpy(tmp[cur], "infty");
  return tmp[cur];

}


/* Describe time delta. Returns one static buffer, 34 chars of less. */

static u8* DTD(u64 cur_ms, u64 event_ms) {

  static u8 tmp[64];
  u64 delta;
  s32 t_d, t_h, t_m, t_s;

  if (!event_ms) return "none seen yet";

  delta = cur_ms - event_ms;

  t_d = delta / 1000 / 60 / 60 / 24;
  t_h = (delta / 1000 / 60 / 60) % 24;
  t_m = (delta / 1000 / 60) % 60;
  t_s = (delta / 1000) % 60;

  sprintf(tmp, "%s days, %u hrs, %u min, %u sec", DI(t_d), t_h, t_m, t_s);
  return tmp;

}


/* Mark deterministic checks as done for a particular queue entry. We use the
   .state file to avoid repeating deterministic fuzzing when resuming aborted
   scans. */

static void mark_as_det_done(struct queue_entry* q) {

  u8* fn = strrchr(q->fname, '/');
  s32 fd;

  fn = alloc_printf("%s/queue/.state/deterministic_done/%s", out_dir, fn + 1);

  fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (fd < 0) PFATAL("Unable to create '%s'", fn);
  close(fd);

  ck_free(fn);

  q->passed_det = 1;

}


/* Mark as variable. Create symlinks if possible to make it easier to examine
   the files. */

static void mark_as_variable(struct queue_entry* q) {

  u8 *fn = strrchr(q->fname, '/') + 1, *ldest;

  ldest = alloc_printf("../../%s", fn);
  fn = alloc_printf("%s/queue/.state/variable_behavior/%s", out_dir, fn);

  if (symlink(ldest, fn)) {

    s32 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
    if (fd < 0) PFATAL("Unable to create '%s'", fn);
    close(fd);

  }

  ck_free(ldest);
  ck_free(fn);

  q->var_behavior = 1;

}


/* Mark / unmark as redundant (edge-only). This is not used for restoring state,
   but may be useful for post-processing datasets. */

static void mark_as_redundant(struct queue_entry* q, u8 state) { //标记/取消标记为冗余(仅限边缘)。这并不用于恢复状态,但可能对后处理数据集有用

  u8* fn;
  s32 fd;

  if (state == q->fs_redundant) return;//如果state == q->fs_redundant,直接返回

  q->fs_redundant = state; //如果不相等就将state值赋值给fs_redundant

  fn = strrchr(q->fname, '/');
  fn = alloc_printf("%s/queue/.state/redundant_edges/%s", out_dir, fn + 1);//拼接路径

  if (state) { //如果state为1
  {

    fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);//尝试创建out_dir/queue/.state/redundant_edges/fname
    if (fd < 0) PFATAL("Unable to create '%s'", fn);
    close(fd);

  } else { //如果state为0

    if (unlink(fn)) PFATAL("Unable to remove '%s'", fn);//尝试删除路径out_dir/queue/.state/redundant_edges/fname

  }

  ck_free(fn);

}


/* Append new test case to the queue. */

static void add_to_queue(u8* fname, u32 len, u8 passed_det) {

  struct queue_entry* q = ck_alloc(sizeof(struct queue_entry));

  q->fname        = fname;
  q->len          = len;
  q->depth        = cur_depth + 1;
  q->passed_det   = passed_det;

  if (q->depth > max_depth) max_depth = q->depth;

  if (queue_top) {

    queue_top->next = q;
    queue_top = q;

  } else q_prev100 = queue = queue_top = q;

  queued_paths++;
  pending_not_fuzzed++;

  cycles_wo_finds = 0;

  if (!(queued_paths % 100)) {

    q_prev100->next_100 = q;
    q_prev100 = q;

  }

  last_path_time = get_cur_time();

}


/* Destroy the entire queue. */

EXP_ST void destroy_queue(void) {

  struct queue_entry *q = queue, *n;

  while (q) {

    n = q->next;
    ck_free(q->fname);
    ck_free(q->trace_mini);
    ck_free(q);
    q = n;

  }

}


/* Write bitmap to file. The bitmap is useful mostly for the secret
   -B option, to focus a separate fuzzing session on a particular
   interesting input without rediscovering all the others. */

EXP_ST void write_bitmap(void) {

  u8* fname;
  s32 fd;

  if (!bitmap_changed) return;
  bitmap_changed = 0;

  fname = alloc_printf("%s/fuzz_bitmap", out_dir);
  fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0600);

  if (fd < 0) PFATAL("Unable to open '%s'", fname);

  ck_write(fd, virgin_bits, MAP_SIZE, fname);

  close(fd);
  ck_free(fname);

}


/* Read bitmap from file. This is for the -B option again. */

EXP_ST void read_bitmap(u8* fname) {

  s32 fd = open(fname, O_RDONLY);

  if (fd < 0) PFATAL("Unable to open '%s'", fname);

  ck_read(fd, virgin_bits, MAP_SIZE, fname);

  close(fd);

}


/* Check if the current execution path brings anything new to the table.
   Update virgin bits to reflect the finds. Returns 1 if the only change is
   the hit-count for a particular tuple; 2 if there are new tuples seen. 
   Updates the map, so subsequent calls will always return 0.

   This function is called after every exec() on a fairly large buffer, so
   it needs to be fast. We do this in 32-bit and 64-bit flavors. */

static inline u8 has_new_bits(u8* virgin_map) {//检查有没有新路径或者某个路径的执行次数有所不同

#ifdef WORD_SIZE_64//64位实现

  u64* current = (u64*)trace_bits;//current指向trace_bits的首地址
  u64* virgin  = (u64*)virgin_map;//virgin指向virgin_map的首地址

  u32  i = (MAP_SIZE >> 3); //MAP_SIZE >> 3代表MAP_SIZE除以2的3次方,即MAP_SIZE/8,按照8个字节一组,一共分了i组

#else //32位实现

  u32* current = (u32*)trace_bits;
  u32* virgin  = (u32*)virgin_map;

  u32  i = (MAP_SIZE >> 2);; //MAP_SIZE >> 2代表MAP_SIZE除以2的2次方,即MAP_SIZE/4,按照4个字节一组,一共分了i组


#endif /* ^WORD_SIZE_64 */

  u8   ret = 0;

  while (i--) {//循环遍历分组

    /* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap
       that have not been already cleared from the virgin map - since this will
       almost always be the case. */

    if (unlikely(*current) && unlikely(*current & *virgin)) { //如果*current不为0且*current & *virgin不为0,代表current发现了新路径或者某条路径的执行次数和之前有所不同

      if (likely(ret < 2)) {//如果ret<2

        u8* cur = (u8*)current;//cur指向current第一个字节
        u8* vir = (u8*)virgin; //vir指向virgin第一个字节

        /* Looks like we have not found any new bytes yet; see if any non-zero
           bytes in current[] are pristine in virgin[]. */

#ifdef WORD_SIZE_64

        if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) ||
            (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff) ||
            (cur[4] && vir[4] == 0xff) || (cur[5] && vir[5] == 0xff) ||
            (cur[6] && vir[6] == 0xff) || (cur[7] && vir[7] == 0xff)) ret = 2;//判断cur[i] && vir[i]==0xff,有一个为真 同时设置ret为2,这代表发现了之前没有出现过的tuple
        else ret = 1;//否则设置ret为1,代表只是命中次数更新

#else

        if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) ||
            (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff)) ret = 2;
        else ret = 1;

#endif /* ^WORD_SIZE_64 */

      }

      *virgin &= ~*current;

    }

    current++;
    virgin++; //current和virgin移动到下一个8字节,直到MAP_ZIE全被遍历完

  }

  if (ret && virgin_map == virgin_bits) bitmap_changed = 1;//如果传入参数virgin_map的值为virgin_bits,且ret不为0//设置bitmap_changed为1

  return ret;//设置bitmap_changed为1

}


/* Count the number of bits set in the provided bitmap. Used for the status
   screen several times every second, does not have to be fast. */

static u32 count_bits(u8* mem) {

  u32* ptr = (u32*)mem;
  u32  i   = (MAP_SIZE >> 2);
  u32  ret = 0;

  while (i--) {

    u32 v = *(ptr++);

    /* This gets called on the inverse, virgin bitmap; optimize for sparse
       data. */

    if (v == 0xffffffff) {
      ret += 32;
      continue;
    }

    v -= ((v >> 1) & 0x55555555);
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
    ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24;

  }

  return ret;

}


#define FF(_b)  (0xff << ((_b) << 3))

/* Count the number of bytes set in the bitmap. Called fairly sporadically,
   mostly to update the status screen or calibrate and examine confirmed
   new paths. */

static u32 count_bytes(u8* mem) {

  u32* ptr = (u32*)mem;
  u32  i   = (MAP_SIZE >> 2);
  u32  ret = 0;

  while (i--) {

    u32 v = *(ptr++);

    if (!v) continue;
    if (v & FF(0)) ret++;
    if (v & FF(1)) ret++;
    if (v & FF(2)) ret++;
    if (v & FF(3)) ret++;

  }

  return ret;

}


/* Count the number of non-255 bytes set in the bitmap. Used strictly for the
   status screen, several calls per second or so. */

static u32 count_non_255_bytes(u8* mem) {

  u32* ptr = (u32*)mem;
  u32  i   = (MAP_SIZE >> 2);
  u32  ret = 0;

  while (i--) {

    u32 v = *(ptr++);

    /* This is called on the virgin bitmap, so optimize for the most likely
       case. */

    if (v == 0xffffffff) continue;
    if ((v & FF(0)) != FF(0)) ret++;
    if ((v & FF(1)) != FF(1)) ret++;
    if ((v & FF(2)) != FF(2)) ret++;
    if ((v & FF(3)) != FF(3)) ret++;

  }

  return ret;

}


/* Destructively simplify trace by eliminating hit count information
   and replacing it with 0x80 or 0x01 depending on whether the tuple
   is hit or not. Called on every new crash or timeout, should be
   reasonably fast. */

static const u8 simplify_lookup[256] = { 

  [0]         = 1,
  [1 ... 255] = 128

};

#ifdef WORD_SIZE_64

static void simplify_trace(u64* mem) {

  u32 i = MAP_SIZE >> 3;

  while (i--) {

    /* Optimize for sparse bitmaps. */

    if (unlikely(*mem)) {

      u8* mem8 = (u8*)mem;

      mem8[0] = simplify_lookup[mem8[0]];
      mem8[1] = simplify_lookup[mem8[1]];
      mem8[2] = simplify_lookup[mem8[2]];
      mem8[3] = simplify_lookup[mem8[3]];
      mem8[4] = simplify_lookup[mem8[4]];
      mem8[5] = simplify_lookup[mem8[5]];
      mem8[6] = simplify_lookup[mem8[6]];
      mem8[7] = simplify_lookup[mem8[7]];

    } else *mem = 0x0101010101010101ULL;

    mem++;

  }

}

#else

static void simplify_trace(u32* mem) {

  u32 i = MAP_SIZE >> 2;

  while (i--) {

    /* Optimize for sparse bitmaps. */

    if (unlikely(*mem)) {

      u8* mem8 = (u8*)mem;

      mem8[0] = simplify_lookup[mem8[0]];
      mem8[1] = simplify_lookup[mem8[1]];
      mem8[2] = simplify_lookup[mem8[2]];
      mem8[3] = simplify_lookup[mem8[3]];

    } else *mem = 0x01010101;

    mem++;
  }

}

#endif /* ^WORD_SIZE_64 */


/* Destructively classify execution counts in a trace. This is used as a
   preprocessing step for any newly acquired traces. Called on every exec,
   must be fast. */

static const u8 count_class_lookup8[256] = { //统计便利路径的数量

  [0]           = 0,
  [1]           = 1,
  [2]           = 2,
  [3]           = 4,
  [4 ... 7]     = 8, //便利了4到7次就认作8次
  [8 ... 15]    = 16,//遍历8次到15次被认作16次
  [16 ... 31]   = 32,//遍历16次到31次被认作32次
  [32 ... 127]  = 64,//遍历32次到127次被认作64次
  [128 ... 255] = 128//遍历128次到255次被认作128次

};

static u16 count_class_lookup16[65536];


EXP_ST void init_count_class16(void) {

  u32 b1, b2;

  for (b1 = 0; b1 < 256; b1++) 
    for (b2 = 0; b2 < 256; b2++)//将count_class_lookup16[65536]拆分成两个嵌套的256次循环,256x256=65536
      count_class_lookup16[(b1 << 8) + b2] = 
        (count_class_lookup8[b1] << 8) |
        count_class_lookup8[b2];//count_class_lookup8[b1]左移8的结果同count_class_lookup8[b2]进行按位或操作,结果放在b1左移8加b2下标的count_class_lookup16数组成员中

}


#ifdef WORD_SIZE_64

static inline void classify_counts(u64* mem) {

  u32 i = MAP_SIZE >> 3;//每8个字节一组,一共i组

  while (i--) {//循环遍历每组

    /* Optimize for sparse bitmaps. */

    if (unlikely(*mem)) {//如果对应的mem不为0

      u16* mem16 = (u16*)mem; / 每次取两个字节

      mem16[0] = count_class_lookup16[mem16[0]];
      mem16[1] = count_class_lookup16[mem16[1]];
      mem16[2] = count_class_lookup16[mem16[2]];
      mem16[3] = count_class_lookup16[mem16[3]];//i从0~4,计算mem[i]的值,在count_class_lookup16[mem16[i]]中找到对应的取值付给mem16[i]

    }

    mem++;

  }

}

#else

static inline void classify_counts(u32* mem) {

  u32 i = MAP_SIZE >> 2;

  while (i--) {

    /* Optimize for sparse bitmaps. */

    if (unlikely(*mem)) {

      u16* mem16 = (u16*)mem;

      mem16[0] = count_class_lookup16[mem16[0]];
      mem16[1] = count_class_lookup16[mem16[1]];

    }

    mem++;

  }

}

#endif /* ^WORD_SIZE_64 */


/* Get rid of shared memory (atexit handler). */

static void remove_shm(void) {

  shmctl(shm_id, IPC_RMID, NULL);

}


/* Compact trace bytes into a smaller bitmap. We effectively just drop the
   count information here. This is called only sporadically, for some
   new paths. */

static void minimize_bits(u8* dst, u8* src) {

  u32 i = 0;

  while (i < MAP_SIZE) {  //*(src++)每次去trace_bits[]数组中一个元素的值 //i>>3获得dst的index,即这个数据会被压缩到dst[index]的bit中

    if (*(src++)) dst[i >> 3] |= 1 << (i & 7); //i & 7,不考虑高位的情况下只保留低三位,相当于dst[]byte中的哪一个bit //1 << (i & 7),把1放在表示对应数字的bit上
    i++;  //遍历下一个

  }

}


/* When we bump into a new path, we call this to see if the path appears
   more "favorable" than any of the existing ones. The purpose of the
   "favorables" is to have a minimal set of paths that trigger all the bits
   seen in the bitmap so far, and focus on fuzzing them at the expense of
   the rest.

   The first step of the process is to maintain a list of top_rated[] entries
   for every byte in the bitmap. We win that slot if there is no previous
   contender, or if the contender has a more favorable speed x size factor. */

static void update_bitmap_score(struct queue_entry* q) {

  u32 i;
  u64 fav_factor = q->exec_us * q->len;//fav_factor=该case的执行时间乘以样例大小,以这两个指标来衡量favorable的权重,越小越优

  /* For every byte set in trace_bits[], see if there is a previous winner,
     and how it compares to us. */

  for (i = 0; i < MAP_SIZE; i++)//遍历trace_bits数组

    if (trace_bits[i]) {//如果当前trace_bits数组数组值不为0,代表这是已经覆盖到的path

       if (top_rated[i]) {//如果该path对应的top_rated存在

         /* Faster-executing or smaller test cases are favored. */

         if (fav_factor > top_rated[i]->exec_us * top_rated[i]->len) continue;//如果fav_factor > 本次path的乘积,代表top_rated[i]的更优,则跳出循环遍历下一个path

         /* Looks like we're going to win. Decrease ref count for the
            previous winner, discard its trace_bits[] if necessary. */

         if (!--top_rated[i]->tc_ref) {
             // 如果该path长度top_rated乘积更大,则将top_rated对应的tc_ref字段 - 1
           ck_free(top_rated[i]->trace_mini);
           top_rated[i]->trace_mini = 0;//释放并清空trace_mini
         }

       }

       /* Insert ourselves as the new winner. */

       top_rated[i] = q;//设置top_rated为当前case,替换为更优
       q->tc_ref++;//tc_ref+1


       if (!q->trace_mini) {//如果trace_mini为空
         q->trace_mini = ck_alloc(MAP_SIZE >> 3);
         minimize_bits(q->trace_mini, trace_bits);//将trace_bits经过minimize_bits压缩,存放在trace_mini中
       }

       score_changed = 1;//设置score_changed为1

     }

}


/* The second part of the mechanism discussed above is a routine that
   goes over top_rated[] entries, and then sequentially grabs winners for
   previously-unseen bytes (temp_v) and marks them as favored, at least
   until the next run. The favored entries are given more air time during
   all fuzzing steps. */

static void cull_queue(void) { //精简队列

  struct queue_entry* q;
  static u8 temp_v[MAP_SIZE >> 3];
  u32 i;

  if (dumb_mode || !score_changed) return;//如果设置了dumb_mode或没有设置scor_changed,直接return

  score_changed = 0;//设置score_changed为0

  memset(temp_v, 255, MAP_SIZE >> 3);//初始化rmp_v数组,初始值为0xff,每位如果为1代表还没有被覆盖到,如果为0就代表已经被覆盖到了

  queued_favored  = 0;//设置queued_favored为0
  pending_favored = 0;//设置pending_favored为0

  q = queue;

  while (q) {//循环队列
    q->favored = 0;//设置favored都为0
    q = q->next;//设置favored都为0
  }

  /* Let's see if anything in the bitmap isn't captured in temp_v.
     If yes, and if it has a top_rated[] contender, let's use it. */

  for (i = 0; i < MAP_SIZE; i++)
    if (top_rated[i] && (temp_v[i >> 3] & (1 << (i & 7)))) {//判断该path对应的bit有没有被置位

      u32 j = MAP_SIZE >> 3;

      /* Remove all bits belonging to the current entry from temp_v. */

      while (j--) 
        if (top_rated[i]->trace_mini[j]) //如果top_rated[i]有值,且该path在tmp_v里被置位
          temp_v[j] &= ~top_rated[i]->trace_mini[j];//从temp_v中清除掉所有top_rated[i]覆盖到的path,将对应的bit设置为0

      top_rated[i]->favored = 1; //设置本次favored为1
      queued_favored++;//queued_favored计数器+1

      if (!top_rated[i]->was_fuzzed) pending_favored++;//如果top_rated[i]->was_fuzzed被设置为0,证明还没有被fuzz过//pending_favored计数器+1

    }

  q = queue;

  while (q) {//遍历队列
    mark_as_redundant(q, !q->favored); //如果不是favored的case,就被标记成redundant_edges
    q = q->next;
  }

}


/* Configure shared memory and virgin_bits. This is called at startup. */

EXP_ST void setup_shm(void) {//virgin_bits:记录整体tuple信息,通过virgin_tmout和virgin_crash记录fuzz过程中出现的所有目标程序超时及崩溃的tuple信息
     //trace_bits:记录当前的tuple信息,位于共享内存上,便于进行进程间通信
  u8* shm_str;

  if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE); //如果in_bitmap为空,就初始化virgin_bits的空间

  memset(virgin_tmout, 255, MAP_SIZE);//初始化virgin_tmout[MAP_SIZE]数组,每个元素值为‘255’(\xff)
  memset(virgin_crash, 255, MAP_SIZE);//初始化virgin_tmout[MAP_SIZE]数组,每个元素值为‘255’(\xff)

  shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
  /*shm_id为shmget()函数返回的共享内存标识符
 shmget()函数创建共享内存,IPC_PRIVATE为0所以创建一块新的共享内存,内存空间为MAP_SIZE字节
 权限:IPC_CREAT如果共享内存不存在,则创建一个共享内存,否则打开操作
      IPC_EXCL只有在共享内存不存在的时候,新的共享内存才创建,否则产生错误
      0600代表拥有者具有读写权限*/
  if (shm_id < 0) PFATAL("shmget() failed"); //如果内存创建失败 则抛出异常

  atexit(remove_shm);//注册程序正常终止时删除共享内存 remove_shm可见1213行,堆内存进行删除操作

  shm_str = alloc_printf("%d", shm_id); //创建shm_str字符串变量,里面存放的还是shm_id

  /* If somebody is asking us to fuzz instrumented binaries in dumb mode,
     we don't want them to detect instrumentation, since we won't be sending
     fork server commands. This should be replaced with better auto-detection
     later on, perhaps? */

  if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1); //如果没有设置dumb_mode模式,则设置环境变量SHM_ENV_VAR的值为shm_str

  ck_free(shm_str);//然后释放掉shm_str的指针

  trace_bits = shmat(shm_id, NULL, 0); //调用shmat()函数启动对段块共享内存的访问,调用成功将trace_bits变量赋值为指向共享内存第一个字节的指针
  
  if (trace_bits == (void *)-1) PFATAL("shmat() failed");//如果调用失败就返回-1然后抛出异常

}


/* Load postprocessor, if available. */

static void setup_post(void) {

  void* dh;
  u8* fn = getenv("AFL_POST_LIBRARY"); //获取环境变量AFL_POST_LIBRARY
  u32 tlen = 6;

  if (!fn) return; //如果没有设置环境变量AFL_POST_LIBRARY,则直接返回

  ACTF("Loading postprocessor from '%s'...", fn);//输出环境变量AFL_POST_LIBRARY

  dh = dlopen(fn, RTLD_NOW);//以RTLD_NOW模式打开AFL_POST_LIBRARY环境变量指向的动态链接库,在返回前解析出所有未定义的符号
  if (!dh) FATAL("%s", dlerror());//如果没有dlopen出相关信息,则直接报错

  post_handler = dlsym(dh, "afl_postprocess");//获取动态链接库中的afl_postprocess的地址然后赋值给post_handler
  if (!post_handler) FATAL("Symbol 'afl_postprocess' not found.");//没有获取成功就报错

  /* Do a quick test. It's better to segfault now than later =) */

  post_handler("hello", &tlen);//afl_postprocess()函数测试

  OKF("Postprocessor installed successfully.");

}


/* Read all testcases from the input directory, then queue them for testing.
   Called at startup. */

static void read_testcases(void) {
    //从输入(input)读取测试用例并入队,在启动时调用
  struct dirent **nl;
  s32 nl_cnt;
  u32 i;
  u8* fn;

  /* Auto-detect non-in-place resumption attempts. */

  fn = alloc_printf("%s/queue", in_dir);//fn:“in_dir/queue”
  if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn);//access函数检查in_dir/queue文件是否存在(f_ok表示文件是否存在)如果路径存在返回0,否则返回-1

  ACTF("Scanning '%s'...", in_dir);

  /* We use scandir() + alphasort() rather than readdir() because otherwise,
     the ordering  of test cases would vary somewhat randomly and would be
     difficult to control. */
     //调用scandir扫描in_dir,并将扫描结果排序后放在dirnet结构体变量nl中,匹配到的文件个数存储在nl_cnt中。这里不使用readdir是因为测试用例的顺序将会随机的变化
  nl_cnt = scandir(in_dir, &nl, NULL, alphasort);

  if (nl_cnt < 0) { //如果没有扫描到文件

    if (errno == ENOENT || errno == ENOTDIR) //抛出异常

      SAYF("\n" cLRD "[-] " cRST
           "The input directory does not seem to be valid - try again. The fuzzer needs\n"
           "    one or more test case to start with - ideally, a small file under 1 kB\n"
           "    or so. The cases must be stored as regular files directly in the input\n"
           "    directory.\n");

    PFATAL("Unable to open '%s'", in_dir);

  }

  if (shuffle_queue && nl_cnt > 1) { //如果设置了shuffle_queue并且扫描有结果 ,

    ACTF("Shuffling queue...");
    shuffle_ptrs((void**)nl, nl_cnt);//使用shuffle_ptrs函数将nl指针数组进行更改

  }

  for (i = 0; i < nl_cnt; i++) { //遍历nl数组中的每项,这里会包含"."和"..",后以输入文件为a.txt为例

    struct stat st;

    u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name);//fn:"in_dir/a.txt"
    u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name); //dfn:"in_dir/.state/deterministic_done/a.txt"

    u8  passed_det = 0;

    free(nl[i]); /* not tracked */
 
    if (lstat(fn, &st) || access(fn, R_OK)) //从fn获取的文件信息保存到st中,同时检查目录是否可以访问 ,不能访问就报错
      PFATAL("Unable to access '%s'", fn);

    /* This also takes care of . and .. */

    if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.txt")) {
        /*S_ISREG查看st.st_mode确定文件属性,st.st_size确定文件大小,strstr(fn, "/README.txt")匹配是否是README.txt
        排除干扰文件,如.或..或README*/
      ck_free(fn);
      ck_free(dfn);
      continue;

    }

    if (st.st_size > MAX_FILE) //如果是有效文件,检查文件大小是否超过规定值界限,默认1024*1024字节,即1M
      FATAL("Test case '%s' is too big (%s, limit is %s)", fn,
            DMS(st.st_size), DMS(MAX_FILE));

    /* Check for metadata that indicates that deterministic fuzzing
       is complete for this entry. We don't want to repeat deterministic
       fuzzing when resuming aborted scans, because it would be pointless
       and probably very time-consuming. */

    if (!access(dfn, F_OK)) passed_det = 1; //检查dfn是否杯创建成功,创建成功就设置passed_det为1(此检查用来判断是否这个入口已经完成deterministic fuzzing。在恢复异常终止的扫描时不想重复进行deterministic fuzzing )
    ck_free(dfn); 

    add_to_queue(fn, st.st_size, passed_det); //调用add_to_queue将这个文件入队

  }

  free(nl); /* not tracked */

  if (!queued_paths) { 如果没有入队的输入文件,则抛出异常

    SAYF("\n" cLRD "[-] " cRST
         "Looks like there are no valid test cases in the input directory! The fuzzer\n"
         "    needs one or more test case to start with - ideally, a small file under\n"
         "    1 kB or so. The cases must be stored as regular files directly in the\n"
         "    input directory.\n");

    FATAL("No usable test cases in '%s'", in_dir);

  }

  last_path_time = 0; //设置last_path_time为0
  queued_at_start = queued_paths; //设置 queued_at_start为qeued_paths

}


/* Helper function for load_extras. */

static int compare_extras_len(const void* p1, const void* p2) {
  struct extra_data *e1 = (struct extra_data*)p1,
                    *e2 = (struct extra_data*)p2;

  return e1->len - e2->len;
}

static int compare_extras_use_d(const void* p1, const void* p2) {
  struct extra_data *e1 = (struct extra_data*)p1,
                    *e2 = (struct extra_data*)p2;

  return e2->hit_cnt - e1->hit_cnt;
}


/* Read extras from a file, sort by size. */

static void load_extras_file(u8* fname, u32* min_len, u32* max_len,
                             u32 dict_level) {

  FILE* f;
  u8  buf[MAX_LINE];
  u8  *lptr;
  u32 cur_line = 0;

  f = fopen(fname, "r");

  if (!f) PFATAL("Unable to open '%s'", fname);

  while ((lptr = fgets(buf, MAX_LINE, f))) {

    u8 *rptr, *wptr;
    u32 klen = 0;

    cur_line++;

    /* Trim on left and right. */

    while (isspace(*lptr)) lptr++;

    rptr = lptr + strlen(lptr) - 1;
    while (rptr >= lptr && isspace(*rptr)) rptr--;
    rptr++;
    *rptr = 0;

    /* Skip empty lines and comments. */

    if (!*lptr || *lptr == '#') continue;

    /* All other lines must end with '"', which we can consume. */

    rptr--;

    if (rptr < lptr || *rptr != '"')
      FATAL("Malformed name=\"value\" pair in line %u.", cur_line);

    *rptr = 0;

    /* Skip alphanumerics and dashes (label). */

    while (isalnum(*lptr) || *lptr == '_') lptr++;

    /* If @number follows, parse that. */

    if (*lptr == '@') {

      lptr++;
      if (atoi(lptr) > dict_level) continue;
      while (isdigit(*lptr)) lptr++;

    }

    /* Skip whitespace and = signs. */

    while (isspace(*lptr) || *lptr == '=') lptr++;

    /* Consume opening '"'. */

    if (*lptr != '"')
      FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line);

    lptr++;

    if (!*lptr) FATAL("Empty keyword in line %u.", cur_line);

    /* Okay, let's allocate memory and copy data between "...", handling
       \xNN escaping, \\, and \". */

    extras = ck_realloc_block(extras, (extras_cnt + 1) *
               sizeof(struct extra_data));

    wptr = extras[extras_cnt].data = ck_alloc(rptr - lptr);

    while (*lptr) {

      char* hexdigits = "0123456789abcdef";

      switch (*lptr) {

        case 1 ... 31:
        case 128 ... 255:
          FATAL("Non-printable characters in line %u.", cur_line);

        case '\\':

          lptr++;

          if (*lptr == '\\' || *lptr == '"') {
            *(wptr++) = *(lptr++);
            klen++;
            break;
          }

          if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2]))
            FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line);

          *(wptr++) =
            ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) |
            (strchr(hexdigits, tolower(lptr[2])) - hexdigits);

          lptr += 3;
          klen++;

          break;

        default:

          *(wptr++) = *(lptr++);
          klen++;

      }

    }

    extras[extras_cnt].len = klen;

    if (extras[extras_cnt].len > MAX_DICT_FILE)
      FATAL("Keyword too big in line %u (%s, limit is %s)", cur_line,
            DMS(klen), DMS(MAX_DICT_FILE));

    if (*min_len > klen) *min_len = klen;
    if (*max_len < klen) *max_len = klen;

    extras_cnt++;

  }

  fclose(f);

}


/* Read extras from the extras directory and sort them by size. */

static void load_extras(u8* dir) { // 如果定义了extras_dir,则从extras_dir读取extras到extras数组里,并按size排序。

  DIR* d;
  struct dirent* de;
  u32 min_len = MAX_DICT_FILE, max_len = 0, dict_level = 0;
  u8* x;

  /* If the name ends with @, extract level and continue. */
   
  if ((x = strchr(dir, '@'))) { //检查dir中是否存在@字符

    *x = 0; //如果有的话替换成空
    dict_level = atoi(x + 1); //将@后面的字符以int的形式赋值给dict_level

  }

  ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level);

  d = opendir(dir); //尝试打开dir路径

  if (!d) { //如果打开失败

    if (errno == ENOTDIR) { //如果报错为enotdir 则表示这不是一个目录文件
      load_extras_file(dir, &min_len, &max_len, dict_level);
      goto check_and_sort;
    }

    PFATAL("Unable to open '%s'", dir);//如果报错不是ENOTDIR,直接抛异常

  }

  if (x) FATAL("Dictionary levels not supported for directories.");

  while ((de = readdir(d))) { //循环扫描并读取目录下的文件到extras[]中,按照size大小排序

    struct stat st;
    u8* fn = alloc_printf("%s/%s", dir, de->d_name);
    s32 fd;

    if (lstat(fn, &st) || access(fn, R_OK))
      PFATAL("Unable to access '%s'", fn);

    /* This also takes care of . and .. */
    if (!S_ISREG(st.st_mode) || !st.st_size) {

      ck_free(fn);
      continue;

    }

    if (st.st_size > MAX_DICT_FILE)
      FATAL("Extra '%s' is too big (%s, limit is %s)", fn,
            DMS(st.st_size), DMS(MAX_DICT_FILE));

    if (min_len > st.st_size) min_len = st.st_size;
    if (max_len < st.st_size) max_len = st.st_size;

    extras = ck_realloc_block(extras, (extras_cnt + 1) *
               sizeof(struct extra_data));

    extras[extras_cnt].data = ck_alloc(st.st_size);
    extras[extras_cnt].len  = st.st_size;

    fd = open(fn, O_RDONLY);

    if (fd < 0) PFATAL("Unable to open '%s'", fn);

    ck_read(fd, extras[extras_cnt].data, st.st_size, fn);

    close(fd);
    ck_free(fn);

    extras_cnt++;

  }

  closedir(d);

check_and_sort:
  //检查token的size
  if (!extras_cnt) FATAL("No usable files in '%s'", dir);

  qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len);

  OKF("Loaded %u extra tokens, size range %s to %s.", extras_cnt,
      DMS(min_len), DMS(max_len));

  if (max_len > 32)
    WARNF("Some tokens are relatively large (%s) - consider trimming.",
          DMS(max_len));

  if (extras_cnt > MAX_DET_EXTRAS)
    WARNF("More than %u tokens - will use them probabilistically.",
          MAX_DET_EXTRAS);

}




/* Helper function for maybe_add_auto() */

static inline u8 memcmp_nocase(u8* m1, u8* m2, u32 len) {

  while (len--) if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1;
  return 0;

}


/* Maybe add automatic extra. */

static void maybe_add_auto(u8* mem, u32 len) { //处理extras数组,判断是否添加新的a_extras[]数组项 //mem为读取的auto extra文件,len为对应长度

  u32 i;

  /* Allow users to specify that they don't want auto dictionaries. */

  if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return; //如果用户设置了MAX_AUTO_EXTRAS和USE_AUTO_EXTRAS为0,则直接返回

  /* Skip runs of identical bytes. */

  for (i = 1; i < len; i++) //从mem[1]开始遍历内容
    if (mem[0] ^ mem[i]) break;//跳过与mem[0]相同的字节,索引i停止在第一个与mem[0]不同的位置

  if (i == len) return; //如果mem中所有字节都相同则跳出函数

  /* Reject builtin interesting values. */

  if (len == 2) { //如果len长度为2

    i = sizeof(interesting_16) >> 1;

    while (i--)  //让mem和interesting_16[i]的数组进行比较,如果相同就返回
      if (*((u16*)mem) == interesting_16[i] ||
          *((u16*)mem) == SWAP16(interesting_16[i])) return;

  }

  if (len == 4) { //如果len长度为4

    i = sizeof(interesting_32) >> 2;

    while (i--) //让mem和 interesting_32[i] 的数组进行比较,如果相同就返回
      if (*((u32*)mem) == interesting_32[i] ||
          *((u32*)mem) == SWAP32(interesting_32[i])) return;

  }

  /* Reject anything that matches existing extras. Do a case-insensitive
     match. We optimize by exploiting the fact that extras[] are sorted
     by size. */

  for (i = 0; i < extras_cnt; i++) //由于extras数组保存元素的顺序是从小到大的,所以依次遍历比较长度,直接在相同位置开始进行比较
    if (extras[i].len >= len) break;

  for (; i < extras_cnt && extras[i].len == len; i++)
    if (!memcmp_nocase(extras[i].data, mem, len)) return;//与 extras[] 数组中已经存在的extras相比,如果相等就return

  /* Last but not least, check a_extras[] for matches. There are no
     guarantees of a particular sort order. */

  auto_changed = 1;//设置auto_changed为1

  for (i = 0; i < a_extras_cnt; i++) {//遍历a_extras数组

    if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) {//比较a_extras[i].data和mem是否相同

      a_extras[i].hit_cnt++; //如果相同就a_extras[i].hit_cnt命中计数+1,这代表预料中被使用的次数
      goto sort_a_extras;//跳转到sort_a_extras

    }

  }

  /* At this point, looks like we're dealing with a new entry. So, let's
     append it if we have room. Otherwise, let's randomly evict some other
     entry from the bottom half of the list. */

  if (a_extras_cnt < MAX_AUTO_EXTRAS) { //如果a_extras_cnt小于MAX_AUTO_EXTRAS(默认USE_AUTO_EXTRAS * 10),说明a_extras还未满,需要构造一个新项放进a_extras数组中

    a_extras = ck_realloc_block(a_extras, (a_extras_cnt + 1) *//调整a_extras_cnt为a_extras_cnt+1
                                sizeof(struct extra_data));

    a_extras[a_extras_cnt].data = ck_memdup(mem, len);//a_extras[a_extras_cnt].data设置为mem
    a_extras[a_extras_cnt].len  = len;//a_extras[a_extras_cnt].len设置为len
    a_extras_cnt++;//a_extras_cnt计数+1

  } else { //如果a_extras满了

    i = MAX_AUTO_EXTRAS / 2 +
        UR((MAX_AUTO_EXTRAS + 1) / 2); //获取一个随机数i

    ck_free(a_extras[i].data);//随机替换掉一个a_extras[i].data

    a_extras[i].data    = ck_memdup(mem, len); //将.data替换为mem 
    a_extras[i].len     = len; //将.len替换为len
    a_extras[i].hit_cnt = 0; //将.hit_cnt替换为0

  }

sort_a_extras:

  /* First, sort all auto extras by use count, descending order. */

  qsort(a_extras, a_extras_cnt, sizeof(struct extra_data),
        compare_extras_use_d);//按使用次数,降序对所有a_extras[i]进行排序

  /* Then, sort the top USE_AUTO_EXTRAS entries by size. */

  qsort(a_extras, MIN(USE_AUTO_EXTRAS, a_extras_cnt), //按size对a_extras中前USE_AUTO_EXTRAS个进行排序(默认50)
        sizeof(struct extra_data), compare_extras_len);

}


/* Save automatically generated extras. */

static void save_auto(void) {// 保存自动生成的extras

  u32 i;

  if (!auto_changed) return;//如果auto_changed为0,则直接返回
  auto_changed = 0;//如果不为0,就设置为0

  for (i = 0; i < MIN(USE_AUTO_EXTRAS, a_extras_cnt); i++) {

    u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i);
    s32 fd;

    fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); //创建out_dir/queue/.state/auto_extras/auto_00000i

    if (fd < 0) PFATAL("Unable to create '%s'", fn);

    ck_write(fd, a_extras[i].data, a_extras[i].len, fn);//写入extras

    close(fd);
    ck_free(fn);

  }

}


/* Load automatically generated extras. */

static void load_auto(void) { //自动生成提取出来的字典token

  u32 i;

  for (i = 0; i < USE_AUTO_EXTRAS; i++) { //遍历循环i< USE_AUTO_EXTRAS

    u8  tmp[MAX_AUTO_EXTRA + 1]; 
    u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i);
    s32 fd, len;

    fd = open(fn, O_RDONLY, 0600);//以只读的形式打开fn 获得句柄fd

    if (fd < 0) { //如果打开失败则报错

      if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
      ck_free(fn);
      break;

    }

    /* We read one byte more to cheaply detect tokens that are too
       long (and skip them). */

    len = read(fd, tmp, MAX_AUTO_EXTRA + 1); //如果能够打开,从fd中读取MAX_AUTO_EXTRA + 1个字节到tmp数组

    if (len < 0) PFATAL("Unable to read from '%s'", fn); //如果没读到报错

    if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)//
      maybe_add_auto(tmp, len);//如果读取长度在MIN_AUTO_EXTRA(默认为3)和MAX_AUTO_EXTRA(默认为32)之间,调用maybe_add_auto函数处理

    close(fd);//关闭
    ck_free(fn);

  }

  if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i);
  else OKF("No auto-generated dictionary tokens to reuse.");

}


/* Destroy extras. */

static void destroy_extras(void) {

  u32 i;

  for (i = 0; i < extras_cnt; i++) 
    ck_free(extras[i].data);

  ck_free(extras);

  for (i = 0; i < a_extras_cnt; i++) 
    ck_free(a_extras[i].data);

  ck_free(a_extras);

}


/* Spin up fork server (instrumented mode only). The idea is explained here:

   http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html

   In essence, the instrumentation allows us to skip execve(), and just keep
   cloning a stopped child. So, we just execute once, and then send commands
   through a pipe. The other part of this logic is in afl-as.h. */

EXP_ST void init_forkserver(char** argv) { //该函数主要为目标程序创建通信管道,并且通过fork server fork出子进程,判断启动失败原因

  static struct itimerval it;
  int st_pipe[2], ctl_pipe[2];
  int status;
  s32 rlen;

  ACTF("Spinning up the fork server...");

  if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");//如果无法创建管道,则报错

  forksrv_pid = fork();//fork出一个子进程,如果fork成功,会出现一个子进程和一个父进程,在子进程中fork函数返回值为0,在父进程中,fork返回创建的子进程的进程ID

  if (forksrv_pid < 0) PFATAL("fork() failed");//判断fork是否成功,失败报错

  if (!forksrv_pid) {//判断当前进程是父进程还是子进程,根据前面的注释,如果为子进程则进入该if分支
       //--------------------------子进程开始---------------------------------
    struct rlimit r;

    /* Umpf. On OpenBSD, the default fd limit for root users is set to
       soft 128. Let's try to fix that... */

    if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) {

      r.rlim_cur = FORKSRV_FD + 2;
      setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */

    }

    if (mem_limit) {

      r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;

#ifdef RLIMIT_AS

      setrlimit(RLIMIT_AS, &r); /* Ignore errors */

#else

      /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but
         according to reliable sources, RLIMIT_DATA covers anonymous
         maps - so we should be getting good protection against OOM bugs. */

      setrlimit(RLIMIT_DATA, &r); /* Ignore errors */

#endif /* ^RLIMIT_AS */


    }

    /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
       before the dump is complete. */

    r.rlim_max = r.rlim_cur = 0;

    setrlimit(RLIMIT_CORE, &r); /* Ignore errors */

    /* Isolate the process and configure standard descriptors. If out_file is
       specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */

    setsid(); //创建新的session,使子进程完全独立,脱离控制

    dup2(dev_null_fd, 1);
    dup2(dev_null_fd, 2);//重新配置fd,关闭子进程中的stdout和stderr,将其重定向到/dev/null中,相当于关闭了子进程的全部输出

    if (out_file) {//如果设置了out_file

      dup2(dev_null_fd, 0); //关闭子进程的stdin,重定向到/dev/null

    } else {//如果没有设置

      dup2(out_fd, 0);//关闭stdin,重定向到out_fd
      close(out_fd);//关闭out_fd

    }

    /* Set up control and status pipes, close the unneeded original fds. */

    if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed"); //将FORKSRV_FD重定向到ctl_pipe[0],子进程只能读取命令
    if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed"); //将FORKSRV_FD + 1重定向到st_pipe[1],子进程只能发送“写出”状态

    close(ctl_pipe[0]);
    close(ctl_pipe[1]);
    close(st_pipe[0]);
    close(st_pipe[1]);

    close(out_dir_fd);
    close(dev_null_fd);
    close(dev_urandom_fd);
    close(fileno(plot_file));//关闭一些进程中的文件描述符

    /* This should improve performance a bit, since it stops the linker from
       doing extra work post-fork(). */

    if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);//如果没有设置LD_BIND_LAZY环境变量 //设置LD_BIND_NOW为1,防止linker在fork之后做额外的操作

    /* Set sane defaults for ASAN if nothing else specified. */

    setenv("ASAN_OPTIONS", "abort_on_error=1:"
                           "detect_leaks=0:"
                           "symbolize=0:"
                           "allocator_may_return_null=1", 0);//设置ASAN选项

    /* MSAN is tricky, because it doesn't support abort_on_error=1 at this
       point. So, we do this in a very hacky way. */

    setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
                           "symbolize=0:"
                           "abort_on_error=1:"
                           "allocator_may_return_null=1:"
                           "msan_track_origins=0", 0); //设置MASN选项

    execv(target_path, argv); //带参数执行target,这个函数除非出错,否自不会返回
     //带参数执行target,这个函数除非出错,否自不会返回
    /* Use a distinctive bitmap signature to tell the parent about execv()
       falling through. */

    *(u32*)trace_bits = EXEC_FAIL_SIG;
    exit(0);

  } //-------------------------子进程结束-------------------------------

  /* Close the unneeded endpoints. */

  close(ctl_pipe[0]);
  close(st_pipe[1]);//关闭ctl_pipe[0]和st_pipe[1],控制管道读与状态管道写(父进程不需要)

  fsrv_ctl_fd = ctl_pipe[1];//父进程只能发送“写出”命令
  fsrv_st_fd  = st_pipe[0];//父进程只能读取状态

  /* Wait for the fork server to come up, but don't wait too long. */

  it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000);
  it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000;//等待fork server启动,但是不能等太久

  setitimer(ITIMER_REAL, &it, NULL);

  rlen = read(fsrv_st_fd, &status, 4);//从管道中读取4个字节放在status中

  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 0;

  setitimer(ITIMER_REAL, &it, NULL);//超时报错

  /* If we have a four-byte "hello" message from the server, we're all set.
     Otherwise, try to figure out what went wrong. */

  if (rlen == 4) {//如果读取到了4个字节
    OKF("All right - fork server is up."); //准备就绪,直接return 
    return;
  }

  if (child_timed_out) //如果设置了child_time_out,提醒用户调整“-t”参数
    FATAL("Timeout while initializing fork server (adjusting -t may help)");

  if (waitpid(forksrv_pid, &status, 0) <= 0) //否则等待forksrv返回status
    PFATAL("waitpid() failed");

  if (WIFSIGNALED(status)) {//判断forksrv是否返回的异常退出信号
      //如果是异常退出信号,进入该if分支
    if (mem_limit && mem_limit < 500 && uses_asan) { //判断是否由于设置的mem_limit过小导致的

      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! Since it seems to be built with ASAN and you have a\n"
           "    restrictive memory limit configured, this is expected; please read\n"
           "    %s/notes_for_asan.txt for help.\n", doc_path);
      //告知用户是由于未收到任何input就crash,可能是由于asan和mem_limit的原因
    } else if (!mem_limit) {//如果没有设置mem_limit

      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"

           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"

#ifdef __APPLE__

           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"

#endif /* __APPLE__ */

           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");
      //告知原因
    } else {
        //否则告知原因
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"

           "    - The current memory limit (%s) is too restrictive, causing the\n"
           "      target to hit an OOM condition in the dynamic linker. Try bumping up\n"
           "      the limit with the -m setting in the command line. A simple way confirm\n"
           "      this diagnosis would be:\n\n"

#ifdef RLIMIT_AS
           "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
           "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#endif /* ^RLIMIT_AS */

           "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
           "      estimate the required amount of virtual memory for the binary.\n\n"

           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"

#ifdef __APPLE__

           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"

#endif /* __APPLE__ */

           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
           DMS(mem_limit << 20), mem_limit - 1);

    }

    FATAL("Fork server crashed with signal %d", WTERMSIG(status));//通过WTERMSIG(status)获取信号告知用户single原因

  }
  //----------------------非异常退出------------------------
  if (*(u32*)trace_bits == EXEC_FAIL_SIG)//检查trace_bits == EXEC_FAIL_SIG,如果设置了说明没正常执行
    FATAL("Unable to execute target application ('%s')", argv[0]);

  if (mem_limit && mem_limit < 500 && uses_asan) {//判断mem_limit && mem_limit < 500 && uses_asan,可能是由于配置的内存限制导致的

    SAYF("\n" cLRD "[-] " cRST
           "Hmm, looks like the target binary terminated before we could complete a\n"
           "    handshake with the injected code. Since it seems to be built with ASAN and\n"
           "    you have a restrictive memory limit configured, this is expected; please\n"
           "    read %s/notes_for_asan.txt for help.\n", doc_path);

  } else if (!mem_limit) {//如果没有设置mem_limit,可能是fuzzer的bug

    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. Perhaps there is a horrible bug in the\n"
         "    fuzzer. Poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");

  } else {
      //告知用户其他可能原因

    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. There are %s probable explanations:\n\n"

         "%s"
         "    - The current memory limit (%s) is too restrictive, causing an OOM\n"
         "      fault in the dynamic linker. This can be fixed with the -m option. A\n"
         "      simple way to confirm the diagnosis may be:\n\n"

#ifdef RLIMIT_AS
         "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
         "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#endif /* ^RLIMIT_AS */

         "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
         "      estimate the required amount of virtual memory for the binary.\n\n"

         "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
         "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
         getenv(DEFER_ENV_VAR) ? "three" : "two",
         getenv(DEFER_ENV_VAR) ?
         "    - You are using deferred forkserver, but __AFL_INIT() is never\n"
         "      reached before the program terminates.\n\n" : "",
         DMS(mem_limit << 20), mem_limit - 1);

  }

  FATAL("Fork server handshake failed");

}


/* Execute target application, monitoring for timeouts. Return status
   information. The called program will update trace_bits[]. */

static u8 run_target(char** argv, u32 timeout) { //通过子进程的方式执行目标应用,这个函数部分代码和前面的init_forkserver很像,但是这个函数处理了在设置了no_forkserver的情况的下的处理过程

  static struct itimerval it;
  static u32 prev_timed_out = 0;
  static u64 exec_ms = 0;

  int status = 0;
  u32 tb4;

  child_timed_out = 0;

  /* After this memset, trace_bits[] are effectively volatile, so we
     must prevent any earlier operations from venturing into that
     territory. */

  memset(trace_bits, 0, MAP_SIZE);//全部清空trace_bits[MAP_SIZE]为0
  MEM_BARRIER();

  /* If we're running in "dumb" mode, we can't rely on the fork server
     logic compiled into the target program, so we will just keep calling
     execve(). There is a bit of code duplication between here and 
     init_forkserver(), but c'est la vie. */

  if (dumb_mode == 1 || no_forkserver) {//如果dumb_mode等于1或设置了no_forkserver

    child_pid = fork();//fork出一个子进程,调用execv执行子进程中的target_path,如果失败了想trace_bits中写入EXEC_FAIL_SIG

    if (child_pid < 0) PFATAL("fork() failed"); //创建失败就报错

    if (!child_pid) {//判断是否是子进程

      struct rlimit r;

      if (mem_limit) {//判断是否设置了mem_limit

        r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;

#ifdef RLIMIT_AS

        setrlimit(RLIMIT_AS, &r); /* Ignore errors */

#else

        setrlimit(RLIMIT_DATA, &r); /* Ignore errors */

#endif /* ^RLIMIT_AS */

      }

      r.rlim_max = r.rlim_cur = 0;

      setrlimit(RLIMIT_CORE, &r); /* Ignore errors */

      /* Isolate the process and configure standard descriptors. If out_file is
         specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */

      setsid();

      dup2(dev_null_fd, 1);
      dup2(dev_null_fd, 2);

      if (out_file) {

        dup2(dev_null_fd, 0);

      } else {

        dup2(out_fd, 0);
        close(out_fd);

      }

      /* On Linux, would be faster to use O_CLOEXEC. Maybe TODO. */

      close(dev_null_fd);
      close(out_dir_fd);
      close(dev_urandom_fd);
      close(fileno(plot_file));

      /* Set sane defaults for ASAN if nothing else specified. */

      setenv("ASAN_OPTIONS", "abort_on_error=1:"
                             "detect_leaks=0:"
                             "symbolize=0:"
                             "allocator_may_return_null=1", 0);

      setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
                             "symbolize=0:"
                             "msan_track_origins=0", 0);

      execv(target_path, argv);

      /* Use a distinctive bitmap value to tell the parent about execv()
         falling through. */

      *(u32*)trace_bits = EXEC_FAIL_SIG;
      exit(0);

    }

  } else {

    s32 res;

    /* In non-dumb mode, we have the fork server up and running, so simply
       tell it to have at it, and then read back PID. */

    if ((res = write(fsrv_ctl_fd, &prev_timed_out, 4)) != 4) {//向控制管道中写入4个字节的prev_timed_out,命令fork server开始fork出一个子进程进行fuzz

      if (stop_soon) return 0;
      RPFATAL(res, "Unable to request new process from fork server (OOM?)");

    }

    if ((res = read(fsrv_st_fd, &child_pid, 4)) != 4) {        // 再从状态管道中读取fork server返回的fork出的子进程的ID到child_pid中

      if (stop_soon) return 0;
      RPFATAL(res, "Unable to request new process from fork server (OOM?)");

    }

    if (child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");

  }

  /* Configure timeout, as requested by user, then wait for child to terminate. */

  it.it_value.tv_sec = (timeout / 1000);
  it.it_value.tv_usec = (timeout % 1000) * 1000;

  setitimer(ITIMER_REAL, &it, NULL);// 超时报错

  /* The SIGALRM handler simply kills the child_pid and sets child_timed_out. */

  if (dumb_mode == 1 || no_forkserver) { //如果满足说明无fork server,以子进程的方式execve

    if (waitpid(child_pid, &status, 0) <= 0) PFATAL("waitpid() failed"); //waitpid等待子进程的status

  } else {

    s32 res;

    if ((res = read(fsrv_st_fd, &status, 4)) != 4) {//否则从状态管道中读取status

      if (stop_soon) return 0;
      RPFATAL(res, "Unable to communicate with fork server (OOM?)");

    }

  }

  if (!WIFSTOPPED(status)) child_pid = 0;

  getitimer(ITIMER_REAL, &it);
  exec_ms = (u64) timeout - (it.it_value.tv_sec * 1000 +
                             it.it_value.tv_usec / 1000);//计算执行时间

  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 0;

  setitimer(ITIMER_REAL, &it, NULL);

  total_execs++;//执行次数计数+1

  /* Any subsequent operations on trace_bits must not be moved by the
     compiler below this point. Past this location, trace_bits[] behave
     very normally and do not have to be treated as volatile. */

  MEM_BARRIER();

  tb4 = *(u32*)trace_bits;

#ifdef WORD_SIZE_64
  classify_counts((u64*)trace_bits);
#else
  classify_counts((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

  prev_timed_out = child_timed_out;

  /* Report outcome to caller. */

  if (WIFSIGNALED(status) && !stop_soon) {//判断子进程是否是异常退出,如果是,判断异常退出原因后return

    kill_signal = WTERMSIG(status);//WTERMSIG(status)获取子进程终止的信号代码

    if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT;//如果child_timed_out为1且状态码为SIGKILL则返回 FAULT_TMOUT

    return FAULT_CRASH;//返回FAULT_CRASH

  }

  /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
     must use a special exit code. */

  if (uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
    kill_signal = 0;
    return FAULT_CRASH;
  }
  //判断是否是无forkserver的情况下execve执行失败
  if ((dumb_mode == 1 || no_forkserver) && tb4 == EXEC_FAIL_SIG)
    return FAULT_ERROR;

  /* It makes sense to account for the slowest units only if the testcase was run
  under the user defined timeout. */
  if (!(timeout > exec_tmout) && (slowest_exec_ms < exec_ms)) {//如果最慢执行时间小于当前执行时间,并且timeout < exec_tmout
    slowest_exec_ms = exec_ms; //更新slowest_exec_ms为exec_ms
  }

  return FAULT_NONE;

}


/* Write modified data to file for testing. If out_file is set, the old file
   is unlinked and a new one is created. Otherwise, out_fd is rewound and
   truncated. */

static void write_to_testcase(void* mem, u32 len) {

  s32 fd = out_fd;

  if (out_file) {

    unlink(out_file); /* Ignore errors. */

    fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);

    if (fd < 0) PFATAL("Unable to create '%s'", out_file);

  } else lseek(fd, 0, SEEK_SET);

  ck_write(fd, mem, len, out_file);

  if (!out_file) {

    if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
    lseek(fd, 0, SEEK_SET);

  } else close(fd);

}


/* The same, but with an adjustable gap. Used for trimming. */

static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) {

  s32 fd = out_fd;
  u32 tail_len = len - skip_at - skip_len;

  if (out_file) {

    unlink(out_file); /* Ignore errors. */

    fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);

    if (fd < 0) PFATAL("Unable to create '%s'", out_file);

  } else lseek(fd, 0, SEEK_SET);

  if (skip_at) ck_write(fd, mem, skip_at, out_file);

  if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file);

  if (!out_file) {

    if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed");
    lseek(fd, 0, SEEK_SET);

  } else close(fd);

}


static void show_stats(void);

/* Calibrate a new test case. This is done when processing the input directory
   to warn about flaky or otherwise problematic test cases early on; and when
   new paths are discovered to detect variable behavior and so on. */

static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, //校验case
                         u32 handicap, u8 from_queue) {

  static u8 first_trace[MAP_SIZE]; //创建first_trace数组

  u8  fault = 0, new_bits = 0, var_detected = 0, hnb = 0,
      first_run = (q->exec_cksum == 0);//如果q->exec_cksum为0,说明这个case是第一次运行,即来自input目录下,将first_run变量设置为1

  u64 start_us, stop_us;

  s32 old_sc = stage_cur, old_sm = stage_max;
  u32 use_tmout = exec_tmout;
  u8* old_sn = stage_name;

  /* Be a bit more generous about timeouts when resuming sessions, or when
     trying to calibrate already-added finds. This helps avoid trouble due
     to intermittent latency. */

  if (!from_queue || resuming_fuzz) //如果from_queue为0或resuming_fuzz为1,代表不来自于queue中或者resuming sessions的时候
    use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD,
                    exec_tmout * CAL_TMOUT_PERC / 100); //则设置use_tmout的值被设置的更大

  q->cal_failed++;//q->cal_failed记录+1

  stage_name = "calibration";//stage_name设置为"calibration"
  stage_max  = fast_cal ? 3 : CAL_CYCLES; //根据判断fast_cal是否为1,来设置stage_max的值为3或者CAL_CYCLES(默认8)
  //每个新测试用例以及显示出可变行为的测试用例的校准周期数,及本次stage要执行几次
  /* Make sure the forkserver is up before we do anything, and let's not
     count its spin-up time toward binary calibration. */

  if (dumb_mode != 1 && !no_forkserver && !forksrv_pid)//如果不是dumb_mode模式,且no_forkserver为0(禁用forkserver),且forksrv_pid为0
    init_forkserver(argv);//调用init_forkserver函数启动fork server

  if (q->exec_cksum) { //如果q->exec_cksum不为空,说明这个queue不是来自input文件夹,而是评估新的case

    memcpy(first_trace, trace_bits, MAP_SIZE);//将trace_bits拷贝到first_trace中
    hnb = has_new_bits(virgin_bits); //调用has_new_bits函数计算,将结果赋值给hnb
    if (hnb > new_bits) new_bits = hnb; //如果新计算结果大于new_bits 就给new_bits重新赋值

  }

  start_us = get_cur_time_us();//获取当前系统时间

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {//循环stage_max次

    u32 cksum;

    if (!first_run && !(stage_cur % stats_update_freq)) show_stats(); // 如果这个queue不是来自input文件夹,而是评估新case,且第一轮calibration stage执行结束时//调用show_stats函数画出界面,用来展示本次执行的结果

    write_to_testcase(use_mem, q->len);//从q->fname中读取内容写入到.cur_imput中

    fault = run_target(argv, use_tmout);//run结果保存到fault中

    /* stop_soon is set by the handler for Ctrl+C. When it's pressed,
       we want to bail out quickly. */

    if (stop_soon || fault != crash_mode) goto abort_calibration;//如果出现终止或者fault结果不为crash_mode,跳转到abort_calibration

    if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) { //如果不是dumb_mode模式,并且这是calibration stage为第一次运行,并且共享内存里没有任何路径(没有任何byte被置位)
      fault = FAULT_NOINST;//设置fault为FAULT_NOINST
      goto abort_calibration;//跳转至abort_calibration
    }

    cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//hash32计算的32位uint值保存到cksum中

    if (q->exec_cksum != cksum) {//如果不相等,代表这是第一次运行,或者在相同的参数下,每次执行,ckdum却不同,是一个路径可变的queue

      hnb = has_new_bits(virgin_bits);
      if (hnb > new_bits) new_bits = hnb;//如果has_new_bits计算值大于new_bits 重新给new_bits赋值为计算结果

      if (q->exec_cksum) { //判断是否是可变queue,如果为1,则说明不是第一次执行queue

        u32 i;

        for (i = 0; i < MAP_SIZE; i++) {

          if (!var_bytes[i] && first_trace[i] != trace_bits[i]) {//如果first_trace[i]为1,且不等于trace_bits[i],说明发现了可变的queue

            var_bytes[i] = 1;//如果var_bytes[i]为空则赋值为1
            stage_max    = CAL_CYCLES_LONG;//设置stage_max为CAL_CYCLES_LONG

          }

        }

        var_detected = 1;//将var_detected设置为1

      } else {//如果q->exec_cksum为0,这代表是第一次执行queue

        q->exec_cksum = cksum;//设置q->exec_cksum的值为计算出来的本次执行的cksum
        memcpy(first_trace, trace_bits, MAP_SIZE);//设置q->exec_cksum的值为计算出来的本次执行的cksum

      }

    }

  }

  stop_us = get_cur_time_us();//保存停止时间

  total_cal_us     += stop_us - start_us;//总轮次执行时间存放在total_cal_us中
  total_cal_cycles += stage_max;//总执行轮次放在stage_max中

  /* OK, let's collect some stats about the performance of this test case.
     This is used for fuzzing air time calculations in calculate_score(). */

  q->exec_us     = (stop_us - start_us) / stage_max; //计算单词执行时间的平均值赋值给q->exec_us 
  q->bitmap_size = count_bytes(trace_bits);//最后一次执行所覆盖到的路径数赋值给q->bitmap_size
  q->handicap    = handicap;
  q->cal_failed  = 0;

  total_bitmap_size += q->bitmap_size;//加上这个queue所覆盖到的路径树
  total_bitmap_entries++;

  update_bitmap_score(q);

  /* If this case didn't result in new output from the instrumentation, tell
     parent. This is a non-critical problem, but something to warn the user
     about. */
     //如果fault为FAULT_NONE,且不是dumb_mode模式,且是第一次运行,且new_bits为0,代表在这个样例所有轮次的执行里,都没有发现新的路径和异常
  if (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS; //设置fault为FAULT_NOBITS

abort_calibration:

  if (new_bits == 2 && !q->has_new_cov) {//如果new_bits为2且q->has_new_cov空
    q->has_new_cov = 1;//设置q->has_new_cov为1
    queued_with_cov++; //queued_with_cov+1,代表queue发现了一条新的路径
  }

  /* Mark variable paths. */

  if (var_detected) {//如果var_detected为1,代表这个queue是可变路径

    var_byte_count = count_bytes(var_bytes);//计算var_bytes里被置位的tuple个数,保存到var_byte_count中,代表这个tuple具有可变行为

    if (!q->var_behavior) {
      mark_as_variable(q);//创建符号连接out_dir/queue/.state/variable_behavior/fname,设置queue的var_behavior为1
      queued_variable++;//variable behavior计数器+1
    }

  }

  stage_name = old_sn;
  stage_cur  = old_sc;
  stage_max  = old_sm; //恢复之前的stage

  if (!first_run) show_stats();//如果不是第一次运行这个queue,展示状态

  return fault; //返回fault值

}


/* Examine map coverage. Called once, for first test case. */

static void check_map_coverage(void) {

  u32 i;

  if (count_bytes(trace_bits) < 100) return;

  for (i = (1 << (MAP_SIZE_POW2 - 1)); i < MAP_SIZE; i++)
    if (trace_bits[i]) return;

  WARNF("Recompile binary with newer version of afl to improve coverage!");

}


/* Perform dry run of all test cases to confirm that the app is working as
   expected. This is done only for the initial inputs, and only once. */

static void perform_dry_run(char** argv) {//执行所有的测试用例,以检查是否按预期工作

  struct queue_entry* q = queue;
  u32 cal_failures = 0;
  u8* skip_crashes = getenv("AFL_SKIP_CRASHES");

  while (q) { //循环队列
       
    u8* use_mem;
    u8  res;
    s32 fd;

    u8* fn = strrchr(q->fname, '/') + 1;//获取测试用例名称,以a.txt为例,fn:"id:0000000,orig:a.txt"

    ACTF("Attempting dry run with '%s'...", fn);

    fd = open(q->fname, O_RDONLY);////以只读模式尝试打开测试用例
    if (fd < 0) PFATAL("Unable to open '%s'", q->fname);//打不开抛异常

    use_mem = ck_alloc_nozero(q->len);//打不开抛异常

    if (read(fd, use_mem, q->len) != q->len) //将测试用例文件中的内容读进use_mem中
      FATAL("Short read from '%s'", q->fname);//没有成功抛异常

    close(fd);

    res = calibrate_case(argv, q, use_mem, 0, 1); //校准测试用例
    ck_free(use_mem);

    if (stop_soon) return;//如果设置stop_soon,直接返回

    if (res == crash_mode || res == FAULT_NOBITS)//如果res结果为crash_mode或者FAULT_NOBITS的话,就打印q->len, q->bitmap_size, q->exec_us
      SAYF(cGRA "    len = %u, map size = %u, exec speed = %llu us\n" cRST, 
           q->len, q->bitmap_size, q->exec_us);

    switch (res) { //进入大switch判断res错误类型

      case FAULT_NONE:

        if (q == queue) check_map_coverage();//如果q是第一个测试用例,调用check_map_coverage()函数,评估覆盖率
        //计数trace_bits发现的路径数,如果小于100,就直接返回,在trace_bits的数组后半段,如果有值就直接返回

        if (crash_mode) FATAL("Test case '%s' does *NOT* crash", fn);//如果设置了crash_mode,抛异常

        break;

      case FAULT_TMOUT:

        if (timeout_given) {//如果指定了“-t”参数

          /* The -t nn+ syntax in the command line sets timeout_given to '2' and
             instructs afl-fuzz to tolerate but skip queue entries that time
             out. */

          if (timeout_given > 1) {
            WARNF("Test case results in a timeout (skipping)");
            q->cal_failed = CAL_CHANCES;//如果指定了“-t”参数
            cal_failures++;//如果指定了“-t”参数
            break;
          }

          SAYF("\n" cLRD "[-] " cRST
               "The program took more than %u ms to process one of the initial test cases.\n"
               "    Usually, the right thing to do is to relax the -t option - or to delete it\n"
               "    altogether and allow the fuzzer to auto-calibrate. That said, if you know\n"
               "    what you are doing and want to simply skip the unruly test cases, append\n"
               "    '+' at the end of the value passed to -t ('-t %u+').\n", exec_tmout,
               exec_tmout);

          FATAL("Test case '%s' results in a timeout", fn);

        } else {

          SAYF("\n" cLRD "[-] " cRST
               "The program took more than %u ms to process one of the initial test cases.\n"
               "    This is bad news; raising the limit with the -t option is possible, but\n"
               "    will probably make the fuzzing process extremely slow.\n\n"

               "    If this test case is just a fluke, the other option is to just avoid it\n"
               "    altogether, and find one that is less of a CPU hog.\n", exec_tmout);

          FATAL("Test case '%s' results in a timeout", fn);

        }

      case FAULT_CRASH:  

        if (crash_mode) break; //如果设置了crash_mod,直接break

        if (skip_crashes) { //如果设置了crash_mod,直接break
          WARNF("Test case results in a crash (skipping)");
          q->cal_failed = CAL_CHANCES;//设置q->cal_failed为CAL_CHANCES
          cal_failures++;//cal_failures计数器+1
          break;
        }

        if (mem_limit) { //如果设置了mem_limit,提示内存不够,抛异常

          SAYF("\n" cLRD "[-] " cRST
               "Oops, the program crashed with one of the test cases provided. There are\n"
               "    several possible explanations:\n\n"

               "    - The test case causes known crashes under normal working conditions. If\n"
               "      so, please remove it. The fuzzer should be seeded with interesting\n"
               "      inputs - but not ones that cause an outright crash.\n\n"

               "    - The current memory limit (%s) is too low for this program, causing\n"
               "      it to die due to OOM when parsing valid files. To fix this, try\n"
               "      bumping it up with the -m setting in the command line. If in doubt,\n"
               "      try something along the lines of:\n\n"

#ifdef RLIMIT_AS
               "      ( ulimit -Sv $[%llu << 10]; /path/to/binary [...] <testcase )\n\n"
#else
               "      ( ulimit -Sd $[%llu << 10]; /path/to/binary [...] <testcase )\n\n"
#endif /* ^RLIMIT_AS */

               "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
               "      estimate the required amount of virtual memory for the binary. Also,\n"
               "      if you are using ASAN, see %s/notes_for_asan.txt.\n\n"

#ifdef __APPLE__
  
               "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
               "      break afl-fuzz performance optimizations when running platform-specific\n"
               "      binaries. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"

#endif /* __APPLE__ */

               "    - Least likely, there is a horrible bug in the fuzzer. If other options\n"
               "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
               DMS(mem_limit << 20), mem_limit - 1, doc_path);

        } else {

          SAYF("\n" cLRD "[-] " cRST
               "Oops, the program crashed with one of the test cases provided. There are\n"
               "    several possible explanations:\n\n"

               "    - The test case causes known crashes under normal working conditions. If\n"
               "      so, please remove it. The fuzzer should be seeded with interesting\n"
               "      inputs - but not ones that cause an outright crash.\n\n"

#ifdef __APPLE__
  
               "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
               "      break afl-fuzz performance optimizations when running platform-specific\n"
               "      binaries. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"

#endif /* __APPLE__ */

               "    - Least likely, there is a horrible bug in the fuzzer. If other options\n"
               "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");

        }

        FATAL("Test case '%s' results in a crash", fn);

      case FAULT_ERROR://无法执行目标应用程序

        FATAL("Unable to execute target application ('%s')", argv[0]);

      case FAULT_NOINST:     //样例没有出现任何路径信息

        FATAL("No instrumentation detected");

      case FAULT_NOBITS: 

        useless_at_start++;//useless_at_start计数器+1

        if (!in_bitmap && !shuffle_queue) //如果这个样例有出现路径信息,但是没有新的路径产生,就会被认为是一条无效路径
          WARNF("No new instrumentation output, test case may be useless.");

        break;

    }

    if (q->var_behavior) WARNF("Instrumentation output varies across runs."); // 如果这个样例的var_behavior为真,说明它多次运行,同样的输入条件下,却出现不同的覆盖信息

    q = q->next;//指向下一条用例

  }

  if (cal_failures) {//如果设置了cal_failures

    if (cal_failures == queued_paths)//代表所有用例均超时
      FATAL("All test cases time out%s, giving up!",
            skip_crashes ? " or crash" : "");

    WARNF("Skipped %u test cases (%0.02f%%) due to timeouts%s.", cal_failures,
          ((double)cal_failures) * 100 / queued_paths,
          skip_crashes ? " or crashes" : "");

    if (cal_failures * 5 > queued_paths)//计算cal_failures * 5是否大于queued_paths,如果大于说明测试用例的问题比例很高,可能需要重新检查设置
      WARNF(cLRD "High percentage of rejected test cases, check settings!");

  }

  OKF("All test cases processed.");

}


/* Helper function: link() if possible, copy otherwise. */

static void link_or_copy(u8* old_path, u8* new_path) {

  s32 i = link(old_path, new_path);
  s32 sfd, dfd;
  u8* tmp;

  if (!i) return;

  sfd = open(old_path, O_RDONLY);
  if (sfd < 0) PFATAL("Unable to open '%s'", old_path);

  dfd = open(new_path, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (dfd < 0) PFATAL("Unable to create '%s'", new_path);

  tmp = ck_alloc(64 * 1024);

  while ((i = read(sfd, tmp, 64 * 1024)) > 0) 
    ck_write(dfd, tmp, i, new_path);

  if (i < 0) PFATAL("read() failed");

  ck_free(tmp);
  close(sfd);
  close(dfd);

}


static void nuke_resume_dir(void);

/* Create hard links for input test cases in the output directory, choosing
   good names and pivoting accordingly. */

static void pivot_inputs(void) {  //为input中的测试用例创建硬链接

  struct queue_entry* q = queue;//创建fuzzing队列结构体queu名称以a.txt举例
  u32 id = 0;

  ACTF("Creating hard links for all input files...");

  while (q) {

    u8  *nfn, *rsl = strrchr(q->fname, '/');//将测试用例文件名称赋值给rsl,此时rsl:"/a.txt"
    u32 orig_id;

    if (!rsl) rsl = q->fname; else rsl++; //如果没有赋值成功,将将用例文件路径赋值给rsl

    /* If the original file name conforms to the syntax and the recorded
       ID matches the one we'd assign, just use the original file name.
       This is valuable for resuming fuzzing runs. */

#ifndef SIMPLE_FILES//如果没有定义SIMPLE_FILES(单文件)
#  define CASE_PREFIX "id:"
#else
#  define CASE_PREFIX "id_"
#endif /* ^!SIMPLE_FILES */

    if (!strncmp(rsl, CASE_PREFIX, 3) &&
        sscanf(rsl + 3, "%06u", &orig_id) == 1 && orig_id == id) { //比较rsl前三个字节,如果是case_prefix 并且可以将CASE_PREFIX之后的数字(id)以格式化"%06u"的形式写入orig_id,并且orig_id==id

      u8* src_str;
      u32 src_id;

      resuming_fuzz = 1;//设置resuming_fuzz
      nfn = alloc_printf("%s/queue/%s", out_dir, rsl);

      /* Since we're at it, let's also try to find parent and figure out the
         appropriate depth for this entry. */

      src_str = strchr(rsl + 3, ':');//设置resuming_fuzz

      if (src_str && sscanf(src_str + 1, "%06u", &src_id) == 1) {//如果获取到src_str,并且能够成功以格式化"%06u"的形式写入src_id中的话

        struct queue_entry* s = queue;//设置resuming_fuzz
        while (src_id-- && s) s = s->next;//让指针s从对头开始扫描,每扫描一个元素,src_id-- s后移到下一个元素
        if (s) q->depth = s->depth + 1; //如果src_id已--至0,且s队列中还有数值 则队列深度加1
         .
        if (max_depth < q->depth) max_depth = q->depth; //判断队列深度是否超过最大深度 如果超过了就将最大深度更新为队列深度

      }

    } else {//如果不是以CASE_PREFIX开头

      /* No dice - invent a new name, capturing the original one as a
         substring. */

#ifndef SIMPLE_FILES//如果没有定义SIMPLE_FILES(单文件)

      u8* use_name = strstr(rsl, ",orig:"); //监测rsl是否存在,orig:为前缀的字符串

      if (use_name) use_name += 6; else use_name = rsl; //如果存在则跳过 ,取后面的字符串给use_name ,如果不存在则rsl赋值给use_name
      nfn = alloc_printf("%s/queue/id:%06u,orig:%s", out_dir, id, use_name);//nfn:"out_dir/queue/id:000000,orig:a.txt"(以id为0举例 )

#else //如果定义了SIMPLE_FILES(单文件)

      nfn = alloc_printf("%s/queue/id_%06u", out_dir, id); //则直接拼接

#endif /* ^!SIMPLE_FILES */

    }

    /* Pivot to the new queue entry. */

    link_or_copy(q->fname, nfn);//调用link_or_copy()函数创建硬链接(q->fname到nfn)
    ck_free(q->fname);
    q->fname = nfn;//重新对队列中这一元素的fname进行赋值为nfn

    /* Make sure that the passed_det value carries over, too. */

    if (q->passed_det) mark_as_det_done(q);//如果设置了q->passed_det为1//调用mark_as_det_done函数标记queue这一项已经fuzz过了,并保持q->passed_det为1

    q = q->next;//指向下一个队列元素
    id++;//id队列增加

  }

  if (in_place_resume) nuke_resume_dir();//遍历结束后检查in_place_resume //如果设置了,调用nuke_resume_dir()函数删除output/_resume/*临时目录,这个目录主要用于本地临时恢复

}


#ifndef SIMPLE_FILES

/* Construct a file name for a new test case, capturing the operation
   that led to its discovery. Uses a static buffer. */

static u8* describe_op(u8 hnb) {

  static u8 ret[256];

  if (syncing_party) {

    sprintf(ret, "sync:%s,src:%06u", syncing_party, syncing_case);

  } else {

    sprintf(ret, "src:%06u", current_entry);

    if (splicing_with >= 0)
      sprintf(ret + strlen(ret), "+%06u", splicing_with);

    sprintf(ret + strlen(ret), ",op:%s", stage_short);

    if (stage_cur_byte >= 0) {

      sprintf(ret + strlen(ret), ",pos:%u", stage_cur_byte);

      if (stage_val_type != STAGE_VAL_NONE)
        sprintf(ret + strlen(ret), ",val:%s%+d", 
                (stage_val_type == STAGE_VAL_BE) ? "be:" : "",
                stage_cur_val);

    } else sprintf(ret + strlen(ret), ",rep:%u", stage_cur_val);

  }

  if (hnb == 2) strcat(ret, ",+cov");

  return ret;

}

#endif /* !SIMPLE_FILES */


/* Write a message accompanying the crash directory :-) */

static void write_crash_readme(void) {

  u8* fn = alloc_printf("%s/crashes/README.txt", out_dir);
  s32 fd;
  FILE* f;

  fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  ck_free(fn);

  /* Do not die on errors here - that would be impolite. */

  if (fd < 0) return;

  f = fdopen(fd, "w");

  if (!f) {
    close(fd);
    return;
  }

  fprintf(f, "Command line used to find this crash:\n\n"

             "%s\n\n"

             "If you can't reproduce a bug outside of afl-fuzz, be sure to set the same\n"
             "memory limit. The limit used for this fuzzing session was %s.\n\n"

             "Need a tool to minimize test cases before investigating the crashes or sending\n"
             "them to a vendor? Check out the afl-tmin that comes with the fuzzer!\n\n"

             "Found any cool bugs in open-source tools using afl-fuzz? If yes, please drop\n"
             "me a mail at <lcamtuf@coredump.cx> once the issues are fixed - I'd love to\n"
             "add your finds to the gallery at:\n\n"

             "  http://lcamtuf.coredump.cx/afl/\n\n"

             "Thanks :-)\n",

             orig_cmdline, DMS(mem_limit << 20)); /* ignore errors */

  fclose(f);

}


/* Check if the result of an execve() during routine fuzzing is interesting,
   save or queue the input test case for further analysis if so. Returns 1 if
   entry is saved, 0 otherwise. */

static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {//检查这个case的执行结果是否是interesting的,决定是否保存或跳过

  u8  *fn = "";
  u8  hnb;
  s32 fd;
  u8  keeping = 0, res;

  if (fault == crash_mode) {//如果fault为crash_mode模式

    /* Keep only if there are new bits in the map, add to queue for
       future fuzzing, etc. */

    if (!(hnb = has_new_bits(virgin_bits))) { //检查是否出现了new bits
      if (crash_mode) total_crashes++; //检查是否出现了new bits//total_crashes计数器+1
      return 0; //如果没有设置crash_mode返回0
    }    

#ifndef SIMPLE_FILES //如果出现了new bits

    fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths,
                      describe_op(hnb)); //拼接路径out_dir/queue/id:00000queued_paths,describe_op(hnb

#else

    fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths);

#endif /* ^!SIMPLE_FILES */

    add_to_queue(fn, len, 0);//调用add_to_queue将拼接路径加入到队列中

    if (hnb == 2) {//如果hnb==2,这表明发现了新的路径
      queue_top->has_new_cov = 1;//设置queue_top->has_new_cov为1,标记覆盖范围+1
      queued_with_cov++; //queued_with_cov计数器+1
    }

    queue_top->exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); //利用hash32计算trace_bits,赋值给exec_cksum

    /* Try to calibrate inline; this also calls update_bitmap_score() when
       successful. */

    res = calibrate_case(argv, queue_top, mem, queue_cycle - 1, 0); //调用calibrate_case函数对新添加的queue进行校验

    if (res == FAULT_ERROR)//如果校验返回值为FAULT_ERROR,抛异常
      FATAL("Unable to execute target application");

    fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
    if (fd < 0) PFATAL("Unable to create '%s'", fn);
    ck_write(fd, mem, len, fn); //打开拼接路径,将mem的内容写入文件fn
    close(fd);

    keeping = 1; //设置keeping为1

  }

  switch (fault) { //通过Switch判断fault类型做进一步处理

    case FAULT_TMOUT://fault == FAULT_TMOUT时

      /* Timeouts are not very interesting, but we're still obliged to keep
         a handful of samples. We use the presence of new bits in the
         hang-specific bitmap as a signal of uniqueness. In "dumb" mode, we
         just keep everything. */

      total_tmouts++;//设置total_tmouts计数器+1

      if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping;//如果unique_hangs的个数超过能保存的最大数量KEEP_UNIQUE_HANG(默认为500)//直接返回keeping

      if (!dumb_mode) {//直接返回keeping

#ifdef WORD_SIZE_64
        simplify_trace((u64*)trace_bits);//调用simplify_trace对trace_bits进行调整
#else
        simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

        if (!has_new_bits(virgin_tmout)) return keeping; //如果没有新的超时路径//直接返回keeping

      }

      unique_tmouts++;//unique_tmouts计数器+1

      /* Before saving, we make sure that it's a genuine hang by re-running
         the target with a more generous timeout (unless the default timeout
         is already generous). */

      if (exec_tmout < hang_tmout) {//unique_tmouts计数器+1

        u8 new_fault;
        write_to_testcase(mem, len);//将mem写入out_file
        new_fault = run_target(argv, hang_tmout);//调用run_target运行一次,返回new_fault

        /* A corner case that one user reported bumping into: increasing the
           timeout actually uncovers a crash. Make sure we don't discard it if
           so. */

        if (!stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash; //如果没有发生主动中断,并且new_fault的结果为FAULT_CRASH,那么直接跳转到keep_as_crash

        if (stop_soon || new_fault != FAULT_TMOUT) return keeping; //如果发生主动中断或者new_fault的结果不为FAULT_CRASH,直接返回keeping

      }

#ifndef SIMPLE_FILES

      fn = alloc_printf("%s/hangs/id:%06llu,%s", out_dir,
                        unique_hangs, describe_op(0));//拼接字符串fn:out_dir/hangs/id:000000unique_hangs,describe_op(0)

#else

      fn = alloc_printf("%s/hangs/id_%06llu", out_dir,
                        unique_hangs);

#endif /* ^!SIMPLE_FILES */

      unique_hangs++;//unique_hangs计数器+1

      last_hang_time = get_cur_time(); //更新last_hang_time的值并保存找到fn中

      break;

    case FAULT_CRASH://fault == FAULT_CRASH时

keep_as_crash:

      /* This is handled in a manner roughly similar to timeouts,
         except for slightly different limits and no need to re-run test
         cases. */

      total_crashes++;//unique_hangs计数器+1

      if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping; //如果unique_crashes数量大于等于能保存的最大数量KEEP_UNIQUE_CRASH(默认5000)//直接返回keeping

      if (!dumb_mode) { //如果不是dumb_mode

#ifdef WORD_SIZE_64
        simplify_trace((u64*)trace_bits);//调用simplify_trace对trace_bits进行调整
#else
        simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

        if (!has_new_bits(virgin_crash)) return keeping;//如果没有发现新的crash路径//如果没有发现新的crash路径

      }

      if (!unique_crashes) write_crash_readme(); //如果unique_crashes为0//调用write_crash_readme编写崩溃目录信息

#ifndef SIMPLE_FILES

      fn = alloc_printf("%s/crashes/id:%06llu,sig:%02u,%s", out_dir,
                        unique_crashes, kill_signal, describe_op(0));//拼接路径

#else

      fn = alloc_printf("%s/crashes/id_%06llu_%02u", out_dir, unique_crashes,
                        kill_signal);

#endif /* ^!SIMPLE_FILES */

      unique_crashes++;//unique_crashes计数器+1

      last_crash_time = get_cur_time();//更新last_crash_time时间
      last_crash_execs = total_execs;//更新last_crash_execs

      break;

    case FAULT_ERROR: FATAL("Unable to execute target application"); //如果fault == FAULT_ERROR,抛出异常

    default: return keeping;//对于其他情况直接返回keeping

  }

  /* If we're here, we apparently want to save the crash or hang
     test case, too. */

  fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (fd < 0) PFATAL("Unable to create '%s'", fn);
  ck_write(fd, mem, len, fn);
  close(fd);

  ck_free(fn);

  return keeping;

}


/* When resuming, try to find the queue position to start from. This makes sense
   only when resuming, and when we can find the original fuzzer_stats. */

static u32 find_start_position(void) {

  static u8 tmp[4096]; /* Ought to be enough for anybody. */

  u8  *fn, *off;
  s32 fd, i;
  u32 ret;

  if (!resuming_fuzz) return 0; //如果不是resuming_fuzz,直接返回

  if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir);//如果是in_place_resume//拼接目录out_dir/fuzzer_stats
  else fn = alloc_printf("%s/../fuzzer_stats", in_dir); //如果不是in_place_resume//拼接目录in_dir/../fuzzer_stats

  fd = open(fn, O_RDONLY); //以只读模式打开fn指向的文件
  ck_free(fn);

  if (fd < 0) return 0;

  i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors *///将这个文件中的内容读到tmp中
  close(fd);

  off = strstr(tmp, "cur_path          : ");//寻找“cur_path”
  if (!off) return 0;

  ret = atoi(off + 20); //将cur_path后面的值设置为ret的值
  if (ret >= queued_paths) ret = 0;//如果大于queued_paths就设置ret为0
  return ret;

}


/* The same, but for timeouts. The idea is that when resuming sessions without
   -t given, we don't want to keep auto-scaling the timeout over and over
   again to prevent it from growing due to random flukes. */

static void find_timeout(void) { //当恢复本地会话时没有使用-t参数进行设置时,防止不停的调整超时时间。调用此函数需判断timeout_given变量是否进行设置,如果没有则调用本函数

  static u8 tmp[4096]; /* Ought to be enough for anybody. */

  u8  *fn, *off;
  s32 fd, i;
  u32 ret;

  if (!resuming_fuzz) return; //如果没有设置resuming_fuzz,则直接return

  if (in_place_resume) fn = alloc_printf("%s/fuzzer_stats", out_dir);//如果设置in_place_resume为1,则fn:“out_dir/fuzzer_stats”
  else fn = alloc_printf("%s/../fuzzer_stats", in_dir); //如果没有设置则fn:“in_dir/fuzzer_stats”

  fd = open(fn, O_RDONLY);//尝试以只读的模式打开fn
  ck_free(fn);

  if (fd < 0) return; //如果没打开就直接return

  i = read(fd, tmp, sizeof(tmp) - 1); (void)i; /* Ignore errors */; //读取文件内容到tmp[4096]中
  close(fd);

  off = strstr(tmp, "exec_timeout      : "); //在tmp中查找"exec_timeout      : "字样
  if (!off) return; //在tmp中查找"exec_timeout      : "字样

  ret = atoi(off + 20); //读取这个timeout值给ret
  if (ret <= 4) return; //读取这个timeout值给ret

  exec_tmout = ret; //读取这个timeout值给ret
  timeout_given = 3; //读取这个timeout值给ret

}


/* Update stats file for unattended monitoring. */

static void write_stats_file(double bitmap_cvg, double stability, double eps) { //更新统计信息文件以进行无人值守的监视

  static double last_bcvg, last_stab, last_eps;
  static struct rusage usage;

  u8* fn = alloc_printf("%s/fuzzer_stats", out_dir);//拼接out_dir/fuzzer_stats
  s32 fd;
  FILE* f;

  fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); //创建文件

  if (fd < 0) PFATAL("Unable to create '%s'", fn);

  ck_free(fn);

  f = fdopen(fd, "w");

  if (!f) PFATAL("fdopen() failed");

  /* Keep last values in case we're called from another context
     where exec/sec stats and such are not readily available. */

  if (!bitmap_cvg && !stability && !eps) {
    bitmap_cvg = last_bcvg;
    stability  = last_stab;
    eps        = last_eps;
  } else {
    last_bcvg = bitmap_cvg;
    last_stab = stability;
    last_eps  = eps;
  }
  //写入统计信息
  fprintf(f, "start_time        : %llu\n" //fuzz运行的开始时间
             "last_update       : %llu\n"//当前时间
             "fuzzer_pid        : %u\n"//获取当前pid
             "cycles_done       : %llu\n"//queue_cycle在queue_cur为空,即执行到当前队列尾的时候才增加1,所以这代表queue队列被完全变异一次的次数
             "execs_done        : %llu\n" //total_execs,target的总的执行次数,每次run_target的时候会增加1
             "execs_per_sec     : %0.02f\n"  //每秒执行的次数
             "paths_total       : %u\n" //queued_paths在每次add_to_queue的时候会增加1,代表queue里的样例总数
             "paths_favored     : %u\n"   //queued_favored,有价值的路径总数
             "paths_found       : %u\n"//queued_discovered在每次common_fuzz_stuff去执行一次fuzz时,发现新的interesting case的时候会增加1,代表在fuzz运行期间发现的新queue entry
             "paths_imported    : %u\n" //queued_imported是master-slave模式下,如果sync过来的case是interesting的,就增加1
             "max_depth         : %u\n"  //最大路径深度
             "cur_path          : %u\n" /* Must match find_start_position() */ //current_entry一般情况下代表的是正在执行的queue entry的整数ID,queue首节点的ID是0(Must match find_start_position())
             "pending_favs      : %u\n" //pending_favored 等待fuzz的favored paths数
             "pending_total     : %u\n" //pending_not_fuzzed 在queue中等待fuzz的case数
             "variable_paths    : %u\n"  //queued_variable在calibrate_case去评估一个新的test case的时候,如果发现这个case的路径是可变的,则将这个计数器加一,代表发现了一个可变case
             "stability         : %0.02f%%\n"         //稳定性
             "bitmap_cvg        : %0.02f%%\n"
             "unique_crashes    : %llu\n" //unique_crashes这是在save_if_interesting时,如果fault是FAULT_CRASH,就将unique_crashes计数器加一
             "unique_hangs      : %llu\n"  //unique_hangs这是在save_if_interesting时,如果fault是FAULT_TMOUT,且exec_tmout小于hang_tmout,就以hang_tmout为超时时间再执行一次,如果还超时,就让hang计数器加一
             "last_path         : %llu\n"//在add_to_queue里将一个新case加入queue时,就设置一次last_path_time为当前时间,last_path_time / 1000
             "last_crash        : %llu\n"//同上,在unique_crashes加一的时候,last_crash也更新时间,last_crash_time / 1000
             "last_hang         : %llu\n"  //同上,在unique_hangs加一的时候,last_hang也更新时间,last_hang_time / 1000
             "execs_since_crash : %llu\n"  //total_execs - last_crash_execs,这里last_crash_execs是在上一次crash的时候的总计执行了多少次
             "exec_timeout      : %u\n" /* Must match find_timeout() */
             "afl_banner        : %s\n"
             "afl_version       : " VERSION "\n"
             "target_mode       : %s%s%s%s%s%s%s\n"
             "command_line      : %s\n"
             "slowest_exec_ms   : %llu\n",
             start_time / 1000, get_cur_time() / 1000, getpid(),
             queue_cycle ? (queue_cycle - 1) : 0, total_execs, eps,
             queued_paths, queued_favored, queued_discovered, queued_imported,
             max_depth, current_entry, pending_favored, pending_not_fuzzed,
             queued_variable, stability, bitmap_cvg, unique_crashes,
             unique_hangs, last_path_time / 1000, last_crash_time / 1000,
             last_hang_time / 1000, total_execs - last_crash_execs,
             exec_tmout, use_banner,
             qemu_mode ? "qemu " : "", dumb_mode ? " dumb " : "",
             no_forkserver ? "no_forksrv " : "", crash_mode ? "crash " : "",
             persistent_mode ? "persistent " : "", deferred_mode ? "deferred " : "",
             (qemu_mode || dumb_mode || no_forkserver || crash_mode ||
              persistent_mode || deferred_mode) ? "" : "default",
             orig_cmdline, slowest_exec_ms);
             /* ignore errors */

  /* Get rss value from the children
     We must have killed the forkserver process and called waitpid
     before calling getrusage */
  if (getrusage(RUSAGE_CHILDREN, &usage)) {
      WARNF("getrusage failed");
  } else if (usage.ru_maxrss == 0) {
    fprintf(f, "peak_rss_mb       : not available while afl is running\n");
  } else {
#ifdef __APPLE__
    fprintf(f, "peak_rss_mb       : %zu\n", usage.ru_maxrss >> 20);
#else
    fprintf(f, "peak_rss_mb       : %zu\n", usage.ru_maxrss >> 10);
#endif /* ^__APPLE__ */
  }

  fclose(f);

}


/* Update the plot file if there is a reason to. */

static void maybe_update_plot_file(double bitmap_cvg, double eps) {

  static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md;
  static u64 prev_qc, prev_uc, prev_uh;

  if (prev_qp == queued_paths && prev_pf == pending_favored && 
      prev_pnf == pending_not_fuzzed && prev_ce == current_entry &&
      prev_qc == queue_cycle && prev_uc == unique_crashes &&
      prev_uh == unique_hangs && prev_md == max_depth) return;

  prev_qp  = queued_paths;
  prev_pf  = pending_favored;
  prev_pnf = pending_not_fuzzed;
  prev_ce  = current_entry;
  prev_qc  = queue_cycle;
  prev_uc  = unique_crashes;
  prev_uh  = unique_hangs;
  prev_md  = max_depth;

  /* Fields in the file:

     unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed,
     favored_not_fuzzed, unique_crashes, unique_hangs, max_depth,
     execs_per_sec */

  fprintf(plot_file, 
          "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n",
          get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths,
          pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes,
          unique_hangs, max_depth, eps); /* ignore errors */

  fflush(plot_file);

}



/* A helper function for maybe_delete_out_dir(), deleting all prefixed
   files in a directory. */

static u8 delete_files(u8* path, u8* prefix) {

  DIR* d;
  struct dirent* d_ent;

  d = opendir(path);

  if (!d) return 0;

  while ((d_ent = readdir(d))) {

    if (d_ent->d_name[0] != '.' && (!prefix ||
        !strncmp(d_ent->d_name, prefix, strlen(prefix)))) {

      u8* fname = alloc_printf("%s/%s", path, d_ent->d_name);
      if (unlink(fname)) PFATAL("Unable to delete '%s'", fname);
      ck_free(fname);

    }

  }

  closedir(d);

  return !!rmdir(path);

}


/* Get the number of runnable processes, with some simple smoothing. */

static double get_runnable_processes(void) {

  static double res;

#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__)

  /* I don't see any portable sysctl or so that would quickly give us the
     number of runnable processes; the 1-minute load average can be a
     semi-decent approximation, though. */

  if (getloadavg(&res, 1) != 1) return 0;

#else

  /* On Linux, /proc/stat is probably the best way; load averages are
     computed in funny ways and sometimes don't reflect extremely short-lived
     processes well. */

  FILE* f = fopen("/proc/stat", "r");
  u8 tmp[1024];
  u32 val = 0;

  if (!f) return 0;

  while (fgets(tmp, sizeof(tmp), f)) {

    if (!strncmp(tmp, "procs_running ", 14) ||
        !strncmp(tmp, "procs_blocked ", 14)) val += atoi(tmp + 14);

  }
 
  fclose(f);

  if (!res) {

    res = val;

  } else {

    res = res * (1.0 - 1.0 / AVG_SMOOTHING) +
          ((double)val) * (1.0 / AVG_SMOOTHING);

  }

#endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */

  return res;

}


/* Delete the temporary directory used for in-place session resume. */

static void nuke_resume_dir(void) {

  u8* fn;

  fn = alloc_printf("%s/_resume/.state/deterministic_done", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/_resume/.state/auto_extras", out_dir);
  if (delete_files(fn, "auto_")) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/_resume/.state/redundant_edges", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/_resume/.state/variable_behavior", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/_resume/.state", out_dir);
  if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/_resume", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  return;

dir_cleanup_failed:

  FATAL("_resume directory cleanup failed");

}


/* Delete fuzzer output directory if we recognize it as ours, if the fuzzer
   is not currently running, and if the last run time isn't too great. */

static void maybe_delete_out_dir(void) {

  FILE* f;
  u8 *fn = alloc_printf("%s/fuzzer_stats", out_dir);

  /* See if the output directory is locked. If yes, bail out. If not,
     create a lock that will persist for the lifetime of the process
     (this requires leaving the descriptor open).*/

  out_dir_fd = open(out_dir, O_RDONLY);
  if (out_dir_fd < 0) PFATAL("Unable to open '%s'", out_dir);

#ifndef __sun

  if (flock(out_dir_fd, LOCK_EX | LOCK_NB) && errno == EWOULDBLOCK) {

    SAYF("\n" cLRD "[-] " cRST
         "Looks like the job output directory is being actively used by another\n"
         "    instance of afl-fuzz. You will need to choose a different %s\n"
         "    or stop the other process first.\n",
         sync_id ? "fuzzer ID" : "output location");

    FATAL("Directory '%s' is in use", out_dir);

  }

#endif /* !__sun */

  f = fopen(fn, "r");

  if (f) {

    u64 start_time, last_update;

    if (fscanf(f, "start_time     : %llu\n"
                  "last_update    : %llu\n", &start_time, &last_update) != 2)
      FATAL("Malformed data in '%s'", fn);

    fclose(f);

    /* Let's see how much work is at stake. */

    if (!in_place_resume && last_update - start_time > OUTPUT_GRACE * 60) {

      SAYF("\n" cLRD "[-] " cRST
           "The job output directory already exists and contains the results of more\n"
           "    than %u minutes worth of fuzzing. To avoid data loss, afl-fuzz will *NOT*\n"
           "    automatically delete this data for you.\n\n"

           "    If you wish to start a new session, remove or rename the directory manually,\n"
           "    or specify a different output location for this job. To resume the old\n"
           "    session, put '-' as the input directory in the command line ('-i -') and\n"
           "    try again.\n", OUTPUT_GRACE);

       FATAL("At-risk data found in '%s'", out_dir);

    }

  }

  ck_free(fn);

  /* The idea for in-place resume is pretty simple: we temporarily move the old
     queue/ to a new location that gets deleted once import to the new queue/
     is finished. If _resume/ already exists, the current queue/ may be
     incomplete due to an earlier abort, so we want to use the old _resume/
     dir instead, and we let rename() fail silently. */

  if (in_place_resume) {

    u8* orig_q = alloc_printf("%s/queue", out_dir);

    in_dir = alloc_printf("%s/_resume", out_dir);

    rename(orig_q, in_dir); /* Ignore errors */

    OKF("Output directory exists, will attempt session resume.");

    ck_free(orig_q);

  } else {

    OKF("Output directory exists but deemed OK to reuse.");

  }

  ACTF("Deleting old session data...");

  /* Okay, let's get the ball rolling! First, we need to get rid of the entries
     in <out_dir>/.synced/.../id:*, if any are present. */

  if (!in_place_resume) {

    fn = alloc_printf("%s/.synced", out_dir);
    if (delete_files(fn, NULL)) goto dir_cleanup_failed;
    ck_free(fn);

  }

  /* Next, we need to clean up <out_dir>/queue/.state/ subdirectories: */

  fn = alloc_printf("%s/queue/.state/deterministic_done", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/queue/.state/auto_extras", out_dir);
  if (delete_files(fn, "auto_")) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/queue/.state/redundant_edges", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/queue/.state/variable_behavior", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  /* Then, get rid of the .state subdirectory itself (should be empty by now)
     and everything matching <out_dir>/queue/id:*. */

  fn = alloc_printf("%s/queue/.state", out_dir);
  if (rmdir(fn) && errno != ENOENT) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/queue", out_dir);
  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  /* All right, let's do <out_dir>/crashes/id:* and <out_dir>/hangs/id:*. */

  if (!in_place_resume) {

    fn = alloc_printf("%s/crashes/README.txt", out_dir);
    unlink(fn); /* Ignore errors */
    ck_free(fn);

  }

  fn = alloc_printf("%s/crashes", out_dir);

  /* Make backup of the crashes directory if it's not empty and if we're
     doing in-place resume. */

  if (in_place_resume && rmdir(fn)) {

    time_t cur_t = time(0);
    struct tm* t = localtime(&cur_t);

#ifndef SIMPLE_FILES

    u8* nfn = alloc_printf("%s.%04u-%02u-%02u-%02u:%02u:%02u", fn,
                           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
                           t->tm_hour, t->tm_min, t->tm_sec);

#else

    u8* nfn = alloc_printf("%s_%04u%02u%02u%02u%02u%02u", fn,
                           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
                           t->tm_hour, t->tm_min, t->tm_sec);

#endif /* ^!SIMPLE_FILES */

    rename(fn, nfn); /* Ignore errors. */
    ck_free(nfn);

  }

  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/hangs", out_dir);

  /* Backup hangs, too. */

  if (in_place_resume && rmdir(fn)) {

    time_t cur_t = time(0);
    struct tm* t = localtime(&cur_t);

#ifndef SIMPLE_FILES

    u8* nfn = alloc_printf("%s.%04u-%02u-%02u-%02u:%02u:%02u", fn,
                           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
                           t->tm_hour, t->tm_min, t->tm_sec);

#else

    u8* nfn = alloc_printf("%s_%04u%02u%02u%02u%02u%02u", fn,
                           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
                           t->tm_hour, t->tm_min, t->tm_sec);

#endif /* ^!SIMPLE_FILES */

    rename(fn, nfn); /* Ignore errors. */
    ck_free(nfn);

  }

  if (delete_files(fn, CASE_PREFIX)) goto dir_cleanup_failed;
  ck_free(fn);

  /* And now, for some finishing touches. */

  fn = alloc_printf("%s/.cur_input", out_dir);
  if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
  ck_free(fn);

  fn = alloc_printf("%s/fuzz_bitmap", out_dir);
  if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
  ck_free(fn);

  if (!in_place_resume) {
    fn  = alloc_printf("%s/fuzzer_stats", out_dir);
    if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
    ck_free(fn);
  }

  fn = alloc_printf("%s/plot_data", out_dir);
  if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed;
  ck_free(fn);

  OKF("Output dir cleanup successful.");

  /* Wow... is that all? If yes, celebrate! */

  return;

dir_cleanup_failed:

  SAYF("\n" cLRD "[-] " cRST
       "Whoops, the fuzzer tried to reuse your output directory, but bumped into\n"
       "    some files that shouldn't be there or that couldn't be removed - so it\n"
       "    decided to abort! This happened while processing this path:\n\n"

       "    %s\n\n"
       "    Please examine and manually delete the files, or specify a different\n"
       "    output location for the tool.\n", fn);

  FATAL("Output directory cleanup failed");

}


static void check_term_size(void);


/* A spiffy retro stats screen! This is called every stats_update_freq
   execve() calls, plus in several other circumstances. */

static void show_stats(void) {

  static u64 last_stats_ms, last_plot_ms, last_ms, last_execs;
  static double avg_exec;
  double t_byte_ratio, stab_ratio;

  u64 cur_ms;
  u32 t_bytes, t_bits;

  u32 banner_len, banner_pad;
  u8  tmp[256];

  cur_ms = get_cur_time();

  /* If not enough time has passed since last UI update, bail out. */

  if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return;

  /* Check if we're past the 10 minute mark. */

  if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1;

  /* Calculate smoothed exec speed stats. */

  if (!last_execs) {
  
    avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time);

  } else {

    double cur_avg = ((double)(total_execs - last_execs)) * 1000 /
                     (cur_ms - last_ms);

    /* If there is a dramatic (5x+) jump in speed, reset the indicator
       more quickly. */

    if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec)
      avg_exec = cur_avg;

    avg_exec = avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) +
               cur_avg * (1.0 / AVG_SMOOTHING);

  }

  last_ms = cur_ms;
  last_execs = total_execs;

  /* Tell the callers when to contact us (as measured in execs). */

  stats_update_freq = avg_exec / (UI_TARGET_HZ * 10);
  if (!stats_update_freq) stats_update_freq = 1;

  /* Do some bitmap stats. */

  t_bytes = count_non_255_bytes(virgin_bits);
  t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE;

  if (t_bytes) 
    stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes;
  else
    stab_ratio = 100;

  /* Roughly every minute, update fuzzer stats and save auto tokens. */

  if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) {

    last_stats_ms = cur_ms;
    write_stats_file(t_byte_ratio, stab_ratio, avg_exec);
    save_auto();
    write_bitmap();

  }

  /* Every now and then, write plot data. */

  if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) {

    last_plot_ms = cur_ms;
    maybe_update_plot_file(t_byte_ratio, avg_exec);
 
  }

  /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */

  if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed &&
      getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2;

  if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2;

  /* If we're not on TTY, bail out. */

  if (not_on_tty) return;

  /* Compute some mildly useful bitmap stats. */

  t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits);

  /* Now, for the visuals... */

  if (clear_screen) {

    SAYF(TERM_CLEAR CURSOR_HIDE);
    clear_screen = 0;

    check_term_size();

  }

  SAYF(TERM_HOME);

  if (term_too_small) {

    SAYF(cBRI "Your terminal is too small to display the UI.\n"
         "Please resize terminal window to at least 80x25.\n" cRST);

    return;

  }

  /* Let's start by drawing a centered banner. */

  banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner);
  banner_pad = (80 - banner_len) / 2;
  memset(tmp, ' ', banner_pad);

  sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN
          " (%s)",  crash_mode ? cPIN "peruvian were-rabbit" : 
          cYEL "american fuzzy lop", use_banner);

  SAYF("\n%s\n\n", tmp);

  /* "Handy" shortcuts for drawing boxes... */

#define bSTG    bSTART cGRA
#define bH2     bH bH
#define bH5     bH2 bH2 bH
#define bH10    bH5 bH5
#define bH20    bH10 bH10
#define bH30    bH20 bH10
#define SP5     "     "
#define SP10    SP5 SP5
#define SP20    SP10 SP10

  /* Lord, forgive me this. */

  SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH2 bHB
       bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n");

  if (dumb_mode) {

    strcpy(tmp, cRST);

  } else {

    u64 min_wo_finds = (cur_ms - last_path_time) / 1000 / 60;

    /* First queue cycle: don't stop now! */
    if (queue_cycle == 1 || min_wo_finds < 15) strcpy(tmp, cMGN); else

    /* Subsequent cycles, but we're still making finds. */
    if (cycles_wo_finds < 25 || min_wo_finds < 30) strcpy(tmp, cYEL); else

    /* No finds for a long time and no test cases to try. */
    if (cycles_wo_finds > 100 && !pending_not_fuzzed && min_wo_finds > 120)
      strcpy(tmp, cLGN);

    /* Default: cautiously OK to stop? */
    else strcpy(tmp, cLBL);

  }

  SAYF(bV bSTOP "        run time : " cRST "%-34s " bSTG bV bSTOP
       "  cycles done : %s%-5s  " bSTG bV "\n",
       DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1));

  /* We want to warn people about not seeing new paths after a full cycle,
     except when resuming fuzzing or running in non-instrumented mode. */

  if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 ||
      in_bitmap || crash_mode)) {

    SAYF(bV bSTOP "   last new path : " cRST "%-34s ",
         DTD(cur_ms, last_path_time));

  } else {

    if (dumb_mode)

      SAYF(bV bSTOP "   last new path : " cPIN "n/a" cRST 
           " (non-instrumented mode)        ");

     else

      SAYF(bV bSTOP "   last new path : " cRST "none yet " cLRD
           "(odd, check syntax!)      ");

  }

  SAYF(bSTG bV bSTOP "  total paths : " cRST "%-5s  " bSTG bV "\n",
       DI(queued_paths));

  /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH
     limit with a '+' appended to the count. */

  sprintf(tmp, "%s%s", DI(unique_crashes),
          (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");

  SAYF(bV bSTOP " last uniq crash : " cRST "%-34s " bSTG bV bSTOP
       " uniq crashes : %s%-6s " bSTG bV "\n",
       DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST,
       tmp);

  sprintf(tmp, "%s%s", DI(unique_hangs),
         (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");

  SAYF(bV bSTOP "  last uniq hang : " cRST "%-34s " bSTG bV bSTOP 
       "   uniq hangs : " cRST "%-6s " bSTG bV "\n",
       DTD(cur_ms, last_hang_time), tmp);

  SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH20 bHB bH bSTOP cCYA
       " map coverage " bSTG bH bHT bH20 bH2 bH bVL "\n");

  /* This gets funny because we want to print several variable-length variables
     together, but then cram them into a fixed-width field - so we need to
     put them in a temporary buffer first. */

  sprintf(tmp, "%s%s (%0.02f%%)", DI(current_entry),
          queue_cur->favored ? "" : "*",
          ((double)current_entry * 100) / queued_paths);

  SAYF(bV bSTOP "  now processing : " cRST "%-17s " bSTG bV bSTOP, tmp);

  sprintf(tmp, "%0.02f%% / %0.02f%%", ((double)queue_cur->bitmap_size) * 
          100 / MAP_SIZE, t_byte_ratio);

  SAYF("    map density : %s%-21s " bSTG bV "\n", t_byte_ratio > 70 ? cLRD : 
       ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp);

  sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths),
          ((double)cur_skipped_paths * 100) / queued_paths);

  SAYF(bV bSTOP " paths timed out : " cRST "%-17s " bSTG bV, tmp);

  sprintf(tmp, "%0.02f bits/tuple",
          t_bytes ? (((double)t_bits) / t_bytes) : 0);

  SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp);

  SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH20 bX bH bSTOP cCYA
       " findings in depth " bSTG bH20 bVL "\n");

  sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored),
          ((double)queued_favored) * 100 / queued_paths);

  /* Yeah... it's still going on... halp? */

  SAYF(bV bSTOP "  now trying : " cRST "%-21s " bSTG bV bSTOP 
       " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp);

  if (!stage_max) {

    sprintf(tmp, "%s/-", DI(stage_cur));

  } else {

    sprintf(tmp, "%s/%s (%0.02f%%)", DI(stage_cur), DI(stage_max),
            ((double)stage_cur) * 100 / stage_max);

  }

  SAYF(bV bSTOP " stage execs : " cRST "%-21s " bSTG bV bSTOP, tmp);

  sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov),
          ((double)queued_with_cov) * 100 / queued_paths);

  SAYF("  new edges on : " cRST "%-22s " bSTG bV "\n", tmp);

  sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes),
          (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");

  if (crash_mode) {

    SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP
         "   new crashes : %s%-22s " bSTG bV "\n", DI(total_execs),
         unique_crashes ? cLRD : cRST, tmp);

  } else {

    SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP
         " total crashes : %s%-22s " bSTG bV "\n", DI(total_execs),
         unique_crashes ? cLRD : cRST, tmp);

  }

  /* Show a warning about slow execution. */

  if (avg_exec < 100) {

    sprintf(tmp, "%s/sec (%s)", DF(avg_exec), avg_exec < 20 ?
            "zzzz..." : "slow!");

    SAYF(bV bSTOP "  exec speed : " cLRD "%-21s ", tmp);

  } else {

    sprintf(tmp, "%s/sec", DF(avg_exec));
    SAYF(bV bSTOP "  exec speed : " cRST "%-21s ", tmp);

  }

  sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts),
          (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");

  SAYF (bSTG bV bSTOP "  total tmouts : " cRST "%-22s " bSTG bV "\n", tmp);

  /* Aaaalmost there... hold on! */

  SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bH bHT bH10
       bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bH bVL "\n");

  if (skip_deterministic) {

    strcpy(tmp, "n/a, n/a, n/a");

  } else {

    sprintf(tmp, "%s/%s, %s/%s, %s/%s",
            DI(stage_finds[STAGE_FLIP1]), DI(stage_cycles[STAGE_FLIP1]),
            DI(stage_finds[STAGE_FLIP2]), DI(stage_cycles[STAGE_FLIP2]),
            DI(stage_finds[STAGE_FLIP4]), DI(stage_cycles[STAGE_FLIP4]));

  }

  SAYF(bV bSTOP "   bit flips : " cRST "%-37s " bSTG bV bSTOP "    levels : "
       cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth));

  if (!skip_deterministic)
    sprintf(tmp, "%s/%s, %s/%s, %s/%s",
            DI(stage_finds[STAGE_FLIP8]), DI(stage_cycles[STAGE_FLIP8]),
            DI(stage_finds[STAGE_FLIP16]), DI(stage_cycles[STAGE_FLIP16]),
            DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32]));

  SAYF(bV bSTOP "  byte flips : " cRST "%-37s " bSTG bV bSTOP "   pending : "
       cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed));

  if (!skip_deterministic)
    sprintf(tmp, "%s/%s, %s/%s, %s/%s",
            DI(stage_finds[STAGE_ARITH8]), DI(stage_cycles[STAGE_ARITH8]),
            DI(stage_finds[STAGE_ARITH16]), DI(stage_cycles[STAGE_ARITH16]),
            DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32]));

  SAYF(bV bSTOP " arithmetics : " cRST "%-37s " bSTG bV bSTOP "  pend fav : "
       cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored));

  if (!skip_deterministic)
    sprintf(tmp, "%s/%s, %s/%s, %s/%s",
            DI(stage_finds[STAGE_INTEREST8]), DI(stage_cycles[STAGE_INTEREST8]),
            DI(stage_finds[STAGE_INTEREST16]), DI(stage_cycles[STAGE_INTEREST16]),
            DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32]));

  SAYF(bV bSTOP "  known ints : " cRST "%-37s " bSTG bV bSTOP " own finds : "
       cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered));

  if (!skip_deterministic)
    sprintf(tmp, "%s/%s, %s/%s, %s/%s",
            DI(stage_finds[STAGE_EXTRAS_UO]), DI(stage_cycles[STAGE_EXTRAS_UO]),
            DI(stage_finds[STAGE_EXTRAS_UI]), DI(stage_cycles[STAGE_EXTRAS_UI]),
            DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO]));

  SAYF(bV bSTOP "  dictionary : " cRST "%-37s " bSTG bV bSTOP
       "  imported : " cRST "%-10s " bSTG bV "\n", tmp,
       sync_id ? DI(queued_imported) : (u8*)"n/a");

  sprintf(tmp, "%s/%s, %s/%s",
          DI(stage_finds[STAGE_HAVOC]), DI(stage_cycles[STAGE_HAVOC]),
          DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE]));

  SAYF(bV bSTOP "       havoc : " cRST "%-37s " bSTG bV bSTOP, tmp);

  if (t_bytes) sprintf(tmp, "%0.02f%%", stab_ratio);
    else strcpy(tmp, "n/a");

  SAYF(" stability : %s%-10s " bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) 
       ? cLRD : ((queued_variable && (!persistent_mode || var_byte_count > 20))
       ? cMGN : cRST), tmp);

  if (!bytes_trim_out) {

    sprintf(tmp, "n/a, ");

  } else {

    sprintf(tmp, "%0.02f%%/%s, ",
            ((double)(bytes_trim_in - bytes_trim_out)) * 100 / bytes_trim_in,
            DI(trim_execs));

  }

  if (!blocks_eff_total) {

    u8 tmp2[128];

    sprintf(tmp2, "n/a");
    strcat(tmp, tmp2);

  } else {

    u8 tmp2[128];

    sprintf(tmp2, "%0.02f%%",
            ((double)(blocks_eff_total - blocks_eff_select)) * 100 /
            blocks_eff_total);

    strcat(tmp, tmp2);

  }

  SAYF(bV bSTOP "        trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n"
       bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp);

  /* Provide some CPU utilization stats. */

  if (cpu_core_count) {

    double cur_runnable = get_runnable_processes();
    u32 cur_utilization = cur_runnable * 100 / cpu_core_count;

    u8* cpu_color = cCYA;

    /* If we could still run one or more processes, use green. */

    if (cpu_core_count > 1 && cur_runnable + 1 <= cpu_core_count)
      cpu_color = cLGN;

    /* If we're clearly oversubscribed, use red. */

    if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD;

#ifdef HAVE_AFFINITY

    if (cpu_aff >= 0) {

      SAYF(SP10 cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, 
           MIN(cpu_aff, 999), cpu_color,
           MIN(cur_utilization, 999));

    } else {

      SAYF(SP10 cGRA "   [cpu:%s%3u%%" cGRA "]\r" cRST,
           cpu_color, MIN(cur_utilization, 999));
 
   }

#else

    SAYF(SP10 cGRA "   [cpu:%s%3u%%" cGRA "]\r" cRST,
         cpu_color, MIN(cur_utilization, 999));

#endif /* ^HAVE_AFFINITY */

  } else SAYF("\r");

  /* Hallelujah! */

  fflush(0);

}


/* Display quick statistics at the end of processing the input directory,
   plus a bunch of warnings. Some calibration stuff also ended up here,
   along with several hardcoded constants. Maybe clean up eventually. */

static void show_init_stats(void) {//在处理输入目录的末尾显示快速统计信息,还有一堆警告。一些校准的东西也在这里结束,还有一些硬编码的常量

  struct queue_entry* q = queue;
  u32 min_bits = 0, max_bits = 0;
  u64 min_us = 0, max_us = 0;
  u64 avg_us = 0;
  u32 max_len = 0;

  if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles; //total_cal_us总轮次执行时间、total_cal_cycles总执行轮次 //计算出平均单轮执行时间avg_us

  while (q) {//遍历queue,更新min_us、max_us、min_bits、max_bits、max_len

    if (!min_us || q->exec_us < min_us) min_us = q->exec_us;
    if (q->exec_us > max_us) max_us = q->exec_us;

    if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size;
    if (q->bitmap_size > max_bits) max_bits = q->bitmap_size;

    if (q->len > max_len) max_len = q->len;

    q = q->next;

  }

  SAYF("\n");

  if (avg_us > (qemu_mode ? 50000 : 10000)) //如果avg_us大于10000就警告
    WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.",
          doc_path);

  /* Let's keep things moving with slow binaries. */

  if (avg_us > 50000) havoc_div = 10;     /* 0-19 execs/sec   */ //如果大于50000,设置havoc_div为10
  else if (avg_us > 20000) havoc_div = 5; /* 20-49 execs/sec  *///如果大于20000,设置havoc_div为5
  else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec *///如果大于10000,设置havoc_div为2

  if (!resuming_fuzz) {//如果不是resuming session

    if (max_len > 50 * 1024)//对queue的大小超限发出警告
      WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!",
            DMS(max_len), doc_path);
    else if (max_len > 10 * 1024)
      WARNF("Some test cases are big (%s) - see %s/perf_tips.txt.",
            DMS(max_len), doc_path);

    if (useless_at_start && !in_bitmap)//如果useless_at_start不为0,就警告可以精简样本
      WARNF(cLRD "Some test cases look useless. Consider using a smaller set.");

    if (queued_paths > 100)
      WARNF(cLRD "You probably have far too many input files! Consider trimming down.");
    else if (queued_paths > 20)
      WARNF("You have lots of input files; try starting small.");

  }

  OKF("Here are some useful stats:\n\n"

      cGRA "    Test case count : " cRST "%u favored, %u variable, %u total\n"
      cGRA "       Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n"
      cGRA "        Exec timing : " cRST "%s to %s us (average: %s us)\n",
      queued_favored, queued_variable, queued_paths, min_bits, max_bits, 
      ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1),
      DI(min_us), DI(max_us), DI(avg_us));

  if (!timeout_given) {//如果timeout_given为0

    /* Figure out the appropriate timeout. The basic idea is: 5x average or
       1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second.

       If the program is slow, the multiplier is lowered to 2x or 3x, because
       random scheduler jitter is less likely to have any impact, and because
       our patience is wearing thin =) */

    if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000;
    else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000;
    else exec_tmout = avg_us * 5 / 1000;//根据avg_us来计算出exec_tmout的值

    exec_tmout = MAX(exec_tmout, max_us / 1000);//exec_tmout和所有样例中执行时间最长的样例进行比较,取最大值给exec_tmout
    exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND;

    if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT;//如果exec_tmout大于EXEC_TIMEOUT//就设置为EXEC_TIMEOUT

    ACTF("No -t option specified, so I'll use exec timeout of %u ms.", 
         exec_tmout);

    timeout_given = 1; //设置timeout_given为1

  } else if (timeout_given == 3) { //如果timeout_given不为0且为3
      //代表这是resuming session
    ACTF("Applying timeout settings from resumed session (%u ms).", exec_tmout);

  }

  /* In dumb mode, re-running every timing out test case with a generous time
     limit is very expensive, so let's select a more conservative default. */

  if (dumb_mode && !getenv("AFL_HANG_TMOUT"))//如果是dumb_mode模式,并且没有设置AFL_HANG_TMOUT环境变量
    hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100);//设置hang_tmout为exec_tmout * 2 + 100和EXEC_TIMEOUT中的最小值

  OKF("All set and ready to roll!");

}


/* Find first power of two greater or equal to val (assuming val under
   2^31). */

static u32 next_p2(u32 val) {

  u32 ret = 1;
  while (val > ret) ret <<= 1;
  return ret;

} 


/* Trim all new test cases to save cycles when doing deterministic checks. The
   trimmer uses power-of-two increments somewhere between 1/16 and 1/1024 of
   file size, to keep the stage short and sweet. */

static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {//修剪case

  static u8 tmp[64];
  static u8 clean_trace[MAP_SIZE];

  u8  needs_write = 0, fault = 0;
  u32 trim_exec = 0;
  u32 remove_len;
  u32 len_p2;

  /* Although the trimmer will be less useful when variable behavior is
     detected, it will still work to some extent, so we don't check for
     this. */

  if (q->len < 5) return 0; //如果case的len小于5字节,就不需要进行修剪//直接返回0

  stage_name = tmp;//让stage_name指向tmp数组首位
  bytes_trim_in += q->len;//bytes_trim_in累加q->len

  /* Select initial chunk len, starting with large steps. */

  len_p2 = next_p2(q->len); //len_p2为大于等于q->len的第一个2的幂次

  remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES);//remove_len取len_p2/16或4之间最大的那个,作为初始步长

  /* Continue until the number of steps gets too high or the stepover
     gets too small. */

  while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) {//如果remove_len大于等于(len_p2/1024)和(4)之间最大的

    u32 remove_pos = remove_len;//设置remove_pos为remove_len

    sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len));//调用sprintf格式化字符串到tmp变量中

    stage_cur = 0;
    stage_max = q->len / remove_len;

    while (remove_pos < q->len) {//循环每次前进remove_len个步长,直到整个文件都被遍历完为止

      u32 trim_avail = MIN(remove_len, q->len - remove_pos);
      u32 cksum;

      write_with_gap(in_buf, q->len, remove_pos, trim_avail);//有in_buf中的remove_pos处开始,向后跳过remove_len个字节,写入到.cur_input中

      fault = run_target(argv, exec_tmout); //调用run_target,返回值赋值给fault中
      trim_execs++;//trim_execs计数器+1

      if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;//如果主动中断,或返回值报错,直接跳转至abort_trimming

      /* Note that we don't keep track of crashes or hangs here; maybe TODO? */

      cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//调用hash32进行计算

      /* If the deletion had no impact on the trace, make it permanent. This
         isn't perfect for variable-path inputs, but we're just making a
         best-effort pass, so it's not a big deal if we end up with false
         negatives every now and then. */

      if (cksum == q->exec_cksum) {//判断校验值如果相等

        u32 move_tail = q->len - remove_pos - trim_avail;//从q->len中减去remove_len个字节

        q->len -= trim_avail;
        len_p2  = next_p2(q->len);//并重新计算出一个len_p2

        memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,  //将in_buf + remove_pos + trim_avail到最后的字节前移到in_buf + remove_pos处,即删除了remove_pos向后的remove_len个字节
                move_tail);

        /* Let's save a clean trace, which will be needed by
           update_bitmap_score once we're done with the trimming stuff. */

        if (!needs_write) {//如果needs_write为0

          needs_write = 1; //设置needs_write为1
          memcpy(clean_trace, trace_bits, MAP_SIZE);//保存当前的trace_bits到clean_trace中

        }

      } else //如果不为0
          remove_pos += remove_len; //remove_pos累加remove_len,即前移remove_len个字节,如果相等则无需前移

      /* Since this can be slow, update the screen every now and then. */

      if (!(trim_exec++ % stats_update_freq)) show_stats();//在trim过程中,每执行stats_update_freq次,刷新一次界面
      stage_cur++;//stage_cur计数器+1

    }

    remove_len >>= 1;//remove_len减半

  }

  /* If we have made changes to in_buf, we also need to update the on-disk
     version of the test case. */

  if (needs_write) { //如果needs_write为1

    s32 fd;

    unlink(q->fname); /* ignore errors */

    fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);

    if (fd < 0) PFATAL("Unable to create '%s'", q->fname);//创建一个新的q->fname

    ck_write(fd, in_buf, q->len, q->fname);//创建一个新的q->fname
    close(fd);

    memcpy(trace_bits, clean_trace, MAP_SIZE);//利用clean_trace恢复trace_bits
    update_bitmap_score(q);

  }

abort_trimming:

  bytes_trim_out += q->len;
  return fault;

}


/* Write a modified test case, run program, process results. Handle
   error conditions, returning 1 if it's time to bail out. This is
   a helper function for fuzz_one(). */

EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {

  u8 fault;

  if (post_handler) {//如果定义了post_handler

    out_buf = post_handler(out_buf, &len); // 调用post_handler返回out_buf
    if (!out_buf || !len) return 0;//如果out_buf或len其中一个为0//直接返回0,如果需要对变异完的queue,最一层包装在写入的时候非常有用

  }

  write_to_testcase(out_buf, len);//写入

  fault = run_target(argv, exec_tmout); //run_target运行一下返回结果

  if (stop_soon) return 1;//如果主动中断 //返回1

  if (fault == FAULT_TMOUT) {
      //如果fault值为FAULT_TMOUT
    if (subseq_tmouts++ > TMOUT_LIMIT) {//如果subseq_tmouts++ > TMOUT_LIMIT(默认250)
      cur_skipped_paths++;//cur_skipped_paths计数器+1
      return 1;
    }

  } else subseq_tmouts = 0;//连续超时计数设置为0

  /* Users can hit us with SIGUSR1 to request the current input
     to be abandoned. */

  if (skip_requested) {//如果设置了skip_requested

     skip_requested = 0; //skip_requested清0
     cur_skipped_paths++;//cur_skipped_paths计数器+1
     return 1; //返回1

  }

  /* This handles FAULT_ERROR for us: */

  queued_discovered += save_if_interesting(argv, out_buf, len, fault);//如果发现了新的路径queued_discovered+1

  if (!(stage_cur % stats_update_freq) || stage_cur + 1 == stage_max)
    show_stats(); //如果stage_cur除以stats_update_freq余数是0,或者其加一等于stage_max,就更新展示界面show_stats

  return 0;//返回0

}


/* Helper to choose random block len for block operations in fuzz_one().
   Doesn't return zero, provided that max_len is > 0. */

static u32 choose_block_len(u32 limit) {

  u32 min_value, max_value;
  u32 rlim = MIN(queue_cycle, 3);

  if (!run_over10m) rlim = 1;

  switch (UR(rlim)) {

    case 0:  min_value = 1;
             max_value = HAVOC_BLK_SMALL;
             break;

    case 1:  min_value = HAVOC_BLK_SMALL;
             max_value = HAVOC_BLK_MEDIUM;
             break;

    default: 

             if (UR(10)) {

               min_value = HAVOC_BLK_MEDIUM;
               max_value = HAVOC_BLK_LARGE;

             } else {

               min_value = HAVOC_BLK_LARGE;
               max_value = HAVOC_BLK_XL;

             }

  }

  if (min_value >= limit) min_value = 1;

  return min_value + UR(MIN(max_value, limit) - min_value + 1);

}


/* Calculate case desirability score to adjust the length of havoc fuzzing.
   A helper function for fuzz_one(). Maybe some of these constants should
   go into config.h. */

static u32 calculate_score(struct queue_entry* q) {//根据queue entry的执行速度、覆盖到的path数和路径深度来评估出一个得分,这个得分perf_score在后面havoc的时候使用

  u32 avg_exec_us = total_cal_us / total_cal_cycles; //计算平均时间
  u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries; //计算平均时间
  u32 perf_score = 100;//设置初始值perf_score为100

  /* Adjust score based on execution speed of this path, compared to the
     global average. Multiplier ranges from 0.1x to 3x. Fast inputs are
     less expensive to fuzz, so we're giving them more air time. */

  if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10;//为q->exec_us乘以一个系数,判断和avg_exec_us的大小来调整perf_score
  else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25;
  else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50;
  else if (q->exec_us * 0.75 > avg_exec_us) perf_score = 75;
  else if (q->exec_us * 4 < avg_exec_us) perf_score = 300;
  else if (q->exec_us * 3 < avg_exec_us) perf_score = 200;
  else if (q->exec_us * 2 < avg_exec_us) perf_score = 150;

  /* Adjust score based on bitmap size. The working theory is that better
     coverage translates to better targets. Multiplier from 0.25x to 3x. */

  if (q->bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3;//为bitmap_size乘以一个系数,判断与avg_bitmap_size的大小来调整perf_score
  else if (q->bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2;
  else if (q->bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5;
  else if (q->bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25;
  else if (q->bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5;
  else if (q->bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75;

  /* Adjust score based on handicap. Handicap is proportional to how late
     in the game we learned about this path. Latecomers are allowed to run
     for a bit longer until they catch up with the rest. */

  if (q->handicap >= 4) {//q->handicap大于等于4

    perf_score *= 4;//perf_score * 4
    q->handicap -= 4; //q->handicap - 4

  } else if (q->handicap) {//如果q->handicap不为0

    perf_score *= 2;//如果q->handicap不为0
    q->handicap--; //q->handicap - 1

  }

  /* Final adjustment based on input depth, under the assumption that fuzzing
     deeper test cases is more likely to reveal stuff that can't be
     discovered with traditional fuzzers. */

  switch (q->depth) { //通过深度来调整perf_score

    case 0 ... 3:   break;
    case 4 ... 7:   perf_score *= 2; break;
    case 8 ... 13:  perf_score *= 3; break;
    case 14 ... 25: perf_score *= 4; break;
    default:        perf_score *= 5;

  }

  /* Make sure that we don't go over limit. */

  if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100; //确保调整后的值不会超过最大的接线

  return perf_score;//返回perf_score

}


/* Helper function to see if a particular change (xor_val = old ^ new) could
   be a product of deterministic bit flips with the lengths and stepovers
   attempted by afl-fuzz. This is used to avoid dupes in some of the
   deterministic fuzzing operations that follow bit flips. We also
   return 1 if xor_val is zero, which implies that the old and attempted new
   values are identical and the exec would be a waste of time. */

static u8 could_be_bitflip(u32 xor_val) {

  u32 sh = 0;

  if (!xor_val) return 1;

  /* Shift left until first bit set. */

  while (!(xor_val & 1)) { sh++; xor_val >>= 1; }

  /* 1-, 2-, and 4-bit patterns are OK anywhere. */

  if (xor_val == 1 || xor_val == 3 || xor_val == 15) return 1;

  /* 8-, 16-, and 32-bit patterns are OK only if shift factor is
     divisible by 8, since that's the stepover for these ops. */

  if (sh & 7) return 0;

  if (xor_val == 0xff || xor_val == 0xffff || xor_val == 0xffffffff)
    return 1;

  return 0;

}


/* Helper function to see if a particular value is reachable through
   arithmetic operations. Used for similar purposes. */

static u8 could_be_arith(u32 old_val, u32 new_val, u8 blen) {

  u32 i, ov = 0, nv = 0, diffs = 0;

  if (old_val == new_val) return 1;

  /* See if one-byte adjustments to any byte could produce this result. */

  for (i = 0; i < blen; i++) {

    u8 a = old_val >> (8 * i),
       b = new_val >> (8 * i);

    if (a != b) { diffs++; ov = a; nv = b; }

  }

  /* If only one byte differs and the values are within range, return 1. */

  if (diffs == 1) {

    if ((u8)(ov - nv) <= ARITH_MAX ||
        (u8)(nv - ov) <= ARITH_MAX) return 1;

  }

  if (blen == 1) return 0;

  /* See if two-byte adjustments to any byte would produce this result. */

  diffs = 0;

  for (i = 0; i < blen / 2; i++) {

    u16 a = old_val >> (16 * i),
        b = new_val >> (16 * i);

    if (a != b) { diffs++; ov = a; nv = b; }

  }

  /* If only one word differs and the values are within range, return 1. */

  if (diffs == 1) {

    if ((u16)(ov - nv) <= ARITH_MAX ||
        (u16)(nv - ov) <= ARITH_MAX) return 1;

    ov = SWAP16(ov); nv = SWAP16(nv);

    if ((u16)(ov - nv) <= ARITH_MAX ||
        (u16)(nv - ov) <= ARITH_MAX) return 1;

  }

  /* Finally, let's do the same thing for dwords. */

  if (blen == 4) {

    if ((u32)(old_val - new_val) <= ARITH_MAX ||
        (u32)(new_val - old_val) <= ARITH_MAX) return 1;

    new_val = SWAP32(new_val);
    old_val = SWAP32(old_val);

    if ((u32)(old_val - new_val) <= ARITH_MAX ||
        (u32)(new_val - old_val) <= ARITH_MAX) return 1;

  }

  return 0;

}


/* Last but not least, a similar helper to see if insertion of an 
   interesting integer is redundant given the insertions done for
   shorter blen. The last param (check_le) is set if the caller
   already executed LE insertion for current blen and wants to see
   if BE variant passed in new_val is unique. */

static u8 could_be_interest(u32 old_val, u32 new_val, u8 blen, u8 check_le) {

  u32 i, j;

  if (old_val == new_val) return 1;

  /* See if one-byte insertions from interesting_8 over old_val could
     produce new_val. */

  for (i = 0; i < blen; i++) {

    for (j = 0; j < sizeof(interesting_8); j++) {

      u32 tval = (old_val & ~(0xff << (i * 8))) |
                 (((u8)interesting_8[j]) << (i * 8));

      if (new_val == tval) return 1;

    }

  }

  /* Bail out unless we're also asked to examine two-byte LE insertions
     as a preparation for BE attempts. */

  if (blen == 2 && !check_le) return 0;

  /* See if two-byte insertions over old_val could give us new_val. */

  for (i = 0; i < blen - 1; i++) {

    for (j = 0; j < sizeof(interesting_16) / 2; j++) {

      u32 tval = (old_val & ~(0xffff << (i * 8))) |
                 (((u16)interesting_16[j]) << (i * 8));

      if (new_val == tval) return 1;

      /* Continue here only if blen > 2. */

      if (blen > 2) {

        tval = (old_val & ~(0xffff << (i * 8))) |
               (SWAP16(interesting_16[j]) << (i * 8));

        if (new_val == tval) return 1;

      }

    }

  }

  if (blen == 4 && check_le) {

    /* See if four-byte insertions could produce the same result
       (LE only). */

    for (j = 0; j < sizeof(interesting_32) / 4; j++)
      if (new_val == (u32)interesting_32[j]) return 1;

  }

  return 0;

}


/* Take the current entry from the queue, fuzz it for a while. This
   function is a tad too long... returns 0 if fuzzed successfully, 1 if
   skipped or bailed out. */

static u8 fuzz_one(char** argv) {//这部分主要是fuzz前最后的一些准备,例如:初始化变量、根据概率及前期轮次结果判断返回节点,映射case等

  s32 len, fd, temp_len, i, j;
  u8  *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0;
  u64 havoc_queued,  orig_hit_cnt, new_hit_cnt;
  u32 splice_cycle = 0, perf_score = 100, orig_perf, prev_cksum, eff_cnt = 1;

  u8  ret_val = 1, doing_det = 0;

  u8  a_collect[MAX_AUTO_EXTRA];
  u32 a_len = 0;

#ifdef IGNORE_FINDS

  /* In IGNORE_FINDS mode, skip any entries that weren't in the
     initial data set. */

  if (queue_cur->depth > 1) return 1;

#else

  if (pending_favored) {//如果pending_favored不为0

    /* If we have any favored, non-fuzzed new arrivals in the queue,
       possibly skip to them at the expense of already-fuzzed or non-favored
       cases. */

    if ((queue_cur->was_fuzzed || !queue_cur->favored) &&
        UR(100) < SKIP_TO_NEW_PROB) return 1; //判断(当前case是否已经fuzz过了或者不是favored)并且产生一个100以内的随机数,如果小于SKIP_TO_NEW_PROB(默认99)
    //即如果当前queue选项被fuzz过,或者不是一个favored,有99%的概率会直接return 1
  } else if (!dumb_mode && !queue_cur->favored && queued_paths > 10) {//如果pending_favored为0,且不是dumb_mode模式,且本次case不是favored,且queue中的case数量大于10

    /* Otherwise, still possibly skip non-favored cases, albeit less often.
       The odds of skipping stuff are higher for already-fuzzed inputs and
       lower for never-fuzzed entries. */

    if (queue_cycle > 1 && !queue_cur->was_fuzzed) { //如果queue_cycle(fuzz轮次)大于1,且本次case没有被fuzz过

      if (UR(100) < SKIP_NFAV_NEW_PROB) return 1;//有75%的概率return 1(SKIP_NFAV_NEW_PROB默认为75)

    } else {

      if (UR(100) < SKIP_NFAV_OLD_PROB) return 1;//否则有95%的概率return 1(SKIP_NFAV_OLD_PROB默认为95)

    }

  }

#endif /* ^IGNORE_FINDS */

  if (not_on_tty) {//如果不是tty模式
    ACTF("Fuzzing test case #%u (%u total, %llu uniq crashes found)...",
         current_entry, queued_paths, unique_crashes); //输出current_entry、queued_paths、unique_crashes
    fflush(stdout); //刷新stdout缓冲区
  }

  /* Map the test case into memory. */

  fd = open(queue_cur->fname, O_RDONLY); //以只读的形式打开case文件

  if (fd < 0) PFATAL("Unable to open '%s'", queue_cur->fname); //如果打开失败抛出异常

  len = queue_cur->len;//将case大小赋值给len

  orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); //mamap对case文件进行映射

  if (orig_in == MAP_FAILED) PFATAL("Unable to mmap '%s'", queue_cur->fname); //如果映射失败就报错

  close(fd);

  /* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every
     single byte anyway, so it wouldn't give us any performance or memory usage
     benefits. */

  out_buf = ck_alloc_nozero(len); //分配len大小的空间

  subseq_tmouts = 0; //将subseq_tmouts赋值为0

  cur_depth = queue_cur->depth;////设置cur_depth为当前case路径深度

  /*******************************************
   * CALIBRATION (only if failed earlier on) *该阶段主要核实case的校验错误,如果小于3则调用calibrate_case进行重新校验,再根据返回结果做下一步判断
   *******************************************/

  if (queue_cur->cal_failed) {//如果当前queue_cur->cal_failed为1,即case存在校准错误

    u8 res = FAULT_TMOUT;

    if (queue_cur->cal_failed < CAL_CHANCES) {//如果校准错误小于3(CAL_CHANCES默认为3)

      /* Reset exec_cksum to tell calibrate_case to re-execute the testcase
         avoiding the usage of an invalid trace_bits.
         For more info: https://github.com/AFLplusplus/AFLplusplus/pull/425 */

      queue_cur->exec_cksum = 0;//设置queue_cur->exec_cksum为0

      res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0);//调用calibrate_case对case进行重新校验

      if (res == FAULT_ERROR)//如果返回值为FAULT_ERROR,直接报错终止
        FATAL("Unable to execute target application");

    }

    if (stop_soon || res != crash_mode) {// 如果设置了stop_soon或者res不等于crash_mode
      cur_skipped_paths++;//cur_skipped_paths计数+1
      goto abandon_entry;//跳转到abandon_entry
    }

  }

  /************
   * TRIMMING *该阶段主要判断case是否已经被修剪,没有过就调用trim_case修剪一次,更新修剪后case的大小
   ************/

  if (!dumb_mode && !queue_cur->trim_done) { //如果不是dumb_mode模式,并且case没有经过修剪

    u8 res = trim_case(argv, queue_cur, in_buf); //调用trim_case对本次case进行修剪,返回res

    if (res == FAULT_ERROR)//如果res结果为FAULT_ERROR,抛异常
      FATAL("Unable to execute target application");

    if (stop_soon) { //如果设置了stop_soon
      cur_skipped_paths++;//cur_skipped_paths计数+1
      goto abandon_entry; //跳转到abandon_entry
    }

    /* Don't retry trimming, even if it failed. */

    queue_cur->trim_done = 1;//设置queue_cur->trim_done为1,标记已被修剪

    if (len != queue_cur->len) len = queue_cur->len; //如果修剪前case大小不等于修剪后长度//更新为修剪后长度

  }

  memcpy(out_buf, in_buf, len); //将in_buf拷贝len个字节到out_buf中

  /*********************
   * PERFORMANCE SCORE *
   *********************/

  orig_perf = perf_score = calculate_score(queue_cur);//调用calculate_score函数计算当前case的可取性得分

  /* Skip right away if -d is given, if we have done deterministic fuzzing on
     this entry ourselves (was_fuzzed), or if it has gone through deterministic
     testing in earlier, resumed runs (passed_det) 如果给出了-d,如果我们自己对这个条目进行了确定性模糊化(was_fuzzed),或者如果它在之前已经进行了确定性测试,则立即跳过,继续运行(passed_det). */

  if (skip_deterministic || queue_cur->was_fuzzed || queue_cur->passed_det)
    goto havoc_stage;//如果已经设置了skip_deterministic,或者case已经被fuzz过,或者queue_cur->passed_det==1,跳转至havoc_stage

  /* Skip deterministic fuzzing if exec path checksum puts this out of scope
     for this master instance. */

  if (master_max && (queue_cur->exec_cksum % master_max) != master_id - 1)//如果执行路径校验和将其置于此主实例的范围之外,则跳转至havoc_stage
    goto havoc_stage;

  doing_det = 1;

  /*********************************************
   * SIMPLE BITFLIP (+dictionary construction) *变异的主要体现
   *********************************************/

#define FLIP_BIT(_ar, _b) do { \ //可以看到整个宏的功能就是对case中的内容进行按位取反,
    u8* _arf = (u8*)(_ar); \
    u32 _bf = (_b); \
    _arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \
  } while (0)

  /* Single walking bit. */

  stage_short = "flip1";
  stage_max   = len << 3; //定义stage_max为len<<3。这里根据len长度判断
  stage_name  = "bitflip 1/1";

  stage_val_type = STAGE_VAL_NONE;

  orig_hit_cnt = queued_paths + unique_crashes;

  prev_cksum = queue_cur->exec_cksum;

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {//循环给FLIP_BIT提供二参

    stage_cur_byte = stage_cur >> 3;

    FLIP_BIT(out_buf, stage_cur);//调用FLIP_BIT将out_buf中的内容按位取反

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//调用common_fuzz_stuff进行fuzz,保存interesting种子 //如果返回为1,跳转到abandon_entry

    FLIP_BIT(out_buf, stage_cur); //翻转回来                                         

    /* While flipping the least significant bit in every byte, pull of an extra
       trick to detect possible syntax tokens. In essence, the idea is that if
       you have a binary blob like this:

       xxxxxxxxIHDRxxxxxxxx

       ...and changing the leading and trailing bytes causes variable or no
       changes in program flow, but touching any character in the "IHDR" string
       always produces the same, distinctive path, it's highly likely that
       "IHDR" is an atomically-checked magic value of special significance to
       the fuzzed format.

       We do this here, rather than as a separate stage, because it's a nice
       way to keep the operation approximately "free" (i.e., no extra execs).
       
       Empirically, performing the check when flipping the least significant bit
       is advantageous, compared to doing it at the time of more disruptive
       changes, where the program flow may be affected in more violent ways.

       The caveat is that we won't generate dictionaries in the -d mode or -S
       mode - but that's probably a fair trade-off.

       This won't work particularly well with paths that exhibit variable
       behavior, but fails gracefully, so we'll carry out the checks anyway.

      在经过bitflip 1/1变异时,会对每一个byte的最低有效位取反后进行识别处理,如果连续 多个bytes的最低位被取反后程序的路径都没有变化,
	但是与原始路径不一致,就会把这一段 连续的bytes判断是一条token(分词器选中词条)

    举例,如果输入数据为“select * from table”时,原始路径应该是执行SQL语句流程路径,第一次取反的应该是“s”,但是在变异后“felect”并不符合语法,
    那么在执行SQL语句时流程路径就会走向报错,与原始路径不同,但是“s”会取反8次,每一次的结果“kelect”、“oekect”....都不符合SQL语句语法,
    因此执行路径就会和“felect”相同。也就是说破坏“select”被破坏将会与正确路径不一致,而破坏之后的路径都一样,那么AFL就会将“select”作为token记录下来
        */

    if (!dumb_mode && (stage_cur & 7) == 7) {//如果不是dumb_mode模式并且(stage_cur & 7) == 7,这意味着只有stage_cur==7、15、23...,即当翻转到每个字节最低有效位的时候进入分支

      u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//对当前trace_bits户进行hash32运算,值记录在cksum中

      if (stage_cur == stage_max - 1 && cksum == prev_cksum) { //如果循环到stage_max - 1次,即内容结尾,并且本次校验和与上一次相同,即当前路径与上一次路径相比没有变化

        /* If at end of file and we are still collecting a string, grab the
           final character and force output. */

        if (a_len < MAX_AUTO_EXTRA)//如果当前token数量小于32(MAX_AUTO_EXTRA默认为32)
            a_collect[a_len] = out_buf[stage_cur >> 3];//将当前字符作为token拼接到a_collect[]数组中
        a_len++;//token数量+1

        if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA) //如果token数量大于等于3(MIN_AUTO_EXTRA默认为3)且小于等于32(MAX_AUTO_EXTRA默认为32)
          maybe_add_auto(a_collect, a_len);//调用maybe_add_auto将累计的a_collect[]数组中的内容添加到a_extras[]数组中

      } else if (cksum != prev_cksum) { //如果当前校验和与上一次校验和不一样,说明与上一次执行路径不一样,那么本次编译的字节是存在问题的

        /* Otherwise, if the checksum has changed, see if we have something
           worthwhile queued up, and collect that if the answer is yes. */

        if (a_len >= MIN_AUTO_EXTRA && a_len <= MAX_AUTO_EXTRA)//如果token数量大于等于3(MIN_AUTO_EXTRA默认为3)且小于等于32(MAX_AUTO_EXTRA默认为32)
          maybe_add_auto(a_collect, a_len);//调用maybe_add_auto将a_collect中的token添加到a_extras数组中

        a_len = 0;//a_len清空
        prev_cksum = cksum;//刷新prev_cksum为当前cksum

      }

      /* Continue collecting string, but only if the bit flip actually made
         any difference - we don't want no-op tokens. */

      if (cksum != queue_cur->exec_cksum) {//如果当前路径与原始路径不相同,这说明可能是因为token被破坏导致与原始执行路径不符

        if (a_len < MAX_AUTO_EXTRA) a_collect[a_len] = out_buf[stage_cur >> 3];    //如果token数量小于32//将当前循环字符添加到a_collect数组中    
        a_len++;//将当前循环字符添加到a_collect数组中

      }

    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP1]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_FLIP1]累加整个FLIP_BIT过程中新发现的路径和crash总和
  stage_cycles[STAGE_FLIP1] += stage_max; //stage_cycles[STAGE_FLIP1]累加整个FLIP_BIT过程中变异的次数

  /* Two walking bits. */

  stage_name  = "bitflip 2/1"; // bitflip 2 / 1相邻两位取反变异策略
  stage_short = "flip2";
  stage_max   = (len << 3) - 1;

  orig_hit_cnt = new_hit_cnt;

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

    stage_cur_byte = stage_cur >> 3;

    FLIP_BIT(out_buf, stage_cur);//第一次按位取反
    FLIP_BIT(out_buf, stage_cur + 1);//第二次按位取反,在stage_cur+1,说明这两次取反会将out_buf中相邻两位数据进行取反

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//调用common_fuzz_stuff对变异后数据进行测试,记录interesting,如果返回1跳转到abandon_entry

    FLIP_BIT(out_buf, stage_cur);//第一次位恢复
    FLIP_BIT(out_buf, stage_cur + 1);//相邻位恢复

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP2]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_FLIP2]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_FLIP2] += stage_max;//stage_cycles[STAGE_FLIP2]累加bitflip 2/1变异次数

  /* Four walking bits. */

  stage_name  = "bitflip 4/1";//bitflip 4/1相邻四位取反变异策略
  stage_short = "flip4";
  stage_max   = (len << 3) - 3;

  orig_hit_cnt = new_hit_cnt;

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {

    stage_cur_byte = stage_cur >> 3;

    FLIP_BIT(out_buf, stage_cur); //第一位取反
    FLIP_BIT(out_buf, stage_cur + 1);//相邻第二位取反
    FLIP_BIT(out_buf, stage_cur + 2);//相邻第二位取反
    FLIP_BIT(out_buf, stage_cur + 3);//相邻第四位取反

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//调用common_fuzz_stuff对变异后数据进行测试,记录interesting,如果返回1跳转到abandon_entry

    FLIP_BIT(out_buf, stage_cur);//第一位取反恢复
    FLIP_BIT(out_buf, stage_cur + 1); //相邻第二位取反恢复
    FLIP_BIT(out_buf, stage_cur + 2);//相邻第三位取反恢复
    FLIP_BIT(out_buf, stage_cur + 3);//相邻第四位取反恢复

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP4]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_FLIP4]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_FLIP4] += stage_max;//stage_cycles[STAGE_FLIP4]累加bitflip 4/1变异次数

  /* Effector map setup. These macros calculate:

     EFF_APOS      - position of a particular file offset in the map.
     EFF_ALEN      - length of a map with a particular number of bytes.
     EFF_SPAN_ALEN - map span for a sequence of bytes.

   */
  //bitflip 8/8 && effector map byte取反 
#define EFF_APOS(_p)          ((_p) >> EFF_MAP_SCALE2)//EFF_APOS(p){ p >> 3 }
#define EFF_REM(_x)           ((_x) & ((1 << EFF_MAP_SCALE2) - 1))//EFF_REM(x){ x & ((1<<3)-1) }
#define EFF_ALEN(_l)          (EFF_APOS(_l) + !!EFF_REM(_l)) //EFF_ALEN(l){ (l>>3) + ((l&((1<<3)-1))==0)?0:1 }
#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) //EFF_SPAN_ALEN(p, l){ (p+l-1) >> 3 - (p>>3) +1 }

  /* Initialize effector map for the next step (see comments below). Always
     flag first and last byte as doing something. */

  eff_map    = ck_alloc(EFF_ALEN(len));//为eff_map开辟case字节长度空间
  eff_map[0] = 1;//设置eff_map[0]为1

  if (EFF_APOS(len - 1) != 0) {//如果(len-1)>>3不等于0,即case数据长度大于等于9
    eff_map[EFF_APOS(len - 1)] = 1;//设置eff_map[(len-1)>>3]为1
    eff_cnt++;
  }

  /* Walking byte. */

  stage_name  = "bitflip 8/8";//bitflip 8/8 字节翻转变异策略
  stage_short = "flip8";
  stage_max   = len;

  orig_hit_cnt = new_hit_cnt;

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) { //注意这里的stage_max直接等于len

    stage_cur_byte = stage_cur;

    out_buf[stage_cur] ^= 0xFF;//直接对out_buf中的每一个字节中的每一个bit做亦或翻转0xFF(十六进制)=11111111(二进制)

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//调用common_fuzz_stuff对变异后数据进行测试,记录interesting,如果返回1跳转到abandon_entry

    /* We also use this stage to pull off a simple trick: we identify
       bytes that seem to have no effect on the current execution path
       even when fully flipped - and we skip them during more expensive
       deterministic stages, such as arithmetics or known ints. */

    if (!eff_map[EFF_APOS(stage_cur)]) { //如果eff_map[stage_cur>>3]为0

      u32 cksum;

      /* If in dumb mode or if the file is very short, just flag everything
         without wasting time on checksums. */

      if (!dumb_mode && len >= EFF_MIN_LEN)//如果不是dumb_mode模式并且len大于等于128(EFF_MIN_LEN默认128)
        cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);//计算当前校验和
      else//否则如果是dumb_mode模式或len小于128
        cksum = ~queue_cur->exec_cksum;//cksum等于queue_cur->exec_cksum按位取反结果

      if (cksum != queue_cur->exec_cksum) {
        eff_map[EFF_APOS(stage_cur)] = 1;//产生新的路径,发生了变化,此时直接将对应的eff_map中的项标记为1
        eff_cnt++;//标记次数+1
      }

    }

    out_buf[stage_cur] ^= 0xFF;//取反复位

  }

  /* If the effector map is more than EFF_MAX_PERC dense, just flag the
     whole thing as worth fuzzing, since we wouldn't be saving much time
     anyway. */

  if (eff_cnt != EFF_ALEN(len) &&
      eff_cnt * 100 / EFF_ALEN(len) > EFF_MAX_PERC) {//如果eff_map的密度超过EFF_MAX_PERC(默认90)

    memset(eff_map, 1, EFF_ALEN(len)); // 将整个eff_map都标记为1

    blocks_eff_select += EFF_ALEN(len); //blocks_eff_select累加更新EFF_ALEN(len)

  } else {

    blocks_eff_select += eff_cnt;

  }

  blocks_eff_total += EFF_ALEN(len);

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP8]  += new_hit_cnt - orig_hit_cnt; //stage_finds[STAGE_FLIP8]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_FLIP8] += stage_max;//stage_cycles[STAGE_FLIP8]累加bitflip 8/1变异次数

  /* Two walking bytes. */

  if (len < 2) goto skip_bitflip;//如果len<2//跳转到skip_bitflip

  stage_name  = "bitflip 16/8";//连续两字节翻转
  stage_short = "flip16";
  stage_cur   = 0;
  stage_max   = len - 1;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 1; i++) {

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {//检查eff_map对应连续两个字节是否为0
      stage_max--;//检查eff_map对应连续两个字节是否为0
      continue;
    }

    stage_cur_byte = i;

    *(u16*)(out_buf + i) ^= 0xFFFF;//连续两字节翻转

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//调用common_fuzz_stuff测试记录//如果返回值为1,跳转到abandon_entry
    stage_cur++;//stage_cur计数+1

    *(u16*)(out_buf + i) ^= 0xFFFF;//stage_cur计数+1


  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP16]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_FLIP16]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_FLIP16] += stage_max;//stage_cycles[STAGE_FLIP16]累加bitflip 16/8变异次数

  if (len < 4) goto skip_bitflip;

  /* Four walking bytes. */

  stage_name  = "bitflip 32/8";//连续四字节翻转
  stage_short = "flip32";
  stage_cur   = 0;
  stage_max   = len - 3;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 3; i++) {

    /* Let's consult the effector map... */
    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {//判断eff_map连续四项是否为空
      stage_max--;//--跳过
      continue;
    }

    stage_cur_byte = i;

    *(u32*)(out_buf + i) ^= 0xFFFFFFFF;//连续四字节翻转

    if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录//如果返回值为1,跳转至abandon_entry //stage_cur计数+1
    stage_cur++;

    *(u32*)(out_buf + i) ^= 0xFFFFFFFF;//翻转复位

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_FLIP32]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_FLIP32]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_FLIP32] += stage_max;//stage_finds[STAGE_FLIP32]累加出现的新路径与crash次数的和

skip_bitflip:

  if (no_arith) goto skip_arith;

  /**********************
   * ARITHMETIC INC/DEC *
   **********************/

  /* 8-bit arithmetics. */

  stage_name  = "arith 8/8";//arith 8/8变异策略,对每个byte(8 bit)进行加减运算
  stage_short = "arith8";
  stage_cur   = 0;
  stage_max   = 2 * len * ARITH_MAX;

  stage_val_type = STAGE_VAL_LE;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len; i++) {//遍历out_buf

    u8 orig = out_buf[i];//orig为一个字节

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)]) {//判断该字节对应eff_map中是否有效
      stage_max -= 2 * ARITH_MAX;//如果无效,stage_max减去2倍的ARITH_MAX,跳过本次变异
      continue;
    }

    stage_cur_byte = i;

    for (j = 1; j <= ARITH_MAX; j++) {//ARITH_MAX上限是35

      u8 r = orig ^ (orig + j);//orig与orig+j做亦或

      /* Do arithmetic operations only if the result couldn't be a product
         of a bitflip. */

      if (!could_be_bitflip(r)) { //判断是否可以通过上一种bitfilp变异方式来获得本次变异结果,如果不能进入分支

        stage_cur_val = j;
        out_buf[i] = orig + j;//按字节做加法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录//如果返回为1则跳转到abandon_entry
        stage_cur++; //变异次数计数器+1

      } else stage_max--;//如果能够通过bitfilp获得,则跳过这个byte

      r =  orig ^ (orig - j);//orig与orig - j做亦或

      if (!could_be_bitflip(r)) {//判断是否可以通过上一种bitfilp变异方式来获得本次变异结果,如果不能进入分支

        stage_cur_val = -j;
        out_buf[i] = orig - j; //按字节做减法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; //测试,记录//如果返回为1则跳转到abandon_entry
        stage_cur++;//变异次数计数器+1

      } else stage_max--; // 如果能够通过bitfilp获得,则跳过这个byte

      out_buf[i] = orig; //复位

    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_ARITH8]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_ARITH8]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_ARITH8] += stage_max;//stage_cycles[STAGE_ARITH8]累加arith 8/8变异次数

  /* 16-bit arithmetics, both endians. */

  if (len < 2) goto skip_arith;

  stage_name  = "arith 16/8";//arith 16/8变异策略,对每个双字(16 bit)进行加减运算
  stage_short = "arith16";
  stage_cur   = 0;
  stage_max   = 4 * (len - 1) * ARITH_MAX;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 1; i++) {

    u16 orig = *(u16*)(out_buf + i);

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {//检验eff_map中本次循环中对应的双字是否有效
      stage_max -= 4 * ARITH_MAX;//如果无效则跳过
      continue;
    }

    stage_cur_byte = i;

    for (j = 1; j <= ARITH_MAX; j++) {

      u16 r1 = orig ^ (orig + j), //orig与orig+j做亦或(小端)
          r2 = orig ^ (orig - j), //orig与orig-j做亦或(小端)
          r3 = orig ^ SWAP16(SWAP16(orig) + j), //orig与orig+j做亦或(大端)
          r4 = orig ^ SWAP16(SWAP16(orig) - j); //orig与orig-j做亦或(大端)

      /* Try little endian addition and subtraction first. Do it only
         if the operation would affect more than one byte (hence the 
         & 0xff overflow checks) and if it couldn't be a product of
         a bitflip. */

      stage_val_type = STAGE_VAL_LE; 

      if ((orig & 0xff) + j > 0xff && !could_be_bitflip(r1)) {//如果r1不能通过bitflip得到且(orig & 0xff) + j > 0xff

        stage_cur_val = j;
        *(u16*)(out_buf + i) = orig + j;//做加法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录//如果返回值为1,则跳转至abandon_entry //变异次数计数器+1
        stage_cur++;
 
      } else stage_max--;//跳过

      if ((orig & 0xff) < j && !could_be_bitflip(r2)) {//如果r2不能通过bitflip得到且(orig & 0xff) < j

        stage_cur_val = -j;
        *(u16*)(out_buf + i) = orig - j;//做减法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录//如果返回值为1,则跳转至abandon_entry
        stage_cur++;//变异次数计数器+1

      } else stage_max--; //跳过

      /* Big endian comes next. Same deal. */

      stage_val_type = STAGE_VAL_BE;


      if ((orig >> 8) + j > 0xff && !could_be_bitflip(r3)) { //如果r3不能通过bitflip得到且(orig >> 8) + j > 0xff

        stage_cur_val = j;
        *(u16*)(out_buf + i) = SWAP16(SWAP16(orig) + j);//大端做加法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录然后计数器加1
        stage_cur++;

      } else stage_max--;//跳过

      if ((orig >> 8) < j && !could_be_bitflip(r4)) {//如果r4不能通过bitflip得到且(orig >> 8) < j

        stage_cur_val = -j;
        *(u16*)(out_buf + i) = SWAP16(SWAP16(orig) - j); //大端做减法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录然后计数器加1
        stage_cur++; //变异次数计数器+1

      } else stage_max--;//跳过

      *(u16*)(out_buf + i) = orig;//跳过

    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_ARITH16]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_ARITH16]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_ARITH16] += stage_max;//stage_cycles[STAGE_ARITH16]累加arith 16/8变异次数

  /* 32-bit arithmetics, both endians. */

  if (len < 4) goto skip_arith;

  stage_name  = "arith 32/8";//arith 32/8变异策略,对每个四字(32 bit)进行加减运算
  stage_short = "arith32";
  stage_cur   = 0;
  stage_max   = 4 * (len - 3) * ARITH_MAX;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 3; i++) {

    u32 orig = *(u32*)(out_buf + i);

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) {//检验本次循环中对应的四字在eff_map中是否有效
      stage_max -= 4 * ARITH_MAX;//检验本次循环中对应的四字在eff_map中是否有效
      continue;
    }

    stage_cur_byte = i;

    for (j = 1; j <= ARITH_MAX; j++) {

      u32 r1 = orig ^ (orig + j),//orig与orig+j做亦或(小端)
          r2 = orig ^ (orig - j),//orig与orig-j做亦或(小端)
          r3 = orig ^ SWAP32(SWAP32(orig) + j),//orig与orig-j做亦或(小端)
          r4 = orig ^ SWAP32(SWAP32(orig) - j); //orig与orig-j做亦或(大端)

      /* Little endian first. Same deal as with 16-bit: we only want to
         try if the operation would have effect on more than two bytes. */

      stage_val_type = STAGE_VAL_LE;

      if ((orig & 0xffff) + j > 0xffff && !could_be_bitflip(r1)) {//如果r1不能由bitflip得到且(orig & 0xffff) + j > 0xffff

        stage_cur_val = j;
        *(u32*)(out_buf + i) = orig + j;//做加法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录然后计数器加1
        stage_cur++;

      } else stage_max--;//跳过

      if ((orig & 0xffff) < j && !could_be_bitflip(r2)) {//如果r2不能由bitflip得到且(orig & 0xffff) < j

        stage_cur_val = -j;
        *(u32*)(out_buf + i) = orig - j; //做减法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录然后计数器加1
        stage_cur++;

      } else stage_max--;//跳过

      /* Big endian next. */

      stage_val_type = STAGE_VAL_BE;

      if ((SWAP32(orig) & 0xffff) + j > 0xffff && !could_be_bitflip(r3)) {//如果r3不能由bitflip得到且(SWAP32(orig) & 0xffff) + j > 0xffff

        stage_cur_val = j;
        *(u32*)(out_buf + i) = SWAP32(SWAP32(orig) + j);//大端做加法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试记录然后计数器加1
        stage_cur++;

      } else stage_max--;

      if ((SWAP32(orig) & 0xffff) < j && !could_be_bitflip(r4)) {//如果r4不能由bitflip得到且(SWAP32(orig) & 0xffff) < j

        stage_cur_val = -j;
        *(u32*)(out_buf + i) = SWAP32(SWAP32(orig) - j);//大端做减法

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
        stage_cur++;

      } else stage_max--;

      *(u32*)(out_buf + i) = orig;//大端做加法


    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_ARITH32]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_ARITH32]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_ARITH32] += stage_max;//stage_cycles[STAGE_ARITH32]累加arith 32/8变异次数

skip_arith:

  /**********************
   * INTERESTING VALUES *
   **********************/

  stage_name  = "interest 8/8";//interest 8/8 按字节替换interest数据变异策略
  stage_short = "int8";
  stage_cur   = 0;
  stage_max   = len * sizeof(interesting_8);

  stage_val_type = STAGE_VAL_LE;

  orig_hit_cnt = new_hit_cnt;

  /* Setting 8-bit integers. */

  for (i = 0; i < len; i++) {//遍历out_buf

    u8 orig = out_buf[i];

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)]) {//判断本次循环中对应在eff_map中的项是否为有效的
      stage_max -= sizeof(interesting_8);//如果无效则跳过本次循环
      continue;
    }

    stage_cur_byte = i;

    for (j = 0; j < sizeof(interesting_8); j++) {//遍历interesting_8数组中的数据

      /* Skip if the value could be a product of bitflips or arithmetics. */

      if (could_be_bitflip(orig ^ (u8)interesting_8[j]) ||
          could_be_arith(orig, (u8)interesting_8[j], 1)) {//如果orig ^ (u8)interesting_8[j]的结果可以通过bitflip或arith阶段获得
        stage_max--;//跳过本次循环
        continue;
      }

      stage_cur_val = interesting_8[j];
      out_buf[i] = interesting_8[j]; //将循环指定的字节替换成interesting_8中的数据

      if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录

      out_buf[i] = orig; //复位
      stage_cur++;//变异次数计数器+1

    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_INTEREST8]  += new_hit_cnt - orig_hit_cnt;; //stage_finds[STAGE_INTEREST8]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_INTEREST8] += stage_max;//stage_cycles[STAGE_INTEREST8]累加interest 8/8变异次数

  /* Setting 16-bit integers, both endians. */

  if (no_arith || len < 2) goto skip_interest;

  stage_name  = "interest 16/8";//interest 16/8 按双字节替换interest数据变异策略
  stage_short = "int16";
  stage_cur   = 0;
  stage_max   = 2 * (len - 1) * (sizeof(interesting_16) >> 1);

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 1; i++) { //遍历out_buf

    u16 orig = *(u16*)(out_buf + i);

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)]) {//判断本次循环中相邻的双字对应在eff_map中的项是否为有效的
      stage_max -= sizeof(interesting_16); //跳过本次循环
      continue;
    }

    stage_cur_byte = i;

    for (j = 0; j < sizeof(interesting_16) / 2; j++) { //遍历interesting_16,这里除以2是因为interesting_16中包含两组数据

      stage_cur_val = interesting_16[j];

      /* Skip if this could be a product of a bitflip, arithmetics,
         or single-byte interesting value insertion. */

      if (!could_be_bitflip(orig ^ (u16)interesting_16[j]) &&
          !could_be_arith(orig, (u16)interesting_16[j], 2) &&
          !could_be_interest(orig, (u16)interesting_16[j], 2, 0)) {//判断orig ^ (u16)interesting_16[j]是否可以通过bitflip阶段、arith阶段、interest第一阶段可以获得

        stage_val_type = STAGE_VAL_LE;

        *(u16*)(out_buf + i) = interesting_16[j];//如果都不能,则进行替换本次循环中out_buf的双字

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; //测试,记录
        stage_cur++;//变异次数计数器+1

      } else stage_max--; //如果可以从上述阶段获得,则跳过

      if ((u16)interesting_16[j] != SWAP16(interesting_16[j]) &&
          !could_be_bitflip(orig ^ SWAP16(interesting_16[j])) &&
          !could_be_arith(orig, SWAP16(interesting_16[j]), 2) &&
          !could_be_interest(orig, SWAP16(interesting_16[j]), 2, 1)) {//判断orig ^ SWAP16(interesting_16[j])是否可以通过bitflip阶段、arith阶段、interest第一阶段可以获得(大端)

        stage_val_type = STAGE_VAL_BE;

        *(u16*)(out_buf + i) = SWAP16(interesting_16[j]); //替换(大端)
        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;
        stage_cur++;//变异次数计数器+1

      } else stage_max--;

    }

    *(u16*)(out_buf + i) = orig;//复位

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_INTEREST16]  += new_hit_cnt - orig_hit_cnt; //stage_finds[STAGE_INTEREST16]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_INTEREST16] += stage_max;//stage_cycles[STAGE_INTEREST8]累加interest 16/8变异次数

  if (len < 4) goto skip_interest;

  /* Setting 32-bit integers, both endians. */

  stage_name  = "interest 32/8";//interest 32/8 按四字节替换interest数据变异策略
  stage_short = "int32";
  stage_cur   = 0;
  stage_max   = 2 * (len - 3) * (sizeof(interesting_32) >> 2);

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len - 3; i++) {//遍历out_buf

    u32 orig = *(u32*)(out_buf + i);

    /* Let's consult the effector map... */

    if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] &&
        !eff_map[EFF_APOS(i + 2)] && !eff_map[EFF_APOS(i + 3)]) { //判断本次循环中相邻的四字对应在eff_map中的项是否为有效的
      stage_max -= sizeof(interesting_32) >> 1;//跳过本次循环
      continue;
    }

    stage_cur_byte = i;

    for (j = 0; j < sizeof(interesting_32) / 4; j++) {//遍历interesting_32

      stage_cur_val = interesting_32[j];

      /* Skip if this could be a product of a bitflip, arithmetics,
         or word interesting value insertion. */

      if (!could_be_bitflip(orig ^ (u32)interesting_32[j]) &&
          !could_be_arith(orig, interesting_32[j], 4) &&
          !could_be_interest(orig, interesting_32[j], 4, 0)) { //判断orig ^ (u32)interesting_32[j]是否可以通过bitflip阶段、arith阶段、interest第一阶段可以获得

        stage_val_type = STAGE_VAL_LE;

        *(u32*)(out_buf + i) = interesting_32[j];//替换四字

        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录
        stage_cur++;//测试计数器+1

      } else stage_max--;//测试计数器+1

      if ((u32)interesting_32[j] != SWAP32(interesting_32[j]) &&
          !could_be_bitflip(orig ^ SWAP32(interesting_32[j])) &&
          !could_be_arith(orig, SWAP32(interesting_32[j]), 4) &&
          !could_be_interest(orig, SWAP32(interesting_32[j]), 4, 1)) { //判断orig ^ SWAP32(interesting_32[j])是否可以通过bitflip阶段、arith阶段、interest第一阶段可以获得(大端)

        stage_val_type = STAGE_VAL_BE;

        *(u32*)(out_buf + i) = SWAP32(interesting_32[j]);//替换四字(大端)
        if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; //测试,记录
        stage_cur++;

      } else stage_max--;

    }

    *(u32*)(out_buf + i) = orig; //复位

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_INTEREST32]  += new_hit_cnt - orig_hit_cnt; //stage_finds[STAGE_INTEREST32]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_INTEREST32] += stage_max; //stage_cycles[STAGE_INTEREST32]累加interest 32/8变异次数

skip_interest:

  /********************
   * DICTIONARY STUFF *本阶段主要基于用户提供的extra token来进行一定的变异,主要为替换或者插入
   ********************/

  if (!extras_cnt) goto skip_user_extras;//如果用户没有通过-x选项指定token,直接跳转至skip_user_extras

  /* Overwrite with user-supplied extras. */

  stage_name  = "user extras (over)";//user extras (over) 以用户指定的token替换out_buf策略
  stage_short = "ext_UO";
  stage_cur   = 0;
  stage_max   = extras_cnt * len;

  stage_val_type = STAGE_VAL_NONE;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len; i++) {//遍历extras token

    u32 last_len = 0;

    stage_cur_byte = i;

    /* Extras are sorted by size, from smallest to largest. This means
       that we don't have to worry about restoring the buffer in
       between writes at a particular offset determined by the outer
       loop. */

    for (j = 0; j < extras_cnt; j++) {

      /* Skip extras probabilistically if extras_cnt > MAX_DET_EXTRAS. Also
         skip them if there's no room to insert the payload, if the token
         is redundant, or if its entire span has no bytes set in the effector
         map. */

      if ((extras_cnt > MAX_DET_EXTRAS && UR(extras_cnt) >= MAX_DET_EXTRAS) ||
          extras[j].len > len - i ||
          !memcmp(extras[j].data, out_buf + i, extras[j].len) ||
          !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, extras[j].len))) {//如果extras token的大小不满足上述条件(UR函数会加入一些随机性)

        stage_max--; //跳过本次循环
        continue;

      }

      last_len = extras[j].len;
      memcpy(out_buf + i, extras[j].data, last_len); //利用memcpy函数将当前指定的extras token覆写进out_buf中

      if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry; //测试,记录

      stage_cur++;//变异次数计数器+1

    }

    /* Restore all the clobbered memory. */
    memcpy(out_buf + i, in_buf + i, last_len); //从in_buf中取值恢复out_buf

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_EXTRAS_UO]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_EXTRAS_UO]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_EXTRAS_UO] += stage_max;//stage_cycles[STAGE_EXTRAS_UO]累加user extras (over)变异次数

  /* Insertion of user-supplied extras. */

  stage_name  = "user extras (insert)"; //user extras (insert) 以用户token插入out_buf策略
  stage_short = "ext_UI";
  stage_cur   = 0;
  stage_max   = extras_cnt * len;

  orig_hit_cnt = new_hit_cnt;

  ex_tmp = ck_alloc(len + MAX_DICT_FILE); //user extras (insert) 以用户token插入out_buf策略

  for (i = 0; i <= len; i++) {

    stage_cur_byte = i;

    for (j = 0; j < extras_cnt; j++) { //遍历extras token

      if (len + extras[j].len > MAX_FILE) {//如果out_buf长度+token长度大于MAX_FILE(默认1024*1024)
        stage_max--; //跳出本次循环
        continue;
      }

      /* Insert token */
      memcpy(ex_tmp + i, extras[j].data, extras[j].len);//利用memcpy在ex_tmp对应位置插入token

      /* Copy tail */
      memcpy(ex_tmp + i + extras[j].len, out_buf + i, len - i);//在对应位置追加out_buf对应后续的内容

      if (common_fuzz_stuff(argv, ex_tmp, len + extras[j].len)) {//测试,记录
        ck_free(ex_tmp);//如果返回值为1,释放ex_tmp空间
        goto abandon_entry;//跳转至abandon_entry
      }

      stage_cur++;//跳转至abandon_entry

    }

    /* Copy head */
    ex_tmp[i] = out_buf[i];//将out_buf[i]对应的前置内容赋值给ex_tmp[i],因为在插入阶段插入的位置是ex_tmp + i,后续补上out_buf + 1的内容

  }

  ck_free(ex_tmp);//释放ex_tmp空间

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_EXTRAS_UI]  += new_hit_cnt - orig_hit_cnt; //stage_finds[STAGE_EXTRAS_UI]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_EXTRAS_UI] += stage_max; //stage_cycles[STAGE_EXTRAS_UI]累加user extras (insert)变异次数

skip_user_extras:

  if (!a_extras_cnt) goto skip_extras;

  stage_name  = "auto extras (over)"; //auto extras (over)替换阶段2,这里替换的不是extras token,而是a_extras token
  stage_short = "ext_AO";
  stage_cur   = 0;
  stage_max   = MIN(a_extras_cnt, USE_AUTO_EXTRAS) * len;

  stage_val_type = STAGE_VAL_NONE;

  orig_hit_cnt = new_hit_cnt;

  for (i = 0; i < len; i++) {

    u32 last_len = 0;

    stage_cur_byte = i;

    for (j = 0; j < MIN(a_extras_cnt, USE_AUTO_EXTRAS); j++) {//遍历a_extras

      /* See the comment in the earlier code; extras are sorted by size. */

      if (a_extras[j].len > len - i ||
          !memcmp(a_extras[j].data, out_buf + i, a_extras[j].len) ||
          !memchr(eff_map + EFF_APOS(i), 1, EFF_SPAN_ALEN(i, a_extras[j].len))) {//如果a_extras大小不满足上述条件

        stage_max--;//跳出本次循环
        continue;

      }

      last_len = a_extras[j].len;
      memcpy(out_buf + i, a_extras[j].data, last_len);//调用memcpy函数将out_buf位置替换成a_extras token

      if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;//测试,记录

      stage_cur++;//变异次数计数器+1

    }

    /* Restore all the clobbered memory. */
    memcpy(out_buf + i, in_buf + i, last_len); //从in_buf中取值恢复out_buf

  }

  new_hit_cnt = queued_paths + unique_crashes;

  stage_finds[STAGE_EXTRAS_AO]  += new_hit_cnt - orig_hit_cnt;//stage_finds[STAGE_EXTRAS_AO]累加出现的新路径与crash次数的和
  stage_cycles[STAGE_EXTRAS_AO] += stage_max;//stage_cycles[STAGE_EXTRAS_UI]累加auto extras (over)变异次数

skip_extras:

  /* If we made this to here without jumping to havoc_stage or abandon_entry,
     we're properly done with deterministic steps and can mark it as such
     in the .state/ directory. */

  if (!queue_cur->passed_det) mark_as_det_done(queue_cur);//如果没有设置queue_cur->passed_det//调用mark_as_det_done函数对queue进行标记,如.state/目录

  /****************
   * RANDOM HAVOC * 随机毁灭模式
   ****************/

havoc_stage:

  stage_cur_byte = -1;

  /* The havoc stage mutation code is also invoked when splicing files; if the
     splice_cycle variable is set, generate different descriptions and such. */

  if (!splice_cycle) {//检测如果没有设置splice_cycle


    stage_name  = "havoc";//标记此阶段为"havoc"
    stage_short = "havoc";
    stage_max   = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) *
                  perf_score / havoc_div / 100;

  } else {//否则标记为"splice"

    static u8 tmp[32];

    perf_score = orig_perf;

    sprintf(tmp, "splice %u", splice_cycle);
    stage_name  = tmp;
    stage_short = "splice";
    stage_max   = SPLICE_HAVOC * perf_score / havoc_div / 100;

  }

  if (stage_max < HAVOC_MIN) stage_max = HAVOC_MIN;//设置目前为止的stage_max、orig_hit_cnt、havoc_queued

  temp_len = len;

  orig_hit_cnt = queued_paths + unique_crashes;

  havoc_queued = queued_paths;

  /* We essentially just do several thousand runs (depending on perf_score)
     where we take the input file and make random stacked tweaks. */

  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {//开始循环stage

    u32 use_stacking = 1 << (1 + UR(HAVOC_STACK_POW2));// 产生随机数use_stacking,该随机数决定一次stage中变化的次数

    stage_cur_val = use_stacking;
 
    for (i = 0; i < use_stacking; i++) {//循环use_stacking次

      switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) {//根据是否存在extras token来决定UR()函数参数(17或15),随机产生的值作为变换方式的case选项

        case 0://随机选择字节进行按位取反

          /* Flip a single bit somewhere. Spooky! */

          FLIP_BIT(out_buf, UR(temp_len << 3));
          break;

        case 1: 

          /* Set byte to interesting value. */

          out_buf[UR(temp_len)] = interesting_8[UR(sizeof(interesting_8))];// 随机选中interesting_8[]中的随机byte替换out_buf中的随机byte
          break;

        case 2://随机选择大小端序后,选中interesting_16[]中的随机word替换out_buf中的随机word

          /* Set word to interesting value, randomly choosing endian. */

          if (temp_len < 2) break;

          if (UR(2)) {

            *(u16*)(out_buf + UR(temp_len - 1)) =
              interesting_16[UR(sizeof(interesting_16) >> 1)];

          } else {

            *(u16*)(out_buf + UR(temp_len - 1)) = SWAP16(
              interesting_16[UR(sizeof(interesting_16) >> 1)]);

          }

          break;

        case 3://随机选择大小端序后,选中interesting_32[]中的随机dword替换out_buf中的随机dword

          /* Set dword to interesting value, randomly choosing endian. */

          if (temp_len < 4) break;

          if (UR(2)) {
  
            *(u32*)(out_buf + UR(temp_len - 3)) =
              interesting_32[UR(sizeof(interesting_32) >> 2)];

          } else {

            *(u32*)(out_buf + UR(temp_len - 3)) = SWAP32(
              interesting_32[UR(sizeof(interesting_32) >> 2)]);

          }

          break;

        case 4://选择out_buf[]中的随机byte减随机数值

          /* Randomly subtract from byte. */

          out_buf[UR(temp_len)] -= 1 + UR(ARITH_MAX);
          break;

        case 5://选择out_buf[]中的随机byte加随机数值

          /* Randomly add to byte. */

          out_buf[UR(temp_len)] += 1 + UR(ARITH_MAX);
          break;

        case 6://随机选择大小端序后,选择out_buf[]中的随机word减随机数值

          /* Randomly subtract from word, random endian. */

          if (temp_len < 2) break;

          if (UR(2)) {

            u32 pos = UR(temp_len - 1);

            *(u16*)(out_buf + pos) -= 1 + UR(ARITH_MAX);

          } else {

            u32 pos = UR(temp_len - 1);
            u16 num = 1 + UR(ARITH_MAX);

            *(u16*)(out_buf + pos) =
              SWAP16(SWAP16(*(u16*)(out_buf + pos)) - num);

          }

          break;

        case 7://随机选择大小端序后,选择out_buf[]中的随机word加随机数值

          /* Randomly add to word, random endian. */

          if (temp_len < 2) break;

          if (UR(2)) {

            u32 pos = UR(temp_len - 1);

            *(u16*)(out_buf + pos) += 1 + UR(ARITH_MAX);

          } else {

            u32 pos = UR(temp_len - 1);
            u16 num = 1 + UR(ARITH_MAX);

            *(u16*)(out_buf + pos) =
              SWAP16(SWAP16(*(u16*)(out_buf + pos)) + num);

          }

          break;

        case 8://随机选择大小端序后,选择out_buf[]中的随机dword减随机数值

          /* Randomly subtract from dword, random endian. */

          if (temp_len < 4) break;

          if (UR(2)) {

            u32 pos = UR(temp_len - 3);

            *(u32*)(out_buf + pos) -= 1 + UR(ARITH_MAX);

          } else {

            u32 pos = UR(temp_len - 3);
            u32 num = 1 + UR(ARITH_MAX);

            *(u32*)(out_buf + pos) =
              SWAP32(SWAP32(*(u32*)(out_buf + pos)) - num);

          }

          break;

        case 9://随机选择大小端序后,选择out_buf[]中的随机dword减随机数值

          /* Randomly add to dword, random endian. */

          if (temp_len < 4) break;

          if (UR(2)) {

            u32 pos = UR(temp_len - 3);

            *(u32*)(out_buf + pos) += 1 + UR(ARITH_MAX);

          } else {

            u32 pos = UR(temp_len - 3);
            u32 num = 1 + UR(ARITH_MAX);

            *(u32*)(out_buf + pos) =
              SWAP32(SWAP32(*(u32*)(out_buf + pos)) + num);

          }

          break;

        case 10://选择out_buf[]中的随机byte做异或翻转

          /* Just set a random byte to a random value. Because,
             why not. We use XOR with 1-255 to eliminate the
             possibility of a no-op. */

          out_buf[UR(temp_len)] ^= 1 + UR(255);
          break;

        case 11 ... 12: {//选择out_buf[]中的随机byte进行删除

            /* Delete bytes. We're making this a bit more likely
               than insertion (the next option) in hopes of keeping
               files reasonably small. */

            u32 del_from, del_len;

            if (temp_len < 2) break;

            /* Don't delete too much. */

            del_len = choose_block_len(temp_len - 1);

            del_from = UR(temp_len - del_len + 1);

            memmove(out_buf + del_from, out_buf + del_from + del_len,
                    temp_len - del_from - del_len);

            temp_len -= del_len;

            break;

          }

        case 13://选择out_buf[]中的随机位置插入随机长度的内容(这段内容有75%的概率是out_buf[]中的内容   有25%的概率是一段相同的随机数字)
            //而这串随机数字中这串数字有50%的概率是随机生成的数字有50%的概率从out_buf[]中随机选取一个字节

          if (temp_len + HAVOC_BLK_XL < MAX_FILE) {

            /* Clone bytes (75%) or insert a block of constant bytes (25%). */

            u8  actually_clone = UR(4);
            u32 clone_from, clone_to, clone_len;
            u8* new_buf;

            if (actually_clone) {

              clone_len  = choose_block_len(temp_len);
              clone_from = UR(temp_len - clone_len + 1);

            } else {

              clone_len = choose_block_len(HAVOC_BLK_XL);
              clone_from = 0;

            }

            clone_to   = UR(temp_len);

            new_buf = ck_alloc_nozero(temp_len + clone_len);

            /* Head */

            memcpy(new_buf, out_buf, clone_to);

            /* Inserted part */

            if (actually_clone)
              memcpy(new_buf + clone_to, out_buf + clone_from, clone_len);
            else
              memset(new_buf + clone_to,
                     UR(2) ? UR(256) : out_buf[UR(temp_len)], clone_len);

            /* Tail */
            memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
                   temp_len - clone_to);

            ck_free(out_buf);
            out_buf = new_buf;
            temp_len += clone_len;

          }

          break;

        case 14: {//选择out_buf[]中的随机位置覆盖随机长度的内容

            /* Overwrite bytes with a randomly selected chunk (75%) or fixed
               bytes (25%). */

            u32 copy_from, copy_to, copy_len;

            if (temp_len < 2) break;

            copy_len  = choose_block_len(temp_len - 1);

            copy_from = UR(temp_len - copy_len + 1);
            copy_to   = UR(temp_len - copy_len + 1);

            if (UR(4)) {

              if (copy_from != copy_to)
                memmove(out_buf + copy_to, out_buf + copy_from, copy_len);

            } else memset(out_buf + copy_to,
                          UR(2) ? UR(256) : out_buf[UR(temp_len)], copy_len);

            break;

          }

        /* Values 15 and 16 can be selected only if there are any extras
           present in the dictionaries. */

        case 15: {//随机选取一段内容覆盖成extra token,token来自a_extras[use_extra].data或者extras[use_extra].data

            /* Overwrite bytes with an extra. */

            if (!extras_cnt || (a_extras_cnt && UR(2))) {

              /* No user-specified extras or odds in our favor. Let's use an
                 auto-detected one. */

              u32 use_extra = UR(a_extras_cnt);
              u32 extra_len = a_extras[use_extra].len;
              u32 insert_at;

              if (extra_len > temp_len) break;

              insert_at = UR(temp_len - extra_len + 1);
              memcpy(out_buf + insert_at, a_extras[use_extra].data, extra_len);

            } else {

              /* No auto extras or odds in our favor. Use the dictionary. */

              u32 use_extra = UR(extras_cnt);
              u32 extra_len = extras[use_extra].len;
              u32 insert_at;

              if (extra_len > temp_len) break;

              insert_at = UR(temp_len - extra_len + 1);
              memcpy(out_buf + insert_at, extras[use_extra].data, extra_len);

            }

            break;

          }

        case 16: {//随机选取一段内容插入extra token,token来自a_extras[use_extra].data或者extras[use_extra].data

            u32 use_extra, extra_len, insert_at = UR(temp_len + 1);
            u8* new_buf;

            /* Insert an extra. Do the same dice-rolling stuff as for the
               previous case. */

            if (!extras_cnt || (a_extras_cnt && UR(2))) {

              use_extra = UR(a_extras_cnt);
              extra_len = a_extras[use_extra].len;

              if (temp_len + extra_len >= MAX_FILE) break;

              new_buf = ck_alloc_nozero(temp_len + extra_len);

              /* Head */
              memcpy(new_buf, out_buf, insert_at);

              /* Inserted part */
              memcpy(new_buf + insert_at, a_extras[use_extra].data, extra_len);

            } else {

              use_extra = UR(extras_cnt);
              extra_len = extras[use_extra].len;

              if (temp_len + extra_len >= MAX_FILE) break;

              new_buf = ck_alloc_nozero(temp_len + extra_len);

              /* Head */
              memcpy(new_buf, out_buf, insert_at);

              /* Inserted part */
              memcpy(new_buf + insert_at, extras[use_extra].data, extra_len);

            }

            /* Tail */
            memcpy(new_buf + insert_at + extra_len, out_buf + insert_at,
                   temp_len - insert_at);

            ck_free(out_buf);
            out_buf   = new_buf;
            temp_len += extra_len;

            break;

          }

      }

    }

    if (common_fuzz_stuff(argv, out_buf, temp_len))//循环叠加变换use_stacking次后调用common_fuzz_stuff函数对变换后的结果进行fuzz
      goto abandon_entry;

    /* out_buf might have been mangled a bit, so let's restore it to its
       original size and shape. */

    if (temp_len < len) out_buf = ck_realloc(out_buf, len);
    temp_len = len;
    memcpy(out_buf, in_buf, len);

    /* If we're finding new stuff, let's run for a bit longer, limits
       permitting. */

    if (queued_paths != havoc_queued) {//如果fuzz后queued_paths与havoc_queued不一样了,说明发现了新路径,更新stage_max、perf_score、havoc_queued

      if (perf_score <= HAVOC_MAX_MULT * 100) {
        stage_max  *= 2;
        perf_score *= 2;
      }

      havoc_queued = queued_paths;

    }

  }

  new_hit_cnt = queued_paths + unique_crashes;

  if (!splice_cycle) {//
    stage_finds[STAGE_HAVOC]  += new_hit_cnt - orig_hit_cnt;
    stage_cycles[STAGE_HAVOC] += stage_max;
  } else {
    stage_finds[STAGE_SPLICE]  += new_hit_cnt - orig_hit_cnt;
    stage_cycles[STAGE_SPLICE] += stage_max;
  }

#ifndef IGNORE_FINDS

  /************
   * SPLICING *在经历过RANDOM HAVOC阶段后没有什么效果,那么就会进入到SPLICING阶段,尝试拼接两个测试用例中的内容,拼接之后重新走一遍RANDOM HAVOC阶段
   ************/

  /* This is a last-resort strategy triggered by a full round with no findings.
     It takes the current input file, randomly selects another input, and
     splices them together at some offset, then relies on the havoc
     code to mutate that blob. */

retry_splicing:

  if (use_splicing && splice_cycle++ < SPLICE_CYCLES &&
      queued_paths > 1 && queue_cur->len > 1) {//如果存在外部输入的fuzzer且数量<SPLICE_CYCLES(默认为15)且queue数量>1且长度>1

    struct queue_entry* target;
    u32 tid, split_at;
    u8* new_buf;
    s32 f_diff, l_diff;

    /* First of all, if we've modified in_buf for havoc, let's clean that
       up... */

    if (in_buf != orig_in) {//如果在经历过havoc阶段后修改过in_buf,那么在这里就需要清理一下in_buf,并重新给in_buf赋值成orig_in
      ck_free(in_buf);
      in_buf = orig_in;
      len = queue_cur->len;
    }

    /* Pick a random queue entry and seek to it. Don't splice with yourself. */

    do { tid = UR(queued_paths); } while (tid == current_entry);

    splicing_with = tid;
    target = queue; //随机在queue中挑选一个target

    while (tid >= 100) { target = target->next_100; tid -= 100; }//纠正target取处
    while (tid--) target = target->next;

    /* Make sure that the target has a reasonable length. */

    while (target && (target->len < 2 || target == queue_cur)) { //确保target长度合法
      target = target->next;
      splicing_with++;
    }

    if (!target) goto retry_splicing;

    /* Read the testcase into a new buffer. */

    fd = open(target->fname, O_RDONLY);//以只读方式打开target

    if (fd < 0) PFATAL("Unable to open '%s'", target->fname);//没打开报错

    new_buf = ck_alloc_nozero(target->len);//创建target长度空间

    ck_read(fd, new_buf, target->len, target->fname);//将target中的内容复制到new_buf内存空间里

    close(fd);

    /* Find a suitable splicing location, somewhere between the first and
       the last differing byte. Bail out if the difference is just a single
       byte or so. */

    locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff);  //拼接in_buf和new_buf的辅助函数

    if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) {//如果被拼接文件只有一个字节左右的长度,那么就退出splicing阶段
      ck_free(new_buf);
      goto retry_splicing;
    }

    /* Split somewhere between the first and last differing byte. */

    split_at = f_diff + UR(l_diff - f_diff);//随机在第一个字节和最后一个字节之间选取一个位置

    /* Do the thing. */

    len = target->len;
    memcpy(new_buf, in_buf, split_at);//调用memcpy函数在new_buf的split_at位置拼接in_buf
    in_buf = new_buf;//更新in_buf

    ck_free(out_buf); //释放out_buf
    out_buf = ck_alloc_nozero(len); //重新创建一个len长度的内存空间out_buf
    memcpy(out_buf, in_buf, len); //将拼接后的in_buf拷贝到out_buf中

    goto havoc_stage; //重新执行havoc阶段

  }

#endif /* !IGNORE_FINDS */

  ret_val = 0;

abandon_entry:

  splicing_with = -1;//设置splicing_with为-1

  /* Update pending_not_fuzzed count if we made it through the calibration
     cycle and have not seen this entry before. */

  if (!stop_soon && !queue_cur->cal_failed && !queue_cur->was_fuzzed) {//如果没有中断,且queue没有校准失败,且没有被fuzz过
    queue_cur->was_fuzzed = 1;//标记当前queue已经被fuzz过了
    pending_not_fuzzed--;//pending_not_fuzzed计数器-1
    if (queue_cur->favored) pending_favored--;////如果当前queue是favored那么favored也-1
  }

  munmap(orig_in, queue_cur->len);

  if (in_buf != orig_in) ck_free(in_buf); //清理缓存
  ck_free(out_buf);
  ck_free(eff_map);

  return ret_val; //返回ret_val

#undef FLIP_BIT

}


/* Grab interesting test cases from other fuzzers. */

static void sync_fuzzers(char** argv) { //读取sync文件夹写的queue文件,保存到自己的queue中

  DIR* sd;
  struct dirent* sd_ent;
  u32 sync_cnt = 0;

  sd = opendir(sync_dir);//尝试打开sync_dir目录
  if (!sd) PFATAL("Unable to open '%s'", sync_dir);//打不开就报错

  stage_max = stage_cur = 0;
  cur_depth = 0;

  /* Look at the entries created for every other fuzzer in the sync directory. */

  while ((sd_ent = readdir(sd))) {//循环读取该文件夹下的目录和文件

    static u8 stage_tmp[128];

    DIR* qd;
    struct dirent* qd_ent;
    u8 *qd_path, *qd_synced_path;
    u32 min_accept = 0, next_min_accept;

    s32 id_fd;

    /* Skip dot files and our own output directory. */

    if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue;//跳过“.”文件和sync_id(输出文件夹)

    /* Skip anything that doesn't have a queue/ subdirectory. */

    qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name);//拼接sync_dir/sd_ent->d_name/queue路径

    if (!(qd = opendir(qd_path))) {//尝试打开路径,如果失败则退出本次循环
      ck_free(qd_path);
      continue;
    }

    /* Retrieve the ID of the last seen test case. */

    qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name);  //拼接out_dir/.synced/sd_ent->d_name目录

    id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600);

    if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path);

    if (read(id_fd, &min_accept, sizeof(u32)) > 0)  //从id_fd中读取4个字节到min_accept中
      lseek(id_fd, 0, SEEK_SET); //如果成功读取,调动lseek函数调整文件内指针到开头

    next_min_accept = min_accept;//更新next_min_accept,该变量代表之前从这个文件夹里读取到的最后一个queue的id

    /* Show stats */    

    sprintf(stage_tmp, "sync %u", ++sync_cnt);//sync_cnt计数器+1,"sync %u"格式化到stage_tmp中
    stage_name = stage_tmp;
    stage_cur  = 0;
    stage_max  = 0; //设置stage_cur和stage_max为0

    /* For every file queued by this fuzzer, parse ID and see if we have looked at
       it before; exec a test case if not. */

    while ((qd_ent = readdir(qd))) {//循环读取sync_dir/sd_ent->d_name/queue文件夹中的目录和文件

      u8* path;
      s32 fd;
      struct stat st;

      if (qd_ent->d_name[0] == '.' ||
          sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || 
          syncing_case < min_accept) continue; //如果文件是以“.”开头,或者syncing_case标识小于min_accept文件,因为这些文件已经被sync过了,则跳出本次循环

      /* OK, sounds like a new one. Let's give it a try. */

      if (syncing_case >= next_min_accept)//如果syncing_case标识大于等于next_min_accept
        next_min_accept = syncing_case + 1;//设置next_min_accept为syncing_case + 1,标明next

      path = alloc_printf("%s/%s", qd_path, qd_ent->d_name);//拼接路径qd_path/qd_ent->d_name

      /* Allow this to fail in case the other fuzzer is resuming or so... */

      fd = open(path, O_RDONLY); //打开路径

      if (fd < 0) {//失败释放path然后返回
         ck_free(path);
         continue;
      }

      if (fstat(fd, &st)) PFATAL("fstat() failed");

      /* Ignore zero-sized or oversized files. */

      if (st.st_size && st.st_size <= MAX_FILE) { //如果文件大小不为0,且小于MAX_FILE(默认1024 * 1024)

        u8  fault;
        u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); //将文件中的内容映射进内存中

        if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path);//报错抛异常

        /* See what happens. We rely on save_if_interesting() to catch major
           errors and save the test case. */

        write_to_testcase(mem, st.st_size);//通过write_to_testcase将mem写到out_file中

        fault = run_target(argv, exec_tmout);//运行对应文件,返回值赋值给fault

        if (stop_soon) return;//运行对应文件,返回值赋值给fault

        syncing_party = sd_ent->d_name; //通过save_if_interesting来决定是否要将这个queue导入到自己的queue里,如果发现了新的path,就导入
        queued_imported += save_if_interesting(argv, mem, st.st_size, fault);//如果返回值为1,则queued_imported计数器+1
        syncing_party = 0;

        munmap(mem, st.st_size);

        if (!(stage_cur++ % stats_update_freq)) show_stats(); //stage_cur计数器+1,如果stage_cur是stats_update_freq的倍数,就刷新一次界面

      }

      ck_free(path);
      close(fd);

    }

    ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path); //向当前id_fd中写入当前的next_min_accept值

    close(id_fd);
    closedir(qd);
    ck_free(qd_path);
    ck_free(qd_synced_path);
    
  }  

  closedir(sd);

}


/* Handle stop signal (Ctrl-C, etc). */

static void handle_stop_sig(int sig) {

  stop_soon = 1; 

  if (child_pid > 0) kill(child_pid, SIGKILL);
  if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);

}


/* Handle skip request (SIGUSR1). */

static void handle_skipreq(int sig) {

  skip_requested = 1;

}

/* Handle timeout (SIGALRM). */

static void handle_timeout(int sig) {

  if (child_pid > 0) {

    child_timed_out = 1; 
    kill(child_pid, SIGKILL);

  } else if (child_pid == -1 && forksrv_pid > 0) {

    child_timed_out = 1; 
    kill(forksrv_pid, SIGKILL);

  }

}


/* Do a PATH search and find target binary to see that it exists and
   isn't a shell script - a common and painful mistake. We also check for
   a valid ELF header and for evidence of AFL instrumentation. */

EXP_ST void check_binary(u8* fname) {//检查指定路径要执行的程序是否存在,是否为shell脚本,同时检查elf文件头是否合法及程序是否被插桩。

  u8* env_path = 0;
  struct stat st;

  s32 fd;
  u8* f_data;
  u32 f_len = 0;

  ACTF("Validating target binary...");

  if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { //查找fname/后面的字符串,以及获取环境变量path的值

    target_path = ck_strdup(fname);
    if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
        !(st.st_mode & 0111) || (f_len = st.st_size) < 4)
      FATAL("Program '%s' not found or not executable", fname);

  } else {

    while (env_path) {

      u8 *cur_elem, *delim = strchr(env_path, ':');

      if (delim) {

        cur_elem = ck_alloc(delim - env_path + 1);
        memcpy(cur_elem, env_path, delim - env_path);
        delim++;

      } else cur_elem = ck_strdup(env_path);

      env_path = delim;

      if (cur_elem[0])
        target_path = alloc_printf("%s/%s", cur_elem, fname);
      else
        target_path = ck_strdup(fname);

      ck_free(cur_elem);

      if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
          (st.st_mode & 0111) && (f_len = st.st_size) >= 4) break;

      ck_free(target_path);
      target_path = 0;

    }

    if (!target_path) FATAL("Program '%s' not found or not executable", fname);

  }

  if (getenv("AFL_SKIP_BIN_CHECK")) return;

  /* Check for blatant user errors. */

  if ((!strncmp(target_path, "/tmp/", 5) && !strchr(target_path + 5, '/')) ||
      (!strncmp(target_path, "/var/tmp/", 9) && !strchr(target_path + 9, '/')))
     FATAL("Please don't keep binaries in /tmp or /var/tmp");

  fd = open(target_path, O_RDONLY);

  if (fd < 0) PFATAL("Unable to open '%s'", target_path);

  f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);

  if (f_data == MAP_FAILED) PFATAL("Unable to mmap file '%s'", target_path);

  close(fd);

  if (f_data[0] == '#' && f_data[1] == '!') {

    SAYF("\n" cLRD "[-] " cRST
         "Oops, the target binary looks like a shell script. Some build systems will\n"
         "    sometimes generate shell stubs for dynamically linked programs; try static\n"
         "    library mode (./configure --disable-shared) if that's the case.\n\n"

         "    Another possible cause is that you are actually trying to use a shell\n" 
         "    wrapper around the fuzzed component. Invoking shell can slow down the\n" 
         "    fuzzing process by a factor of 20x or more; it's best to write the wrapper\n"
         "    in a compiled language instead.\n");

    FATAL("Program '%s' is a shell script", target_path);

  }

#ifndef __APPLE__

  if (f_data[0] != 0x7f || memcmp(f_data + 1, "ELF", 3))
    FATAL("Program '%s' is not an ELF binary", target_path);

#else

  if (f_data[0] != 0xCF || f_data[1] != 0xFA || f_data[2] != 0xED)
    FATAL("Program '%s' is not a 64-bit Mach-O binary", target_path);

#endif /* ^!__APPLE__ */

  if (!qemu_mode && !dumb_mode &&
      !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {

    SAYF("\n" cLRD "[-] " cRST
         "Looks like the target binary is not instrumented! The fuzzer depends on\n"
         "    compile-time instrumentation to isolate interesting test cases while\n"
         "    mutating the input data. For more information, and for tips on how to\n"
         "    instrument binaries, please see %s/README.\n\n"

         "    When source code is not available, you may be able to leverage QEMU\n"
         "    mode support. Consult the README for tips on how to enable this.\n"

         "    (It is also possible to use afl-fuzz as a traditional, \"dumb\" fuzzer.\n"
         "    For that, you can use the -n option - but expect much worse results.)\n",
         doc_path);

    FATAL("No instrumentation detected");

  }

  if (qemu_mode &&
      memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) {

    SAYF("\n" cLRD "[-] " cRST
         "This program appears to be instrumented with afl-gcc, but is being run in\n"
         "    QEMU mode (-Q). This is probably not what you want - this setup will be\n"
         "    slow and offer no practical benefits.\n");

    FATAL("Instrumentation found in -Q mode");

  }

  if (memmem(f_data, f_len, "libasan.so", 10) ||
      memmem(f_data, f_len, "__msan_init", 11)) uses_asan = 1;

  /* Detect persistent & deferred init signatures in the binary. */

  if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) {

    OKF(cPIN "Persistent mode binary detected.");
    setenv(PERSIST_ENV_VAR, "1", 1);
    persistent_mode = 1;

  } else if (getenv("AFL_PERSISTENT")) {

    WARNF("AFL_PERSISTENT is no longer supported and may misbehave!");

  }

  if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) {

    OKF(cPIN "Deferred forkserver binary detected.");
    setenv(DEFER_ENV_VAR, "1", 1);
    deferred_mode = 1;

  } else if (getenv("AFL_DEFER_FORKSRV")) {

    WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!");

  }

  if (munmap(f_data, f_len)) PFATAL("unmap() failed");

}


/* Trim and possibly create a banner for the run. */

static void fix_up_banner(u8* name) {//获取目标的路径或路径加省略号

  if (!use_banner) { //检查是否有设置use_banner

    if (sync_id) { //如果有设置则检查是否有设置sync_id的值,如果有设置

      use_banner = sync_id;//将sync_id赋值给use_banner

    } else {

      u8* trim = strrchr(name, '/'); //获取最后一个参数中最后一个/后的内容,也就是要测试的目标文件名称
      if (!trim) use_banner = name; else use_banner = trim + 1; //如果没有获取到,就将目标文件路径赋值给use_banner

    }

  }

  if (strlen(use_banner) > 40) {//use_banner的长度如果大于40

    u8* tmp = ck_alloc(44);//就创建一个44字节大小的空间
    sprintf(tmp, "%.40s...", use_banner);//去前40个字节然后后面加胜率号
    use_banner = tmp;//然后再将tmp赋值给ues_bannef

  }

}


/* Check if we're on TTY. 检查是否在TTY终端上运行(tty就是linux终端)*/

static void check_if_tty(void) {

  struct winsize ws;

  if (getenv("AFL_NO_UI")) { //如果设置了环境变量afl_no_ui的值则提示禁用ui
    OKF("Disabling the UI because AFL_NO_UI is set.");
    not_on_tty = 1;//设置not_on_tty为1
    return;
  }

  if (ioctl(1, TIOCGWINSZ, &ws)) {//通过ioctl获取窗口大小

    if (errno == ENOTTY) { //如果报错为enotty
      OKF("Looks like we're not running on a tty, so I'll be a bit less verbose."); //提示并不是在tty终端运行
      not_on_tty = 1;//设置not_on_tty为1
    }

    return;
  }

}


/* Check terminal dimensions after resize. */

static void check_term_size(void) {

  struct winsize ws;

  term_too_small = 0;

  if (ioctl(1, TIOCGWINSZ, &ws)) return;

  if (ws.ws_row == 0 && ws.ws_col == 0) return;
  if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1;

}



/* Display usage hints. */

static void usage(u8* argv0) {

  SAYF("\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n"

       "Required parameters:\n\n"

       "  -i dir        - input directory with test cases\n"
       "  -o dir        - output directory for fuzzer findings\n\n"

       "Execution control settings:\n\n"

       "  -f file       - location read by the fuzzed program (stdin)\n"
       "  -t msec       - timeout for each run (auto-scaled, 50-%u ms)\n"
       "  -m megs       - memory limit for child process (%u MB)\n"
       "  -Q            - use binary-only instrumentation (QEMU mode)\n\n"     
 
       "Fuzzing behavior settings:\n\n"

       "  -d            - quick & dirty mode (skips deterministic steps)\n"
       "  -n            - fuzz without instrumentation (dumb mode)\n"
       "  -x dir        - optional fuzzer dictionary (see README)\n\n"

       "Other stuff:\n\n"

       "  -T text       - text banner to show on the screen\n"
       "  -M / -S id    - distributed mode (see parallel_fuzzing.txt)\n"
       "  -C            - crash exploration mode (the peruvian rabbit thing)\n\n"

       "For additional tips, please consult %s/README.\n\n",

       argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);

  exit(1);

}


/* Prepare output directories and fds. */

EXP_ST void setup_dirs_fds(void) {
    //准备输出文件目录(out_dir或sync_dir),创建plot_file

  u8* tmp;
  s32 fd;

  ACTF("Setting up output directories...");

  if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST) //如果sync_id存在则创建sync_dir目录,并给可读可写可执行权限
      PFATAL("Unable to create '%s'", sync_dir); //如果创建失败则抛出异常

  if (mkdir(out_dir, 0700)) { //创建out_dir 赋予可读可写可执行权限

    if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir); //创建失败抛出异常

    maybe_delete_out_dir();//调用这个函数

  } else {//如果创建out_dir失败

    if (in_place_resume)//如果设置了in_place_resume就抛出异常
      FATAL("Resume attempted but old output directory not found");

    out_dir_fd = open(out_dir, O_RDONLY); //以只读的形式打开out_dir文件并行给out_dir_fd权柄

#ifndef __sun //如果没有定义宏_sun
     
    if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) //如果打开out_dir_fd失败或者out_dir通过flock函数建立互斥锁定失败,则抛出异常
      PFATAL("Unable to flock() output directory.");

#endif /* !__sun */

  }

  /* Queue directory for any starting & discovered paths. */
  //创建out_dir/queue目录,权限读写执行
  tmp = alloc_printf("%s/queue", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* Top-level directory for queue metadata used for session
     resume and related tasks. */
     //创建out_dir/queue/.state/目录,权限读写执行。该目录用于保存session resume和tasks的队列元数据
  tmp = alloc_printf("%s/queue/.state/", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* Directory for flagging queue entries that went through
     deterministic fuzzing in the past. */
     //创建out_dir/queue/.state/deterministic_done/目录,权限读写执行,该文目录标记过去经历过deterministic fuzzing的队列条目
  tmp = alloc_printf("%s/queue/.state/deterministic_done/", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* Directory with the auto-selected dictionary entries. */
  //创建out_dir/queue/.state/auto_extras/目录,权限读写执行,该目录用于保存带有自动选择的字典条目
  tmp = alloc_printf("%s/queue/.state/auto_extras/", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* The set of paths currently deemed redundant. */
   //创建out_dir/queue/.state/redundant_edges/目录,权限读写执行,该目录保存被认为是多余的路径集
  tmp = alloc_printf("%s/queue/.state/redundant_edges/", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* The set of paths showing variable behavior. */
    //创建out_dir/queue/.state/variable_behavior/目录,权限读写执行,该目录保存不同行为的路径集
  tmp = alloc_printf("%s/queue/.state/variable_behavior/", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* Sync directory for keeping track of cooperating fuzzers. */

  if (sync_id) { //如果sync_id存在

    tmp = alloc_printf("%s/.synced/", out_dir);
    //创建out_dir/.synced/目录,权限读写执行,做目录同步,该目录用于跟踪cooperating fuzzers
    if (mkdir(tmp, 0700) && (!in_place_resume || errno != EEXIST))
      PFATAL("Unable to create '%s'", tmp);

    ck_free(tmp);

  }

  /* All recorded crashes. */
   //创建out_dir/crashes/目录,权限读写执行,该目录用于记录crash
  tmp = alloc_printf("%s/crashes", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* All recorded hangs. */
   //创建out_dir/hangs/目录,权限读写执行,该目录用于记录hangs
  tmp = alloc_printf("%s/hangs", out_dir);
  if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  /* Generally useful file descriptors. */

  dev_null_fd = open("/dev/null", O_RDWR);//尝试以读写方式打开/dev/null
  if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");

  dev_urandom_fd = open("/dev/urandom", O_RDONLY);
  if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom");

  /* Gnuplot output file.建立Gnuplot目录 */

  tmp = alloc_printf("%s/plot_data", out_dir);
  fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600); // 以只写方式打开out_dir / plot_data文件,如果文件不存在就创建,返回句柄fd
  if (fd < 0) PFATAL("Unable to create '%s'", tmp);
  ck_free(tmp);

  plot_file = fdopen(fd, "w"); //根据句柄得到FILE结构体指针plot_file
  if (!plot_file) PFATAL("fdopen() failed");

  //向plot_file中写入# unix_time, cycles_done, cur_path, paths_total, pending_total, pending_favs, map_size, unique_crashes, unique_hangs, max_depth, execs_per_sec
  fprintf(plot_file, "# unix_time, cycles_done, cur_path, paths_total, "
                     "pending_total, pending_favs, map_size, unique_crashes, "
                     "unique_hangs, max_depth, execs_per_sec\n");
                     /* ignore errors */

}


/* Setup the output file for fuzzed data, if not using -f. */

EXP_ST void setup_stdio_file(void) {//如果没有设置out_file,调用该函数。如果没有使用-f,就删除原本的out_dir/.cur_input,创建一个新的out_dir/.cur_input,保存其文件描述符在out_fd中

  u8* fn = alloc_printf("%s/.cur_input", out_dir);//拼接fn:"out_dir/.cur_input"

  unlink(fn); /* Ignore errors */

  out_fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600); //删除原本的out_dir/.cur_input,创建新的。文件描述符给out_fd

  if (out_fd < 0) PFATAL("Unable to create '%s'", fn);

  ck_free(fn);

}


/* Make sure that core dumps don't go to a program. */

static void check_crash_handling(void) { //确保core dump不会进入程序 属于cpu的检查函数

#ifdef __APPLE__

  /* Yuck! There appears to be no simple C API to query for the state of 
     loaded daemons on MacOS X, and I'm a bit hesitant to do something
     more sophisticated, such as disabling crash reporting via Mach ports,
     until I get a box to test the code. So, for now, we check for crash
     reporting the awful way. */
  
  if (system("launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'")) return;

  SAYF("\n" cLRD "[-] " cRST
       "Whoops, your system is configured to forward crash notifications to an\n"
       "    external crash reporting utility. This will cause issues due to the\n"
       "    extended delay between the fuzzed binary malfunctioning and this fact\n"
       "    being relayed to the fuzzer via the standard waitpid() API.\n\n"
       "    To avoid having crashes misinterpreted as timeouts, please run the\n" 
       "    following commands:\n\n"

       "    SL=/System/Library; PL=com.apple.ReportCrash\n"
       "    launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n"
       "    sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n");

  if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES"))
    FATAL("Crash reporter detected");

#else

  /* This is Linux specific, but I don't think there's anything equivalent on
     *BSD, so we can just let it slide for now. */

  s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY);
  u8  fchar;

  if (fd < 0) return;

  ACTF("Checking core_pattern...");

  if (read(fd, &fchar, 1) == 1 && fchar == '|') {

    SAYF("\n" cLRD "[-] " cRST
         "Hmm, your system is configured to send core dump notifications to an\n"
         "    external utility. This will cause issues: there will be an extended delay\n"
         "    between stumbling upon a crash and having this information relayed to the\n"
         "    fuzzer via the standard waitpid() API.\n\n"

         "    To avoid having crashes misinterpreted as timeouts, please log in as root\n" 
         "    and temporarily modify /proc/sys/kernel/core_pattern, like so:\n\n"

         "    echo core >/proc/sys/kernel/core_pattern\n");

    if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES"))
      FATAL("Pipe at the beginning of 'core_pattern'");

  }
 
  close(fd);

#endif /* ^__APPLE__ */

}


/* Check CPU governor. */

static void check_cpu_governor(void) {  //确保cpu的管理者,属于cpu的相关检查函数

  FILE* f;
  u8 tmp[128];
  u64 min = 0, max = 0;

  if (getenv("AFL_SKIP_CPUFREQ")) return;

  f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r");
  if (!f) return;

  ACTF("Checking CPU scaling governor...");

  if (!fgets(tmp, 128, f)) PFATAL("fgets() failed");

  fclose(f);

  if (!strncmp(tmp, "perf", 4)) return;

  f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", "r");

  if (f) {
    if (fscanf(f, "%llu", &min) != 1) min = 0;
    fclose(f);
  }

  f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r");

  if (f) {
    if (fscanf(f, "%llu", &max) != 1) max = 0;
    fclose(f);
  }

  if (min == max) return;

  SAYF("\n" cLRD "[-] " cRST
       "Whoops, your system uses on-demand CPU frequency scaling, adjusted\n"
       "    between %llu and %llu MHz. Unfortunately, the scaling algorithm in the\n"
       "    kernel is imperfect and can miss the short-lived processes spawned by\n"
       "    afl-fuzz. To keep things moving, run these commands as root:\n\n"

       "    cd /sys/devices/system/cpu\n"
       "    echo performance | tee cpu*/cpufreq/scaling_governor\n\n"

       "    You can later go back to the original state by replacing 'performance' with\n"
       "    'ondemand'. If you don't want to change the settings, set AFL_SKIP_CPUFREQ\n"
       "    to make afl-fuzz skip this check - but expect some performance drop.\n",
       min / 1024, max / 1024);

  FATAL("Suboptimal CPU scaling governor");

}


/* Count the number of logical CPU cores. */

static void get_core_count(void) { //获取核心数量,属于cpu的检查参数

  u32 cur_runnable = 0;

#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__)

  size_t s = sizeof(cpu_core_count);

  /* On *BSD systems, we can just use a sysctl to get the number of CPUs. */

#ifdef __APPLE__

  if (sysctlbyname("hw.logicalcpu", &cpu_core_count, &s, NULL, 0) < 0)
    return;

#else

  int s_name[2] = { CTL_HW, HW_NCPU };

  if (sysctl(s_name, 2, &cpu_core_count, &s, NULL, 0) < 0) return;

#endif /* ^__APPLE__ */

#else

#ifdef HAVE_AFFINITY

  cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN);

#else

  FILE* f = fopen("/proc/stat", "r");
  u8 tmp[1024];

  if (!f) return;

  while (fgets(tmp, sizeof(tmp), f))
    if (!strncmp(tmp, "cpu", 3) && isdigit(tmp[3])) cpu_core_count++;

  fclose(f);

#endif /* ^HAVE_AFFINITY */

#endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */

  if (cpu_core_count > 0) {

    cur_runnable = (u32)get_runnable_processes();

#if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__)

    /* Add ourselves, since the 1-minute average doesn't include that yet. */

    cur_runnable++;

#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */

    OKF("You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%).",
        cpu_core_count, cpu_core_count > 1 ? "s" : "",
        cur_runnable, cur_runnable * 100.0 / cpu_core_count);

    if (cpu_core_count > 1) {

      if (cur_runnable > cpu_core_count * 1.5) {

        WARNF("System under apparent load, performance may be spotty.");

      } else if (cur_runnable + 1 <= cpu_core_count) {

        OKF("Try parallel jobs - see %s/parallel_fuzzing.txt.", doc_path);
  
      }

    }

  } else {

    cpu_core_count = 0;
    WARNF("Unable to figure out the number of CPU cores.");

  }

}


/* Validate and fix up out_dir and sync_dir when using -S. */

static void fix_up_sync(void) { //检查函数检查参数的语法,fuzzid 和sync_id是否过长,然后更新sync_id和out_dir

  u8* x = sync_id;

  if (dumb_mode) //如果在设置dumb_mode模式,提示-S / -M参数与 -n参数互斥
    FATAL("-S / -M and -n are mutually exclusive");

  if (skip_deterministic) {

    if (force_deterministic) //-d参数纠正
      FATAL("use -S instead of -M -d");
    else
      FATAL("-S already implies -d");

  }

  while (*x) {

    if (!isalnum(*x) && *x != '_' && *x != '-')//fuzzer ID纠正
      FATAL("Non-alphanumeric fuzzer ID specified via -S or -M");

    x++;

  }

  if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long");//如果sync_id的长度大于32则抛出异常

  x = alloc_printf("%s/%s", out_dir, sync_id); 

  sync_dir = out_dir; //更新sync_dir的值
  out_dir  = x; //更新out_dir的值设置为sync_id

  if (!force_deterministic) { //如果没有设置force_deterministic的值
    skip_deterministic = 1;//则设置为1
    use_splicing = 1;//设置use_splicing为1
  }

}


/* Handle screen resize (SIGWINCH). */

static void handle_resize(int sig) {
  clear_screen = 1;
}


/* Check ASAN options. */

static void check_asan_opts(void) { //获取环境变量,根据获取值抛出异常  属于前置函数
  u8* x = getenv("ASAN_OPTIONS");//获取ASAN_OPTIONS环境变量

  if (x) {//如果已经设置ASAN_OPTIONS

    if (!strstr(x, "abort_on_error=1")) //检查是否同时设置了abort_on_error为1 如果没有就抛出异常
      FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");

    if (!strstr(x, "symbolize=0")) //检查是否同时设置了symbolize为0如果没有就抛出异常
      FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");

  }

  x = getenv("MSAN_OPTIONS");//获取环境变量MSAN_OPTIONS

  if (x) {//如果已经设置了MSAN_OPTIONS

    if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) // 检查是否同时设置了exit_code对应的状态码,如果没有抛异常
      FATAL("Custom MSAN_OPTIONS set without exit_code="
            STRINGIFY(MSAN_ERROR) " - please fix!");

    if (!strstr(x, "symbolize=0"))//检查是否同时设置了symbolize=0,如果没有抛异常
      FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");

  }

} 


/* Detect @@ in args. */

EXP_ST void detect_file_args(char** argv) {//检查输入argv中是否存在@@,替换成out_dir/.cur_input

  u32 i = 0;
  u8* cwd = getcwd(NULL, 0); //获取当前工作目录的绝对路径

  if (!cwd) PFATAL("getcwd() failed"); //如果没有获取到则直接抛出异常

  while (argv[i]) {//遍历输入参数 查找argv数组里面是否存在@@,存在就存放到aaloc里面

    u8* aa_loc = strstr(argv[i], "@@");

    if (aa_loc) {//如果存在

      u8 *aa_subst, *n_arg;

      /* If we don't have a file name chosen yet, use a safe default. */

      if (!out_file) //如果没有设置out_file的值则赋值out_file为路径:“out_dir/.cur_input”
        out_file = alloc_printf("%s/.cur_input", out_dir);

      /* Be sure that we're always using fully-qualified paths. */

      if (out_file[0] == '/') aa_subst = out_file; //判断路径是否以“/”开始,应该是判断是不是绝对路径 //如果是,则直接赋值给aa_subst变量
      else aa_subst = alloc_printf("%s/%s", cwd, out_file); //如果不是 则直接拼接成绝对路径

      /* Construct a replacement argv value. */

      *aa_loc = 0;//将当前位置的@替换成空字节
      n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2); //跳过aal_loc的两个字节进行拼接
      argv[i] = n_arg; //n_arg:“out_dir/.cur_input”
      *aa_loc = '@';//将当前位置替换成“@”

      if (out_file[0] != '/') ck_free(aa_subst);

    }

    i++;

  }

  free(cwd); /* not tracked */

}


/* Set up signal handlers. More complicated that needs to be, because libc on
   Solaris doesn't resume interrupted reads(), sets SA_RESETHAND when you call
   siginterrupt(), and does other unnecessary things. */

EXP_ST void setup_signal_handlers(void) {
    //该函数在main()函数中对输入参数进行配置后第一个初始配置函数,主要作用为注册必要的信号处理函数

  struct sigaction sa; //创建sigcation结构体变量sa

  sa.sa_handler   = NULL;  //设置sa_handler为null (处理函数指针。)
  sa.sa_flags     = SA_RESTART; //设置flags为SA_RESTART (信号处理修改器;)
  sa.sa_sigaction = NULL; //设置sigaction为null 

  sigemptyset(&sa.sa_mask); //初始化mask为空

  /* Various ways of saying "stop".主要是stop函数的处理 */

  sa.sa_handler = handle_stop_sig;//handle_stop_sig设置top_soon为1,如果child_pid存在,向其发送SIGKILL终止信号,从而被系统杀死,如果forksrv_pid存在,向其发送SIGKILL终止信号
  sigaction(SIGHUP, &sa, NULL); //(该信号是处于非链接状态的中断发送给控制进程,或者由控制进程在本身结束时发送给每个前台进程)
  sigaction(SIGINT, &sa, NULL);//SIGINT是比如crtl+c的中断信号 SIGTERM是要求进程结束的信号,是kill命令默认发送的信号
  sigaction(SIGTERM, &sa, NULL); //SIGHUP、SIGINT、SIGTERM信号关联执行handle_stop_sig


  /* Exec timeout notifications.处理超时情况 */

  sa.sa_handler = handle_timeout; //handle_timeout判断如果child_pid > 0,则设置child_time_out为1,并kill掉child_pid。如果child_pid==-1,且forksrv_pid>0,则设置child_time_out为1,并kill掉forksrv_pid
  sigaction(SIGALRM, &sa, NULL);//SIGALRM信号关联执行handle_timeout(SIGALRM是由alarm函数的定时器产生)

  /* Window resize 处理窗口大小的变化信号*/

  sa.sa_handler = handle_resize;//handle_resize设置clear_screen=1
  sigaction(SIGWINCH, &sa, NULL);//SIGWINCH信号关联执行handle_resize

  /* SIGUSR1: skip entry */

  sa.sa_handler = handle_skipreq;//handle_skipreq设置skip_requested=1
  sigaction(SIGUSR1, &sa, NULL);//用户自定义信号,定义成skip_request(SIGUSR1),信号关联执行handle_skipreq

  /* Things we don't care about.;不关心的信号种类 */

  sa.sa_handler = SIG_IGN;
  sigaction(SIGTSTP, &sa, NULL);
  sigaction(SIGPIPE, &sa, NULL);

}


/* Rewrite argv for QEMU. */

static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {

  char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
  u8 *tmp, *cp, *rsl, *own_copy;

  /* Workaround for a QEMU stability glitch. */

  setenv("QEMU_LOG", "nochain", 1);

  memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);

  new_argv[2] = target_path;
  new_argv[1] = "--";

  /* Now we need to actually find the QEMU binary to put in argv[0]. */

  tmp = getenv("AFL_PATH");

  if (tmp) {

    cp = alloc_printf("%s/afl-qemu-trace", tmp);

    if (access(cp, X_OK))
      FATAL("Unable to find '%s'", tmp);

    target_path = new_argv[0] = cp;
    return new_argv;

  }

  own_copy = ck_strdup(own_loc);
  rsl = strrchr(own_copy, '/');

  if (rsl) {

    *rsl = 0;

    cp = alloc_printf("%s/afl-qemu-trace", own_copy);
    ck_free(own_copy);

    if (!access(cp, X_OK)) {

      target_path = new_argv[0] = cp;
      return new_argv;

    }

  } else ck_free(own_copy);

  if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {

    target_path = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace");
    return new_argv;

  }

  SAYF("\n" cLRD "[-] " cRST
       "Oops, unable to find the 'afl-qemu-trace' binary. The binary must be built\n"
       "    separately by following the instructions in qemu_mode/README.qemu. If you\n"
       "    already have the binary installed, you may need to specify AFL_PATH in the\n"
       "    environment.\n\n"

       "    Of course, even without QEMU, afl-fuzz can still work with binaries that are\n"
       "    instrumented at compile time with afl-gcc. It is also possible to use it as a\n"
       "    traditional \"dumb\" fuzzer by specifying '-n' in the command line.\n");

  FATAL("Failed to locate 'afl-qemu-trace'.");

}


/* Make a copy of the current command line. */

static void save_cmdline(u32 argc, char** argv) {//将当前的参数拷贝进bug空间

  u32 len = 1, i;
  u8* buf;

  for (i = 0; i < argc; i++) //计算参数的长度
    len += strlen(argv[i]) + 1;
  
  buf = orig_cmdline = ck_alloc(len); //为buf申请空间

  for (i = 0; i < argc; i++) { //计算当前acrg的长度

    u32 l = strlen(argv[i]);

    memcpy(buf, argv[i], l);//将argv复制到buf
    buf += l;

    if (i != argc - 1) *(buf++) = ' ';//检查后面是否还有蚕食

  }

  *buf = 0;//结尾

}


#ifndef AFL_LIB

/* Main entry point */

int main(int argc, char** argv) {

  s32 opt;
  u64 prev_queued = 0;
  u32 sync_interval_cnt = 0, seek_to;
  u8  *extras_dir = 0;
  u8  mem_limit_given = 0;
  u8  exit_1 = !!getenv("AFL_BENCH_JUST_ONE");
  char** use_argv;

  struct timeval tv;
  struct timezone tz;

  SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");

  doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;

  gettimeofday(&tv, &tz);
  srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());

  while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Q")) > 0)

    switch (opt) {

      case 'i': /* input dir */

        if (in_dir) FATAL("Multiple -i options not supported");
        in_dir = optarg;

        if (!strcmp(in_dir, "-")) in_place_resume = 1;

        break;

      case 'o': /* output dir */

        if (out_dir) FATAL("Multiple -o options not supported");
        out_dir = optarg;
        break;

      case 'M': { /* master sync ID */

          u8* c;

          if (sync_id) FATAL("Multiple -S or -M options not supported");
          sync_id = ck_strdup(optarg);

          if ((c = strchr(sync_id, ':'))) {

            *c = 0;

            if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
                !master_id || !master_max || master_id > master_max ||
                master_max > 1000000) FATAL("Bogus master ID passed to -M");

          }

          force_deterministic = 1;

        }

        break;

      case 'S': 

        if (sync_id) FATAL("Multiple -S or -M options not supported");
        sync_id = ck_strdup(optarg);
        break;

      case 'f': /* target file */

        if (out_file) FATAL("Multiple -f options not supported");
        out_file = optarg;
        break;

      case 'x': /* dictionary */

        if (extras_dir) FATAL("Multiple -x options not supported");
        extras_dir = optarg;
        break;

      case 't': { /* timeout */

          u8 suffix = 0;

          if (timeout_given) FATAL("Multiple -t options not supported");

          if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -t");

          if (exec_tmout < 5) FATAL("Dangerously low value of -t");

          if (suffix == '+') timeout_given = 2; else timeout_given = 1;

          break;

      }

      case 'm': { /* mem limit */

          u8 suffix = 'M';

          if (mem_limit_given) FATAL("Multiple -m options not supported");
          mem_limit_given = 1;

          if (!strcmp(optarg, "none")) {

            mem_limit = 0;
            break;

          }

          if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -m");

          switch (suffix) {

            case 'T': mem_limit *= 1024 * 1024; break;
            case 'G': mem_limit *= 1024; break;
            case 'k': mem_limit /= 1024; break;
            case 'M': break;

            default:  FATAL("Unsupported suffix or bad syntax for -m");

          }

          if (mem_limit < 5) FATAL("Dangerously low value of -m");

          if (sizeof(rlim_t) == 4 && mem_limit > 2000)
            FATAL("Value of -m out of range on 32-bit systems");

        }

        break;

      case 'd': /* skip deterministic */

        if (skip_deterministic) FATAL("Multiple -d options not supported");
        skip_deterministic = 1;
        use_splicing = 1;
        break;

      case 'B': /* load bitmap */

        /* This is a secret undocumented option! It is useful if you find
           an interesting test case during a normal fuzzing process, and want
           to mutate it without rediscovering any of the test cases already
           found during an earlier run.

           To use this mode, you need to point -B to the fuzz_bitmap produced
           by an earlier run for the exact same binary... and that's it.

           I only used this once or twice to get variants of a particular
           file, so I'm not making this an official setting. */

        if (in_bitmap) FATAL("Multiple -B options not supported");

        in_bitmap = optarg;
        read_bitmap(in_bitmap);
        break;

      case 'C': /* crash mode */

        if (crash_mode) FATAL("Multiple -C options not supported");
        crash_mode = FAULT_CRASH;
        break;

      case 'n': /* dumb mode */

        if (dumb_mode) FATAL("Multiple -n options not supported");
        if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;

        break;

      case 'T': /* banner */

        if (use_banner) FATAL("Multiple -T options not supported");
        use_banner = optarg;
        break;

      case 'Q': /* QEMU mode */

        if (qemu_mode) FATAL("Multiple -Q options not supported");
        qemu_mode = 1;

        if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;

        break;

      default:

        usage(argv[0]);

    }

  if (optind == argc || !in_dir || !out_dir) usage(argv[0]);

  setup_signal_handlers();
  check_asan_opts();

  if (sync_id) fix_up_sync();

  if (!strcmp(in_dir, out_dir))  //检查in_dir和out_dir是否相同,不同就报错
    FATAL("Input and output directories can't be the same");

  if (dumb_mode) {//如果设置了dumb_mode的值

    if (crash_mode) FATAL("-C and -n are mutually exclusive");
    if (qemu_mode)  FATAL("-Q and -n are mutually exclusive");//不能设置互斥的crash_mode和qemu_mode,否则就报错

  }

  if (getenv("AFL_NO_FORKSRV"))    no_forkserver    = 1; //如果设置了环境变量的值,就将某值设置为1
  if (getenv("AFL_NO_CPU_RED"))    no_cpu_meter_red = 1;
  if (getenv("AFL_NO_ARITH"))      no_arith         = 1;
  if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue    = 1;
  if (getenv("AFL_FAST_CAL"))      fast_cal         = 1;

  if (getenv("AFL_HANG_TMOUT")) { //如果获取了环境变量AFL_HANG_TMOUT
    hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));//设置hang_tmout对应的时间
    if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");//如果没有设置成功则报错
  }

  if (dumb_mode == 2 && no_forkserver)//dumb_mode == 2与no_forkserver互斥
    FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");

  if (getenv("AFL_PRELOAD")) {//如果设置了AFL_PRELOAD环境变量
    setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
    setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);//将LD_PRELOAD和DYLD_INSERT_LIBRARIES设置为AFL_PRELOAD
  }

  if (getenv("AFL_LD_PRELOAD"))//如果设置了AFL_LD_PRELOAD,提示使用AFL_PRELOAD来代替AFL_LD_PRELOAD
    FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");

  save_cmdline(argc, argv);

  fix_up_banner(argv[optind]);

  check_if_tty();

  get_core_count();

#ifdef HAVE_AFFINITY
  bind_to_free_cpu();
#endif /* HAVE_AFFINITY */

  check_crash_handling();
  check_cpu_governor();

  setup_post();
  setup_shm();
  init_count_class16();

  setup_dirs_fds();
  read_testcases();
  load_auto();

  pivot_inputs();

  if (extras_dir) load_extras(extras_dir);

  if (!timeout_given) find_timeout();

  detect_file_args(argv + optind + 1);

  if (!out_file) setup_stdio_file();

  check_binary(argv[optind]);

  start_time = get_cur_time();

  if (qemu_mode)
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else
    use_argv = argv + optind;

  perform_dry_run(use_argv);

  cull_queue();

  show_init_stats();

  seek_to = find_start_position();

  write_stats_file(0, 0, 0);
  save_auto();

  if (stop_soon) goto stop_fuzzing;

  /* Woop woop woop */

  if (!not_on_tty) {
    sleep(4);
    start_time += 4000;
    if (stop_soon) goto stop_fuzzing;
  }

  while (1) { //fuzz流程

    u8 skipped_fuzz;

    cull_queue();//精简队列

    if (!queue_cur) {

      queue_cycle++;//queue_cycle计数器+1,代表所有queue被完整执行轮次
      current_entry     = 0;//queue入口参数至0
      cur_skipped_paths = 0; //废弃输入至0
      queue_cur         = queue;//queue_cur指向queue头,开始新一轮fuzz

      while (seek_to) {//如果是resume fuzz,检查seek_to是否为空,如果不为空就从seek_to指定的queue项开始执行(seek_to由find_start_position函数返回)
        current_entry++; //current_entry+1
        seek_to--;//seek_to-1
        queue_cur = queue_cur->next; //指向下一个queue
      }

      show_stats(); //刷新展示界面

      if (not_on_tty) {//如果不是终端模式(not_on_tty==1)
        ACTF("Entering queue cycle %llu.", queue_cycle);//输出当前是第几次循环
        fflush(stdout);
      }

      /* If we had a full queue cycle with no new finds, try
         recombination strategies next. */
         //如果经历了一个完整的扫描周期后都没有新的路径发现,那么尝试调整策略
      if (queued_paths == prev_queued) {//如果在执行一次完整的扫描周期后新发现的路径数与执行之前的一样,这代表没有发现任何新的路径

        if (use_splicing) //如果use_splicing为1
            cycles_wo_finds++; //设置cycles_wo_finds计数器+1,记录本次扫描无新路径
        else 
            use_splicing = 1; //否则设置use_splicing为1,代表接下来需要通过splice重组queue

      } else cycles_wo_finds = 0; //如果执行后和执行前路径数不一样,那么设置cycles_wo_finds为0

      prev_queued = queued_paths;//更新“上一次”路径数

      if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))//如果设置了sync_id(fuzzer)并且queue_cycle==1且能够获取AFL_IMPORT_FIRST环境变量
        sync_fuzzers(use_argv); //调用sync_fuzzers()函数读取其他sync文件夹下的fuzzer,然后保存到自己的queue中

    }

    skipped_fuzz = fuzz_one(use_argv); //调用fuzz_one,对queue_cur进行一次测试,如果不执行返回1,否则返回0

    if (!stop_soon && sync_id && !skipped_fuzz) { //如果没有设置stop_soon,且sync_id(fuzzer)存在,且skipped_fuzz为0
      
      if (!(sync_interval_cnt++ % SYNC_INTERVAL)) //sync_interval_cnt计数器+1,对SYNC_INTERVAL(默认为5)求余,即如果是5的倍数
        sync_fuzzers(use_argv);//调用sync_fuzzers,同步其他fuzzer

    }

    if (!stop_soon && exit_1) stop_soon = 2; //如果没有设置stop_soon,且exit_1不为0 //设置stop_soon为2

    if (stop_soon) break;//检查stop_soon是否为空 //不为空break出主循环

    queue_cur = queue_cur->next;//否则准备queue中的下一个样本
    current_entry++;//queue ID+1

  }

  if (queue_cur) show_stats();

  /* If we stopped programmatically, we kill the forkserver and the current runner. 
     If we stopped manually, this is done by the signal handler. */
  if (stop_soon == 2) {
      if (child_pid > 0) kill(child_pid, SIGKILL);
      if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
  }
  /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */
  if (waitpid(forksrv_pid, NULL, 0) <= 0) {
    WARNF("error waitpid\n");
  }

  write_bitmap();
  write_stats_file(0, 0, 0);
  save_auto();

stop_fuzzing:

  SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
       stop_soon == 2 ? "programmatically" : "by user");

  /* Running for more than 30 minutes but still doing first cycle? */

  if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {

    SAYF("\n" cYEL "[!] " cRST
           "Stopped during the first cycle, results may be incomplete.\n"
           "    (For info on resuming, see %s/README.)\n", doc_path);

  }

  fclose(plot_file);
  destroy_queue();
  destroy_extras();
  ck_free(target_path);
  ck_free(sync_id);

  alloc_report();

  OKF("We're done here. Have a nice day!\n");

  exit(0);

}

#endif /* !AFL_LIB */

  

  冲!为了更好的自己

 

  

 

posted @ 2022-02-17 16:30  庄周恋蝶蝶恋花  阅读(1181)  评论(0)    收藏  举报