[PATCH 6/9] rust: io: add checked offset copy helpers

Mike Lothian <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.driver-core,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
I/O mappings often need to copy a bounded byte range rather than
the complete mapping. Add checked helpers that project the requested
range before using the backend copy operation. This keeps raw backend
pointers out of consumers and reports invalid ranges instead.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <[email protected]>
---
 rust/kernel/io.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 95f46bb75f9e..aea346b8b79e 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -225,6 +225,30 @@ fn io_view<'a, IO: Io<'a>, U>(
     Ok(unsafe { IO::Backend::project_view(view, projected_ptr) })
 }
 
+/// Returns a byte-slice view for a given range, performing runtime bounds checks.
+#[inline]
+fn io_byte_slice<'a, IO>(
+    this: IO,
+    offset: usize,
+    len: usize,
+) -> Result<<IO::Backend as IoBackend>::View<'a, [u8]>>
+where
+    IO: Io<'a, Target = [u8]>,
+{
+    let view = this.as_view();
+    let ptr = IO::Backend::as_ptr(view);
+    let end = offset.checked_add(len).ok_or(EINVAL)?;
+
+    if end > ptr.len() {
+        return Err(EINVAL);
+    }
+
+    let projected_ptr =
+        core::ptr::slice_from_raw_parts_mut(ptr.cast::<u8>().wrapping_add(offset), len);
+    // SAFETY: The bounds check above proves that `projected_ptr` is a sub-slice of `ptr`.
+    Ok(unsafe { IO::Backend::project_view(view, projected_ptr) })
+}
+
 /// I/O backends.
 ///
 /// This is an abstract representation to be implemented by arbitrary I/O
@@ -640,6 +664,32 @@ fn copy_to_slice(self, data: &mut [u8])
         }
     }
 
+    /// Copy bytes from `data` to a range of I/O memory.
+    ///
+    /// Returns [`EINVAL`] if `offset..offset + data.len()` is outside the I/O region.
+    #[inline]
+    fn try_copy_from_slice(self, offset: usize, data: &[u8]) -> Result
+    where
+        Self::Backend: IoCopyable,
+        Self: Io<'a, Target = [u8]>,
+    {
+        io_byte_slice(self, offset, data.len())?.copy_from_slice(data);
+        Ok(())
+    }
+
+    /// Copy a range of I/O memory to `data`.
+    ///
+    /// Returns [`EINVAL`] if `offset..offset + data.len()` is outside the I/O region.
+    #[inline]
+    fn try_copy_to_slice(self, offset: usize, data: &mut [u8]) -> Result
+    where
+        Self::Backend: IoCopyable,
+        Self: Io<'a, Target = [u8]>,
+    {
+        io_byte_slice(self, offset, data.len())?.copy_to_slice(data);
+        Ok(())
+    }
+
     /// Fallible 8-bit read with runtime bounds check.
     #[inline(always)]
     fn try_read8(self, offset: usize) -> Result<u8>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.