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