Skip to main content

kernel/lib/types/
user_slice.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Untrusted userspace slice
3//!
4//! Authors: MarioS271
5
6use crate::lib::panic::kernel_panic;
7use crate::lib::panic_codes::PanicCode;
8use crate::lib::types::fmt_buffer::FmtBuffer;
9use crate::mm::layout::USER_MAX;
10use crate::syscall::result::{SyscallError, SyscallResult};
11
12/// Data structure to store fat pointers provided by userspace
13#[derive(Copy, Clone, Debug)]
14pub struct UserSlice {
15    address: u64,
16    length: u64
17}
18
19impl UserSlice {
20    /// Create a new [`UserSlice`] from the given params
21    ///
22    /// The returned `Ok(Self)` is guaranteed to be fully below `USER_MAX`
23    ///
24    /// # Errors
25    /// Returns `SyscallError::BadAddress` if the given memory region isn't fully below `USER_MAX`
26    /// or if it overflows
27    pub fn new(address: u64, length: u64) -> SyscallResult<Self> {
28        let Some(end) = address.checked_add(length) else {
29            return Err(SyscallError::BadAddress)
30        };
31        if end > USER_MAX {
32            return Err(SyscallError::BadAddress)
33        }
34
35        Ok(Self {
36            address,
37            length
38        })
39    }
40
41    /// Getter for `self.length`
42    pub fn len(&self) -> u64 {
43        self.length
44    }
45
46    /// Check whether the given `self` is empty (zero-length)
47    pub fn is_empty(&self) -> bool {
48        self.length == 0
49    }
50
51    /// Copies the userspace data into the given `slice`
52    ///
53    /// # Panics
54    /// - `slice` is not exactly as large as this [`UserSlice`] (only with the `debug-checks` feature)
55    /// - any of the cases listed on [`Self::partial_copy_to_slice`]
56    pub fn copy_to_slice(&self, slice: &mut [u8]) {
57        #[cfg(feature = "debug-checks")]
58        if self.length != slice.len() as u64 {
59            self.incorrect_size_panic(slice.len(), self.length);
60        }
61
62        self.partial_copy_to_slice(slice, 0, self.length)
63    }
64
65    /// Copies a part of the given userspace data into the given `slice`
66    ///
67    /// # Panics
68    /// - `offset + length` overflows
69    /// - the requested range reaches past the end of this [`UserSlice`]
70    /// - `slice` is smaller than `length`
71    ///
72    /// All of these indicate a kernel-side bug, never bad userspace input
73    pub fn partial_copy_to_slice(&self, slice: &mut [u8], offset: u64, length: u64) {
74        let Some(end) = offset.checked_add(length) else {
75            kernel_panic(
76                PanicCode::InternalKernelError,
77                "UserSlice: adding offset and length overflowed"
78            );
79        };
80
81        if end > self.length {
82            kernel_panic(
83                PanicCode::InternalKernelError,
84                "UserSlice: requested range exceeds slice bounds"
85            );
86        }
87        if length > slice.len() as u64 {
88            self.incorrect_size_panic(slice.len(), length);
89        }
90
91        if length == 0 {
92            return;
93        }
94
95        #[cfg(feature = "debug-checks")]
96        if self.address.checked_add(offset).is_none() {
97            kernel_panic(
98                PanicCode::InternalKernelError,
99                "UserSlice: adding address and offset overflowed"
100            );
101        }
102        let start_addr = self.address.wrapping_add(offset);
103
104        // Safety:
105        // - self.address is under USER_MAX (guaranteed by Self::new)
106        // - length of dst buffer is equal or larger than src buffer
107        unsafe {
108            #[cfg(target_arch = "x86_64")]
109            core::arch::asm!(
110                "rep movsb",
111
112                inout("rsi") start_addr => _,
113                inout("rdi") slice.as_mut_ptr() => _,
114                inout("rcx") length => _,
115
116                options(nostack, preserves_flags)
117            );
118
119            #[cfg(target_arch = "aarch64")]
120            compile_error!("not implemented");
121        }
122    }
123
124    /// Panic Message for when `dst_len` is not sized like `src_len` (they are not equal)
125    #[cold]
126    #[inline(never)]
127    fn incorrect_size_panic(&self, dst_len: usize, src_len: u64) -> ! {
128        use core::fmt::Write;
129
130        let mut fmt_buffer = FmtBuffer::<512>::new();
131        let _ = write!(
132            &mut fmt_buffer,
133            "Attempted to copy data from a UserSlice ({} bytes) into an incorrectly sized slice ({} bytes)",
134            src_len,
135            dst_len
136        );
137
138        kernel_panic(
139            PanicCode::InternalKernelError,
140            fmt_buffer.as_str()
141        );
142    }
143}