From f823fe6219f888ff4245d117cdf6a1221c8f347e Mon Sep 17 00:00:00 2001 From: zhongfengrong <1363383697@qq.com> Date: Wed, 8 Jan 2025 20:46:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=A0=E7=9A=84=E6=8F=90=E4=BA=A4=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/afl-cmin | 330 +++++++++---------- src/afl-plot | 159 ++++----- src/alloc-inl.h | 858 ++++++++++++++++++++++++++++++++++++------------ src/debug.h | 188 ++++++----- 4 files changed, 980 insertions(+), 555 deletions(-) diff --git a/src/afl-cmin b/src/afl-cmin index e89b895..a28a24e 100644 --- a/src/afl-cmin +++ b/src/afl-cmin @@ -1,89 +1,91 @@ #!/usr/bin/env bash # -# american fuzzy lop - corpus minimization tool +# American Fuzzy Lop - 语料库最小化工具 # --------------------------------------------- # -# Written and maintained by Michal Zalewski +# 作者和维护者:Michal Zalewski # -# Copyright 2014, 2015 Google LLC All rights reserved. +# 版权所有 2014, 2015 Google LLC 保留所有权利。 # -# 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: +# 根据 Apache 许可证 2.0 版("许可证")授权; +# 除非符合许可证的规定,否则您不得使用此文件。 +# 您可以从以下网址获取许可证的副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# This tool tries to find the smallest subset of files in the input directory -# that still trigger the full range of instrumentation data points seen in -# the starting corpus. This has two uses: +# 此工具尝试查找输入目录中最小的文件子集, +# 该子集仍然触发启动语料库中看到的所有仪器数据点。 +# 这有两个用途: # -# - Screening large corpora of input files before using them as a seed for -# afl-fuzz. The tool will remove functionally redundant files and likely -# leave you with a much smaller set. +# - 在将大的输入文件用作 afl-fuzz 的种子之前筛选。 +# 该工具将删除功能上冗余的文件,并可能 +# 留下一个更小的集合。 # -# (In this case, you probably also want to consider running afl-tmin on -# the individual files later on to reduce their size.) +# (在这种情况下,您可能还想考虑稍后对 +# 各个文件运行 afl-tmin 以减少其大小。) # -# - Minimizing the corpus generated organically by afl-fuzz, perhaps when -# planning to feed it to more resource-intensive tools. The tool achieves -# this by removing all entries that used to trigger unique behaviors in the -# past, but have been made obsolete by later finds. +# - 最小化由 afl-fuzz 自然生成的语料库, +# 可能在计划将其供给更多资源密集型工具时。 +# 该工具通过删除所有曾经触发独特行为的条目实现此目的, +# 但这些条目已被后来的结果取代。 # -# Note that the tool doesn't modify the files themselves. For that, you want -# afl-tmin. +# 请注意,该工具不会修改文件本身。 +# 对于此,您希望使用 afl-tmin。 # -# This script must use bash because other shells may have hardcoded limits on -# array sizes. +# 此脚本必须使用 bash,因为其他 shell 可能对 +# 数组大小有硬编码限制。 # -echo "corpus minimization tool for afl-fuzz by " +echo "为 afl-fuzz 提供的语料库最小化工具 " # 输出程序名称 echo ######### -# SETUP # +# 设置 # ######### -# Process command-line options... +# 处理命令行选项... -MEM_LIMIT=100 -TIMEOUT=none +MEM_LIMIT=100 # 内存限制初始值为 100 MB +TIMEOUT=none # 超时初始值为无 +# 取消设置以下变量 unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \ AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE +# 解析命令行选项 while getopts "+i:o:f:m:t:eQC" opt; do case "$opt" in - "i") + "i") # 输入目录选项 IN_DIR="$OPTARG" ;; - "o") + "o") # 输出目录选项 OUT_DIR="$OPTARG" ;; - "f") + "f") # 从中读取的模糊程序位置(标准输入) STDIN_FILE="$OPTARG" ;; - "m") + "m") # 内存限制 MEM_LIMIT="$OPTARG" MEM_LIMIT_GIVEN=1 ;; - "t") + "t") # 超时时间 TIMEOUT="$OPTARG" ;; - "e") + "e") # 额外参数 EXTRA_PAR="$EXTRA_PAR -e" ;; - "C") + "C") # 仅保留崩溃输入 export AFL_CMIN_CRASHES_ONLY=1 ;; - "Q") + "Q") # 使用仅二进制的仪器(QEMU 模式) EXTRA_PAR="$EXTRA_PAR -Q" test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250 QEMU_MODE=1 ;; - "?") + "?") # 无效选项 exit 1 ;; @@ -91,84 +93,84 @@ while getopts "+i:o:f:m:t:eQC" opt; do done -shift $((OPTIND-1)) +shift $((OPTIND-1)) # 移动位置参数 -TARGET_BIN="$1" +TARGET_BIN="$1" # 目标二进制文件 +# 检查必需参数是否缺失 if [ "$TARGET_BIN" = "" -o "$IN_DIR" = "" -o "$OUT_DIR" = "" ]; then + # 输出用法信息到标准错误 cat 1>&2 <<_EOF_ -Usage: $0 [ options ] -- /path/to/target_app [ ... ] +使用: $0 [选项] -- /path/to/target_app [ ... ] -Required parameters: +所需参数: - -i dir - input directory with the starting corpus - -o dir - output directory for minimized files + -i dir - 包含起始语料库的输入目录 + -o dir - 最小化文件的输出目录 -Execution control settings: +执行控制设置: - -f file - location read by the fuzzed program (stdin) - -m megs - memory limit for child process ($MEM_LIMIT MB) - -t msec - run time limit for child process (none) - -Q - use binary-only instrumentation (QEMU mode) + -f file - 由模糊程序读取的位置(标准输入) + -m megs - 子进程的内存限制($MEM_LIMIT MB) + -t msec - 子进程的运行时间限制(无) + -Q - 使用仅二进制的仪器(QEMU 模式) -Minimization settings: +最小化设置: - -C - keep crashing inputs, reject everything else - -e - solve for edge coverage only, ignore hit counts + -C - 保留崩溃输入,拒绝其他所有内容 + -e - 仅解决边缘覆盖,忽略命中计数 -For additional tips, please consult docs/README. +有关其他提示,请参阅 docs/README。 _EOF_ exit 1 fi -# Do a sanity check to discourage the use of /tmp, since we can't really -# handle this safely from a shell script. +# 进行完整性检查,避免使用 /tmp,因为我们无法安全处理它。 if [ "$AFL_ALLOW_TMP" = "" ]; then - echo "$IN_DIR" | grep -qE '^(/var)?/tmp/' + echo "$IN_DIR" | grep -qE '^(/var)?/tmp/' # 检查输入目录是否在/tmp T1="$?" - echo "$TARGET_BIN" | grep -qE '^(/var)?/tmp/' + echo "$TARGET_BIN" | grep -qE '^(/var)?/tmp/' # 检查目标二进制文件是否在/tmp T2="$?" - echo "$OUT_DIR" | grep -qE '^(/var)?/tmp/' + echo "$OUT_DIR" | grep -qE '^(/var)?/tmp/' # 检查输出目录是否在/tmp T3="$?" - echo "$STDIN_FILE" | grep -qE '^(/var)?/tmp/' + echo "$STDIN_FILE" | grep -qE '^(/var)?/tmp/' # 检查标准输入文件是否在/tmp T4="$?" - echo "$PWD" | grep -qE '^(/var)?/tmp/' + echo "$PWD" | grep -qE '^(/var)?/tmp/' # 检查当前工作目录是否在/tmp T5="$?" if [ "$T1" = "0" -o "$T2" = "0" -o "$T3" = "0" -o "$T4" = "0" -o "$T5" = "0" ]; then - echo "[-] Error: do not use this script in /tmp or /var/tmp." 1>&2 + echo "[-] 错误: 请勿在 /tmp 或 /var/tmp 中使用此脚本。" 1>&2 exit 1 fi fi -# If @@ is specified, but there's no -f, let's come up with a temporary input -# file name. +# 如果指定了 @@,但没有 -f,创建一个临时输入文件名。 TRACE_DIR="$OUT_DIR/.traces" if [ "$STDIN_FILE" = "" ]; then if echo "$*" | grep -qF '@@'; then - STDIN_FILE="$TRACE_DIR/.cur_input" + STDIN_FILE="$TRACE_DIR/.cur_input" # 使用当前输入文件名 fi fi -# Check for obvious errors. +# 检查明显的错误。 if [ ! "$MEM_LIMIT" = "none" ]; then if [ "$MEM_LIMIT" -lt "5" ]; then - echo "[-] Error: dangerously low memory limit." 1>&2 + echo "[-] 错误: 内存限制过低。" 1>&2 exit 1 fi @@ -177,7 +179,7 @@ fi if [ ! "$TIMEOUT" = "none" ]; then if [ "$TIMEOUT" -lt "10" ]; then - echo "[-] Error: dangerously low timeout." 1>&2 + echo "[-] 错误: 超时过低。" 1>&2 exit 1 fi @@ -185,92 +187,91 @@ fi if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then - TNEW="`which "$TARGET_BIN" 2>/dev/null`" + TNEW="`which "$TARGET_BIN" 2>/dev/null`" # 查找目标二进制文件的路径 if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then - echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2 + echo "[-] 错误: 未找到或不可执行的二进制文件 '$TARGET_BIN'。" 1>&2 exit 1 fi - TARGET_BIN="$TNEW" + TARGET_BIN="$TNEW" # 更新目标二进制文件路径 fi if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" ]; then if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then - echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2 + echo "[-] 错误: 二进制文件 '$TARGET_BIN' 似乎没有被仪器化。" 1>&2 exit 1 fi fi if [ ! -d "$IN_DIR" ]; then - echo "[-] Error: directory '$IN_DIR' not found." 1>&2 + echo "[-] 错误: 目录 '$IN_DIR' 未找到。" 1>&2 exit 1 fi -test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue" +test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue" # 如果存在队列目录,则更新输入目录 -find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null -rm -rf "$TRACE_DIR" 2>/dev/null +find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null # 删除输出目录中的旧轨迹 +rm -rf "$TRACE_DIR" 2>/dev/null # 删除临时轨迹目录 -rmdir "$OUT_DIR" 2>/dev/null +rmdir "$OUT_DIR" 2>/dev/null # 删除输出目录 if [ -d "$OUT_DIR" ]; then - echo "[-] Error: directory '$OUT_DIR' exists and is not empty - delete it first." 1>&2 + echo "[-] 错误: 目录 '$OUT_DIR' 已存在且非空 - 请先删除它。" 1>&2 exit 1 fi -mkdir -m 700 -p "$TRACE_DIR" || exit 1 +mkdir -m 700 -p "$TRACE_DIR" || exit 1 # 创建临时轨迹目录并设置权限 if [ ! "$STDIN_FILE" = "" ]; then - rm -f "$STDIN_FILE" || exit 1 - touch "$STDIN_FILE" || exit 1 + rm -f "$STDIN_FILE" || exit 1 # 删除旧的标准输入文件 + touch "$STDIN_FILE" || exit 1 # 创建新的标准输入文件 fi if [ "$AFL_PATH" = "" ]; then - SHOWMAP="${0%/afl-cmin}/afl-showmap" + SHOWMAP="${0%/afl-cmin}/afl-showmap" # 设置 afl-showmap 的路径 else - SHOWMAP="$AFL_PATH/afl-showmap" + SHOWMAP="$AFL_PATH/afl-showmap" # 使用 AFL_PATH 中指定的路径 fi if [ ! -x "$SHOWMAP" ]; then - echo "[-] Error: can't find 'afl-showmap' - please set AFL_PATH." 1>&2 - rm -rf "$TRACE_DIR" + echo "[-] 错误: 找不到 'afl-showmap' - 请设置 AFL_PATH。" 1>&2 + rm -rf "$TRACE_DIR" # 删除临时轨迹目录 exit 1 fi -IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`)) +IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`)) # 计算输入目录中的文件数量 if [ "$IN_COUNT" = "0" ]; then - echo "[+] Hmm, no inputs in the target directory. Nothing to be done." - rm -rf "$TRACE_DIR" + echo "[+] 嗯,目标目录中没有输入。无需处理。" + rm -rf "$TRACE_DIR" # 删除临时轨迹目录 exit 1 fi -FIRST_FILE=`ls "$IN_DIR" | head -1` +FIRST_FILE=`ls "$IN_DIR" | head -1` # 获取第一个文件名 -# Make sure that we're not dealing with a directory. +# 确保不是处理目录。 if [ -d "$IN_DIR/$FIRST_FILE" ]; then - echo "[-] Error: The target directory contains subdirectories - please fix." 1>&2 - rm -rf "$TRACE_DIR" + echo "[-] 错误: 目标目录包含子目录 - 请修复。" 1>&2 + rm -rf "$TRACE_DIR" # 删除临时轨迹目录 exit 1 fi -# Check for the more efficient way to copy files... +# 检查复制文件的更有效方法... if ln "$IN_DIR/$FIRST_FILE" "$TRACE_DIR/.link_test" 2>/dev/null; then - CP_TOOL=ln + CP_TOOL=ln # 如果可以链接,则设置复制工具为 ln else - CP_TOOL=cp + CP_TOOL=cp # 否则使用 cp fi -# Make sure that we can actually get anything out of afl-showmap before we -# waste too much time. +# 确保我们能从 afl-showmap 中获取任何信息,以免浪费时间。 -echo "[*] Testing the target binary..." +echo "[*] 测试目标二进制文件..." if [ "$STDIN_FILE" = "" ]; then @@ -278,43 +279,42 @@ if [ "$STDIN_FILE" = "" ]; then else - cp "$IN_DIR/$FIRST_FILE" "$STDIN_FILE" + cp "$IN_DIR/$FIRST_FILE" "$STDIN_FILE" # 复制第一个文件到标准输入文件 AFL_CMIN_ALLOW_ANY=1 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/.run_test" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" &2 - test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" + echo "[-] 错误: 未检测到仪器输出(可能崩溃或超时)。" 1>&2 + test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" # 删除临时轨迹 exit 1 fi -# Let's roll! +# 开始工作! ############################# -# STEP 1: COLLECTING TRACES # +# 步骤 1:收集轨迹 # ############################# -echo "[*] Obtaining traces for input files in '$IN_DIR'..." +echo "[*] 获取 '$IN_DIR' 中输入文件的轨迹..." ( - CUR=0 + CUR=0 # 当前文件计数器 if [ "$STDIN_FILE" = "" ]; then - while read -r fn; do - + while read -r fn; do # 逐行读取输入文件名 CUR=$((CUR+1)) - printf "\\r Processing file $CUR/$IN_COUNT... " + printf "\\r 正在处理文件 $CUR/$IN_COUNT... " # 输出当前进度 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$fn" @@ -322,18 +322,16 @@ echo "[*] Obtaining traces for input files in '$IN_DIR'..." else - while read -r fn; do - + while read -r fn; do # 逐行读取输入文件名 CUR=$((CUR+1)) - printf "\\r Processing file $CUR/$IN_COUNT... " + printf "\\r 正在处理文件 $CUR/$IN_COUNT... " # 输出当前进度 - cp "$IN_DIR/$fn" "$STDIN_FILE" + cp "$IN_DIR/$fn" "$STDIN_FILE" # 复制文件到标准输入文件 "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -A "$STDIN_FILE" -- "$@" "$TRACE_DIR/.all_uniq" + sort | uniq -c | sort -n >"$TRACE_DIR/.all_uniq" # 获取所有唯一元组 -TUPLE_COUNT=$((`grep -c . "$TRACE_DIR/.all_uniq"`)) +TUPLE_COUNT=$((`grep -c . "$TRACE_DIR/.all_uniq"`)) # 计算唯一元组数量 -echo "[+] Found $TUPLE_COUNT unique tuples across $IN_COUNT files." +echo "[+] 找到 $TUPLE_COUNT 个唯一元组,遍历了 $IN_COUNT 个文件。" ##################################### -# STEP 3: SELECTING CANDIDATE FILES # +# 步骤 3:选择候选文件 # ##################################### -# The next step is to find the best candidate for each tuple. The "best" -# part is understood simply as the smallest input that includes a particular -# tuple in its trace. Empirical evidence suggests that this produces smaller -# datasets than more involved algorithms that could be still pulled off in -# a shell script. +# 下一步是找到每个元组的最佳候选者。这里的“最佳” +# 指的是包含特定元组的最小输入文件。 +# 经验表明这比更复杂的算法要好, +# 而这些算法在 shell 脚本中仍然可以执行。 -echo "[*] Finding best candidates for each tuple..." +echo "[*] 寻找每个元组的最佳候选者..." CUR=0 -while read -r fn; do - +while read -r fn; do # 逐行读取输入文件名 CUR=$((CUR+1)) - printf "\\r Processing file $CUR/$IN_COUNT... " + printf "\\r 正在处理文件 $CUR/$IN_COUNT... " # 输出当前进度 - sed "s#\$# $fn#" "$TRACE_DIR/$fn" >>"$TRACE_DIR/.candidate_list" + sed "s#\$# $fn#" "$TRACE_DIR/$fn" >>"$TRACE_DIR/.candidate_list" # 将元组与文件名关联 -done < <(ls -rS "$IN_DIR") +done < <(ls -rS "$IN_DIR") # 按文件大小倒序列出文件名 echo ############################## -# STEP 4: LOADING CANDIDATES # +# 步骤 4:加载候选 # ############################## -# At this point, we have a file of tuple-file pairs, sorted by file size -# in ascending order (as a consequence of ls -rS). By doing sort keyed -# only by tuple (-k 1,1) and configured to output only the first line for -# every key (-s -u), we end up with the smallest file for each tuple. +# 此时,我们有一个元组-文件对的文件,按文件大小升序排序 +# (由于 ls -rS 的结果)。通过仅按元组排序 (-k 1,1) +# 并配置为对每个键的第一个输出行 (-s -u), +# 我们最终得到了每个元组的最小文件。 -echo "[*] Sorting candidate list (be patient)..." +echo "[*] 排序候选列表(耐心等候)..." sort -k1,1 -s -u "$TRACE_DIR/.candidate_list" | \ - sed 's/^/BEST_FILE[/;s/ /]="/;s/$/"/' >"$TRACE_DIR/.candidate_script" + sed 's/^/BEST_FILE[/;s/ /]="/;s/$/"/' >"$TRACE_DIR/.candidate_script" # 创建候选脚本 if [ ! -s "$TRACE_DIR/.candidate_script" ]; then - echo "[-] Error: no traces obtained from test cases, check syntax!" 1>&2 + echo "[-] 错误: 从测试用例中未获得轨迹,请检查语法!" 1>&2 test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" exit 1 fi -# The sed command converted the sorted list to a shell script that populates -# BEST_FILE[tuple]="fname". Let's load that! +# sed 命令将排序后的列表转换为一个填充 +# BEST_FILE[tuple]="fname" 的 shell 脚本。让我们加载它! -. "$TRACE_DIR/.candidate_script" +. "$TRACE_DIR/.candidate_script" # 执行候选脚本 ########################## -# STEP 5: WRITING OUTPUT # +# 步骤 5:写出输出 # ########################## -# The final trick is to grab the top pick for each tuple, unless said tuple is -# already set due to the inclusion of an earlier candidate; and then put all -# tuples associated with the newly-added file to the "already have" list. The -# loop works from least popular tuples and toward the most common ones. +# 最后一步是获取每个元组的最佳选择,除非由于包含 +# 早期候选者而已经设置了该元组;然后将所有 +# 与新添加文件关联的元组放入“已拥有”列表。 +# 循环从最不常见的元组开始,到最常见的元组结束。 -echo "[*] Processing candidates and writing output files..." +echo "[*] 处理候选并写入输出文件..." CUR=0 -touch "$TRACE_DIR/.already_have" - -while read -r cnt tuple; do +touch "$TRACE_DIR/.already_have" # 创建已拥有文件 +while read -r cnt tuple; do # 逐行读取元组和计数 CUR=$((CUR+1)) - printf "\\r Processing tuple $CUR/$TUPLE_COUNT... " + printf "\\r 正在处理元组 $CUR/$TUPLE_COUNT... " # 输出当前进度 - # If we already have this tuple, skip it. + # 如果我们已经拥有此元组,跳过它。 - grep -q "^$tuple\$" "$TRACE_DIR/.already_have" && continue + grep -q "^$tuple\$" "$TRACE_DIR/.already_have" && continue # 检查已拥有列表 - FN=${BEST_FILE[tuple]} + FN=${BEST_FILE[tuple]} # 获取最佳候选文件名 - $CP_TOOL "$IN_DIR/$FN" "$OUT_DIR/$FN" + $CP_TOOL "$IN_DIR/$FN" "$OUT_DIR/$FN" # 复制文件到输出目录 - if [ "$((CUR % 5))" = "0" ]; then - sort -u "$TRACE_DIR/$FN" "$TRACE_DIR/.already_have" >"$TRACE_DIR/.tmp" - mv -f "$TRACE_DIR/.tmp" "$TRACE_DIR/.already_have" + if [ "$((CUR % 5))" = "0" ]; then # 每处理五个元组时进行一次排序 + sort -u "$TRACE_DIR/$FN" "$TRACE_DIR/.already_have" >"$TRACE_DIR/.tmp" # 合并并去重 + mv -f "$TRACE_DIR/.tmp" "$TRACE_DIR/.already_have" # 替换原有的已拥有文件 else - cat "$TRACE_DIR/$FN" >>"$TRACE_DIR/.already_have" + cat "$TRACE_DIR/$FN" >>"$TRACE_DIR/.already_have" # 否则直接追加到已拥有文件 fi -done <"$TRACE_DIR/.all_uniq" +done <"$TRACE_DIR/.all_uniq" # 从所有唯一元组读取数据 echo -OUT_COUNT=`ls -- "$OUT_DIR" | wc -l` +OUT_COUNT=`ls -- "$OUT_DIR" | wc -l` # 统计输出目录中的文件数量 if [ "$OUT_COUNT" = "1" ]; then - echo "[!] WARNING: All test cases had the same traces, check syntax!" + echo "[!] 警告: 所有测试用例具有相同的轨迹,请检查语法!" fi -echo "[+] Narrowed down to $OUT_COUNT files, saved in '$OUT_DIR'." +echo "[+] 已缩小到 $OUT_COUNT 个文件,保存在 '$OUT_DIR' 中。" # 输出结果数量 echo -test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" +test "$AFL_KEEP_TRACES" = "" && rm -rf "$TRACE_DIR" # 删除临时轨迹目录(如果需要) -exit 0 +exit 0 # 正常退出脚本 diff --git a/src/afl-plot b/src/afl-plot index 78825a9..355dd00 100644 --- a/src/afl-plot +++ b/src/afl-plot @@ -1,170 +1,175 @@ #!/bin/sh # -# american fuzzy lop - Advanced Persistent Graphing +# American Fuzzy Lop - 高级持久图形化工具 # ------------------------------------------------- # -# Written and maintained by Michal Zalewski -# Based on a design & prototype by Michael Rash. +# 作者和维护者:Michal Zalewski +# 基于 Michael Rash 的设计和原型。 # -# Copyright 2014, 2015 Google LLC All rights reserved. +# 版权所有 2014, 2015 Google LLC 保留所有权利。 # -# 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: +# 根据 Apache 许可证,版本 2.0("许可证")授权; +# 除非遵循许可证,否则您不能使用此文件。 +# 您可以从以下网址获取许可证的副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -echo "progress plotting utility for afl-fuzz by " +echo "为 afl-fuzz 提供的进度绘图工具 " # 输出程序名称 echo +# 检查参数数量是否为 2 if [ ! "$#" = "2" ]; then + # 输出使用说明到标准错误 cat 1>&2 <<_EOF_ -This program generates gnuplot images from afl-fuzz output data. Usage: +此程序从 afl-fuzz 输出数据生成 gnuplot 图像。用法: $0 afl_state_dir graph_output_dir -The afl_state_dir parameter should point to an existing state directory for any -active or stopped instance of afl-fuzz; while graph_output_dir should point to -an empty directory where this tool can write the resulting plots to. +参数 afl_state_dir 应指向现有的状态目录,该目录属于任何 +正在运行或已停止的 afl-fuzz 实例;而 graph_output_dir 应指向 +一个空目录,在该目录中此工具可以写入结果图表。 -The program will put index.html and three PNG images in the output directory; -you should be able to view it with any web browser of your choice. +该程序将在输出目录中放置 index.html 和三张 PNG 图像; +您应该能够用任何您喜欢的 web 浏览器查看它。 _EOF_ - exit 1 + exit 1 # 退出程序,返回错误状态 fi +# 如果 AFL_ALLOW_TMP 变量为空,则进行临时目录检查 if [ "$AFL_ALLOW_TMP" = "" ]; then - echo "$1" | grep -qE '^(/var)?/tmp/' + echo "$1" | grep -qE '^(/var)?/tmp/' # 检查第一个参数是否在/tmp T1="$?" - echo "$2" | grep -qE '^(/var)?/tmp/' + echo "$2" | grep -qE '^(/var)?/tmp/' # 检查第二个参数是否在/tmp T2="$?" if [ "$T1" = "0" -o "$T2" = "0" ]; then - - echo "[-] Error: this script shouldn't be used with shared /tmp directories." 1>&2 - exit 1 - + echo "[-] 错误: 不应在共享 /tmp 目录中使用此脚本。" 1>&2 # 输出错误信息 + exit 1 # 退出程序,返回错误状态 fi fi +# 检查输入目录是否有效(必须存在 'plot_data' 文件) if [ ! -f "$1/plot_data" ]; then - - echo "[-] Error: input directory is not valid (missing 'plot_data')." 1>&2 - exit 1 - + echo "[-] 错误: 输入目录无效(缺少 'plot_data' 文件)。" 1>&2 # 输出错误信息 + exit 1 # 退出程序,返回错误状态 fi +# 从 fuzzer_stats 文件中提取 banner 信息 BANNER="`cat "$1/fuzzer_stats" | grep '^afl_banner ' | cut -d: -f2- | cut -b2-`" +test "$BANNER" = "" && BANNER="(none)" # 如果未找到 banner,设置为 (none) -test "$BANNER" = "" && BANNER="(none)" - +# 查找 gnuplot 命令 GNUPLOT=`which gnuplot 2>/dev/null` +# 检查是否能找到 gnuplot if [ "$GNUPLOT" = "" ]; then - - echo "[-] Error: can't find 'gnuplot' in your \$PATH." 1>&2 - exit 1 - + echo "[-] 错误: 在您的 \$PATH 中找不到 'gnuplot'。" 1>&2 # 输出错误信息 + exit 1 # 退出程序,返回错误状态 fi +# 创建输出目录,如果目录已经存在则忽略错误 mkdir "$2" 2>/dev/null +# 检查输出目录是否成功创建 if [ ! -d "$2" ]; then - - echo "[-] Error: unable to create the output directory - pick another location." 1>&2 - exit 1 - + echo "[-] 错误: 无法创建输出目录 - 请选择另一个位置。" 1>&2 # 输出错误信息 + exit 1 # 退出程序,返回错误状态 fi +# 删除旧的图像文件 rm -f "$2/high_freq.png" "$2/low_freq.png" "$2/exec_speed.png" -mv -f "$2/index.html" "$2/index.html.orig" 2>/dev/null +mv -f "$2/index.html" "$2/index.html.orig" 2>/dev/null # 备份旧的 index.html 文件 -echo "[*] Generating plots..." +echo "[*] 生成图表..." # 输出生成图表的提示 ( - +# gnuplot 脚本开始 cat <<_EOF_ -set terminal png truecolor enhanced size 1000,300 butt +set terminal png truecolor enhanced size 1000,300 butt # 设置输出为 PNG 格式,启用颜色和大小 -set output '$2/high_freq.png' +set output '$2/high_freq.png' # 设置输出文件为高频图像文件 -set xdata time -set timefmt '%s' -set format x "%b %d\n%H:%M" -set tics font 'small' -unset mxtics -unset mytics +set xdata time # 设置 x 轴数据为时间 +set timefmt '%s' # 设置时间格式 +set format x "%b %d\n%H:%M" # 设置 x 轴刻度格式 +set tics font 'small' # 设置刻度字体 +unset mxtics # 禁用 x 轴的次刻度 +unset mytics # 禁用 y 轴的次刻度 -set grid xtics linetype 0 linecolor rgb '#e0e0e0' -set grid ytics linetype 0 linecolor rgb '#e0e0e0' -set border linecolor rgb '#50c0f0' -set tics textcolor rgb '#000000' -set key outside +set grid xtics linetype 0 linecolor rgb '#e0e0e0' # 设置 x 轴网格线样式和颜色 +set grid ytics linetype 0 linecolor rgb '#e0e0e0' # 设置 y 轴网格线样式和颜色 +set border linecolor rgb '#50c0f0' # 设置边框颜色 +set tics textcolor rgb '#000000' # 设置刻度文本颜色 +set key outside # 设置图例位置在外部 -set autoscale xfixmin -set autoscale xfixmax +set autoscale xfixmin # 自动缩放 x 轴最小值 +set autoscale xfixmax # 自动缩放 x 轴最大值 +# 绘制高频图像 plot '$1/plot_data' using 1:4 with filledcurve x1 title 'total paths' linecolor rgb '#000000' fillstyle transparent solid 0.2 noborder, \\ '' using 1:3 with filledcurve x1 title 'current path' linecolor rgb '#f0f0f0' fillstyle transparent solid 0.5 noborder, \\ '' using 1:5 with lines title 'pending paths' linecolor rgb '#0090ff' linewidth 3, \\ '' using 1:6 with lines title 'pending favs' linecolor rgb '#c00080' linewidth 3, \\ '' using 1:2 with lines title 'cycles done' linecolor rgb '#c000f0' linewidth 3 -set terminal png truecolor enhanced size 1000,200 butt -set output '$2/low_freq.png' +set terminal png truecolor enhanced size 1000,200 butt # 设置输出为低频图像文件 + +set output '$2/low_freq.png' # 设置输出文件为低频图像文件 +# 绘制低频图像 plot '$1/plot_data' using 1:8 with filledcurve x1 title '' linecolor rgb '#c00080' fillstyle transparent solid 0.2 noborder, \\ '' using 1:8 with lines title ' uniq crashes' linecolor rgb '#c00080' linewidth 3, \\ '' using 1:9 with lines title 'uniq hangs' linecolor rgb '#c000f0' linewidth 3, \\ '' using 1:10 with lines title 'levels' linecolor rgb '#0090ff' linewidth 3 -set terminal png truecolor enhanced size 1000,200 butt -set output '$2/exec_speed.png' +set terminal png truecolor enhanced size 1000,200 butt # 设置输出为执行速度图像文件 +set output '$2/exec_speed.png' # 设置输出文件为执行速度图像文件 + +# 绘制执行速度图像 plot '$1/plot_data' using 1:11 with filledcurve x1 title '' linecolor rgb '#0090ff' fillstyle transparent solid 0.2 noborder, \\ - '$1/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier; + '$1/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier; # 使用平滑贝塞尔曲线 _EOF_ -) | gnuplot +) | gnuplot # 将生成的 gnuplot 脚本管道到 gnuplot +# 检查执行速度图像是否生成成功 if [ ! -s "$2/exec_speed.png" ]; then - - echo "[-] Error: something went wrong! Perhaps you have an ancient version of gnuplot?" 1>&2 - exit 1 - + echo "[-] 错误: 出现问题!您可能使用了古老版本的 gnuplot。" 1>&2 # 输出错误信息 + exit 1 # 退出程序,返回错误状态 fi -echo "[*] Generating index.html..." +echo "[*] 生成 index.html..." # 输出生成 index.html 的提示 +# 创建 index.html 文件 cat >"$2/index.html" <<_EOF_ - - - + # 显示 banner 信息 + # 显示输入目录 + # 显示生成日期
Banner:$BANNER
Directory:$1
Generated on:`date`
Banner:$BANNER
Directory:$1
Generated on:`date`

