From a9ad0f2b5a53a4f01e41644ae066b3b88d7b4b8f Mon Sep 17 00:00:00 2001 From: Pankaj Garg Date: Wed, 15 Feb 2023 09:05:26 -0800 Subject: [PATCH] Code to load a program from its account (#30282) --- program-runtime/src/loaded_programs.rs | 6 ++ programs/bpf_loader/src/lib.rs | 121 +++++++++++++++++++------ 2 files changed, 97 insertions(+), 30 deletions(-) diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index d6a992145d..957f224e9b 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -70,6 +70,8 @@ impl Debug for LoadedProgramType { pub struct LoadedProgram { /// The program of this entry pub program: LoadedProgramType, + /// Size of account that stores the program and program data + pub account_size: usize, /// Slot in which the program was (re)deployed pub deployment_slot: Slot, /// Slot in which this entry will become active (can be in the future) @@ -85,6 +87,7 @@ impl LoadedProgram { loader: Arc>>, deployment_slot: Slot, elf_bytes: &[u8], + account_size: usize, ) -> Result { let program = if bpf_loader_deprecated::check_id(loader_key) { let executable = Executable::load(elf_bytes, loader.clone())?; @@ -97,6 +100,7 @@ impl LoadedProgram { }; Ok(Self { deployment_slot, + account_size, effective_slot: deployment_slot.saturating_add(1), usage_counter: AtomicU64::new(0), program, @@ -110,6 +114,7 @@ impl LoadedProgram { ) -> Self { Self { deployment_slot, + account_size: 0, effective_slot: deployment_slot.saturating_add(1), usage_counter: AtomicU64::new(0), program: LoadedProgramType::BuiltIn(program), @@ -375,6 +380,7 @@ mod tests { fn new_test_loaded_program(deployment_slot: Slot, effective_slot: Slot) -> Arc { Arc::new(LoadedProgram { program: LoadedProgramType::Invalid, + account_size: 0, deployment_slot, effective_slot, usage_counter: AtomicU64::default(), diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 462961c536..70a4849907 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -22,6 +22,7 @@ use { executor_cache::TransactionExecutorCache, ic_logger_msg, ic_msg, invoke_context::InvokeContext, + loaded_programs::LoadedProgram, log_collector::LogCollector, stable_log, sysvar_cache::get_sysvar_with_account_check, @@ -38,6 +39,7 @@ use { solana_sdk::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, + clock::Slot, entrypoint::{HEAP_LENGTH, SUCCESS}, feature_set::{ cap_accounts_data_allocations_per_transaction, cap_bpf_program_instruction_accounts, @@ -180,6 +182,93 @@ fn create_executor_from_bytes( })) } +fn get_programdata_offset_and_depoyment_offset( + log_collector: &Option>>, + 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>>, + program: &BorrowedAccount, + programdata: &BorrowedAccount, +) -> Result<(LoadedProgram, Option), InstructionError> { + if !check_loader_id(program.get_owner()) { + ic_logger_msg!( + log_collector, + "Executable account not owned by the BPF loader" + ); + return Err(InstructionError::IncorrectProgramId); + } + + let (programdata_offset, deployment_slot) = + get_programdata_offset_and_depoyment_offset(&log_collector, program, programdata)?; + let programdata_size = if programdata_offset != 0 { + programdata.get_data().len() + } else { + 0 + }; + + let mut create_executor_metrics = CreateMetrics { + program_id: program.get_key().to_string(), + ..CreateMetrics::default() + }; + + let mut register_syscalls_time = Measure::start("register_syscalls_time"); + let loader = syscalls::create_loader(feature_set, compute_budget, false, false, false) + .map_err(|e| { + ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e); + InstructionError::ProgramEnvironmentSetupFailure + })?; + register_syscalls_time.stop(); + create_executor_metrics.register_syscalls_us = register_syscalls_time.as_us(); + + let mut load_elf_time = Measure::start("load_elf_time"); + let loaded_program = LoadedProgram::new( + program.get_owner(), + loader, + deployment_slot, + programdata + .get_data() + .get(programdata_offset..) + .ok_or(InstructionError::AccountDataTooSmall)?, + program.get_data().len().saturating_add(programdata_size), + ) + .map_err(|err| { + ic_logger_msg!(log_collector, "{}", err); + InstructionError::InvalidAccountData + })?; + load_elf_time.stop(); + create_executor_metrics.load_elf_us = load_elf_time.as_us(); + + Ok((loaded_program, Some(create_executor_metrics))) +} + pub fn create_executor_from_account( feature_set: &FeatureSet, compute_budget: &ComputeBudget, @@ -197,36 +286,8 @@ pub fn create_executor_from_account( return Err(InstructionError::IncorrectProgramId); } - let programdata_offset = if bpf_loader_upgradeable::check_id(program.get_owner()) { - if let UpgradeableLoaderState::Program { - programdata_address, - } = program.get_state()? - { - if &programdata_address != programdata.get_key() { - ic_logger_msg!( - log_collector, - "Wrong ProgramData account for this Program account" - ); - return Err(InstructionError::InvalidArgument); - } - if !matches!( - programdata.get_state()?, - UpgradeableLoaderState::ProgramData { - slot: _, - upgrade_authority_address: _, - } - ) { - ic_logger_msg!(log_collector, "Program has been closed"); - return Err(InstructionError::InvalidAccountData); - } - UpgradeableLoaderState::size_of_programdata_metadata() - } else { - ic_logger_msg!(log_collector, "Invalid Program account"); - return Err(InstructionError::InvalidAccountData); - } - } else { - 0 - }; + let (programdata_offset, _) = + get_programdata_offset_and_depoyment_offset(&log_collector, program, programdata)?; if let Some(ref tx_executor_cache) = tx_executor_cache { match tx_executor_cache.get(program.get_key()) {