1 个不稳定版本
0.1.0 | 2018年11月26日 |
---|
#8 在 #sysctl
634 个星标 & 23 个关注者
5KB
98 行
用Rust编写Linux内核模块
在Linux供电的嵌入式或物联网系统中,设备驱动程序在内核空间执行,因此必须完全可信。任何驱动程序的故障都可能对整个系统产生重大影响。然而,第三方嵌入式硬件制造商通常会将其专有设备驱动程序与其嵌入式设备一起发货。这些树外设备驱动程序通常质量较差,因为缺乏代码审计。
我们提出了一种方法,帮助第三方开发者在不修改内核的情况下提高设备驱动程序的可靠性和安全性:用名为Rust的内存安全编程语言重写设备驱动程序。Rust的严谨语言模型帮助设备驱动程序开发者编译时检测到许多安全问题。我们设计了一个框架,帮助开发者快速在Rust中构建设备驱动程序。我们还利用Rust的安全功能为开发者提供了一些有用的基础设施,使他们可以轻松处理内核内存分配和并发管理,同时,一些常见错误(例如use-after-free)也可以得到缓解。
我们通过在树莓派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
(树莓派)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
(树莓派)为了使
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
(树莓派)$ RUST_TARGET_PATH=$(pwd)/.. KDIR=<path-to-your-compiled-kernel> cargo xbuild --target armv7l-linux-kernel-module
-
作为内核模块链接
x86_64
$ make
ARMv7
(树莓派)$ make TARGET=armv7l-linux-kernel-module KDIR=<path-to-your-compiled-kernel> CROSS=arm-linux-gnueabihf-
-
加载并测试
见下文。
-
如果您想清理它
x86_64
$ make clean;cargo clean
ARMv7
(树莓派)$ 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 重新编译预编译的库(core、compiler_builtins、alloc)
- 使用 bindgen 为内核头文件生成 Rust 绑定,以重用内核内部定义的现有数据结构和函数。
- 使用 GlobalAlloc 特性实现内核分配器。
- 通过重新实现同步原语来实现内核同步。
- 为 LAN9512 提供一个简单的现实世界设备驱动程序。
- 最小化 unsafe Rust 的使用。
- 找到定义回调函数的惯用方法。
- 故障恢复。
- 使用静态分析工具对 unsafe 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