-

-

- +

# 显示高频图像 +

# 显示低频图像 + # 显示执行速度图像 _EOF_ -# Make it easy to remotely view results when outputting directly to a directory -# served by Apache or other HTTP daemon. Since the plots aren't horribly -# sensitive, this seems like a reasonable trade-off. +# 使在直接输出到由 Apache 或其他 HTTP 守护进程服务的目录时, +# 容易查看结果。由于图表不太敏感,这似乎是合理的权衡。 -chmod 755 "$2" -chmod 644 "$2/high_freq.png" "$2/low_freq.png" "$2/exec_speed.png" "$2/index.html" +chmod 755 "$2" # 设置输出目录权限 +chmod 644 "$2/high_freq.png" "$2/low_freq.png" "$2/exec_speed.png" "$2/index.html" # 设置文件权限 -echo "[+] All done - enjoy your charts!" +echo "[+] 完成 - 享受您的图表!" # 输出完成提示 -exit 0 +exit 0 # 正常退出程序 diff --git a/src/alloc-inl.h b/src/alloc-inl.h index 9a68126..58f52cc 100644 --- a/src/alloc-inl.h +++ b/src/alloc-inl.h @@ -1,346 +1,776 @@ /* - Copyright 2013 Google LLC All rights reserved. + 版权所有 2013 Google LLC 保留所有权利。 - 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: + 根据 Apache 许可证 2.0 版("许可证")授权; + 除非符合许可证的规定,否则您不得使用此文件。 + 您可以从以下网址获取许可证的副本: 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 - error-checking, memory-zeroing alloc routines + American Fuzzy Lop - 错误检查、内存清零分配例程 ------------------------------------------------------------------ - Written and maintained by Michal Zalewski + 作者和维护者:Michal Zalewski - This allocator is not designed to resist malicious attackers (the canaries - are small and predictable), but provides a robust and portable way to detect - use-after-free, off-by-one writes, stale pointers, and so on. + 此分配器并未设计为抵抗恶意攻击者(由于哨兵小且可预测), + 但提供了一种强健且可移植的方法来检测 + 使用后释放、越界写入、过时指针等问题。 +*/ + +#ifndef _HAVE_ALLOC_INL_H // 如果没有定义 _HAVE_ALLOC_INL_H +#define _HAVE_ALLOC_INL_H // 定义 _HAVE_ALLOC_INL_H + +#include // 包含标准输入输出库 +#include // 包含标准库 +#include // 包含字符串处理库 + +#include "config.h" // 包含自定义配置文件 +#include "types.h" // 包含自定义类型定义 +#include "debug.h" // 包含调试相关的定义 + +/* 用户接口宏,将 sprintf() 输出到动态分配的缓冲区。 */ + +#define alloc_printf(_str...) ({ // 定义宏 alloc_printf + u8* _tmp; // 定义一个临时指针变量 + s32 _len = snprintf(NULL, 0, _str); // 计算格式化字符串的长度 + if (_len < 0) FATAL("Whoa, snprintf() fails?!"); // 检查 snprintf 是否成功 + _tmp = ck_alloc(_len + 1); // 动态分配内存,包括结束符 + snprintf((char*)_tmp, _len + 1, _str); // 格式化字符串并存储到分配的内存中 + _tmp; // 返回分配的指针 + }) + +/* 宏来强制分配限制作为防止整数溢出的最后防线。 */ + +#define ALLOC_CHECK_SIZE(_s) do { // 定义宏检查分配大小 + if ((_s) > MAX_ALLOC) // 如果请求的大小超过最大分配限制 + ABORT("Bad alloc request: %u bytes", (_s)); // 抛出异常 + } while (0) + +/* 宏来检查 malloc() 失败等情况。 */ + +#define ALLOC_CHECK_RESULT(_r, _s) do { // 定义宏检查分配结果 + if (!(_r)) // 如果分配结果为空 + ABORT("Out of memory: can't allocate %u bytes", (_s)); // 抛出异常 + } while (0) + +/* 用于标记使用/释放块的魔法令牌。 */ + +#define ALLOC_MAGIC_C1 0xFF00FF00 /* 使用头部(双字) */ +#define ALLOC_MAGIC_F 0xFE00FE00 /* 释放头部(双字) */ +#define ALLOC_MAGIC_C2 0xF0 /* 使用尾部(字节) */ + +/* 哨兵标记相对于用户可见指针的位置。 */ + +#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2]) // 获取头部的魔法值 +#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) // 获取当前块的大小 +#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)]) // 获取尾部的魔法值 + +#define ALLOC_OFF_HEAD 8 // 头部偏移量 +#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) // 总偏移量 + +/* 分配器增量,用于 ck_realloc_block()。 */ + +#define ALLOC_BLK_INC 256 // 分配块的增量大小 + +/* 指针的完整性检查宏。 */ + +#define CHECK_PTR(_p) do { // 定义指针检查宏 + if (_p) { // 如果指针不为空 + if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { // 检查头部的魔法值是否正确 + if (ALLOC_C1(_p) == ALLOC_MAGIC_F) // 如果是释放过的指针 + ABORT("Use after free."); // 抛出异常 + else ABORT("Corrupted head alloc canary."); // 抛出异常 + } + if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) // 检查尾部的魔法值是否正确 + ABORT("Corrupted tail alloc canary."); // 抛出异常 + } + } while (0) + +#define CHECK_PTR_EXPR(_p) ({ // 定义检查指针的表达式宏 + typeof (_p) _tmp = (_p); // 保存指针值 + CHECK_PTR(_tmp); // 检查指针 + _tmp; // 返回指针值 + }) + +/* 分配一个缓冲区,显式不清零。对零大小请求返回 NULL。 */ + +static inline void* DFL_ck_alloc_nozero(u32 size) { // 定义内联函数 + + void* ret; // 定义返回指针 + + if (!size) return NULL; // 如果请求大小为零,则返回 NULL + + ALLOC_CHECK_SIZE(size); // 检查请求的大小 + ret = malloc(size + ALLOC_OFF_TOTAL); // 分配内存 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + + ret += ALLOC_OFF_HEAD; // 调整指针,跳过头部 + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; // 设置头部魔法值 + ALLOC_S(ret) = size; // 设置当前块的大小 + ALLOC_C2(ret) = ALLOC_MAGIC_C2; // 设置尾部魔法值 + + return ret; // 返回分配的指针 +} + + +/* 分配一个缓冲区,返回清零的内存。 */ + +static inline void* DFL_ck_alloc(u32 size) { // 定义内联函数 + + void* mem; // 定义返回指针 + + if (!size) return NULL; // 如果请求大小为零,则返回 NULL + mem = DFL_ck_alloc_nozero(size); // 调用不清零的分配函数 + + return memset(mem, 0, size); // 将分配的内存清零并返回指针 +} + + +/* 释放内存,检查重复释放和堆损坏。当 DEBUG_BUILD 被定义时, + 旧内存也会用 0xFF 覆盖。 */ + +static inline void DFL_ck_free(void* mem) { // 定义内联释放函数 + + if (!mem) return; // 如果指针为空,则返回 + + CHECK_PTR(mem); // 检查指针的完整性 + +#ifdef DEBUG_BUILD // 如果定义了 DEBUG_BUILD + + /* 及时捕捉指针问题。 */ + memset(mem, 0xFF, ALLOC_S(mem)); // 将内存清零以检测问题 + +#endif /* DEBUG_BUILD */ + + ALLOC_C1(mem) = ALLOC_MAGIC_F; // 设置魔法值以标记为释放 + + free(mem - ALLOC_OFF_HEAD); // 实际释放内存 +} + + +/* 重新分配缓冲区,检查问题并清零任何新增尾数据。 + 在 DEBUG_BUILD 下,缓冲区总是重新分配到新地址,并且 + 旧内存用 0xFF 覆盖。 */ + +static inline void* DFL_ck_realloc(void* orig, u32 size) { // 定义内联重新分配函数 + + void* ret; // 定义返回指针 + u32 old_size = 0; // 定义旧大小变量 + + if (!size) { // 如果请求大小为零 + DFL_ck_free(orig); // 释放原内存 + return NULL; // 返回 NULL + } + + if (orig) { // 如果原指针不为空 + CHECK_PTR(orig); // 检查指针的完整性 + +#ifndef DEBUG_BUILD + ALLOC_C1(orig) = ALLOC_MAGIC_F; // 标记原指针为释放 +#endif /* !DEBUG_BUILD */ + + old_size = ALLOC_S(orig); // 获取原指针的大小 + orig -= ALLOC_OFF_HEAD; // 调整指针以获取实际内存 + + ALLOC_CHECK_SIZE(old_size); // 检查旧大小 + } + + ALLOC_CHECK_SIZE(size); // 检查新请求的大小 + +#ifndef DEBUG_BUILD + + ret = realloc(orig, size + ALLOC_OFF_TOTAL); // 进行重新分配 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + +#else + + /* 提早捕捉指针问题:强制重定位并确保原始缓冲区被清除。 */ + + ret = malloc(size + ALLOC_OFF_TOTAL); // 分配新的内存 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + + if (orig) { // 如果原指针不为空 + memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); // 拷贝旧数据 + memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); // 清零旧数据 + + ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; // 标记为释放 + + free(orig); // 释放原指针 + } + +#endif /* ^!DEBUG_BUILD */ + + ret += ALLOC_OFF_HEAD; // 调整返回指针 + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; // 设置头部魔法值 + ALLOC_S(ret) = size; // 设置当前块的大小 + ALLOC_C2(ret) = ALLOC_MAGIC_C2; // 设置尾部魔法值 + + if (size > old_size) // 如果新大小大于旧大小 + memset(ret + old_size, 0, size - old_size); // 清零新增的内存 + + return ret; // 返回分配的指针 +} + + +/* 以 ALLOC_BLK_INC 增量重新分配缓冲区(用于加速 + 重复的小块重新分配而不复杂化用户代码)。 */ + +static inline void* DFL_ck_realloc_block(void* orig, u32 size) { // 定义块重新分配函数 + +#ifndef DEBUG_BUILD + + if (orig) { // 如果原指针不为空 + + CHECK_PTR(orig); // 检查指针的完整性 + + if (ALLOC_S(orig) >= size) return orig; // 如果当前大小足够,返回原指针 + + size += ALLOC_BLK_INC; // 否则增加大小 + } + +#endif /* !DEBUG_BUILD */ + + return DFL_ck_realloc(orig, size); // 调用重新分配函数 +} + + +/* 创建一个包含字符串副本的缓冲区。对 NULL 输入返回 NULL。 */ + +static inline u8* DFL_ck_strdup(u8* str) { // 定义字符串复制函数 + + void* ret; // 定义返回指针 + u32 size; // 定义大小变量 + + if (!str) return NULL; // 如果输入字符串为空,则返回 NULL + + size = strlen((char*)str) + 1; // 计算字符串大小,包括结束符 + + ALLOC_CHECK_SIZE(size); // 检查请求的大小 + ret = malloc(size + ALLOC_OFF_TOTAL); // 分配内存 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + + ret += ALLOC_OFF_HEAD; // 调整指针以跳过头部 + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; // 设置头部魔法值 + ALLOC_S(ret) = size; // 设置当前块的大小 + ALLOC_C2(ret) = ALLOC_MAGIC_C2; // 设置尾部魔法值 + + return memcpy(ret, str, size); // 返回字符串副本 +} + + +/* 创建一个包含内存块副本的缓冲区。对零大小或 NULL 输入返回 NULL。 */ + +static inline void* DFL_ck_memdup(void* mem, u32 size) { // 定义内存复制函数 + + void* ret; // 定义返回指针 + + if (!mem || !size) return NULL; // 如果输入为空或大小为零,则返回 NULL + + ALLOC_CHECK_SIZE(size); // 检查请求的大小 + ret = malloc(size + ALLOC_OFF_TOTAL); // 分配内存 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + + ret += ALLOC_OFF_HEAD; // 调整指针以跳过头部 + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; // 设置头部魔法值 + ALLOC_S(ret) = size; // 设置当前块的大小 + ALLOC_C2(ret) = ALLOC_MAGIC_C2; // 设置尾部魔法值 + + return memcpy(ret, mem, size); // 返回内存副本 +} + + +/* 创建一个包含文本块的缓冲区,并在末尾附加 NUL 终止符。 + 对零大小或 NULL 输入返回 NULL。 */ + +static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) { // 定义文本内存复制函数 + + u8* ret; // 定义返回指针 + + if (!mem || !size) return NULL; // 如果输入为空或大小为零,则返回 NULL + + ALLOC_CHECK_SIZE(size); // 检查请求的大小 + ret = malloc(size + ALLOC_OFF_TOTAL + 1); // 分配内存,包括结束符 + ALLOC_CHECK_RESULT(ret, size); // 检查分配结果 + + ret += ALLOC_OFF_HEAD; // 调整指针以跳过头部 + + ALLOC_C1(ret) = ALLOC_MAGIC_C1; // 设置头部魔法值 + ALLOC_S(ret) = size; // 设置当前块的大小 + ALLOC_C2(ret) = ALLOC_MAGIC_C2; // 设置尾部魔法值 + + memcpy(ret, mem, size); // 拷贝内存块 + ret[size] = 0; // 添加结束符 + + return ret; // 返回文本副本 +} + + +#ifndef DEBUG_BUILD // 如果没有定义 DEBUG_BUILD + +/* 在非调试模式下,我们只需将上述函数简单别名化为用户可见名称,如 ck_alloc()。 */ + +#define ck_alloc DFL_ck_alloc // 分配内存 +#define ck_alloc_nozero DFL_ck_alloc_nozero // 分配不清零内存 +#define ck_realloc DFL_ck_realloc // 重新分配内存 +#define ck_realloc_block DFL_ck_realloc_block // 块级重新分配内存 +#define ck_strdup DFL_ck_strdup // 字符串复制 +#define ck_memdup DFL_ck_memdup // 内存块复制 +#define ck_memdup_str DFL_ck_memdup_str // 字符串内存块复制 +#define ck_free DFL_ck_free // 释放内存 + +#define alloc_report() // 释放报告宏 + +#else + +/* 在调试模式下,我们还跟踪分配以检测内存泄漏, + 处理过程多了一层间接性。 */ + +/* 分配跟踪数据结构: */ + +#define ALLOC_BUCKETS 4096 // 定义分配桶的数量 + +struct TRK_obj { // 定义跟踪对象结构 + void *ptr; // 指针 + char *file, *func; // 文件名和函数名 + u32 line; // 行号 +}; + +#ifdef AFL_MAIN // 如果是主程序 + +struct TRK_obj* TRK[ALLOC_BUCKETS]; // 指针数组,用于跟踪对象 +u32 TRK_cnt[ALLOC_BUCKETS]; // 计数数组 + +# define alloc_report() TRK_report() // 定义分配报告宏 + +#else + +extern struct TRK_obj* TRK[ALLOC_BUCKETS]; // 声明外部跟踪对象 +extern u32 TRK_cnt[ALLOC_BUCKETS]; // 声明外部计数数组 + +# define alloc_report() // 定义外部报告宏 + +#endif /* ^AFL_MAIN */ + +/* 指定位于给定指针的桶位置: */ + +#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) // 计算桶索引 + +/* 向已分配对象列表添加新条目。 */ + +static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,u32 line) { // 定义分配跟踪函数 + + u32 i, bucket; // 定义桶索引和循环变量 + + if (!ptr) return; // 如果指针为空则返回 + + bucket = TRKH(ptr); // 获取桶索引 + + /* 在该桶的条目列表中查找空闲插槽。 */ + + for (i = 0; i < TRK_cnt[bucket]; i++) // 遍历桶中的计数 + if (!TRK[bucket][i].ptr) { // 如果找到空闲插槽 + TRK[bucket][i].ptr = ptr; // 保存指针 + TRK[bucket][i].file = (char*)file; // 保存文件名 + TRK[bucket][i].func = (char*)func; // 保存函数名 + TRK[bucket][i].line = line; // 保存行号 + return; // 返回 + } + + /* 没有可用空间 - 分配更多。 */ + + TRK[bucket] = DFL_ck_realloc_block(TRK[bucket], // 重新分配桶 + (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); // 增加桶的大小 + + TRK[bucket][i].ptr = ptr; // 保存指针 + TRK[bucket][i].file = (char*)file; // 保存文件名 + TRK[bucket][i].func = (char*)func; // 保存函数名 + TRK[bucket][i].line = line; // 保存行号 + + TRK_cnt[bucket]++; // 增加桶的计数 +} + + +/* 从已分配对象列表中删除条目。 */ + +static inline void TRK_free_buf(void* ptr, const char* file, const char* func, + u32 line) { // 定义释放跟踪函数 + + u32 i, bucket; // 定义桶索引和循环变量 + + if (!ptr) return; // 如果指针为空则返回 + + bucket = TRKH(ptr); // 获取桶索引 + + /* 在列表中查找元素... */ + + for (i = 0; i < TRK_cnt[bucket]; i++) // 遍历桶中的计数 + if (TRK[bucket][i].ptr == ptr) { // 如果找到对应指针 + TRK[bucket][i].ptr = 0; // 将指针标记为已释放 + return; + } + + WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", // 否则输出警告 + func, file, line); +} + + +/* 对所有未释放的对象进行最终报告。 */ + +static inline void TRK_report(void) { // 定义报告函数 + + u32 i, bucket; // 定义桶索引和循环变量 + + fflush(0); // 刷新输出缓冲区 + + for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) // 遍历每个桶 + for (i = 0; i < TRK_cnt[bucket]; i++) // 遍历桶中的计数 + if (TRK[bucket][i].ptr) // 如果指针不为空 + WARNF("ALLOC: Memory never freed, created in %s (%s:%u)", // 输出未释放内存警告 + TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); +} + + +/* 简单的非调试函数的包装: */ + +static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func, + u32 line) { // 定义跟踪分配函数 + + void* ret = DFL_ck_alloc(size);�������� + +```c +/* + 版权所有 2013 Google LLC 保留所有权利。 + + 根据 Apache 许可证 2.0 版("许可证")授权; + 除非符合许可证的规定,否则您不得使用此文件。 + 您可以从以下网址获取许可证的副本: + + http://www.apache.org/licenses/LICENSE-2.0 + + 除非适用法律要求或书面同意,软件 + 根据许可证分发是一种"按现状"的基础, + 不提供任何形式的保证或条件,无论是明确的还是隐含的。 + 请参阅许可证以了解管理权限和 + 限制的具体语言。 +*/ + +/* + American Fuzzy Lop - 错误检查、内存清零的分配例程 + ------------------------------------------------------------------ + + 作者和维护者:Michal Zalewski + + 此分配器并非设计用于抵抗恶意攻击(金丝雀值较小且可预测), + 但提供了一种健壮且可移植的方式来检测 + 释放后使用、越界写入、悬空指针等问题。 */ #ifndef _HAVE_ALLOC_INL_H #define _HAVE_ALLOC_INL_H -#include -#include -#include +#include // 标准输入输出库 +#include // 标准库函数 +#include // 字符串处理函数 -#include "config.h" -#include "types.h" -#include "debug.h" +#include "config.h" // 配置文件 +#include "types.h" // 类型定义 +#include "debug.h" // 调试工具 -/* User-facing macro to sprintf() to a dynamically allocated buffer. */ +/* 用户可见的宏,用于将格式化字符串输出到动态分配的缓冲区。 */ #define alloc_printf(_str...) ({ \ u8* _tmp; \ - s32 _len = snprintf(NULL, 0, _str); \ - if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ - _tmp = ck_alloc(_len + 1); \ - snprintf((char*)_tmp, _len + 1, _str); \ - _tmp; \ + s32 _len = snprintf(NULL, 0, _str); /* 计算格式化字符串的长度 */ \ + if (_len < 0) FATAL("Whoa, snprintf() fails?!"); /* 如果失败则报错 */ \ + _tmp = ck_alloc(_len + 1); /* 分配足够的内存 */ \ + snprintf((char*)_tmp, _len + 1, _str); /* 将格式化字符串写入缓冲区 */ \ + _tmp; /* 返回分配的缓冲区 */ \ }) -/* Macro to enforce allocation limits as a last-resort defense against - integer overflows. */ +/* 宏用于强制执行分配限制,作为防止整数溢出的最后手段。 */ #define ALLOC_CHECK_SIZE(_s) do { \ - if ((_s) > MAX_ALLOC) \ - ABORT("Bad alloc request: %u bytes", (_s)); \ + if ((_s) > MAX_ALLOC) /* 检查分配大小是否超过最大限制 */ \ + ABORT("Bad alloc request: %u bytes", (_s)); /* 如果超过则报错 */ \ } while (0) -/* Macro to check malloc() failures and the like. */ +/* 宏用于检查 malloc() 失败等情况。 */ #define ALLOC_CHECK_RESULT(_r, _s) do { \ - if (!(_r)) \ - ABORT("Out of memory: can't allocate %u bytes", (_s)); \ + if (!(_r)) /* 检查分配是否成功 */ \ + ABORT("Out of memory: can't allocate %u bytes", (_s)); /* 失败则报错 */ \ } while (0) -/* Magic tokens used to mark used / freed chunks. */ +/* 用于标记已使用/已释放块的神奇令牌。 */ -#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ -#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ -#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */ +#define ALLOC_MAGIC_C1 0xFF00FF00 /* 已使用的头部标记(双字) */ +#define ALLOC_MAGIC_F 0xFE00FE00 /* 已释放的头部标记(双字) */ +#define ALLOC_MAGIC_C2 0xF0 /* 已使用的尾部标记(字节) */ -/* Positions of guard tokens in relation to the user-visible pointer. */ +/* 相对于用户可见指针的守卫令牌位置。 */ -#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2]) -#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) -#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)]) +#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2]) /* 获取头部标记 */ +#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) /* 获取分配大小 */ +#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)]) /* 获取尾部标记 */ -#define ALLOC_OFF_HEAD 8 -#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) +#define ALLOC_OFF_HEAD 8 /* 头部偏移量 */ +#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) /* 总偏移量 */ -/* Allocator increments for ck_realloc_block(). */ +/* ck_realloc_block() 的分配增量。 */ -#define ALLOC_BLK_INC 256 +#define ALLOC_BLK_INC 256 /* 块增量 */ -/* Sanity-checking macros for pointers. */ +/* 用于指针的健全性检查宏。 */ #define CHECK_PTR(_p) do { \ - if (_p) { \ - if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\ - if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ - ABORT("Use after free."); \ - else ABORT("Corrupted head alloc canary."); \ + if (_p) { /* 检查指针是否为空 */ \ + if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { /* 检查头部标记是否损坏 */ \ + if (ALLOC_C1(_p) == ALLOC_MAGIC_F) /* 如果是已释放的标记 */ \ + ABORT("Use after free."); /* 报错:释放后使用 */ \ + else ABORT("Corrupted head alloc canary."); /* 否则报错:头部标记损坏 */ \ } \ - if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ - ABORT("Corrupted tail alloc canary."); \ + if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) /* 检查尾部标记是否损坏 */ \ + ABORT("Corrupted tail alloc canary."); /* 报错:尾部标记损坏 */ \ } \ } while (0) #define CHECK_PTR_EXPR(_p) ({ \ - typeof (_p) _tmp = (_p); \ - CHECK_PTR(_tmp); \ - _tmp; \ + typeof (_p) _tmp = (_p); /* 临时存储指针 */ \ + CHECK_PTR(_tmp); /* 检查指针 */ \ + _tmp; /* 返回指针 */ \ }) - -/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized - requests. */ +/* 分配一个缓冲区,明确不清零。对于零大小的请求返回 NULL。 */ static inline void* DFL_ck_alloc_nozero(u32 size) { void* ret; - if (!size) return NULL; + if (!size) return NULL; /* 如果大小为 0,返回 NULL */ - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); + ALLOC_CHECK_SIZE(size); /* 检查分配大小是否合法 */ + ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ - ret += ALLOC_OFF_HEAD; + ret += ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - return ret; + ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部标记 */ + ALLOC_S(ret) = size; /* 设置分配大小 */ + ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部标记 */ + return ret; /* 返回分配的缓冲区 */ } - -/* Allocate a buffer, returning zeroed memory. */ +/* 分配一个缓冲区,返回清零的内存。 */ static inline void* DFL_ck_alloc(u32 size) { void* mem; - if (!size) return NULL; - mem = DFL_ck_alloc_nozero(size); - - return memset(mem, 0, size); + if (!size) return NULL; /* 如果大小为 0,返回 NULL */ + mem = DFL_ck_alloc_nozero(size); /* 分配内存 */ + return memset(mem, 0, size); /* 清零内存并返回 */ } - -/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD - is set, the old memory will be also clobbered with 0xFF. */ +/* 释放内存,检查双重释放和堆损坏。当 DEBUG_BUILD 启用时, + 旧内存将被填充为 0xFF。 */ static inline void DFL_ck_free(void* mem) { - if (!mem) return; + if (!mem) return; /* 如果指针为空,直接返回 */ - CHECK_PTR(mem); + CHECK_PTR(mem); /* 检查指针是否合法 */ #ifdef DEBUG_BUILD - /* Catch pointer issues sooner. */ - memset(mem, 0xFF, ALLOC_S(mem)); + /* 尽早捕获指针问题。 */ + memset(mem, 0xFF, ALLOC_S(mem)); /* 用 0xFF 填充内存 */ #endif /* DEBUG_BUILD */ - ALLOC_C1(mem) = ALLOC_MAGIC_F; - - free(mem - ALLOC_OFF_HEAD); + ALLOC_C1(mem) = ALLOC_MAGIC_F; /* 标记为已释放 */ + free(mem - ALLOC_OFF_HEAD); /* 释放内存 */ } - -/* Re-allocate a buffer, checking for issues and zeroing any newly-added tail. - With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the - old memory is clobbered with 0xFF. */ +/* 重新分配缓冲区,检查问题并清零任何新添加的尾部。 + 在 DEBUG_BUILD 下,缓冲区总是重新分配到新地址, + 旧内存被填充为 0xFF。 */ static inline void* DFL_ck_realloc(void* orig, u32 size) { void* ret; u32 old_size = 0; - if (!size) { + if (!size) { /* 如果新大小为 0,释放原内存并返回 NULL */ DFL_ck_free(orig); return NULL; } - if (orig) { + if (orig) { /* 如果原指针不为空 */ - CHECK_PTR(orig); + CHECK_PTR(orig); /* 检查原指针是否合法 */ #ifndef DEBUG_BUILD - ALLOC_C1(orig) = ALLOC_MAGIC_F; + ALLOC_C1(orig) = ALLOC_MAGIC_F; /* 标记为已释放 */ #endif /* !DEBUG_BUILD */ - old_size = ALLOC_S(orig); - orig -= ALLOC_OFF_HEAD; + old_size = ALLOC_S(orig); /* 获取原分配大小 */ + orig -= ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - ALLOC_CHECK_SIZE(old_size); + ALLOC_CHECK_SIZE(old_size); /* 检查原分配大小是否合法 */ } - ALLOC_CHECK_SIZE(size); + ALLOC_CHECK_SIZE(size); /* 检查新分配大小是否合法 */ #ifndef DEBUG_BUILD - ret = realloc(orig, size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); + ret = realloc(orig, size + ALLOC_OFF_TOTAL); /* 重新分配内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ #else - /* Catch pointer issues sooner: force relocation and make sure that the - original buffer is wiped. */ + /* 尽早捕获指针问题:强制重新分配并确保原缓冲区被清除。 */ - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); + ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配新内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ - if (orig) { + if (orig) { /* 如果原指针不为空 */ - memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); - memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); + memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); /* 复制数据 */ + memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); /* 用 0xFF 填充原内存 */ - ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; + ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; /* 标记为已释放 */ - free(orig); + free(orig); /* 释放原内存 */ } #endif /* ^!DEBUG_BUILD */ - ret += ALLOC_OFF_HEAD; + ret += ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; + ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部标记 */ + ALLOC_S(ret) = size; /* 设置分配大小 */ + ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部标记 */ - if (size > old_size) - memset(ret + old_size, 0, size - old_size); - - return ret; + if (size > old_size) /* 如果新大小大于原大小 */ + memset(ret + old_size, 0, size - old_size); /* 清零新添加的部分 */ + return ret; /* 返回重新分配的缓冲区 */ } - -/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up - repeated small reallocs without complicating the user code). */ +/* 使用 ALLOC_BLK_INC 增量重新分配缓冲区(用于加速重复的小型重新分配, + 而不会使用户代码复杂化)。 */ static inline void* DFL_ck_realloc_block(void* orig, u32 size) { #ifndef DEBUG_BUILD - if (orig) { + if (orig) { /* 如果原指针不为空 */ - CHECK_PTR(orig); + CHECK_PTR(orig); /* 检查原指针是否合法 */ - if (ALLOC_S(orig) >= size) return orig; + if (ALLOC_S(orig) >= size) return orig; /* 如果原大小足够,直接返回 */ - size += ALLOC_BLK_INC; + size += ALLOC_BLK_INC; /* 增加分配大小 */ } #endif /* !DEBUG_BUILD */ - return DFL_ck_realloc(orig, size); - + return DFL_ck_realloc(orig, size); /* 重新分配内存 */ } - -/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ +/* 创建一个包含字符串副本的缓冲区。对于 NULL 输入返回 NULL。 */ static inline u8* DFL_ck_strdup(u8* str) { void* ret; u32 size; - if (!str) return NULL; + if (!str) return NULL; /* 如果字符串为空,返回 NULL */ - size = strlen((char*)str) + 1; + size = strlen((char*)str) + 1; /* 计算字符串长度 */ - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); + ALLOC_CHECK_SIZE(size); /* 检查分配大小是否合法 */ + ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ - ret += ALLOC_OFF_HEAD; + ret += ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - return memcpy(ret, str, size); + ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部标记 */ + ALLOC_S(ret) = size; /* 设置分配大小 */ + ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部标记 */ + return memcpy(ret, str, size); /* 复制字符串并返回 */ } - -/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized - or NULL inputs. */ +/* 创建一个包含内存块副本的缓冲区。对于零大小或 NULL 输入返回 NULL。 */ static inline void* DFL_ck_memdup(void* mem, u32 size) { void* ret; - if (!mem || !size) return NULL; + if (!mem || !size) return NULL; /* 如果内存块为空或大小为 0,返回 NULL */ - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL); - ALLOC_CHECK_RESULT(ret, size); + ALLOC_CHECK_SIZE(size); /* 检查分配大小是否合法 */ + ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ - ret += ALLOC_OFF_HEAD; + ret += ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; - - return memcpy(ret, mem, size); + ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部标记 */ + ALLOC_S(ret) = size; /* 设置分配大小 */ + ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部标记 */ + return memcpy(ret, mem, size); /* 复制内存块并返回 */ } - -/* Create a buffer with a block of text, appending a NUL terminator at the end. - Returns NULL for zero-sized or NULL inputs. */ +/* 创建一个包含文本块的缓冲区,并在末尾附加一个 NUL 终止符。 + 对于零大小或 NULL 输入返回 NULL。 */ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) { u8* ret; - if (!mem || !size) return NULL; + if (!mem || !size) return NULL; /* 如果内存块为空或大小为 0,返回 NULL */ - ALLOC_CHECK_SIZE(size); - ret = malloc(size + ALLOC_OFF_TOTAL + 1); - ALLOC_CHECK_RESULT(ret, size); + ALLOC_CHECK_SIZE(size); /* 检查分配大小是否合法 */ + ret = malloc(size + ALLOC_OFF_TOTAL + 1); /* 分配内存 */ + ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */ - ret += ALLOC_OFF_HEAD; - - ALLOC_C1(ret) = ALLOC_MAGIC_C1; - ALLOC_S(ret) = size; - ALLOC_C2(ret) = ALLOC_MAGIC_C2; + ret += ALLOC_OFF_HEAD; /* 调整指针以跳过头部标记 */ - memcpy(ret, mem, size); - ret[size] = 0; + ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部标记 */ + ALLOC_S(ret) = size; /* 设置分配大小 */ + ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部标记 */ - return ret; + memcpy(ret, mem, size); /* 复制内存块 */ + ret[size] = 0; /* 添加 NUL 终止符 */ + return ret; /* 返回缓冲区 */ } - #ifndef DEBUG_BUILD -/* In non-debug mode, we just do straightforward aliasing of the above functions - to user-visible names such as ck_alloc(). */ +/* 在非调试模式下,我们直接将上述函数别名为用户可见的名称,如 ck_alloc()。 */ #define ck_alloc DFL_ck_alloc #define ck_alloc_nozero DFL_ck_alloc_nozero @@ -351,123 +781,121 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) { #define ck_memdup_str DFL_ck_memdup_str #define ck_free DFL_ck_free -#define alloc_report() +#define alloc_report() /* 无操作 */ #else -/* In debugging mode, we also track allocations to detect memory leaks, and the - flow goes through one more layer of indirection. */ +/* 在调试模式下,我们还跟踪分配以检测内存泄漏, + 并且流程通过另一层间接性。 */ -/* Alloc tracking data structures: */ +/* 分配跟踪数据结构: */ -#define ALLOC_BUCKETS 4096 +#define ALLOC_BUCKETS 4096 /* 分配桶的数量 */ struct TRK_obj { - void *ptr; - char *file, *func; - u32 line; + void *ptr; /* 分配的指针 */ + char *file, *func; /* 分配位置的文件名和函数名 */ + u32 line; /* 分配位置的行号 */ }; #ifdef AFL_MAIN -struct TRK_obj* TRK[ALLOC_BUCKETS]; -u32 TRK_cnt[ALLOC_BUCKETS]; +struct TRK_obj* TRK[ALLOC_BUCKETS]; /* 分配跟踪数组 */ +u32 TRK_cnt[ALLOC_BUCKETS]; /* 每个桶的计数 */ -# define alloc_report() TRK_report() +# define alloc_report() TRK_report() /* 定义分配报告宏 */ #else -extern struct TRK_obj* TRK[ALLOC_BUCKETS]; -extern u32 TRK_cnt[ALLOC_BUCKETS]; +extern struct TRK_obj* TRK[ALLOC_BUCKETS]; /* 外部声明分配跟踪数组 */ +extern u32 TRK_cnt[ALLOC_BUCKETS]; /* 外部声明每个桶的计数 */ -# define alloc_report() +# define alloc_report() /* 无操作 */ #endif /* ^AFL_MAIN */ -/* Bucket-assigning function for a given pointer: */ +/* 为给定指针分配桶的函数: */ #define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) - -/* Add a new entry to the list of allocated objects. */ +/* 将新条目添加到已分配对象的列表中。 */ static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func, u32 line) { u32 i, bucket; - if (!ptr) return; + if (!ptr) return; /* 如果指针为空,直接返回 */ - bucket = TRKH(ptr); + bucket = TRKH(ptr); /* 计算桶索引 */ - /* Find a free slot in the list of entries for that bucket. */ + /* 在该桶的条目列表中查找空闲槽。 */ for (i = 0; i < TRK_cnt[bucket]; i++) - if (!TRK[bucket][i].ptr) { + if (!TRK[bucket][i].ptr) { /* 如果找到空闲槽 */ - TRK[bucket][i].ptr = ptr; - TRK[bucket][i].file = (char*)file; - TRK[bucket][i].func = (char*)func; - TRK[bucket][i].line = line; + TRK[bucket][i].ptr = ptr; /* 存储指针 */ + TRK[bucket][i].file = (char*)file; /* 存储文件名 */ + TRK[bucket][i].func = (char*)func; /* 存储函数名 */ + TRK[bucket][i].line = line; /* 存储行号 */ return; } - /* No space available - allocate more. */ + /* 没有可用空间 - 分配更多。 */ TRK[bucket] = DFL_ck_realloc_block(TRK[bucket], - (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); + (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); /* 重新分配内存 */ - TRK[bucket][i].ptr = ptr; - TRK[bucket][i].file = (char*)file; - TRK[bucket][i].func = (char*)func; - TRK[bucket][i].line = line; + TRK[bucket][i].ptr = ptr; /* 存储指针 */ + TRK[bucket][i].file = (char*)file; /* 存储文件名 */ + TRK[bucket][i].func = (char*)func; /* 存储函数名 */ + TRK[bucket][i].line = line; /* 存储行号 */ - TRK_cnt[bucket]++; + TRK_cnt[bucket]++; /* 增加计数 */ } - -/* Remove entry from the list of allocated objects. */ +/* 从已分配对象的列表中删除条目。 */ static inline void TRK_free_buf(void* ptr, const char* file, const char* func, u32 line) { u32 i, bucket; - if (!ptr) return; + if (!ptr) return; /* 如果指针为空,直接返回 */ - bucket = TRKH(ptr); + bucket = TRKH(ptr); /* 计算桶索引 */ - /* Find the element on the list... */ + /* 在列表中查找元素... */ for (i = 0; i < TRK_cnt[bucket]; i++) - if (TRK[bucket][i].ptr == ptr) { + if (TRK[bucket][i].ptr == ptr) { /* 如果找到匹配的指针 */ - TRK[bucket][i].ptr = 0; + TRK[bucket][i].ptr = 0; /* 标记为已释放 */ return; } WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", - func, file, line); + func, file, line); /* 报错:尝试释放未分配的内存 */ } - -/* Do a final report on all non-deallocated objects. */ +/* 对所有未释放的对象进行最终报告。 */ static inline void TRK_report(void) { u32 i, bucket; - fflush(0); + fflush(0); /* 刷新所有输出流 */ + + for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) /* 遍历所有桶 */ + for (i = 0; i < TRK_cnt[bucket]; i++) /* 遍历每个桶的条目 */ + if (TRK[bucket][i].ptr) /* 如果 - for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) - for (i = 0; i < TRK_cnt[bucket]; i++) - if (TRK[bucket][i].ptr) WARNF("ALLOC: Memory never freed, created in %s (%s:%u)", TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); diff --git a/src/debug.h b/src/debug.h index 5f75974..d01621a 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,24 +1,24 @@ /* - Copyright 2013 Google LLC All rights reserved. + 版权所有 2013 Google LLC 保留所有权利。 - 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: + 根据 Apache 许可证 2.0 版("许可证")授权; + 除非符合许可证的规定,否则您不得使用此文件。 + 您可以从以下网址获取许可证的副本: 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 - debug / error handling macros + American Fuzzy Lop - 调试/错误处理宏 -------------------------------------------------- - Written and maintained by Michal Zalewski + 作者和维护者:Michal Zalewski */ #ifndef _HAVE_DEBUG_H @@ -30,49 +30,49 @@ #include "config.h" /******************* - * Terminal colors * + * 终端颜色定义 * *******************/ #ifdef USE_COLOR -# define cBLK "\x1b[0;30m" -# define cRED "\x1b[0;31m" -# define cGRN "\x1b[0;32m" -# define cBRN "\x1b[0;33m" -# define cBLU "\x1b[0;34m" -# define cMGN "\x1b[0;35m" -# define cCYA "\x1b[0;36m" -# define cLGR "\x1b[0;37m" -# define cGRA "\x1b[1;90m" -# define cLRD "\x1b[1;91m" -# define cLGN "\x1b[1;92m" -# define cYEL "\x1b[1;93m" -# define cLBL "\x1b[1;94m" -# define cPIN "\x1b[1;95m" -# define cLCY "\x1b[1;96m" -# define cBRI "\x1b[1;97m" -# define cRST "\x1b[0m" - -# define bgBLK "\x1b[40m" -# define bgRED "\x1b[41m" -# define bgGRN "\x1b[42m" -# define bgBRN "\x1b[43m" -# define bgBLU "\x1b[44m" -# define bgMGN "\x1b[45m" -# define bgCYA "\x1b[46m" -# define bgLGR "\x1b[47m" -# define bgGRA "\x1b[100m" -# define bgLRD "\x1b[101m" -# define bgLGN "\x1b[102m" -# define bgYEL "\x1b[103m" -# define bgLBL "\x1b[104m" -# define bgPIN "\x1b[105m" -# define bgLCY "\x1b[106m" -# define bgBRI "\x1b[107m" +# define cBLK "\x1b[0;30m" // 黑色文本 +# define cRED "\x1b[0;31m" // 红色文本 +# define cGRN "\x1b[0;32m" // 绿色文本 +# define cBRN "\x1b[0;33m" // 棕色文本 +# define cBLU "\x1b[0;34m" // 蓝色文本 +# define cMGN "\x1b[0;35m" // 紫色文本 +# define cCYA "\x1b[0;36m" // 青色文本 +# define cLGR "\x1b[0;37m" // 浅灰色文本 +# define cGRA "\x1b[1;90m" // 深灰色文本 +# define cLRD "\x1b[1;91m" // 浅红色文本 +# define cLGN "\x1b[1;92m" // 浅绿色文本 +# define cYEL "\x1b[1;93m" // 浅黄色文本 +# define cLBL "\x1b[1;94m" // 浅蓝色文本 +# define cPIN "\x1b[1;95m" // 浅紫色文本 +# define cLCY "\x1b[1;96m" // 浅青色文本 +# define cBRI "\x1b[1;97m" // 白色文本 +# define cRST "\x1b[0m" // 重置颜色 + +# define bgBLK "\x1b[40m" // 黑色背景 +# define bgRED "\x1b[41m" // 红色背景 +# define bgGRN "\x1b[42m" // 绿色背景 +# define bgBRN "\x1b[43m" // 棕色背景 +# define bgBLU "\x1b[44m" // 蓝色背景 +# define bgMGN "\x1b[45m" // 紫色背景 +# define bgCYA "\x1b[46m" // 青色背景 +# define bgLGR "\x1b[47m" // 浅灰色背景 +# define bgGRA "\x1b[100m" // 深灰色背景 +# define bgLRD "\x1b[101m" // 浅红色背景 +# define bgLGN "\x1b[102m" // 浅绿色背景 +# define bgYEL "\x1b[103m" // 浅黄色背景 +# define bgLBL "\x1b[104m" // 浅蓝色背景 +# define bgPIN "\x1b[105m" // 浅紫色背景 +# define bgLCY "\x1b[106m" // 浅青色背景 +# define bgBRI "\x1b[107m" // 白色背景 #else -# define cBLK "" +# define cBLK "" // 不使用颜色 # define cRED "" # define cGRN "" # define cBRN "" @@ -90,7 +90,7 @@ # define cBRI "" # define cRST "" -# define bgBLK "" +# define bgBLK "" // 不使用背景颜色 # define bgRED "" # define bgGRN "" # define bgBRN "" @@ -115,25 +115,25 @@ #ifdef FANCY_BOXES -# define SET_G1 "\x1b)0" /* Set G1 for box drawing */ -# define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */ -# define bSTART "\x0e" /* Enter G1 drawing mode */ -# define bSTOP "\x0f" /* Leave G1 drawing mode */ -# define bH "q" /* Horizontal line */ -# define bV "x" /* Vertical line */ -# define bLT "l" /* Left top corner */ -# define bRT "k" /* Right top corner */ -# define bLB "m" /* Left bottom corner */ -# define bRB "j" /* Right bottom corner */ -# define bX "n" /* Cross */ -# define bVR "t" /* Vertical, branch right */ -# define bVL "u" /* Vertical, branch left */ -# define bHT "v" /* Horizontal, branch top */ -# define bHB "w" /* Horizontal, branch bottom */ +# define SET_G1 "\x1b)0" /* 设置 G1 用于绘制框 */ +# define RESET_G1 "\x1b)B" /* 重置 G1 为 ASCII 字符 */ +# define bSTART "\x0e" /* 进入 G1 绘制模式 */ +# define bSTOP "\x0f" /* 离开 G1 绘制模式 */ +# define bH "q" /* 水平线 */ +# define bV "x" /* 垂直线 */ +# define bLT "l" /* 左上角 */ +# define bRT "k" /* 右上角 */ +# define bLB "m" /* 左下角 */ +# define bRB "j" /* 右下角 */ +# define bX "n" /* 交叉点 */ +# define bVR "t" /* 垂直,右分支 */ +# define bVL "u" /* 垂直,左分支 */ +# define bHT "v" /* 水平,顶部分支 */ +# define bHB "w" /* 水平,底部分支 */ #else -# define SET_G1 "" +# define SET_G1 "" // 不使用 G1 绘制框 # define RESET_G1 "" # define bSTART "" # define bSTOP "" @@ -152,107 +152,105 @@ #endif /* ^FANCY_BOXES */ /*********************** - * Misc terminal codes * + * 其他终端代码 * ***********************/ -#define TERM_HOME "\x1b[H" -#define TERM_CLEAR TERM_HOME "\x1b[2J" -#define cEOL "\x1b[0K" -#define CURSOR_HIDE "\x1b[?25l" -#define CURSOR_SHOW "\x1b[?25h" +#define TERM_HOME "\x1b[H" // 移动光标到屏幕左上角 +#define TERM_CLEAR TERM_HOME "\x1b[2J" // 清除屏幕 +#define cEOL "\x1b[0K" // 清除光标到行尾 +#define CURSOR_HIDE "\x1b[?25l" // 隐藏光标 +#define CURSOR_SHOW "\x1b[?25h" // 显示光标 /************************ - * Debug & error macros * + * 调试和错误宏定义 * ************************/ -/* Just print stuff to the appropriate stream. */ +/* 只是将内容打印到适当的输出流。 */ #ifdef MESSAGES_TO_STDOUT -# define SAYF(x...) printf(x) +# define SAYF(x...) printf(x) // 输出到标准输出 #else -# define SAYF(x...) fprintf(stderr, x) +# define SAYF(x...) fprintf(stderr, x) // 输出到标准错误 #endif /* ^MESSAGES_TO_STDOUT */ -/* Show a prefixed warning. */ +/* 显示带前缀的警告信息。 */ #define WARNF(x...) do { \ - SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \ + SAYF(cYEL "[!] " cBRI "警告: " cRST x); \ SAYF(cRST "\n"); \ } while (0) -/* Show a prefixed "doing something" message. */ +/* 显示带前缀的"正在做某事"消息。 */ #define ACTF(x...) do { \ SAYF(cLBL "[*] " cRST x); \ SAYF(cRST "\n"); \ } while (0) -/* Show a prefixed "success" message. */ +/* 显示带前缀的"成功"消息。 */ #define OKF(x...) do { \ SAYF(cLGN "[+] " cRST x); \ SAYF(cRST "\n"); \ } while (0) -/* Show a prefixed fatal error message (not used in afl). */ +/* 显示带前缀的致命错误消息(未在 afl 中使用)。 */ #define BADF(x...) do { \ SAYF(cLRD "\n[-] " cRST x); \ SAYF(cRST "\n"); \ } while (0) -/* Die with a verbose non-OS fatal error message. */ +/* 带有详细非操作系统致命错误消息退出程序。 */ #define FATAL(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] 程序中止 : " \ cBRI x); \ - SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \ + SAYF(cLRD "\n 位置 : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ exit(1); \ } while (0) -/* Die by calling abort() to provide a core dump. */ +/* 通过调用 abort() 以提供核心转储而退出。 */ #define ABORT(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] 程序中止 : " \ cBRI x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \ + SAYF(cLRD "\n 停止位置 : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ abort(); \ } while (0) -/* Die while also including the output of perror(). */ +/* 在包含 perror() 输出的同时终止程序。 */ #define PFATAL(x...) do { \ fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] 系统错误 : " \ cBRI x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \ + SAYF(cLRD "\n 停止位置 : " cRST "%s(), %s:%u\n", \ __FUNCTION__, __FILE__, __LINE__); \ - SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ + SAYF(cLRD " 操作系统消息 : " cRST "%s\n", strerror(errno)); \ exit(1); \ } while (0) -/* Die with FAULT() or PFAULT() depending on the value of res (used to - interpret different failure modes for read(), write(), etc). */ +/* 根据 res 的值(用于解释 read()、write() 等的不同失败模式)调用 FATAL() 或 PFATAL()。 */ #define RPFATAL(res, x...) do { \ if (res < 0) PFATAL(x); else FATAL(x); \ } while (0) -/* Error-checking versions of read() and write() that call RPFATAL() as - appropriate. */ +/* 检查错误的 read() 和 write() 的版本,在适当的情况下调用 RPFATAL()。 */ #define ck_write(fd, buf, len, fn) do { \ u32 _len = (len); \ s32 _res = write(fd, buf, _len); \ - if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \ + if (_res != _len) RPFATAL(_res, "对 %s 的短写入", fn); \ } while (0) #define ck_read(fd, buf, len, fn) do { \ u32 _len = (len); \ s32 _res = read(fd, buf, _len); \ - if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \ + if (_res != _len) RPFATAL(_res, "对 %s 的短读取", fn); \ } while (0) #endif /* ! _HAVE_DEBUG_H */