On Sat Jun 20, 2026 at 7:47 PM BST, Danilo Krummrich wrote:
> References to dev, data, and file in the declare_drm_ioctls! macro are
> created via unsafe pointer dereferences, producing unbounded lifetimes.
> If an ioctl handler explicitly annotates its parameters with 'static,
> the compiler accepts this, allowing the handler to stash references that
> outlive the ioctl call.
>
> Fix this by routing all references through a helper function whose
> lifetime parameter 'a is tied to a local anchor variable. Since 'a is
> bounded by the anchor's stack lifetime, handlers can no longer demand
> 'static on any parameter.
>
> Cc: [email protected]
> Fixes: 9a69570682b1 ("rust: drm: ioctl: Add DRM ioctl abstraction")
> Reported-by: [email protected]
> Closes: 
> https://lore.kernel.org/all/[email protected]/
> Signed-off-by: Danilo Krummrich <[email protected]>
> ---
>  rust/kernel/drm/ioctl.rs | 59 +++++++++++++++++++++++++++++++---------
>  1 file changed, 46 insertions(+), 13 deletions(-)
>
> diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs
> index cf328101dde4..023e6da5c1e4 100644
> --- a/rust/kernel/drm/ioctl.rs
> +++ b/rust/kernel/drm/ioctl.rs
> @@ -70,6 +70,39 @@ pub mod internal {
>      pub use bindings::drm_device;
>      pub use bindings::drm_file;
>      pub use bindings::drm_ioctl_desc;
> +
> +    /// Call an ioctl handler with lifetime-bounded references.
> +    ///
> +    /// The lifetime `'a` is tied to the `_anchor` parameter. This prevents 
> handlers from
> +    /// declaring `'static` on `dev`, `data`, or `file`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// - `raw_data` must point to a valid, exclusively-owned instance of 
> `Data` for the duration
> +    ///   of the call.
> +    /// - `raw_file` must be a valid pointer to a `struct drm_file`.
> +    #[doc(hidden)]
> +    #[inline(always)]
> +    pub unsafe fn __call_ioctl<
> +        'a,
> +        Dev: 'a,
> +        Data: 'a,
> +        F: super::super::file::DriverFile + 'a,
> +        Ret,
> +    >(
> +        _anchor: &'a (),
> +        dev: &'a Dev,
> +        raw_data: *mut ::core::ffi::c_void,
> +        raw_file: *mut drm_file,
> +        f: impl FnOnce(&'a Dev, &'a mut Data, &'a super::super::File<F>) -> 
> Ret,
> +    ) -> Ret {
> +        // SAFETY: Caller guarantees raw_data points to a valid instance of 
> Data with the correct
> +        // size and alignment, exclusively owned for the duration of the 
> ioctl call.
> +        let data = unsafe { &mut *(raw_data.cast::<Data>()) };
> +        // SAFETY: Caller guarantees raw_file is a valid pointer to a 
> `struct drm_file`.
> +        let file = unsafe { super::super::File::<F>::from_raw(raw_file) };
> +        f(dev, data, file)
> +    }
>  }
>  
>  /// Declare the DRM ioctls for a driver.
> @@ -135,19 +168,19 @@ macro_rules! declare_drm_ioctls {
>                              // dev/file match the current driver these 
> ioctls are being declared
>                              // for, and it's not clear how to enforce this 
> within the type system.
>                              let dev = 
> $crate::drm::device::Device::from_raw(raw_dev);
> -                            // SAFETY: The ioctl argument has size 
> `_IOC_SIZE(cmd)`, which we
> -                            // asserted above matches the size of this type, 
> and all bit patterns of
> -                            // UAPI structs must be valid.
> -                            // The `ioctl` argument is exclusively owned by 
> the handler
> -                            // and guaranteed by the C implementation 
> (`drm_ioctl()`) to remain
> -                            // valid for the entire lifetime of the 
> reference taken here.
> -                            // There is no concurrent access or aliasing; no 
> other references
> -                            // to this object exist during this call.
> -                            let data = unsafe { &mut 
> *(raw_data.cast::<$crate::uapi::$struct>()) };
> -                            // SAFETY: This is just the DRM file structure
> -                            let file = unsafe { 
> $crate::drm::File::from_raw(raw_file) };
> -

This could be more simply fixed by just adding

    let _: for<'a> fn(&'a _, &'a mut _, &'a _) -> _ = $func;

here.

Best,
Gary

> -                            match $func(dev, data, file) {
> +                            let __anchor = ();
> +
> +                            // SAFETY:
> +                            // - The ioctl argument has size 
> `_IOC_SIZE(cmd)`, which we asserted
> +                            //   above matches the size of this type, and 
> all bit patterns of UAPI
> +                            //   structs must be valid. The argument is 
> exclusively owned by this
> +                            //   handler, guaranteed by `drm_ioctl()` to 
> remain valid for the
> +                            //   duration of the call.
> +                            // - `raw_file` is a valid `struct drm_file` 
> pointer provided by the
> +                            //   DRM core.
> +                            match unsafe { 
> $crate::drm::ioctl::internal::__call_ioctl(
> +                                &__anchor, dev, raw_data, raw_file, $func,
> +                            ) } {
>                                  Err(e) => e.to_errno(),
>                                  Ok(i) => i.try_into()
>                                              
> .unwrap_or($crate::error::code::ERANGE.to_errno()),


Reply via email to