Re: [PATCH v4 01/16] rust: drm: ioctl: fix unbounded lifetimes in ioctl handler arguments
From: Gary Guo
Date: Thu Jun 25 2026 - 10:51:44 EST
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: stable@xxxxxxxxxxxxxxx
> Fixes: 9a69570682b1 ("rust: drm: ioctl: Add DRM ioctl abstraction")
> Reported-by: sashiko-bot@xxxxxxxxxx
> Closes: https://lore.kernel.org/all/20260620011346.A47D01F000E9@xxxxxxxxxxxxxxx/
> Signed-off-by: Danilo Krummrich <dakr@xxxxxxxxxx>
> ---
> 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()),