Commit 8da881d3 authored by Alice Ryhl's avatar Alice Ryhl Committed by Miguel Ojeda
Browse files

rust: uaccess: add strncpy_from_user



This patch adds a direct wrapper around the C function of the same name.
It's not really intended for direct use by Rust code since
strncpy_from_user has a somewhat unfortunate API where it only
nul-terminates the buffer if there's space for the nul-terminator. This
means that a direct Rust wrapper around it could not return a &CStr
since the buffer may not be a cstring. However, we still add the method
to build more convenient APIs on top of it, which will happen in
subsequent patches.

Reviewed-by: default avatarDanilo Krummrich <dakr@kernel.org>
Reviewed-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: default avatarBoqun Feng <boqun.feng@gmail.com>
Reviewed-by: default avatarBenno Lossin <lossin@kernel.org>
Signed-off-by: default avatarAlice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20250616-strncpy-from-user-v5-1-2d3fb0e1f5af@google.com


[ Reworded title. - Miguel ]
Signed-off-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent e8fa0481
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@
    alloc::{Allocator, Flags},
    bindings,
    error::Result,
    ffi::c_void,
    ffi::{c_char, c_void},
    prelude::*,
    transmute::{AsBytes, FromBytes},
};
@@ -367,3 +367,37 @@ pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
        Ok(())
    }
}

/// Reads a nul-terminated string into `dst` and returns the length.
///
/// This reads from userspace until a NUL byte is encountered, or until `dst.len()` bytes have been
/// read. Fails with [`EFAULT`] if a read happens on a bad address (some data may have been
/// copied). When the end of the buffer is encountered, no NUL byte is added, so the string is
/// *not* guaranteed to be NUL-terminated when `Ok(dst.len())` is returned.
///
/// # Guarantees
///
/// 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;

    // SAFETY: `dst` is valid for writing `dst.len()` bytes.
    let res = unsafe {
        bindings::strncpy_from_user(dst.as_mut_ptr().cast::<c_char>(), src as *const c_char, len)
    };

    if res < 0 {
        return Err(Error::from_errno(res as i32));
    }

    #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)]
    assert!(res <= len);

    // GUARANTEES: `strncpy_from_user` was successful, so `dst` has contents in accordance with the
    // guarantees of this function.
    Ok(res as usize)
}