Using Rust for Linux kernel development — toolchain setup, the kernel crate API, writing kernel modules, safety requirements, and C interoperability via bindgen.
Rust support in the Linux kernel was merged in Linux 6.1. The goal is to provide a second language for kernel development that offers stronger memory safety guarantees than C, reducing entire classes of bugs (use-after-free, data races, uninitialized memory) at compile time.
Rust in the kernel uses only core (the #![no_std] subset of the Rust standard library). The full std library is not available. All kernel Rust code must use #![no_std].
Rust is an opt-in language in the kernel. It must be enabled with CONFIG_RUST. Current Rust support exists in:
Core abstractions
rust/kernel/ contains abstractions for memory allocation, synchronization primitives (Mutex, SpinLock, RwLock), task management, work queues, IRQ handling, and more.
Drivers
Device drivers in drivers/ can be written in Rust. The Apple AGX GPU driver, several PCI and platform drivers, and misc device drivers use Rust.
Filesystems
rust/kernel/fs.rs and rust/kernel/fs/ provide filesystem abstractions. The null_blk driver has a Rust implementation.
# Install rustupcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh# In the kernel source directory, set the toolchainrustup override set stable# Add required componentsrustup component add rust-src rustfmt clippy# Install bindgencargo install --locked bindgen-cli
3
Verify requirements are met
# This checks all Rust requirements and explains any failuresmake LLVM=1 rustavailable
4
Enable Rust in kernel configuration
# Via menuconfig: General setup -> Rust supportmake LLVM=1 menuconfig# Or directlyscripts/config --enable CONFIG_RUST
5
Build with LLVM
# Full LLVM toolchain is the best-supported setupmake LLVM=1# Generate rust-analyzer configuration for IDE supportmake LLVM=1 rust-analyzer# Generate rustdoc API documentationmake LLVM=1 rustdocxdg-open Documentation/output/rust/rustdoc/kernel/index.html
The libclang (part of LLVM) is required for bindgen to parse C headers. Building with LLVM=1 ensures a consistent LLVM version across the C and Rust toolchains.
The module! macro generates the module metadata (MODULE_AUTHOR, MODULE_LICENSE, etc.) and the init_module/cleanup_module symbols expected by the kernel module loader.Module initialization returns Result<Self> — returning Err(...) causes the module load to fail with the corresponding error code. Cleanup happens in Drop::drop, which runs when the module is unloaded.
// Every unsafe block MUST have a // SAFETY: comment explaining why// the code is sound — why it cannot cause undefined behavior.// SAFETY: `ptr` is valid for writes of `size` bytes as guaranteed// by the caller's contract documented in the # Safety section.unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
// SAFETY: comments are mandatory before every unsafe block. They document not just that you checked, but what you checked and why it’s sufficient.
/// Dereferences a raw pointer.////// # Safety////// - `ptr` must be non-null and properly aligned./// - `ptr` must point to a valid, initialized `T`./// - The pointed-to value must not be aliased by any mutable reference/// for the duration of the returned reference's lifetime.pub unsafe fn deref_ptr<'a, T>(ptr: *const T) -> &'a T { // SAFETY: All invariants verified by the caller per the # Safety section. unsafe { &*ptr }}
Drivers and subsystem code that only uses the abstraction layer (not bindings directly) should annotate unsafe code appropriately:
// Leaf drivers that use only safe abstractions often have no unsafe blocks at all.// The kernel crate's abstractions encapsulate all necessary unsafe internally.use kernel::prelude::*;use kernel::sync::Mutex;use kernel::net::SkBuff;
Kernel Rust uses kernel::error::Result<T> which is core::result::Result<T, kernel::error::Error>. The Error type wraps kernel error codes (-ENOMEM, -EINVAL, etc.):
use kernel::prelude::*;use kernel::error::{code, Result};fn allocate_buffer(size: usize) -> Result<Vec<u8>> { if size == 0 { return Err(code::EINVAL); } if size > MAX_BUFFER_SIZE { return Err(code::ENOMEM); } let mut buf = Vec::new(); buf.try_resize(size, 0, GFP_KERNEL)?; Ok(buf)}
The ? operator propagates errors, converting from C error codes to Rust Result automatically.
use kernel::prelude::*;use kernel::sync::{Arc, Mutex};struct SharedState { // Data protected by the mutex is stored inside it count: Mutex<u32>,}impl SharedState { fn increment(&self) -> Result { let mut guard = self.count.lock(); *guard += 1; Ok(()) }}
The Mutex<T> type enforces that the data inside can only be accessed while the lock is held — this is enforced at compile time by the borrow checker, unlike C’s struct mutex.
Use type aliases from the kernel prelude for C types, not core::ffi:
use kernel::prelude::*;// c_int, c_uint, c_char, c_void, c_ulong, etc. are available// via the kernel crate — do NOT use core::ffi aliases directlyfn my_callback(arg: c_int) -> c_int { // ... 0}
Kernel Rust code is formatted with rustfmt using default settings (4-space indentation, unlike C’s 8-character tabs):
# Format all Rust sourcesmake LLVM=1 rustfmt# Check formatting (prints diff without modifying)make LLVM=1 rustfmtcheck# Run Clippy lintermake LLVM=1 CLIPPY=1
Follow standard Rust naming conventions (CamelCase for types, snake_case for functions and variables, SCREAMING_SNAKE_CASE for constants). When wrapping C concepts, use a name as close as reasonably possible to the C side:
Rust kernel code is documented with rustdoc (Markdown), not kernel-doc:
/// Returns the number of active connections.////// Returns `0` if the device has not been initialized.////// # Examples////// ```/// let dev = MyDevice::new()?;/// assert_eq!(dev.active_connections(), 0);/// ```pub fn active_connections(&self) -> usize { self.inner.lock().connections.len()}
Use /// for public-facing documentation (rendered by rustdoc) and // for implementation notes. Safety preconditions go under # Safety. Potential panics go under # Panics. Avoid panics — return Result instead.
Browse the generated API documentation at https://rust.docs.kernel.org or generate it locally with make LLVM=1 rustdoc. The docs include the full kernel crate API with search and cross-links.