12 个版本

0.2.4 2023 年 9 月 30 日
0.2.3 2023 年 9 月 15 日
0.2.0 2023 年 8 月 14 日
0.1.8 2023 年 6 月 15 日
0.1.3 2023 年 1 月 11 日

#82 in 测试

49 每月下载量

MIT 许可证

4MB
2.5K SLoC

ECFuzz

ECFuzz

进化覆盖指导模糊测试引擎。轻量级、多线程、确定性。支持突变和基于树的生成模糊测试。突变由遗传算法管理,以最大化代码覆盖率为目标,过滤冗余输入。除了命令行工具外,还提供了库接口。需要 clang 14(或更高版本)和 llvm 工具。

快速开始

Clang 和 llvm 工具可以使用您首选的包管理器安装。为了获得最佳模糊测试性能,请参阅下文关于从源安装 clang 和 LLVM的部分。

使用 cargo 安装 ecfuzz,并通过命令行界面运行它。设置 --mutate-stdin 标志可以从标准输入生成单个突变而不测量代码覆盖率。突变结果输出到 stdout。

cargo install ecfuzz
ecfuzz --help
echo 'Hello world!' | ecfuzz --mutate-stdin --seed 0

请参阅以下示例,了解如何测量预设突变数量的源代码覆盖率。

工作原理

接口

语料库精炼

使用遗传算法最大化代码覆盖率,同时最小化语料库大小

  1. 编译带有代码覆盖率映射和清理器的目标
  2. 突变其中一个种子输入,并通过 stdin、输入文件或命令参数将其发送到目标
  3. 测量代码覆盖率作为执行的代码分支集合
  4. 如果突变发现新的分支,将其添加到语料库
    • 将新分支覆盖率与现有语料库条目进行比较,并修剪具有新覆盖率子集的条目
    • 如果两个覆盖集相等,则选择最短的输入作为平局决定。如果输入长度相等,则按字节顺序排序选择第一个输入。

测试用例最小化

除了在语料库中维护最小输入集外,语料库中的每个输入都可以进行最小化。将移除输入中的字节,直到无法再移除字节而不会改变目标可执行程序返回的代码覆盖率、stdout和stderr。

数字生成

使用xxhash算法进行数字生成,从而生成完全确定性的fuzzer输出。

字节突变

  • 异或位翻转 + 字节移位
  • 字节插入
  • 字节删除
  • 字节替换
  • 魔法字符替换
  • 字典插入
  • 标记字典替换

字典突变

为了启用字典突变,必须包含字典文件路径。包含key项的字典文件中的行将被拼接到输入中。包含key=value的字典行将使用标记替换插入,例如,通过用value替换它来突变种子输入中的key项。键在第一个=符号处分割,键可以在新行上重复,以表示多个值。

语法模糊测试

除了突变模糊测试外,还可以提供语法文件以指定语法语法树。文件中的每一行定义一个节点,通过第一个=符号分隔key=value。必须在定义子节点之前定义父节点。将从结果树的深度优先遍历生成突变,节点导航由哈希选择。

从提供的语法文件--grammar <path>发送到stdin的语法生成的输入,来自语法文件--arg-grammar <path>生成的输入将作为参数传递给目标可执行程序。由ECFUZZ_START_MUTATIONECFUZZ_END_MUTATION包围的输入子串将使用字节和字典突变进行突变。如果设置了--mutate-file,则与ECFUZZ_MUTATED_FILE匹配的输入子串将被替换为突变文件的路径。

可以使用--print-grammar-file <path>来显示结果树的字符串表示形式,例如,ecfuzz --print-grammar-file ./tests/phone_number.grammar

示例

CLI

fuzz_target.c中有2个错误,这些错误发生在某些取决于用户输入的'if'语句之后。程序将编译并运行带有嵌入式调试器的目标文件,并将基于./examples/cli/input/corpus中的样本的突变输入发送到可执行程序。监视每个新输入的代码覆盖率,并将产生新代码覆盖率的任何输入添加到语料库中。

