diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index fede3d28f2..9d86cfb951 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -79,6 +79,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "695579f0f2520f3774bb40461e5adb066459d4e0af4d59d20175484fb8e9edf1" +[[package]] +name = "async-trait" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.6", + "syn 1.0.67", +] + [[package]] name = "atty" version = "0.2.14" @@ -222,7 +233,7 @@ dependencies = [ "borsh-schema-derive-internal", "proc-macro-crate", "proc-macro2 1.0.24", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -233,7 +244,7 @@ checksum = "d813fa25eb0bed78c36492cff4415f38c760d6de833d255ba9095bd8ebb7d725" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -244,7 +255,7 @@ checksum = "dcf78ee4a98c8cb9eba1bac3d3e2a1ea3d7673c719ce691e67b5cbafc472d3b7" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -367,6 +378,15 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "chrono-humanize" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8164ae3089baf04ff71f32aeb70213283dcd236dce8bc976d00b17a458f5f71c" +dependencies = [ + "chrono", +] + [[package]] name = "clap" version = "2.33.3" @@ -684,7 +704,7 @@ checksum = "eaed5874effa6cde088c644ddcdcb4ffd1511391c5be4fdd7a5ccd02c7e4a183" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -790,6 +810,18 @@ dependencies = [ "sha2 0.9.2", ] +[[package]] +name = "educe" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" +dependencies = [ + "enum-ordinalize", + "proc-macro2 1.0.24", + "quote 1.0.6", + "syn 1.0.67", +] + [[package]] name = "either" version = "1.5.3" @@ -820,6 +852,19 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "enum-ordinalize" +version = "3.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2 1.0.24", + "quote 1.0.6", + "syn 1.0.67", +] + [[package]] name = "env_logger" version = "0.8.3" @@ -851,7 +896,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", "synstructure", ] @@ -1007,7 +1052,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1134,7 +1179,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.1.1", + "tokio 1.7.1", "tokio-util", "tracing", ] @@ -1291,7 +1336,7 @@ dependencies = [ "itoa", "pin-project", "socket2", - "tokio 1.1.1", + "tokio 1.7.1", "tower-service", "tracing", "want", @@ -1307,7 +1352,7 @@ dependencies = [ "hyper", "log", "rustls", - "tokio 1.1.1", + "tokio 1.7.1", "tokio-rustls", "webpki", ] @@ -1729,6 +1774,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.2.5" @@ -1748,7 +1804,7 @@ checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1799,7 +1855,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -1887,7 +1943,7 @@ dependencies = [ "Inflector", "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2034,7 +2090,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2333,7 +2389,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "tokio 1.1.1", + "tokio 1.7.1", "tokio-rustls", "url", "wasm-bindgen", @@ -2456,7 +2512,7 @@ checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2551,7 +2607,7 @@ checksum = "8dee1f300f838c8ac340ecb0112b3ac472464fa67e87292bdb3dfc9c49128e17" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -2708,6 +2764,51 @@ dependencies = [ "zstd", ] +[[package]] +name = "solana-banks-client" +version = "1.6.15" +dependencies = [ + "bincode", + "borsh", + "borsh-derive", + "futures 0.3.12", + "mio 0.7.7", + "solana-banks-interface", + "solana-program 1.6.15", + "solana-sdk", + "tarpc", + "tokio 1.7.1", + "tokio-serde", +] + +[[package]] +name = "solana-banks-interface" +version = "1.6.15" +dependencies = [ + "mio 0.7.7", + "serde", + "solana-sdk", + "tarpc", +] + +[[package]] +name = "solana-banks-server" +version = "1.6.15" +dependencies = [ + "bincode", + "futures 0.3.12", + "log", + "mio 0.7.7", + "solana-banks-interface", + "solana-metrics", + "solana-runtime", + "solana-sdk", + "tarpc", + "tokio 1.7.1", + "tokio-serde", + "tokio-stream", +] + [[package]] name = "solana-bpf-loader-program" version = "1.6.15" @@ -2907,6 +3008,16 @@ name = "solana-bpf-rust-mem" version = "1.6.15" dependencies = [ "solana-program 1.6.15", + "solana-program-test", + "solana-sdk", +] + +[[package]] +name = "solana-bpf-rust-membuiltins" +version = "1.6.15" +dependencies = [ + "solana-bpf-rust-mem", + "solana-program 1.6.15", ] [[package]] @@ -3080,7 +3191,7 @@ dependencies = [ "solana-version", "solana-vote-program", "thiserror", - "tokio 1.1.1", + "tokio 1.7.1", "tungstenite", "url", ] @@ -3115,7 +3226,7 @@ dependencies = [ "reqwest", "serde", "syn 0.15.44", - "syn 1.0.60", + "syn 1.0.67", "tokio 0.1.22", "winapi 0.3.8", ] @@ -3138,7 +3249,7 @@ dependencies = [ "solana-version", "spl-memo", "thiserror", - "tokio 1.1.1", + "tokio 1.7.1", ] [[package]] @@ -3187,7 +3298,7 @@ dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", "rustc_version", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3199,7 +3310,7 @@ dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", "rustc_version", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3260,7 +3371,7 @@ dependencies = [ "solana-clap-utils", "solana-logger 1.6.15", "solana-version", - "tokio 1.1.1", + "tokio 1.7.1", "url", ] @@ -3330,6 +3441,30 @@ dependencies = [ "thiserror", ] +[[package]] +name = "solana-program-test" +version = "1.6.15" +dependencies = [ + "async-trait", + "base64 0.12.3", + "bincode", + "chrono", + "chrono-humanize", + "log", + "mio 0.7.7", + "serde", + "serde_derive", + "solana-banks-client", + "solana-banks-server", + "solana-bpf-loader-program", + "solana-logger 1.6.15", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "thiserror", + "tokio 1.7.1", +] + [[package]] name = "solana-rayon-threadlimit" version = "1.6.15" @@ -3461,7 +3596,7 @@ dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", "rustversion", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3474,7 +3609,7 @@ dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", "rustversion", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3629,6 +3764,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -3666,9 +3807,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.60" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", @@ -3683,7 +3824,7 @@ checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", "unicode-xid 0.2.0", ] @@ -3699,6 +3840,38 @@ dependencies = [ "xattr", ] +[[package]] +name = "tarpc" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e325774dd5b35d979e9f4db2b0f0d7d85dc2ff2b676a3150af56c09eafc14b07" +dependencies = [ + "anyhow", + "fnv", + "futures 0.3.12", + "humantime", + "log", + "pin-project", + "rand 0.7.3", + "serde", + "static_assertions", + "tarpc-plugins", + "tokio 1.7.1", + "tokio-serde", + "tokio-util", +] + +[[package]] +name = "tarpc-plugins" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3240378a22b1195734e085ba71d1d4188d50f034aea82635acc430b7005afb5" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.6", + "syn 1.0.67", +] + [[package]] name = "tempfile" version = "3.2.0" @@ -3767,7 +3940,7 @@ checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3848,9 +4021,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.1.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6714d663090b6b0acb0fa85841c6d66233d150cdb2602c8f9b8abb03370beb3f" +checksum = "5fb2ed024293bb19f7a5dc54fe83bf86532a44c12a2bb8ba40d64a4509395ca2" dependencies = [ "autocfg", "bytes 1.0.1", @@ -3921,13 +4094,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494" +checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", ] [[package]] @@ -3956,10 +4129,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", - "tokio 1.1.1", + "tokio 1.7.1", "webpki", ] +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes 1.0.1", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.4", + "tokio 1.7.1", +] + [[package]] name = "tokio-sync" version = "0.1.8" @@ -4057,7 +4257,7 @@ dependencies = [ "futures-sink", "log", "pin-project-lite 0.2.4", - "tokio 1.1.1", + "tokio 1.7.1", ] [[package]] @@ -4292,7 +4492,7 @@ dependencies = [ "log", "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", "wasm-bindgen-shared", ] @@ -4326,7 +4526,7 @@ checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4463,7 +4663,7 @@ checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.6", - "syn 1.0.60", + "syn 1.0.67", "synstructure", ] diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index fe741a29c6..450d8efc7b 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -63,6 +63,7 @@ members = [ "rust/many_args", "rust/many_args_dep", "rust/mem", + "rust/membuiltins", "rust/noop", "rust/panic", "rust/param_passing", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 8304d3d2c8..b08174fecc 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -78,6 +78,7 @@ fn main() { "iter", "many_args", "mem", + "membuiltins", "noop", "panic", "param_passing", diff --git a/programs/bpf/rust/mem/Cargo.toml b/programs/bpf/rust/mem/Cargo.toml index 4f284ad2bf..ec8dd0b8c6 100644 --- a/programs/bpf/rust/mem/Cargo.toml +++ b/programs/bpf/rust/mem/Cargo.toml @@ -9,11 +9,18 @@ homepage = "https://solana.com/" documentation = "https://docs.rs/solana-bpf-rust-mem" edition = "2018" +[features] +no-entrypoint = [] + [dependencies] solana-program = { path = "../../../../sdk/program", version = "=1.6.15" } +[dev-dependencies] +solana-program-test = { path = "../../../../program-test", version = "=1.6.15" } +solana-sdk = { path = "../../../../sdk", version = "=1.6.15" } + [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "lib"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/mem/src/entrypoint.rs b/programs/bpf/rust/mem/src/entrypoint.rs new file mode 100644 index 0000000000..65d25ef8f2 --- /dev/null +++ b/programs/bpf/rust/mem/src/entrypoint.rs @@ -0,0 +1,39 @@ +//! @brief Test mem functions + +use crate::{run_mem_tests, MemOps}; +use solana_program::{ + account_info::AccountInfo, + entrypoint, + entrypoint::ProgramResult, + program_memory::{sol_memcmp, sol_memcpy, sol_memmove, sol_memset}, + pubkey::Pubkey, +}; + +entrypoint!(process_instruction); +#[allow(clippy::unnecessary_wraps)] +pub fn process_instruction( + _program_id: &Pubkey, + _accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { + // Via syscalls + #[derive(Default)] + struct MemOpSyscalls(); + impl MemOps for MemOpSyscalls { + fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) { + sol_memcpy(dst, src, n) + } + unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) { + sol_memmove(dst, src, n) + } + fn memset(&self, s: &mut [u8], c: u8, n: usize) { + sol_memset(s, c, n) + } + fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 { + sol_memcmp(s1, s2, n) + } + } + run_mem_tests(MemOpSyscalls::default()); + + Ok(()) +} diff --git a/programs/bpf/rust/mem/src/lib.rs b/programs/bpf/rust/mem/src/lib.rs index 3817010ad2..91a98a208c 100644 --- a/programs/bpf/rust/mem/src/lib.rs +++ b/programs/bpf/rust/mem/src/lib.rs @@ -1,190 +1,165 @@ -//! @brief Test builtin mem functions +//! @brief Test mem functions -#![cfg(target_arch = "bpf")] -#![feature(rustc_private)] +#[cfg(not(feature = "no-entrypoint"))] +pub mod entrypoint; -extern crate compiler_builtins; -use solana_program::{custom_panic_default, entrypoint::SUCCESS}; +pub trait MemOps { + fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize); + /// # Safety + unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize); + fn memset(&self, s: &mut [u8], c: u8, n: usize); + fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32; +} -#[no_mangle] -pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { +pub fn run_mem_tests(mem_ops: T) { + // memcpy + let src = &[1_u8; 18]; + let dst = &mut [0_u8; 1]; + mem_ops.memcpy(dst, src, 1); + assert_eq!(&src[..1], dst); + let dst = &mut [0_u8; 3]; + mem_ops.memcpy(dst, src, 3); + assert_eq!(&src[..3], dst); + let dst = &mut [0_u8; 8]; + mem_ops.memcpy(dst, src, 8); + assert_eq!(&src[..8], dst); + let dst = &mut [0_u8; 9]; + mem_ops.memcpy(dst, src, 9); + assert_eq!(&src[..9], dst); + let dst = &mut [0_u8; 16]; + mem_ops.memcpy(dst, src, 16); + assert_eq!(&src[..16], dst); + let dst = &mut [0_u8; 18]; + mem_ops.memcpy(dst, src, 18); + assert_eq!(&src[..18], dst); + let dst = &mut [0_u8; 18]; + mem_ops.memcpy(dst, &src[1..], 17); + assert_eq!(&src[1..], &dst[..17]); + let dst = &mut [0_u8; 18]; + mem_ops.memcpy(&mut dst[1..], &src[1..], 17); + assert_eq!(&src[1..], &dst[1..]); + + // memmove unsafe { - // memcpy - let src = &mut [1_u8; 18]; - let dst = &mut [0_u8; 1]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 1); - assert_eq!(&src[..1], dst); - let dst = &mut [0_u8; 3]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 3); - assert_eq!(&src[..3], dst); - let dst = &mut [0_u8; 8]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 8); - assert_eq!(&src[..8], dst); - let dst = &mut [0_u8; 9]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 9); - assert_eq!(&src[..9], dst); - let dst = &mut [0_u8; 16]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 16); - assert_eq!(&src[..16], dst); - let dst = &mut [0_u8; 18]; - compiler_builtins::mem::memcpy(&mut src[0] as *mut u8, &mut dst[0] as *mut u8, 18); - assert_eq!(&src[..18], dst); - let dst = &mut [0_u8; 18]; - compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[0] as *mut u8, 17); - assert_eq!(&src[1..], &dst[1..]); - let dst = &mut [0_u8; 18]; - compiler_builtins::mem::memcpy(&mut src[1] as *mut u8, &mut dst[1] as *mut u8, 17); - assert_eq!(&src[1..], &dst[..17]); - - // memmove let buf = &mut [1_u8, 0]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[1] as *mut u8, 1); assert_eq!(buf[0], buf[1]); let buf = &mut [1_u8, 0]; - compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1); + mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 1); assert_eq!(buf[0], buf[1]); let buf = &mut [1_u8, 1, 1, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 3); assert_eq!(buf[..3], buf[3..]); let buf = &mut [1_u8, 1, 1, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3); + mem_ops.memmove(&mut buf[3] as *mut u8, &mut buf[0] as *mut u8, 3); assert_eq!(buf[..3], buf[3..]); let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[8] as *mut u8, 8); assert_eq!(buf[..8], buf[8..]); let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8); + mem_ops.memmove(&mut buf[8] as *mut u8, &mut buf[0] as *mut u8, 8); assert_eq!(buf[..8], buf[8..]); let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[9] as *mut u8, 9); assert_eq!(buf[..9], buf[9..]); let buf = &mut [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9); + mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[0] as *mut u8, 9); assert_eq!(&mut [0_u8, 0, 1, 2, 3, 4, 5, 6, 7, 8], buf); let buf = &mut [1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - compiler_builtins::mem::memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9); + mem_ops.memmove(&mut buf[9] as *mut u8, &mut buf[0] as *mut u8, 9); assert_eq!(buf[..9], buf[9..]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[16] as *mut u8, 16); assert_eq!(buf[..16], buf[16..]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16); + mem_ops.memmove(&mut buf[16] as *mut u8, &mut buf[0] as *mut u8, 16); assert_eq!(buf[..16], buf[16..]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18); + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[18] as *mut u8, 18); assert_eq!(buf[..18], buf[18..]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18); + mem_ops.memmove(&mut buf[18] as *mut u8, &mut buf[0] as *mut u8, 18); assert_eq!(buf[..18], buf[18..]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17); + mem_ops.memmove(&mut buf[1] as *mut u8, &mut buf[18] as *mut u8, 17); assert_eq!(buf[1..17], buf[18..34]); let buf = &mut [ 1_u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - compiler_builtins::mem::memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17); + mem_ops.memmove(&mut buf[19] as *mut u8, &mut buf[1] as *mut u8, 17); assert_eq!(buf[..17], buf[19..]); - - // memset - let exp = &[1_u8; 18]; - let buf = &mut [0_u8; 18]; - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 1); - assert_eq!(exp[..1], buf[..1]); - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 3); - assert_eq!(exp[..3], buf[..3]); - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 8); - assert_eq!(exp[..8], buf[..8]); - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 9); - assert_eq!(exp[..9], buf[..9]); - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 16); - assert_eq!(exp[..16], buf[..16]); - compiler_builtins::mem::memset(&mut buf[0] as *mut u8, 1, 18); - assert_eq!(exp[..18], buf[..18]); - compiler_builtins::mem::memset(&mut buf[1] as *mut u8, 1, 17); - assert_eq!(exp[1..18], buf[1..18]); - - // memcmp - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&[0_u8] as *const u8, &[1_u8] as *const u8, 1) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp( - &[0_u8, 0, 0] as *const u8, - &[0_u8, 0, 1] as *const u8, - 3 - ) - ); - assert_eq!( - 0, - compiler_builtins::mem::memcmp( - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8, - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8, - 9 - ) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp( - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8, - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8, - 9 - ) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp( - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0] as *const u8, - &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1] as *const u8, - 10 - ) - ); - assert_eq!( - 0, - compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[0_u8; 8] as *const u8, 8) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&[0_u8; 8] as *const u8, &[1_u8; 8] as *const u8, 8) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&[0_u8; 16] as *const u8, &[1_u8; 16] as *const u8, 16) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&[0_u8; 18] as *const u8, &[1_u8; 18] as *const u8, 18) - ); - let one = &[0_u8; 18]; - let two = &[1_u8; 18]; - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[0] as *const u8, 17) - ); - assert_eq!( - -1, - compiler_builtins::mem::memcmp(&one[1] as *const u8, &two[1] as *const u8, 17) - ); + let buf = &mut [0_u8, 0, 0, 1, 1, 1, 1, 1, 0]; + mem_ops.memmove(&mut buf[0] as *mut u8, &mut buf[3] as *mut u8, 5); + assert_eq!(buf, &mut [1, 1, 1, 1, 1, 1, 1, 1, 0]); } - SUCCESS -} + // memset + let exp = &[1_u8; 18]; + let buf = &mut [0_u8; 18]; + mem_ops.memset(&mut buf[0..], 1, 1); + assert_eq!(exp[..1], buf[..1]); + mem_ops.memset(&mut buf[0..], 1, 3); + assert_eq!(exp[..3], buf[..3]); + mem_ops.memset(&mut buf[0..], 1, 8); + assert_eq!(exp[..8], buf[..8]); + mem_ops.memset(&mut buf[0..], 1, 9); + assert_eq!(exp[..9], buf[..9]); + mem_ops.memset(&mut buf[0..], 1, 16); + assert_eq!(exp[..16], buf[..16]); + mem_ops.memset(&mut buf[0..], 1, 18); + assert_eq!(exp[..18], buf[..18]); + mem_ops.memset(&mut buf[1..], 1, 17); + assert_eq!(exp[1..18], buf[1..18]); -custom_panic_default!(); + // memcmp + assert_eq!(-1, mem_ops.memcmp(&[0_u8], &[1_u8], 1)); + assert_eq!(-1, mem_ops.memcmp(&[0_u8, 0, 0], &[0_u8, 0, 1], 3)); + assert_eq!( + 0, + mem_ops.memcmp( + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0], + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0], + 9 + ) + ); + assert_eq!( + -1, + mem_ops.memcmp( + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0], + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 1], + 9 + ) + ); + assert_eq!( + -1, + mem_ops.memcmp( + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0], + &[0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 1], + 10 + ) + ); + assert_eq!(0, mem_ops.memcmp(&[0_u8; 8], &[0_u8; 8], 8)); + assert_eq!(-1, mem_ops.memcmp(&[0_u8; 8], &[1_u8; 8], 8)); + assert_eq!(-1, mem_ops.memcmp(&[0_u8; 16], &[1_u8; 16], 16)); + assert_eq!(-1, mem_ops.memcmp(&[0_u8; 18], &[1_u8; 18], 18)); + let one = &[0_u8; 18]; + let two = &[1_u8; 18]; + assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[0..], 17)); + assert_eq!(-1, mem_ops.memcmp(&one[1..], &two[1..], 17)); +} diff --git a/programs/bpf/rust/mem/tests/lib.rs b/programs/bpf/rust/mem/tests/lib.rs new file mode 100644 index 0000000000..5bc591d728 --- /dev/null +++ b/programs/bpf/rust/mem/tests/lib.rs @@ -0,0 +1,23 @@ +use solana_bpf_rust_mem::entrypoint::process_instruction; +use solana_program_test::*; +use solana_sdk::{ + instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction, +}; + +#[tokio::test] +async fn test_mem() { + let program_id = Pubkey::new_unique(); + let program_test = ProgramTest::new( + "solana_bpf_rust_mem", + program_id, + processor!(process_instruction), + ); + let (mut banks_client, payer, recent_blockhash) = program_test.start().await; + + let mut transaction = Transaction::new_with_payer( + &[Instruction::new_with_bincode(program_id, &(), vec![])], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer], recent_blockhash); + banks_client.process_transaction(transaction).await.unwrap(); +} diff --git a/programs/bpf/rust/membuiltins/Cargo.toml b/programs/bpf/rust/membuiltins/Cargo.toml new file mode 100644 index 0000000000..fd67828fd9 --- /dev/null +++ b/programs/bpf/rust/membuiltins/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "solana-bpf-rust-membuiltins" +version = "1.6.15" +description = "Solana BPF test program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +documentation = "https://docs.rs/solana-bpf-rust-mem" +edition = "2018" + +[dependencies] +solana-bpf-rust-mem = { path = "../mem", version = "=1.6.15", features = [ "no-entrypoint" ] } +solana-program = { path = "../../../../sdk/program", version = "=1.6.15" } + +[lib] +crate-type = ["cdylib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/membuiltins/src/lib.rs b/programs/bpf/rust/membuiltins/src/lib.rs new file mode 100644 index 0000000000..1ce2ed339c --- /dev/null +++ b/programs/bpf/rust/membuiltins/src/lib.rs @@ -0,0 +1,39 @@ +//! @brief Test builtin mem functions + +#![cfg(target_arch = "bpf")] +#![feature(rustc_private)] + +extern crate compiler_builtins; +use solana_bpf_rust_mem::{run_mem_tests, MemOps}; +use solana_program::{custom_panic_default, entrypoint::SUCCESS}; + +#[no_mangle] +pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { + #[derive(Default)] + struct MemOpSyscalls(); + impl MemOps for MemOpSyscalls { + fn memcpy(&self, dst: &mut [u8], src: &[u8], n: usize) { + unsafe { + compiler_builtins::mem::memcpy(dst.as_mut_ptr(), src.as_ptr(), n); + } + } + unsafe fn memmove(&self, dst: *mut u8, src: *mut u8, n: usize) { + compiler_builtins::mem::memmove(dst, src, n); + } + fn memset(&self, s: &mut [u8], c: u8, n: usize) { + unsafe { + compiler_builtins::mem::memset(s.as_mut_ptr(), c as i32, n); + } + } + fn memcmp(&self, s1: &[u8], s2: &[u8], n: usize) -> i32 { + unsafe { compiler_builtins::mem::memcmp(s1.as_ptr(), s2.as_ptr(), n) } + } + } + let mem_ops = MemOpSyscalls::default(); + + run_mem_tests(mem_ops); + + SUCCESS +} + +custom_panic_default!(); diff --git a/programs/bpf/rust/sysvar/tests/lib.rs b/programs/bpf/rust/sysvar/tests/lib.rs new file mode 100644 index 0000000000..bb0fbd59ae --- /dev/null +++ b/programs/bpf/rust/sysvar/tests/lib.rs @@ -0,0 +1,46 @@ +use solana_bpf_rust_sysvar::process_instruction; +use solana_program_test::*; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + signature::Signer, + sysvar::{ + clock, epoch_schedule, fees, instructions, recent_blockhashes, rent, slot_hashes, + slot_history, stake_history, + }, + transaction::Transaction, +}; + +#[tokio::test] +async fn test_sysvars() { + let program_id = Pubkey::new_unique(); + let program_test = ProgramTest::new( + "solana_bpf_rust_sysvar", + program_id, + processor!(process_instruction), + ); + let (mut banks_client, payer, recent_blockhash) = program_test.start().await; + + let mut transaction = Transaction::new_with_payer( + &[Instruction::new_with_bincode( + program_id, + &(), + vec![ + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new(Pubkey::new_unique(), false), + AccountMeta::new_readonly(clock::id(), false), + AccountMeta::new_readonly(epoch_schedule::id(), false), + AccountMeta::new_readonly(fees::id(), false), + AccountMeta::new_readonly(instructions::id(), false), + AccountMeta::new_readonly(recent_blockhashes::id(), false), + AccountMeta::new_readonly(rent::id(), false), + AccountMeta::new_readonly(slot_hashes::id(), false), + AccountMeta::new_readonly(slot_history::id(), false), + AccountMeta::new_readonly(stake_history::id(), false), + ], + )], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer], recent_blockhash); + banks_client.process_transaction(transaction).await.unwrap(); +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 79d1cfa172..5e950e5d23 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -439,7 +439,7 @@ fn test_program_bpf_sanity() { ("solana_bpf_rust_external_spend", false), ("solana_bpf_rust_iter", true), ("solana_bpf_rust_many_args", true), - ("solana_bpf_rust_mem", true), + ("solana_bpf_rust_membuiltins", true), ("solana_bpf_rust_noop", true), ("solana_bpf_rust_panic", false), ("solana_bpf_rust_param_passing", true), @@ -1291,6 +1291,8 @@ fn assert_instruction_count() { ("solana_bpf_rust_external_spend", 521), ("solana_bpf_rust_iter", 724), ("solana_bpf_rust_many_args", 237), + ("solana_bpf_rust_mem", 3143), + ("solana_bpf_rust_membuiltins", 4069), ("solana_bpf_rust_noop", 495), ("solana_bpf_rust_param_passing", 46), ("solana_bpf_rust_sanity", 917), diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 13e7cd543d..8b8b227db7 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -20,7 +20,7 @@ use solana_sdk::{ epoch_schedule::EpochSchedule, feature_set::{ cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, - enforce_aligned_host_addrs, keccak256_syscall_enabled, + enforce_aligned_host_addrs, keccak256_syscall_enabled, memory_ops_syscalls, set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall, update_data_on_realloc, }, hash::{Hasher, HASH_BYTES}, @@ -74,6 +74,8 @@ pub enum SyscallError { InstructionTooLarge(usize, usize), #[error("Too many accounts passed to inner instruction")] TooManyAccounts, + #[error("Overlapping copy")] + CopyOverlapping, } impl From for EbpfError { fn from(error: SyscallError) -> Self { @@ -145,10 +147,20 @@ pub fn register_syscalls( .register_syscall_by_name(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?; } + if invoke_context.is_feature_active(&memory_ops_syscalls::id()) { + syscall_registry.register_syscall_by_name(b"sol_memcpy_", SyscallMemcpy::call)?; + syscall_registry.register_syscall_by_name(b"sol_memmove_", SyscallMemmove::call)?; + syscall_registry.register_syscall_by_name(b"sol_memcmp_", SyscallMemcmp::call)?; + syscall_registry.register_syscall_by_name(b"sol_memset_", SyscallMemset::call)?; + } + + // Cross-program invocation syscalls syscall_registry .register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?; syscall_registry .register_syscall_by_name(b"sol_invoke_signed_rust", SyscallInvokeSignedRust::call)?; + + // Memory allocator syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?; Ok(syscall_registry) @@ -269,6 +281,43 @@ pub fn bind_syscall_context_objects<'a>( }), ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&memory_ops_syscalls::id()), + Box::new(SyscallMemcpy { + cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&memory_ops_syscalls::id()), + Box::new(SyscallMemmove { + cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&memory_ops_syscalls::id()), + Box::new(SyscallMemcmp { + cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + bind_feature_gated_syscall_context_object!( + vm, + invoke_context.is_feature_active(&memory_ops_syscalls::id()), + Box::new(SyscallMemset { + cost: invoke_context.get_bpf_compute_budget().cpi_bytes_per_unit, + compute_meter: invoke_context.get_compute_meter(), + loader_id, + }), + ); + let is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id()); let invoke_context = Rc::new(RefCell::new(invoke_context)); @@ -325,7 +374,6 @@ pub fn bind_syscall_context_objects<'a>( )?; // Memory allocator - vm.bind_syscall_context_object( Box::new(SyscallAllocFree { aligned: *loader_id != bpf_loader_deprecated::id(), @@ -1140,6 +1188,150 @@ impl<'a> SyscallObject for SyscallKeccak256<'a> { } } +/// memcpy +pub struct SyscallMemcpy<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallMemcpy<'a> { + fn call( + &mut self, + dst_addr: u64, + src_addr: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + // cannot be overlapping + if dst_addr + n > src_addr && src_addr > dst_addr { + *result = Err(SyscallError::CopyOverlapping.into()); + return; + } + + question_mark!(self.compute_meter.consume(n / self.cost), result); + let dst = question_mark!( + translate_slice_mut::(memory_mapping, dst_addr, n, self.loader_id, true), + result + ); + let src = question_mark!( + translate_slice::(memory_mapping, src_addr, n, self.loader_id, true), + result + ); + unsafe { + std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), n as usize); + } + *result = Ok(0); + } +} +/// memcpy +pub struct SyscallMemmove<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallMemmove<'a> { + fn call( + &mut self, + dst_addr: u64, + src_addr: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + question_mark!(self.compute_meter.consume(n / self.cost), result); + let dst = question_mark!( + translate_slice_mut::(memory_mapping, dst_addr, n, self.loader_id, true), + result + ); + let src = question_mark!( + translate_slice::(memory_mapping, src_addr, n, self.loader_id, true), + result + ); + unsafe { + std::ptr::copy(src.as_ptr(), dst.as_mut_ptr(), n as usize); + } + *result = Ok(0); + } +} +/// memcmp +pub struct SyscallMemcmp<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallMemcmp<'a> { + fn call( + &mut self, + s1_addr: u64, + s2_addr: u64, + n: u64, + cmp_result_addr: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + question_mark!(self.compute_meter.consume(n / self.cost), result); + let s1 = question_mark!( + translate_slice::(memory_mapping, s1_addr, n, self.loader_id, true), + result + ); + let s2 = question_mark!( + translate_slice::(memory_mapping, s2_addr, n, self.loader_id, true), + result + ); + let cmp_result = question_mark!( + translate_type_mut::(memory_mapping, cmp_result_addr, self.loader_id, true), + result + ); + let mut i = 0; + while i < n as usize { + let a = s1[i]; + let b = s2[i]; + if a != b { + *cmp_result = a as i32 - b as i32; + *result = Ok(0); + return; + } + i += 1; + } + *cmp_result = 0; + *result = Ok(0); + } +} +/// memset +pub struct SyscallMemset<'a> { + cost: u64, + compute_meter: Rc>, + loader_id: &'a Pubkey, +} +impl<'a> SyscallObject for SyscallMemset<'a> { + fn call( + &mut self, + s_addr: u64, + c: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &MemoryMapping, + result: &mut Result>, + ) { + question_mark!(self.compute_meter.consume(n / self.cost), result); + let s = question_mark!( + translate_slice_mut::(memory_mapping, s_addr, n, self.loader_id, true), + result + ); + for val in s.iter_mut().take(n as usize) { + *val = c as u8; + } + *result = Ok(0); + } +} + // Cross-program invocation syscalls struct AccountReferences<'a> { diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 0941db372b..60606d7fcb 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -30,6 +30,7 @@ pub mod native_token; pub mod nonce; pub mod program; pub mod program_error; +pub mod program_memory; pub mod program_option; pub mod program_pack; pub mod program_stubs; diff --git a/sdk/program/src/program_memory.rs b/sdk/program/src/program_memory.rs new file mode 100644 index 0000000000..b213aa0342 --- /dev/null +++ b/sdk/program/src/program_memory.rs @@ -0,0 +1,89 @@ +//! @brief Solana Rust-based BPF memory operations + +/// Memcpy +/// +/// @param dst - Destination +/// @param src - Source +/// @param n - Number of bytes to copy +#[inline] +pub fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) { + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64); + } + unsafe { + sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64); + } + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_memcpy(dst.as_mut_ptr(), src.as_ptr(), n); +} + +/// Memmove +/// +/// @param dst - Destination +/// @param src - Source +/// @param n - Number of bytes to copy +/// +/// # Safety +#[inline] +pub unsafe fn sol_memmove(dst: *mut u8, src: *mut u8, n: usize) { + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64); + } + sol_memmove_(dst, src, n as u64); + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_memmove(dst, src, n); +} + +/// Memcmp +/// +/// @param s1 - Slice to be compared +/// @param s2 - Slice to be compared +/// @param n - Number of bytes to compare +#[inline] +pub fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 { + let mut result = 0; + + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32); + } + unsafe { + sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, &mut result as *mut i32); + } + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_memcmp(s1.as_ptr(), s2.as_ptr(), n, &mut result as *mut i32); + + result +} + +/// Memset +/// +/// @param s1 - Slice to be compared +/// @param s2 - Slice to be compared +/// @param n - Number of bytes to compare +#[inline] +pub fn sol_memset(s: &mut [u8], c: u8, n: usize) { + #[cfg(target_arch = "bpf")] + { + extern "C" { + fn sol_memset_(s: *mut u8, c: u8, n: u64); + } + unsafe { + sol_memset_(s.as_mut_ptr(), c, n as u64); + } + } + + #[cfg(not(target_arch = "bpf"))] + crate::program_stubs::sol_memset(s.as_mut_ptr(), c, n); +} diff --git a/sdk/program/src/program_stubs.rs b/sdk/program/src/program_stubs.rs index 8aa67b6c78..f576ae723e 100644 --- a/sdk/program/src/program_stubs.rs +++ b/sdk/program/src/program_stubs.rs @@ -12,12 +12,13 @@ lazy_static::lazy_static! { static ref SYSCALL_STUBS: Arc>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {}))); } -// The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in -// alternatives +// The default syscall stubs may not do much, but `set_syscalls()` can be used +// to swap in alternatives pub fn set_syscall_stubs(syscall_stubs: Box) -> Box { std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs) } +#[allow(clippy::integer_arithmetic)] pub trait SyscallStubs: Sync + Send { fn sol_log(&self, message: &str) { println!("{}", message); @@ -34,7 +35,6 @@ pub trait SyscallStubs: Sync + Send { sol_log("SyscallStubs: sol_invoke_signed() not available"); Ok(()) } - fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 { UNSUPPORTED_SYSVAR } @@ -47,6 +47,39 @@ pub trait SyscallStubs: Sync + Send { fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 { UNSUPPORTED_SYSVAR } + /// # Safety + unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) { + // cannot be overlapping + if dst as usize + n > src as usize && src as usize > dst as usize { + panic!("memcpy does not support oveerlapping regions"); + } + std::ptr::copy_nonoverlapping(src, dst, n as usize); + } + /// # Safety + unsafe fn sol_memmove(&self, dst: *mut u8, src: *const u8, n: usize) { + std::ptr::copy(src, dst, n as usize); + } + /// # Safety + unsafe fn sol_memcmp(&self, s1: *const u8, s2: *const u8, n: usize, result: *mut i32) { + let mut i = 0; + while i < n { + let a = *s1.add(i); + let b = *s2.add(i); + if a != b { + *result = a as i32 - b as i32; + return; + } + i += 1; + } + *result = 0 + } + /// # Safety + unsafe fn sol_memset(&self, s: *mut u8, c: u8, n: usize) { + let s = std::slice::from_raw_parts_mut(s, n); + for val in s.iter_mut().take(n) { + *val = c; + } + } } struct DefaultSyscallStubs {} @@ -96,3 +129,27 @@ pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 { pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 { SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr) } + +pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) { + unsafe { + SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n); + } +} + +pub(crate) fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) { + unsafe { + SYSCALL_STUBS.read().unwrap().sol_memmove(dst, src, n); + } +} + +pub(crate) fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) { + unsafe { + SYSCALL_STUBS.read().unwrap().sol_memcmp(s1, s2, n, result); + } +} + +pub(crate) fn sol_memset(s: *mut u8, c: u8, n: usize) { + unsafe { + SYSCALL_STUBS.read().unwrap().sol_memset(s, c, n); + } +} diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 1f1fca57be..1c76705305 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -146,6 +146,10 @@ pub mod system_transfer_zero_check { solana_sdk::declare_id!("BrTR9hzw4WBGFP65AJMbpAo64DcA3U6jdPSga9fMV5cS"); } +pub mod memory_ops_syscalls { + solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -181,6 +185,7 @@ lazy_static! { (keccak256_syscall_enabled::id(), "keccak256 syscall"), (stake_program_v4::id(), "solana_stake_program v4"), (system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"), + (memory_ops_syscalls::id(), "add syscalls for memory operations"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()