diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 8b29746d81039e..7e59d0271f7dd5 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1072,31 +1072,6 @@ fn test_program_bpf_invoke_sanity() { TEST_INSTRUCTION_META_TOO_LARGE, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], -<<<<<<< HEAD -======= - Some(vec![ - format!("Program {invoke_program_id} invoke [1]"), - format!("Program log: invoke {program_lang} program"), - "Program log: Test max instruction accounts exceeded".into(), - "skip".into(), // don't compare compute consumption logs - "Program failed to complete: Invoked an instruction with too many accounts (256 > 255)".into(), - format!("Program {invoke_program_id} failed: Program failed to complete"), - ]), - ); - - do_invoke_failure_test_local( - TEST_MAX_ACCOUNT_INFOS_EXCEEDED, - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), - &[], - Some(vec![ - format!("Program {invoke_program_id} invoke [1]"), - format!("Program log: invoke {program_lang} program"), - "Program log: Test max account infos exceeded".into(), - "skip".into(), // don't compare compute consumption logs - "Program failed to complete: Invoked an instruction with too many account info's (129 > 128)".into(), - format!("Program {invoke_program_id} failed: Program failed to complete"), - ]), ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) ); do_invoke_failure_test_local( diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs deleted file mode 100644 index f11c7981571d7e..00000000000000 --- a/programs/bpf_loader/src/syscalls/cpi.rs +++ /dev/null @@ -1,997 +0,0 @@ -use { - super::*, - crate::declare_syscall, - solana_sdk::syscalls::{ - MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN, - }, -}; - -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, - executable: bool, - rent_epoch: u64, -} -type TranslatedAccounts<'a> = Vec<(IndexOfAccount, 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: &[IndexOfAccount], - 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 ix_data_len = ix.data.len() as u64; - if invoke_context - .feature_set - .is_active(&feature_set::loosen_cpi_size_restriction::id()) - { - invoke_context.get_compute_meter().consume( - (ix_data_len) - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - } - - let data = translate_slice::( - memory_mapping, - ix.data.as_ptr() as u64, - ix_data_len, - 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: &[IndexOfAccount], - 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) = { - // 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 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, - ) - }; - - Ok(CallerAccount { - lamports, - owner, - original_data_len: 0, // set later - data, - vm_data_addr, - ref_to_len_in_vm, - 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 ix_data_len = ix_c.data_len as u64; - if invoke_context - .feature_set - .is_active(&feature_set::loosen_cpi_size_restriction::id()) - { - invoke_context.get_compute_meter().consume( - (ix_data_len) - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - } - - let data = translate_slice::( - memory_mapping, - ix_c.data_addr, - ix_data_len, - 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: &[IndexOfAccount], - 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) }; - - Ok(CallerAccount { - lamports, - owner, - original_data_len: 0, // set later - data, - vm_data_addr, - ref_to_len_in_vm, - 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: &[IndexOfAccount], - 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 is_disable_cpi_setting_executable_and_rent_epoch_active = invoke_context - .feature_set - .is_active(&disable_cpi_setting_executable_and_rent_epoch::id()); - - 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 as IndexOfAccount != 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 !is_disable_cpi_setting_executable_and_rent_epoch_active - && 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 !is_disable_cpi_setting_executable_and_rent_epoch_active - && callee_account.borrow().rent_epoch() != caller_account.rent_epoch - { - if invoke_context - .feature_set - .is_active(&enable_early_verification_of_account_modifications::id()) - { - return Err(SyscallError::InstructionError( - InstructionError::RentEpochModified, - ) - .into()); - } 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 as usize) - .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> { - if invoke_context - .feature_set - .is_active(&feature_set::loosen_cpi_size_restriction::id()) - { - let data_len = data_len as u64; - let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN; - if data_len > max_data_len { - return Err(SyscallError::MaxInstructionDataLenExceeded { - data_len, - max_data_len, - } - .into()); - } - - let num_accounts = num_accounts as u64; - let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64; - if num_accounts > max_accounts { - return Err(SyscallError::MaxInstructionAccountsExceeded { - num_accounts, - max_accounts, - } - .into()); - } - } else { - let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; - let size = num_accounts - .saturating_mul(size_of::()) - .saturating_add(data_len); - if size > max_size { - return Err(SyscallError::InstructionTooLarge(size, max_size).into()); - } - } - Ok(()) -} - -fn check_account_infos( - num_account_infos: usize, - invoke_context: &mut InvokeContext, -) -> Result<(), EbpfError> { - if invoke_context - .feature_set - .is_active(&feature_set::loosen_cpi_size_restriction::id()) - { - let max_cpi_account_infos = if invoke_context - .feature_set - .is_active(&feature_set::increase_tx_account_lock_limit::id()) - { - MAX_CPI_ACCOUNT_INFOS - } else { - 64 - }; - let num_account_infos = num_account_infos as u64; - let max_account_infos = max_cpi_account_infos as u64; - if num_account_infos > max_account_infos { - return Err(SyscallError::MaxInstructionAccountInfosExceeded { - num_account_infos, - max_account_infos, - } - .into()); - } - } else { - let adjusted_len = if invoke_context - .feature_set - .is_active(&syscall_saturated_math::id()) - { - num_account_infos.saturating_mul(size_of::()) - } else { - #[allow(clippy::integer_arithmetic)] - { - num_account_infos * 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> { - 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))) - || 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; - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - caller_account - .vm_data_addr - .saturating_sub(std::mem::size_of::() as u64), - invoke_context.get_check_aligned(), - )?; - *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/runtime/src/accounts.rs b/runtime/src/accounts.rs index 0ff43636c44e34..e8859b85228d1e 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -1112,10 +1112,11 @@ impl Accounts { pub fn lock_accounts<'a>( &self, txs: impl Iterator, - feature_set: &FeatureSet, + tx_account_lock_limit: usize, ) -> Vec> { - let tx_account_locks_results: Vec> = - txs.map(|tx| tx.get_account_locks(feature_set)).collect(); + let tx_account_locks_results: Vec> = txs + .map(|tx| tx.get_account_locks(tx_account_lock_limit)) + .collect(); self.lock_accounts_inner(tx_account_locks_results) } @@ -1125,12 +1126,12 @@ impl Accounts { &self, txs: impl Iterator, results: impl Iterator>, - feature_set: &FeatureSet, + tx_account_lock_limit: usize, ) -> Vec> { let tx_account_locks_results: Vec> = txs .zip(results) .map(|(tx, result)| match result { - Ok(()) => tx.get_account_locks(feature_set), + Ok(()) => tx.get_account_locks(tx_account_lock_limit), Err(err) => Err(err.clone()), }) .collect(); @@ -2507,7 +2508,7 @@ mod tests { }; let tx = new_sanitized_tx(&[&keypair], message, Hash::default()); - let results = accounts.lock_accounts([tx].iter(), &FeatureSet::all_enabled()); + let results = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Err(TransactionError::AccountLoadedTwice)); } @@ -2540,12 +2541,12 @@ mod tests { }; let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())]; - let results = accounts.lock_accounts(txs.iter(), &FeatureSet::all_enabled()); + let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Ok(())); accounts.unlock_accounts(txs.iter(), &results); } - // Allow over MAX_TX_ACCOUNT_LOCKS before feature activation + // Disallow over MAX_TX_ACCOUNT_LOCKS { let num_account_keys = MAX_TX_ACCOUNT_LOCKS + 1; let mut account_keys: Vec<_> = (0..num_account_keys) @@ -2562,29 +2563,7 @@ mod tests { }; let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())]; - let results = accounts.lock_accounts(txs.iter(), &FeatureSet::default()); - assert_eq!(results[0], Ok(())); - accounts.unlock_accounts(txs.iter(), &results); - } - - // Disallow over MAX_TX_ACCOUNT_LOCKS after feature activation - { - let num_account_keys = MAX_TX_ACCOUNT_LOCKS + 1; - let mut account_keys: Vec<_> = (0..num_account_keys) - .map(|_| Pubkey::new_unique()) - .collect(); - account_keys[0] = keypair.pubkey(); - let message = Message { - header: MessageHeader { - num_required_signatures: 1, - ..MessageHeader::default() - }, - account_keys, - ..Message::default() - }; - - let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())]; - let results = accounts.lock_accounts(txs.iter(), &FeatureSet::all_enabled()); + let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Err(TransactionError::TooManyAccountLocks)); } } @@ -2623,7 +2602,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair0], message, Hash::default()); - let results0 = accounts.lock_accounts([tx.clone()].iter(), &FeatureSet::all_enabled()); + let results0 = accounts.lock_accounts([tx.clone()].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results0[0].is_ok()); assert_eq!( @@ -2658,7 +2637,7 @@ mod tests { ); let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default()); let txs = vec![tx0, tx1]; - let results1 = accounts.lock_accounts(txs.iter(), &FeatureSet::all_enabled()); + let results1 = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times assert!(results1[1].is_err()); // Read-only account (keypair1) cannot also be locked as writable @@ -2685,7 +2664,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair1], message, Hash::default()); - let results2 = accounts.lock_accounts([tx].iter(), &FeatureSet::all_enabled()); + let results2 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable // Check that read-only lock with zero references is deleted @@ -2756,7 +2735,7 @@ mod tests { let txs = vec![writable_tx.clone()]; let results = accounts_clone .clone() - .lock_accounts(txs.iter(), &FeatureSet::all_enabled()); + .lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); for result in results.iter() { if result.is_ok() { counter_clone.clone().fetch_add(1, Ordering::SeqCst); @@ -2773,7 +2752,7 @@ mod tests { let txs = vec![readonly_tx.clone()]; let results = accounts_arc .clone() - .lock_accounts(txs.iter(), &FeatureSet::all_enabled()); + .lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); if results[0].is_ok() { let counter_value = counter_clone.clone().load(Ordering::SeqCst); thread::sleep(time::Duration::from_millis(50)); @@ -2819,7 +2798,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair0], message, Hash::default()); - let results0 = accounts.lock_accounts([tx].iter(), &FeatureSet::all_enabled()); + let results0 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results0[0].is_ok()); // Instruction program-id account demoted to readonly @@ -2913,7 +2892,7 @@ mod tests { let results = accounts.lock_accounts_with_results( txs.iter(), qos_results.iter(), - &FeatureSet::all_enabled(), + MAX_TX_ACCOUNT_LOCKS, ); assert!(results[0].is_ok()); // Read-only account (keypair0) can be referenced multiple times diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 2fadfc97e6979a..70157eae23ca78 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -134,7 +134,7 @@ use { timing::years_as_slots, transaction::{ MessageHash, Result, SanitizedTransaction, Transaction, TransactionError, - TransactionVerificationMode, VersionedTransaction, + TransactionVerificationMode, VersionedTransaction, MAX_TX_ACCOUNT_LOCKS, }, transaction_context::{InstructionTrace, TransactionAccount, TransactionContext}, }, @@ -3507,26 +3507,12 @@ impl Bank { } pub fn is_block_boundary(&self, tick_height: u64) -> bool { -<<<<<<< HEAD tick_height % self.ticks_per_slot == 0 -======= - if self - .feature_set - .is_active(&feature_set::fix_recent_blockhashes::id()) - { - tick_height == self.max_tick_height - } else { - tick_height % self.ticks_per_slot == 0 - } } /// Get the max number of accounts that a transaction may lock in this block pub fn get_transaction_account_lock_limit(&self) -> usize { - if let Some(transaction_account_lock_limit) = - self.runtime_config.transaction_account_lock_limit - { - transaction_account_lock_limit - } else if self + if self .feature_set .is_active(&feature_set::increase_tx_account_lock_limit::id()) { @@ -3534,7 +3520,6 @@ impl Bank { } else { 64 } ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) } /// Prepare a transaction batch from a list of legacy transactions. Used for tests only. @@ -3543,10 +3528,10 @@ impl Bank { .into_iter() .map(SanitizedTransaction::from_transaction_for_tests) .collect::>(); - let lock_results = self - .rc - .accounts - .lock_accounts(sanitized_txs.iter(), &FeatureSet::all_enabled()); + let lock_results = self.rc.accounts.lock_accounts( + sanitized_txs.iter(), + self.get_transaction_account_lock_limit(), + ); TransactionBatch::new(lock_results, self, Cow::Owned(sanitized_txs)) } @@ -3566,10 +3551,10 @@ impl Bank { ) }) .collect::>>()?; - let lock_results = self - .rc - .accounts - .lock_accounts(sanitized_txs.iter(), &FeatureSet::all_enabled()); + let lock_results = self.rc.accounts.lock_accounts( + sanitized_txs.iter(), + self.get_transaction_account_lock_limit(), + ); Ok(TransactionBatch::new( lock_results, self, @@ -3585,7 +3570,7 @@ impl Bank { let lock_results = self .rc .accounts - .lock_accounts(txs.iter(), &self.feature_set); + .lock_accounts(txs.iter(), self.get_transaction_account_lock_limit()); TransactionBatch::new(lock_results, self, Cow::Borrowed(txs)) } @@ -3600,7 +3585,7 @@ impl Bank { let lock_results = self.rc.accounts.lock_accounts_with_results( transactions.iter(), transaction_results, - &self.feature_set, + self.get_transaction_account_lock_limit(), ); TransactionBatch::new(lock_results, self, Cow::Borrowed(transactions)) } @@ -3610,7 +3595,9 @@ impl Bank { &'a self, transaction: SanitizedTransaction, ) -> TransactionBatch<'a, '_> { - let lock_result = transaction.get_account_locks(&self.feature_set).map(|_| ()); + let lock_result = transaction + .get_account_locks(self.get_transaction_account_lock_limit()) + .map(|_| ()); let mut batch = TransactionBatch::new(vec![lock_result], self, Cow::Owned(vec![transaction])); batch.set_needs_unlock(false); @@ -7034,12 +7021,7 @@ pub(crate) mod tests { system_program, sysvar::rewards::Rewards, timing::duration_as_s, -<<<<<<< HEAD - transaction::MAX_TX_ACCOUNT_LOCKS, transaction_context::InstructionContext, -======= - transaction_context::IndexOfAccount, ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) }, solana_vote_program::{ vote_instruction, diff --git a/sdk/bpf/c/inc/sol/cpi.h b/sdk/bpf/c/inc/sol/cpi.h index 5061fadcb8c958..2cb11e9df9e51f 100644 --- a/sdk/bpf/c/inc/sol/cpi.h +++ b/sdk/bpf/c/inc/sol/cpi.h @@ -12,31 +12,6 @@ extern "C" { #endif /** -<<<<<<< HEAD -======= - * Maximum CPI instruction data size. 10 KiB was chosen to ensure that CPI - * instructions are not more limited than transaction instructions if the size - * of transactions is doubled in the future. - */ -static const uint64_t MAX_CPI_INSTRUCTION_DATA_LEN = 10240; - -/** - * Maximum CPI instruction accounts. 255 was chosen to ensure that instruction - * accounts are always within the maximum instruction account limit for BPF - * program instructions. - */ -static const uint8_t MAX_CPI_INSTRUCTION_ACCOUNTS = 255; - -/** - * Maximum number of account info structs that can be used in a single CPI - * invocation. A limit on account info structs is effectively the same as - * limiting the number of unique accounts. 128 was chosen to match the max - * number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). - */ -static const uint16_t MAX_CPI_ACCOUNT_INFOS = 128; - -/** ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) * Account Meta */ typedef struct { diff --git a/sdk/bpf/c/inc/sol/inc/cpi.inc b/sdk/bpf/c/inc/sol/inc/cpi.inc deleted file mode 100644 index 41ce4fb01a691b..00000000000000 --- a/sdk/bpf/c/inc/sol/inc/cpi.inc +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once -/** - * @brief Solana Cross-Program Invocation - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Maximum CPI instruction data size. 10 KiB was chosen to ensure that CPI - * instructions are not more limited than transaction instructions if the size - * of transactions is doubled in the future. - */ -static const uint64_t MAX_CPI_INSTRUCTION_DATA_LEN = 10240; - -/** - * Maximum CPI instruction accounts. 255 was chosen to ensure that instruction - * accounts are always within the maximum instruction account limit for BPF - * program instructions. - */ -static const uint8_t MAX_CPI_INSTRUCTION_ACCOUNTS = 255; - -/** - * Maximum number of account info structs that can be used in a single CPI - * invocation. A limit on account info structs is effectively the same as - * limiting the number of unique accounts. 128 was chosen to match the max - * number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). - */ -static const uint16_t MAX_CPI_ACCOUNT_INFOS = 128; - -/** - * Account Meta - */ -typedef struct { - SolPubkey *pubkey; /** An account's public key */ - bool is_writable; /** True if the `pubkey` can be loaded as a read-write account */ - bool is_signer; /** True if an Instruction requires a Transaction signature matching `pubkey` */ -} SolAccountMeta; - -/** - * Instruction - */ -typedef struct { - SolPubkey *program_id; /** Pubkey of the instruction processor that executes this instruction */ - SolAccountMeta *accounts; /** Metadata for what accounts should be passed to the instruction processor */ - uint64_t account_len; /** Number of SolAccountMetas */ - uint8_t *data; /** Opaque data passed to the instruction processor */ - uint64_t data_len; /** Length of the data in bytes */ -} SolInstruction; - -/** - * Internal cross-program invocation function - */ -@SYSCALL uint64_t sol_invoke_signed_c( - const SolInstruction *, - const SolAccountInfo *, - int, - const SolSignerSeeds *, - int -); - -/** - * Invoke another program and sign for some of the keys - * - * @param instruction Instruction to process - * @param account_infos Accounts used by instruction - * @param account_infos_len Length of account_infos array - * @param seeds Seed bytes used to sign program accounts - * @param seeds_len Length of the seeds array - */ -static uint64_t sol_invoke_signed( - const SolInstruction *instruction, - const SolAccountInfo *account_infos, - int account_infos_len, - const SolSignerSeeds *signers_seeds, - int signers_seeds_len -) { - return sol_invoke_signed_c( - instruction, - account_infos, - account_infos_len, - signers_seeds, - signers_seeds_len - ); -} -/** - * Invoke another program - * - * @param instruction Instruction to process - * @param account_infos Accounts used by instruction - * @param account_infos_len Length of account_infos array -*/ -static uint64_t sol_invoke( - const SolInstruction *instruction, - const SolAccountInfo *account_infos, - int account_infos_len -) { - const SolSignerSeeds signers_seeds[] = {{}}; - return sol_invoke_signed( - instruction, - account_infos, - account_infos_len, - signers_seeds, - 0 - ); -} - -#ifdef __cplusplus -} -#endif - -/**@}*/ diff --git a/sdk/program/src/syscalls/mod.rs b/sdk/program/src/syscalls/mod.rs deleted file mode 100644 index d66c9361e95792..00000000000000 --- a/sdk/program/src/syscalls/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[cfg(target_os = "solana")] -mod definitions; - -#[cfg(target_os = "solana")] -pub use definitions::*; - -/// Maximum CPI instruction data size. 10 KiB was chosen to ensure that CPI -/// instructions are not more limited than transaction instructions if the size -/// of transactions is doubled in the future. -pub const MAX_CPI_INSTRUCTION_DATA_LEN: u64 = 10 * 1024; - -/// Maximum CPI instruction accounts. 255 was chosen to ensure that instruction -/// accounts are always within the maximum instruction account limit for BPF -/// program instructions. -pub const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX; - -/// Maximum number of account info structs that can be used in a single CPI -/// invocation. A limit on account info structs is effectively the same as -/// limiting the number of unique accounts. 128 was chosen to match the max -/// number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). -pub const MAX_CPI_ACCOUNT_INFOS: usize = 128; diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 1f8956f84e0496..01e8203943aa9b 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -423,21 +423,10 @@ pub mod return_none_for_zero_lamport_accounts { solana_sdk::declare_id!("7K5HFrS1WAq6ND7RQbShXZXbtAookyTfaDQPTJNuZpze"); } -<<<<<<< HEAD -======= -pub mod epoch_accounts_hash { - solana_sdk::declare_id!("5GpmAKxaGsWWbPp4bNXFLJxZVvG92ctxf7jQnzTQjF3n"); -} - -pub mod remove_deprecated_request_unit_ix { - solana_sdk::declare_id!("EfhYd3SafzGT472tYQDUc4dPd2xdEfKs5fwkowUgVt4W"); -} - pub mod increase_tx_account_lock_limit { solana_sdk::declare_id!("9LZdXeKGeBV6hRLdxS1rHbHoEUsKqesCC2ZAPTPKJAbK"); } ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -538,12 +527,7 @@ lazy_static! { (sign_repair_requests::id(), "sign repair requests #26834"), (check_ping_ancestor_requests::id(), "ancestor hash repair socket ping/pong support #26963"), (return_none_for_zero_lamport_accounts::id(), "return none for zero lamport accounts #27800"), -<<<<<<< HEAD -======= - (epoch_accounts_hash::id(), "enable epoch accounts hash calculation #27539"), - (remove_deprecated_request_unit_ix::id(), "remove support for RequestUnitsDeprecated instruction #27500"), (increase_tx_account_lock_limit::id(), "increase tx account lock limit to 128 #27241"), ->>>>>>> b9700244b5 (Increase transaction account lock limit from 64 to 128 (#27242)) /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/sdk/src/transaction/sanitized.rs b/sdk/src/transaction/sanitized.rs index 96de379a1fecb1..28136fdc79498a 100644 --- a/sdk/src/transaction/sanitized.rs +++ b/sdk/src/transaction/sanitized.rs @@ -210,13 +210,11 @@ impl SanitizedTransaction { /// Validate and return the account keys locked by this transaction pub fn get_account_locks( &self, - feature_set: &feature_set::FeatureSet, + tx_account_lock_limit: usize, ) -> Result { if self.message.has_duplicates() { Err(TransactionError::AccountLoadedTwice) - } else if feature_set.is_active(&feature_set::max_tx_account_locks::id()) - && self.message.account_keys().len() > MAX_TX_ACCOUNT_LOCKS - { + } else if self.message.account_keys().len() > tx_account_lock_limit { Err(TransactionError::TooManyAccountLocks) } else { Ok(self.get_account_locks_unchecked())