// 覃业斌注释
/*
   american fuzzy lop++ - common routines
   --------------------------------------

   Originally written by Michal Zalewski

   Now maintained by Marc Heuse <mh@mh-sec.de>,
                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                        Andrea Fioraldi <andreafioraldi@gmail.com>

   Copyright 2016, 2017 Google Inc. All rights reserved.
   Copyright 2019-2024 AFLplusplus Project. 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:

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

   Gather some functions common to multiple executables

   - detect_file_args

 */

#include <stdlib.h>
#include <stdio.h>
#include "forkserver.h"
#ifndef _GNU_SOURCE
  #define _GNU_SOURCE
#endif
#ifndef __USE_GNU
  #define __USE_GNU
#endif
#include <string.h>
#include <strings.h>
#include <time.h>
#include <math.h>
#include <sys/mman.h>

#include "debug.h"
#include "alloc-inl.h"
#include "envs.h"
#include "common.h"

/* Detect @@ in args. */
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

u8  be_quiet = 0;
u8 *doc_path = "";
u8  last_intr = 0;

#ifndef AFL_PATH
  #define AFL_PATH "/usr/local/lib/afl/"
#endif

/*
 * 在大字符串中查找小字符串的第一次出现位置
 *
 * @param haystack 大字符串
 * @param haystacklen 大字符串的长度
 * @param needle 小字符串
 * @param needlelen 小字符串的长度
 *
 * @return
 * 如果找到小字符串，则返回其在大字符串中的第一次出现位置的指针；否则返回NULL
 */
void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle,
                 size_t needlelen) {
  // 如果小字符串的长度大于大字符串的长度，则直接返回NULL，因为不可能找到
  if (unlikely(needlelen > haystacklen)) { return NULL; }

  // 遍历大字符串，从头到尾依次检查每个位置
  for (u32 i = 0; i <= haystacklen - needlelen; ++i) {
    // 如果当前位置的子字符串与小字符串完全匹配，则返回当前位置的指针
    if (unlikely(memcmp(haystack + i, needle, needlelen) == 0)) {
      return (void *)(haystack + i);
    }
  }

  // 如果遍历完大字符串都没有找到匹配的小字符串，则返回NULL
  return (void *)NULL;
}

/*
 * 设置各种sanitizer的默认配置选项
 *
 * 如果没有指定其他配置选项，则为ASAN、UBSAN、MSAN和LSAN设置合理的默认配置选项
 */
void set_sanitizer_defaults() {
  // 获取环境变量ASAN_OPTIONS、UBSAN_OPTIONS、MSAN_OPTIONS和LSAN_OPTIONS的值
  u8 *have_asan_options = getenv("ASAN_OPTIONS");
  u8 *have_ubsan_options = getenv("UBSAN_OPTIONS");
  u8 *have_msan_options = getenv("MSAN_OPTIONS");
  u8 *have_lsan_options = getenv("LSAN_OPTIONS");
  // 标记是否已经设置了sanitizer的配置选项
  u8 have_san_options = 0;
  // 默认的sanitizer配置选项字符串
  u8 default_options[1024] =
      "detect_odr_violation=0:abort_on_error=1:symbolize=0:"  // 禁止检测ODR违规、出错时终止程序、不符号化
      "allocator_may_return_null=1:handle_segv=0:handle_sigbus=0:"  // 允许分配器返回NULL、不处理SEGV和SIGBUS信号
      "handle_abort=0:handle_sigfpe=0:handle_sigill=0:"  // 不处理abort、SIGFPE和SIGILL信号
      "detect_stack_use_after_return=0:check_initialization_order=0:";  // 禁止检测栈使用后返回和初始化顺序检查

  // 如果已经设置了ASAN_OPTIONS、UBSAN_OPTIONS、MSAN_OPTIONS或LSAN_OPTIONS中的任何一个，则标记为已设置sanitizer配置选项
  if (have_asan_options || have_ubsan_options || have_msan_options ||
      have_lsan_options) {
    have_san_options = 1;
  }

  // LSAN不支持abort_on_error=1（这一点是否仍然成立？？）
  u8 should_detect_leaks = 0;  // 标记是否应该检测内存泄漏

  // 如果没有设置LSAN_OPTIONS，则设置其默认配置选项
  if (!have_lsan_options) {
    u8 buf[2048] = "";  // 缓冲区用于构建LSAN_OPTIONS字符串
    // 如果没有设置sanitizer配置选项，则使用默认的sanitizer配置选项
    if (!have_san_options) { strcpy(buf, default_options); }
    // 如果设置了ASAN_OPTIONS
    if (have_asan_options) {
      // 如果ASAN_OPTIONS中包含"detect_leaks=0"或"detect_leaks=false"，则设置LSAN_OPTIONS为不检测内存泄漏
      if (NULL != strstr(have_asan_options, "detect_leaks=0") ||
          NULL != strstr(have_asan_options, "detect_leaks=false")) {
        strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=0:malloc_context_size=0:");

      } else {
        // 否则，应该检测内存泄漏，并设置LSAN_OPTIONS为检测内存泄漏
        should_detect_leaks = 1;
        strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=1:malloc_context_size=30:");
      }
    }

    // 设置环境变量LSAN_OPTIONS
    setenv("LSAN_OPTIONS", buf, 1);
  }

  // 对于非LSAN的sanitizer，禁用内存泄漏检测

  // 如果没有设置LSAN_OPTIONS
  if (!have_lsan_options) {
    // 如果应该检测内存泄漏，则在默认的sanitizer配置选项字符串中添加"detect_leaks=1:malloc_context_size=30:"
    if (should_detect_leaks) {
      strcat(default_options, "detect_leaks=1:malloc_context_size=30:");

    } else {
      // 否则，添加"detect_leaks=0:malloc_context_size=0:"
      strcat(default_options, "detect_leaks=0:malloc_context_size=0:");
    }
  }

  // 如果没有设置sanitizer配置选项，则为ASAN设置默认配置选项
  if (!have_san_options) { setenv("ASAN_OPTIONS", default_options, 1); }

  // 如果没有设置sanitizer配置选项，则为UBSAN设置默认配置选项
  if (!have_san_options) { setenv("UBSAN_OPTIONS", default_options, 1); }

  // MSAN比较棘手，因为它目前不支持abort_on_error=1。因此，我们以一种非常hacky的方式进行设置
  if (!have_msan_options) {
    u8 buf[2048] = "";  // 缓冲区用于构建MSAN_OPTIONS字符串
    // 如果没有设置sanitizer配置选项，则使用默认的sanitizer配置选项
    if (!have_san_options) { strcpy(buf, default_options); }
    // 添加MSAN特有的配置选项
    strcat(buf, "exit_code=" STRINGIFY(MSAN_ERROR) ":msan_track_origins=0:");
    // 设置环境变量MSAN_OPTIONS
    setenv("MSAN_OPTIONS", buf, 1);
  }

  // 设置QASan的环境变量
  setenv("QASAN_MAX_CALL_STACK", "0", 0);
  setenv("QASAN_SYMBOLIZE", "0", 0);
}

/*
 * 检查二进制文件的签名
 *
 * @param fn 二进制文件的路径
 *
 * @return 返回一个标志值，表示检测到的二进制文件的特性：
 *         - 0: 没有检测到任何特性
 *         - 1: 检测到持久化模式的二进制文件
 *         - 2: 检测到延迟forkserver模式的二进制文件
 *         - 3: 同时检测到持久化模式和延迟forkserver模式的二进制文件
 */
