henryhchchc/lsp-fuzz
GitHub: henryhchchc/lsp-fuzz
LSPFuzz 是基于 LibAFL 实现的灰盒混合模糊测试工具,通过自动生成测试用例来发现语言服务器中的 bug 和崩溃。
Stars: 13 | Forks: 1
# LSPFuzz: Hunting Bugs in Language Servers
LSPFuzz 是一个灰盒混合 fuzzer,用于为 [Language Servers](https://microsoft.github.io/language-server-protocol/) 生成测试用例。
它是基于 [LibAFL](https://github.com/AFLplusplus/LibAFL) 实现的。
## 这是什么?
听起来很熟悉?理应如此!即使你没有任何不当操作,Language Server 中的 bug 也可能导致你的开发工作流中断。LSPFuzz 旨在自动发现此类 bug,避免它们被交付给你。
## 技术细节
LSPFuzz 配备了一个两阶段的 mutation pipeline,能够生成有效且多样的输入,以触发 LSP Server 中各种分析例程。
要了解更多关于其工作原理的信息,请查看以下研究论文:
Hengcheng Zhu, Songqiang Chen, Valerio Terragni, Lili Wei, Yepang Liu, Jiarong Wu, and Shing-Chi Cheung.
**LSPFuzz: Hunting Bugs in Language Servers.**
刊于 _Proceedings of the 40th IEEE/ACM International Conference on Automated Software Engineering._ 韩国首尔。2025 年 11 月。
[🔗 DOI](https://doi.org/10.1109/ASE63991.2025.00183)
| [🎤 会议](https://conf.researchr.org/details/ase-2025/ase-2025-papers/203/LSPFuzz-Hunting-Bugs-in-Language-Servers)
| [📄 预印本](https://scholar.henryhc.net/files/publications/2025/ASE2025-LSPFuzz.pdf)
| [📦 Artifacts](https://doi.org/10.5281/zenodo.17052142)
如果你将 LSPFuzz 用于学术目的,请引用上述论文。
用于开展论文实验的代码快照可以在 [ase25-major-revision](https://github.com/henryhchchc/lsp-fuzz/releases/tag/ase25-major-revision) tag 中找到。
## 用法
### 准备工作
1. 准备一个与 [AFL++](https://github.com/AFLplusplus/AFLplusplus) 兼容的 fuzz target。
强烈建议使用 [LTO mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md) 和 [persistent mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md)。
以下是 fuzz target 的注释模板:
#include "your_header_file.h"
#ifndef __AFL_FUZZ_TESTCASE_LEN
// The following definitions allow compilation without the AFL++ compiler.
ssize_t fuzz_len;
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
const uint8_t fuzz_buf[1024000];
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#define __AFL_FUZZ_INIT() void sync(void);
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
#define __AFL_INIT() sync()
#endif
__AFL_FUZZ_INIT();
int main(int argc, const char* argv[]) {
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
// [Initialization]
// Perform one-time initialization for the target LSP server.
// Or call `LLVMFuzzerInitialize(argc, argv)` here.
const uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
ssize_t len = __AFL_FUZZ_TESTCASE_LEN;
// [Input Processing]
// Process an input here:
// 1. Read `len` bytes from `buf` for LSP inputs, as if they were read from `stdin`.
// 2. Process the LSP inputs. Note that the input contains the `Content-Length` header.
// 3. Release resources and reset states.
// Or call `LLVMFuzzerTestOneInput(buf, len)` here.
}
return 0;
}
2. 获取 coverage map 大小:
AFL_DUMP_MAP_SIZE=1 ./fuzz-target
3. 挖掘用于代码生成的代码片段:
lsp-fuzz-cli mine-code-fragments \
--search-directory \ # Directory containing code files of the target language for the LSP servers
--output # File to store the mined code fragments
### 开始 Fuzzing
```
lsp-fuzz-cli fuzz \
--state \ # Directory to store the fuzzing state (e.g., generated inputs, found crashes)
--lsp-executable \ # Executable file of the LSP server fuzz target
--language-fragments Language=\ # Comma-separated list of files containing the mined code fragments, (e.g., `C=c.frag,CPlusPlus=cpp.frag`)
--coverage-map-size \ # Size of the coverage map to use for coverage-guided fuzzing
--time-budget 24 # Time budget for fuzzing in hours
```
要了解更多关于选项的信息,请运行 `lsp-fuzz-cli fuzz --help`。
### 重现检测到的崩溃
1. 导出生成的触发崩溃的输入:
lsp-fuzz-cli export \
--input /solutions \ # Directory containing the generated crash-triggering inputs
--output # Directory to store the exported crash-triggering inputs
`` 的内容将被组织如下:
├──
│ ├── workspace
│ │ ├── file1.txt
│ │ └── file2.txt
│ └── requests
│ ├── message_0001
│ └── message_0002
├──
│ ├── workspace
│ │ ├── file1.txt
│ │ └── file2.txt
│ └── requests
│ ├── message_0001
│ └── message_0002
└── ...
每个 `` 目录代表 LSPFuzz 生成的一个唯一输入。
在每个 `` 目录中,有两个子目录:`workspace` 和 `requests`。
`workspace` 目录包含代码文件,`requests` 目录包含在 fuzzing 期间发送给 LSP server 的 LSP 请求。
2. 将导出的输入提供给 LSP server:
要重现崩溃,请 `cd` 到包含导出输入的目录。
cat requests/* | ./target-lsp-server
请注意,`target-lsp-server` 是实际接受测试的 LSP server,而不是 fuzz target。
确保它从 `stdin` 读取请求,并且 CLI 选项已正确设置。
为了重现被 sanitizers 捕获的 bug,编译 `target-lsp-server` 时应启用 sanitizers。
## 许可证
LSPFuzz 在 MIT 许可证下发布。详情请参阅 [LICENSE](./LICENSE) 文件。
遵循开放科学政策,研究论文和 artifact 公开发布。
标签:LibAFL, 代码安全, 可视化界面, 测试用例生成, 漏洞枚举, 语言服务器协议, 通知系统