diff options
Diffstat (limited to 'rust/kernel/src/user_ptr.rs')
-rw-r--r-- | rust/kernel/src/user_ptr.rs | 176 |
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(()) + } +} |