u32 check_binary_signatures(u8 *fn) {
  int ret = 0;                  // 初始化返回值为0
  int fd = open(fn, O_RDONLY);  // 打开二进制文件，只读模式
  if (fd < 0) {
    PFATAL("Unable to open '%s'", fn);
  }  // 如果打开失败，则打印错误信息并退出

  struct stat st;  // 用于存储文件状态信息
  if (fstat(fd, &st) < 0) {
    PFATAL("Unable to fstat '%s'", fn);
  }  // 获取文件状态信息，如果失败则打印错误信息并退出
  u32 f_len = st.st_size;  // 获取文件长度
  u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd,
                    0);  // 将文件映射到内存，只读模式
  if (f_data == MAP_FAILED) {
    PFATAL("Unable to mmap file '%s'", fn);
  }  // 如果映射失败，则打印错误信息并退出
  close(fd);  // 关闭文件描述符

  // 检测持久化模式的二进制文件
  if (afl_memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) {
    if (!be_quiet) {
      OKF(cPIN "Persistent mode binary detected.");
    }  // 如果检测到持久化模式的二进制文件，则打印提示信息
    setenv(PERSIST_ENV_VAR, "1", 1);  // 设置环境变量表示持久化模式
    ret = 1;                          // 设置返回值为1
  } else if (getenv("AFL_PERSISTENT")) {  // 如果环境变量AFL_PERSISTENT存在
    if (!be_quiet) { OKF(cPIN "Persistent mode enforced."); }  // 打印提示信息
    setenv(PERSIST_ENV_VAR, "1", 1);  // 设置环境变量表示持久化模式
    ret = 1;                          // 设置返回值为1
  } else if (
      getenv(
          "AFL_FRIDA_PERSISTENT_ADDR")) {  // 如果环境变量AFL_FRIDA_PERSISTENT_ADDR存在
    if (!be_quiet) {
      OKF("FRIDA Persistent mode configuration options detected.");  // 打印提示信息
    }
    setenv(PERSIST_ENV_VAR, "1", 1);  // 设置环境变量表示持久化模式
    ret = 1;                          // 设置返回值为1
  }

  // 检测延迟forkserver模式的二进制文件
  if (afl_memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) {
    if (!be_quiet) {
      OKF(cPIN "Deferred forkserver binary detected.");
    }  // 如果检测到延迟forkserver模式的二进制文件，则打印提示信息
    setenv(DEFER_ENV_VAR, "1", 1);  // 设置环境变量表示延迟forkserver模式
    ret += 2;                       // 设置返回值增加2
  } else if (getenv(
                 "AFL_DEFER_FORKSRV")) {  // 如果环境变量AFL_DEFER_FORKSRV存在
    if (!be_quiet) {
      OKF(cPIN "Deferred forkserver enforced.");
    }  // 打印提示信息
    setenv(DEFER_ENV_VAR, "1", 1);  // 设置环境变量表示延迟forkserver模式
    ret += 2;                       // 设置返回值增加2
  }

  if (munmap(f_data, f_len)) {
    PFATAL("unmap() failed");
  }  // 取消内存映射，如果失败则打印错误信息并退出

  return ret;  // 返回检测结果
}

/*
 * 检测命令行参数中的@@符号，并替换为实际的文件路径
 *
 * @param argv 命令行参数数组
 * @param prog_in 输入文件的路径
 * @param use_stdin 标记是否使用标准输入
 */
void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) {
  u32 i = 0;          // 初始化索引变量
  u8  cwd[PATH_MAX];  // 存储当前工作目录的路径
  // 获取当前工作目录的路径，如果失败则打印错误信息并退出
  if (getcwd(cwd, (size_t)sizeof(cwd)) == NULL) { PFATAL("getcwd() failed"); }

  // 我们正在处理由libc堆分配的argv。因此，不要将其与其他分配API（如ck_alloc）混合使用。
  // 这样会干扰free()调用。
  // 遍历命令行参数数组
  while (argv[i]) {
    u8 *aa_loc = strstr(argv[i], "@@");  // 查找当前参数中的@@符号

    if (aa_loc) {  // 如果找到@@符号
      if (!prog_in) {
        FATAL("@@ syntax is not supported by this tool.");
      }  // 如果没有提供输入文件路径，则打印错误信息并退出

      *use_stdin = false;  // 标记不使用标准输入

      // 确保我们始终使用完全限定的路径
      *aa_loc = 0;  // 将@@符号替换为字符串终止符

      // 构建替换后的命令行参数值
      u8 *n_arg;
      if (prog_in[0] == '/') {  // 如果输入文件路径是绝对路径
        // 使用绝对路径构建替换后的命令行参数值
        n_arg = alloc_printf("%s%s%s", argv[i], prog_in, aa_loc + 2);

      } else {  // 如果输入文件路径是相对路径
        // 使用当前工作目录和相对路径构建替换后的命令行参数值
        n_arg = alloc_printf("%s%s/%s%s", argv[i], cwd, prog_in, aa_loc + 2);
      }

      ck_free(argv[i]);  // 释放原始命令行参数的内存
      argv[i] = n_arg;   // 将替换后的命令行参数值赋给argv[i]
    }

    i++;  // 移动到下一个命令行参数
  }

  // 命令行参数在退出时会自动释放内存
}

/* duplicate the system argv so that
  we can edit (and free!) it later */

/*
 * 复制命令行参数数组
 *
 * @param argc 命令行参数的数量
 * @param argv 命令行参数数组
 *
 * @return 返回复制后的命令行参数数组
 */
char **argv_cpy_dup(int argc, char **argv) {
  int i = 0;  // 初始化索引变量

  // 为复制后的命令行参数数组分配内存，大小为(argc + 1)个指针大小
  char **ret = ck_alloc((argc + 1) * sizeof(char *));
  // 如果分配失败，则打印错误信息并退出
  if (unlikely(!ret)) { FATAL("Amount of arguments specified is too high"); }

  // 遍历命令行参数数组，复制每个参数
  for (i = 0; i < argc; i++) {
    ret[i] = ck_strdup(argv[i]);  // 使用ck_strdup复制每个参数
  }

  // 设置复制后的命令行参数数组的最后一个元素为NULL
  ret[i] = NULL;

  // 返回复制后的命令行参数数组
  return ret;
}

/* frees all args in the given argv,
   previously created by argv_cpy_dup */

/*
 * 释放命令行参数数组的内存
 *
 * @param argv 命令行参数数组
 */
void argv_cpy_free(char **argv) {
  u32 i = 0;  // 初始化索引变量

  // 遍历命令行参数数组，释放每个参数的内存
  while (argv[i]) {
    ck_free(argv[i]);  // 使用ck_free释放当前参数的内存
    argv[i] = NULL;    // 将当前参数指针设置为NULL，避免野指针
    i++;               // 移动到下一个参数
  }

  // 释放命令行参数数组本身的内存
  ck_free(argv);
}

/* Rewrite argv for CoreSight process tracer. */

/*
 * 获取用于afl-cs-proxy的命令行参数数组
 *
 * @param own_loc 当前执行文件的路径
 * @param target_path_p 目标程序路径的指针
 * @param argc 原始命令行参数的数量
 * @param argv 原始命令行参数数组
 *
 * @return 返回新的命令行参数数组，用于afl-cs-proxy
 */
