Introduction
Rust is officially supported in the Linux kernel starting with version 6.1. This opens new possibilities for systems programming, drivers, and kernel extensionsโall with Rust’s safety guarantees.
This guide covers building Linux kernel modules in Rust.
Why Rust for Kernel Programming?
Safety in Kernel Code
Traditional C kernel code risks:
โ Buffer overflows โ arbitrary code execution
โ Use-after-free โ system crashes
โ Data races โ kernel panics
โ Integer overflows โ security vulnerabilities
Rust in kernel:
โ
Memory safety at compile time
โ
Thread safety guarantees
โ
Integer overflow checks
โ
No undefined behavior
Real-World Impact
Linux kernel bugs by year:
Year Total Bugs Memory Bugs % Memory
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2015 521 229 44%
2016 445 198 45%
2017 514 237 46%
2018 513 251 49%
Rust could eliminate ~45% of kernel bugs!
Setting Up Rust Kernel Development
Prerequisites
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
# Install kernel sources
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
# Check Rust support
grep CONFIG_RUST .config
# CONFIG_RUST=y (or =m for modules)
Build Requirements
# Rust compiler for kernel
rustc --version # Must be nightly
# Additional tools
rustup component add rust-src
cargo install bindgen
# Kernel build tools
sudo apt install build-essential linux-headers-$(uname -r)
Hello World Kernel Module
Module Structure
// hello.rs
#![no_std]
#![feature(never_type)]
use kernel::prelude::*;
module! {
type: HelloWorld,
name: "hello_world",
author: "Your Name",
license: "GPL",
description: "A simple Hello World kernel module",
}
struct HelloWorld;
impl kernel::module::Module for HelloWorld {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
println!("Hello from Rust kernel module!");
Ok(HelloWorld)
}
}
impl Drop for HelloWorld {
fn drop(&mut self) {
println!("Goodbye from Rust kernel module!");
}
}
Building the Module
# Add to Kconfig
config HELLO_RUST
tristate "Hello World Rust Module"
help
A simple hello world module written in Rust
# Add to Makefile
obj-$(CONFIG_HELLO_RUST) += hello.o
hello-y := hello.rs
# Build
make modules LLVM=1
insmod hello.ko
Testing
# Check loaded modules
lsmod | grep hello_world
# hello_world 16384 0
# Check kernel messages
dmesg
# [12345.678901] Hello from Rust kernel module!
# Unload
rmmod hello_world
# [12345.789012] Goodbye from Rust kernel module!
Character Device Driver
Device Registration
use kernel::prelude::*;
use kernel::file_operations::FileOperations;
use kernel::miscdev::MiscDevice;
module! {
type: CharDevice,
name: "rust_char_dev",
author: "Your Name",
license: "GPL",
description: "Character device driver in Rust",
}
struct CharDeviceOps;
impl FileOperations for CharDeviceOps {
type Data = ();
type OpenData = ();
fn open(_context: &(), _file: &kernel::file::File) -> Result<()> {
println!("Device opened");
Ok(())
}
fn release(_context: &()) {
println!("Device closed");
}
}
struct CharDevice {
_miscdev: MiscDevice,
}
impl kernel::module::Module for CharDevice {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
let miscdev = MiscDevice::new(
CharDeviceOps,
(),
c_str!("rust_device"),
)?;
println!("Character device created");
Ok(CharDevice { _miscdev: miscdev })
}
}
impl Drop for CharDevice {
fn drop(&mut self) {
println!("Character device destroyed");
}
}
Memory and Data Structures
Safe Kernel Allocations
use kernel::alloc::BoxExt;
use kernel::prelude::*;
pub fn allocate_example() -> Result<()> {
// Allocate a boxed i32
let boxed = Box::try_new(42)?;
println!("Value: {}", *boxed);
// Automatically freed when dropped
Ok(())
}
pub fn allocate_vec() -> Result<()> {
// Allocate vector
let mut vec: Vec<i32> = Vec::new();
vec.push(1)?;
vec.push(2)?;
vec.push(3)?;
println!("Vector: {:?}", vec.len());
Ok(())
}
Kernel Data Structures
use kernel::sync::{Arc, Mutex};
use kernel::prelude::*;
pub struct DeviceState {
counter: u32,
name: CString,
}
pub fn create_state() -> Result<()> {
let state = Arc::try_new(Mutex::new(DeviceState {
counter: 0,
name: CString::try_from_fmt(fmt!("device"))?,
}))?;
{
let mut guard = state.lock();
guard.counter += 1;
println!("Counter: {}", guard.counter);
}
Ok(())
}
Synchronization Primitives
Mutex
use kernel::sync::Mutex;
pub fn mutex_example() -> Result<()> {
let counter = Mutex::new(0);
{
let mut c = counter.lock();
*c += 1;
}
println!("Count: {}", *counter.lock());
Ok(())
}
Spinlock
use kernel::sync::SpinLock;
pub fn spinlock_example() -> Result<()> {
let value = SpinLock::new(42);
{
let mut v = value.lock();
*v = 100;
}
println!("Value: {}", *value.lock());
Ok(())
}
Interrupt Handling
IRQ Handler Registration
use kernel::irq::{self, IrqHandler};
use kernel::prelude::*;
struct IrqData {
count: u32,
}
impl IrqHandler for IrqData {
type Data = ();
fn handle_irq(_: &Self::Data) -> irq::IrqReturn {
println!("Interrupt received");
irq::IrqReturn::Handled
}
}
pub fn register_handler() -> Result<()> {
// Register IRQ handler
// irq::request_irq(IRQ_NUM, &handler)?;
Ok(())
}
System Call Interface
Exporting Kernel Symbols
use kernel::prelude::*;
use kernel::module;
pub fn exported_function(value: i32) -> i32 {
value * 2
}
module_export!();
#[export_name = "rust_exported_function"]
pub fn exported(value: i32) -> i32 {
exported_function(value)
}
Real-World Example: Simple Counter Device
#![no_std]
use kernel::prelude::*;
use kernel::file::{File, SeekFrom};
use kernel::file_operations::FileOperations;
use kernel::miscdev::MiscDevice;
use kernel::sync::Mutex;
module! {
type: CounterDevice,
name: "rust_counter",
author: "Kernel Developer",
license: "GPL",
description: "A simple counter device in Rust",
}
struct CounterData {
counter: Mutex<i32>,
}
struct CounterOps;
impl FileOperations for CounterOps {
type Data = CounterData;
type OpenData = ();
fn open(_context: &(), _file: &File) -> Result<()> {
pr_info!("Counter device opened\n");
Ok(())
}
fn release(_context: &CounterData) {
pr_info!("Counter device closed\n");
}
}
struct CounterDevice {
_miscdev: MiscDevice,
}
impl kernel::module::Module for CounterDevice {
fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
let data = CounterData {
counter: Mutex::new(0),
};
let miscdev = MiscDevice::new(
CounterOps,
data,
c_str!("rust_counter"),
)?;
pr_info!("Counter module loaded\n");
Ok(CounterDevice { _miscdev: miscdev })
}
}
impl Drop for CounterDevice {
fn drop(&mut self) {
pr_info!("Counter module unloaded\n");
}
}
Debugging Kernel Modules
Logging
use kernel::prelude::*;
// Different log levels
pr_info!("Information message\n");
pr_warn!("Warning message\n");
pr_err!("Error message\n");
pr_debug!("Debug message (if CONFIG_DEBUG enabled)\n");
// String formatting
let value = 42;
pr_info!("Value: {}\n", value);
GDB Debugging
# Build with debug info
make modules DEBUG=1
# Load module with GDB
gdb vmlinux
(gdb) target remote :1234 # Connect to QEMU
(gdb) add-symbol-file hello.ko 0xXXXX
(gdb) break hello_init
(gdb) continue
Best Practices
โ
Use kernel provided allocators
โ
Always check Result types
โ
Use Mutex for shared state
โ
Document safety requirements
โ
Test thoroughly in VMs
โ
Follow kernel style guidelines
โ
Use pr_info for tracing
Limitations
Current limitations in Rust for Linux:
โ No floating-point support
โ Limited inline assembly support
โ No Rust standard library
โ Async/await limited
โ
But rapidly improving with each kernel release
Glossary
- Module: Loadable kernel object (.ko file)
- Character Device: Device providing byte-stream interface
- Spinlock: Lock for interrupt contexts
- IRQ: Interrupt request handler
- pr_info: Kernel logging macro
Comments