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