char **get_cs_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
  // 如果环境变量AFL_CS_CUSTOM_BIN已设置
  if (unlikely(getenv("AFL_CS_CUSTOM_BIN"))) {
    WARNF(
        "AFL_CS_CUSTOM_BIN is enabled. "
        "You must run your target under afl-cs-proxy on your own!");
    // 提醒用户必须自行在afl-cs-proxy下运行目标程序
    return argv;  // 返回原始命令行参数数组
  }

  // 为新的命令行参数数组分配内存，大小为(argc + 4)个指针大小
  char **new_argv = ck_alloc(sizeof(char *) * (argc + 4));
  // 如果分配失败，则打印错误信息并退出
  if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); }

  // 复制原始命令行参数到新的参数数组，从索引3开始
  memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1));
  // 设置新的命令行参数数组的最后一个元素为NULL
  new_argv[argc + 3] = NULL;

  // 设置新的命令行参数数组的第二个元素为目标程序路径
  new_argv[2] = *target_path_p;
  // 设置新的命令行参数数组的第一个元素为"--"
  new_argv[1] = "--";

  // 现在我们需要找到afl-cs-proxy的二进制文件路径，并将其设置为新的命令行参数数组的第一个元素
  *target_path_p = new_argv[0] = find_afl_binary(own_loc, "afl-cs-proxy");

  // 返回新的命令行参数数组
  return new_argv;
}

/* Rewrite argv for QEMU. */

/*
 * 获取用于afl-qemu-trace的命令行参数数组
 *
 * @param own_loc 当前执行文件的路径
 * @param target_path_p 目标程序路径的指针
 * @param argc 原始命令行参数的数量
 * @param argv 原始命令行参数数组
 *
 * @return 返回新的命令行参数数组，用于afl-qemu-trace
 */
char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
  // 如果环境变量AFL_QEMU_CUSTOM_BIN已设置
  if (unlikely(getenv("AFL_QEMU_CUSTOM_BIN"))) {
    WARNF(
        "AFL_QEMU_CUSTOM_BIN is enabled. "
        "You must run your target under afl-qemu-trace on your own!");
    // 提醒用户必须自行在afl-qemu-trace下运行目标程序
    return argv;  // 返回原始命令行参数数组
  }

  // 为新的命令行参数数组分配内存，大小为(argc + 3)个指针大小
  char **new_argv = ck_alloc(sizeof(char *) * (argc + 3));
  // 如果分配失败，则打印错误信息并退出
  if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); }

  // 复制原始命令行参数到新的参数数组，从索引3开始
  memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1));
  // 设置新的命令行参数数组的最后一个元素为NULL
  new_argv[argc + 2] = NULL;

  // 设置新的命令行参数数组的第二个元素为目标程序路径
  new_argv[2] = *target_path_p;
  // 设置新的命令行参数数组的第一个元素为"--"
  new_argv[1] = "--";

  // 现在我们需要找到afl-qemu-trace的二进制文件路径，并将其设置为新的命令行参数数组的第一个元素
  *target_path_p = new_argv[0] = find_afl_binary(own_loc, "afl-qemu-trace");

  // 返回新的命令行参数数组
  return new_argv;
}

/* Rewrite argv for Wine+QEMU. */

/*
 * 获取用于afl-wine-trace的命令行参数数组
 *
 * @param own_loc 当前执行文件的路径
 * @param target_path_p 目标程序路径的指针
 * @param argc 原始命令行参数的数量
 * @param argv 原始命令行参数数组
 *
 * @return 返回新的命令行参数数组，用于afl-wine-trace
 */
char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
  // 为新的命令行参数数组分配内存，大小为(argc + 2)个指针大小
  char **new_argv = ck_alloc(sizeof(char *) * (argc + 2));
  // 如果分配失败，则打印错误信息并退出
  if (unlikely(!new_argv)) { FATAL("Illegal amount of arguments specified"); }

  // 复制原始命令行参数到新的参数数组，从索引2开始
  memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1));
  // 设置新的命令行参数数组的最后一个元素为NULL
  new_argv[argc + 1] = NULL;

  // 设置新的命令行参数数组的第二个元素为目标程序路径
  new_argv[1] = *target_path_p;

  // 现在我们需要找到afl-wine-trace的二进制文件路径，并将其设置为新的命令行参数数组的第一个元素
  // 首先尝试找到afl-qemu-trace的二进制文件路径，但不使用它
  u8 *tmp = find_afl_binary(own_loc, "afl-qemu-trace");
  ck_free(tmp);  // 释放临时变量的内存
  // 找到afl-wine-trace的二进制文件路径
  *target_path_p = new_argv[0] = find_afl_binary(own_loc, "afl-wine-trace");

  // 返回新的命令行参数数组
  return new_argv;
}

/* Find binary, used by analyze, showmap, tmin
   @returns the path, allocating the string */

/*
 * 查找可执行的二进制文件路径
 *
 * @param fname 二进制文件名
 *
 * @return 返回可执行的二进制文件路径
 */
u8 *find_binary(u8 *fname) {
  // TODO: 将此函数与afl-fuzz-init.c中的check_binary函数合并

  u8 *env_path = NULL;     // 存储环境变量PATH的值
  u8 *target_path = NULL;  // 存储找到的二进制文件路径

  struct stat st;  // 用于存储文件状态信息

  // 如果没有提供二进制文件名，则打印错误信息并退出
  if (unlikely(!fname)) { FATAL("No binary supplied"); }

  // 如果二进制文件名包含路径分隔符'/'，或者环境变量PATH不存在
  if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
    // 直接使用二进制文件名作为目标路径
    target_path = ck_strdup(fname);

    // 检查目标路径的文件是否存在、是否为普通文件、是否可执行以及文件大小是否大于等于4
    if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
        !(st.st_mode & 0111) || st.st_size < 4) {
      ck_free(target_path);  // 释放目标路径的内存
      FATAL("Program '%s' not found or not executable",
            fname);  // 打印错误信息并退出
    }

  } else {
    // 遍历环境变量PATH中的每个目录
    while (env_path) {
      u8 *cur_elem,
          *delim =
              strchr(env_path, ':');  // 查找当前目录与下一个目录之间的分隔符':'

      if (delim) {
        // 分配内存用于存储当前目录路径
        cur_elem = ck_alloc(delim - env_path + 1);
        // 如果分配失败，则打印错误信息并退出
        if (unlikely(!cur_elem)) {
          FATAL(
              "Unexpected overflow when processing ENV. This should never "
              "had happened.");
        }

        // 复制当前目录路径到cur_elem
        memcpy(cur_elem, env_path, delim - env_path);
        delim++;  // 移动到下一个目录

      } else {
        // 如果没有找到分隔符，则直接复制环境变量PATH的值到cur_elem
        cur_elem = ck_strdup(env_path);
      }

      env_path = delim;  // 更新env_path为下一个目录的起始位置

      // 构建完整的二进制文件路径
      if (cur_elem[0]) {
        target_path = alloc_printf("%s/%s", cur_elem, fname);

      } else {
        target_path = ck_strdup(fname);
      }

      ck_free(cur_elem);  // 释放当前目录路径的内存

      // 检查构建的二进制文件路径是否存在、是否为普通文件、是否可执行以及文件大小是否大于等于4
      if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
          (st.st_mode & 0111) && st.st_size >= 4) {
        break;  // 如果找到符合条件的二进制文件，则退出循环
      }

      ck_free(target_path);  // 释放目标路径的内存
      target_path = NULL;    // 重置目标路径为NULL
    }

    // 如果遍历完环境变量PATH中的所有目录仍未找到符合条件的二进制文件
    if (!target_path) {
      FATAL("Program '%s' not found or not executable",
            fname);  // 打印错误信息并退出
    }
  }

  // 返回找到的可执行的二进制文件路径
  return target_path;
}

