diff --git a/Cargo.lock b/Cargo.lock index 0d9e7fec9f..0f22d48ff9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2294,7 +2294,7 @@ dependencies = [ "solana-logger 0.15.0", "solana-runtime 0.15.0", "solana-sdk 0.15.0", - "solana_rbpf 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "solana_rbpf 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2309,7 +2309,7 @@ dependencies = [ "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "solana-logger 0.15.0", "solana-sdk 0.15.0", - "solana_rbpf 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "solana_rbpf 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2820,7 +2820,7 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2828,6 +2828,7 @@ dependencies = [ "elfkit 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3665,7 +3666,7 @@ dependencies = [ "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "4e626972d3593207547f14bf5fc9efa4d0e7283deb73fef1dff313dae9ab8878" "checksum solana-ed25519-dalek 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c21f9d5aa62959872194dfd086feb4e8efec1c2589d27e6a0339904759e99fc" -"checksum solana_rbpf 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "694a4947fb9db55e5ddc285b48b5e280167347a676c52061263a4ea6e862369b" +"checksum solana_rbpf 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2b57f7d1bae8d902a3819f66aecb4006b6d8b181388a1580593979952f12df80" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index f53b44ca19..07767778cf 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -21,7 +21,7 @@ walkdir = "2" bincode = "1.1.4" byteorder = "1.3.1" elf = "0.0.10" -solana_rbpf = "=0.1.11" +solana_rbpf = "=0.1.12" solana-bpfloader = { path = "../bpf_loader", version = "0.15.0" } solana-logger = { path = "../../logger", version = "0.15.0" } solana-runtime = { path = "../../runtime", version = "0.15.0" } diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 50cc5cb5b9..60f5ae187e 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -76,12 +76,12 @@ fn bench_program_alu(bencher: &mut Bencher) { inner_iter.write_u64::(0).unwrap(); let elf = load_elf().unwrap(); - let mut vm = solana_bpf_loader::create_vm(&elf).unwrap(); + let (mut vm, _) = solana_bpf_loader::create_vm(&elf).unwrap(); println!("Interpreted:"); assert_eq!( 1, /*true*/ - vm.execute_program(&mut inner_iter).unwrap() + vm.execute_program(&mut inner_iter, &[], &[]).unwrap() ); assert_eq!(ARMSTRONG_LIMIT, LittleEndian::read_u64(&inner_iter)); assert_eq!( @@ -90,7 +90,7 @@ fn bench_program_alu(bencher: &mut Bencher) { ); bencher.iter(|| { - vm.execute_program(&mut inner_iter).unwrap(); + vm.execute_program(&mut inner_iter, &[], &[]).unwrap(); }); let instructions = vm.get_last_instruction_count(); let summary = bencher.bench(|_bencher| {}).unwrap(); diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index f03928071e..d08337cf9c 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -67,7 +67,7 @@ fn main() { .expect("Unable to create BPF install directory") .success()); - let rust_programs = ["iter", "noop"]; + let rust_programs = ["alloc", "iter", "noop"]; for program in rust_programs.iter() { println!( "cargo:warning=(not a warning) Building Rust-based BPF programs: solana_bpf_rust_{}", diff --git a/programs/bpf/rust/alloc/.gitignore b/programs/bpf/rust/alloc/.gitignore new file mode 100644 index 0000000000..e13de17f65 --- /dev/null +++ b/programs/bpf/rust/alloc/.gitignore @@ -0,0 +1,3 @@ +/target/ + +Cargo.lock diff --git a/programs/bpf/rust/alloc/Cargo.toml b/programs/bpf/rust/alloc/Cargo.toml new file mode 100644 index 0000000000..566a9048e3 --- /dev/null +++ b/programs/bpf/rust/alloc/Cargo.toml @@ -0,0 +1,22 @@ + +# Note: This crate must be built using build.sh + +[package] +name = "solana-bpf-rust-alloc" +version = "0.15.0" +description = "Solana BPF alloc program written in Rust" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +edition = "2018" + +[dependencies] +solana-sdk-bpf-utils = { path = "../../../../sdk/bpf/rust-utils", version = "0.15.0" } + +[workspace] +members = [] + +[lib] +name = "solana_bpf_rust_alloc" +crate-type = ["cdylib"] diff --git a/programs/bpf/rust/alloc/Xargo.toml b/programs/bpf/rust/alloc/Xargo.toml new file mode 100644 index 0000000000..4203d10a3e --- /dev/null +++ b/programs/bpf/rust/alloc/Xargo.toml @@ -0,0 +1,7 @@ + + +[dependencies.compiler_builtins] +path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins" + +[target.bpfel-unknown-unknown.dependencies] +alloc = { path = "../../../../sdk/bpf/rust-bpf-sysroot/src/liballoc" } \ No newline at end of file diff --git a/programs/bpf/rust/alloc/src/lib.rs b/programs/bpf/rust/alloc/src/lib.rs new file mode 100644 index 0000000000..163fbc50e4 --- /dev/null +++ b/programs/bpf/rust/alloc/src/lib.rs @@ -0,0 +1,98 @@ +//! @brief Example Rust-based BPF program that prints out the parameters passed to it + +#![no_std] + +#[macro_use] +extern crate alloc; +extern crate solana_sdk_bpf_utils; + +use solana_sdk_bpf_utils::log::*; + +use core::alloc::Layout; +use core::mem; + +#[no_mangle] +pub extern "C" fn entrypoint(_input: *mut u8) -> bool { + unsafe { + // Confirm large allocation fails + + let layout = Layout::from_size_align(core::usize::MAX, mem::align_of::()).unwrap(); + let ptr = alloc::alloc::alloc(layout); + if !ptr.is_null() { + sol_log("Error: Alloc of very larger buffer should fail"); + panic!(); + } + } + + unsafe { + // Test modest allocation and deallocation + + let layout = Layout::from_size_align(100, mem::align_of::()).unwrap(); + let ptr = alloc::alloc::alloc(layout); + if ptr.is_null() { + sol_log("Error: Alloc of 100 bytes failed"); + alloc::alloc::handle_alloc_error(layout); + } + alloc::alloc::dealloc(ptr, layout); + } + + unsafe { + // Test allocated memory read and write + + let layout = Layout::from_size_align(100, mem::align_of::()).unwrap(); + let ptr = alloc::alloc::alloc(layout); + if ptr.is_null() { + sol_log("Error: Alloc of 100 bytes failed"); + alloc::alloc::handle_alloc_error(layout); + } + let iter = 0..100; // This weirdness due to #issue $#4271 + for (i, _) in iter.enumerate() { + *ptr.add(i) = i as u8; + } + let iter = 0..100; // This weirdness due to #issue $#4271 + for (i, _) in iter.enumerate() { + assert_eq!(*ptr.add(i as usize), i as u8); + } + sol_log_64(0x3, 0, 0, 0, *ptr.add(42) as u64); + assert_eq!(*ptr.add(42), 42); + alloc::alloc::dealloc(ptr, layout); + } + + // // TODO not supported for system or bump allocator + // unsafe { + // // Test alloc all bytes and one more (assumes heap size of 2048) + + // let layout = Layout::from_size_align(2048, mem::align_of::()).unwrap(); + // let ptr = alloc::alloc::alloc(layout); + // if ptr.is_null() { + // sol_log("Error: Alloc of 2048 bytes failed"); + // alloc::alloc::handle_alloc_error(layout); + // } + // let layout = Layout::from_size_align(1, mem::align_of::()).unwrap(); + // let ptr_fail = alloc::alloc::alloc(layout); + // if !ptr_fail.is_null() { + // sol_log("Error: Able to alloc 1 more then max"); + // panic!(); + // } + // alloc::alloc::dealloc(ptr, layout); + // } + + // TODO gremlin causes crash only on CI + // { + // // Test allocated vector + + // const ITERS: usize = 100; + // let ones = vec![1_u64; ITERS]; + // let mut sum: u64 = 0; + + // for (i, v) in ones.iter().enumerate() { + // sol_log_64(i as u64, 0, 0, 0, 0); + // sum += ones[i as usize]; + // } + // sol_log_64(0x4, 0, 0, 0, sum); + // assert_eq!(sum, ITERS as u64); + // } + + sol_log("Success"); + true +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 180b2bbb4d..e8b2def75b 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -107,6 +107,7 @@ mod bpf { solana_logger::setup(); let programs = [ + "solana_bpf_rust_alloc", // Disable due to #4271 "solana_bpf_rust_iter", "solana_bpf_rust_noop", ]; diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 59bcbeba4d..7a9045e24f 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -13,7 +13,7 @@ bincode = "1.1.4" byteorder = "1.3.1" libc = "0.2.55" log = "0.4.2" -solana_rbpf = "=0.1.11" +solana_rbpf = "=0.1.12" serde = "1.0.91" solana-logger = { path = "../../logger", version = "0.15.0" } solana-sdk = { path = "../../sdk", version = "0.15.0" } diff --git a/programs/bpf_loader/src/alloc.rs b/programs/bpf_loader/src/alloc.rs new file mode 100644 index 0000000000..aa6c3d4176 --- /dev/null +++ b/programs/bpf_loader/src/alloc.rs @@ -0,0 +1,17 @@ +use std::alloc::Layout; +use std::fmt; + +/// Based loosely on the unstable std::alloc::Alloc trait +pub trait Alloc { + fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + fn dealloc(&mut self, ptr: *mut u8, layout: Layout); +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AllocErr; + +impl fmt::Display for AllocErr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Error: Memory allocation failed") + } +} diff --git a/programs/bpf_loader/src/allocator_bump.rs b/programs/bpf_loader/src/allocator_bump.rs new file mode 100644 index 0000000000..7755b08544 --- /dev/null +++ b/programs/bpf_loader/src/allocator_bump.rs @@ -0,0 +1,32 @@ +use crate::alloc; + +use alloc::{Alloc, AllocErr}; +use std::alloc::Layout; + +#[derive(Debug)] +pub struct BPFAllocator { + heap: Vec, + pos: usize, +} + +impl BPFAllocator { + pub fn new(heap: Vec) -> Self { + Self { heap, pos: 0 } + } +} + +impl Alloc for BPFAllocator { + fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + if self.pos + layout.size() <= self.heap.len() { + let ptr = unsafe { self.heap.as_mut_ptr().add(self.pos) }; + self.pos += layout.size(); + Ok(ptr) + } else { + Err(AllocErr) + } + } + + fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) { + // It's a bump allocator, free not supported + } +} diff --git a/programs/bpf_loader/src/allocator_system.rs b/programs/bpf_loader/src/allocator_system.rs new file mode 100644 index 0000000000..e20027bf0d --- /dev/null +++ b/programs/bpf_loader/src/allocator_system.rs @@ -0,0 +1,40 @@ +use crate::alloc; + +use alloc::{Alloc, AllocErr}; +use std::alloc::{self as system_alloc, Layout}; + +#[derive(Debug)] +pub struct BPFAllocator { + allocated: usize, + size: usize, +} + +impl BPFAllocator { + pub fn new(heap: Vec) -> Self { + Self { + allocated: 0, + size: heap.len(), + } + } +} + +impl Alloc for BPFAllocator { + fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + if self.allocated + layout.size() <= self.size { + let ptr = unsafe { system_alloc::alloc(layout) }; + if !ptr.is_null() { + self.allocated += layout.size(); + return Ok(ptr); + } + } + Err(AllocErr) + } + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + self.allocated -= layout.size(); + unsafe { + system_alloc::dealloc(ptr, layout); + } + } +} diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index d162d6befc..4fe2f88851 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -1,5 +1,9 @@ +pub mod alloc; +pub mod allocator_bump; +pub mod allocator_system; pub mod bpf_verifier; +use alloc::Alloc; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use libc::c_char; use log::*; @@ -9,32 +13,38 @@ use solana_sdk::instruction::InstructionError; use solana_sdk::loader_instruction::LoaderInstruction; use solana_sdk::pubkey::Pubkey; use solana_sdk::solana_entrypoint; +use std::alloc::Layout; +use std::any::Any; use std::ffi::CStr; use std::io::prelude::*; use std::io::{Error, ErrorKind}; use std::mem; -// TODO use rbpf's disassemble -#[allow(dead_code)] -fn dump_program(key: &Pubkey, prog: &[u8]) { - let mut eight_bytes: Vec = Vec::new(); - info!("BPF Program: {:?}", key); - for i in prog.iter() { - if eight_bytes.len() >= 7 { - info!("{:02X?}", eight_bytes); - eight_bytes.clear(); - } else { - eight_bytes.push(i.clone()); - } - } -} +/// Program heap allocators are intended to allocate/free from a given +/// chunk of memory. The specific allocator implementation is +/// selectable at build-time. +/// Enable only one of the following BPFAllocator implementations. +/// Simple bump allocator, never frees +use allocator_bump::BPFAllocator; + +/// Use the system heap (test purposes only). This allocator relies on the system heap +/// and there is no mechanism to check read-write access privileges +/// at the moment. Therefor you must disable memory bounds checking +// use allocator_system::BPFAllocator; + +/// Default program heap size, allocators +/// are expected to enforce this +const DEFAULT_HEAP_SIZE: usize = 32 * 1024; + +/// Abort helper functions, called when the BPF program calls `abort()` pub fn helper_abort_verify( _arg1: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64, + _context: &mut Option>, _ro_regions: &[MemoryRegion], _rw_regions: &[MemoryRegion], ) -> Result<(()), Error> { @@ -43,35 +53,55 @@ pub fn helper_abort_verify( "Error: BPF program called abort()!", )) } - -pub fn helper_abort(_arg1: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64) -> u64 { +pub fn helper_abort( + _arg1: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + _context: &mut Option>, +) -> u64 { // Never called because its verify function always returns an error 0 } +/// Panic helper functions, called when the BPF program calls 'sol_panic_()` +/// The verify function returns an error which will cause the BPF program +/// to be halted immediately pub fn helper_sol_panic_verify( _arg1: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64, + _context: &mut Option>, _ro_regions: &[MemoryRegion], _rw_regions: &[MemoryRegion], ) -> Result<(()), Error> { Err(Error::new(ErrorKind::Other, "Error: BPF program Panic!")) } - -pub fn helper_sol_panic(_arg1: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64) -> u64 { +pub fn helper_sol_panic( + _arg1: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + _context: &mut Option>, +) -> u64 { // Never called because its verify function always returns an error 0 } +/// Logging helper functions, called when the BPF program calls `sol_log_()` or +/// `sol_log_64_()`. Both functions use a common verify function to validate +/// their parameters. pub fn helper_sol_log_verify( addr: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64, + _context: &mut Option>, ro_regions: &[MemoryRegion], _rw_regions: &[MemoryRegion], ) -> Result<(()), Error> { @@ -94,8 +124,14 @@ pub fn helper_sol_log_verify( "Error: Load segfault, bad string pointer", )) } - -pub fn helper_sol_log(addr: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64) -> u64 { +pub fn helper_sol_log( + addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + _context: &mut Option>, +) -> u64 { let c_buf: *const c_char = addr as *const c_char; let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; match c_str.to_str() { @@ -104,8 +140,14 @@ pub fn helper_sol_log(addr: u64, _arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64) }; 0 } - -pub fn helper_sol_log_u64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { +pub fn helper_sol_log_u64( + arg1: u64, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, + _context: &mut Option>, +) -> u64 { info!( "sol_log_u64: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}", arg1, arg2, arg3, arg4, arg5 @@ -113,23 +155,78 @@ pub fn helper_sol_log_u64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) 0 } -pub fn create_vm(prog: &[u8]) -> Result { +/// Dynamic memory allocation helper called when the BPF program calls +/// `sol_alloc_free_()`. The allocator is expected to allocate/free +/// from/to a given chunk of memory and enforce size restrictions. The +/// memory chunk is given to the allocator during allocator creation and +/// information about that memory (start address and size) is passed +/// to the VM to use for enforcement. +pub fn helper_sol_alloc_free( + size: u64, + free_ptr: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + context: &mut Option>, +) -> u64 { + if let Some(context) = context { + if let Some(allocator) = context.downcast_mut::() { + return { + let layout = Layout::from_size_align(size as usize, mem::align_of::()).unwrap(); + if free_ptr == 0 { + match allocator.alloc(layout) { + Ok(ptr) => ptr as u64, + Err(_) => 0, + } + } else { + allocator.dealloc(free_ptr as *mut u8, layout); + 0 + } + }; + }; + } + panic!("Failed to get alloc_free context"); +} + +pub fn create_vm(prog: &[u8]) -> Result<(EbpfVmRaw, MemoryRegion), Error> { let mut vm = EbpfVmRaw::new(None)?; vm.set_verifier(bpf_verifier::check)?; - vm.set_max_instruction_count(36000)?; // TODO 36000 is a wag, need to tune + vm.set_max_instruction_count(36000)?; vm.set_elf(&prog)?; - vm.register_helper_ex("abort", Some(helper_abort_verify), helper_abort)?; - vm.register_helper_ex("sol_panic", Some(helper_sol_panic_verify), helper_sol_panic)?; + vm.register_helper_ex("abort", Some(helper_abort_verify), helper_abort, None)?; + vm.register_helper_ex( + "sol_panic", + Some(helper_sol_panic_verify), + helper_sol_panic, + None, + )?; vm.register_helper_ex( "sol_panic_", Some(helper_sol_panic_verify), helper_sol_panic, + None, )?; - vm.register_helper_ex("sol_log", Some(helper_sol_log_verify), helper_sol_log)?; - vm.register_helper_ex("sol_log_", Some(helper_sol_log_verify), helper_sol_log)?; - vm.register_helper_ex("sol_log_64", None, helper_sol_log_u64)?; - vm.register_helper_ex("sol_log_64_", None, helper_sol_log_u64)?; - Ok(vm) + vm.register_helper_ex("sol_log", Some(helper_sol_log_verify), helper_sol_log, None)?; + vm.register_helper_ex( + "sol_log_", + Some(helper_sol_log_verify), + helper_sol_log, + None, + )?; + vm.register_helper_ex("sol_log_64", None, helper_sol_log_u64, None)?; + vm.register_helper_ex("sol_log_64_", None, helper_sol_log_u64, None)?; + + let heap = vec![0_u8; DEFAULT_HEAP_SIZE]; + let heap_region = MemoryRegion::new_from_slice(&heap); + let context = Box::new(BPFAllocator::new(heap)); + vm.register_helper_ex( + "sol_alloc_free_", + None, + helper_sol_alloc_free, + Some(context), + )?; + + Ok((vm, heap_region)) } fn serialize_parameters( @@ -192,16 +289,16 @@ fn entrypoint( let (progs, params) = keyed_accounts.split_at_mut(1); let prog = &progs[0].account.data; info!("Call BPF program"); - //dump_program(keyed_accounts[0].key, prog); - let mut vm = match create_vm(prog) { - Ok(vm) => vm, + let (mut vm, heap_region) = match create_vm(prog) { + Ok(info) => info, Err(e) => { warn!("Failed to create BPF VM: {}", e); return Err(InstructionError::GenericError); } }; let mut v = serialize_parameters(program_id, params, &tx_data, tick_height); - match vm.execute_program(v.as_mut_slice()) { + + match vm.execute_program(v.as_mut_slice(), &[], &[heap_region]) { Ok(status) => { if 0 == status { warn!("BPF program failed: {}", status); @@ -272,6 +369,6 @@ mod tests { vm.set_verifier(bpf_verifier::check).unwrap(); vm.set_max_instruction_count(10).unwrap(); vm.set_program(prog).unwrap(); - vm.execute_program(input).unwrap(); + vm.execute_program(input, &[], &[]).unwrap(); } } diff --git a/sdk/bpf/rust-utils/src/alloc.rs b/sdk/bpf/rust-utils/src/alloc.rs new file mode 100644 index 0000000000..fe0b1f1460 --- /dev/null +++ b/sdk/bpf/rust-utils/src/alloc.rs @@ -0,0 +1,23 @@ +//! @brief Solana Rust-based BPF program memory allocator shim + +use crate::log::*; +use core::alloc::{GlobalAlloc, Layout}; + +pub struct Allocator; +unsafe impl GlobalAlloc for Allocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + sol_alloc_free_(layout.size() as u64, 0) + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + sol_alloc_free_(layout.size() as u64, ptr as u64); + } +} +extern "C" { + fn sol_alloc_free_(size: u64, ptr: u64) -> *mut u8; +} + +#[alloc_error_handler] +fn my_alloc_error_handler(_: core::alloc::Layout) -> ! { + sol_log("alloc_error_handler"); + panic!(); +} diff --git a/sdk/bpf/rust-utils/src/lib.rs b/sdk/bpf/rust-utils/src/lib.rs index 48f7c654be..b5d4b353e4 100644 --- a/sdk/bpf/rust-utils/src/lib.rs +++ b/sdk/bpf/rust-utils/src/lib.rs @@ -1,7 +1,13 @@ //! @brief Solana Rust-based BPF program utility functions and types #![no_std] +#![feature(allocator_api)] +#![feature(alloc_error_handler)] +pub mod alloc; pub mod entrypoint; pub mod log; pub mod panic; + +#[global_allocator] +static A: alloc::Allocator = alloc::Allocator; diff --git a/sdk/bpf/rust-utils/src/panic.rs b/sdk/bpf/rust-utils/src/panic.rs index 330a540ea7..681a549020 100644 --- a/sdk/bpf/rust-utils/src/panic.rs +++ b/sdk/bpf/rust-utils/src/panic.rs @@ -1,6 +1,5 @@ //! @brief Solana Rust-based BPF program panic handling -// #[cfg(not(test))] use crate::log::*; use core::panic::PanicInfo; @@ -18,9 +17,15 @@ fn panic(info: &PanicInfo) -> ! { sol_panic_(); } } - sol_log_64(0, 0, 0, u64::from(location.line()), u64::from(location.column())); + sol_log_64( + 0, + 0, + 0, + u64::from(location.line()), + u64::from(location.column()), + ); } else { - sol_log("Panic! but could not get location information"); + sol_log("Panic!, but could not get location information"); } unsafe { sol_panic_();