1个不稳定版本
0.1.0 | 2019年3月30日 |
---|
#29 in #kernel-module
634 个星标 & 23 个关注者
9KB
221 行
使用Rust编写Linux内核模块
基于Linux的嵌入式或物联网系统中的设备驱动在内核空间执行,因此必须完全信任。驱动程序中的任何故障都可能严重影响整个系统。然而,第三方嵌入式硬件制造商通常会将其专有设备驱动程序与其嵌入式设备一起发货。这些树外设备驱动程序通常由于缺乏代码审计而质量较差。
我们提出了一种方法,帮助第三方开发者在不修改内核的情况下提高设备驱动程序的可靠性和安全性:使用称为Rust的内存安全编程语言重写设备驱动程序。Rust的严格语言模型有助于设备驱动程序开发者在编译时检测到许多安全问题。我们设计了一个框架,帮助开发者快速在Rust中构建设备驱动程序。我们还利用Rust的安全功能为开发者提供了一些有用的基础设施,以便他们可以轻松处理内核内存分配和并发管理,同时,一些常见错误(例如,使用后释放)可以减轻。
我们通过在Raspberry Pi 3上实现一个真实世界的设备驱动程序来展示我们框架的通用性,我们的评估表明,我们框架生成的设备驱动程序对于标准的嵌入式系统来说具有可接受的二进制大小,运行时开销可以忽略不计。
有关设计和实现的更多详细信息可以在我们的论文中找到: 确保嵌入式系统设备驱动程序的安全:框架和原型。
要求
工具链
-
x86_64
Rust nightly。在
nightly-2018-09-30-x86_64-unknown-linux-gnu
上测试过。 -
ARMv7
(Raspberry Pi)Rust nightly。此外,您还需要安装新目标
$ rustup target add arm-unknown-linux-gnueabi
和
arm-linux-gnueabihf-
交叉编译器。
Linux 内核头文件
需要一个预构建的内核(包含配置和头文件)。
-
x86_64
您的 Linux 发行版应该提供这个软件包。例如,在 Ubuntu 上,您可以尝试
$ sudo apt-get install linux-headers-`uname -r`
-
ARMv7
(Raspberry Pi)为了使
bindgen
工作您需要 编译自己的内核。
构建
cargo-xbuild
,rust-src
和rustfmt-preview
$ cargo install cargo-xbuild
$ rustup component add --toolchain=nightly rust-src
$ rustup component add rustfmt-preview
- 选择一个示例
$ cd hello_world
-
编译为静态库
x86_64
$ RUST_TARGET_PATH=$(pwd)/.. cargo xbuild --target x86_64-linux-kernel-module
ARMv7
(Raspberry Pi)$ RUST_TARGET_PATH=$(pwd)/.. KDIR=<path-to-your-compiled-kernel> cargo xbuild --target armv7l-linux-kernel-module
-
链接为内核模块
x86_64
$ make
ARMv7
(Raspberry Pi)$ make TARGET=armv7l-linux-kernel-module KDIR=<path-to-your-compiled-kernel> CROSS=arm-linux-gnueabihf-
-
加载并测试
见下文。
-
如果您想清理它
x86_64
$ make clean;cargo clean
ARMv7
(Raspberry Pi)$ make clean TARGET=armv7l-linux-kernel-module KDIR=<path-to-your-compiled-kernel> CROSS=arm-linux-gnueabihf-;cargo clean
加载并测试
示例在 Ubuntu 18.04(Linux 内核 4.15.0-46-generic)上测试过,smsc95xx
在 Raspberry Pi 3(Linux 内核 4.19.29)上测试过。
hello_world
最简单的内核模块。它只打印 "hello" 和 "goodbye"。
$ sudo insmod helloworld.ko # load the module
$ sudo rmmod helloworld # remove the module
$ dmesg # dump kernel messages
yes_chardev
一个简单的字符设备,类似于 Unix 命令 yes
。
$ sudo insmod yes_chardev.ko
$ cat /proc/devices # find the major number of the device 'yes', for example, 243
$ sudo mknod /dev/yes c 243 0 # make a filesystem node (replace 243 with your own major number)
$ sudo cat /dev/yes # read from the device
$ sudo rmmod yes_chardev
simple_sysctl
一个简单的 sysctl 设备驱动程序。
$ sudo insmod simple_sysctl.ko
$ cat /proc/sys/rust/example/test # the default value should be 1
$ sudo sh -c "echo 2 > /proc/sys/rust/example/test" # change the value
$ cat /proc/sys/rust/example/test # now the value is 2
$ sudo rmmod simple_sysctl
还有另一种方法来读写 sysctl 值
$ sysctl rust.example.test # read
$ sudo sysctl -w rust.example.test=2 # write
sync_example
一个简单的示例,说明了 Spinlock
和 Mutex
的使用。
let mutex_data = sync::Mutex::new(50);
let mut data = mutex_data.lock();
println!("Data {} is locked by a mutex", *data);
*data = 100;
println!("Now data is {}", *data);
println!("Hello from Rust!");
上面的代码片段将输出如下
[ 424.328154] Mutex is locked!
[ 424.328156] Data 50 is locked by a mutex
[ 424.328158] Now data is 100
[ 424.328158] Hello from Rust!
[ 424.328160] Mutex is dropped!
smsc95xx
这是一个高度简化的实际设备驱动程序,用于 LAN9512 USB 到以太网控制器,该控制器用于 Raspberry Pi 3。其实现类似于 C 版本。
路线图
编写 Rust 内核模块的努力可以追溯到 2013 年(rust.ko 的第一个提交),在 Rust 的第一个稳定版本发布之前很久。以下是人们已经达到的目标和我们计划在未来实现的目标。
- 使用 JSON 规范文件生成独立的操作系统机器代码
- 使用 cargo-xbuild 重新编译预编译的库(核心、compiler_builtins、alloc)
- 使用 bindgen 生成内核头文件的 Rust 绑定,以重用内核内部定义的现有数据结构和函数。
- 使用 GlobalAlloc 特性实现内核分配器。
- 通过重新实现同步原语实现内核同步。
- 为 LAN9512 实现一个简单的实际设备驱动程序。
- 最小化使用不安全的 Rust。
- 找到定义回调函数的惯用方法。
- 故障恢复。
- 使用静态分析工具对不安全的 Rust 代码进行推理。
致谢
感谢这些关于在 Rust 中编写 Linux 内核驱动的先前工作。他们的尝试给了我们很多启发。
fishinabarrel/linux-kernel-module-rust
: https://github.com/fishinabarrel/linux-kernel-module-rusttsgates/rust.ko
: https://github.com/tsgates/rust.kokernel-roulette
: https://github.com/souvik1997/kernel-roulette
许可证
GPL-2.0
依赖关系
~7KB