/*
 * 查找AFL相关的可执行文件或库文件路径
 *
 * @param own_loc 当前执行文件的路径
 * @param fname 要查找的AFL文件名
 *
 * @return 返回找到的AFL文件路径
 */
u8 *find_afl_binary(u8 *own_loc, u8 *fname) {
  u8 *afl_path = NULL;  // 存储环境变量AFL_PATH的值
  u8 *target_path;      // 存储找到的AFL文件路径
  u8 *own_copy;         // 当前执行文件路径的副本
  u8 *tmp;              // 临时变量，用于处理文件名
  int perm = X_OK;      // 默认权限为可执行权限

  // 如果文件名包含扩展名
  if ((tmp = strrchr(fname, '.'))) {
    // 如果扩展名为.so或.dylib，则将权限设置为可读权限
    if (!strcasecmp(tmp, ".so") || !strcasecmp(tmp, ".dylib")) { perm = R_OK; }
  }

  // 如果环境变量AFL_PATH存在
  if ((afl_path = getenv("AFL_PATH"))) {
    // 构建AFL文件路径
    target_path = alloc_printf("%s/%s", afl_path, fname);
    // 如果文件存在且具有指定权限，则返回文件路径
    if (!access(target_path, perm)) {
      return target_path;

    } else {
      ck_free(target_path);  // 释放文件路径的内存
    }
  }

  // 如果提供了当前执行文件的路径
  if (own_loc) {
    own_copy = ck_strdup(own_loc);     // 复制当前执行文件路径
    u8 *rsl = strrchr(own_copy, '/');  // 查找路径中的最后一个'/'

    if (rsl) {
      *rsl = 0;  // 将最后一个'/'替换为字符串终止符

      // 构建AFL文件路径
      target_path = alloc_printf("%s/%s", own_copy, fname);
      ck_free(own_copy);  // 释放当前执行文件路径副本的内存

      // 如果文件存在且具有指定权限，则返回文件路径
      if (!access(target_path, perm)) {
        return target_path;

      } else {
        ck_free(target_path);  // 释放文件路径的内存
      }

    } else {
      ck_free(own_copy);  // 释放当前执行文件路径副本的内存
    }
  }

  // 如果权限为可执行权限
  if (perm == X_OK) {
    // 构建AFL文件路径
    target_path = alloc_printf("%s/%s", BIN_PATH, fname);

  } else {
    // 构建AFL文件路径
    target_path = alloc_printf("%s/%s", AFL_PATH, fname);
  }

  // 如果文件存在且具有指定权限，则返回文件路径
  if (!access(target_path, perm)) {
    return target_path;

  } else {
    ck_free(target_path);  // 释放文件路径的内存
  }

  // 如果权限为可执行权限，则调用find_binary函数查找可执行文件
  if (perm == X_OK) {
    return find_binary(fname);

  } else {
    // 如果权限为可读权限且文件不存在，则打印错误信息并退出
    FATAL("Library '%s' not found", fname);
  }
}

/*
 * 解析AFL终止信号的数值
 *
 * @param numeric_signal_as_str 表示信号的字符串
 * @param default_signal 默认信号值
 *
 * @return 返回解析后的信号数值，如果解析失败则返回默认信号值
 */
int parse_afl_kill_signal(u8 *numeric_signal_as_str, int default_signal) {
  // 如果提供了表示信号的字符串且字符串非空
  if (numeric_signal_as_str && numeric_signal_as_str[0]) {
    char *endptr;  // 用于指向字符串中第一个非数字字符的位置
    u8    signal_code;  // 存储解析后的信号数值

    // 将字符串转换为无符号长整数，并获取转换后的数值和非数字字符的位置
    signal_code = (u8)strtoul(numeric_signal_as_str, &endptr, 10);

    /* Did we manage to parse the full string? */
    // 如果字符串未完全解析（即存在非数字字符）或未进行任何转换（即字符串不是数字）
    if (*endptr != '\0' || endptr == (char *)numeric_signal_as_str) {
      FATAL("Invalid signal name: %s",
            numeric_signal_as_str);  // 打印错误信息并退出

    } else {
      return signal_code;  // 返回解析后的信号数值
    }
  }

  // 如果未提供表示信号的字符串或字符串为空，则返回默认信号值
  return default_signal;
}

/*
 * 配置AFL终止信号
 *
 * @param fsrv 指向afl_forkserver_t结构体的指针，用于存储配置的终止信号
 * @param afl_kill_signal_env
 * AFL终止信号的环境变量值，如果为空则使用"AFL_KILL_SIGNAL"环境变量
 * @param afl_fsrv_kill_signal_env AFL fork
 * server终止信号的环境变量值，如果为空则使用"AFL_FORK_SERVER_KILL_SIGNAL"环境变量
 * @param default_server_kill_signal 默认的fork server终止信号
 */
void configure_afl_kill_signals(afl_forkserver_t *fsrv,
                                char             *afl_kill_signal_env,
                                char             *afl_fsrv_kill_signal_env,
                                int               default_server_kill_signal) {
  // 如果afl_kill_signal_env为空，则从"AFL_KILL_SIGNAL"环境变量获取AFL终止信号
  afl_kill_signal_env =
      afl_kill_signal_env ? afl_kill_signal_env : getenv("AFL_KILL_SIGNAL");
  // 如果afl_fsrv_kill_signal_env为空，则从"AFL_FORK_SERVER_KILL_SIGNAL"环境变量获取AFL
  // fork server终止信号
  afl_fsrv_kill_signal_env = afl_fsrv_kill_signal_env
                                 ? afl_fsrv_kill_signal_env
                                 : getenv("AFL_FORK_SERVER_KILL_SIGNAL");

  // 解析AFL终止信号，并将其存储在fsrv->child_kill_signal中
  fsrv->child_kill_signal = parse_afl_kill_signal(afl_kill_signal_env, SIGKILL);

  // 如果afl_kill_signal_env非空且afl_fsrv_kill_signal_env为空
  if (afl_kill_signal_env && !afl_fsrv_kill_signal_env) {
    /*
     * 为了向后兼容，将AFL终止信号的值设置为AFL fork server终止信号的值。
     * 但是，如果AFL_FORK_SERVER_KILL_SIGNAL已设置，则优先使用其值。
     */
    afl_fsrv_kill_signal_env = afl_kill_signal_env;
  }

  // 解析AFL fork server终止信号，并将其存储在fsrv->fsrv_kill_signal中
  fsrv->fsrv_kill_signal = parse_afl_kill_signal(afl_fsrv_kill_signal_env,
                                                 default_server_kill_signal);
}

/*
 * 计算三个无符号整数中的最小值
 *
 * @param a 第一个无符号整数
 * @param b 第二个无符号整数
 * @param c 第三个无符号整数
 *
 * @return 返回三个无符号整数中的最小值
 */
static inline unsigned int helper_min3(unsigned int a, unsigned int b,
                                       unsigned int c) {
  // 使用条件运算符比较三个无符号整数的大小
  // 如果a小于b，则比较a和c的大小，返回较小的值；否则，比较b和c的大小，返回较小的值
  return a < b ? (a < c ? a : c) : (b < c ? b : c);
}

// from
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C
/*
 * 计算两个字符串之间的Levenshtein距离
 *
 * Levenshtein距离是指将一个字符串转换为另一个字符串所需的最少单字符编辑操作（插入、删除或替换）次数。
 *
 * @param s1 第一个字符串
 * @param s2 第二个字符串
 *
 * @return 返回两个字符串之间的Levenshtein距离
 */
