Skip to main content

user_copy_rs_kernel_tests/
kernel.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
7#![no_std]
8#![allow(clippy::missing_safety_doc)]
9
10use user_copy::{UserInIovec, UserInOutPtr, UserInPtr, UserOutPtr, UserStringView};
11use zx_status::Status;
12use zx_types::{zx_iovec_t, zx_status_t};
13
14fn status_raw<T>(res: Result<T, Status>) -> zx_status_t {
15    match res {
16        Ok(_) => Status::OK.into_raw(),
17        Err(err) => err.into_raw(),
18    }
19}
20
21/// # Safety
22/// Caller must ensure `ptr` points to valid memory.
23#[unsafe(no_mangle)]
24pub unsafe extern "C" fn rust_user_copy_user_out_ptr_write(
25    ptr: UserOutPtr<u32>,
26    val: u32,
27) -> zx_status_t {
28    status_raw(ptr.write(val))
29}
30
31/// # Safety
32/// Caller must ensure `ptr` and `out_val` point to valid memory.
33#[unsafe(no_mangle)]
34pub unsafe extern "C" fn rust_user_copy_user_in_ptr_read(
35    ptr: UserInPtr<u32>,
36    out_val: *mut u32,
37) -> zx_status_t {
38    match ptr.read() {
39        // SAFETY: Caller guarantees `out_val` points to valid memory.
40        Ok(val) => unsafe {
41            *out_val = val;
42            Status::OK.into_raw()
43        },
44        Err(err) => err.into_raw(),
45    }
46}
47
48/// # Safety
49/// Caller must ensure `ptr` and `out_val` point to valid memory.
50#[unsafe(no_mangle)]
51pub unsafe extern "C" fn rust_user_copy_user_in_ptr_copy_from_user(
52    ptr: UserInPtr<u32>,
53    out_val: *mut u32,
54) -> zx_status_t {
55    let mut uninit = core::mem::MaybeUninit::uninit();
56    match ptr.copy_from_user(&mut uninit) {
57        // SAFETY: Caller guarantees `out_val` points to valid memory.
58        Ok(val_ref) => unsafe {
59            *out_val = *val_ref;
60            Status::OK.into_raw()
61        },
62        Err(err) => err.into_raw(),
63    }
64}
65
66/// # Safety
67/// Caller must ensure `ptr` and `dst` point to valid memory buffer of size `dst_len`.
68#[unsafe(no_mangle)]
69pub unsafe extern "C" fn rust_user_copy_user_in_ptr_copy_slice_from_user(
70    ptr: UserInPtr<u32>,
71    dst: *mut u32,
72    dst_len: usize,
73) -> zx_status_t {
74    // SAFETY: Caller guarantees `dst` points to a buffer of at least `dst_len` elements.
75    let slice = unsafe {
76        core::slice::from_raw_parts_mut(dst.cast::<core::mem::MaybeUninit<u32>>(), dst_len)
77    };
78    status_raw(ptr.copy_slice_from_user(slice))
79}
80
81/// # Safety
82/// Caller must ensure `ptr` and `out_capacity` point to valid memory.
83#[unsafe(no_mangle)]
84pub unsafe extern "C" fn rust_user_copy_user_in_iovec_get_total_capacity(
85    ptr: UserInPtr<zx_iovec_t>,
86    count: usize,
87    out_capacity: *mut usize,
88) -> zx_status_t {
89    let iovec = UserInIovec::new(ptr, count);
90    match iovec.get_total_capacity() {
91        // SAFETY: Caller guarantees `out_capacity` points to valid memory.
92        Ok(cap) => unsafe {
93            *out_capacity = cap;
94            Status::OK.into_raw()
95        },
96        Err(err) => err.into_raw(),
97    }
98}
99
100/// # Safety
101/// Caller must ensure `ptr` and `out_product` point to valid memory.
102#[unsafe(no_mangle)]
103pub unsafe extern "C" fn rust_user_copy_user_in_iovec_for_each(
104    ptr: UserInPtr<zx_iovec_t>,
105    count: usize,
106    out_product: *mut usize,
107) -> zx_status_t {
108    let iovec = UserInIovec::new(ptr, count);
109    let mut product = 2usize;
110    let status = iovec.for_each(|_buf, cap| {
111        product = product.wrapping_mul(cap);
112        Ok(())
113    });
114    match status {
115        // SAFETY: Caller guarantees `out_product` points to valid memory.
116        Ok(()) => unsafe {
117            *out_product = product;
118            Status::OK.into_raw()
119        },
120        Err(err) => err.into_raw(),
121    }
122}
123
124/// # Safety
125/// Caller must ensure `ptr` and `dst` point to valid memory buffer of size `dst_len`.
126#[unsafe(no_mangle)]
127pub unsafe extern "C" fn rust_user_copy_user_string_view_copy_slice_from_user(
128    ptr: UserInPtr<u8>,
129    length: usize,
130    dst: *mut u8,
131    dst_len: usize,
132) -> zx_status_t {
133    let sv = UserStringView { data: ptr, length };
134    // SAFETY: Caller guarantees `dst` points to a buffer of at least `dst_len` bytes.
135    let slice = unsafe {
136        core::slice::from_raw_parts_mut(dst.cast::<core::mem::MaybeUninit<u8>>(), dst_len)
137    };
138    status_raw(sv.copy_slice_from_user(slice))
139}
140
141#[unsafe(no_mangle)]
142pub extern "C" fn rust_user_copy_test_offsets() -> zx_status_t {
143    let base = 0x1000 as *mut u32;
144
145    // UserInPtr offset tests
146    let in_ptr = UserInPtr::new(base as *const u32);
147    if in_ptr.byte_offset(8).as_ptr() != (0x1008 as *const u32) {
148        return Status::INTERNAL.into_raw();
149    }
150    if in_ptr.byte_offset(-4).as_ptr() != (0x0ffc as *const u32) {
151        return Status::INTERNAL.into_raw();
152    }
153    if in_ptr.element_offset(3).as_ptr() != (0x100c as *const u32) {
154        return Status::INTERNAL.into_raw();
155    }
156    let null_in = UserInPtr::<u32>::new(core::ptr::null());
157    if !null_in.byte_offset(8).is_null() || !null_in.element_offset(3).is_null() {
158        return Status::INTERNAL.into_raw();
159    }
160    let def_in = UserInPtr::<u32>::default();
161    if !def_in.is_null() {
162        return Status::INTERNAL.into_raw();
163    }
164
165    // UserOutPtr offset tests
166    let out_ptr = UserOutPtr::new(base);
167    if out_ptr.byte_offset(8).as_ptr() != (0x1008 as *mut u32) {
168        return Status::INTERNAL.into_raw();
169    }
170    if out_ptr.byte_offset(-4).as_ptr() != (0x0ffc as *mut u32) {
171        return Status::INTERNAL.into_raw();
172    }
173    if out_ptr.element_offset(3).as_ptr() != (0x100c as *mut u32) {
174        return Status::INTERNAL.into_raw();
175    }
176    let null_out = UserOutPtr::<u32>::new(core::ptr::null_mut());
177    if !null_out.byte_offset(8).is_null() || !null_out.element_offset(3).is_null() {
178        return Status::INTERNAL.into_raw();
179    }
180    let def_out = UserOutPtr::<u32>::default();
181    if !def_out.is_null() {
182        return Status::INTERNAL.into_raw();
183    }
184
185    // UserInOutPtr offset tests
186    let inout_ptr = UserInOutPtr::new(base);
187    if inout_ptr.byte_offset(8).as_ptr() != (0x1008 as *mut u32) {
188        return Status::INTERNAL.into_raw();
189    }
190    if inout_ptr.byte_offset(-4).as_ptr() != (0x0ffc as *mut u32) {
191        return Status::INTERNAL.into_raw();
192    }
193    if inout_ptr.element_offset(3).as_ptr() != (0x100c as *mut u32) {
194        return Status::INTERNAL.into_raw();
195    }
196    let null_inout = UserInOutPtr::<u32>::new(core::ptr::null_mut());
197    if !null_inout.byte_offset(8).is_null() || !null_inout.element_offset(3).is_null() {
198        return Status::INTERNAL.into_raw();
199    }
200    let def_inout = UserInOutPtr::<u32>::default();
201    if !def_inout.is_null() {
202        return Status::INTERNAL.into_raw();
203    }
204
205    // UserStringView Default test
206    let def_sv = UserStringView::default();
207    if !def_sv.data.is_null() || !def_sv.is_empty() {
208        return Status::INTERNAL.into_raw();
209    }
210
211    Status::OK.into_raw()
212}