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