static int string_distance_levenshtein(char *s1, char *s2) {
  unsigned int s1len, s2len, x, y, lastdiag, olddiag;  // 变量声明
  s1len = strlen(s1);              // 获取第一个字符串的长度
  s2len = strlen(s2);              // 获取第二个字符串的长度
  unsigned int column[s1len + 1];  // 用于存储Levenshtein距离计算过程中的列值
  column[s1len] = 1;               // 初始化列值数组的最后一个元素

  // 初始化列值数组的第一列，表示将空字符串转换为s1所需的编辑操作次数
  for (y = 1; y <= s1len; y++)
    column[y] = y;

  // 计算Levenshtein距离
  for (x = 1; x <= s2len; x++) {
    column[0] =
        x;  // 初始化列值数组的第一行，表示将空字符串转换为s2所需的编辑操作次数
    for (y = 1, lastdiag = x - 1; y <= s1len; y++) {
      olddiag = column[y];  // 保存当前列值
      // 计算当前位置的Levenshtein距离，选择插入、删除或替换操作中代价最小的
      column[y] = helper_min3(column[y] + 1, column[y - 1] + 1,
                              lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1));
      lastdiag = olddiag;  // 更新lastdiag值
    }
  }

  // 返回两个字符串之间的Levenshtein距离，即列值数组的最后一个元素
  return column[s1len];
}

#define ENV_SIMILARITY_TRESHOLD 3

/*
 * 打印建议的环境变量名称
 *
 * 当用户输入错误的环境变量名称时，此函数会根据Levenshtein距离算法，找出与之相似的正确环境变量名称，并打印出来。
 *
 * @param mispelled_env 用户输入的错误环境变量名称
 */
void print_suggested_envs(char *mispelled_env) {
  // 计算环境变量名称的长度，去掉"AFL_"前缀
  size_t env_name_len = strcspn(mispelled_env, "=") - 4;
  // 分配内存存储环境变量名称
  char *env_name = ck_alloc(env_name_len + 1);
  // 复制环境变量名称到分配的内存中
  memcpy(env_name, mispelled_env + 4, env_name_len);

  // 分配内存存储环境变量是否已被打印的标记
  char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *));
  // 标记是否找到相似的环境变量
  int found = 0;

  // 遍历所有AFL环境变量
  int j;
  for (j = 0; afl_environment_variables[j] != NULL; ++j) {
    // 获取当前AFL环境变量的名称
    char *afl_env = afl_environment_variables[j] + 4;
    // 计算当前AFL环境变量名称与用户输入的环境变量名称之间的Levenshtein距离
    int distance = string_distance_levenshtein(afl_env, env_name);
    // 如果Levenshtein距离小于相似度阈值且当前AFL环境变量未被打印，则打印当前AFL环境变量名称
    if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
      SAYF("Did you mean %s?\n", afl_environment_variables[j]);
      seen[j] = 1;  // 标记当前AFL环境变量已被打印
      found = 1;    // 标记已找到相似的环境变量
    }
  }

  // 如果已找到相似的环境变量，则跳转到清理阶段
  if (found) goto cleanup;

  // 如果未找到相似的环境变量，则尝试去掉用户输入的环境变量名称中的下划线，再次进行匹配
  for (j = 0; afl_environment_variables[j] != NULL; ++j) {
    // 获取当前AFL环境变量的名称
    char *afl_env = afl_environment_variables[j] + 4;
    // 计算当前AFL环境变量名称的长度
    size_t afl_env_len = strlen(afl_env);
    // 分配内存存储去掉下划线后的AFL环境变量名称
    char *reduced = ck_alloc(afl_env_len + 1);

    // 初始化去掉下划线后的AFL环境变量名称的起始位置
    size_t start = 0;
    // 遍历当前AFL环境变量名称中的每个下划线
    while (start < afl_env_len) {
      // 计算当前下划线的位置
      size_t end = start + strcspn(afl_env + start, "_") + 1;
      // 复制当前AFL环境变量名称中去掉下划线的部分到分配的内存中
      memcpy(reduced, afl_env, start);
      if (end < afl_env_len) {
        memcpy(reduced + start, afl_env + end, afl_env_len - end);
      }

      // 如果去掉下划线后的AFL环境变量名称长度小于当前AFL环境变量名称的长度，则设置字符串终止符
      if (afl_env_len + start >= end) {
        reduced[afl_env_len - end + start] = 0;
      }

      // 计算去掉下划线后的AFL环境变量名称与用户输入的环境变量名称之间的Levenshtein距离
      int distance = string_distance_levenshtein(reduced, env_name);
      // 如果Levenshtein距离小于相似度阈值且当前AFL环境变量未被打印，则打印当前AFL环境变量名称
      if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
        SAYF("Did you mean %s?\n", afl_environment_variables[j]);
        seen[j] = 1;  // 标记当前AFL环境变量已被打印
        found = 1;    // 标记已找到相似的环境变量
      }

      // 更新去掉下划线后的AFL环境变量名称的起始位置
      start = end;
    };

    // 释放去掉下划线后的AFL环境变量名称的内存
    ck_free(reduced);
  }

  // 如果未找到相似的环境变量，则尝试去掉AFL环境变量名称中的下划线，再次进行匹配
  if (found) goto cleanup;

  // 分配内存存储去掉下划线后的用户输入的环境变量名称
  char *reduced = ck_alloc(env_name_len + 1);
  // 初始化去掉下划线后的用户输入的环境变量名称的起始位置
  size_t start = 0;
  // 遍历用户输入的环境变量名称中的每个下划线
  while (start < env_name_len) {
    // 计算当前下划线的位置
    size_t end = start + strcspn(env_name + start, "_") + 1;
    // 复制用户输入的环境变量名称中去掉下划线的部分到分配的内存中
    memcpy(reduced, env_name, start);
    if (end < env_name_len)
      memcpy(reduced + start, env_name + end, env_name_len - end);
    // 设置字符串终止符
    reduced[env_name_len - end + start] = 0;

    // 遍历所有AFL环境变量
    for (j = 0; afl_environment_variables[j] != NULL; ++j) {
      // 计算当前AFL环境变量名称与去掉下划线后的用户输入的环境变量名称之间的Levenshtein距离
      int distance = string_distance_levenshtein(
          afl_environment_variables[j] + 4, reduced);
      // 如果Levenshtein距离小于相似度阈值且当前AFL环境变量未被打印，则打印当前AFL环境变量名称
      if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
        SAYF("Did you mean %s?\n", afl_environment_variables[j]);
        seen[j] = 1;  // 标记当前AFL环境变量已被打印
      }
    }

    // 更新去掉下划线后的用户输入的环境变量名称的起始位置
    start = end;
  };

  // 释放去掉下划线后的用户输入的环境变量名称的内存
  ck_free(reduced);

cleanup:
  // 释放环境变量名称的内存
  ck_free(env_name);
  // 释放环境变量是否已被打印的标记的内存
  ck_free(seen);
}

/*
 * 检查环境变量
 *
 * 检查环境变量列表中是否存在拼写错误的AFL环境变量，并打印警告信息。
 *
 * @param envp 环境变量列表
 */