git clone https://github.com/matt24smith/ecfuzz.git && cd ecfuzz

# set additional flags for the clang compiler 
export CFLAGS="-std=c17 -g -fcolor-diagnostics -O3"

# see 'ecfuzz --help' for a complete description of arguments
ecfuzz \
  --target ./examples/cli/fuzz_target.c \
  --corpus ./examples/cli/input/corpus \
  --output-dir ./output/cli_demo/ \
  --dictionary-path ./examples/cli/input/sample.dict \
  --iterations 10000 \
  --seed 117 --plaintext 2>&1 | tee fuzz.log

清理器输出和其他目标错误消息将写入stderr。

示例输出

Fuzz.log
CFLAGS="-std=c17 -g -fcolor-diagnostics -O3 -fuse-ld=lld"
LDFLAGS="-fuse-ld=lld"
compiling...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.address-sanitized.fuzz_target.out newer than target source, skipping compilation...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.cfi-sanitized.fuzz_target.out newer than target source, skipping compilation...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.safe-stack-sanitized.fuzz_target.out newer than target source, skipping compilation...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.thread-sanitized.fuzz_target.out newer than target source, skipping compilation...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.memory-sanitized.fuzz_target.out newer than target source, skipping compilation...
target binary /home/matt/ecfuzz/output/cli_demo/ecfuzz_target.undefined-sanitized.fuzz_target.out newer than target source, skipping compilation...
done compiling
New coverage! execs: 512    pruned: 1  corpus size: 1    updating inputs...            
    CorpusInput:  { coverage: 2, lifetime: 1, preview: "000000000000000" }

coverage:     2/12     exec/s: 2626  corpus size: 1    unique crashes: 0    i: 512     
New coverage! execs: 535    pruned: 0  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 3, lifetime: 1, preview: "AB0000000000000" }

coverage:     4/12     exec/s: 2548  corpus size: 2    unique crashes: 0    i: 535     
New coverage! execs: 773    pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 4, lifetime: 1, preview: "GH0000000000000" }

coverage:     6/12     exec/s: 2159  corpus size: 2    unique crashes: 0    i: 773     
New coverage! execs: 1938   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 4, lifetime: 1, preview: "ABC000000000000" }

coverage:     7/12     exec/s: 1778  corpus size: 2    unique crashes: 0    i: 1938    
New coverage! execs: 1940   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 5, lifetime: 2, preview: "GHI000000000000" }

coverage:     8/12     exec/s: 1777  corpus size: 2    unique crashes: 0    i: 1940    
New coverage! execs: 2909   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 6, lifetime: 3, preview: "GHIJ000000000000" }

coverage:     9/12     exec/s: 1709  corpus size: 2    unique crashes: 0    i: 2909    
New coverage! execs: 3397   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 5, lifetime: 1, preview: "ABCD0000000000" }

coverage:    10/12     exec/s: 1693  corpus size: 2    unique crashes: 0    i: 3397    
New coverage! execs: 4218   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 6, lifetime: 2, preview: "ABCDE0000000000" }

coverage:    11/12     exec/s: 1670  corpus size: 2    unique crashes: 0    i: 4218    
New coverage! execs: 5142   pruned: 1  corpus size: 2    updating inputs...            
    CorpusInput:  { coverage: 7, lifetime: 4, preview: "GHIJK000000000" }

coverage:    12/12     exec/s: 1646  corpus size: 2    unique crashes: 0    i: 5142    

New crash! execs: 6165   pruned: 1   unique crashes: 1    updating crash log...                              
    CorpusInput:  { coverage: 6, lifetime: 2, preview: "ABCDE00000000" }
