Commit 17bbbefb authored by Alice Ryhl's avatar Alice Ryhl Committed by Miguel Ojeda
Browse files

rust: uaccess: add UserSliceReader::strcpy_into_buf



This patch adds a more convenient method for reading C strings from
userspace. Logic is added to NUL-terminate the buffer when necessary so
that a &CStr can be returned.

Note that we treat attempts to read past `self.length` as a fault, so
this returns EFAULT if that limit is exceeded before `buf.len()` is
reached.

Reviewed-by: default avatarDanilo Krummrich <dakr@kernel.org>
Signed-off-by: default avatarAlice Ryhl <aliceryhl@google.com>
Reviewed-by: default avatarBenno Lossin <lossin@kernel.org>
Link: https://lore.kernel.org/r/20250616-strncpy-from-user-v5-2-2d3fb0e1f5af@google.com


[ Use `from_mut` to clean `clippy::ref_as_ptr` lint. Reworded
  title. - Miguel ]
Signed-off-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent 8da881d3
Loading
Loading
Loading
Loading
+59 −1
Original line number Diff line number Diff line
@@ -291,6 +291,65 @@ pub fn read_all<A: Allocator>(mut self, buf: &mut Vec<u8, A>, flags: Flags) -> R
        unsafe { buf.inc_len(len) };
        Ok(())
    }

    /// Read a NUL-terminated string from userspace and return it.
    ///
    /// The string is read into `buf` and a NUL-terminator is added if the end of `buf` is reached.
    /// Since there must be space to add a NUL-terminator, the buffer must not be empty. The
    /// returned `&CStr` points into `buf`.
    ///
    /// Fails with [`EFAULT`] if the read happens on a bad address (some data may have been
    /// copied).
    #[doc(alias = "strncpy_from_user")]
    pub fn strcpy_into_buf<'buf>(self, buf: &'buf mut [u8]) -> Result<&'buf CStr> {
        if buf.is_empty() {
            return Err(EINVAL);
        }

        // SAFETY: The types are compatible and `strncpy_from_user` doesn't write uninitialized
        // bytes to `buf`.
        let mut dst = unsafe { &mut *(core::ptr::from_mut(buf) as *mut [MaybeUninit<u8>]) };

        // We never read more than `self.length` bytes.
        if dst.len() > self.length {
            dst = &mut dst[..self.length];
        }

        let mut len = raw_strncpy_from_user(dst, self.ptr)?;
        if len < dst.len() {
            // Add one to include the NUL-terminator.
            len += 1;
        } else if len < buf.len() {
            // This implies that `len == dst.len() < buf.len()`.
            //
            // This means that we could not fill the entire buffer, but we had to stop reading
            // because we hit the `self.length` limit of this `UserSliceReader`. Since we did not
            // fill the buffer, we treat this case as if we tried to read past the `self.length`
            // limit and received a page fault, which is consistent with other `UserSliceReader`
            // methods that also return page faults when you exceed `self.length`.
            return Err(EFAULT);
        } else {
            // This implies that `len == buf.len()`.
            //
            // This means that we filled the buffer exactly. In this case, we add a NUL-terminator
            // and return it. Unlike the `len < dst.len()` branch, don't modify `len` because it
            // already represents the length including the NUL-terminator.
            //
            // SAFETY: Due to the check at the beginning, the buffer is not empty.
            unsafe { *buf.last_mut().unwrap_unchecked() = 0 };
        }

        // This method consumes `self`, so it can only be called once, thus we do not need to
        // update `self.length`. This sidesteps concerns such as whether `self.length` should be
        // incremented by `len` or `len-1` in the `len == buf.len()` case.

        // SAFETY: There are two cases:
        // * If we hit the `len < dst.len()` case, then `raw_strncpy_from_user` guarantees that
        //   this slice contains exactly one NUL byte at the end of the string.
        // * Otherwise, `raw_strncpy_from_user` guarantees that the string contained no NUL bytes,
        //   and we have since added a NUL byte at the end.
        Ok(unsafe { CStr::from_bytes_with_nul_unchecked(&buf[..len]) })
    }
}

/// A writer for [`UserSlice`].
@@ -380,7 +439,6 @@ pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
/// When this function returns `Ok(len)`, it is guaranteed that the first `len` bytes of `dst` are
/// initialized and non-zero. Furthermore, if `len < dst.len()`, then `dst[len]` is a NUL byte.
#[inline]
#[expect(dead_code)]
fn raw_strncpy_from_user(dst: &mut [MaybeUninit<u8>], src: UserPtr) -> Result<usize> {
    // CAST: Slice lengths are guaranteed to be `<= isize::MAX`.
    let len = dst.len() as isize;