summaryrefslogtreecommitdiffstats
path: root/rust/kernel/src/user_ptr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/src/user_ptr.rs')
-rw-r--r--rust/kernel/src/user_ptr.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/rust/kernel/src/user_ptr.rs b/rust/kernel/src/user_ptr.rs
new file mode 100644
index 000000000000..19ecb711e24a
--- /dev/null
+++ b/rust/kernel/src/user_ptr.rs
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use alloc::vec;
+use alloc::vec::Vec;
+use core::u32;
+
+use crate::bindings;
+use crate::c_types;
+use crate::error;
+
+extern "C" {
+ fn rust_helper_access_ok(addr: *const c_types::c_void, len: c_types::c_ulong) -> c_types::c_int;
+}
+
+/// A reference to an area in userspace memory, which can be either
+/// read-only or read-write.
+///
+/// All methods on this struct are safe: invalid pointers return
+/// `EFAULT`. Concurrent access, _including data races to/from userspace
+/// memory_, is permitted, because fundamentally another userspace
+/// thread / process could always be modifying memory at the same time
+/// (in the same way that userspace Rust's std::io permits data races
+/// with the contents of files on disk). In the presence of a race, the
+/// exact byte values read/written are unspecified but the operation is
+/// well-defined. Kernelspace code should validate its copy of data
+/// after completing a read, and not expect that multiple reads of the
+/// same address will return the same value.
+///
+/// All APIs enforce the invariant that a given byte of memory from userspace
+/// may only be read once. By pretenting double-fetches we avoid TOCTOU
+/// vulnerabilities. This is accomplished by taking `self` by value to prevent
+/// obtaining multiple readers on a given UserSlicePtr, and the readers only
+/// permitting forward reads.
+///
+/// Constructing a `UserSlicePtr` only checks that the range is in valid
+/// userspace memory, and does not depend on the current process (and
+/// can safely be constructed inside a kernel thread with no current
+/// userspace process). Reads and writes wrap the kernel APIs
+/// `copy_from_user` and `copy_to_user`, and check the memory map of the
+/// current process.
+pub struct UserSlicePtr(*mut c_types::c_void, usize);
+
+impl UserSlicePtr {
+ /// Construct a user slice from a raw pointer and a length in bytes.
+ ///
+ /// Checks that the provided range is within the legal area for
+ /// userspace memory, using `access_ok` (e.g., on i386, the range
+ /// must be within the first 3 gigabytes), but does not check that
+ /// the actual pages are mapped in the current process with
+ /// appropriate permissions. Those checks are handled in the read
+ /// and write methods.
+ ///
+ /// This is `unsafe` because if it is called within `set_fs(KERNEL_DS)` context then
+ /// `access_ok` will not do anything. As a result the only place you can safely use this is
+ /// with an `__user` pointer that was provided by the kernel.
+ pub(crate) unsafe fn new(
+ ptr: *mut c_types::c_void,
+ length: usize,
+ ) -> error::KernelResult<UserSlicePtr> {
+ if rust_helper_access_ok(ptr, length as c_types::c_ulong) == 0 {
+ return Err(error::Error::EFAULT);
+ }
+ Ok(UserSlicePtr(ptr, length))
+ }
+
+ /// Read the entirety of the user slice and return it in a `Vec`.
+ ///
+ /// Returns EFAULT if the address does not currently point to
+ /// mapped, readable memory.
+ pub fn read_all(self) -> error::KernelResult<Vec<u8>> {
+ self.reader().read_all()
+ }
+
+ /// Construct a `UserSlicePtrReader` that can incrementally read
+ /// from the user slice.
+ pub fn reader(self) -> UserSlicePtrReader {
+ UserSlicePtrReader(self.0, self.1)
+ }
+
+ /// Write the provided slice into the user slice.
+ ///
+ /// Returns EFAULT if the address does not currently point to
+ /// mapped, writable memory (in which case some data from before the
+ /// fault may be written), or `data` is larger than the user slice
+ /// (in which case no data is written).
+ pub fn write_all(self, data: &[u8]) -> error::KernelResult<()> {
+ self.writer().write(data)
+ }
+
+ /// Construct a `UserSlicePtrWrite` that can incrementally write
+ /// into the user slice.
+ pub fn writer(self) -> UserSlicePtrWriter {
+ UserSlicePtrWriter(self.0, self.1)
+ }
+}
+
+pub struct UserSlicePtrReader(*mut c_types::c_void, usize);
+
+impl UserSlicePtrReader {
+ /// Returns the number of bytes left to be read from this. Note that even
+ /// reading less than this number of bytes may return an Error().
+ pub fn len(&self) -> usize {
+ self.1
+ }
+
+ /// Returns `true` if `self.len()` is 0.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Read all data remaining in the user slice and return it in a `Vec`.
+ ///
+ /// Returns EFAULT if the address does not currently point to
+ /// mapped, readable memory.
+ pub fn read_all(&mut self) -> error::KernelResult<Vec<u8>> {
+ let mut data = vec![0; self.1];
+ self.read(&mut data)?;
+ Ok(data)
+ }
+
+ pub fn read(&mut self, data: &mut [u8]) -> error::KernelResult<()> {
+ if data.len() > self.1 || data.len() > u32::MAX as usize {
+ return Err(error::Error::EFAULT);
+ }
+ let res = unsafe {
+ bindings::_copy_from_user(
+ data.as_mut_ptr() as *mut c_types::c_void,
+ self.0,
+ data.len() as _,
+ )
+ };
+ if res != 0 {
+ return Err(error::Error::EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(data.len());
+ self.1 -= data.len();
+ Ok(())
+ }
+}
+
+pub struct UserSlicePtrWriter(*mut c_types::c_void, usize);
+
+impl UserSlicePtrWriter {
+ pub fn len(&self) -> usize {
+ self.1
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ pub fn write(&mut self, data: &[u8]) -> error::KernelResult<()> {
+ if data.len() > self.1 || data.len() > u32::MAX as usize {
+ return Err(error::Error::EFAULT);
+ }
+ let res = unsafe {
+ bindings::_copy_to_user(
+ self.0,
+ data.as_ptr() as *const c_types::c_void,
+ data.len() as _,
+ )
+ };
+ if res != 0 {
+ return Err(error::Error::EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(data.len());
+ self.1 -= data.len();
+ Ok(())
+ }
+}