Add memory operation syscalls (backport #16447) (#18149)

* Add memory operation syscalls (#16447)

(cherry picked from commit 2b50529265)

# Conflicts:
#	programs/bpf/Cargo.lock
#	programs/bpf/rust/sysvar/tests/lib.rs
#	programs/bpf/tests/programs.rs
#	programs/bpf_loader/src/syscalls.rs
#	sdk/src/feature_set.rs

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
This commit is contained in:
mergify[bot]
2021-06-22 19:57:51 +00:00
committed by GitHub
parent e259388069
commit 57baf7f79b
16 changed files with 877 additions and 180 deletions

276
programs/bpf/Cargo.lock generated
View File

@@ -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",
]

View File

@@ -63,6 +63,7 @@ members = [
"rust/many_args",
"rust/many_args_dep",
"rust/mem",
"rust/membuiltins",
"rust/noop",
"rust/panic",
"rust/param_passing",

View File

@@ -78,6 +78,7 @@ fn main() {
"iter",
"many_args",
"mem",
"membuiltins",
"noop",
"panic",
"param_passing",

View File

@@ -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"]

View File

@@ -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(())
}

View File

@@ -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<T: MemOps>(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));
}

View File

@@ -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();
}

View File

@@ -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 <maintainers@solana.foundation>"]
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"]

View File

@@ -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!();

View File

@@ -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();
}

View File

@@ -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),

View File

@@ -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<SyscallError> for EbpfError<BpfError> {
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<BpfError> for SyscallKeccak256<'a> {
}
}
/// memcpy
pub struct SyscallMemcpy<'a> {
cost: u64,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> 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<u64, EbpfError<BpfError>>,
) {
// 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::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
result
);
let src = question_mark!(
translate_slice::<u8>(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<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> 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<u64, EbpfError<BpfError>>,
) {
question_mark!(self.compute_meter.consume(n / self.cost), result);
let dst = question_mark!(
translate_slice_mut::<u8>(memory_mapping, dst_addr, n, self.loader_id, true),
result
);
let src = question_mark!(
translate_slice::<u8>(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<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> 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<u64, EbpfError<BpfError>>,
) {
question_mark!(self.compute_meter.consume(n / self.cost), result);
let s1 = question_mark!(
translate_slice::<u8>(memory_mapping, s1_addr, n, self.loader_id, true),
result
);
let s2 = question_mark!(
translate_slice::<u8>(memory_mapping, s2_addr, n, self.loader_id, true),
result
);
let cmp_result = question_mark!(
translate_type_mut::<i32>(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<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallMemset<'a> {
fn call(
&mut self,
s_addr: u64,
c: u64,
n: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
question_mark!(self.compute_meter.consume(n / self.cost), result);
let s = question_mark!(
translate_slice_mut::<u8>(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> {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -12,12 +12,13 @@ lazy_static::lazy_static! {
static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = 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<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
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);
}
}

View File

@@ -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<Pubkey, &'static str> = [
@@ -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()