Write Your First Rust Linux Kernel Module: 2025 Guide

The integration of Rust into the Linux kernel represents one of the most significant developments in systems programming since the kernel's inception. Starting with Linux 6.1 in late 2022 and gaining momentum through 2025, Rust offers memory safety guarantees that eliminate entire categories of vulnerabilities that have plagued C-based kernel code for decades.
This tutorial will guide you through writing, compiling, and testing your first Rust kernel module using the latest Linux 6.8+ infrastructure. You'll learn the essential patterns, understand the Rust-for-Linux framework, and create a working kernel module from scratch.
Link to section: Prerequisites and Environment SetupPrerequisites and Environment Setup
Before diving into kernel module development, ensure your system meets the requirements for Rust kernel development. You'll need Linux 6.1 or newer with Rust support enabled, though Linux 6.8+ provides the most stable experience with production-ready Rust drivers.
Install the required toolchain components:
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Install specific Rust version for kernel development
rustup install nightly-2024-12-01
rustup default nightly-2024-12-01
# Install bindgen for C bindings generation
cargo install bindgen-cli
# Install kernel build dependencies
sudo apt-get update
sudo apt-get install build-essential libncurses-dev bison flex libssl-dev libelf-dev
Download and configure the kernel source with Rust support:
# Clone Linux kernel source
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
# Create kernel configuration
make defconfig
make menuconfig
In the menuconfig interface, navigate to General Setup → Rust support
and enable CONFIG_RUST=y
. This option appears only when all Rust dependencies are properly installed and detected.
Verify your setup by checking the Rust configuration:
# Verify Rust support is enabled
grep CONFIG_RUST .config
# Should output: CONFIG_RUST=y
# Test kernel build with Rust support
make rustavailable
# Should output: Rust is available!
Link to section: Understanding the Rust-for-Linux ArchitectureUnderstanding the Rust-for-Linux Architecture
The Rust-for-Linux project provides a safe abstraction layer over kernel internals without sacrificing performance. Unlike user-space Rust programs, kernel modules operate in a constrained environment without standard library access, heap allocation by default, or panic handling.
The kernel Rust environment includes several key components:
The kernel
crate serves as the primary interface, providing safe wrappers around kernel APIs, memory management primitives, and synchronization mechanisms. The bindings
crate contains automatically generated bindings to C kernel functions, while helper macros simplify common patterns like module initialization and error handling.
Create a new directory for your kernel module:
mkdir ~/rust_kernel_modules
cd ~/rust_kernel_modules
mkdir hello_rust
cd hello_rust
Understanding memory management in kernel space is crucial. Unlike user-space Rust, kernel modules cannot use std::vec::Vec
or std::collections
. Instead, you'll use kernel-specific allocators and data structures that integrate with the kernel's memory management subsystem.

