aravinth-kanesh/polyml-fuzz
GitHub: aravinth-kanesh/polyml-fuzz
针对 Poly/ML 编译器在 ARM64 上的首个系统性模糊测试框架,利用 AFL++ 发现问题并提供修复验证。
Stars: 0 | Forks: 0
# Poly/ML 编译器模糊测试(ARM64)
一个用于在 ARM64 上测试 Poly/ML 编译器前端的基于覆盖率引导的模糊测试框架,作为伦敦国王学院(King's College London)本科毕业设计项目。
**动机:** Poly/ML 是 Isabelle/HOL 证明助手的核心组件,构成了其可信计算基础的一部分。本项目建立了首个针对 ARM64 上 Poly/ML 的系统性基于模糊测试的可靠性基线。
**范围:** 词法分析与语法解析(仅限编译器前端)。
**工具:** AFL++、AddressSanitizer、UndefinedBehaviorSanitizer、LLVM 源代码覆盖率。
**报告:** [aravinth-kaneshalingam-fyp-report](https://github.com/aravinth-kanesh/aravinth-kaneshalingam-fyp-report)
## 快速开始
```
# 在 AWS Graviton (Ubuntu 22.04 ARM64) 或任何 ARM64 Linux 机器上:
git clone https://github.com/aravinth-kanesh/polyml-fuzz.git polyml-fuzz
cd polyml-fuzz
# 一键式安装:安装工具,构建 poly,验证并验证种子
./setup.sh
# 交互式向导:配置阶段、持续时间、实例,然后启动
./fuzz.sh
```
`setup.sh` 处理所有依赖并构建。`fuzz.sh` 引导你完成整个活动周期,包括活动后分析和第 1 阶段到第 2 阶段语料库的交接。
## 仓库结构
```
polyml-fuzz/
|-- setup.sh # One-command setup (run this first on a fresh clone)
|-- fuzz.sh # Interactive wizard: configure and launch campaigns
|-- Makefile # Named targets: make smoke / phase1 / phase2 / report
|-- harness/
| \-- main.c # Initial harness design (reference only; not used in campaigns)
|-- seeds/ # 72 SML seed programs
| |-- basic/ # Core language features (12 seeds, Phase 1)
| |-- operators/ # Infix, precedence, fixity (11 seeds, Phase 1)
| |-- edge-cases/ # Lexer boundary cases, numeric literals ( 9 seeds, Phase 1)
| |-- regression/ # Known UBSan-triggering seeds ( 3 seeds, Phase 1)
| |-- stress/ # Deep nesting, long identifiers (14 seeds, Phase 2)
| |-- modules/ # Structures, signatures, functors (12 seeds, Phase 2)
| \-- datatypes/ # ADTs, pattern matching, records (11 seeds, Phase 2)
|-- scripts/
| |-- ec2-setup.sh # AWS ARM64 system dependency installer
| |-- build-polyml.sh # Build AFL++-instrumented Poly/ML
| |-- build-polyml-coverage.sh # Build LLVM coverage-instrumented Poly/ML (for analysis)
| |-- build-harness.sh # Build harness binary (reference only)
| |-- coverage-report.sh # Generate per-file LLVM source coverage report
| |-- verify-build.sh # Verify AFL++ instrumentation is active
| |-- validate-seeds.sh # Run all 72 seeds through poly
| |-- prepare-evolved-seeds.sh # Copy Phase 1 queue for use as Phase 2 seeds
| |-- fetch-isabelle-seeds.sh # Extract SML seeds from Isabelle source
| |-- sml_mutator.py # Grammar-aware AFL++ custom mutator
| \-- trim-seeds.sh # Minimise large seeds with afl-tmin
|-- campaign/
| |-- start.sh # tmux launcher: opens fuzzer, monitor, and analytics panes
| |-- launch.sh # Core AFL++ campaign launcher (called by start.sh)
| |-- monitor.sh # Live coverage/crash dashboard
| |-- analytics.sh # Edges/hour logging and saturation detection
| |-- analyse.sh # Post-campaign one-liner: crashes + triage + report + coverage
| |-- collect-crashes.sh # Deduplicate and minimise crashes
| |-- triage.sh # Reproduce and classify crashes by fault type
| |-- reproduce-crash.sh # Standalone single-crash reproduction tool
| \-- report.sh # Generate Markdown campaign summary
|-- results/
| \-- early-findings/ # Bugs found before the main campaign
|-- docs/ # Supporting documentation
\-- build/ # Build outputs (gitignored)
```
外部依赖(在 Linux 上由 `./setup.sh` 自动克隆):
- `AFLplusplus/`:[github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
- `polyml-src/`:[github.com/polyml/polyml](https://github.com/polyml/polyml)
## 安装
### 在 AWS Graviton 或任何 ARM64 Linux 机器上
```
git clone https://github.com/aravinth-kanesh/polyml-fuzz.git polyml-fuzz && cd polyml-fuzz
./setup.sh
```
`setup.sh` 运行五个步骤,并跳过已完成的部分,因此可以安全地重新运行:
1. 安装系统包(clang-15、AFL++、autoconf 2.72、构建工具)
2. 构建 AFL++ 插桩的 `poly` 二进制文件
3. 验证 AFL++ 插桩在二进制文件中处于激活状态
4. 验证所有 72 个种子(预期:71 通过,1 超时,0 崩溃)
5. 构建用于活动后分析的 LLVM 覆盖率插桩 `poly` 二进制文件
### 在 macOS 上(仅用于验证)
由于共享内存限制,AFL++ 持久模式在 macOS 上无法工作。你可以在本地构建以验证种子,但所有模糊测试活动必须在 Linux ARM64 上运行。
在运行 `./setup.sh` 之前,你需要手动克隆 `AFLplusplus/` 和 `polyml-src/`。
## 运行活动
### 交互式向导(推荐)
```
./fuzz.sh
```
该向导会提示输入阶段、持续时间和实例数量,然后在一个包含三个窗口的 tmux 会话中启动活动:模糊器、实时监控和分析日志记录。活动结束时,它会自动运行活动后分析并提供启动第 2 阶段的选项。
### Make 目标
```
make smoke # 30-minute validation run (Phase 1, 1 instance)
make phase1 # Full Phase 1 campaign (3 days, 4 instances)
make phase2 # Phase 2 without evolved seeds
make phase2 EVOLVED=phase1-lexer-YYYYMMDD-HHMMSS # Phase 2 with Phase 1 corpus
make monitor CAMPAIGN=phase1-lexer-YYYYMMDD-HHMMSS # Live dashboard
make report CAMPAIGN=phase1-lexer-YYYYMMDD-HHMMSS # Re-run post-campaign analysis
```
### 手动启动
```
./campaign/start.sh --phase 1 --duration 259200 --instances 4
```
## 活动策略
两个连续的 3-4 天阶段,每个阶段针对不同的种子子集。
### 第 1 阶段:词法分析器重点(子集 A)
种子:`basic/`、`operators/`、`edge-cases/`、`regression/`
这些用于测试词法分析器:标记化、操作符、字面量、嵌套注释和边界值。
### 第 2 阶段:解析器重点(子集 B,条件性)
种子:`stress/`、`modules/`、`datatypes/`
这些用于测试解析器,处理深度嵌套、模块层次结构、复杂类型和函子应用。仅在活动 1 产生有效结果后运行。第 2 阶段使用缩短的超时(5000ms)、对模糊器 01 和 02 启用 CMPLOG 插桩以辅助魔字节求解,并在模糊器 03 上使用 `rare` 调度策略以多样化边缘探索。
当通过 `fuzz.sh` 启动时,第 1 阶段完成后会自动提供第 2 阶段,并可选择从第 1 阶段的演化语料库为其播种。在启动第 2 阶段之前,可选择执行可选的 `afl-cmin` 最小化步骤。
### 语法感知变异器(可选)
一个自定义 AFL++ 变异器(`scripts/sml_mutator.py`)应用结构感知的 SML 变异,例如病态浮点字面量、长标识符和嵌套表达式变体。通过以下方式启用:
```
./campaign/launch.sh --phase 2 --grammar-mutator --duration 259200 --instances 4
```
### 饱和度
当每小时发现的新边少于 10 条且连续 3 小时发生时,声明覆盖率饱和。`analytics.sh` 会自动跟踪此信息并记录到 `results//analytics/saturation.log`。
## 活动后分析
当活动通过 `fuzz.sh` 或 `campaign/start.sh` 结束时,分析会自动运行。也可手动运行:
```
./campaign/analyse.sh
```
这按顺序执行四个步骤:
1. 收集并去重崩溃(SHA-256 去重,然后使用 `afl-tmin` 最小化)
2. 对每个崩溃进行诊断(重现、按故障类型分类:UBSan / ASan / 信号)
3. 生成 `results//REPORT.md`,包含覆盖率、崩溃数量和语料库统计信息
4. 对演化语料库运行 LLVM 源代码覆盖率,将每个文件的结果写入 `results//coverage/`
报告包含 `libpolyml/` 区域的总覆盖率,以及如果构建了覆盖率二进制文件,则包含 `arm64.cpp` 的覆盖率。
## 崩溃重现
```
# 从 AFL++ 输出目录重现崩溃
./campaign/reproduce-crash.sh results//fuzzer01/crashes/id:000001,sig:06,...
# 手动重现
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1 \
UBSAN_OPTIONS=print_stacktrace=1 \
poly <
```
## 发现
在预活动验证和两个生产活动中确认了四个可靠性问题。截至 2026 年 4 月,Polyml 源代码仓库中仍有三个未解决;问题 3 的阶段 2 模块展开缺陷(崩溃 1 和 2)在提交 `cf7b84a` 中由 Poly/ML 开发团队确认并修复。
**发现 1 - ARM64 特定的 UBSan 整数溢出(预活动)**
`libpolyml/arm64.cpp:246`(当前上游主版本中的第 440 行)的 UBSan 无符号整数溢出。由两个有效的 SML 程序触发:在 x86-64 上执行相同程序不会产生输出。
```
poly < results/early-findings/ub1/inputs/seed_fun.sml
poly < results/early-findings/ub1/inputs/seed_datatype.sml
```
一行修复(将除数强制转换为 `POLYSIGNED`)已在本地验证,但尚未合并到上游。
**发现 2 - 词法分析器在病态浮点字面量上的无限制内存分配(阶段 1)**
`LEX_.ML` 中的 `readChars` 在读取浮点字面量中 `e`/`E` 后的指数数字时没有长度限制。指数位数百个数字会导致内存分配与长度成正比(确认无需 ASan:200 位数字 → 20 MB,1,000,000 位数字 → 192 MB)。已通过限制(`readCharsMax`,上限为 20 位)在本地验证修复。错误报告已提交至 Poly/ML 邮件列表;分类为缺陷尚待维护者确认。
**发现 3 - 模块展开 SIGSEGV:带有损坏标识符的嵌套结构(阶段 2)**
两个独立发现的 AFL++ 输入在 AFL++ 变异破坏嵌套几何模块中的结构名称时触发模块展开代码中的 SIGSEGV。根本原因:`TYPE_TREE.ML` 中重载解析的类型安全缺陷——`OverloadSetVar` 未在函数声明时提交,导致使用浮点参数调用时出现类型不匹配崩溃。最小化重现程序:`fun ma x y = (x - y); ma 0.0 0.0`。
**发现 4 - 模块展开 SIGSEGV:值绑定中整数字面量模式(阶段 2)**
一个包含 `val 0 = 0` 的 80 字节结构定义会触发 SIGSEGV。解析器接受该整数字面量模式而不出错,但展开器在处理时会崩溃。重现程序:
```
structure Mat0 = struct val 0 = 0 fun s0uare x = x+x fun e 0 = () end; val a = Mat0.s0uare 0.0
```
完整的崩溃输入、诊断报告和 LLVM 覆盖率报告位于 `results/findings/`。有关活动摘要,请参见 `results/findings/README.md`。
## AFL++ 变异策略
| 变异器 | 对 SML 的影响 |
|--------|---------------|
| 位/字节翻转 | 破坏单个标记字符 |
| 算术 | 扰动整数字面量 |
| 有趣的值 | 在字节边界替换 0、MAX_INT、-1 |
| 拼接 | 将语料库条目重新组合为语法混合体 |
| 混乱 | 将上述随机变异堆叠 |
通过 `-x` 传递 SML 令牌字典(`seeds/sml.dict`)以补充混乱变异中的关键字和运算符替换。使用 `-a text` 标志使变异偏向可打印 ASCII 字符。
## 故障排除
**`afl-clang-lto` 未找到**
```
cd AFLplusplus && make distclean && make
```
**macOS 上的 `shmget()` 失败**
在 Linux ARM64 上运行活动。macOS 系统 V 共享内存限制会阻止 AFL++ 持久模式运行。
**0 exec/sec**
```
./scripts/validate-seeds.sh # confirm the binary and seeds work
./scripts/verify-build.sh # confirm AFL++ instrumentation is active
```
**覆盖率报告显示“未生成”**
```
./scripts/build-polyml-coverage.sh # or re-run ./setup.sh
```
标签:AFL++, ARM64, ASan, BSc 毕设, Fuzzing, Fuzzing Framework, ISCAS, King's College London, LLVM 源码覆盖率, Poly/ML, UBSan, 代码安全, 内核测试, 可靠性基线, 地址消毒剂, 子域名枚举, 客户端加密, 未定义行为消毒剂, 漏洞枚举, 系统安全, 编译前端, 编译器, 覆盖率引导, 逆向工具