== Overview ==
One of the changes that has received the most attention in the kernel recently, starting with 6.1, is the introduction of the Rust programming language [1].
This new addition allows developers to create external kernel modules using Rust (as an alternative to C). Such modules can be linked against the Linux kernel and loaded or unloaded like any other “regular” .ko module.
The main advantages of writing modules in Rust are:
- memory safety (no out of bounds accesses, no use after free, data race safety)
- strongly typed and statically typed language
- extremely compact source code
Considering that approximately 70% of the security issues [2] that have a CVE assigned are related to memory safety, incorporating Rust into the kernel development process can be extremely beneficial to provide more secure and resilient kernel code, resulting in a reduction of potential CVEs.
== State of the art ==
Enabling Rust in the kernel and providing the infrastructure to build external modules requires specific versions of the “rustc” compiler and the bindgen utility (used to generate the bindings to the C side of the kernel).
At the time of writing this post the requirements are the following:
- rustc 1.62.0
- bindgen 0.56.0
- clang
- llvm
The versioning requirements are strict, meaning that we need exactly these versions. The reason is that the kernel relies on certain “unstable” (non-standard) features [3], so some of them are not available in later versions of the “rustc” compiler.
In addition to that, certain kernel functionalities are conflicting with the upstream Rust support. At the time of writing this post, the conflicting features are the following:
- BTF needs to be disabled (Rust code is not compatible with BPF/BTF)
- CONFIG_INIT_STACK_ZERO needs to be disabled (clang doesn’t support yet the
option -ftrivial-auto-var-init=zero) - module versions / module signing need to be disabled (symbols from Rust code
do not fit in the current modversion name limitation of64 - sizeof(long)
characters)
== Rust support in the Ubuntu kernel ==
The good news is that you don’t have to deal with all of the requirements above to write your own kernel modules in Rust if you are using Ubuntu.
Ubuntu has done all the work to provide the right toolchain in the distro and custom kernel patches (SAUCE) that allow to relax the kernel constraints mentioned above [4].
As a result, it is possible to write out-of-tree kernel modules in Rust using the generic Ubuntu kernel, simply by installing the required packages and running “make”.
As an example, below there is a template of a simple “hello world” kernel module written in Rust.
To build this module, install the toolchain requirements:
$ sudo apt update
$ sudo apt install rustc-1.62 rust-1.62-src rustfmt-1.62 \
bindgen-0.56 llvm clang gcc make \
linux-lib-rust-`uname -r` \
linux-headers-`uname -r`
Create Makefile and hello_rust.rs using the template below and run make
:
$ make
make[1]: Entering directory '/usr/src/linux-headers-6.2.0-18-generic'
RUSTC [M] /home/ubuntu/hello_rust.o
MODPOST /home/ubuntu/Module.symvers
CC [M] /home/ubuntu/hello_rust.mod.o
LD [M] /home/ubuntu/hello_rust.ko
BTF [M] /home/ubuntu/hello_rust.ko
Skipping BTF generation for /home/ubuntu/hello_rust.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.2.0-18-generic'
At this point you should be able to load/unload the module:
$ sudo insmod ./hello_rust.ko
$ sudo dmesg | tail -1
[ 53.191032] hello_rust: Hello from Rust
$ sudo rmmod hello_rust
$ sudo dmesg | tail -1
[ 74.054869] hello_rust: Goodbye from Rust
Makefile
NAME=hello_rust
ifndef KERNELRELEASE
ifndef KDIR
KDIR:=/lib/modules/`uname -r`/build
endif
PWD := $(shell pwd)
rust_flags=CROSS_COMPILE=x86_64-linux-gnu- HOSTRUSTC=rustc-1.62 RUSTC=rustc-1.62 BINDGEN=bindgen-0.56 RUSTFMT=rustfmt-1.62 RUST_LIB_SRC=/usr/src/rustc-1.62.1/library
all:
@$(MAKE) $(rust_flags) -C $(KDIR) M=$(PWD) modules
install:
@$(MAKE) $(rust_flags) -C $(KDIR) M=$(PWD) modules_install
clean:
@rm -f *.o *.ko *.mod* .*.cmd *.d Module.symvers modules.order
@rm -rf .tmp_versions
else
obj-m := $(NAME).o
endif
hello_rust.rs
// SPDX-License-Identifier: GPL-2.0
//! Rust hello world example.
use kernel::prelude::*;
module! {
type: HelloRust,
name: "hello_rust",
author: "Andrea Righi <andrea.righi@canonical.com>",
description: "Rust hello world example",
license: "GPL",
}
struct HelloRust {
}
impl kernel::Module for HelloRust {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Hello from Rust\n");
Ok(HelloRust { })
}
}
impl Drop for HelloRust {
fn drop(&mut self) {
pr_info!("Goodbye from Rust\n");
}
}
== Conclusion ==
So, the question is: what can I do right now with Rust?
Short answer: not much. Upstream support for Rust at the moment is very minimal, Linus requested very clearly to apply something that could just do “hello world” and that is literally what we got in 6.1.
However, a lot more Rust kernel code has been published and 6.2 has got a few more additional features [5].
Even more features are likely to get merged in the next kernels, to a point when we will be able to write kernel Rust modules usable in real-world scenarios, such as fully functional device drivers, network protocols, in-kernel servers, and so on.
For the reasons mentioned above, Rust support in the Ubuntu kernel should be considered like a “technology preview” for now, not a feature ready for production and at the moment we only have plans to support this in Ubuntu 23.04 ‘Lunar Lobster’.
In conclusion, Ubuntu can now be used by all the developers that want to easily and quickly begin to familiarize themselves with kernel programming in Rust, without dealing with external software/packages and simply using the standard Ubuntu kernel and toolchain.
== References ==
[1] https://lwn.net/Articles/910762/
[2] https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/
[3] https://github.com/Rust-for-Linux/linux/issues/2
[4] https://bugs.launchpad.net/bugs/2007654
[5] https://lwn.net/Articles/914458/