1 个不稳定版本
0.1.0 | 2019年8月18日 |
---|
#15 in #drivers
634 星 & 23 关注者
40KB
944 行
使用Rust编写Linux内核模块
在由Linux供电的嵌入式或物联网系统上,设备驱动程序在内核空间中执行,因此必须完全可信。任何驱动程序中的故障都可能对整个系统产生重大影响。然而,第三方嵌入式硬件制造商通常将其专有设备驱动程序与其嵌入式设备一起发货。这些树外设备驱动程序通常质量较差,因为缺乏代码审核。
我们提出了一种方法,帮助第三方开发者在不修改内核的情况下提高设备驱动程序的可靠性和安全性:使用称为Rust的内存安全编程语言重写设备驱动程序。Rust的严格语言模型帮助设备驱动程序开发者检测许多在编译时出现的安全问题。我们设计了一个框架,帮助开发者快速在Rust中构建设备驱动程序。我们还利用Rust的安全特性为开发者提供了一些有用的基础设施,以便他们可以轻松地处理内核内存分配和并发管理,同时,一些常见错误(例如use-after-free)可以得到缓解。
我们通过在Raspberry Pi 3上实现一个真实世界的设备驱动程序来证明我们框架的通用性,我们的评估显示,我们框架生成的设备驱动程序的二进制大小对于规范嵌入式系统是可以接受的,运行时开销可以忽略不计。
有关设计和实现的更多详细信息,请参阅我们的论文:Securing the Device Drivers of Your Embedded Systems: Framework and Prototype。
需求
工具链
-
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 trait进行内核分配器
- 通过重新实现同步原语进行内核同步
- 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
无运行时依赖
~0–2MB
~37K SLoC