作者丨王文婧
在很长一段时间里,Linux 内核和驱动程序开发,基本上都是用 C 语言编写的。
然而,C 写 driver 的主要缺陷在于 C 赋予程序员的自由度过大,所谓能力越大责任越大,程序员需要小心处理各种细节以保证 driver 的安全。在一些情况下,程序员容易因为疏忽大意写出不安全的代码。例如申请的内存忘记释放(内存泄漏),使用了已经被释放的指针(use-after-free),数组越界访问(buffer overflow)等等。这些错误大多归因为 C 是一门不安全的语言。
前段时间,随着微软计划用 Rust 取代 C 和 C++ 新闻的曝出,Rust 被认为是 C/C++ 的接班人。由 Mozilla 开发的 Rust 被认为具有较强的安全性。同时,由于特殊的类型系统,Rust 不需要垃圾收集,也被认为是高效的。Rust 程序运行速度极快,可防止段错误并保证线程安全。这些属性使该语言极大地吸引了专注于应用程序安全性的开发人员。香港中文大学系统安全系博士李卓华指出,Rust 主要通过严格的语法和安全检查来限制程序员,“逼迫”程序员写出更安全的代码。例如 Rust 的 ownership 特性保证任何时刻,一个对象最多只有一个可变引用,且在其生命期结束后编译器会自动将其内存释放,从而避免了内存泄漏和 use-after-free 的问题。
《Rust 编程之道》作者张汉东表示,Rust 的优势在于内存安全和并发安全,以及现代化的类型系统,更能保证程序的正确性。并且拥有现代化的工具链,比如对 SIMD、WASM 等的支持。现在已经有人用 Rust 写了 WASM 的虚拟机(Wasmer),允许在 Linux 内核中实现安全的 WebAssembly 运行环境,这样可以避免如系统调用(上下文切换)、用户态 / 内核态数据复制等性能损耗(当前评测情况是提升 10%)。看得出来,使用 Rust 扩展 Linux 内核,会带来更多的可能性。
北美华人安全论坛 BASec 创始人韦韬认为,Rust 有着出色的性能表现,不过对于普通业务而言,性能不是关键,稳定性才是。这个恰恰是 Rust 的最强项。就稳定性而言,Rust 碾压大部分语言,包括 C,C++,Go,Python,PHP 等等。但是没有免费的午餐,Rust 的稳定性来自于 Borrow Checker 的 " 严苛 ",Ownership 机制对于 Rust 入门者有一定的门槛。但大部分情况下,配合上基本的编程规范 (严格限制 unsafe/unwrap/…等),只要 Rust 编译器点头,程序运行起来就没什么问题。需要注意的是,Rust 保障的内存安全不包括防止内存泄露。因为内存泄露的语义和具体应用逻辑强相关,所以还需要做额外的内存泄露检查,但这方面的工具比较现成,一般不是大问题。但即使如此,Rust 写驱动也不太乐观,主要是两个原因。一是需要把底层的 unsafe 仔细封装,因为在驱动场景下,很多操作不满足 Rust safe 的要求,一旦代码里混杂了很多 unsafe,那么因常规安全检验工具的缺乏,Rust 反而会不如 C。二是硬件厂家的工程师从 C 改为 Rust 更漫长,广泛的硬件驱动支持才是 Linux 生态繁荣昌盛的根基,这个生态挑战比单纯的技术挑战更大。
就 Rust 目前开发 Linux 驱动的落地难点问题,李卓华列出以下几点:
Linux 内核中定义的函数,结构体众多,想要在 Rust 中使用需要将它们重新改写成对应的 Rust 版本,这一点人工完成的话工作量极大,自动完成的话(用 bindgen 将 C 翻译成 Rust)代码丑陋杂乱,且缺乏安全抽象。
Linux 内核使用的很多 C 的特性没有对应的 Rust 实现,例如 C 结构体的 bitfield。
Driver 主要是底层的代码,有时需要 Rust 的 unsafe 特性,无法保证安全性。
编写 Driver 主要是提供一组回调函数(callback function),由内核负责在合适的时候调用。这就需要回调函数必须用 FFI(Foreign Function Interface)编写(因为 Linux 内核只提供 C 的函数原型),且必须是全局的(因为要让 Linux 内核可以找到它们),这就限制了开发者封装和组织风格良好的代码。
调用内核函数也是 unsafe 的,除非为其提供一层安全抽象。
Linux 不保证 API 和 ABI 的稳定性,因此难以封装内核函数和数据结构。
在此前 InfoQ 发表的文章中,有些读者评论 Rust 的生态尚未建立,可能会成为落地的阻碍。对此,韦韬表示,Crate 虽然还不够丰富,但现在逐渐在改善;其实更大的问题是低质量的 Crate 混杂其中,会破坏 Rust 应用的稳定性和安全性。但相比较而言,Rust 的 crate 包管理比 C/C++ 强太多,已经有很多落地实践表明是完全可行的。
关于人们关心的如何为 Linux 的内核函数提供一层安全的抽象的问题。专家认为,这里存在不少挑战,但是是一个正确的方向,这个方向如果成功,会极大的提升 Linux 内核的安全性和稳定性。
感兴趣的 Rust 开发人员不必从头开始。目前至少有一个 Github 项目已经存在,它适用于内核模块的框架,有关该项目的背景信息可以在 2019 年 Linux 北美安全峰会的演示文稿中获取。
相关链接:
https://lwn.net/Articles/797828/
https://mssun.me/assets/ares19securing.pdf
https://github.com/lizhuohua/linux-kernel-module-rust
点个在看少个 bug 👇