Skip to main content

user_copy/
user_ptr.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use arch_rs::{arch_copy_from_user, arch_copy_to_user};
8use core::mem::MaybeUninit;
9use zerocopy::{FromBytes, Immutable, IntoBytes};
10use zx_status::Status;
11
12/// A wrapper around a const pointer to user memory.
13#[repr(transparent)]
14#[derive(Debug, Copy, Clone, Default)]
15pub struct UserInPtr<T> {
16    ptr: *const T,
17}
18
19impl<T> UserInPtr<T> {
20    /// Constructs a new `UserInPtr`.
21    pub const fn new(ptr: *const T) -> Self {
22        Self { ptr }
23    }
24
25    /// Returns true if the underlying pointer is null.
26    pub fn is_null(&self) -> bool {
27        self.ptr.is_null()
28    }
29
30    /// Returns the underlying raw pointer.
31    pub fn as_ptr(&self) -> *const T {
32        self.ptr
33    }
34
35    /// Returns a pointer offset by `count` bytes.
36    pub fn byte_offset(&self, count: isize) -> Self {
37        if self.ptr.is_null() {
38            return Self::new(core::ptr::null());
39        }
40        Self::new(self.ptr.wrapping_byte_offset(count))
41    }
42
43    /// Returns a pointer offset by `index` elements.
44    pub fn element_offset(&self, index: usize) -> Self {
45        if self.ptr.is_null() {
46            return Self::new(core::ptr::null());
47        }
48        Self::new(self.ptr.wrapping_add(index))
49    }
50
51    /// Reinterprets the pointer as a pointer to type `U`.
52    pub fn reinterpret<U>(&self) -> UserInPtr<U> {
53        UserInPtr::new(self.ptr.cast::<U>())
54    }
55
56    /// Copies a single element from userspace into `dst`.
57    pub fn copy_from_user<'a>(&self, dst: &'a mut MaybeUninit<T>) -> Result<&'a mut T, Status>
58    where
59        T: FromBytes + IntoBytes,
60    {
61        // SAFETY: `dst.as_mut_ptr()` points to `size_of::<T>()` bytes of valid kernel memory.  If
62        // `arch_copy_from_user` succeeds, `dst` is fully initialized with bytes from user memory.
63        // Since `T: FromBytes`, any bit pattern of size `size_of::<T>()` is a valid representation
64        // of `T`, making `assume_init_mut()` safe to call.
65        unsafe {
66            arch_copy_from_user(
67                dst.as_mut_ptr() as *mut core::ffi::c_void,
68                self.ptr as *const core::ffi::c_void,
69                core::mem::size_of::<T>(),
70            )?;
71            Ok(dst.assume_init_mut())
72        }
73    }
74
75    /// Reads and returns a single copyable element from userspace.
76    pub fn read(&self) -> Result<T, Status>
77    where
78        T: FromBytes + IntoBytes + Immutable,
79    {
80        let mut val = MaybeUninit::uninit();
81        self.copy_from_user(&mut val)?;
82        // SAFETY: `copy_from_user` succeeded, so `val` has been fully initialized with a valid
83        // byte representation of `T` (since `T: FromBytes`).
84        Ok(unsafe { val.assume_init() })
85    }
86
87    /// Copies a slice of elements from userspace into `dst`.
88    pub fn copy_slice_from_user<'a>(
89        &self,
90        dst: &'a mut [MaybeUninit<T>],
91    ) -> Result<&'a mut [T], Status>
92    where
93        T: FromBytes + IntoBytes,
94    {
95        let len_bytes = core::mem::size_of_val(dst);
96        // SAFETY: `dst.as_mut_ptr()` points to `len_bytes` of valid kernel memory buffer space.
97        // Upon successful return, all elements in `dst` are initialized with bytes from user
98        // memory. Since `T: FromBytes`, converting the raw pointer to a mutable slice of `T` with
99        // length `dst.len()` is safe.
100        unsafe {
101            arch_copy_from_user(
102                dst.as_mut_ptr() as *mut core::ffi::c_void,
103                self.ptr as *const core::ffi::c_void,
104                len_bytes,
105            )?;
106            Ok(core::slice::from_raw_parts_mut(dst.as_mut_ptr() as *mut T, dst.len()))
107        }
108    }
109}
110
111/// A wrapper around a mutable pointer to user memory (write-only).
112#[repr(transparent)]
113#[derive(Debug, Copy, Clone, Default)]
114pub struct UserOutPtr<T> {
115    ptr: *mut T,
116}
117
118impl<T> UserOutPtr<T> {
119    /// Constructs a new `UserOutPtr`.
120    pub const fn new(ptr: *mut T) -> Self {
121        Self { ptr }
122    }
123
124    /// Returns true if the underlying pointer is null.
125    pub fn is_null(&self) -> bool {
126        self.ptr.is_null()
127    }
128
129    /// Returns the underlying raw pointer.
130    pub fn as_ptr(&self) -> *mut T {
131        self.ptr
132    }
133
134    /// Returns a pointer offset by `count` bytes.
135    pub fn byte_offset(&self, count: isize) -> Self {
136        if self.ptr.is_null() {
137            return Self::new(core::ptr::null_mut());
138        }
139        Self::new(self.ptr.wrapping_byte_offset(count))
140    }
141
142    /// Returns a pointer offset by `index` elements.
143    pub fn element_offset(&self, index: usize) -> Self {
144        if self.ptr.is_null() {
145            return Self::new(core::ptr::null_mut());
146        }
147        Self::new(self.ptr.wrapping_add(index))
148    }
149
150    /// Reinterprets the pointer as a pointer to type `U`.
151    pub fn reinterpret<U>(&self) -> UserOutPtr<U> {
152        UserOutPtr::new(self.ptr.cast::<U>())
153    }
154
155    /// Copies a single element from `src` to userspace.
156    pub fn copy_to_user(&self, src: &T) -> Result<(), Status>
157    where
158        T: IntoBytes + Immutable,
159    {
160        let src_bytes = src.as_bytes();
161        // SAFETY: `src_bytes.as_ptr()` points to `src_bytes.len()` bytes of valid kernel memory.
162        unsafe {
163            arch_copy_to_user(
164                self.ptr as *mut core::ffi::c_void,
165                src_bytes.as_ptr() as *const core::ffi::c_void,
166                src_bytes.len(),
167            )
168        }
169    }
170
171    /// Writes a single copyable value to userspace.
172    pub fn write(&self, val: T) -> Result<(), Status>
173    where
174        T: IntoBytes + Immutable,
175    {
176        self.copy_to_user(&val)
177    }
178
179    /// Copies a slice of elements from `src` to userspace.
180    pub fn copy_slice_to_user(&self, src: &[T]) -> Result<(), Status>
181    where
182        T: IntoBytes + Immutable,
183    {
184        let src_bytes = src.as_bytes();
185        // SAFETY: `src_bytes.as_ptr()` points to `src_bytes.len()` bytes of valid kernel memory
186        // slice.
187        unsafe {
188            arch_copy_to_user(
189                self.ptr as *mut core::ffi::c_void,
190                src_bytes.as_ptr() as *const core::ffi::c_void,
191                src_bytes.len(),
192            )
193        }
194    }
195}
196
197/// A wrapper around a mutable pointer to user memory (read-write).
198#[repr(transparent)]
199#[derive(Debug, Copy, Clone, Default)]
200pub struct UserInOutPtr<T> {
201    ptr: *mut T,
202}
203
204impl<T> UserInOutPtr<T> {
205    /// Constructs a new `UserInOutPtr`.
206    pub const fn new(ptr: *mut T) -> Self {
207        Self { ptr }
208    }
209
210    /// Returns true if the underlying pointer is null.
211    pub fn is_null(&self) -> bool {
212        self.ptr.is_null()
213    }
214
215    /// Returns the underlying raw pointer.
216    pub fn as_ptr(&self) -> *mut T {
217        self.ptr
218    }
219
220    /// Returns a pointer offset by `count` bytes.
221    pub fn byte_offset(&self, count: isize) -> Self {
222        if self.ptr.is_null() {
223            return Self::new(core::ptr::null_mut());
224        }
225        Self::new(self.ptr.wrapping_byte_offset(count))
226    }
227
228    /// Returns a pointer offset by `index` elements.
229    pub fn element_offset(&self, index: usize) -> Self {
230        if self.ptr.is_null() {
231            return Self::new(core::ptr::null_mut());
232        }
233        Self::new(self.ptr.wrapping_add(index))
234    }
235
236    /// Reinterprets the pointer as a pointer to type `U`.
237    pub fn reinterpret<U>(&self) -> UserInOutPtr<U> {
238        UserInOutPtr::new(self.ptr.cast::<U>())
239    }
240
241    /// Returns a `UserInPtr` viewing the same user memory.
242    pub const fn as_in_ptr(&self) -> UserInPtr<T> {
243        UserInPtr::new(self.ptr)
244    }
245
246    /// Returns a `UserOutPtr` viewing the same user memory.
247    pub const fn as_out_ptr(&self) -> UserOutPtr<T> {
248        UserOutPtr::new(self.ptr)
249    }
250
251    /// Copies a single element from userspace into `dst`.
252    pub fn copy_from_user<'a>(&self, dst: &'a mut MaybeUninit<T>) -> Result<&'a mut T, Status>
253    where
254        T: FromBytes + IntoBytes,
255    {
256        self.as_in_ptr().copy_from_user(dst)
257    }
258
259    /// Reads and returns a single copyable element from userspace.
260    pub fn read(&self) -> Result<T, Status>
261    where
262        T: FromBytes + IntoBytes + Immutable,
263    {
264        self.as_in_ptr().read()
265    }
266
267    /// Copies a slice of elements from userspace into `dst`.
268    pub fn copy_slice_from_user<'a>(
269        &self,
270        dst: &'a mut [MaybeUninit<T>],
271    ) -> Result<&'a mut [T], Status>
272    where
273        T: FromBytes + IntoBytes,
274    {
275        self.as_in_ptr().copy_slice_from_user(dst)
276    }
277
278    /// Copies a single element from `src` to userspace.
279    pub fn copy_to_user(&self, src: &T) -> Result<(), Status>
280    where
281        T: IntoBytes + Immutable,
282    {
283        self.as_out_ptr().copy_to_user(src)
284    }
285
286    /// Writes a single copyable value to userspace.
287    pub fn write(&self, val: T) -> Result<(), Status>
288    where
289        T: IntoBytes + Immutable,
290    {
291        self.as_out_ptr().write(val)
292    }
293
294    /// Copies a slice of elements from `src` to userspace.
295    pub fn copy_slice_to_user(&self, src: &[T]) -> Result<(), Status>
296    where
297        T: IntoBytes + Immutable,
298    {
299        self.as_out_ptr().copy_slice_to_user(src)
300    }
301}