rialo_s_program_memory/lib.rs
1//! Basic low-level memory operations.
2//!
3//! Within the SBF environment, these are implemented as syscalls and executed by
4//! the runtime in native code.
5#![allow(unsafe_code)]
6
7#[cfg(target_os = "solana")]
8pub mod syscalls {
9 pub use rialo_s_define_syscall::definitions::{
10 rlo_memcmp_, rlo_memcpy_, rlo_memmove_, rlo_memset_,
11 };
12}
13
14/// Check that two regions do not overlap.
15///
16/// Hidden to share with bpf_loader without being part of the API surface.
17#[doc(hidden)]
18pub fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
19where
20 N: Ord + num_traits::SaturatingSub,
21{
22 // If the absolute distance between the ptrs is at least as big as the size of the other,
23 // they do not overlap.
24 if src > dst {
25 src.saturating_sub(&dst) >= dst_len
26 } else {
27 dst.saturating_sub(&src) >= src_len
28 }
29}
30
31#[cfg(not(target_os = "solana"))]
32#[allow(clippy::arithmetic_side_effects)]
33pub mod stubs {
34 use super::is_nonoverlapping;
35 /// # Safety
36 pub unsafe fn rlo_memcpy(dst: *mut u8, src: *const u8, n: usize) {
37 // cannot be overlapping
38 assert!(
39 is_nonoverlapping(src as usize, n, dst as usize, n),
40 "memcpy does not support overlapping regions"
41 );
42 std::ptr::copy_nonoverlapping(src, dst, n);
43 }
44 /// # Safety
45 pub unsafe fn rlo_memmove(dst: *mut u8, src: *const u8, n: usize) {
46 std::ptr::copy(src, dst, n);
47 }
48 /// # Safety
49 pub unsafe fn rlo_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
50 let mut i = 0;
51 while i < n {
52 let a = *s1.add(i);
53 let b = *s2.add(i);
54 if a != b {
55 *result = a as i32 - b as i32;
56 return;
57 }
58 i += 1;
59 }
60 *result = 0
61 }
62 /// # Safety
63 pub unsafe fn rlo_memset(s: *mut u8, c: u8, n: usize) {
64 let s = std::slice::from_raw_parts_mut(s, n);
65 for val in s.iter_mut().take(n) {
66 *val = c;
67 }
68 }
69}
70
71/// Like C `memcpy`.
72///
73/// # Arguments
74///
75/// - `dst` - Destination
76/// - `src` - Source
77/// - `n` - Number of bytes to copy
78///
79/// # Errors
80///
81/// When executed within a SBF program, the memory regions spanning `n` bytes
82/// from from the start of `dst` and `src` must be mapped program memory. If not,
83/// the program will abort.
84///
85/// The memory regions spanning `n` bytes from `dst` and `src` from the start
86/// of `dst` and `src` must not overlap. If they do, then the program will abort
87/// or, if run outside of the SBF VM, will panic.
88///
89/// # Safety
90///
91/// __This function is incorrectly missing an `unsafe` declaration.__
92///
93/// This function does not verify that `n` is less than or equal to the
94/// lengths of the `dst` and `src` slices passed to it — it will copy
95/// bytes to and from beyond the slices.
96///
97/// Specifying an `n` greater than either the length of `dst` or `src` will
98/// likely introduce undefined behavior.
99#[inline]
100pub fn rlo_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
101 #[cfg(target_os = "solana")]
102 unsafe {
103 syscalls::rlo_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
104 }
105
106 #[cfg(not(target_os = "solana"))]
107 unsafe {
108 stubs::rlo_memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
109 }
110}
111
112/// Like C `memmove`.
113///
114/// # Arguments
115///
116/// - `dst` - Destination
117/// - `src` - Source
118/// - `n` - Number of bytes to copy
119///
120/// # Errors
121///
122/// When executed within a SBF program, the memory regions spanning `n` bytes
123/// from from `dst` and `src` must be mapped program memory. If not, the program
124/// will abort.
125///
126/// # Safety
127///
128/// The same safety rules apply as in [`ptr::copy`].
129///
130/// [`ptr::copy`]: https://doc.rust-lang.org/std/ptr/fn.copy.html
131#[inline]
132pub unsafe fn rlo_memmove(dst: *mut u8, src: *const u8, n: usize) {
133 #[cfg(target_os = "solana")]
134 syscalls::rlo_memmove_(dst, src, n as u64);
135
136 #[cfg(not(target_os = "solana"))]
137 stubs::rlo_memmove(dst, src, n);
138}
139
140/// Like C `memcmp`.
141///
142/// # Arguments
143///
144/// - `s1` - Slice to be compared
145/// - `s2` - Slice to be compared
146/// - `n` - Number of bytes to compare
147///
148/// # Errors
149///
150/// When executed within a SBF program, the memory regions spanning `n` bytes
151/// from from the start of `dst` and `src` must be mapped program memory. If not,
152/// the program will abort.
153///
154/// # Safety
155///
156/// __This function is incorrectly missing an `unsafe` declaration.__
157///
158/// It does not verify that `n` is less than or equal to the lengths of the
159/// `dst` and `src` slices passed to it — it will read bytes beyond the
160/// slices.
161///
162/// Specifying an `n` greater than either the length of `dst` or `src` will
163/// likely introduce undefined behavior.
164#[inline]
165pub fn rlo_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
166 let mut result = 0;
167
168 #[cfg(target_os = "solana")]
169 unsafe {
170 syscalls::rlo_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32);
171 }
172
173 #[cfg(not(target_os = "solana"))]
174 unsafe {
175 stubs::rlo_memcmp(s1.as_ptr(), s2.as_ptr(), n, &mut result as *mut i32);
176 }
177
178 result
179}
180
181/// Like C `memset`.
182///
183/// # Arguments
184///
185/// - `s` - Slice to be set
186/// - `c` - Repeated byte to set
187/// - `n` - Number of bytes to set
188///
189/// # Errors
190///
191/// When executed within a SBF program, the memory region spanning `n` bytes
192/// from from the start of `s` must be mapped program memory. If not, the program
193/// will abort.
194///
195/// # Safety
196///
197/// __This function is incorrectly missing an `unsafe` declaration.__
198///
199/// This function does not verify that `n` is less than or equal to the length
200/// of the `s` slice passed to it — it will write bytes beyond the
201/// slice.
202///
203/// Specifying an `n` greater than the length of `s` will likely introduce
204/// undefined behavior.
205#[inline]
206pub fn rlo_memset(s: &mut [u8], c: u8, n: usize) {
207 #[cfg(target_os = "solana")]
208 unsafe {
209 syscalls::rlo_memset_(s.as_mut_ptr(), c.into(), n as u64);
210 }
211
212 #[cfg(not(target_os = "solana"))]
213 unsafe {
214 stubs::rlo_memset(s.as_mut_ptr(), c, n);
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_is_nonoverlapping() {
224 for dst in 0..8 {
225 assert!(is_nonoverlapping(10, 3, dst, 3));
226 }
227 for dst in 8..13 {
228 assert!(!is_nonoverlapping(10, 3, dst, 3));
229 }
230 for dst in 13..20 {
231 assert!(is_nonoverlapping(10, 3, dst, 3));
232 }
233 assert!(is_nonoverlapping::<u8>(255, 3, 254, 1));
234 assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
235 }
236}