keos/syscall/
uaccess.rs

1//! The `uaccess` module provides abstractions for interacting with user-space
2//! memory in a kernel context.
3//!
4//! This module defines several types of user-space pointers that allow the
5//! kernel to access user-space data with various access modes, such as
6//! read-only, write-only, or read-write.
7//!
8//! The types provided by this module include:
9//!
10//! - [`UserPtrRO`]: A one-time, read-only pointer to a user-space object of
11//!   type `T`. It allows the kernel to read from user-space but does not permit
12//!   writing to the data.
13//! - [`UserPtrWO`]: A one-time, write-only pointer to a user-space object of
14//!   type `T`. It allows the kernel to write data to user-space but does not
15//!   permit reading from it.
16//! - [`UserU8SliceRO`]: A one-time, read-only pointer to a slice of `u8` in
17//!   user-space. This type allows the kernel to read byte slices from
18//!   user-space.
19//! - [`UserU8SliceWO`]: A one-time, write-only pointer to a slice of `u8` in
20//!   user-space. This type allows the kernel to write byte slices to
21//!   user-space.
22//! - [`UserCString`]: A utility to handle C-style null-terminated strings from
23//!   user-space. It provides methods for reading and converting the string into
24//!   a `String` in the kernel.
25//!
26//! These types use unsafe code to access memory directly. The user-space
27//! addresses must be valid and within bounds to prevent undefined behavior or
28//! security vulnerabilities. To ensure the memory safety, these types use
29//! [`Task::access_ok`] before accessing user-space memory. This function
30//! verifies that the provided memory range is valid and accessible, preventing
31//! potential security vulnerabilities and undefined behavior. If the memory is
32//! not accessible, the operation will fail gracefully instead of causing
33//! undefined behavior.
34use crate::KernelError;
35#[cfg(doc)]
36use crate::task::Task;
37use crate::thread::with_current;
38use abyss::addressing::Va;
39use alloc::string::String;
40use alloc::vec::Vec;
41
42/// A one-time, read-only pointer to a user-space object of type `T`.
43///
44/// This struct allows the kernel to read from user-space while ensuring
45/// safe access patterns. It prevents TOCTOU (Time-of-Check to Time-of-Use)
46/// attacks by taking ownership of the pointer during operations.
47///
48/// # Type Parameter
49/// - `T`: The type of the data being accessed. Must implement `Copy`.
50#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
51pub struct UserPtrRO<T>
52where
53    T: Copy,
54{
55    addr: usize,
56    _ty: core::marker::PhantomData<T>,
57}
58
59impl<T> UserPtrRO<T>
60where
61    T: Copy,
62{
63    /// Creates a new `UserPtrRO` instance with the given user-space address.
64    pub fn new(addr: usize) -> Self {
65        UserPtrRO {
66            addr,
67            _ty: core::marker::PhantomData,
68        }
69    }
70
71    /// Reads a value of type `T` from the user-space address.
72    ///
73    /// Takes ownership of `self` to prevent TOCTOU attacks.
74    ///
75    /// Returns `Ok(T)` if successful, otherwise
76    /// `Err(KernelError::BadAddress)`.
77    pub fn get(self) -> Result<T, KernelError> {
78        let access_range = Va::new(self.addr).ok_or(KernelError::BadAddress)?
79            ..Va::new(self.addr + core::mem::size_of::<T>()).ok_or(KernelError::BadAddress)?;
80        with_current(|th| {
81            let task = th
82                .task
83                .as_ref()
84                .expect("Try to call UserPtrRO::get() on the kernel thread.");
85            if task.access_ok(access_range, false) {
86                Ok(unsafe { { self.addr as *const T }.read_unaligned() })
87            } else {
88                Err(KernelError::BadAddress)
89            }
90        })
91    }
92}
93
94/// A one-time, write-only pointer to a user-space object of type `T`.
95///
96/// This struct allows the kernel to write to user-space while ensuring
97/// safe access patterns. It prevents TOCTOU (Time-of-Check to Time-of-Use)
98/// attacks by taking ownership of the pointer during operations.
99///
100/// # Type Parameter
101/// - `T`: The type of the data being accessed. Must implement `Copy`.
102#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
103pub struct UserPtrWO<T>
104where
105    T: Copy,
106{
107    addr: usize,
108    _ty: core::marker::PhantomData<T>,
109}
110
111impl<T> UserPtrWO<T>
112where
113    T: Copy,
114{
115    /// Creates a new `UserPtrWO` instance with the given user-space address.
116    pub fn new(addr: usize) -> Self {
117        UserPtrWO {
118            addr,
119            _ty: core::marker::PhantomData,
120        }
121    }
122
123    /// Writes a value of type `T` to the user-space address.
124    ///
125    /// Takes ownership of `self` to prevent TOCTOU attacks.
126    ///
127    /// Returns `Ok(usize)` indicating the number of bytes written, or
128    /// `Err(KernelError::BadAddress)` on failure.
129    pub fn put(self, other: T) -> Result<usize, KernelError> {
130        let access_range = Va::new(self.addr).ok_or(KernelError::BadAddress)?
131            ..Va::new(self.addr + core::mem::size_of::<T>()).ok_or(KernelError::BadAddress)?;
132        with_current(|th| {
133            let task = th
134                .task
135                .as_ref()
136                .expect("Try to call UserPtrWO::put() on the kernel thread.");
137            if task.access_ok(access_range, true) {
138                let target = self.addr as *mut T;
139                unsafe {
140                    // Safety: By calling access_ok, verifying `target` is valid, aligned, and
141                    // accessible.
142                    *target = other;
143                }
144                Ok(core::mem::size_of::<T>())
145            } else {
146                Err(KernelError::BadAddress)
147            }
148        })
149    }
150}
151
152/// A one-time, read-only pointer to a slice of `u8` in user-space.
153///
154/// This struct allows the kernel to safely read from a user-space buffer while
155/// preventing TOCTOU attacks by taking ownership of the pointer during
156/// operations.
157#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
158pub struct UserU8SliceRO {
159    addr: usize,
160    len: usize,
161}
162
163impl UserU8SliceRO {
164    /// Creates a new `UserU8SliceRO` instance with the given user-space address
165    /// and length.
166    pub fn new(addr: usize, len: usize) -> Self {
167        UserU8SliceRO { addr, len }
168    }
169
170    /// Reads data from the user-space buffer into a `Vec<u8>`.
171    ///
172    /// Takes ownership of `self` to prevent TOCTOU attacks.
173    ///
174    /// Returns `Ok(Vec<u8>)` containing the data if successful, otherwise
175    /// `Err(KernelError::BadAddress)`.
176    pub fn get(self) -> Result<Vec<u8>, KernelError> {
177        let access_range = Va::new(self.addr).ok_or(KernelError::BadAddress)?
178            ..Va::new(self.addr + self.len).ok_or(KernelError::BadAddress)?;
179        with_current(|th| {
180            let task = th
181                .task
182                .as_ref()
183                .expect("Try to call UserU8SliceRO::get() on the kernel thread.");
184            if task.access_ok(access_range, false) {
185                let mut result = Vec::new();
186                result.extend_from_slice(unsafe {
187                    core::slice::from_raw_parts(self.addr as *const u8, self.len)
188                });
189                Ok(result)
190            } else {
191                Err(KernelError::BadAddress)
192            }
193        })
194    }
195}
196
197/// A one-time, write-only pointer to a slice of `u8` in user-space.
198///
199/// This struct allows the kernel to safely write to a user-space buffer while
200/// preventing TOCTOU attacks by taking ownership of the pointer during
201/// operations.
202#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
203pub struct UserU8SliceWO {
204    addr: usize,
205    len: usize,
206}
207
208impl UserU8SliceWO {
209    /// Creates a new `UserU8SliceWO` instance with the given user-space address
210    /// and length.
211    pub fn new(addr: usize, len: usize) -> Self {
212        UserU8SliceWO { addr, len }
213    }
214    /// Writes data from a slice to the user-space buffer.
215    ///
216    /// Takes ownership of `self` to prevent TOCTOU attacks.
217    ///
218    /// Returns `Ok(usize)` indicating the number of bytes written, or
219    /// `Err(KernelError::BadAddress)` on failure.
220    pub fn put(self, other: &[u8]) -> Result<usize, KernelError> {
221        let size = self.len.min(other.len());
222        let access_range = Va::new(self.addr).ok_or(KernelError::BadAddress)?
223            ..Va::new(self.addr + self.len).ok_or(KernelError::BadAddress)?;
224        with_current(|th| {
225            let task = th
226                .task
227                .as_ref()
228                .expect("Try to call UserU8SliceWO::put() on the kernel thread.");
229            if task.access_ok(access_range, true) {
230                unsafe {
231                    core::ptr::copy_nonoverlapping(
232                        other[..size].as_ptr(),
233                        self.addr as *mut u8,
234                        size,
235                    );
236                }
237                Ok(size)
238            } else {
239                Err(KernelError::BadAddress)
240            }
241        })
242    }
243}
244
245/// A pointer to a null-terminated C-style string in user-space.
246///
247/// This struct provides a safe abstraction for reading strings from user-space.
248/// It iterates over the bytes until a null-terminator (`0x00`) is encountered,
249/// converting the byte sequence into a valid UTF-8 `String`.
250#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
251pub struct UserCString {
252    addr: usize,
253}
254
255impl UserCString {
256    /// Creates a new `UserCString` instance with the given user-space address.
257    pub fn new(addr: usize) -> Self {
258        Self { addr }
259    }
260
261    /// Reads a null-terminated string from the user-space address.
262    ///
263    /// This function iterates over user-space memory, collecting bytes until
264    /// a null terminator (`0x00`) is found. It then attempts to convert the
265    /// byte sequence into a UTF-8 `String`.
266    ///
267    /// Returns `Some(String)` if successful, otherwise `None` if the operation
268    /// fails.
269    pub fn read(self) -> Result<String, KernelError> {
270        let mut ptr = self.addr;
271        let mut result = Vec::new();
272        // Iterate over the bytes to find the null-terminator (0x00).
273        // If the byte is 0, we've found the null-terminator.
274        loop {
275            match UserPtrRO::<u8>::new(ptr).get() {
276                Ok(0) => {
277                    return core::str::from_utf8(&result)
278                        .ok()
279                        .map(String::from)
280                        .ok_or(KernelError::InvalidArgument);
281                }
282                Ok(v) => {
283                    ptr += 1;
284                    result.push(v);
285                }
286                Err(e) => return Err(e),
287            }
288        }
289    }
290}