void check_environment_vars(char **envp) {
  // 如果be_quiet标志为真，则不执行检查，直接返回
  if (be_quiet) { return; }

  int index = 0, issue_detected = 0;  // 初始化索引变量和问题检测标志
  char *env, *val,
      *ignore = getenv(
          "AFL_IGNORE_UNKNOWN_ENVS");  // 获取环境变量"AFL_IGNORE_UNKNOWN_ENVS"的值

  // 遍历环境变量列表
  while ((env = envp[index++]) != NULL) {
    // 检查是否存在拼写错误的AFL环境变量前缀
    if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 ||
        strncmp(env, "__ALF", 5) == 0 || strncmp(env, "_AFL", 4) == 0 ||
        strncmp(env, "__AFL", 5) == 0) {
      WARNF("Potentially mistyped AFL environment variable: %s",
            env);          // 打印警告信息
      issue_detected = 1;  // 标记检测到问题

    } else if (strncmp(env, "AFL_", 4) == 0) {  // 如果环境变量以"AFL_"开头
      int i = 0, match = 0;  // 初始化索引变量和匹配标志

      // 检查环境变量是否是已知的AFL环境变量
      while (match == 0 && afl_environment_variables[i] != NULL) {
        if (strncmp(env, afl_environment_variables[i],
                    strlen(afl_environment_variables[i])) == 0 &&
            env[strlen(afl_environment_variables[i])] == '=') {
          match = 1;  // 标记找到匹配的环境变量

          // 获取环境变量的值
          if ((val = getenv(afl_environment_variables[i])) && !*val) {
            WARNF(
                "AFL environment variable %s defined but is empty, this can "
                "lead to unexpected consequences",
                afl_environment_variables[i]);  // 打印警告信息
            issue_detected = 1;                 // 标记检测到问题
          }

        } else {
          i++;  // 移动到下一个环境变量
        }
      }

      i = 0;  // 重置索引变量
      // 检查环境变量是否是已弃用的AFL环境变量
      while (match == 0 && afl_environment_deprecated[i] != NULL) {
        if (strncmp(env, afl_environment_deprecated[i],
                    strlen(afl_environment_deprecated[i])) == 0 &&
            env[strlen(afl_environment_deprecated[i])] == '=') {
          match = 1;  // 标记找到匹配的环境变量

          WARNF("AFL environment variable %s is deprecated!",
                afl_environment_deprecated[i]);  // 打印警告信息
          issue_detected = 1;                    // 标记检测到问题

        } else {
          i++;  // 移动到下一个环境变量
        }
      }

      // 如果未找到匹配的环境变量且未设置"AFL_IGNORE_UNKNOWN_ENVS"环境变量
      if (match == 0 && !ignore) {
        WARNF("Mistyped AFL environment variable: %s", env);  // 打印警告信息
        issue_detected = 1;  // 标记检测到问题

        print_suggested_envs(env);  // 打印建议的环境变量名称
      }
    }
  }

  // 如果检测到问题，则暂停2秒
  if (issue_detected) { sleep(2); }
}

/*
 * 获取AFL环境变量的值
 *
 * @param env AFL环境变量的名称
 *
 * @return 返回环境变量的值，如果环境变量未定义或值为空，则返回NULL
 */
char *get_afl_env(char *env) {
  char *val;  // 用于存储环境变量的值

  // 获取环境变量的值
  if ((val = getenv(env))) {
    // 如果环境变量的值非空
    if (*val) {
      // 如果be_quiet标志为假，则打印环境变量的名称和值
      if (!be_quiet) {
        OKF("Enabled environment variable %s with value %s", env, val);
      }

      // 返回环境变量的值
      return val;
    }
  }

  // 如果环境变量未定义或值为空，则返回NULL
  return NULL;
}

/*
 * 解析环境变量字符串并设置环境变量
 *
 * @param env_str 包含环境变量定义的字符串
 *
 * @return 如果成功设置至少一个环境变量，则返回true；否则返回false
 */
bool extract_and_set_env(u8 *env_str) {
  // 如果输入的环境变量字符串为空，则返回false
  if (!env_str) { return false; }

  bool ret = false;  // 默认返回值为false

  // 复制输入的环境变量字符串
  u8 *p = ck_strdup(env_str);
  u8 *end = p + strlen((char *)p);  // 获取字符串的结束位置
  u8 *rest = p;                     // 初始化剩余字符串的起始位置

  u8 closing_sym = ' ';  // 用于标记字符串值的结束符号，默认为空格
  u8 c;                  // 用于存储当前字符

  size_t num_pairs = 0;  // 记录成功设置的环境变量对的数量

  // 遍历环境变量字符串
  while (rest < end) {
    // 跳过空格字符
    while (*rest == ' ') {
      rest++;
    }

    // 如果剩余字符串不足两个字符，则退出循环
    if (rest + 1 >= end) break;

    u8 *key = rest;  // 获取环境变量的名称
    // 环境变量名称不能以数字或'='开头
    if (*key == '=' || (*key >= '0' && *key <= '9')) { goto free_and_return; }

    // 遍历环境变量名称，直到遇到'='或空格
    while (rest < end && *rest != '=' && *rest != ' ') {
      c = *rest;
      // 环境变量名称只能包含字母、数字和下划线
      if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
          (c < '0' || c > '9') && c != '_') {
        goto free_and_return;
      }

      rest++;
    }

    // 如果没有找到'='，则退出
    if (*rest != '=') { goto free_and_return; }

    *rest = '\0';  // 结束环境变量名称

    rest += 1;
    // 如果剩余字符串为空或以空格开头，则退出
    if (rest >= end || *rest == ' ') { goto free_and_return; }

    u8 *val = rest;  // 获取环境变量的值
    if (*val == '\'' || *val == '"') {
      // 如果值以单引号或双引号开头，则记录结束符号，并跳过引号
      closing_sym = *val;
      val += 1;
      rest += 1;
      // 如果剩余字符串为空，则退出
      if (rest >= end) { goto free_and_return; }

    } else {
      closing_sym = ' ';  // 如果值不以引号开头，则结束符号为空格
    }

    // 遍历环境变量值，直到遇到结束符号
    while (rest < end && *rest != closing_sym) {
      rest++;
    }

    // 如果值以引号开头但没有找到对应的结束引号，则退出
    if (closing_sym != ' ' && *rest != closing_sym) { goto free_and_return; }

    *rest = '\0';  // 结束环境变量值

    rest += 1;
    num_pairs++;          // 增加成功设置的环境变量对的数量
    setenv(key, val, 1);  // 设置环境变量
  }

  // 如果成功设置至少一个环境变量，则返回true
  if (num_pairs) { ret = true; }

free_and_return:
  ck_free(p);  // 释放复制的环境变量字符串
  return ret;  // 返回结果
}

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

/*
 * 读取位图文件
 *
 * @param fname 位图文件的路径
 * @param map 用于存储位图数据的缓冲区
 * @param len 位图数据的长度
 */
void read_bitmap(u8 *fname, u8 *map, size_t len) {
  s32 fd = open(fname, O_RDONLY);  // 打开位图文件，只读模式

  if (fd < 0) {
    PFATAL("Unable to open '%s'", fname);
  }  // 如果打开失败，则打印错误信息并退出

  ck_read(fd, map, len, fname);  // 从文件中读取位图数据到缓冲区

  close(fd);  // 关闭文件描述符
}

/* Get unix time in milliseconds */

/*
 * 获取当前时间（以毫秒为单位）
 *
 * @return 返回当前时间的毫秒值
 */
inline u64 get_cur_time(void) {
  struct timeval  tv;  // 用于存储当前时间的结构体
  struct timezone tz;  // 用于存储时区信息的结构体

  // 使用gettimeofday函数获取当前时间，注意不要替换为clock_gettime函数
  gettimeofday(&tv, &tz);

  // 计算当前时间的毫秒值，tv_sec表示秒，tv_usec表示微秒
  return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);
}

