From 07d7c938b9b9ebf8a17b7226b46222faa66dacef Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 16 Jul 2022 18:33:57 +0100 Subject: [PATCH] Refactor: split up syscalls module (#26637) * Refactor: split up syscalls module * fix ci script * fix visibility --- programs/bpf_loader/gen-syscall-list/build.rs | 2 +- programs/bpf_loader/src/syscalls/cpi.rs | 927 ++++++++++ programs/bpf_loader/src/syscalls/logging.rs | 222 +++ programs/bpf_loader/src/syscalls/mem_ops.rs | 240 +++ .../src/{syscalls.rs => syscalls/mod.rs} | 1547 +---------------- programs/bpf_loader/src/syscalls/sysvar.rs | 141 ++ 6 files changed, 1549 insertions(+), 1530 deletions(-) create mode 100644 programs/bpf_loader/src/syscalls/cpi.rs create mode 100644 programs/bpf_loader/src/syscalls/logging.rs create mode 100644 programs/bpf_loader/src/syscalls/mem_ops.rs rename programs/bpf_loader/src/{syscalls.rs => syscalls/mod.rs} (68%) create mode 100644 programs/bpf_loader/src/syscalls/sysvar.rs diff --git a/programs/bpf_loader/gen-syscall-list/build.rs b/programs/bpf_loader/gen-syscall-list/build.rs index 5a22239ce0..bbc63518f0 100644 --- a/programs/bpf_loader/gen-syscall-list/build.rs +++ b/programs/bpf_loader/gen-syscall-list/build.rs @@ -14,7 +14,7 @@ use { * to verify undefined symbols in a .so module that cargo-build-bpf has built. */ fn main() { - let syscalls_rs_path = PathBuf::from("../src/syscalls.rs"); + let syscalls_rs_path = PathBuf::from("../src/syscalls/mod.rs"); let syscalls_txt_path = PathBuf::from("../../../sdk/bpf/syscalls.txt"); println!( "cargo:warning=(not a warning) Generating {1} from {0}", diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs new file mode 100644 index 0000000000..b607a1c907 --- /dev/null +++ b/programs/bpf_loader/src/syscalls/cpi.rs @@ -0,0 +1,927 @@ +use {super::*, crate::declare_syscall}; + +struct CallerAccount<'a> { + lamports: &'a mut u64, + owner: &'a mut Pubkey, + original_data_len: usize, + data: &'a mut [u8], + vm_data_addr: u64, + ref_to_len_in_vm: &'a mut u64, + serialized_len_ptr: &'a mut u64, + executable: bool, + rent_epoch: u64, +} +type TranslatedAccounts<'a> = Vec<(usize, Option>)>; + +/// Implemented by language specific data structure translators +trait SyscallInvokeSigned<'a, 'b> { + fn get_context_mut(&self) -> Result>, EbpfError>; + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result>; + fn translate_accounts<'c>( + &'c self, + instruction_accounts: &[InstructionAccount], + program_indices: &[usize], + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result, EbpfError>; + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result, EbpfError>; +} + +declare_syscall!( + /// Cross-program invocation called from Rust + SyscallInvokeSignedRust, + fn call( + &mut self, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + *result = call( + self, + instruction_addr, + account_infos_addr, + account_infos_len, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + ); + } +); + +impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { + fn get_context_mut(&self) -> Result>, EbpfError> { + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result> { + let ix = translate_type::( + memory_mapping, + addr, + invoke_context.get_check_aligned(), + )?; + + check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?; + + let accounts = translate_slice::( + memory_mapping, + ix.accounts.as_ptr() as u64, + ix.accounts.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )? + .to_vec(); + let data = translate_slice::( + memory_mapping, + ix.data.as_ptr() as u64, + ix.data.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )? + .to_vec(); + Ok(Instruction { + program_id: ix.program_id, + accounts, + data, + }) + } + + fn translate_accounts<'c>( + &'c self, + instruction_accounts: &[InstructionAccount], + program_indices: &[usize], + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result, EbpfError> { + let account_infos = translate_slice::( + memory_mapping, + account_infos_addr, + account_infos_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + check_account_infos(account_infos.len(), invoke_context)?; + let account_info_keys = account_infos + .iter() + .map(|account_info| { + translate_type::( + memory_mapping, + account_info.key as *const _ as u64, + invoke_context.get_check_aligned(), + ) + }) + .collect::, EbpfError>>()?; + + let translate = |account_info: &AccountInfo, invoke_context: &InvokeContext| { + // Translate the account from user space + + let lamports = { + // Double translate lamports out of RefCell + let ptr = translate_type::( + memory_mapping, + account_info.lamports.as_ptr() as u64, + invoke_context.get_check_aligned(), + )?; + translate_type_mut::(memory_mapping, *ptr, invoke_context.get_check_aligned())? + }; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner as *const _ as u64, + invoke_context.get_check_aligned(), + )?; + + let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { + // Double translate data out of RefCell + let data = *translate_type::<&[u8]>( + memory_mapping, + account_info.data.as_ptr() as *const _ as u64, + invoke_context.get_check_aligned(), + )?; + + invoke_context.get_compute_meter().consume( + (data.len() as u64) + .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), + )?; + + let translated = translate( + memory_mapping, + AccessType::Store, + (account_info.data.as_ptr() as *const u64 as u64) + .saturating_add(size_of::() as u64), + 8, + )? as *mut u64; + let ref_to_len_in_vm = unsafe { &mut *translated }; + let ref_of_len_in_input_buffer = + (data.as_ptr() as *const _ as u64).saturating_sub(8); + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer, + invoke_context.get_check_aligned(), + )?; + let vm_data_addr = data.as_ptr() as u64; + ( + translate_slice_mut::( + memory_mapping, + vm_data_addr, + data.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + ) + }; + + Ok(CallerAccount { + lamports, + owner, + original_data_len: 0, // set later + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + executable: account_info.executable, + rent_epoch: account_info.rent_epoch, + }) + }; + + get_translated_accounts( + instruction_accounts, + program_indices, + &account_info_keys, + account_infos, + invoke_context, + translate, + ) + } + + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result, EbpfError> { + let mut signers = Vec::new(); + if signers_seeds_len > 0 { + let signers_seeds = translate_slice::<&[&[u8]]>( + memory_mapping, + signers_seeds_addr, + signers_seeds_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } + for signer_seeds in signers_seeds.iter() { + let untranslated_seeds = translate_slice::<&[u8]>( + memory_mapping, + signer_seeds.as_ptr() as *const _ as u64, + signer_seeds.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + if untranslated_seeds.len() > MAX_SEEDS { + return Err(SyscallError::InstructionError( + InstructionError::MaxSeedLengthExceeded, + ) + .into()); + } + let seeds = untranslated_seeds + .iter() + .map(|untranslated_seed| { + translate_slice::( + memory_mapping, + untranslated_seed.as_ptr() as *const _ as u64, + untranslated_seed.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ) + }) + .collect::, EbpfError>>()?; + let signer = Pubkey::create_program_address(&seeds, program_id) + .map_err(SyscallError::BadSeeds)?; + signers.push(signer); + } + Ok(signers) + } else { + Ok(vec![]) + } + } +} + +/// Rust representation of C's SolInstruction +#[derive(Debug)] +#[repr(C)] +struct SolInstruction { + program_id_addr: u64, + accounts_addr: u64, + accounts_len: u64, + data_addr: u64, + data_len: u64, +} + +/// Rust representation of C's SolAccountMeta +#[derive(Debug)] +#[repr(C)] +struct SolAccountMeta { + pubkey_addr: u64, + is_writable: bool, + is_signer: bool, +} + +/// Rust representation of C's SolAccountInfo +#[derive(Debug)] +#[repr(C)] +struct SolAccountInfo { + key_addr: u64, + lamports_addr: u64, + data_len: u64, + data_addr: u64, + owner_addr: u64, + rent_epoch: u64, + #[allow(dead_code)] + is_signer: bool, + #[allow(dead_code)] + is_writable: bool, + executable: bool, +} + +/// Rust representation of C's SolSignerSeed +#[derive(Debug)] +#[repr(C)] +struct SolSignerSeedC { + addr: u64, + len: u64, +} + +/// Rust representation of C's SolSignerSeeds +#[derive(Debug)] +#[repr(C)] +struct SolSignerSeedsC { + addr: u64, + len: u64, +} + +declare_syscall!( + /// Cross-program invocation called from C + SyscallInvokeSignedC, + fn call( + &mut self, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + *result = call( + self, + instruction_addr, + account_infos_addr, + account_infos_len, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + ); + } +); + +impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { + fn get_context_mut(&self) -> Result>, EbpfError> { + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } + + fn translate_instruction( + &self, + addr: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result> { + let ix_c = translate_type::( + memory_mapping, + addr, + invoke_context.get_check_aligned(), + )?; + + check_instruction_size( + ix_c.accounts_len as usize, + ix_c.data_len as usize, + invoke_context, + )?; + let program_id = translate_type::( + memory_mapping, + ix_c.program_id_addr, + invoke_context.get_check_aligned(), + )?; + let meta_cs = translate_slice::( + memory_mapping, + ix_c.accounts_addr, + ix_c.accounts_len as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + let data = translate_slice::( + memory_mapping, + ix_c.data_addr, + ix_c.data_len as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )? + .to_vec(); + let accounts = meta_cs + .iter() + .map(|meta_c| { + let pubkey = translate_type::( + memory_mapping, + meta_c.pubkey_addr, + invoke_context.get_check_aligned(), + )?; + Ok(AccountMeta { + pubkey: *pubkey, + is_signer: meta_c.is_signer, + is_writable: meta_c.is_writable, + }) + }) + .collect::, EbpfError>>()?; + + Ok(Instruction { + program_id: *program_id, + accounts, + data, + }) + } + + fn translate_accounts<'c>( + &'c self, + instruction_accounts: &[InstructionAccount], + program_indices: &[usize], + account_infos_addr: u64, + account_infos_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, + ) -> Result, EbpfError> { + let account_infos = translate_slice::( + memory_mapping, + account_infos_addr, + account_infos_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + check_account_infos(account_infos.len(), invoke_context)?; + let account_info_keys = account_infos + .iter() + .map(|account_info| { + translate_type::( + memory_mapping, + account_info.key_addr, + invoke_context.get_check_aligned(), + ) + }) + .collect::, EbpfError>>()?; + + let translate = |account_info: &SolAccountInfo, invoke_context: &InvokeContext| { + // Translate the account from user space + + let lamports = translate_type_mut::( + memory_mapping, + account_info.lamports_addr, + invoke_context.get_check_aligned(), + )?; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner_addr, + invoke_context.get_check_aligned(), + )?; + let vm_data_addr = account_info.data_addr; + + invoke_context.get_compute_meter().consume( + account_info + .data_len + .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), + )?; + + let data = translate_slice_mut::( + memory_mapping, + vm_data_addr, + account_info.data_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + + let first_info_addr = account_infos.first().ok_or(SyscallError::InstructionError( + InstructionError::InvalidArgument, + ))? as *const _ as u64; + let addr = &account_info.data_len as *const u64 as u64; + let vm_addr = if invoke_context + .feature_set + .is_active(&syscall_saturated_math::id()) + { + account_infos_addr.saturating_add(addr.saturating_sub(first_info_addr)) + } else { + #[allow(clippy::integer_arithmetic)] + { + account_infos_addr + (addr - first_info_addr) + } + }; + let _ = translate( + memory_mapping, + AccessType::Store, + vm_addr, + size_of::() as u64, + )?; + let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) }; + + let ref_of_len_in_input_buffer = + (account_info.data_addr as *mut u8 as u64).saturating_sub(8); + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer, + invoke_context.get_check_aligned(), + )?; + + Ok(CallerAccount { + lamports, + owner, + original_data_len: 0, // set later + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + executable: account_info.executable, + rent_epoch: account_info.rent_epoch, + }) + }; + + get_translated_accounts( + instruction_accounts, + program_indices, + &account_info_keys, + account_infos, + invoke_context, + translate, + ) + } + + fn translate_signers( + &self, + program_id: &Pubkey, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result, EbpfError> { + if signers_seeds_len > 0 { + let signers_seeds = translate_slice::( + memory_mapping, + signers_seeds_addr, + signers_seeds_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } + Ok(signers_seeds + .iter() + .map(|signer_seeds| { + let seeds = translate_slice::( + memory_mapping, + signer_seeds.addr, + signer_seeds.len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + )?; + if seeds.len() > MAX_SEEDS { + return Err(SyscallError::InstructionError( + InstructionError::MaxSeedLengthExceeded, + ) + .into()); + } + let seeds_bytes = seeds + .iter() + .map(|seed| { + translate_slice::( + memory_mapping, + seed.addr, + seed.len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ) + }) + .collect::, EbpfError>>()?; + Pubkey::create_program_address(&seeds_bytes, program_id) + .map_err(|err| SyscallError::BadSeeds(err).into()) + }) + .collect::, EbpfError>>()?) + } else { + Ok(vec![]) + } + } +} + +fn get_translated_accounts<'a, T, F>( + instruction_accounts: &[InstructionAccount], + program_indices: &[usize], + account_info_keys: &[&Pubkey], + account_infos: &[T], + invoke_context: &mut InvokeContext, + do_translate: F, +) -> Result, EbpfError> +where + F: Fn(&T, &InvokeContext) -> Result, EbpfError>, +{ + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context + .get_current_instruction_context() + .map_err(SyscallError::InstructionError)?; + let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1)); + + let program_account_index = program_indices + .last() + .ok_or(SyscallError::InstructionError( + InstructionError::MissingAccount, + ))?; + accounts.push((*program_account_index, None)); + + for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate() + { + if instruction_account_index != instruction_account.index_in_callee { + continue; // Skip duplicate account + } + let mut callee_account = instruction_context + .try_borrow_instruction_account( + transaction_context, + instruction_account.index_in_caller, + ) + .map_err(SyscallError::InstructionError)?; + let account_key = invoke_context + .transaction_context + .get_key_of_account_at_index(instruction_account.index_in_transaction) + .map_err(SyscallError::InstructionError)?; + if callee_account.is_executable() { + // Use the known account + invoke_context.get_compute_meter().consume( + (callee_account.get_data().len() as u64) + .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), + )?; + + accounts.push((instruction_account.index_in_caller, None)); + } else if let Some(caller_account_index) = + account_info_keys.iter().position(|key| *key == account_key) + { + let mut caller_account = do_translate( + account_infos + .get(caller_account_index) + .ok_or(SyscallError::InvalidLength)?, + invoke_context, + )?; + { + if callee_account.get_lamports() != *caller_account.lamports { + callee_account + .set_lamports(*caller_account.lamports) + .map_err(SyscallError::InstructionError)?; + } + // The redundant check helps to avoid the expensive data comparison if we can + match callee_account + .can_data_be_resized(caller_account.data.len()) + .and_then(|_| callee_account.can_data_be_changed()) + { + Ok(()) => callee_account + .set_data(caller_account.data) + .map_err(SyscallError::InstructionError)?, + Err(err) if callee_account.get_data() != caller_account.data => { + return Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(err), + ))); + } + _ => {} + } + if callee_account.is_executable() != caller_account.executable { + callee_account + .set_executable(caller_account.executable) + .map_err(SyscallError::InstructionError)?; + } + // Change the owner at the end so that we are allowed to change the lamports and data before + if callee_account.get_owner() != caller_account.owner { + callee_account + .set_owner(caller_account.owner.as_ref()) + .map_err(SyscallError::InstructionError)?; + } + drop(callee_account); + let callee_account = invoke_context + .transaction_context + .get_account_at_index(instruction_account.index_in_transaction) + .map_err(SyscallError::InstructionError)?; + if callee_account.borrow().rent_epoch() != caller_account.rent_epoch { + if invoke_context + .feature_set + .is_active(&enable_early_verification_of_account_modifications::id()) + { + Err(SyscallError::InstructionError( + InstructionError::RentEpochModified, + ))?; + } else { + callee_account + .borrow_mut() + .set_rent_epoch(caller_account.rent_epoch); + } + } + } + let caller_account = if instruction_account.is_writable { + let orig_data_lens = invoke_context + .get_orig_account_lengths() + .map_err(SyscallError::InstructionError)?; + caller_account.original_data_len = *orig_data_lens + .get(instruction_account.index_in_caller) + .ok_or_else(|| { + ic_msg!( + invoke_context, + "Internal error: index mismatch for account {}", + account_key + ); + SyscallError::InstructionError(InstructionError::MissingAccount) + })?; + Some(caller_account) + } else { + None + }; + accounts.push((instruction_account.index_in_caller, caller_account)); + } else { + ic_msg!( + invoke_context, + "Instruction references an unknown account {}", + account_key + ); + return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); + } + } + + Ok(accounts) +} + +fn check_instruction_size( + num_accounts: usize, + data_len: usize, + invoke_context: &mut InvokeContext, +) -> Result<(), EbpfError> { + let size = num_accounts + .saturating_mul(size_of::()) + .saturating_add(data_len); + let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; + if size > max_size { + return Err(SyscallError::InstructionTooLarge(size, max_size).into()); + } + Ok(()) +} + +fn check_account_infos( + len: usize, + invoke_context: &mut InvokeContext, +) -> Result<(), EbpfError> { + let adjusted_len = if invoke_context + .feature_set + .is_active(&syscall_saturated_math::id()) + { + len.saturating_mul(size_of::()) + } else { + #[allow(clippy::integer_arithmetic)] + { + len * size_of::() + } + }; + if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size { + // Cap the number of account_infos a caller can pass to approximate + // maximum that accounts that could be passed in an instruction + return Err(SyscallError::TooManyAccounts.into()); + }; + Ok(()) +} + +fn check_authorized_program( + program_id: &Pubkey, + instruction_data: &[u8], + invoke_context: &InvokeContext, +) -> Result<(), EbpfError> { + #[allow(clippy::blocks_in_if_conditions)] + if native_loader::check_id(program_id) + || bpf_loader::check_id(program_id) + || bpf_loader_deprecated::check_id(program_id) + || (bpf_loader_upgradeable::check_id(program_id) + && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data) + || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data) + || bpf_loader_upgradeable::is_close_instruction(instruction_data))) + || (invoke_context + .feature_set + .is_active(&prevent_calling_precompiles_as_programs::id()) + && is_precompile(program_id, |feature_id: &Pubkey| { + invoke_context.feature_set.is_active(feature_id) + })) + { + return Err(SyscallError::ProgramNotSupported(*program_id).into()); + } + Ok(()) +} + +/// Call process instruction, common to both Rust and C +fn call<'a, 'b: 'a>( + syscall: &mut dyn SyscallInvokeSigned<'a, 'b>, + instruction_addr: u64, + account_infos_addr: u64, + account_infos_len: u64, + signers_seeds_addr: u64, + signers_seeds_len: u64, + memory_mapping: &mut MemoryMapping, +) -> Result> { + let mut invoke_context = syscall.get_context_mut()?; + invoke_context + .get_compute_meter() + .consume(invoke_context.get_compute_budget().invoke_units)?; + + // Translate and verify caller's data + let instruction = + syscall.translate_instruction(instruction_addr, memory_mapping, *invoke_context)?; + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context + .get_current_instruction_context() + .map_err(SyscallError::InstructionError)?; + let caller_program_id = instruction_context + .get_last_program_key(transaction_context) + .map_err(SyscallError::InstructionError)?; + let signers = syscall.translate_signers( + caller_program_id, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + *invoke_context, + )?; + let (instruction_accounts, program_indices) = invoke_context + .prepare_instruction(&instruction, &signers) + .map_err(SyscallError::InstructionError)?; + check_authorized_program(&instruction.program_id, &instruction.data, *invoke_context)?; + let mut accounts = syscall.translate_accounts( + &instruction_accounts, + &program_indices, + account_infos_addr, + account_infos_len, + memory_mapping, + *invoke_context, + )?; + + // Process instruction + let mut compute_units_consumed = 0; + invoke_context + .process_instruction( + &instruction.data, + &instruction_accounts, + &program_indices, + &mut compute_units_consumed, + &mut ExecuteTimings::default(), + ) + .map_err(SyscallError::InstructionError)?; + + // Copy results back to caller + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context + .get_current_instruction_context() + .map_err(SyscallError::InstructionError)?; + for (index_in_caller, caller_account) in accounts.iter_mut() { + if let Some(caller_account) = caller_account { + let callee_account = instruction_context + .try_borrow_instruction_account(transaction_context, *index_in_caller) + .map_err(SyscallError::InstructionError)?; + *caller_account.lamports = callee_account.get_lamports(); + *caller_account.owner = *callee_account.get_owner(); + let new_len = callee_account.get_data().len(); + if caller_account.data.len() != new_len { + let data_overflow = if invoke_context + .feature_set + .is_active(&syscall_saturated_math::id()) + { + new_len + > caller_account + .original_data_len + .saturating_add(MAX_PERMITTED_DATA_INCREASE) + } else { + #[allow(clippy::integer_arithmetic)] + { + new_len > caller_account.original_data_len + MAX_PERMITTED_DATA_INCREASE + } + }; + if data_overflow { + ic_msg!( + invoke_context, + "Account data size realloc limited to {} in inner instructions", + MAX_PERMITTED_DATA_INCREASE + ); + return Err( + SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), + ); + } + if new_len < caller_account.data.len() { + caller_account + .data + .get_mut(new_len..) + .ok_or(SyscallError::InstructionError( + InstructionError::AccountDataTooSmall, + ))? + .fill(0); + } + caller_account.data = translate_slice_mut::( + memory_mapping, + caller_account.vm_data_addr, + new_len as u64, + false, // Don't care since it is byte aligned + invoke_context.get_check_size(), + )?; + *caller_account.ref_to_len_in_vm = new_len as u64; + *caller_account.serialized_len_ptr = new_len as u64; + } + let to_slice = &mut caller_account.data; + let from_slice = callee_account + .get_data() + .get(0..new_len) + .ok_or(SyscallError::InvalidLength)?; + if to_slice.len() != from_slice.len() { + return Err( + SyscallError::InstructionError(InstructionError::AccountDataTooSmall).into(), + ); + } + to_slice.copy_from_slice(from_slice); + } + } + + Ok(SUCCESS) +} diff --git a/programs/bpf_loader/src/syscalls/logging.rs b/programs/bpf_loader/src/syscalls/logging.rs new file mode 100644 index 0000000000..ebb40967ee --- /dev/null +++ b/programs/bpf_loader/src/syscalls/logging.rs @@ -0,0 +1,222 @@ +use {super::*, crate::declare_syscall}; + +declare_syscall!( + /// Log a user's info message + SyscallLog, + fn call( + &mut self, + addr: u64, + len: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + let cost = invoke_context + .get_compute_budget() + .syscall_base_cost + .max(len); + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + question_mark!( + translate_string_and_do( + memory_mapping, + addr, + len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + &mut |string: &str| { + stable_log::program_log(&invoke_context.get_log_collector(), string); + Ok(0) + } + ), + result + ); + *result = Ok(0); + } +); + +declare_syscall!( + /// Log 5 64-bit values + SyscallLogU64, + fn call( + &mut self, + arg1: u64, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, + _memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + let cost = invoke_context.get_compute_budget().log_64_units; + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + stable_log::program_log( + &invoke_context.get_log_collector(), + &format!( + "{:#x}, {:#x}, {:#x}, {:#x}, {:#x}", + arg1, arg2, arg3, arg4, arg5 + ), + ); + *result = Ok(0); + } +); + +declare_syscall!( + /// Log current compute consumption + SyscallLogBpfComputeUnits, + fn call( + &mut self, + _arg1: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + _memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + let cost = invoke_context.get_compute_budget().syscall_base_cost; + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + ic_logger_msg!( + invoke_context.get_log_collector(), + "Program consumption: {} units remaining", + invoke_context.get_compute_meter().borrow().get_remaining() + ); + *result = Ok(0); + } +); + +declare_syscall!( + /// Log 5 64-bit values + SyscallLogPubkey, + fn call( + &mut self, + pubkey_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + let cost = invoke_context.get_compute_budget().log_pubkey_units; + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + let pubkey = question_mark!( + translate_type::( + memory_mapping, + pubkey_addr, + invoke_context.get_check_aligned() + ), + result + ); + stable_log::program_log(&invoke_context.get_log_collector(), &pubkey.to_string()); + *result = Ok(0); + } +); + +declare_syscall!( + /// Log data handling + SyscallLogData, + fn call( + &mut self, + addr: u64, + len: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + let budget = invoke_context.get_compute_budget(); + + question_mark!( + invoke_context + .get_compute_meter() + .consume(budget.syscall_base_cost), + result + ); + + let untranslated_fields = question_mark!( + translate_slice::<&[u8]>( + memory_mapping, + addr, + len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + + question_mark!( + invoke_context.get_compute_meter().consume( + budget + .syscall_base_cost + .saturating_mul(untranslated_fields.len() as u64) + ), + result + ); + question_mark!( + invoke_context.get_compute_meter().consume( + untranslated_fields + .iter() + .fold(0, |total, e| total.saturating_add(e.len() as u64)) + ), + result + ); + + let mut fields = Vec::with_capacity(untranslated_fields.len()); + + for untranslated_field in untranslated_fields { + fields.push(question_mark!( + translate_slice::( + memory_mapping, + untranslated_field.as_ptr() as *const _ as u64, + untranslated_field.len() as u64, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + )); + } + + let log_collector = invoke_context.get_log_collector(); + + stable_log::program_data(&log_collector, &fields); + + *result = Ok(0); + } +); diff --git a/programs/bpf_loader/src/syscalls/mem_ops.rs b/programs/bpf_loader/src/syscalls/mem_ops.rs new file mode 100644 index 0000000000..2fb5cd064a --- /dev/null +++ b/programs/bpf_loader/src/syscalls/mem_ops.rs @@ -0,0 +1,240 @@ +use {super::*, crate::declare_syscall}; + +fn mem_op_consume<'a, 'b>( + invoke_context: &Ref<&'a mut InvokeContext<'b>>, + n: u64, +) -> Result<(), EbpfError> { + let compute_budget = invoke_context.get_compute_budget(); + let cost = compute_budget + .mem_op_base_cost + .max(n.saturating_div(compute_budget.cpi_bytes_per_unit)); + invoke_context.get_compute_meter().consume(cost) +} + +declare_syscall!( + /// memcpy + SyscallMemcpy, + fn call( + &mut self, + dst_addr: u64, + src_addr: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + question_mark!(mem_op_consume(&invoke_context, n), result); + + let do_check_physical_overlapping = invoke_context + .feature_set + .is_active(&check_physical_overlapping::id()); + + if !is_nonoverlapping(src_addr, dst_addr, n) { + *result = Err(SyscallError::CopyOverlapping.into()); + return; + } + + let dst_ptr = question_mark!( + translate_slice_mut::( + memory_mapping, + dst_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size() + ), + result + ) + .as_mut_ptr(); + let src_ptr = question_mark!( + translate_slice::( + memory_mapping, + src_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size() + ), + result + ) + .as_ptr(); + if do_check_physical_overlapping + && !is_nonoverlapping(src_ptr as usize, dst_ptr as usize, n as usize) + { + unsafe { + std::ptr::copy(src_ptr, dst_ptr, n as usize); + } + } else { + unsafe { + std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, n as usize); + } + } + *result = Ok(0); + } +); + +declare_syscall!( + /// memmove + SyscallMemmove, + fn call( + &mut self, + dst_addr: u64, + src_addr: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + question_mark!(mem_op_consume(&invoke_context, n), result); + + let dst = question_mark!( + translate_slice_mut::( + memory_mapping, + dst_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size() + ), + result + ); + let src = question_mark!( + translate_slice::( + memory_mapping, + src_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size() + ), + result + ); + unsafe { + std::ptr::copy(src.as_ptr(), dst.as_mut_ptr(), n as usize); + } + *result = Ok(0); + } +); + +declare_syscall!( + /// memcmp + SyscallMemcmp, + fn call( + &mut self, + s1_addr: u64, + s2_addr: u64, + n: u64, + cmp_result_addr: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + question_mark!(mem_op_consume(&invoke_context, n), result); + + let s1 = question_mark!( + translate_slice::( + memory_mapping, + s1_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + let s2 = question_mark!( + translate_slice::( + memory_mapping, + s2_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + let cmp_result = question_mark!( + translate_type_mut::( + memory_mapping, + cmp_result_addr, + invoke_context.get_check_aligned() + ), + result + ); + let mut i = 0; + while i < n as usize { + let a = *question_mark!(s1.get(i).ok_or(SyscallError::InvalidLength,), result); + let b = *question_mark!(s2.get(i).ok_or(SyscallError::InvalidLength,), result); + if a != b { + *cmp_result = if invoke_context + .feature_set + .is_active(&syscall_saturated_math::id()) + { + (a as i32).saturating_sub(b as i32) + } else { + #[allow(clippy::integer_arithmetic)] + { + a as i32 - b as i32 + } + }; + *result = Ok(0); + return; + }; + i = i.saturating_add(1); + } + *cmp_result = 0; + *result = Ok(0); + } +); + +declare_syscall!( + /// memset + SyscallMemset, + fn call( + &mut self, + s_addr: u64, + c: u64, + n: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + question_mark!(mem_op_consume(&invoke_context, n), result); + + let s = question_mark!( + translate_slice_mut::( + memory_mapping, + s_addr, + n, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + for val in s.iter_mut().take(n as usize) { + *val = c as u8; + } + *result = Ok(0); + } +); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls/mod.rs similarity index 68% rename from programs/bpf_loader/src/syscalls.rs rename to programs/bpf_loader/src/syscalls/mod.rs index 02b603f4b9..d96345c98d 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -1,5 +1,16 @@ #[allow(deprecated)] use { + self::{ + cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust}, + logging::{ + SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64, + }, + mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset}, + sysvar::{ + SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar, + SyscallGetRentSysvar, + }, + }, crate::{allocator_bump::BpfAllocator, BpfError}, solana_program_runtime::{ ic_logger_msg, ic_msg, @@ -56,6 +67,11 @@ use { thiserror::Error as ThisError, }; +mod cpi; +mod logging; +mod mem_ops; +mod sysvar; + /// Maximum signers pub const MAX_SIGNERS: usize = 16; @@ -486,11 +502,12 @@ fn translate_string_and_do( type SyscallContext<'a, 'b> = Rc>>; +#[macro_export] macro_rules! declare_syscall { ($(#[$attr:meta])* $name:ident, $call:item) => { $(#[$attr])* pub struct $name<'a, 'b> { - invoke_context: SyscallContext<'a, 'b>, + pub(crate) invoke_context: SyscallContext<'a, 'b>, } impl<'a, 'b> $name<'a, 'b> { pub fn init( @@ -564,148 +581,6 @@ declare_syscall!( } ); -declare_syscall!( - /// Log a user's info message - SyscallLog, - fn call( - &mut self, - addr: u64, - len: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - let cost = invoke_context - .get_compute_budget() - .syscall_base_cost - .max(len); - question_mark!(invoke_context.get_compute_meter().consume(cost), result); - - question_mark!( - translate_string_and_do( - memory_mapping, - addr, - len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - &mut |string: &str| { - stable_log::program_log(&invoke_context.get_log_collector(), string); - Ok(0) - } - ), - result - ); - *result = Ok(0); - } -); - -declare_syscall!( - /// Log 5 64-bit values - SyscallLogU64, - fn call( - &mut self, - arg1: u64, - arg2: u64, - arg3: u64, - arg4: u64, - arg5: u64, - _memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - let cost = invoke_context.get_compute_budget().log_64_units; - question_mark!(invoke_context.get_compute_meter().consume(cost), result); - - stable_log::program_log( - &invoke_context.get_log_collector(), - &format!( - "{:#x}, {:#x}, {:#x}, {:#x}, {:#x}", - arg1, arg2, arg3, arg4, arg5 - ), - ); - *result = Ok(0); - } -); - -declare_syscall!( - /// Log current compute consumption - SyscallLogBpfComputeUnits, - fn call( - &mut self, - _arg1: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - _memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - let cost = invoke_context.get_compute_budget().syscall_base_cost; - question_mark!(invoke_context.get_compute_meter().consume(cost), result); - - ic_logger_msg!( - invoke_context.get_log_collector(), - "Program consumption: {} units remaining", - invoke_context.get_compute_meter().borrow().get_remaining() - ); - *result = Ok(0); - } -); - -declare_syscall!( - /// Log 5 64-bit values - SyscallLogPubkey, - fn call( - &mut self, - pubkey_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - let cost = invoke_context.get_compute_budget().log_pubkey_units; - question_mark!(invoke_context.get_compute_meter().consume(cost), result); - - let pubkey = question_mark!( - translate_type::( - memory_mapping, - pubkey_addr, - invoke_context.get_check_aligned() - ), - result - ); - stable_log::program_log(&invoke_context.get_log_collector(), &pubkey.to_string()); - *result = Ok(0); - } -); - declare_syscall!( /// Dynamic memory allocation syscall called when the BPF program calls /// `sol_alloc_free_()`. The allocator is expected to allocate/free @@ -1022,146 +897,6 @@ declare_syscall!( } ); -fn get_sysvar( - sysvar: Result, InstructionError>, - var_addr: u64, - check_aligned: bool, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, -) -> Result> { - invoke_context.get_compute_meter().consume( - invoke_context - .get_compute_budget() - .sysvar_base_cost - .saturating_add(size_of::() as u64), - )?; - let var = translate_type_mut::(memory_mapping, var_addr, check_aligned)?; - - let sysvar: Arc = sysvar.map_err(SyscallError::InstructionError)?; - *var = T::clone(sysvar.as_ref()); - - Ok(SUCCESS) -} - -declare_syscall!( - /// Get a Clock sysvar - SyscallGetClockSysvar, - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let mut invoke_context = question_mark!( - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - *result = get_sysvar( - invoke_context.get_sysvar_cache().get_clock(), - var_addr, - invoke_context.get_check_aligned(), - memory_mapping, - &mut invoke_context, - ); - } -); - -declare_syscall!( - /// Get a EpochSchedule sysvar - SyscallGetEpochScheduleSysvar, - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let mut invoke_context = question_mark!( - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - *result = get_sysvar( - invoke_context.get_sysvar_cache().get_epoch_schedule(), - var_addr, - invoke_context.get_check_aligned(), - memory_mapping, - &mut invoke_context, - ); - } -); - -declare_syscall!( - /// Get a Fees sysvar - SyscallGetFeesSysvar, - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let mut invoke_context = question_mark!( - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - #[allow(deprecated)] - { - *result = get_sysvar( - invoke_context.get_sysvar_cache().get_fees(), - var_addr, - invoke_context.get_check_aligned(), - memory_mapping, - &mut invoke_context, - ); - } - } -); - -declare_syscall!( - /// Get a Rent sysvar - SyscallGetRentSysvar, - fn call( - &mut self, - var_addr: u64, - _arg2: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let mut invoke_context = question_mark!( - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - *result = get_sysvar( - invoke_context.get_sysvar_cache().get_rent(), - var_addr, - invoke_context.get_check_aligned(), - memory_mapping, - &mut invoke_context, - ); - } -); - declare_syscall!( // Keccak256 SyscallKeccak256, @@ -1246,245 +981,6 @@ declare_syscall!( } ); -fn mem_op_consume<'a, 'b>( - invoke_context: &Ref<&'a mut InvokeContext<'b>>, - n: u64, -) -> Result<(), EbpfError> { - let compute_budget = invoke_context.get_compute_budget(); - let cost = compute_budget - .mem_op_base_cost - .max(n.saturating_div(compute_budget.cpi_bytes_per_unit)); - invoke_context.get_compute_meter().consume(cost) -} - -declare_syscall!( - /// memcpy - SyscallMemcpy, - fn call( - &mut self, - dst_addr: u64, - src_addr: u64, - n: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - question_mark!(mem_op_consume(&invoke_context, n), result); - - let do_check_physical_overlapping = invoke_context - .feature_set - .is_active(&check_physical_overlapping::id()); - - if !is_nonoverlapping(src_addr, dst_addr, n) { - *result = Err(SyscallError::CopyOverlapping.into()); - return; - } - - let dst_ptr = question_mark!( - translate_slice_mut::( - memory_mapping, - dst_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size() - ), - result - ) - .as_mut_ptr(); - let src_ptr = question_mark!( - translate_slice::( - memory_mapping, - src_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size() - ), - result - ) - .as_ptr(); - if do_check_physical_overlapping - && !is_nonoverlapping(src_ptr as usize, dst_ptr as usize, n as usize) - { - unsafe { - std::ptr::copy(src_ptr, dst_ptr, n as usize); - } - } else { - unsafe { - std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, n as usize); - } - } - *result = Ok(0); - } -); - -declare_syscall!( - /// memmove - SyscallMemmove, - fn call( - &mut self, - dst_addr: u64, - src_addr: u64, - n: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - question_mark!(mem_op_consume(&invoke_context, n), result); - - let dst = question_mark!( - translate_slice_mut::( - memory_mapping, - dst_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size() - ), - result - ); - let src = question_mark!( - translate_slice::( - memory_mapping, - src_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size() - ), - result - ); - unsafe { - std::ptr::copy(src.as_ptr(), dst.as_mut_ptr(), n as usize); - } - *result = Ok(0); - } -); - -declare_syscall!( - /// memcmp - SyscallMemcmp, - fn call( - &mut self, - s1_addr: u64, - s2_addr: u64, - n: u64, - cmp_result_addr: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - question_mark!(mem_op_consume(&invoke_context, n), result); - - let s1 = question_mark!( - translate_slice::( - memory_mapping, - s1_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ), - result - ); - let s2 = question_mark!( - translate_slice::( - memory_mapping, - s2_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ), - result - ); - let cmp_result = question_mark!( - translate_type_mut::( - memory_mapping, - cmp_result_addr, - invoke_context.get_check_aligned() - ), - result - ); - let mut i = 0; - while i < n as usize { - let a = *question_mark!(s1.get(i).ok_or(SyscallError::InvalidLength,), result); - let b = *question_mark!(s2.get(i).ok_or(SyscallError::InvalidLength,), result); - if a != b { - *cmp_result = if invoke_context - .feature_set - .is_active(&syscall_saturated_math::id()) - { - (a as i32).saturating_sub(b as i32) - } else { - #[allow(clippy::integer_arithmetic)] - { - a as i32 - b as i32 - } - }; - *result = Ok(0); - return; - }; - i = i.saturating_add(1); - } - *cmp_result = 0; - *result = Ok(0); - } -); - -declare_syscall!( - /// memset - SyscallMemset, - fn call( - &mut self, - s_addr: u64, - c: u64, - n: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - question_mark!(mem_op_consume(&invoke_context, n), result); - - let s = question_mark!( - translate_slice_mut::( - memory_mapping, - s_addr, - n, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ), - result - ); - for val in s.iter_mut().take(n as usize) { - *val = c as u8; - } - *result = Ok(0); - } -); - declare_syscall!( /// secp256k1_recover SyscallSecp256k1Recover, @@ -2013,934 +1509,6 @@ declare_syscall!( } ); -// Cross-program invocation syscalls - -struct CallerAccount<'a> { - lamports: &'a mut u64, - owner: &'a mut Pubkey, - original_data_len: usize, - data: &'a mut [u8], - vm_data_addr: u64, - ref_to_len_in_vm: &'a mut u64, - serialized_len_ptr: &'a mut u64, - executable: bool, - rent_epoch: u64, -} -type TranslatedAccounts<'a> = Vec<(usize, Option>)>; - -/// Implemented by language specific data structure translators -trait SyscallInvokeSigned<'a, 'b> { - fn get_context_mut(&self) -> Result>, EbpfError>; - fn translate_instruction( - &self, - addr: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result>; - fn translate_accounts<'c>( - &'c self, - instruction_accounts: &[InstructionAccount], - program_indices: &[usize], - account_infos_addr: u64, - account_infos_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result, EbpfError>; - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &InvokeContext, - ) -> Result, EbpfError>; -} - -declare_syscall!( - /// Cross-program invocation called from Rust - SyscallInvokeSignedRust, - fn call( - &mut self, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - *result = call( - self, - instruction_addr, - account_infos_addr, - account_infos_len, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - ); - } -); - -impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { - fn get_context_mut(&self) -> Result>, EbpfError> { - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - - fn translate_instruction( - &self, - addr: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result> { - let ix = translate_type::( - memory_mapping, - addr, - invoke_context.get_check_aligned(), - )?; - - check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?; - - let accounts = translate_slice::( - memory_mapping, - ix.accounts.as_ptr() as u64, - ix.accounts.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); - let data = translate_slice::( - memory_mapping, - ix.data.as_ptr() as u64, - ix.data.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); - Ok(Instruction { - program_id: ix.program_id, - accounts, - data, - }) - } - - fn translate_accounts<'c>( - &'c self, - instruction_accounts: &[InstructionAccount], - program_indices: &[usize], - account_infos_addr: u64, - account_infos_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result, EbpfError> { - let account_infos = translate_slice::( - memory_mapping, - account_infos_addr, - account_infos_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - check_account_infos(account_infos.len(), invoke_context)?; - let account_info_keys = account_infos - .iter() - .map(|account_info| { - translate_type::( - memory_mapping, - account_info.key as *const _ as u64, - invoke_context.get_check_aligned(), - ) - }) - .collect::, EbpfError>>()?; - - let translate = |account_info: &AccountInfo, invoke_context: &InvokeContext| { - // Translate the account from user space - - let lamports = { - // Double translate lamports out of RefCell - let ptr = translate_type::( - memory_mapping, - account_info.lamports.as_ptr() as u64, - invoke_context.get_check_aligned(), - )?; - translate_type_mut::(memory_mapping, *ptr, invoke_context.get_check_aligned())? - }; - let owner = translate_type_mut::( - memory_mapping, - account_info.owner as *const _ as u64, - invoke_context.get_check_aligned(), - )?; - - let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { - // Double translate data out of RefCell - let data = *translate_type::<&[u8]>( - memory_mapping, - account_info.data.as_ptr() as *const _ as u64, - invoke_context.get_check_aligned(), - )?; - - invoke_context.get_compute_meter().consume( - (data.len() as u64) - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - - let translated = translate( - memory_mapping, - AccessType::Store, - (account_info.data.as_ptr() as *const u64 as u64) - .saturating_add(size_of::() as u64), - 8, - )? as *mut u64; - let ref_to_len_in_vm = unsafe { &mut *translated }; - let ref_of_len_in_input_buffer = - (data.as_ptr() as *const _ as u64).saturating_sub(8); - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - ref_of_len_in_input_buffer, - invoke_context.get_check_aligned(), - )?; - let vm_data_addr = data.as_ptr() as u64; - ( - translate_slice_mut::( - memory_mapping, - vm_data_addr, - data.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - ) - }; - - Ok(CallerAccount { - lamports, - owner, - original_data_len: 0, // set later - data, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - executable: account_info.executable, - rent_epoch: account_info.rent_epoch, - }) - }; - - get_translated_accounts( - instruction_accounts, - program_indices, - &account_info_keys, - account_infos, - invoke_context, - translate, - ) - } - - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &InvokeContext, - ) -> Result, EbpfError> { - let mut signers = Vec::new(); - if signers_seeds_len > 0 { - let signers_seeds = translate_slice::<&[&[u8]]>( - memory_mapping, - signers_seeds_addr, - signers_seeds_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - if signers_seeds.len() > MAX_SIGNERS { - return Err(SyscallError::TooManySigners.into()); - } - for signer_seeds in signers_seeds.iter() { - let untranslated_seeds = translate_slice::<&[u8]>( - memory_mapping, - signer_seeds.as_ptr() as *const _ as u64, - signer_seeds.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - if untranslated_seeds.len() > MAX_SEEDS { - return Err(SyscallError::InstructionError( - InstructionError::MaxSeedLengthExceeded, - ) - .into()); - } - let seeds = untranslated_seeds - .iter() - .map(|untranslated_seed| { - translate_slice::( - memory_mapping, - untranslated_seed.as_ptr() as *const _ as u64, - untranslated_seed.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ) - }) - .collect::, EbpfError>>()?; - let signer = Pubkey::create_program_address(&seeds, program_id) - .map_err(SyscallError::BadSeeds)?; - signers.push(signer); - } - Ok(signers) - } else { - Ok(vec![]) - } - } -} - -/// Rust representation of C's SolInstruction -#[derive(Debug)] -#[repr(C)] -struct SolInstruction { - program_id_addr: u64, - accounts_addr: u64, - accounts_len: u64, - data_addr: u64, - data_len: u64, -} - -/// Rust representation of C's SolAccountMeta -#[derive(Debug)] -#[repr(C)] -struct SolAccountMeta { - pubkey_addr: u64, - is_writable: bool, - is_signer: bool, -} - -/// Rust representation of C's SolAccountInfo -#[derive(Debug)] -#[repr(C)] -struct SolAccountInfo { - key_addr: u64, - lamports_addr: u64, - data_len: u64, - data_addr: u64, - owner_addr: u64, - rent_epoch: u64, - #[allow(dead_code)] - is_signer: bool, - #[allow(dead_code)] - is_writable: bool, - executable: bool, -} - -/// Rust representation of C's SolSignerSeed -#[derive(Debug)] -#[repr(C)] -struct SolSignerSeedC { - addr: u64, - len: u64, -} - -/// Rust representation of C's SolSignerSeeds -#[derive(Debug)] -#[repr(C)] -struct SolSignerSeedsC { - addr: u64, - len: u64, -} - -declare_syscall!( - /// Cross-program invocation called from C - SyscallInvokeSignedC, - fn call( - &mut self, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - *result = call( - self, - instruction_addr, - account_infos_addr, - account_infos_len, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - ); - } -); - -impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { - fn get_context_mut(&self) -> Result>, EbpfError> { - self.invoke_context - .try_borrow_mut() - .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) - } - - fn translate_instruction( - &self, - addr: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result> { - let ix_c = translate_type::( - memory_mapping, - addr, - invoke_context.get_check_aligned(), - )?; - - check_instruction_size( - ix_c.accounts_len as usize, - ix_c.data_len as usize, - invoke_context, - )?; - let program_id = translate_type::( - memory_mapping, - ix_c.program_id_addr, - invoke_context.get_check_aligned(), - )?; - let meta_cs = translate_slice::( - memory_mapping, - ix_c.accounts_addr, - ix_c.accounts_len as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - let data = translate_slice::( - memory_mapping, - ix_c.data_addr, - ix_c.data_len as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); - let accounts = meta_cs - .iter() - .map(|meta_c| { - let pubkey = translate_type::( - memory_mapping, - meta_c.pubkey_addr, - invoke_context.get_check_aligned(), - )?; - Ok(AccountMeta { - pubkey: *pubkey, - is_signer: meta_c.is_signer, - is_writable: meta_c.is_writable, - }) - }) - .collect::, EbpfError>>()?; - - Ok(Instruction { - program_id: *program_id, - accounts, - data, - }) - } - - fn translate_accounts<'c>( - &'c self, - instruction_accounts: &[InstructionAccount], - program_indices: &[usize], - account_infos_addr: u64, - account_infos_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &mut InvokeContext, - ) -> Result, EbpfError> { - let account_infos = translate_slice::( - memory_mapping, - account_infos_addr, - account_infos_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - check_account_infos(account_infos.len(), invoke_context)?; - let account_info_keys = account_infos - .iter() - .map(|account_info| { - translate_type::( - memory_mapping, - account_info.key_addr, - invoke_context.get_check_aligned(), - ) - }) - .collect::, EbpfError>>()?; - - let translate = |account_info: &SolAccountInfo, invoke_context: &InvokeContext| { - // Translate the account from user space - - let lamports = translate_type_mut::( - memory_mapping, - account_info.lamports_addr, - invoke_context.get_check_aligned(), - )?; - let owner = translate_type_mut::( - memory_mapping, - account_info.owner_addr, - invoke_context.get_check_aligned(), - )?; - let vm_data_addr = account_info.data_addr; - - invoke_context.get_compute_meter().consume( - account_info - .data_len - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - - let data = translate_slice_mut::( - memory_mapping, - vm_data_addr, - account_info.data_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - - let first_info_addr = account_infos.first().ok_or(SyscallError::InstructionError( - InstructionError::InvalidArgument, - ))? as *const _ as u64; - let addr = &account_info.data_len as *const u64 as u64; - let vm_addr = if invoke_context - .feature_set - .is_active(&syscall_saturated_math::id()) - { - account_infos_addr.saturating_add(addr.saturating_sub(first_info_addr)) - } else { - #[allow(clippy::integer_arithmetic)] - { - account_infos_addr + (addr - first_info_addr) - } - }; - let _ = translate( - memory_mapping, - AccessType::Store, - vm_addr, - size_of::() as u64, - )?; - let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) }; - - let ref_of_len_in_input_buffer = - (account_info.data_addr as *mut u8 as u64).saturating_sub(8); - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - ref_of_len_in_input_buffer, - invoke_context.get_check_aligned(), - )?; - - Ok(CallerAccount { - lamports, - owner, - original_data_len: 0, // set later - data, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - executable: account_info.executable, - rent_epoch: account_info.rent_epoch, - }) - }; - - get_translated_accounts( - instruction_accounts, - program_indices, - &account_info_keys, - account_infos, - invoke_context, - translate, - ) - } - - fn translate_signers( - &self, - program_id: &Pubkey, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, - invoke_context: &InvokeContext, - ) -> Result, EbpfError> { - if signers_seeds_len > 0 { - let signers_seeds = translate_slice::( - memory_mapping, - signers_seeds_addr, - signers_seeds_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - if signers_seeds.len() > MAX_SIGNERS { - return Err(SyscallError::TooManySigners.into()); - } - Ok(signers_seeds - .iter() - .map(|signer_seeds| { - let seeds = translate_slice::( - memory_mapping, - signer_seeds.addr, - signer_seeds.len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; - if seeds.len() > MAX_SEEDS { - return Err(SyscallError::InstructionError( - InstructionError::MaxSeedLengthExceeded, - ) - .into()); - } - let seeds_bytes = seeds - .iter() - .map(|seed| { - translate_slice::( - memory_mapping, - seed.addr, - seed.len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ) - }) - .collect::, EbpfError>>()?; - Pubkey::create_program_address(&seeds_bytes, program_id) - .map_err(|err| SyscallError::BadSeeds(err).into()) - }) - .collect::, EbpfError>>()?) - } else { - Ok(vec![]) - } - } -} - -fn get_translated_accounts<'a, T, F>( - instruction_accounts: &[InstructionAccount], - program_indices: &[usize], - account_info_keys: &[&Pubkey], - account_infos: &[T], - invoke_context: &mut InvokeContext, - do_translate: F, -) -> Result, EbpfError> -where - F: Fn(&T, &InvokeContext) -> Result, EbpfError>, -{ - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context - .get_current_instruction_context() - .map_err(SyscallError::InstructionError)?; - let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1)); - - let program_account_index = program_indices - .last() - .ok_or(SyscallError::InstructionError( - InstructionError::MissingAccount, - ))?; - accounts.push((*program_account_index, None)); - - for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate() - { - if instruction_account_index != instruction_account.index_in_callee { - continue; // Skip duplicate account - } - let mut callee_account = instruction_context - .try_borrow_instruction_account( - transaction_context, - instruction_account.index_in_caller, - ) - .map_err(SyscallError::InstructionError)?; - let account_key = invoke_context - .transaction_context - .get_key_of_account_at_index(instruction_account.index_in_transaction) - .map_err(SyscallError::InstructionError)?; - if callee_account.is_executable() { - // Use the known account - invoke_context.get_compute_meter().consume( - (callee_account.get_data().len() as u64) - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - - accounts.push((instruction_account.index_in_caller, None)); - } else if let Some(caller_account_index) = - account_info_keys.iter().position(|key| *key == account_key) - { - let mut caller_account = do_translate( - account_infos - .get(caller_account_index) - .ok_or(SyscallError::InvalidLength)?, - invoke_context, - )?; - { - if callee_account.get_lamports() != *caller_account.lamports { - callee_account - .set_lamports(*caller_account.lamports) - .map_err(SyscallError::InstructionError)?; - } - // The redundant check helps to avoid the expensive data comparison if we can - match callee_account - .can_data_be_resized(caller_account.data.len()) - .and_then(|_| callee_account.can_data_be_changed()) - { - Ok(()) => callee_account - .set_data(caller_account.data) - .map_err(SyscallError::InstructionError)?, - Err(err) if callee_account.get_data() != caller_account.data => { - return Err(EbpfError::UserError(BpfError::SyscallError( - SyscallError::InstructionError(err), - ))); - } - _ => {} - } - if callee_account.is_executable() != caller_account.executable { - callee_account - .set_executable(caller_account.executable) - .map_err(SyscallError::InstructionError)?; - } - // Change the owner at the end so that we are allowed to change the lamports and data before - if callee_account.get_owner() != caller_account.owner { - callee_account - .set_owner(caller_account.owner.as_ref()) - .map_err(SyscallError::InstructionError)?; - } - drop(callee_account); - let callee_account = invoke_context - .transaction_context - .get_account_at_index(instruction_account.index_in_transaction) - .map_err(SyscallError::InstructionError)?; - if callee_account.borrow().rent_epoch() != caller_account.rent_epoch { - if invoke_context - .feature_set - .is_active(&enable_early_verification_of_account_modifications::id()) - { - Err(SyscallError::InstructionError( - InstructionError::RentEpochModified, - ))?; - } else { - callee_account - .borrow_mut() - .set_rent_epoch(caller_account.rent_epoch); - } - } - } - let caller_account = if instruction_account.is_writable { - let orig_data_lens = invoke_context - .get_orig_account_lengths() - .map_err(SyscallError::InstructionError)?; - caller_account.original_data_len = *orig_data_lens - .get(instruction_account.index_in_caller) - .ok_or_else(|| { - ic_msg!( - invoke_context, - "Internal error: index mismatch for account {}", - account_key - ); - SyscallError::InstructionError(InstructionError::MissingAccount) - })?; - Some(caller_account) - } else { - None - }; - accounts.push((instruction_account.index_in_caller, caller_account)); - } else { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); - } - } - - Ok(accounts) -} - -fn check_instruction_size( - num_accounts: usize, - data_len: usize, - invoke_context: &mut InvokeContext, -) -> Result<(), EbpfError> { - let size = num_accounts - .saturating_mul(size_of::()) - .saturating_add(data_len); - let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; - if size > max_size { - return Err(SyscallError::InstructionTooLarge(size, max_size).into()); - } - Ok(()) -} - -fn check_account_infos( - len: usize, - invoke_context: &mut InvokeContext, -) -> Result<(), EbpfError> { - let adjusted_len = if invoke_context - .feature_set - .is_active(&syscall_saturated_math::id()) - { - len.saturating_mul(size_of::()) - } else { - #[allow(clippy::integer_arithmetic)] - { - len * size_of::() - } - }; - if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size { - // Cap the number of account_infos a caller can pass to approximate - // maximum that accounts that could be passed in an instruction - return Err(SyscallError::TooManyAccounts.into()); - }; - Ok(()) -} - -fn check_authorized_program( - program_id: &Pubkey, - instruction_data: &[u8], - invoke_context: &InvokeContext, -) -> Result<(), EbpfError> { - #[allow(clippy::blocks_in_if_conditions)] - if native_loader::check_id(program_id) - || bpf_loader::check_id(program_id) - || bpf_loader_deprecated::check_id(program_id) - || (bpf_loader_upgradeable::check_id(program_id) - && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data) - || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data) - || bpf_loader_upgradeable::is_close_instruction(instruction_data))) - || (invoke_context - .feature_set - .is_active(&prevent_calling_precompiles_as_programs::id()) - && is_precompile(program_id, |feature_id: &Pubkey| { - invoke_context.feature_set.is_active(feature_id) - })) - { - return Err(SyscallError::ProgramNotSupported(*program_id).into()); - } - Ok(()) -} - -/// Call process instruction, common to both Rust and C -fn call<'a, 'b: 'a>( - syscall: &mut dyn SyscallInvokeSigned<'a, 'b>, - instruction_addr: u64, - account_infos_addr: u64, - account_infos_len: u64, - signers_seeds_addr: u64, - signers_seeds_len: u64, - memory_mapping: &mut MemoryMapping, -) -> Result> { - let mut invoke_context = syscall.get_context_mut()?; - invoke_context - .get_compute_meter() - .consume(invoke_context.get_compute_budget().invoke_units)?; - - // Translate and verify caller's data - let instruction = - syscall.translate_instruction(instruction_addr, memory_mapping, *invoke_context)?; - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context - .get_current_instruction_context() - .map_err(SyscallError::InstructionError)?; - let caller_program_id = instruction_context - .get_last_program_key(transaction_context) - .map_err(SyscallError::InstructionError)?; - let signers = syscall.translate_signers( - caller_program_id, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - *invoke_context, - )?; - let (instruction_accounts, program_indices) = invoke_context - .prepare_instruction(&instruction, &signers) - .map_err(SyscallError::InstructionError)?; - check_authorized_program(&instruction.program_id, &instruction.data, *invoke_context)?; - let mut accounts = syscall.translate_accounts( - &instruction_accounts, - &program_indices, - account_infos_addr, - account_infos_len, - memory_mapping, - *invoke_context, - )?; - - // Process instruction - let mut compute_units_consumed = 0; - invoke_context - .process_instruction( - &instruction.data, - &instruction_accounts, - &program_indices, - &mut compute_units_consumed, - &mut ExecuteTimings::default(), - ) - .map_err(SyscallError::InstructionError)?; - - // Copy results back to caller - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context - .get_current_instruction_context() - .map_err(SyscallError::InstructionError)?; - for (index_in_caller, caller_account) in accounts.iter_mut() { - if let Some(caller_account) = caller_account { - let callee_account = instruction_context - .try_borrow_instruction_account(transaction_context, *index_in_caller) - .map_err(SyscallError::InstructionError)?; - *caller_account.lamports = callee_account.get_lamports(); - *caller_account.owner = *callee_account.get_owner(); - let new_len = callee_account.get_data().len(); - if caller_account.data.len() != new_len { - let data_overflow = if invoke_context - .feature_set - .is_active(&syscall_saturated_math::id()) - { - new_len - > caller_account - .original_data_len - .saturating_add(MAX_PERMITTED_DATA_INCREASE) - } else { - #[allow(clippy::integer_arithmetic)] - { - new_len > caller_account.original_data_len + MAX_PERMITTED_DATA_INCREASE - } - }; - if data_overflow { - ic_msg!( - invoke_context, - "Account data size realloc limited to {} in inner instructions", - MAX_PERMITTED_DATA_INCREASE - ); - return Err( - SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), - ); - } - if new_len < caller_account.data.len() { - caller_account - .data - .get_mut(new_len..) - .ok_or(SyscallError::InstructionError( - InstructionError::AccountDataTooSmall, - ))? - .fill(0); - } - caller_account.data = translate_slice_mut::( - memory_mapping, - caller_account.vm_data_addr, - new_len as u64, - false, // Don't care since it is byte aligned - invoke_context.get_check_size(), - )?; - *caller_account.ref_to_len_in_vm = new_len as u64; - *caller_account.serialized_len_ptr = new_len as u64; - } - let to_slice = &mut caller_account.data; - let from_slice = callee_account - .get_data() - .get(0..new_len) - .ok_or(SyscallError::InvalidLength)?; - if to_slice.len() != from_slice.len() { - return Err( - SyscallError::InstructionError(InstructionError::AccountDataTooSmall).into(), - ); - } - to_slice.copy_from_slice(from_slice); - } - } - - Ok(SUCCESS) -} - declare_syscall!( /// Set return data SyscallSetReturnData, @@ -3103,85 +1671,6 @@ declare_syscall!( } ); -declare_syscall!( - /// Log data handling - SyscallLogData, - fn call( - &mut self, - addr: u64, - len: u64, - _arg3: u64, - _arg4: u64, - _arg5: u64, - memory_mapping: &mut MemoryMapping, - result: &mut Result>, - ) { - let invoke_context = question_mark!( - self.invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed), - result - ); - let budget = invoke_context.get_compute_budget(); - - question_mark!( - invoke_context - .get_compute_meter() - .consume(budget.syscall_base_cost), - result - ); - - let untranslated_fields = question_mark!( - translate_slice::<&[u8]>( - memory_mapping, - addr, - len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ), - result - ); - - question_mark!( - invoke_context.get_compute_meter().consume( - budget - .syscall_base_cost - .saturating_mul(untranslated_fields.len() as u64) - ), - result - ); - question_mark!( - invoke_context.get_compute_meter().consume( - untranslated_fields - .iter() - .fold(0, |total, e| total.saturating_add(e.len() as u64)) - ), - result - ); - - let mut fields = Vec::with_capacity(untranslated_fields.len()); - - for untranslated_field in untranslated_fields { - fields.push(question_mark!( - translate_slice::( - memory_mapping, - untranslated_field.as_ptr() as *const _ as u64, - untranslated_field.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ), - result - )); - } - - let log_collector = invoke_context.get_log_collector(); - - stable_log::program_data(&log_collector, &fields); - - *result = Ok(0); - } -); - declare_syscall!( /// Get a processed sigling instruction SyscallGetProcessedSiblingInstruction, diff --git a/programs/bpf_loader/src/syscalls/sysvar.rs b/programs/bpf_loader/src/syscalls/sysvar.rs new file mode 100644 index 0000000000..5cd339c927 --- /dev/null +++ b/programs/bpf_loader/src/syscalls/sysvar.rs @@ -0,0 +1,141 @@ +use {super::*, crate::declare_syscall}; + +fn get_sysvar( + sysvar: Result, InstructionError>, + var_addr: u64, + check_aligned: bool, + memory_mapping: &mut MemoryMapping, + invoke_context: &mut InvokeContext, +) -> Result> { + invoke_context.get_compute_meter().consume( + invoke_context + .get_compute_budget() + .sysvar_base_cost + .saturating_add(size_of::() as u64), + )?; + let var = translate_type_mut::(memory_mapping, var_addr, check_aligned)?; + + let sysvar: Arc = sysvar.map_err(SyscallError::InstructionError)?; + *var = T::clone(sysvar.as_ref()); + + Ok(SUCCESS) +} + +declare_syscall!( + /// Get a Clock sysvar + SyscallGetClockSysvar, + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let mut invoke_context = question_mark!( + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_clock(), + var_addr, + invoke_context.get_check_aligned(), + memory_mapping, + &mut invoke_context, + ); + } +); + +declare_syscall!( + /// Get a EpochSchedule sysvar + SyscallGetEpochScheduleSysvar, + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let mut invoke_context = question_mark!( + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_epoch_schedule(), + var_addr, + invoke_context.get_check_aligned(), + memory_mapping, + &mut invoke_context, + ); + } +); + +declare_syscall!( + /// Get a Fees sysvar + SyscallGetFeesSysvar, + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let mut invoke_context = question_mark!( + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + #[allow(deprecated)] + { + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_fees(), + var_addr, + invoke_context.get_check_aligned(), + memory_mapping, + &mut invoke_context, + ); + } + } +); + +declare_syscall!( + /// Get a Rent sysvar + SyscallGetRentSysvar, + fn call( + &mut self, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + let mut invoke_context = question_mark!( + self.invoke_context + .try_borrow_mut() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_rent(), + var_addr, + invoke_context.get_check_aligned(), + memory_mapping, + &mut invoke_context, + ); + } +);