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