lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250530142447.166524-7-dakr@kernel.org>
Date: Fri, 30 May 2025 16:24:19 +0200
From: Danilo Krummrich <dakr@...nel.org>
To: gregkh@...uxfoundation.org,
	rafael@...nel.org,
	ojeda@...nel.org,
	alex.gaynor@...il.com,
	boqun.feng@...il.com,
	gary@...yguo.net,
	bjorn3_gh@...tonmail.com,
	benno.lossin@...ton.me,
	a.hindborg@...nel.org,
	aliceryhl@...gle.com,
	tmgross@...ch.edu,
	chrisi.schrefl@...il.com
Cc: rust-for-linux@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	Danilo Krummrich <dakr@...nel.org>
Subject: [PATCH 6/7] rust: miscdevice: expose the parent device as &Device<Bound>

Now that the misc device abstraction design considers device drivers,
take advantage of the fact that we can safely expose the parent of the
misc device as &Device<Bound> within the misc device's file operations.

Drivers can use this bound device reference to access device resources
without additional overhead through Devres::access().

Expose the &Device<Bound> parent, the &Device representation of the misc
device and the registration data through a new type MiscArgs through the
MiscDevice callbacks.

Signed-off-by: Danilo Krummrich <dakr@...nel.org>
---
 rust/kernel/miscdevice.rs        | 173 ++++++++++++++++++++-----------
 samples/rust/rust_misc_device.rs |  20 ++--
 2 files changed, 128 insertions(+), 65 deletions(-)

diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 6801fe72a8a6..937d0945d827 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -18,7 +18,7 @@
     prelude::*,
     seq_file::SeqFile,
     str::CStr,
-    types::{ARef, ForeignOwnable, Opaque},
+    types::{ARef, Opaque},
 };
 use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin, ptr::NonNull};
 
@@ -51,6 +51,7 @@ struct RawDeviceRegistration<T: MiscDevice> {
     #[pin]
     inner: Opaque<bindings::miscdevice>,
     data: NonNull<T::RegistrationData>,
+    private: Opaque<T::Ptr>,
     _t: PhantomData<T>,
 }
 
@@ -67,6 +68,7 @@ fn new<'a>(
             // INVARIANT: `Self` is always embedded in a `MiscDeviceRegistration<T>`, hence `data`
             // is guaranteed to be valid for the entire lifetime of `Self`.
             data: NonNull::from(data),
+            private: Opaque::uninit(),
             inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| {
                 let mut value = opts.into_raw::<T>();
 
@@ -227,11 +229,21 @@ fn drop(self: Pin<&mut Self>) {
     }
 }
 