==3339243==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x55c542fd86b0 in do_comparison /home/matt/ecfuzz/examples/cli/fuzz_target.c:4:7
    #1 0x55c542fd86b0 in main /home/matt/ecfuzz/examples/cli/fuzz_target.c:40:3
    #2 0x7f908554accf  (/usr/lib/libc.so.6+0x27ccf) (BuildId: 023ea16fd6c04ef9cf094507024e6ecdb35e02ca)
    #3 0x7f908554ad89 in __libc_start_main (/usr/lib/libc.so.6+0x27d89) (BuildId: 023ea16fd6c04ef9cf094507024e6ecdb35e02ca)
    #4 0x55c542f422c4 in _start (/home/matt/ecfuzz/output/cli_demo/ecfuzz_target.memory-sanitized.fuzz_target.out+0x672c4)

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/matt/ecfuzz/examples/cli/fuzz_target.c:4:7 in do_comparison
Exiting

coverage:    12/12     exec/s: 1631  corpus size: 2    unique crashes: 1    i: 6165    

New crash! execs: 6400   pruned: 1   unique crashes: 2    updating crash log...                              
    CorpusInput:  { coverage: 7, lifetime: 4, preview: "GHIJKL00000000" }
crashing path B...
==3340218==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x561478b4671b in do_comparison /home/matt/ecfuzz/examples/cli/fuzz_target.c:10:15
    #1 0x561478b4671b in main /home/matt/ecfuzz/examples/cli/fuzz_target.c:40:3
    #2 0x7f13677afccf  (/usr/lib/libc.so.6+0x27ccf) (BuildId: 023ea16fd6c04ef9cf094507024e6ecdb35e02ca)
    #3 0x7f13677afd89 in __libc_start_main (/usr/lib/libc.so.6+0x27d89) (BuildId: 023ea16fd6c04ef9cf094507024e6ecdb35e02ca)
    #4 0x561478ab02c4 in _start (/home/matt/ecfuzz/output/cli_demo/ecfuzz_target.memory-sanitized.fuzz_target.out+0x672c4)

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/matt/ecfuzz/examples/cli/fuzz_target.c:10:15 in do_comparison
Exiting

coverage:    12/12     exec/s: 1630  corpus size: 2    unique crashes: 2    i: 6400    
coverage:    12/12     exec/s: 1607  corpus size: 2    unique crashes: 2    i: 10000   

LibFuzzer示例

待办事项:更新LibFuzzer示例

从源代码安装Clang和LLVM

从最新源代码构建 clang 编译器,而不是使用包管理器安装,可能显著提高模糊测试性能。使用 Ninja 构建系统 将 clang 和 LLVM 工具构建并安装到 /opt/bin/ 的推荐配置如下:

# tested with clang v18.0.0
git clone https://github.com/llvm/llvm-project.git
cd llvm-project

# configure the build
cmake -S llvm -B build -G Ninja \
  -DCMAKE_BUILD_TYPE="Release" \
  -DCMAKE_INSTALL_PREFIX=/opt \
  -DLLVM_ENABLE_RUNTIMES=all \
  -DLLVM_PARALLEL_LINK_JOBS=1 \
  -DLLVM_USE_LINKER="lld" \
  -DLLDB_ENABLE_LIBEDIT=1 \
  -DLLDB_ENABLE_PYTHON=1 \
  -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;lld;lldb;polly;compiler-rt'


# build and install
ninja -C build check-llvm
sudo -E ninja -C build install

然后更新环境

# install paths
export ECFUZZ_CC_PATH="/opt/bin/clang"
export ECFUZZ_LLVM_COV_PATH="/opt/bin/llvm-cov"
export ECFUZZ_LLVM_PROFDATA_PATH="/opt/bin/llvm-profdata"

# build options
export CFLAGS="-O3 -mllvm -polly -std=c17 -g -fcolor-diagnostics -fuse-ld=lld -fsanitize=undefined,address"

有关从源代码构建 clang 和 LLVM 的更多信息,请参阅:https://llvm.net.cn/docs/GettingStarted.html#getting-the-source-code-and-building-llvm

有关确保 clang 编译器输出确定性结果的更多信息,请参阅:https://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html,

依赖项