Link to section: Creating Your First Rust Kernel ModuleCreating Your First Rust Kernel Module
Start by creating the basic module structure. Every Rust kernel module requires specific boilerplate and follows patterns established by the Rust-for-Linux project.
Create the main module file:
touch src/lib.rs
mkdir src
Edit src/lib.rs
with your first kernel module:
//! Hello Rust Kernel Module
//!
//! A minimal example demonstrating Rust kernel module basics
use kernel::prelude::*;
// Module metadata
module! {
type: HelloRustModule,
name: "hello_rust",
author: "Your Name",
description: "First Rust kernel module tutorial",
license: "GPL",
}
// Module structure
struct HelloRustModule;
// Kernel module trait implementation
impl kernel::Module for HelloRustModule {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Hello from Rust kernel module!\n");
pr_info!("Module loaded successfully at {}\n", module_name!());
Ok(HelloRustModule)
}
}
impl Drop for HelloRustModule {
fn drop(&mut self) {
pr_info!("Goodbye from Rust kernel module!\n");
}
}
The module!
macro defines essential metadata that appears in modinfo
output. The kernel::Module
trait requires an init
function that returns Result<Self>
, following Rust's error handling conventions even in kernel space.
Create the Kbuild file to integrate with the kernel build system:
# Kbuild
obj-m := hello_rust.o
hello_rust-y := src/lib.o
# Enable Rust support for this module
$(src)/src/lib.o: $(src)/src/lib.rs
Create a Makefile for convenient building:
# Makefile
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m := hello_rust.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
.PHONY: all clean install
Link to section: Advanced Module FunctionalityAdvanced Module Functionality
Extend your module with practical functionality by adding device file operations, kernel parameter handling, and proper error management. This demonstrates real-world kernel programming patterns in Rust.
Modify your src/lib.rs
to include device operations:
use kernel::prelude::*;
use kernel::{
file::{File, Operations},
io_buffer::{IoBufferReader, IoBufferWriter},
miscdev,
sync::{Arc, Mutex},
};
module! {
type: HelloRustModule,
name: "hello_rust",
author: "Your Name",
description: "Advanced Rust kernel module with device operations",
license: "GPL",
}
// Shared module state
struct ModuleData {
message: Mutex<alloc::string::String>,
counter: Mutex<u64>,
}
struct HelloRustModule {
_dev: Pin<Box<miscdev::Registration<HelloRustModule>>>,
data: Arc<ModuleData>,
}
// File operations structure
#[vtable]
impl Operations for HelloRustModule {
type OpenData = Arc<ModuleData>;
type Data = Arc<ModuleData>;
fn open(_context: &Self::OpenData, _file: &File) -> Result<Self::Data> {
pr_info!("Device file opened\n");
Ok(_context.clone())
}
fn read(
data: ArcBorrow<'_, ModuleData>,
_file: &File,
writer: &mut impl IoBufferWriter,
offset: u64,
) -> Result<usize> {
let message = data.message.lock();
let counter = data.counter.lock();
let content = format!("Message: {}\nCounter: {}\n", *message, *counter);
if offset >= content.len() as u64 {
return Ok(0);
}
let remaining = &content[offset as usize..];
let bytes_to_write = core::cmp::min(remaining.len(), writer.len());
writer.write(&remaining.as_bytes()[..bytes_to_write])?;
Ok(bytes_to_write)
}
fn write(
data: ArcBorrow<'_, ModuleData>,
_file: &File,
reader: &mut impl IoBufferReader,
) -> Result<usize> {
let mut message = data.message.lock();
let mut counter = data.counter.lock();
let mut buffer = vec![0u8; reader.len()];
reader.read(&mut buffer)?;
*message = alloc::string::String::from_utf8(buffer)
.map_err(|_| EINVAL)?;
*counter += 1;
pr_info!("Received message: {}\n", *message);
Ok(reader.len())
}
}
impl kernel::Module for HelloRustModule {
fn init(_module: &'static ThisModule) -> Result<Self> {
pr_info!("Advanced Rust kernel module initializing\n");
let data = Arc::try_new(ModuleData {
message: Mutex::new(alloc::string::String::from("Hello from Rust!")),
counter: Mutex::new(0),
})?;
let reg = miscdev::Registration::new_pinned(
fmt!("hello_rust_dev"),
data.clone(),
)?;
pr_info!("Device registered at /dev/hello_rust_dev\n");
Ok(HelloRustModule {
_dev: reg,
data,
})
}
}
impl Drop for HelloRustModule {
fn drop(&mut self) {
pr_info!("Advanced Rust kernel module unloading\n");
}
}
Link to section: Building and Testing Your ModuleBuilding and Testing Your Module
Build your kernel module using the integrated build system. The kernel's Kbuild infrastructure automatically handles Rust compilation, linking, and dependency management.
Build the module:
# Clean any previous builds
make clean
# Build the module
make
# Verify the module was built successfully
ls -la *.ko
# Should show: hello_rust.ko
# Check module information
modinfo hello_rust.ko
The modinfo
command displays metadata embedded during compilation, including author, description, license, and dependencies. This information comes from your module!
macro declaration.
Load and test your module:
# Load the module (requires root privileges)
sudo insmod hello_rust.ko
# Check kernel messages
dmesg | tail -10
# Should show: "Hello from Rust kernel module!"
# List loaded modules
lsmod | grep hello_rust
# Test device file operations (for advanced module)
echo "Test message from userspace" | sudo tee /dev/hello_rust_dev
sudo cat /dev/hello_rust_dev
# Remove the module
sudo rmmod hello_rust
# Verify cleanup messages
dmesg | tail -5
Monitor kernel logs continuously during development:
# Watch kernel messages in real-time
sudo dmesg -w
# In another terminal, load/unload module
sudo insmod hello_rust.ko
sudo rmmod hello_rust
Link to section: Debugging and TroubleshootingDebugging and Troubleshooting
Kernel module development requires different debugging approaches compared to user-space programming. Rust's safety guarantees help prevent many common kernel bugs, but understanding debugging techniques remains essential.
Common compilation errors and their solutions:
Error: "Rust support not available"
# Check Rust installation
rustup show
# Verify kernel configuration
grep CONFIG_RUST /boot/config-$(uname -r)
# Rebuild kernel with Rust support if necessary
Error: "bindgen not found"
# Install bindgen globally
cargo install bindgen-cli
# Verify installation
which bindgen
Error: Module loading fails with "Invalid module format"
# Check kernel version compatibility
uname -r
# Rebuild module against current kernel headers
make clean && make
Use kernel debugging techniques specific to Rust modules:
// Add debug prints throughout your code
pr_debug!("Debug: Variable value = {}\n", some_variable);
// Use conditional compilation for debugging
#[cfg(debug_assertions)]
pr_info!("Debug build - extra logging enabled\n");
// Handle errors explicitly
match some_operation() {
Ok(result) => pr_info!("Operation succeeded: {:?}\n", result),
Err(e) => pr_err!("Operation failed: {:?}\n", e),
}
Enable debug output by setting kernel log levels:
# Enable all kernel debug messages
echo 8 | sudo tee /proc/sys/kernel/printk
# Or enable specific module debugging
echo 'module hello_rust +p' | sudo tee /sys/kernel/debug/dynamic_debug/control
Link to section: Integration with Existing Kernel SubsystemsIntegration with Existing Kernel Subsystems
Modern kernel development often involves integrating with existing subsystems. Rust modules can interact with networking, filesystem, device driver, and scheduling subsystems through safe abstractions.
Here's how to integrate with the kernel's timer subsystem:
use kernel::prelude::*;
use kernel::{
sync::{Arc, Mutex},
time::Timer,
workqueue::{self, Work, WorkItem},
};
struct PeriodicWork {
counter: Arc<Mutex<u64>>,
}
impl WorkItem for PeriodicWork {
type Pointer = Arc<PeriodicWork>;
fn run(this: Arc<PeriodicWork>) {
let mut counter = this.counter.lock();
*counter += 1;
pr_info!("Periodic work executed: count = {}\n", *counter);
// Schedule next execution
let timer = Timer::new();
timer.setup(|| {
workqueue::system().enqueue(this.clone());
});
timer.expires_from_now(kernel::time::msecs_to_jiffies(5000));
timer.add();
}
}
// In your module init function:
let work = Arc::try_new(PeriodicWork {
counter: Arc::try_new(Mutex::new(0))?,
})?;
workqueue::system().enqueue(work);
This demonstrates several key concepts: safe memory sharing between kernel contexts, integration with the kernel's work queue system, and proper resource management using Rust's ownership model.
Link to section: Performance Considerations and Best PracticesPerformance Considerations and Best Practices
Rust kernel modules benefit from zero-cost abstractions, but certain patterns optimize performance and memory usage in kernel space. Understanding these patterns helps you write efficient kernel code.
Memory allocation in kernel space differs significantly from user space. Prefer stack allocation and avoid frequent heap allocations:
// Prefer fixed-size arrays over dynamic allocation
let buffer: [u8; 4096] = [0; 4096];
// Use kernel-specific allocators when dynamic allocation is necessary
use kernel::allocator::KBox;
let dynamic_data = KBox::try_new(SomeStruct::new())?;
// Minimize lock contention in hot paths
use kernel::sync::SpinLock; // For very short critical sections
use kernel::sync::Mutex; // For longer critical sections
The integration represents a fundamental shift in kernel development practices. As more drivers and subsystems adopt Rust, developers gain access to memory-safe systems programming without sacrificing performance. The Linux kernel community continues expanding Rust support, with networking, storage, and scheduling subsystems gradually incorporating Rust modules.
Future developments include enhanced debugging tools, expanded API coverage, and integration with emerging kernel features. The performance advantages of systems programming languages become particularly evident in kernel development, where efficiency directly impacts system stability and throughput.
Learning Rust kernel development positions developers at the forefront of systems programming evolution. The memory safety guarantees eliminate entire categories of vulnerabilities while maintaining the low-level control essential for kernel development. As the ecosystem matures, Rust kernel modules will become increasingly common in production systems.
This tutorial provides the foundation for advanced kernel development in Rust. Start with simple modules, gradually incorporate complex functionality, and engage with the Rust-for-Linux community as you develop expertise in this revolutionary approach to kernel programming.