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] [day] [month] [year] [list]
Message-ID: <4742691b-0d36-4d9d-a629-95cd046f4f35@gmail.com>
Date: Sat, 25 Jan 2025 00:42:38 +0100
From: Christian Schrefl <chrisi.schrefl@...il.com>
To: Alice Ryhl <aliceryhl@...gle.com>,
 Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
 Arnd Bergmann <arnd@...db.de>
Cc: Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>,
 Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
 Björn Roy Baron <bjorn3_gh@...tonmail.com>,
 Benno Lossin <benno.lossin@...ton.me>,
 Andreas Hindborg <a.hindborg@...nel.org>, Trevor Gross <tmgross@...ch.edu>,
 rust-for-linux@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] rust: miscdevice: change how f_ops vtable is constructed

On 17.01.25 3:22 PM, Alice Ryhl wrote:
> I was helping someone with writing a new Rust abstraction, and we were
> using the miscdevice abstraction as an example. While doing this, it
> became clear to me that the way I implemented the f_ops vtable is
> confusing to new Rust users, and that the approach used by the block
> abstractions is less confusing.
> 
> Thus, update the miscdevice abstractions to use the same approach as
> rust/kernel/block/mq/operations.rs.
> 
> Sorry about the large diff. This changes the indentation of a large
> amount of code.
> 
> Signed-off-by: Alice Ryhl <aliceryhl@...gle.com>
> ---

Reviewed-by: Christian Schrefl <chrisi.schrefl@...il.com>

