From ad31768dd9f1dda3e5cc8311f10ece43f4550b16 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2020 23:27:59 +0000 Subject: [PATCH] Add adjustable stack size and call depth (bp #12728) (#12769) * Add adjustable stack size and call depth (#12728) (cherry picked from commit c3907be623eb9c15c6f6b70fb451a395758036dc) # Conflicts: # programs/bpf/Cargo.lock # programs/bpf/Cargo.toml # programs/bpf/build.rs # programs/bpf_loader/Cargo.toml # programs/bpf_loader/src/lib.rs # runtime/src/feature_set.rs # runtime/src/process_instruction.rs * resolve conflicts Co-authored-by: Jack May --- Cargo.lock | 4 +-- programs/bpf/Cargo.lock | 11 ++++++-- programs/bpf/Cargo.toml | 3 +- programs/bpf/build.rs | 1 + programs/bpf/rust/call_depth/Cargo.toml | 26 +++++++++++++++++ programs/bpf/rust/call_depth/Xargo.toml | 2 ++ programs/bpf/rust/call_depth/src/lib.rs | 27 ++++++++++++++++++ programs/bpf/tests/programs.rs | 37 +++++++++++++++++++++++++ programs/bpf_loader/Cargo.toml | 2 +- programs/bpf_loader/src/lib.rs | 15 ++++++++-- runtime/src/feature_set.rs | 5 ++++ runtime/src/process_instruction.rs | 19 +++++++++++-- 12 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 programs/bpf/rust/call_depth/Cargo.toml create mode 100644 programs/bpf/rust/call_depth/Xargo.toml create mode 100644 programs/bpf/rust/call_depth/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0eedeca160..e2144aaf6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4783,9 +4783,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962f8f04ac7239fe4dd45fa4ce706ec78b59a0da9f41def463832857e36c60b0" +checksum = "9a95dbe2b00920ac4e1524b7442cf5319f01e8fa5742930ac60148882fd7738b" dependencies = [ "byteorder", "combine", diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 54d96c5c33..56186439ce 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -1784,6 +1784,13 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-bpf-rust-call-depth" +version = "1.3.16" +dependencies = [ + "solana-sdk", +] + [[package]] name = "solana-bpf-rust-dep-crate" version = "1.3.16" @@ -2145,9 +2152,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962f8f04ac7239fe4dd45fa4ce706ec78b59a0da9f41def463832857e36c60b0" +checksum = "9a95dbe2b00920ac4e1524b7442cf5319f01e8fa5742930ac60148882fd7738b" dependencies = [ "byteorder 1.3.4", "combine", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index 8ea1e6bf38..519843628f 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -27,7 +27,7 @@ solana-logger = { path = "../../logger", version = "1.3.16" } solana-measure = { path = "../../measure", version = "1.3.16" } solana-runtime = { path = "../../runtime", version = "1.3.16" } solana-sdk = { path = "../../sdk", version = "1.3.16" } -solana_rbpf = "=0.1.31" +solana_rbpf = "=0.1.32" [[bench]] name = "bpf_loader" @@ -37,6 +37,7 @@ members = [ "rust/128bit", "rust/128bit_dep", "rust/alloc", + "rust/call_depth", "rust/dep_crate", "rust/deprecated_loader", "rust/dup_accounts", diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 2926062117..70f022f308 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -67,6 +67,7 @@ fn main() { let rust_programs = [ "128bit", "alloc", + "call_depth", "dep_crate", "deprecated_loader", "dup_accounts", diff --git a/programs/bpf/rust/call_depth/Cargo.toml b/programs/bpf/rust/call_depth/Cargo.toml new file mode 100644 index 0000000000..39258a22f2 --- /dev/null +++ b/programs/bpf/rust/call_depth/Cargo.toml @@ -0,0 +1,26 @@ + +# Note: This crate must be built using do.sh + +[package] +name = "solana-bpf-rust-call-depth" +version = "1.3.16" +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/" +edition = "2018" + +[dependencies] +solana-sdk = { path = "../../../../sdk/", version = "1.3.16", default-features = false } + +[features] +program = ["solana-sdk/program"] +default = ["program", "solana-sdk/default"] + +[lib] +name = "solana_bpf_rust_call_depth" +crate-type = ["cdylib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/call_depth/Xargo.toml b/programs/bpf/rust/call_depth/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/programs/bpf/rust/call_depth/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/programs/bpf/rust/call_depth/src/lib.rs b/programs/bpf/rust/call_depth/src/lib.rs new file mode 100644 index 0000000000..a2a33e1d34 --- /dev/null +++ b/programs/bpf/rust/call_depth/src/lib.rs @@ -0,0 +1,27 @@ +//! @brief Example Rust-based BPF program that tests call depth and stack usage + +use solana_sdk::{entrypoint::SUCCESS, info}; + +#[inline(never)] +pub fn recurse(data: &mut [u8]) { + if data.len() <= 1 { + return; + } + recurse(&mut data[1..]); + info!(line!(), 0, 0, 0, data[0]); +} + +/// # Safety +#[inline(never)] +#[no_mangle] +pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 { + info!("Call depth"); + let depth = *(input.add(16) as *mut u8); + info!(line!(), 0, 0, 0, depth); + let mut data = Vec::with_capacity(depth as usize); + for i in 0_u8..depth { + data.push(i); + } + recurse(&mut data); + SUCCESS +} diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index b7008df292..9c270665b6 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -638,6 +638,43 @@ fn test_program_bpf_invoke() { } } +#[cfg(feature = "bpf_rust")] +#[test] +fn test_program_bpf_call_depth() { + solana_logger::setup(); + + println!("Test program: solana_bpf_rust_call_depth"); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + let mut bank = Bank::new(&genesis_config); + let (name, id, entrypoint) = solana_bpf_loader_program!(); + bank.add_builtin_loader(&name, id, entrypoint); + let bank_client = BankClient::new(bank); + let program_id = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_call_depth", + ); + + let instruction = Instruction::new( + program_id, + &(ComputeBudget::default().max_call_depth - 1), + vec![], + ); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert!(result.is_ok()); + + let instruction = + Instruction::new(program_id, &ComputeBudget::default().max_call_depth, vec![]); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert!(result.is_err()); +} + #[test] fn assert_instruction_count() { solana_logger::setup(); diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 6f88737430..0f276e86c3 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -15,7 +15,7 @@ num-derive = "0.3" num-traits = "0.2" solana-runtime = { path = "../../runtime", version = "1.3.16" } solana-sdk = { path = "../../sdk", version = "1.3.16" } -solana_rbpf = "=0.1.31" +solana_rbpf = "=0.1.32" thiserror = "1.0" [dev-dependencies] diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 8f689c1778..6b26a6572e 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -14,7 +14,7 @@ use num_derive::{FromPrimitive, ToPrimitive}; use solana_rbpf::{ error::{EbpfError, UserDefinedError}, memory_region::MemoryRegion, - vm::{EbpfVm, Executable, InstructionMeter}, + vm::{Config, EbpfVm, Executable, InstructionMeter}, }; use solana_runtime::{ feature_set::compute_budget_balancing, @@ -116,7 +116,14 @@ pub fn create_vm<'a>( parameter_accounts: &'a [KeyedAccount<'a>], invoke_context: &'a mut dyn InvokeContext, ) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError> { - let mut vm = EbpfVm::new(executable)?; + let compute_budget = invoke_context.get_compute_budget(); + let mut vm = EbpfVm::new( + executable, + Config { + max_call_depth: compute_budget.max_call_depth, + stack_frame_size: compute_budget.stack_frame_size, + }, + )?; let heap_region = syscalls::register_syscalls(loader_id, &mut vm, parameter_accounts, invoke_context)?; Ok((vm, heap_region)) @@ -404,7 +411,7 @@ mod tests { let input = &mut [0x00]; let executable = EbpfVm::create_executable_from_text_bytes(program, None).unwrap(); - let mut vm = EbpfVm::::new(executable.as_ref()).unwrap(); + let mut vm = EbpfVm::::new(executable.as_ref(), Config::default()).unwrap(); let instruction_meter = TestInstructionMeter { remaining: 10 }; vm.execute_program_metered(input, &[], &[], instruction_meter) .unwrap(); @@ -601,6 +608,8 @@ mod tests { create_program_address_units: 1500, invoke_units: 1000, max_invoke_depth: 2, + max_call_depth: 20, + stack_frame_size: 4096, }, Rc::new(RefCell::new(Executors::default())), None, diff --git a/runtime/src/feature_set.rs b/runtime/src/feature_set.rs index 68e3ff336d..2b203edc94 100644 --- a/runtime/src/feature_set.rs +++ b/runtime/src/feature_set.rs @@ -45,6 +45,10 @@ pub mod max_invoke_depth_4 { solana_sdk::declare_id!("EdM9xggY5y7AhNMskRG8NgGMnaP4JFNsWi8ZZtyT1af5"); } +pub mod max_program_call_depth_64 { + solana_sdk::declare_id!("YCKSgA6XmjtkQrHBQjpyNrX6EMhJPcYcLWMVgWn36iv"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -59,6 +63,7 @@ lazy_static! { (compute_budget_balancing::id(), "compute budget balancing"), (no_overflow_rent_distribution::id(), "no overflow rent distribution"), (max_invoke_depth_4::id(), "max invoke call depth 4"), + (max_program_call_depth_64::id(), "max program call depth 64") /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/runtime/src/process_instruction.rs b/runtime/src/process_instruction.rs index c823693c85..693363d19c 100644 --- a/runtime/src/process_instruction.rs +++ b/runtime/src/process_instruction.rs @@ -1,4 +1,6 @@ -use crate::feature_set::{compute_budget_balancing, max_invoke_depth_4, FeatureSet}; +use crate::feature_set::{ + compute_budget_balancing, max_invoke_depth_4, max_program_call_depth_64, FeatureSet, +}; use solana_sdk::{ account::{Account, KeyedAccount}, instruction::{CompiledInstruction, Instruction, InstructionError}, @@ -88,6 +90,10 @@ pub struct ComputeBudget { pub invoke_units: u64, /// Maximum cross-program invocation depth allowed including the orignal caller pub max_invoke_depth: usize, + /// Maximum BPF to BPF call depth + pub max_call_depth: usize, + /// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend + pub stack_frame_size: usize, } impl Default for ComputeBudget { fn default() -> Self { @@ -105,6 +111,8 @@ impl ComputeBudget { create_program_address_units: 0, invoke_units: 0, max_invoke_depth: 1, + max_call_depth: 20, + stack_frame_size: 4_096, }; if feature_set.is_active(&compute_budget_balancing::id()) { @@ -121,9 +129,14 @@ impl ComputeBudget { compute_budget = ComputeBudget { max_invoke_depth: 4, ..compute_budget - } + }; + } + if feature_set.is_active(&max_program_call_depth_64::id()) { + compute_budget = ComputeBudget { + max_call_depth: 64, + ..compute_budget + }; } - compute_budget } }