+/// The arguments passed to the file operation callbacks of a [`MiscDeviceRegistration`].
+pub struct MiscArgs<'a, T: MiscDevice> {
+    /// The [`Device`] representation of the `struct miscdevice`.
+    pub device: &'a Device,
+    /// The parent [`Device`] of [`Self::device`].
+    pub parent: Option<&'a Device<Bound>>,
+    /// The `RegistrationData` passed to [`MiscDeviceRegistration::register`].
+    pub data: &'a T::RegistrationData,
+}
+
 /// Trait implemented by the private data of an open misc device.
 #[vtable]
 pub trait MiscDevice: Sized {
     /// What kind of pointer should `Self` be wrapped in.
-    type Ptr: ForeignOwnable + Send + Sync;
+    type Ptr: Send + Sync;
 
     /// The additional data carried by the [`MiscDeviceRegistration`] for this [`MiscDevice`].
     /// If no additional data is required than the unit type `()` should be used.
@@ -242,12 +254,7 @@ pub trait MiscDevice: Sized {
     /// Called when the misc device is opened.
     ///
     /// The returned pointer will be stored as the private data for the file.
-    fn open(_file: &File, _misc: &Device, _data: &Self::RegistrationData) -> Result<Self::Ptr>;
-
-    /// Called when the misc device is released.
-    fn release(device: Self::Ptr, _file: &File) {
-        drop(device);
-    }
+    fn open(_file: &File, _args: MiscArgs<'_, Self>) -> Result<Self::Ptr>;
 
     /// Handler for ioctls.
     ///
@@ -255,7 +262,8 @@ fn release(device: Self::Ptr, _file: &File) {
     ///
     /// [`kernel::ioctl`]: mod@...te::ioctl
     fn ioctl(
-        _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+        _args: MiscArgs<'_, Self>,
+        _device: &Self::Ptr,
         _file: &File,
         _cmd: u32,
         _arg: usize,
@@ -272,7 +280,8 @@ fn ioctl(
     /// provided, then `compat_ptr_ioctl` will be used instead.
     #[cfg(CONFIG_COMPAT)]
     fn compat_ioctl(
-        _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
+        _args: MiscArgs<'_, Self>,
+        _device: &Self::Ptr,
         _file: &File,
         _cmd: u32,
         _arg: usize,
@@ -281,11 +290,7 @@ fn compat_ioctl(
     }
 
     /// Show info for this fd.
-    fn show_fdinfo(
-        _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
-        _m: &SeqFile,
-        _file: &File,
-    ) {
+    fn show_fdinfo(_args: MiscArgs<'_, Self>, _device: &Self::Ptr, _m: &SeqFile, _file: &File) {
         build_error!(VTABLE_DEFAULT_ERROR)
     }
 }
@@ -296,16 +301,15 @@ fn show_fdinfo(
 impl<T: MiscDevice> MiscdeviceVTable<T> {
     /// # Safety
     ///
-    /// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
-    /// The file must be associated with a `MiscDeviceRegistration<T>`.
-    unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int {
-        // SAFETY: The pointers are valid and for a file being opened.
-        let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
-        if ret != 0 {
-            return ret;
-        }
-
-        // SAFETY: The open call of a file can access the private data.
+    /// This function must only be called from misc device file operations with the `struct file`
+    /// pointer provided by the corresponding callback.
+    unsafe fn registration_from_file<'a>(
+        raw_file: *mut bindings::file,
+    ) -> &'a RawDeviceRegistration<T> {
+        // SAFETY:
+        // * Since `raw_file` comes from a misc device file operation callback, it is a pointer to a
+        //   valid `struct file`.
+        // * All file operations can access the file's private data.
         let misc_ptr = unsafe { (*raw_file).private_data };
 
         // This is a miscdevice, so `misc_open()` sets the private data to a pointer to the
@@ -313,30 +317,57 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
         let misc_ptr = misc_ptr.cast::<bindings::miscdevice>();
 
         // SAFETY:
-        // * `misc_open()` ensures that the `struct miscdevice` can't be unregistered and freed
-        //   during this call to `fops_open`.
+        // * File operation callbacks ensure that the `struct miscdevice` can't be unregistered and
+        //   freed during a call.
         // * The `misc_ptr` always points to the `inner` field of a `RawDeviceRegistration<T>`.
         // * The `RawDeviceRegistration<T>` is valid until the `struct miscdevice` was
         //   unregistered.
-        let registration = unsafe { &*container_of!(misc_ptr, RawDeviceRegistration<T>, inner) };
+        unsafe { &*container_of!(misc_ptr, RawDeviceRegistration<T>, inner) }
+    }
+
+    fn args_from_registration<'a>(registration: &'a RawDeviceRegistration<T>) -> MiscArgs<'a, T> {
+        let parent: Option<&'a Device<Bound>> = registration.device().parent().map(|parent| {
+            // SAFETY: We just convert from `&Device` into `Device<Bound>`.
+            unsafe { Device::as_ref(parent.as_raw()) }
+        });
+
+        MiscArgs {
+            device: registration.device(),
+            parent,
+            data: registration.data(),
+        }
+    }
+
+    /// # Safety
+    ///
+    /// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
+    /// The file must be associated with a `MiscDeviceRegistration<T>`.
+    unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int {
+        // SAFETY: The pointers are valid and for a file being opened.
+        let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
+        if ret != 0 {
+            return ret;
+        }
+
+        // SAFETY: Called from a misc device file operation callback with the corresponding pointer
+        // to a `struct file`.
+        let registration = unsafe { Self::registration_from_file(raw_file) };
 
         // SAFETY:
         // * This underlying file is valid for (much longer than) the duration of `T::open`.
         // * There is no active fdget_pos region on the file on this thread.
         let file = unsafe { File::from_raw_file(raw_file) };
 
-        let ptr = match T::open(file, registration.device(), registration.data()) {
+        let ptr = match T::open(file, Self::args_from_registration(registration)) {
             Ok(ptr) => ptr,
             Err(err) => return err.to_errno(),
         };
 
-        // This overwrites the private data with the value specified by the user, changing the type
-        // of this file's private data. All future accesses to the private data is performed by
-        // other fops_* methods in this file, which all correctly cast the private data to the new
-        // type.
-        //
-        // SAFETY: The open call of a file can access the private data.
-        unsafe { (*raw_file).private_data = ptr.into_foreign().cast() };
+        // SAFETY:
+        // * We only ever write `registration.private` from `open()`, which does not race with other
+        //   file operation callbacks, i.e. there are no concurrent reads.
+        // * `registration.private.get()` is properly aligned.
+        unsafe { registration.private.get().write(ptr) };
 
         0
     }
@@ -346,15 +377,16 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
     /// `file` and `inode` must be the file and inode for a file that is being released. The file
     /// must be associated with a `MiscDeviceRegistration<T>`.
     unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int {
-        // SAFETY: The release call of a file owns the private data.
-        let private = unsafe { (*file).private_data }.cast();
-        // SAFETY: The release call of a file owns the private data.
-        let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
+        // SAFETY: Called from a misc device file operation callback with the corresponding pointer
+        // to a `struct file`.
+        let registration = unsafe { Self::registration_from_file(file) };
 
         // SAFETY:
-        // * The file is valid for the duration of this call.
-        // * There is no active fdget_pos region on the file on this thread.
-        T::release(ptr, unsafe { File::from_raw_file(file) });
+        // * There won't be any subsequent reads or writes to `registration.private` once
+        //   `release()` is called.
+        // * `registration.private` has been initialized in `open()`.
+        // * `registration.private.get()` is properly aligned.
+        unsafe { core::ptr::drop_in_place(registration.private.get()) };
 
         0
     }
@@ -363,17 +395,25 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
     ///
     /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
     unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long {
-        // SAFETY: The ioctl call of a file can access the private data.
-        let private = unsafe { (*file).private_data }.cast();
-        // SAFETY: Ioctl calls can borrow the private data of the file.
-        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+        // SAFETY: Called from a misc device file operation callback with the corresponding pointer
+        // to a `struct file`.
+        let registration = unsafe { Self::registration_from_file(file) };
+
+        // SAFETY:
+        // * `registration.private` is initialized in `open()`, which is guaranteed to called
+        //   before this callback.
+        // * `registration.private.get()` is properly aligned.
+        // * There are no concurrent writes.
+        let private = unsafe { &*registration.private.get() };
 
         // SAFETY:
         // * The file is valid for the duration of this call.
         // * There is no active fdget_pos region on the file on this thread.
         let file = unsafe { File::from_raw_file(file) };
 
-        match T::ioctl(device, file, cmd, arg) {
+        let args = Self::args_from_registration(registration);
+
+        match T::ioctl(args, private, file, cmd, arg) {
             Ok(ret) => ret as c_long,
             Err(err) => err.to_errno() as c_long,
         }
@@ -388,17 +428,25 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
         cmd: c_uint,
         arg: c_ulong,
     ) -> c_long {
-        // SAFETY: The compat ioctl call of a file can access the private data.
-        let private = unsafe { (*file).private_data }.cast();
-        // SAFETY: Ioctl calls can borrow the private data of the file.
-        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+        // SAFETY: Called from a misc device file operation callback with the corresponding pointer
+        // to a `struct file`.
+        let registration = unsafe { Self::registration_from_file(file) };
+
+        // SAFETY:
+        // * `registration.private` is initialized in `open()`, which is guaranteed to called
+        //   before this callback.
+        // * `registration.private.get()` is properly aligned.
+        // * There are no concurrent writes.
+        let private = unsafe { &*registration.private.get() };
 
         // SAFETY:
         // * The file is valid for the duration of this call.
         // * There is no active fdget_pos region on the file on this thread.
         let file = unsafe { File::from_raw_file(file) };
 
-        match T::compat_ioctl(device, file, cmd, arg) {
+        let args = Self::args_from_registration(registration);
+
+        match T::compat_ioctl(args, private, file, cmd, arg) {
             Ok(ret) => ret as c_long,
             Err(err) => err.to_errno() as c_long,
         }
@@ -409,10 +457,17 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
     /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
     /// - `seq_file` must be a valid `struct seq_file` that we can write to.
     unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) {
-        // SAFETY: The release call of a file owns the private data.
-        let private = unsafe { (*file).private_data }.cast();
-        // SAFETY: Ioctl calls can borrow the private data of the file.
-        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
+        // SAFETY: Called from a misc device file operation callback with the corresponding pointer
+        // to a `struct file`.
+        let registration = unsafe { Self::registration_from_file(file) };
+
+        // SAFETY:
+        // * `registration.private` is initialized in `open()`, which is guaranteed to called
+        //   before this callback.
+        // * `registration.private.get()` is properly aligned.
+        // * There are no concurrent writes.
+        let private = unsafe { &*registration.private.get() };
+
         // SAFETY:
         // * The file is valid for the duration of this call.
         // * There is no active fdget_pos region on the file on this thread.
@@ -421,7 +476,9 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
         // which this method is called.
         let m = unsafe { SeqFile::from_raw(seq_file) };
 
-        T::show_fdinfo(device, m, file);
+        let args = Self::args_from_registration(registration);
+
+        T::show_fdinfo(args, private, m, file);
     }
 
     const VTABLE: bindings::file_operations = bindings::file_operations {
diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs
index b60fd063afa8..9bf1a0f64e6e 100644
--- a/samples/rust/rust_misc_device.rs
+++ b/samples/rust/rust_misc_device.rs
@@ -159,7 +159,7 @@
     device::Device,
     fs::File,
     ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
-    miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
+    miscdevice::{MiscArgs, MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
     new_mutex,
     prelude::*,
     sync::{Arc, Mutex},
@@ -223,15 +223,15 @@ impl MiscDevice for RustMiscDevice {
 
     type RegistrationData = Arc<Mutex<Inner>>;
 
-    fn open(_file: &File, misc: &Device, data: &Self::RegistrationData) -> Result<Pin<KBox<Self>>> {
-        let dev = ARef::from(misc);
+    fn open(_file: &File, args: MiscArgs<'_, Self>) -> Result<Pin<KBox<Self>>> {
+        let dev = ARef::from(args.device);
 
         dev_info!(dev, "Opening Rust Misc Device Sample\n");
 
         KBox::try_pin_init(
             try_pin_init! {
                 RustMiscDevice {
-                    shared: data.clone(),
+                    shared: args.data.clone(),
                     unique <- new_mutex!(Inner { value: 0_i32 }),
                     dev: dev,
                 }
@@ -240,8 +240,14 @@ fn open(_file: &File, misc: &Device, data: &Self::RegistrationData) -> Result<Pi
         )
     }
 
-    fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
-        dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
+    fn ioctl(
+        args: MiscArgs<'_, Self>,
+        me: &Self::Ptr,
+        _file: &File,
+        cmd: u32,
+        arg: usize,
+    ) -> Result<isize> {
+        dev_info!(args.device, "IOCTLing Rust Misc Device Sample\n");
 
         let size = _IOC_SIZE(cmd);
 
@@ -256,7 +262,7 @@ fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result
             }
             RUST_MISC_DEV_HELLO => me.hello()?,
             _ => {
-                dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
+                dev_err!(args.device, "-> IOCTL not recognised: {}\n", cmd);
                 return Err(ENOTTY);
             }
         };
-- 
2.49.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