/* Get unix time in microseconds */

/*
 * 获取当前时间（以微秒为单位）
 *
 * @return 返回当前时间的微秒值
 */
inline u64 get_cur_time_us(void) {
  struct timeval  tv;  // 用于存储当前时间的结构体
  struct timezone tz;  // 用于存储时区信息的结构体

  // 使用gettimeofday函数获取当前时间，注意不要替换为clock_gettime函数
  gettimeofday(&tv, &tz);

  // 计算当前时间的微秒值，tv_sec表示秒，tv_usec表示微秒
  return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}

/* Describe integer. The buf should be
   at least 6 bytes to fit all ints we randomly see.
   Will return buf for convenience. */

/*
 * 将整数转换为字符串表示，同时根据数值大小使用合适的单位（k、M、G、T等）
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param len 缓冲区的长度
 * @param val 要转换的整数值
 *
 * @return 返回转换后的字符串
 */
u8 *stringify_int(u8 *buf, size_t len, u64 val) {
  // 定义一个宏，用于检查数值是否在某个范围内，并将其格式化为相应的字符串表示
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast)     \
  do {                                                     \
    if (val < (_divisor) * (_limit_mult)) {                \
      snprintf(buf, len, _fmt, ((_cast)val) / (_divisor)); \
      return buf;                                          \
    }                                                      \
                                                           \
  } 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.99T
  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+
  strncpy(buf, "infty", len);
  buf[len - 1] = '\0';  // 确保字符串以空字符结尾

  return buf;
}

/* Describe float. Similar as int. */

/*
 * 将浮点数转换为字符串表示，根据数值大小使用合适的格式
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param len 缓冲区的长度
 * @param val 要转换的浮点数值
 *
 * @return 返回转换后的字符串
 */
u8 *stringify_float(u8 *buf, size_t len, double val) {
  // 如果数值小于99.995，则使用两位小数的格式化字符串表示
  if (val < 99.995) {
    snprintf(buf, len, "%0.02f", val);

    // 如果数值小于999.95，则使用一位小数的格式化字符串表示
  } else if (val < 999.95) {
    snprintf(buf, len, "%0.01f", val);

    // 如果数值是NaN（非数字）或无穷大，则将其表示为"inf"
  } else if (unlikely(isnan(val) || isinf(val))) {
    strcpy(buf, "inf");

    // 对于其他较大的数值，将其转换为整数并使用stringify_int函数进行格式化
  } else {
    stringify_int(buf, len, (u64)val);
  }

  // 返回转换后的字符串
  return buf;
}

/* Describe integer as memory size. */

/*
 * 将内存大小转换为字符串表示，使用合适的单位（B、kB、MB、GB、TB等）
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param len 缓冲区的长度
 * @param val 要转换的内存大小值
 *
 * @return 返回转换后的字符串
 */
u8 *stringify_mem_size(u8 *buf, size_t len, u64 val) {
  // 定义一个宏，用于检查内存大小是否在某个范围内，并将其格式化为相应的字符串表示
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast)     \
  do {                                                     \
    if (val < (_divisor) * (_limit_mult)) {                \
      snprintf(buf, len, _fmt, ((_cast)val) / (_divisor)); \
      return buf;                                          \
    }                                                      \
  } while (0)

  // 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.99T 太字节
  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);

  // 取消CHK_FORMAT宏定义
#undef CHK_FORMAT

  // 100T+ 太字节以上
  strncpy(buf, "infty", len - 1);
  buf[len - 1] = '\0';  // 确保字符串以空字符结尾

  return buf;
}

/* Describe time delta as string.
   Returns a pointer to buf for convenience. */

/*
 * 将时间差转换为字符串表示
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param len 缓冲区的长度
 * @param cur_ms 当前时间的毫秒值
 * @param event_ms 事件发生的时间的毫秒值
 *
 * @return 返回转换后的时间差字符串
 */
u8 *stringify_time_diff(u8 *buf, size_t len, u64 cur_ms, u64 event_ms) {
  // 如果事件发生的时间为0，则表示尚未看到事件
  if (!event_ms) {
    snprintf(buf, len, "none seen yet");

  } else {
    u64 delta;                           // 时间差的毫秒值
    s32 t_d, t_h, t_m, t_s;              // 天、小时、分钟、秒
    u8 val_buf[STRINGIFY_VAL_SIZE_MAX];  // 用于存储天数的字符串表示

    // 计算时间差的毫秒值
    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;

    // 将天数转换为字符串表示
    stringify_int(val_buf, sizeof(val_buf), t_d);
    // 将时间差格式化为字符串表示
    snprintf(buf, len, "%s days, %d hrs, %d min, %d sec", val_buf, t_h, t_m,
             t_s);
  }

  // 返回转换后的时间差字符串
  return buf;
}

/* Unsafe Describe integer. The buf sizes are not checked.
   This is unsafe but fast.
   Will return buf for convenience. */

/*
 * 将整数转换为字符串表示，同时根据数值大小使用合适的单位（k、M、G、T等）
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param val 要转换的整数值
 *
 * @return 返回转换后的字符串
 */
u8 *u_stringify_int(u8 *buf, u64 val) {
  // 定义一个宏，用于检查数值是否在某个范围内，并将其格式化为相应的字符串表示
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) \
  do {                                                 \
    if (val < (_divisor) * (_limit_mult)) {            \
      sprintf(buf, _fmt, ((_cast)val) / (_divisor));   \
      return buf;                                      \
    }                                                  \
                                                       \
  } 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.99T
  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(buf, "infty");  // 对于100T以上的数值，使用"infty"表示

  return buf;
}

/* Unsafe describe float. Similar as unsafe int. */

/*
 * 将浮点数转换为字符串表示，根据数值大小使用合适的格式
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param val 要转换的浮点数值
 *
 * @return 返回转换后的字符串
 */
u8 *u_stringify_float(u8 *buf, double val) {
  // 如果数值小于99.995，则使用两位小数的格式化字符串表示
  if (val < 99.995) {
    sprintf(buf, "%0.02f", val);

    // 如果数值小于999.95，则使用一位小数的格式化字符串表示
  } else if (val < 999.95) {
    sprintf(buf, "%0.01f", val);

    // 如果数值是NaN（非数字）或无穷大，则将其表示为"infinite"
  } else if (unlikely(isnan(val) || isinf(val))) {
    strcpy(buf, "infinite");

    // 对于其他较大的数值，将其转换为整数并使用u_stringify_int函数进行格式化
  } else {
    return u_stringify_int(buf, (u64)val);
  }

  // 返回转换后的字符串
  return buf;
}

/* Unsafe describe integer as memory size. */

/*
 * 将内存大小转换为字符串表示，使用合适的单位（B、kB、MB、GB、TB等）
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param val 要转换的内存大小值
 *
 * @return 返回转换后的字符串
 */
u8 *u_stringify_mem_size(u8 *buf, u64 val) {
  // 定义一个宏，用于检查内存大小是否在某个范围内，并将其格式化为相应的字符串表示
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) \
  do {                                                 \
    if (val < (_divisor) * (_limit_mult)) {            \
      sprintf(buf, _fmt, ((_cast)val) / (_divisor));   \
      return buf;                                      \
    }                                                  \
  } while (0)

  // 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.99T 太字节
  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);

  // 取消CHK_FORMAT宏定义
