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 每月下载量
4MB
2.5K SLoC
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
请参阅以下示例,了解如何测量预设突变数量的源代码覆盖率。
工作原理
接口
- CLI(见
ecfuzz --help
) - Rust 库(文档:https://docs.rs/ecfuzz/latest/ecfuzz/)
- LibFuzzer 模糊测试目标
libfuzz-driver.cpp
可以作为目标从项目仓库中包含,作为StandaloneFuzzTargetMain.c
的替代品。
语料库精炼
使用遗传算法最大化代码覆盖率,同时最小化语料库大小
- 编译带有代码覆盖率映射和清理器的目标
- 突变其中一个种子输入,并通过 stdin、输入文件或命令参数将其发送到目标
- 测量代码覆盖率作为执行的代码分支集合
- 如果突变发现新的分支,将其添加到语料库
- 将新分支覆盖率与现有语料库条目进行比较,并修剪具有新覆盖率子集的条目
- 如果两个覆盖集相等,则选择最短的输入作为平局决定。如果输入长度相等,则按字节顺序排序选择第一个输入。
测试用例最小化
除了在语料库中维护最小输入集外,语料库中的每个输入都可以进行最小化。将移除输入中的字节,直到无法再移除字节而不会改变目标可执行程序返回的代码覆盖率、stdout和stderr。
数字生成
使用xxhash算法进行数字生成,从而生成完全确定性的fuzzer输出。
字节突变
- 异或位翻转 + 字节移位
- 字节插入
- 字节删除
- 字节替换
- 魔法字符替换
- 字典插入
- 标记字典替换
字典突变
为了启用字典突变,必须包含字典文件路径。包含key
项的字典文件中的行将被拼接到输入中。包含key=value
的字典行将使用标记替换插入,例如,通过用value
替换它来突变种子输入中的key
项。键在第一个=
符号处分割,键可以在新行上重复,以表示多个值。
语法模糊测试
除了突变模糊测试外,还可以提供语法文件以指定语法语法树。文件中的每一行定义一个节点,通过第一个=
符号分隔key=value
。必须在定义子节点之前定义父节点。将从结果树的深度优先遍历生成突变,节点导航由哈希选择。
从提供的语法文件--grammar <path>
发送到stdin的语法生成的输入,来自语法文件--arg-grammar <path>
生成的输入将作为参数传递给目标可执行程序。由ECFUZZ_START_MUTATION
和ECFUZZ_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,