>  rust/kernel/miscdevice.rs | 295 ++++++++++++++++++++++------------------------
>  1 file changed, 141 insertions(+), 154 deletions(-)
> 
> diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
> index dfb363630c70..ad02434cbeaf 100644
> --- a/rust/kernel/miscdevice.rs
> +++ b/rust/kernel/miscdevice.rs
> @@ -39,7 +39,7 @@ pub const fn into_raw<T: MiscDevice>(self) -> bindings::miscdevice {
>          let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() };
>          result.minor = bindings::MISC_DYNAMIC_MINOR as _;
>          result.name = self.name.as_char_ptr();
> -        result.fops = create_vtable::<T>();
> +        result.fops = MiscdeviceVTable::<T>::build();
>          result
>      }
>  }
> @@ -164,171 +164,158 @@ fn show_fdinfo(
>      }
>  }
>  
> -const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
> -    const fn maybe_fn<T: Copy>(check: bool, func: T) -> Option<T> {
> -        if check {
> -            Some(func)
> -        } else {
> -            None
> +/// A vtable for the file operations of a Rust miscdevice.
> +struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>);
> +
> +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;
>          }
> -    }
>  
> -    struct VtableHelper<T: MiscDevice> {
> -        _t: PhantomData<T>,
> -    }
> -    impl<T: MiscDevice> VtableHelper<T> {
> -        const VTABLE: bindings::file_operations = bindings::file_operations {
> -            open: Some(fops_open::<T>),
> -            release: Some(fops_release::<T>),
> -            unlocked_ioctl: maybe_fn(T::HAS_IOCTL, fops_ioctl::<T>),
> -            #[cfg(CONFIG_COMPAT)]
> -            compat_ioctl: if T::HAS_COMPAT_IOCTL {
> -                Some(fops_compat_ioctl::<T>)
> -            } else if T::HAS_IOCTL {
> -                Some(bindings::compat_ptr_ioctl)
> -            } else {
> -                None
> -            },
> -            show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::<T>),
> -            // SAFETY: All zeros is a valid value for `bindings::file_operations`.
> -            ..unsafe { MaybeUninit::zeroed().assume_init() }
> -        };
> -    }
> +        // SAFETY: The open call of a file can access the private data.
> +        let misc_ptr = unsafe { (*raw_file).private_data };
>  
> -    &VtableHelper::<T>::VTABLE
> -}
> +        // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
> +        // associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
> +        // ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
> +        let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<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 fops_open<T: MiscDevice>(
> -    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:
> +        // * 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) };
>  
> -    // SAFETY: The open call of a file can access the private data.
> -    let misc_ptr = unsafe { (*raw_file).private_data };
> -
> -    // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
> -    // associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
> -    // ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
> -    let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
> +        let ptr = match T::open(file, misc) {
> +            Ok(ptr) => ptr,
> +            Err(err) => return err.to_errno(),
> +        };
>  
> -    // 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) };
> +        // 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_mut() };
>  
> -    let ptr = match T::open(file, misc) {
> -        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_mut() };
> +        0
> +    }
>  
> -    0
> -}
> +    /// # Safety
> +    ///
> +    /// `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 };
> +        // SAFETY: The release call of a file owns the private data.
> +        let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
> +
> +        // 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) });
> +
> +        0
> +    }
>  
> -/// # Safety
> -///
> -/// `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 fops_release<T: MiscDevice>(
> -    _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 };
> -    // SAFETY: The release call of a file owns the private data.
> -    let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
> -
> -    // 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) });
> -
> -    0
> -}
> +    /// # Safety
> +    ///
> +    /// `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 };
> +        // SAFETY: Ioctl calls can borrow the private data of the file.
> +        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> +
> +        // 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 as usize) {
> +            Ok(ret) => ret as c_long,
> +            Err(err) => err.to_errno() as c_long,
> +        }
> +    }
>  
> -/// # Safety
> -///
> -/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
> -unsafe extern "C" fn fops_ioctl<T: MiscDevice>(
> -    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 };
> -    // SAFETY: Ioctl calls can borrow the private data of the file.
> -    let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> -
> -    // 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 as usize) {
> -        Ok(ret) => ret as c_long,
> -        Err(err) => err.to_errno() as c_long,
> +    /// # Safety
> +    ///
> +    /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
> +    #[cfg(CONFIG_COMPAT)]
> +    unsafe extern "C" fn compat_ioctl(
> +        file: *mut bindings::file,
> +        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 };
> +        // SAFETY: Ioctl calls can borrow the private data of the file.
> +        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> +
> +        // 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 as usize) {
> +            Ok(ret) => ret as c_long,
> +            Err(err) => err.to_errno() as c_long,
> +        }
>      }
> -}
>  
> -/// # Safety
> -///
> -/// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
> -#[cfg(CONFIG_COMPAT)]
> -unsafe extern "C" fn fops_compat_ioctl<T: MiscDevice>(
> -    file: *mut bindings::file,
> -    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 };
> -    // SAFETY: Ioctl calls can borrow the private data of the file.
> -    let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> -
> -    // 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 as usize) {
> -        Ok(ret) => ret as c_long,
> -        Err(err) => err.to_errno() as c_long,
> +    /// # Safety
> +    ///
> +    /// - `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 };
> +        // SAFETY: Ioctl calls can borrow the private data of the file.
> +        let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> +        // 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) };
> +        // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
> +        // this method is called.
> +        let m = unsafe { SeqFile::from_raw(seq_file) };
> +
> +        T::show_fdinfo(device, m, file);
>      }
> -}
>  
> -/// # Safety
> -///
> -/// - `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 fops_show_fdinfo<T: MiscDevice>(
> -    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 };
> -    // SAFETY: Ioctl calls can borrow the private data of the file.
> -    let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
> -    // 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) };
> -    // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
> -    // this method is called.
> -    let m = unsafe { SeqFile::from_raw(seq_file) };
> -
> -    T::show_fdinfo(device, m, file);
> +    const VTABLE: bindings::file_operations = bindings::file_operations {
> +        open: Some(Self::open),
> +        release: Some(Self::release),
> +        unlocked_ioctl: if T::HAS_IOCTL {
> +            Some(Self::ioctl)
> +        } else {
> +            None
> +        },
> +        #[cfg(CONFIG_COMPAT)]
> +        compat_ioctl: if T::HAS_COMPAT_IOCTL {
> +            Some(Self::compat_ioctl)
> +        } else if T::HAS_IOCTL {
> +            Some(Self::bindings::compat_ptr_ioctl)
> +        } else {
> +            None
> +        },
> +        show_fdinfo: if T::HAS_SHOW_FDINFO {
> +            Some(Self::show_fdinfo)
> +        } else {
> +            None
> +        },
> +        // SAFETY: All zeros is a valid value for `bindings::file_operations`.
> +        ..unsafe { MaybeUninit::zeroed().assume_init() }
> +    };
> +
> +    const fn build() -> &'static bindings::file_operations {
> +        &Self::VTABLE
> +    }
>  }
> 
> ---
> base-commit: 01b3cb620815fc3feb90ee117d9445a5b608a9f7
> change-id: 20250117-miscdevice-fops-change-260352fd8957
> 
> Best regards,


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