[PATCH v3 04/10] rust: uaccess: add UserSliceWriter::write_slice_partial()

Danilo Krummrich posted 10 patches 3 months, 2 weeks ago
[PATCH v3 04/10] rust: uaccess: add UserSliceWriter::write_slice_partial()
Posted by Danilo Krummrich 3 months, 2 weeks ago
The existing write_slice() method is a wrapper around copy_to_user() and
expects the user buffer to be larger than the source buffer.

However, userspace may split up reads in multiple partial operations
providing an offset into the source buffer and a smaller user buffer.

In order to support this common case, provide a helper for partial
writes.

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Matthew Maurer <mmaurer@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/uaccess.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index c2d3dfee8934..539e77a09cbc 100644
--- a/rust/kernel/uaccess.rs
+++ b/rust/kernel/uaccess.rs
@@ -479,6 +479,22 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result {
         Ok(())
     }
 
+    /// Writes raw data to this user pointer from a kernel buffer partially.
+    ///
+    /// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and
+    /// truncates the write to the boundaries of `self` and `data`.
+    ///
+    /// On success, returns the number of bytes written.
+    pub fn write_slice_partial(&mut self, data: &[u8], offset: usize) -> Result<usize> {
+        let end = offset
+            .checked_add(self.len())
+            .unwrap_or(data.len())
+            .min(data.len());
+
+        data.get(offset..end)
+            .map_or(Ok(0), |src| self.write_slice(src).map(|()| src.len()))
+    }
+
     /// Writes the provided Rust value to this userspace pointer.
     ///
     /// Fails with [`EFAULT`] if the write happens on a bad address, or if the write goes out of
-- 
2.51.0
Re: [PATCH v3 04/10] rust: uaccess: add UserSliceWriter::write_slice_partial()
Posted by Alexandre Courbot 3 months, 1 week ago
On Wed Oct 22, 2025 at 11:30 PM JST, Danilo Krummrich wrote:
> The existing write_slice() method is a wrapper around copy_to_user() and
> expects the user buffer to be larger than the source buffer.
>
> However, userspace may split up reads in multiple partial operations
> providing an offset into the source buffer and a smaller user buffer.
>
> In order to support this common case, provide a helper for partial
> writes.
>
> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Reviewed-by: Matthew Maurer <mmaurer@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/uaccess.rs | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
>
> diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
> index c2d3dfee8934..539e77a09cbc 100644
> --- a/rust/kernel/uaccess.rs
> +++ b/rust/kernel/uaccess.rs
> @@ -479,6 +479,22 @@ pub fn write_slice(&mut self, data: &[u8]) -> Result {
>          Ok(())
>      }
>  
> +    /// Writes raw data to this user pointer from a kernel buffer partially.
> +    ///
> +    /// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and
> +    /// truncates the write to the boundaries of `self` and `data`.
> +    ///
> +    /// On success, returns the number of bytes written.
> +    pub fn write_slice_partial(&mut self, data: &[u8], offset: usize) -> Result<usize> {
> +        let end = offset
> +            .checked_add(self.len())
> +            .unwrap_or(data.len())
> +            .min(data.len());

Same suggestion as the read counterpart about `saturating_add`.

Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Re: [PATCH v3 04/10] rust: uaccess: add UserSliceWriter::write_slice_partial()
Posted by Alice Ryhl 3 months, 2 weeks ago
On Wed, Oct 22, 2025 at 04:30:38PM +0200, Danilo Krummrich wrote:
> The existing write_slice() method is a wrapper around copy_to_user() and
> expects the user buffer to be larger than the source buffer.
> 
> However, userspace may split up reads in multiple partial operations
> providing an offset into the source buffer and a smaller user buffer.
> 
> In order to support this common case, provide a helper for partial
> writes.
> 
> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Reviewed-by: Matthew Maurer <mmaurer@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

This code is ok

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

but:

> +    /// Writes raw data to this user pointer from a kernel buffer partially.
> +    ///
> +    /// This is the same as [`Self::write_slice`] but considers the given `offset` into `data` and
> +    /// truncates the write to the boundaries of `self` and `data`.
> +    ///
> +    /// On success, returns the number of bytes written.
> +    pub fn write_slice_partial(&mut self, data: &[u8], offset: usize) -> Result<usize> {
> +        let end = offset
> +            .checked_add(self.len())
> +            .unwrap_or(data.len())
> +            .min(data.len());
> +
> +        data.get(offset..end)
> +            .map_or(Ok(0), |src| self.write_slice(src).map(|()| src.len()))

Isn't it more readable to write it like this?

	let Some(src) = data.get(offset..end) else {
	    return Ok(0);
	};
	
	self.write_slice(src)?;
	Ok(src.len())

Alice
Re: [PATCH v3 04/10] rust: uaccess: add UserSliceWriter::write_slice_partial()
Posted by Miguel Ojeda 3 months, 1 week ago
On Thu, Oct 23, 2025 at 10:33 AM Alice Ryhl <aliceryhl@google.com> wrote:
>
> Isn't it more readable to write it like this?
>
>         let Some(src) = data.get(offset..end) else {
>             return Ok(0);
>         };
>
>         self.write_slice(src)?;
>         Ok(src.len())

Yeah, I also tend to prefer that style for things like this, because
the "main" operation (like forwarding to `write_slice()`) is at the
top-level. rather than deep in a closure. Plus early returns look
closer to C.

Cheers,
Miguel