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