Add adjustable stack size and call depth (bp #12728) (#12769)

* Add adjustable stack size and call depth (#12728)

(cherry picked from commit c3907be623)

# 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 <jack@solana.com>
This commit is contained in:
mergify[bot]
2020-10-09 23:27:59 +00:00
committed by GitHub
parent 079ea91d6f
commit ad31768dd9
12 changed files with 140 additions and 12 deletions

4
Cargo.lock generated
View File

@@ -4783,9 +4783,9 @@ dependencies = [
[[package]] [[package]]
name = "solana_rbpf" name = "solana_rbpf"
version = "0.1.31" version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "962f8f04ac7239fe4dd45fa4ce706ec78b59a0da9f41def463832857e36c60b0" checksum = "9a95dbe2b00920ac4e1524b7442cf5319f01e8fa5742930ac60148882fd7738b"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"combine", "combine",

View File

@@ -1784,6 +1784,13 @@ dependencies = [
"solana-sdk", "solana-sdk",
] ]
[[package]]
name = "solana-bpf-rust-call-depth"
version = "1.3.16"
dependencies = [
"solana-sdk",
]
[[package]] [[package]]
name = "solana-bpf-rust-dep-crate" name = "solana-bpf-rust-dep-crate"
version = "1.3.16" version = "1.3.16"
@@ -2145,9 +2152,9 @@ dependencies = [
[[package]] [[package]]
name = "solana_rbpf" name = "solana_rbpf"
version = "0.1.31" version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "962f8f04ac7239fe4dd45fa4ce706ec78b59a0da9f41def463832857e36c60b0" checksum = "9a95dbe2b00920ac4e1524b7442cf5319f01e8fa5742930ac60148882fd7738b"
dependencies = [ dependencies = [
"byteorder 1.3.4", "byteorder 1.3.4",
"combine", "combine",

View File

@@ -27,7 +27,7 @@ solana-logger = { path = "../../logger", version = "1.3.16" }
solana-measure = { path = "../../measure", version = "1.3.16" } solana-measure = { path = "../../measure", version = "1.3.16" }
solana-runtime = { path = "../../runtime", version = "1.3.16" } solana-runtime = { path = "../../runtime", version = "1.3.16" }
solana-sdk = { path = "../../sdk", version = "1.3.16" } solana-sdk = { path = "../../sdk", version = "1.3.16" }
solana_rbpf = "=0.1.31" solana_rbpf = "=0.1.32"
[[bench]] [[bench]]
name = "bpf_loader" name = "bpf_loader"
@@ -37,6 +37,7 @@ members = [
"rust/128bit", "rust/128bit",
"rust/128bit_dep", "rust/128bit_dep",
"rust/alloc", "rust/alloc",
"rust/call_depth",
"rust/dep_crate", "rust/dep_crate",
"rust/deprecated_loader", "rust/deprecated_loader",
"rust/dup_accounts", "rust/dup_accounts",

View File

@@ -67,6 +67,7 @@ fn main() {
let rust_programs = [ let rust_programs = [
"128bit", "128bit",
"alloc", "alloc",
"call_depth",
"dep_crate", "dep_crate",
"deprecated_loader", "deprecated_loader",
"dup_accounts", "dup_accounts",

View File

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

View File

@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

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

View File

@@ -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] #[test]
fn assert_instruction_count() { fn assert_instruction_count() {
solana_logger::setup(); solana_logger::setup();

View File

@@ -15,7 +15,7 @@ num-derive = "0.3"
num-traits = "0.2" num-traits = "0.2"
solana-runtime = { path = "../../runtime", version = "1.3.16" } solana-runtime = { path = "../../runtime", version = "1.3.16" }
solana-sdk = { path = "../../sdk", 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" thiserror = "1.0"
[dev-dependencies] [dev-dependencies]

View File

@@ -14,7 +14,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
use solana_rbpf::{ use solana_rbpf::{
error::{EbpfError, UserDefinedError}, error::{EbpfError, UserDefinedError},
memory_region::MemoryRegion, memory_region::MemoryRegion,
vm::{EbpfVm, Executable, InstructionMeter}, vm::{Config, EbpfVm, Executable, InstructionMeter},
}; };
use solana_runtime::{ use solana_runtime::{
feature_set::compute_budget_balancing, feature_set::compute_budget_balancing,
@@ -116,7 +116,14 @@ pub fn create_vm<'a>(
parameter_accounts: &'a [KeyedAccount<'a>], parameter_accounts: &'a [KeyedAccount<'a>],
invoke_context: &'a mut dyn InvokeContext, invoke_context: &'a mut dyn InvokeContext,
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> { ) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
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 = let heap_region =
syscalls::register_syscalls(loader_id, &mut vm, parameter_accounts, invoke_context)?; syscalls::register_syscalls(loader_id, &mut vm, parameter_accounts, invoke_context)?;
Ok((vm, heap_region)) Ok((vm, heap_region))
@@ -404,7 +411,7 @@ mod tests {
let input = &mut [0x00]; let input = &mut [0x00];
let executable = EbpfVm::create_executable_from_text_bytes(program, None).unwrap(); let executable = EbpfVm::create_executable_from_text_bytes(program, None).unwrap();
let mut vm = EbpfVm::<BPFError>::new(executable.as_ref()).unwrap(); let mut vm = EbpfVm::<BPFError>::new(executable.as_ref(), Config::default()).unwrap();
let instruction_meter = TestInstructionMeter { remaining: 10 }; let instruction_meter = TestInstructionMeter { remaining: 10 };
vm.execute_program_metered(input, &[], &[], instruction_meter) vm.execute_program_metered(input, &[], &[], instruction_meter)
.unwrap(); .unwrap();
@@ -601,6 +608,8 @@ mod tests {
create_program_address_units: 1500, create_program_address_units: 1500,
invoke_units: 1000, invoke_units: 1000,
max_invoke_depth: 2, max_invoke_depth: 2,
max_call_depth: 20,
stack_frame_size: 4096,
}, },
Rc::new(RefCell::new(Executors::default())), Rc::new(RefCell::new(Executors::default())),
None, None,

View File

@@ -45,6 +45,10 @@ pub mod max_invoke_depth_4 {
solana_sdk::declare_id!("EdM9xggY5y7AhNMskRG8NgGMnaP4JFNsWi8ZZtyT1af5"); solana_sdk::declare_id!("EdM9xggY5y7AhNMskRG8NgGMnaP4JFNsWi8ZZtyT1af5");
} }
pub mod max_program_call_depth_64 {
solana_sdk::declare_id!("YCKSgA6XmjtkQrHBQjpyNrX6EMhJPcYcLWMVgWn36iv");
}
lazy_static! { lazy_static! {
/// Map of feature identifiers to user-visible description /// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [ pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@@ -59,6 +63,7 @@ lazy_static! {
(compute_budget_balancing::id(), "compute budget balancing"), (compute_budget_balancing::id(), "compute budget balancing"),
(no_overflow_rent_distribution::id(), "no overflow rent distribution"), (no_overflow_rent_distribution::id(), "no overflow rent distribution"),
(max_invoke_depth_4::id(), "max invoke call depth 4"), (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 ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()

View File

@@ -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::{ use solana_sdk::{
account::{Account, KeyedAccount}, account::{Account, KeyedAccount},
instruction::{CompiledInstruction, Instruction, InstructionError}, instruction::{CompiledInstruction, Instruction, InstructionError},
@@ -88,6 +90,10 @@ pub struct ComputeBudget {
pub invoke_units: u64, pub invoke_units: u64,
/// Maximum cross-program invocation depth allowed including the orignal caller /// Maximum cross-program invocation depth allowed including the orignal caller
pub max_invoke_depth: usize, 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 { impl Default for ComputeBudget {
fn default() -> Self { fn default() -> Self {
@@ -105,6 +111,8 @@ impl ComputeBudget {
create_program_address_units: 0, create_program_address_units: 0,
invoke_units: 0, invoke_units: 0,
max_invoke_depth: 1, max_invoke_depth: 1,
max_call_depth: 20,
stack_frame_size: 4_096,
}; };
if feature_set.is_active(&compute_budget_balancing::id()) { if feature_set.is_active(&compute_budget_balancing::id()) {
@@ -121,9 +129,14 @@ impl ComputeBudget {
compute_budget = ComputeBudget { compute_budget = ComputeBudget {
max_invoke_depth: 4, max_invoke_depth: 4,
..compute_budget ..compute_budget
} };
}
if feature_set.is_active(&max_program_call_depth_64::id()) {
compute_budget = ComputeBudget {
max_call_depth: 64,
..compute_budget
};
} }
compute_budget compute_budget
} }
} }