#undef CHK_FORMAT

  // 100T+ 太字节以上
  strcpy(buf, "infty");  // 对于100T以上的数值，使用"infty"表示

  return buf;
}

/* Unsafe describe time delta as string.
   Returns a pointer to buf for convenience. */

/*
 * 将时间差转换为字符串表示
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param cur_ms 当前时间的毫秒值
 * @param event_ms 事件发生的时间的毫秒值
 *
 * @return 返回转换后的时间差字符串
 */
u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) {
  // 如果事件发生的时间为0，则表示尚未看到事件
  if (!event_ms) {
    sprintf(buf, "none seen yet");

  } else {
    u64 delta;                           // 时间差的毫秒值
    s32 t_d, t_h, t_m, t_s;              // 天、小时、分钟、秒
    u8 val_buf[STRINGIFY_VAL_SIZE_MAX];  // 用于存储天数的字符串表示

    // 计算时间差的毫秒值
    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;

    // 将天数转换为字符串表示
    u_stringify_int(val_buf, t_d);
    // 将时间差格式化为字符串表示
    sprintf(buf, "%s days, %d hrs, %d min, %d sec", val_buf, t_h, t_m, t_s);
  }

  // 返回转换后的时间差字符串
  return buf;
}

/* Unsafe describe time delta as simple string.
   Returns a pointer to buf for convenience. */

/*
 * 将时间差转换为简单的字符串表示，格式为“天:小时:分钟:秒”
 *
 * @param buf 用于存储转换后的字符串的缓冲区
 * @param cur_ms 当前时间的毫秒值
 * @param event_ms 事件发生的时间的毫秒值
 *
 * @return 返回转换后的时间差字符串
 */
u8 *u_simplestring_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) {
  // 如果事件发生的时间为0，则表示尚未看到事件，将字符串设置为"00:00:00"
  if (!event_ms) {
    sprintf(buf, "00:00:00");

  } else {
    u64 delta;               // 时间差的毫秒值
    s32 t_d, t_h, t_m, t_s;  // 天、小时、分钟、秒

    // 计算时间差的毫秒值
    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(buf, "%d:%02d:%02d:%02d", t_d, t_h, t_m, t_s);
  }

  // 返回转换后的时间差字符串
  return buf;
}

/* Reads the map size from ENV */
/*
 * 获取共享内存映射的大小
 *
 * @return 返回共享内存映射的大小
 */
u32 get_map_size(void) {
  uint32_t map_size = DEFAULT_SHMEM_SIZE;  // 默认共享内存映射大小
  char    *ptr;  // 用于存储环境变量值的指针

  // 尝试从环境变量AFL_MAP_SIZE或AFL_MAPSIZE获取共享内存映射大小
  if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) {
    map_size = atoi(ptr);  // 将环境变量值转换为整数
    // 检查共享内存映射大小是否合法
    if (!map_size || map_size > (1 << 29)) {
      FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 64U,
            1U << 29);  // 如果不合法，则打印错误信息并退出
    }

    // 确保共享内存映射大小是64的倍数
    if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); }

  } else if (getenv("AFL_SKIP_BIN_CHECK")) {
    // 如果设置了环境变量AFL_SKIP_BIN_CHECK，则使用MAP_SIZE作为共享内存映射大小
    map_size = MAP_SIZE;
  }

  // 返回共享内存映射的大小
  return map_size;
}

/* Create a stream file */

/*
 * 创建一个文件，并返回一个指向该文件的FILE指针
 *
 * @param fn 要创建的文件的路径
 *
 * @return 返回一个指向创建的文件的FILE指针
 */
FILE *create_ffile(u8 *fn) {
  s32   fd;  // 文件描述符
  FILE *f;   // FILE指针

  // 打开文件，如果文件不存在则创建，如果文件已存在则截断为零长度
  fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_PERMISSION);

  // 如果打开文件失败，则打印错误信息并退出
  if (fd < 0) { PFATAL("Unable to create '%s'", fn); }

  // 将文件描述符转换为FILE指针，以便使用标准C I/O函数
  f = fdopen(fd, "w");

  // 如果转换失败，则打印错误信息并退出
  if (!f) { PFATAL("fdopen() failed"); }

  // 返回指向创建的文件的FILE指针
  return f;
}

/* Create a file */

/*
 * 创建一个文件，并返回文件描述符
 *
 * @param fn 要创建的文件的路径
 *
 * @return 返回创建的文件的文件描述符
 */
s32 create_file(u8 *fn) {
  s32 fd;  // 文件描述符

  // 打开文件，如果文件不存在则创建，如果文件已存在则截断为零长度
  fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_PERMISSION);

  // 如果打开文件失败，则打印错误信息并退出
  if (fd < 0) { PFATAL("Unable to create '%s'", fn); }

  // 返回创建的文件的文件描述符
  return fd;
}

#ifdef __linux__

/* Nyx requires a tmp workdir to access specific files (such as mmapped files,
 * etc.). This helper function basically creates both a path to a tmp workdir
 * and the workdir itself. If the environment variable TMPDIR is set, we use
 * that as the base directory, otherwise we use /tmp. */
/*
 * 创建Nyx的临时工作目录
 *
 * Nyx需要一个临时工作目录来访问特定文件（如内存映射文件等）。此辅助函数基本上创建了临时工作目录的路径和工作目录本身。
 * 如果环境变量TMPDIR已设置，我们将其作为基础目录；否则，我们使用/tmp。
 *
 * @return 返回创建的Nyx临时工作目录的路径
 */
char *create_nyx_tmp_workdir(void) {
  char *tmpdir = getenv("TMPDIR");  // 获取环境变量TMPDIR的值

  // 如果TMPDIR未设置，则使用默认的临时目录/tmp
  if (!tmpdir) { tmpdir = "/tmp"; }

  // 创建Nyx临时工作目录的路径，格式为/tmp/.nyx_tmp_<pid>/
  char *nyx_out_dir_path =
      alloc_printf("%s/.nyx_tmp_%d/", tmpdir, (u32)getpid());

  // 创建Nyx临时工作目录，权限为0700（仅所有者可读写执行）
  if (mkdir(nyx_out_dir_path, 0700)) { PFATAL("Unable to create nyx workdir"); }

  // 返回创建的Nyx临时工作目录的路径
  return nyx_out_dir_path;
}

/* Vice versa, we remove the tmp workdir for nyx with this helper function. */
/*
 * 删除Nyx的临时工作目录
 *
 * @param fsrv 指向afl_forkserver_t结构体的指针，包含Nyx处理程序的相关信息
 * @param nyx_out_dir_path Nyx临时工作目录的路径
 */
void remove_nyx_tmp_workdir(afl_forkserver_t *fsrv, char *nyx_out_dir_path) {
  // 构建Nyx工作目录的完整路径
  char *workdir_path = alloc_printf("%s/workdir", nyx_out_dir_path);

  // 检查工作目录是否存在且可读
  if (access(workdir_path, R_OK) == 0) {
    // 调用Nyx处理程序的函数来删除工作目录
    if (fsrv->nyx_handlers->nyx_remove_work_dir(workdir_path) != true) {
      WARNF("Unable to remove nyx workdir (%s)",
            workdir_path);  // 如果删除失败，打印警告信息
    }
  }

  // 尝试删除Nyx临时工作目录
  if (rmdir(nyx_out_dir_path)) {
    WARNF("Unable to remove nyx workdir (%s)",
          nyx_out_dir_path);  // 如果删除失败，打印警告信息
  }

  // 释放分配的内存
  ck_free(workdir_path);
  ck_free(nyx_out_dir_path);
}

#endif
