Fil-C: Garbage In, Memory Safety Out
摘要
Fil-C 是一个完全内存安全的 C/C++ 实现,通过 LLVM IR 阶段的不可见能力(invisicaps)将指针值与边界信息捆绑,实现高兼容性,性能损失约 4 倍。
<p><a href="https://lobste.rs/s/badu44/fil_c_garbage_memory_safety_out">Comments</a></p>
查看缓存全文
缓存时间: 2026/07/24 04:59
### TL;DR
Fil-C 是一个完全内存安全的 C/C++ 实现,它通过不可见能力(invisicaps)将指针值与边界信息捆绑,并在 LLVM IR 阶段拦截指针操作,使得编译后的程序即使存在越界或释放后使用错误,也不会让攻击者控制整个内存。性能中位数约慢 4 倍,对 SQLite 约慢 6 倍,但兼容性极高,约一半 C/C++ 软件包可直接编译运行。
## 什么是内存安全?
定义一个内存安全的编程语言:内存逻辑错误不会让攻击者控制整个内存。在非内存安全语言(如传统的 C/C++,演讲者称为“YOLO C”)中,攻击者可以利用数组越界访问、类型混淆等手段,将任意地址写入指针,从而访问或篡改任意内存位置。即使现代操作系统部署了 ASLR、NX bit、CFI、指针认证等缓解措施,攻击者仍能通过 ROP 链或覆盖关键数据(如命令缓冲区)绕过这些防护,甚至利用“怪异执行”将程序的指令序列拼接成任意操作。因此,核心目标是防止攻击者获得“不受限制的内存访问能力”,而是让越界访问仅访问错误索引,释放后使用仅导致程序崩溃。
## 先前工作的不足
从 90 年代起,人们尝试让 C 语言内存安全,但大多数方案未成功:
- **Safe C**:不支持完整的 C 语言、不支持 C++、不支持线程。
- **Sanitizers**:本身不提供内存安全(只检查访问已释放区域,但不阻止访问存活对象)。
- **MTE/PA**:概率性防御,攻击者约有 1/5 的概率绕过。
- **CHERI**:依赖特殊硬件,无法购买,且通用硬件性能长期领先。
## Fil-C 的设计目标与范围
演讲者从管理角度对问题做了范围界定,放弃了不必要的目标:
- **不追求二进制兼容性**:不能与未用 Fil-C 编译的对象混合链接。
- **初期不追求速度**:第一个版本慢了 200 倍,现在中位数约慢 4 倍,SQLite 约慢 6 倍,Blake 3 上甚至比 Rust 更快(得益于更好的 SIMD 支持)。
- **核心目标**:内存安全 + 高度兼容(约一半软件包零修改)。
## 关键诀窍:利用 LLVM IR 的规整性 + 不可见能力
演讲者发现,在 Clang 刚生成 LLVM IR 但尚未经过优化时,指针值的使用方式非常规整:
- 程序员在源码中声明为指针或引用类型的字段,在 LLVM IR 中对应 `ptr` 类型;非指针字段对应整数等其他类型。
- 因此,可以在 LLVM IR 阶段识别所有指针操作,并为每个指针植入**不可见能力(invisicaps)**。
不可见能力的工作原理:
- 每个指针在数据流中表示为两个部分:
- `int val`:指针的整数值(用户可见可操作)。
- 下界(lower bound):用户不可见,随指针流动,存储在局部数据流(寄存器)中。
- 通过下界可以找到能力头(capability header),其中包含上界(upper bound)和辅助字(ox word)。
- 任何分配产生的能力,其下界和上界永远不能包围“红色”标记的区域,因此用户无法修改边界。
- 当指针被存储到内存中时,辅助字会指向一个辅助分配,其中保存该内存中每个指针的下界。
这样,所有指针操作都携带边界信息,且边界信息不可篡改,实现了内存安全。
## 编译器和运行时
Fil-C 基于 Clang/LLVM 修改,在 LLVM IR 阶段植入 invisicaps。运行时负责管理能力头、辅助分配等。演讲者承诺演示一个实际运行的例子(转录中未展开),但强调对于日常使用性能足够。
## 总结
Fil-C 是一个个人项目,目前仅支持 Linux x86-64,但兼容性极高,性能损失可接受,是通往内存安全 C/C++ 的一个实用的垫脚石技术。
**Source**: https://www.youtube.com/watch?v=5F-2Y1LPRek
相似文章
InvisiCaps:Fil-C 能力模型
描述了 Fil-C 的 InvisiCaps 能力模型,该模型通过跟踪指针权限来确保 C 和 C++ 的内存安全,同时追求极致的兼容性和合理的性能。
内存安全的内联汇编
Fil-C 引入了内存安全的内联汇编,确保程序员错误导致 panic 或 trap,而不是错误编译。
Fil-C 优化调用约定
Fil-C 优化调用约定确保 C 程序即使在恶意滥用情况下也能保持内存安全性,同时通过在常见情况下省略安全检查来保持效率。它解释了通过 panic 或定义明确的行为来处理类型违规的通用优化和寄存器传递优化。
Fil-C中内存安全的上下文切换(longjmp, setjmp)
Fil-C引入了setjmp/longjmp和ucontext API的内存安全实现,防止悬空栈损坏,并在误用时触发panic。
发布 Fil-C Linux/x86_64 版本 0.679 · pizlonator/fil-c
Fil-C 0.679 是一个全新版本,它是 C 和 C++ 的一种极度兼容的内存安全实现,采用并发垃圾回收和隐形能力来防止所有内存安全错误,且没有逃生舱口。