solana_program/
program_stubs.rs

1//! Implementations of syscalls used when `safecoin-program` is built for non-BPF targets.
2
3#![cfg(not(target_os = "solana"))]
4
5use {
6    crate::{
7        account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
8        program_error::UNSUPPORTED_SYSVAR, pubkey::Pubkey,
9    },
10    itertools::Itertools,
11    std::sync::{Arc, RwLock},
12};
13
14lazy_static::lazy_static! {
15    static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {})));
16}
17
18// The default syscall stubs may not do much, but `set_syscalls()` can be used
19// to swap in alternatives
20pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
21    std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
22}
23
24#[allow(clippy::integer_arithmetic)]
25pub trait SyscallStubs: Sync + Send {
26    fn sol_log(&self, message: &str) {
27        println!("{}", message);
28    }
29    fn sol_log_compute_units(&self) {
30        sol_log("SyscallStubs: sol_log_compute_units() not available");
31    }
32    fn sol_invoke_signed(
33        &self,
34        _instruction: &Instruction,
35        _account_infos: &[AccountInfo],
36        _signers_seeds: &[&[&[u8]]],
37    ) -> ProgramResult {
38        sol_log("SyscallStubs: sol_invoke_signed() not available");
39        Ok(())
40    }
41    fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
42        UNSUPPORTED_SYSVAR
43    }
44    fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
45        UNSUPPORTED_SYSVAR
46    }
47    fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
48        UNSUPPORTED_SYSVAR
49    }
50    fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
51        UNSUPPORTED_SYSVAR
52    }
53    /// # Safety
54    unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
55        // cannot be overlapping
56        assert!(
57            is_nonoverlapping(src as usize, n, dst as usize, n),
58            "memcpy does not support overlapping regions"
59        );
60        std::ptr::copy_nonoverlapping(src, dst, n as usize);
61    }
62    /// # Safety
63    unsafe fn sol_memmove(&self, dst: *mut u8, src: *const u8, n: usize) {
64        std::ptr::copy(src, dst, n as usize);
65    }
66    /// # Safety
67    unsafe fn sol_memcmp(&self, s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
68        let mut i = 0;
69        while i < n {
70            let a = *s1.add(i);
71            let b = *s2.add(i);
72            if a != b {
73                *result = a as i32 - b as i32;
74                return;
75            }
76            i += 1;
77        }
78        *result = 0
79    }
80    /// # Safety
81    unsafe fn sol_memset(&self, s: *mut u8, c: u8, n: usize) {
82        let s = std::slice::from_raw_parts_mut(s, n);
83        for val in s.iter_mut().take(n) {
84            *val = c;
85        }
86    }
87    fn sol_get_return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
88        None
89    }
90    fn sol_set_return_data(&self, _data: &[u8]) {}
91    fn sol_log_data(&self, fields: &[&[u8]]) {
92        println!("data: {}", fields.iter().map(base64::encode).join(" "));
93    }
94    fn sol_get_processed_sibling_instruction(&self, _index: usize) -> Option<Instruction> {
95        None
96    }
97    fn sol_get_stack_height(&self) -> u64 {
98        0
99    }
100}
101
102struct DefaultSyscallStubs {}
103impl SyscallStubs for DefaultSyscallStubs {}
104
105pub(crate) fn sol_log(message: &str) {
106    SYSCALL_STUBS.read().unwrap().sol_log(message);
107}
108
109pub(crate) fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
110    sol_log(&format!(
111        "{:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
112        arg1, arg2, arg3, arg4, arg5
113    ));
114}
115
116pub(crate) fn sol_log_compute_units() {
117    SYSCALL_STUBS.read().unwrap().sol_log_compute_units();
118}
119
120pub(crate) fn sol_invoke_signed(
121    instruction: &Instruction,
122    account_infos: &[AccountInfo],
123    signers_seeds: &[&[&[u8]]],
124) -> ProgramResult {
125    SYSCALL_STUBS
126        .read()
127        .unwrap()
128        .sol_invoke_signed(instruction, account_infos, signers_seeds)
129}
130
131pub(crate) fn sol_get_clock_sysvar(var_addr: *mut u8) -> u64 {
132    SYSCALL_STUBS.read().unwrap().sol_get_clock_sysvar(var_addr)
133}
134
135pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 {
136    SYSCALL_STUBS
137        .read()
138        .unwrap()
139        .sol_get_epoch_schedule_sysvar(var_addr)
140}
141
142pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
143    SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr)
144}
145
146pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
147    SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
148}
149
150pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
151    unsafe {
152        SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n);
153    }
154}
155
156pub(crate) fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
157    unsafe {
158        SYSCALL_STUBS.read().unwrap().sol_memmove(dst, src, n);
159    }
160}
161
162pub(crate) fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
163    unsafe {
164        SYSCALL_STUBS.read().unwrap().sol_memcmp(s1, s2, n, result);
165    }
166}
167
168pub(crate) fn sol_memset(s: *mut u8, c: u8, n: usize) {
169    unsafe {
170        SYSCALL_STUBS.read().unwrap().sol_memset(s, c, n);
171    }
172}
173
174pub(crate) fn sol_get_return_data() -> Option<(Pubkey, Vec<u8>)> {
175    SYSCALL_STUBS.read().unwrap().sol_get_return_data()
176}
177
178pub(crate) fn sol_set_return_data(data: &[u8]) {
179    SYSCALL_STUBS.read().unwrap().sol_set_return_data(data)
180}
181
182pub(crate) fn sol_log_data(data: &[&[u8]]) {
183    SYSCALL_STUBS.read().unwrap().sol_log_data(data)
184}
185
186pub(crate) fn sol_get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
187    SYSCALL_STUBS
188        .read()
189        .unwrap()
190        .sol_get_processed_sibling_instruction(index)
191}
192
193pub(crate) fn sol_get_stack_height() -> u64 {
194    SYSCALL_STUBS.read().unwrap().sol_get_stack_height()
195}
196
197/// Check that two regions do not overlap.
198///
199/// Hidden to share with bpf_loader without being part of the API surface.
200#[doc(hidden)]
201pub fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
202where
203    N: Ord + std::ops::Sub<Output = N>,
204    <N as std::ops::Sub>::Output: Ord,
205{
206    // If the absolute distance between the ptrs is at least as big as the size of the other,
207    // they do not overlap.
208    if src > dst {
209        src - dst >= dst_len
210    } else {
211        dst - src >= src_len
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn test_is_nonoverlapping() {
221        for dst in 0..8 {
222            assert!(is_nonoverlapping(10, 3, dst, 3));
223        }
224        for dst in 8..13 {
225            assert!(!is_nonoverlapping(10, 3, dst, 3));
226        }
227        for dst in 13..20 {
228            assert!(is_nonoverlapping(10, 3, dst, 3));
229        }
230        assert!(is_nonoverlapping::<u8>(255, 3, 254, 1));
231        assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
232    }
233}