-
Notifications
You must be signed in to change notification settings - Fork 474
Description
TLDR: Hi, while migrating our C driver into Rust misc device, I found there's no API expected for module reference.
Can we add such API? I am not sure what's proper fix, so want to ask
Here's long story.
Summary
All rust-for-linux drivers using MiscDevice
are vulnerable to use-after-free crashes during module removal while file operations are active. This is a critical safety issue that causes immediate system reboots.
Problem Description
The rust-for-linux MiscDevice
implementation generates file_operations
structures without setting the .owner
field, which is essential for kernel module reference counting. This leads to use-after-free vulnerabilities when:
- A userspace process opens a misc device file (
/dev/device
) - The kernel module is unloaded (
rmmod
) while the file is still open - VFS continues to access the freed module memory → immediate system crash
In C kernel drivers, this is automatically prevented by setting .owner = THIS_MODULE
in file_operations
, which makes the VFS call try_module_get()
/module_put()
to prevent module unloading while files are open.
Reproduction Steps
Environment: Any system with a rust-for-linux misc device driver
# Terminal 1 - Open the device file
tail -f /dev/rust-misc-device
# Terminal 2 - Unload the module
sudo rmmod rust_misc_device_module
# Result: Immediate system reboot/crash (no error messages)
Expected vs Actual Behavior
Expected (like C drivers):
$ sudo rmmod rust_misc_device_module
rmmod: ERROR: Module 'rust_misc_device_module' is in use
# Module removal blocks safely until file is closed
Actual:
$ sudo rmmod rust_misc_device_module
# System immediately reboots - use-after-free crash
Root Cause Analysis
File: rust/kernel/miscdevice.rs
, line 325
const VTABLE: bindings::file_operations = bindings::file_operations {
// MISSING: owner field is not set!
open: Some(Self::open),
release: Some(Self::release),
unlocked_ioctl: if T::HAS_IOCTL { Some(Self::ioctl) } else { None },
// ...
..unsafe { MaybeUninit::zeroed().assume_init() }
};
The generated file_operations
structure has owner: null
instead of pointing to the module, so the VFS cannot perform automatic module reference counting.
Impact Assessment
- Scope: Every Rust driver using
MiscDevice
trait - Severity: System crashes/reboots during normal module operations
- Examples affected:
samples/rust/rust_misc_device.rs
- All out-of-tree Rust misc device drivers
- Security implications: Use-after-free vulnerabilities in kernel space
Technical Analysis
C driver equivalent (works correctly):
static const struct file_operations my_fops = {
.owner = THIS_MODULE, // ← This prevents the crash
.open = my_open,
.release = my_release,
// ...
};
Rust driver (vulnerable):
// In MiscdeviceVTable<T>::VTABLE - owner field is missing
const VTABLE: bindings::file_operations = bindings::file_operations {
// owner: ???, // No access to THIS_MODULE in this context
open: Some(Self::open),
// ...
};
Proposed Solution Approaches
Option 1: Extend Registration API
// New method alongside existing register()
pub fn register_with_module(
opts: MiscDeviceOptions,
module: &'static ThisModule
) -> impl PinInit<Self, Error>
Option 2: Automatic Injection
Modify the #[vtable]
proc macro system to automatically inject module references during vtable generation.
Option 3: Context-based Solution
Make THIS_MODULE
available in the vtable generation context through thread-local storage or similar mechanism.
Verification
This issue can be confirmed by:
- Building any Rust misc device driver
- Checking that
cat /proc/modules
shows the module as loaded - Opening the device file:
tail -f /dev/device
- Running
rmmod module_name
→ immediate system crash
References
- Linux VFS module reference counting:
fs/file_table.c:__fget_files_rcu()
- C driver example: Any driver in
drivers/
with.owner = THIS_MODULE
- rust-for-linux MiscDevice:
rust/kernel/miscdevice.rs:325
Workaround (Temporary)
Until this is fixed upstream, drivers must implement manual module reference counting:
// In device open()
let success = unsafe { kernel::bindings::try_module_get(module_ptr) };
if !success { return Err(ENODEV); }
// In device drop()
unsafe { kernel::bindings::module_put(module_ptr) };