【Code with SOLO】用 SOLO 构建跨语言 FFI 安全分析器 OmniScope — 10+ 真实项目,独立发现高危 CVE
1. 摘要
作为一名算法工程师,日常使用 Rust + C + CUDA 进行 GPU 加速计算开发,跨语言 FFI 交互是最高频也是最危险的环节。我借助 TRAE SOLO 构建了 OmniScope——一个基于 LLVM IR 的跨语言 FFI 资源安全静态分析框架。
v0.1.5 版本引入了 Zone Classification 架构——只分析语言安全保障失效的地方(unsafe、extern "C"、@cImport、cgo),默认信任语言自身的安全机制。在 10+ 个真实开源项目上完成验证,覆盖密码学、WebAssembly、FFI 密集型等场景,wasmtime 可疑点从 4023 降到 9(-99.8%),并独立发现高危沙箱逃逸漏洞 GHSA-4pww-gw9q-vvvh。项目完全开源(Apache 2.0)。
2. 背景
我是一名算法工程师,日常工作涉及 GPU 加速算法开发,技术栈以 Rust + C + CUDA 为主:
Rust(业务逻辑)→ C(FFI 桥接层)→ CUDA(GPU 计算)
↑ ↑ ↑
所有权模型 裸指针传递 设备内存管理
最严格 最危险 最容易泄漏
每一层都有自己的内存管理哲学,拼在一起的时候,bug 不是"有没有"的问题,而是"什么时候炸"的问题。
我踩过这些坑:
-
Rust
Box::into_raw()把内存交给 C,C 侧free()后 Rust 侧 Drop 再跑一遍 → double free -
CUDA
cudaMalloc的设备指针传给 C wrapper,C 又malloc了中间缓冲,哪个该谁释放? -
Rust
borrow的指针传给 C,C 存进全局变量,Rust 以为借用结束了 → use-after-free
编译器不管、运行时不报、单元测试过得了、上线才炸。 现有工具(Clang SA、CodeQL、MIRI、Valgrind)都是单语言或运行时的,无法解决跨语言静态分析问题。
所以我决定在 LLVM IR 层 做统一分析——不管 Rust、C、Zig、Go,最终都编译到 LLVM IR。
3. 实践过程
架构图
任务拆解
-
设计三层资源状态机架构(Core Engine → Semantic Adapter → Boundary Analyzer)
-
实现数据驱动的语义映射规则系统(14 条规则覆盖 5 种语言)
-
构建 Pass 管线(CFG → DFG → Alias → Taint → Ownership),从 9 个扩展到 15 个
-
用合成测试集(Rust/C++/Zig/Go FFI)验证基础检测能力
-
用 5 个真实开源项目做大规模验证
-
【v0.1.5 新增】 实现 Zone Classification——识别语言安全域的逃逸点,只对
unsafe/FFI边界深分析 -
【v0.1.5 新增】 密码学项目压测(ring、blst、zkcrypto 等)+ WebAssembly 运行时分析(wasmtime)
SOLO 的使用方式
-
架构讨论伙伴 :讨论三层架构设计和资源状态机模型
-
代码生成加速器 :核心分析 Pass 的 Zig 代码生成
-
深度调试助手 :排查 LLVM C API 调用问题(见下方)
-
安全审计员 :对 OmniScope 自身进行代码审计,5 轮审计发现并修复 15+ 个问题
-
文档生成 :README、架构图、测试报告、Release Note
开发模式
约 20-30% 的代码为手敲 (核心架构设计、关键算法逻辑、AI 生成代码的 bug 修复),其余 70-80% 由 SOLO 辅助生成 。AI 负责大量重复性编码和文档工作,人类负责架构决策和质量把关。
踩过的坑
坑 1:LLVM C API 调用方式错误
CFG 构建时 crash,我以为是 LLVM 的 bug。给 SOLO 以下指令:
-
总结当前遇到的问题,节选出对应的代码片段
-
列出你认为存在的问题,是设计问题还是 LLVM 本身的 bug
-
输出为 markdown 供我分析
SOLO 信誓旦旦地告诉我"这是 LLVM 的 bug"。我让它把猜测输出成 markdown,然后自己去查 LLVM 文档和源码,发现其实是我的参数传递方式不对——LLVMGetOperand 返回 LLVMValueRef,而 bb_id_map 期望 LLVMBasicBlockRef。确认应使用 LLVMGetSuccessor() 后,一次修复。
教训:SOLO 给了方向,但验证还得靠自己。AI 会犯错,而且犯错时很自信。
坑 2:SOLO 误删项目文件(真实事故 )
让 SOLO 安装 LLVM 工具链时,操作失误导致项目文件被系统移到回收站 ,workspace 瞬间清空。万幸我提前做了 git 备份 ,完整恢复。
教训:用 AI 操作项目目录时,一定要有版本控制。AI 会犯错,而且犯的错可能很致命。 这也是为什么 OmniScope 的 README 反复强调"单一所有权"和"资源安全"——因为我自己就被"所有权丢失"坑过
坑 3:C++ 误报风暴
v0.1.3 分析 jsoncpp 产生 37 个内存泄漏误报。C++ 的 RAII 模式(智能指针、STL、异常处理)在 IR 层表现为 alloc 无显式 free。实现了 8 层 C++ 误报消除系统 (STL 过滤、RAII 检测、ABI 运行时过滤、Meyers 单例检测、引用计数容器检测等),jsoncpp 误报从 37 降到 0 。
坑 4:__memcpy_chk 噪音
glibc fortified 函数被误报为 FFI RISK,SQLite 中 278 处调用。添加 skip list 修复。
坑 5:噪音太多,找不到真 bug 【v0.1.5 关键转折】
v0.1.4 分析 wasmtime 产生 4023 个可疑点,实际有用的不到 1%。想明白了一件事:我不需要理解语言的安全规则,我只需要知道代码在哪一行开始不再安全。 于是实现了 Zone Classification——Rust safe code 跳过、Zig 普通 slice 跳过、Go 非 cgo 跳过,只盯着 unsafe、extern "C"、@cImport、cgo 这些逃逸点。wasmtime 可疑点从 4023 降到 9。
附与Solo 交互截图
附:我和solo的商业互吹
4. 成果展示
v0.1.5 Zone Classification 验证(10+ 项目)
| 项目 | 语言 | 函数数 | 跳过率 | Issues | 说明 |
|---|---|---|---|---|---|
| ring | Rust + C/asm | 278 | 100% | 0 | Google 密码学库,零噪音 |
| wasmtime | Rust | 619 | 74.3% | 96 | WebAssembly 运行时 |
| blst | Rust + C | 267 | 64.0% | 48 | 以太坊 BLS12-381 签名库 |
| zkcrypto/bls12_381 | Rust | 259 | 66.4% | 0 | ZK 密码学原语 |
| SQLite 3.47.2 | C | 3,237 | - | 8 | 基础验证 |
| jsoncpp 1.9.5 | C++ | 1,537 | - | 3 | C++ RAII 验证 |
| zlib-binding | Rust FFI | 12 | 0% | 14 | FFI 密集型 |
| openssl-wrapper | Rust FFI | 12 | 0% | 7 | FFI 密集型 |
| gnark-crypto | Go | 838 | 29.8% | 1 | Go ZK 库 |
| 其他 | - | - | - | - | ripgrep, rust-sqlite, ark-ff, libsodium 等 |
噪音消除效果
| 指标 | v0.1.4 | v0.1.5 | 变化 |
|---|---|---|---|
| wasmtime 可疑点 | 4,023 | 9 | -99.8% |
| UAF 误报 (blst) | 185 | 48 | -74% |
| 分析耗时 (blst) | 3100ms | 836ms | -73% |
| 分析耗时 (ring) | 793ms | 269ms | -66% |
独立发现真实 CVE
用 OmniScope 分析 wasmtime IR 时,在 wasmtime_fiber_switch 和 occupy_next_slots 两个函数上标记了栈操作异常和边界检查缺失。去源码验证,对应 GHSA-4pww-gw9q-vvvh——一个真实的高危沙箱逃逸漏洞:
Continuation Control-Context Overwrite → Arbitrary Native Control-Flow Redirection → Sandbox Escape
攻击路径:Guest 代码创建小容量 continuation → trap/return 混淆 → cont.bind 越界写入 → 覆盖 saved RIP/RBP → 控制流劫持 → 沙箱逃逸。
这个漏洞是 OmniScope 从 IR 层面独立发现的模式,然后我去源码里定位确认的。
v0.1.4 基础验证(5 项目,5,180 函数)
| 项目 | 语言 | 函数数 | Issues | 泄漏误报 | 耗时 |
|---|---|---|---|---|---|
| SQLite 3.47.2 | C | 3,237 | 8 | 0 | 5.8s |
| libcurl 8.14.0 | C | 68 | 1 | 0 | 0.05s |
| libuv 1.50.0 | C | 145 | 1 | 0 | 0.07s |
| jsoncpp 1.9.5 | C++ | 1,537 | 3 | 0 | 1.4s |
| abseil-cpp 2024 | C++ | 193 | 0 | 0 | 0.37s |
版本迭代对比
| 指标 | v0.1.3 | v0.1.4 | v0.1.5 | 变化 |
|---|---|---|---|---|
| C++ 泄漏误报 | 37(jsoncpp) | 0 | 0 | -100% |
| RC 容器误报 | 9(abseil) | 0 | 0 | -100% |
| wasmtime 噪音 | - | 4,023 | 9 | -99.8% |
| 真实项目数 | 5 | 5 | 10+ | +100% |
| 分析 Pass 数 | 9 | 15 | 15+ | - |
支持的跨语言边界检测
| 调用方 | 被调用方 | 状态 |
|---|---|---|
| Rust | C | Stable |
| C | Rust | Stable |
| Zig | C | Stable |
| Go | C | Experimental |
| C++ | C | Stable |
项目规模(顺带宣传自己的开源项目)
| 项目 | 主要语言 | 代码量 | 状态 |
|---|---|---|---|
| OmniScope | Zig / C / Rust | ~35,000 行 | v0.1.5 已发布 |
| MemScope-rs | Rust | ~100,000 行 | 已上线使用 |
| GoAgent | Go / Python | ~63,000 行 | 已上线使用 |
| 合计 | 5 种语言 | ~200,000 行 | 全部 Apache 2.0 |
项目地址
-
* OmniScope— 跨语言 FFI/Unsafe 安全分析器
-
MemScope-rs — Rust 内存分析与可视化工具
-
GoAgent — 多 Agent 协作开发框架
5. 效果与总结
OmniScope 是我构建的开发者工具集的一部分。从 MemScope(Rust 内存分析)到 OmniScope(跨语言 FFI 分析),再到规划中的 PloyScope,我在系统性地覆盖开发者最痛的安全分析环节。GoAgent 则是 AI 工程化实践——多 Agent 协作 + 记忆蒸馏 + 向量检索 + 长期记忆存储。
v0.1.5 的核心思路转变:不重建语言的安全语义,只识别语言在哪一行代码开始失效。 这让 OmniScope 从一个"什么都能扫但噪音爆炸"的工具,变成了一个"只扫危险地带且精准打击"的工具。
SOLO 在开发中扮演了三个角色:
-
架构讨论伙伴 :帮我理清三层架构和资源状态机设计
-
代码生成加速器 :核心 Pass 的 Zig 代码生成效率提升显著
-
深度调试助手 :CFG crash 时 SOLO 信誓旦旦说是 LLVM 的 bug,我自己查文档发现是参数传递方式不对。SOLO 给了方向,但验证还得靠自己。
当然,SOLO 也翻过车——它误删过我的项目文件,幸好有 git 备份。AI 是强大的工具,但开发者自身的工程素养(版本控制、独立验证、代码审查)才是底线。
所有工具均以 Apache 2.0 开源,因为我踩过的坑,不想让别人再踩一遍。
附图,对于sqlite3.ll 的分析图







