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}