Refactor - Share RBPF config and syscall registry globally (#31727)

* Adds LoadedPrograms::program_runtime_environment_v1.

* No need to explicitly remove precompiles from program_accounts_map.
This commit is contained in:
Alexander Meißner 2023-05-20 20:28:00 +02:00 committed by GitHub
parent 4796e2fd84
commit 83f692ce67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 123 deletions

View File

@ -10,7 +10,7 @@ use {
clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
log::*,
solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
solana_bpf_loader_program::syscalls::create_loader,
solana_bpf_loader_program::syscalls::create_program_runtime_environment,
solana_clap_utils::{
self, hidden_unless_forced, input_parsers::*, input_validators::*, keypair::*,
},
@ -2022,16 +2022,18 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
.map_err(|err| format!("Unable to read program file: {err}"))?;
// Verify the program
let loader = create_loader(
let program_runtime_environment = create_program_runtime_environment(
&FeatureSet::default(),
&ComputeBudget::default(),
true,
false,
)
.unwrap();
let executable =
Executable::<TautologyVerifier, InvokeContext>::from_elf(&program_data, loader)
.map_err(|err| format!("ELF error: {err}"))?;
let executable = Executable::<TautologyVerifier, InvokeContext>::from_elf(
&program_data,
Arc::new(program_runtime_environment),
)
.map_err(|err| format!("ELF error: {err}"))?;
let _ = Executable::<RequisiteVerifier, InvokeContext>::verified(executable)
.map_err(|err| format!("ELF error: {err}"))?;

View File

@ -6,7 +6,7 @@ use {
serde_json::Result,
solana_bpf_loader_program::{
create_vm, load_program_from_bytes, serialization::serialize_parameters,
syscalls::create_loader,
syscalls::create_program_runtime_environment,
},
solana_clap_utils::input_parsers::pubkeys_of,
solana_ledger::{
@ -341,8 +341,6 @@ fn load_program<'a>(
let mut contents = Vec::new();
file.read_to_end(&mut contents).unwrap();
let slot = Slot::default();
let reject_deployment_of_broken_elfs = false;
let debugging_features = true;
let log_collector = invoke_context.get_log_collector();
let loader_key = bpf_loader_upgradeable::id();
let mut load_program_metrics = LoadProgramMetrics {
@ -350,46 +348,51 @@ fn load_program<'a>(
..LoadProgramMetrics::default()
};
let account_size = contents.len();
let program_runtime_environment = create_program_runtime_environment(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
false, /* deployment */
true, /* debugging_features */
)
.unwrap();
// Allowing mut here, since it may be needed for jit compile, which is under a config flag
#[allow(unused_mut)]
let mut verified_executable = if is_elf {
let result = load_program_from_bytes(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
log_collector,
&mut load_program_metrics,
&contents,
&loader_key,
account_size,
slot,
reject_deployment_of_broken_elfs,
debugging_features,
Arc::new(program_runtime_environment),
);
match result {
Ok(loaded_program) => match loaded_program.program {
LoadedProgramType::LegacyV1(program) => Ok(unsafe { std::mem::transmute(program) }),
LoadedProgramType::LegacyV1(program) => Ok(program),
_ => unreachable!(),
},
Err(err) => Err(format!("Loading executable failed: {err:?}")),
}
} else {
let loader = create_loader(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
true,
true,
let executable = assemble::<InvokeContext>(
std::str::from_utf8(contents.as_slice()).unwrap(),
Arc::new(program_runtime_environment),
)
.unwrap();
let executable =
assemble::<InvokeContext>(std::str::from_utf8(contents.as_slice()).unwrap(), loader)
.unwrap();
Executable::<RequisiteVerifier, InvokeContext>::verified(executable)
.map_err(|err| format!("Assembling executable failed: {err:?}"))
}
.unwrap();
#[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
verified_executable.jit_compile().unwrap();
verified_executable
unsafe {
std::mem::transmute::<
Executable<RequisiteVerifier, InvokeContext<'static>>,
Executable<RequisiteVerifier, InvokeContext<'a>>,
>(verified_executable)
}
}
enum Action {
@ -537,7 +540,7 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
let mut loaded_programs =
LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
for key in cached_account_keys {
let program = bank.load_program(&key, true).unwrap_or_else(|err| {
let program = bank.load_program(&key).unwrap_or_else(|err| {
// Create a tombstone for the program in the cache
debug!("Failed to load program {}, error {:?}", key, err);
Arc::new(LoadedProgram::new_tombstone(

View File

@ -204,7 +204,7 @@ impl LoadedProgram {
/// Creates a new user program
pub fn new(
loader_key: &Pubkey,
loader: Arc<BuiltInProgram<InvokeContext<'static>>>,
program_runtime_environment: Arc<BuiltInProgram<InvokeContext<'static>>>,
deployment_slot: Slot,
effective_slot: Slot,
maybe_expiration_slot: Option<Slot>,
@ -213,7 +213,7 @@ impl LoadedProgram {
metrics: &mut LoadProgramMetrics,
) -> Result<Self, Box<dyn std::error::Error>> {
let mut load_elf_time = Measure::start("load_elf_time");
let executable = Executable::load(elf_bytes, loader.clone())?;
let executable = Executable::load(elf_bytes, program_runtime_environment.clone())?;
load_elf_time.stop();
metrics.load_elf_us = load_elf_time.as_us();
@ -337,7 +337,8 @@ pub struct LoadedPrograms {
///
/// Pubkey is the address of a program, multiple versions can coexists simultaneously under the same address (in different slots).
entries: HashMap<Pubkey, Vec<Arc<LoadedProgram>>>,
/// Globally shared RBPF config and syscall registry
pub program_runtime_environment_v1: Arc<BuiltInProgram<InvokeContext<'static>>>,
latest_root: Slot,
pub stats: Stats,
}

View File

@ -7,7 +7,6 @@ pub mod syscalls;
use {
solana_measure::measure::Measure,
solana_program_runtime::{
compute_budget::ComputeBudget,
ic_logger_msg, ic_msg,
invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
loaded_programs::{
@ -24,7 +23,7 @@ use {
error::EbpfError,
memory_region::{AccessType, MemoryCowCallback, MemoryMapping, MemoryRegion},
verifier::RequisiteVerifier,
vm::{ContextObject, EbpfVm, ProgramResult},
vm::{BuiltInProgram, ContextObject, EbpfVm, ProgramResult},
},
solana_sdk::{
account::WritableAccount,
@ -60,34 +59,20 @@ use {
rc::Rc,
sync::{atomic::Ordering, Arc},
},
syscalls::create_program_runtime_environment,
};
#[allow(clippy::too_many_arguments)]
pub fn load_program_from_bytes(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
load_program_metrics: &mut LoadProgramMetrics,
programdata: &[u8],
loader_key: &Pubkey,
account_size: usize,
deployment_slot: Slot,
reject_deployment_of_broken_elfs: bool,
debugging_features: bool,
program_runtime_environment: Arc<BuiltInProgram<InvokeContext<'static>>>,
) -> Result<LoadedProgram, InstructionError> {
let mut register_syscalls_time = Measure::start("register_syscalls_time");
let loader = syscalls::create_loader(
feature_set,
compute_budget,
reject_deployment_of_broken_elfs,
debugging_features,
)
.map_err(|e| {
ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e);
InstructionError::ProgramEnvironmentSetupFailure
})?;
register_syscalls_time.stop();
load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) {
deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET)
} else {
@ -95,7 +80,7 @@ pub fn load_program_from_bytes(
};
let loaded_program = LoadedProgram::new(
loader_key,
loader,
program_runtime_environment,
deployment_slot,
effective_slot,
None,
@ -110,42 +95,12 @@ pub fn load_program_from_bytes(
Ok(loaded_program)
}
fn get_programdata_offset_and_deployment_offset(
log_collector: &Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount,
programdata: &BorrowedAccount,
) -> Result<(usize, Slot), InstructionError> {
if bpf_loader_upgradeable::check_id(program.get_owner()) {
if let UpgradeableLoaderState::Program {
programdata_address: _,
} = program.get_state()?
{
if let UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address: _,
} = programdata.get_state()?
{
Ok((UpgradeableLoaderState::size_of_programdata_metadata(), slot))
} else {
ic_logger_msg!(log_collector, "Program has been closed");
Err(InstructionError::InvalidAccountData)
}
} else {
ic_logger_msg!(log_collector, "Invalid Program account");
Err(InstructionError::InvalidAccountData)
}
} else {
Ok((0, 0))
}
}
pub fn load_program_from_account(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount,
programdata: &BorrowedAccount,
debugging_features: bool,
program_runtime_environment: Arc<BuiltInProgram<InvokeContext<'static>>>,
) -> Result<(Arc<LoadedProgram>, LoadProgramMetrics), InstructionError> {
if !check_loader_id(program.get_owner()) {
ic_logger_msg!(
@ -156,7 +111,28 @@ pub fn load_program_from_account(
}
let (programdata_offset, deployment_slot) =
get_programdata_offset_and_deployment_offset(&log_collector, program, programdata)?;
if bpf_loader_upgradeable::check_id(program.get_owner()) {
if let UpgradeableLoaderState::Program {
programdata_address: _,
} = program.get_state()?
{
if let UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address: _,
} = programdata.get_state()?
{
(UpgradeableLoaderState::size_of_programdata_metadata(), slot)
} else {
ic_logger_msg!(log_collector, "Program has been closed");
return Err(InstructionError::InvalidAccountData);
}
} else {
ic_logger_msg!(log_collector, "Invalid Program account");
return Err(InstructionError::InvalidAccountData);
}
} else {
(0, 0)
};
let programdata_size = if programdata_offset != 0 {
programdata.get_data().len()
@ -171,7 +147,6 @@ pub fn load_program_from_account(
let loaded_program = Arc::new(load_program_from_bytes(
feature_set,
compute_budget,
log_collector,
&mut load_program_metrics,
programdata
@ -181,8 +156,7 @@ pub fn load_program_from_account(
program.get_owner(),
program.get_data().len().saturating_add(programdata_size),
deployment_slot,
false, /* reject_deployment_of_broken_elfs */
debugging_features,
program_runtime_environment,
)?);
Ok((loaded_program, load_program_metrics))
@ -204,17 +178,27 @@ macro_rules! deploy_program {
($invoke_context:expr, $program_id:expr, $loader_key:expr,
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
let mut load_program_metrics = LoadProgramMetrics::default();
let executor = load_program_from_bytes(
let mut register_syscalls_time = Measure::start("register_syscalls_time");
let program_runtime_environment = create_program_runtime_environment(
&$invoke_context.feature_set,
$invoke_context.get_compute_budget(),
true, /* deployment */
false, /* debugging_features */
).map_err(|e| {
ic_msg!($invoke_context, "Failed to register syscalls: {}", e);
InstructionError::ProgramEnvironmentSetupFailure
})?;
register_syscalls_time.stop();
load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
let executor = load_program_from_bytes(
&$invoke_context.feature_set,
$invoke_context.get_log_collector(),
&mut load_program_metrics,
$new_programdata,
$loader_key,
$account_size,
$slot,
true, /* reject_deployment_of_broken_elfs */
false, /* debugging_features */
Arc::new(program_runtime_environment),
)?;
if let Some(old_entry) = find_program_in_cache($invoke_context, &$program_id) {
let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed);
@ -1675,6 +1659,14 @@ pub mod test_utils {
};
pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
let mut load_program_metrics = LoadProgramMetrics::default();
let program_runtime_environment = create_program_runtime_environment(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
false, /* deployment */
false, /* debugging_features */
);
let program_runtime_environment = Arc::new(program_runtime_environment.unwrap());
let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
for index in 0..num_accounts {
let account = invoke_context
@ -1690,19 +1682,15 @@ pub mod test_utils {
.get_key_of_account_at_index(index)
.expect("Failed to get account key");
let mut load_program_metrics = LoadProgramMetrics::default();
if let Ok(loaded_program) = load_program_from_bytes(
&FeatureSet::all_enabled(),
&ComputeBudget::default(),
None,
&mut load_program_metrics,
account.data(),
owner,
account.data().len(),
0,
true,
false,
program_runtime_environment.clone(),
) {
invoke_context
.programs_modified_by_tx

View File

@ -142,12 +142,12 @@ macro_rules! register_feature_gated_function {
};
}
pub fn create_loader<'a>(
pub fn create_program_runtime_environment<'a>(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
reject_deployment_of_broken_elfs: bool,
debugging_features: bool,
) -> Result<Arc<BuiltInProgram<InvokeContext<'a>>>, Error> {
) -> Result<BuiltInProgram<InvokeContext<'a>>, Error> {
use rand::Rng;
let config = Config {
max_call_depth: compute_budget.max_call_depth,
@ -312,7 +312,7 @@ pub fn create_loader<'a>(
// Log data
result.register_function(b"sol_log_data", SyscallLogData::call)?;
Ok(Arc::new(result))
Ok(result)
}
fn translate(

View File

@ -13,7 +13,8 @@ extern crate test;
use {
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
solana_bpf_loader_program::{
create_vm, serialization::serialize_parameters, syscalls::create_loader,
create_vm, serialization::serialize_parameters,
syscalls::create_program_runtime_environment,
},
solana_measure::measure::Measure,
solana_program_runtime::{compute_budget::ComputeBudget, invoke_context::InvokeContext},
@ -89,16 +90,19 @@ macro_rules! with_mock_invoke_context {
fn bench_program_create_executable(bencher: &mut Bencher) {
let elf = load_program_from_file("bench_alu");
let loader = create_loader(
let program_runtime_environment = create_program_runtime_environment(
&FeatureSet::default(),
&ComputeBudget::default(),
true,
false,
)
.unwrap();
);
let program_runtime_environment = Arc::new(program_runtime_environment.unwrap());
bencher.iter(|| {
let _ =
Executable::<TautologyVerifier, InvokeContext>::from_elf(&elf, loader.clone()).unwrap();
let _ = Executable::<TautologyVerifier, InvokeContext>::from_elf(
&elf,
program_runtime_environment.clone(),
)
.unwrap();
});
}
@ -114,15 +118,17 @@ fn bench_program_alu(bencher: &mut Bencher) {
let elf = load_program_from_file("bench_alu");
with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001);
let loader = create_loader(
let program_runtime_environment = create_program_runtime_environment(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
false,
);
let executable = Executable::<TautologyVerifier, InvokeContext>::from_elf(
&elf,
Arc::new(program_runtime_environment.unwrap()),
)
.unwrap();
let executable =
Executable::<TautologyVerifier, InvokeContext>::from_elf(&elf, loader).unwrap();
let mut verified_executable =
Executable::<RequisiteVerifier, InvokeContext>::verified(executable).unwrap();
@ -231,15 +237,17 @@ fn bench_create_vm(bencher: &mut Bencher) {
let direct_mapping = invoke_context
.feature_set
.is_active(&bpf_account_data_direct_mapping::id());
let loader = create_loader(
let program_runtime_environment = create_program_runtime_environment(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
false,
);
let executable = Executable::<TautologyVerifier, InvokeContext>::from_elf(
&elf,
Arc::new(program_runtime_environment.unwrap()),
)
.unwrap();
let executable =
Executable::<TautologyVerifier, InvokeContext>::from_elf(&elf, loader).unwrap();
let verified_executable =
Executable::<RequisiteVerifier, InvokeContext>::verified(executable).unwrap();
@ -291,15 +299,17 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
)
.unwrap();
let loader = create_loader(
let program_runtime_environment = create_program_runtime_environment(
&invoke_context.feature_set,
&ComputeBudget::default(),
true,
false,
);
let executable = Executable::<TautologyVerifier, InvokeContext>::from_elf(
&elf,
Arc::new(program_runtime_environment.unwrap()),
)
.unwrap();
let executable =
Executable::<TautologyVerifier, InvokeContext>::from_elf(&elf, loader).unwrap();
let verified_executable =
Executable::<RequisiteVerifier, InvokeContext>::verified(executable).unwrap();

View File

@ -89,6 +89,7 @@ use {
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
ThreadPool, ThreadPoolBuilder,
},
solana_bpf_loader_program::syscalls::create_program_runtime_environment,
solana_measure::{measure, measure::Measure, measure_us},
solana_perf::perf_libs,
solana_program_runtime::{
@ -4117,11 +4118,7 @@ impl Bank {
}
}
pub fn load_program(
&self,
pubkey: &Pubkey,
debugging_features: bool,
) -> Result<Arc<LoadedProgram>> {
pub fn load_program(&self, pubkey: &Pubkey) -> Result<Arc<LoadedProgram>> {
let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) {
program
} else {
@ -4174,13 +4171,18 @@ impl Bank {
} else {
None
};
let program_runtime_environment_v1 = self
.loaded_programs_cache
.read()
.unwrap()
.program_runtime_environment_v1
.clone();
solana_bpf_loader_program::load_program_from_account(
&self.feature_set,
&self.runtime_config.compute_budget.unwrap_or_default(),
None, // log_collector
&program,
programdata.as_ref().unwrap_or(&program),
debugging_features,
program_runtime_environment_v1,
)
.map(|(loaded_program, metrics)| {
let mut timings = ExecuteDetailsTimings::default();
@ -4428,7 +4430,7 @@ impl Bank {
let missing_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = missing_programs
.iter()
.map(|key| {
let program = self.load_program(key, false).unwrap_or_else(|err| {
let program = self.load_program(key).unwrap_or_else(|err| {
// Create a tombstone for the program in the cache
debug!("Failed to load program {}, error {:?}", key, err);
Arc::new(LoadedProgram::new_tombstone(
@ -4525,9 +4527,6 @@ impl Bank {
&program_owners_refs,
&self.blockhash_queue.read().unwrap(),
);
for precompile in get_precompiles() {
program_accounts_map.remove(&precompile.program_id);
}
let native_loader = native_loader::id();
for builtin_program in self.builtin_programs.iter() {
program_accounts_map.insert(*builtin_program, &native_loader);
@ -6265,7 +6264,23 @@ impl Bank {
self.rewards_pool_pubkeys =
Arc::new(genesis_config.rewards_pools.keys().cloned().collect());
self.apply_feature_activations(
ApplyFeatureActivationsCaller::FinishInit,
debug_do_not_add_builtins,
);
if !debug_do_not_add_builtins {
let program_runtime_environment_v1 = create_program_runtime_environment(
&self.feature_set,
&self.runtime_config.compute_budget.unwrap_or_default(),
false, /* deployment */
false, /* debugging_features */
)
.unwrap();
self.loaded_programs_cache
.write()
.unwrap()
.program_runtime_environment_v1 = Arc::new(program_runtime_environment_v1);
for builtin in BUILTINS
.iter()
.chain(additional_builtins.unwrap_or(&[]).iter())
@ -6285,11 +6300,6 @@ impl Bank {
}
}
self.apply_feature_activations(
ApplyFeatureActivationsCaller::FinishInit,
debug_do_not_add_builtins,
);
if self
.feature_set
.is_active(&feature_set::cap_accounts_data_len::id())

View File

@ -7397,7 +7397,7 @@ fn test_bank_load_program() {
programdata_account.set_rent_epoch(1);
bank.store_account_and_update_capitalization(&key1, &program_account);
bank.store_account_and_update_capitalization(&programdata_key, &programdata_account);
let program = bank.load_program(&key1, false);
let program = bank.load_program(&key1);
assert!(program.is_ok());
let program = program.unwrap();
assert!(matches!(program.program, LoadedProgramType::LegacyV1(_)));
@ -7555,7 +7555,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
}
let loaded_program = bank
.load_program(&program_keypair.pubkey(), false)
.load_program(&program_keypair.pubkey())
.expect("Failed to load the program");
// Invoke deployed program