diff --git a/programs/bpf/c/src/invoked/invoked.c b/programs/bpf/c/src/invoked/invoked.c index 73c1a9e398..4da30a9add 100644 --- a/programs/bpf/c/src/invoked/invoked.c +++ b/programs/bpf/c/src/invoked/invoked.c @@ -127,9 +127,9 @@ extern uint64_t entrypoint(const uint8_t *input) { const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}, {seeds2, SOL_ARRAY_SIZE(seeds2)}}; - sol_assert(SUCCESS == sol_invoke_signed( - &instruction, accounts, SOL_ARRAY_SIZE(accounts), - signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); + sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts, + params.ka_num, signers_seeds, + SOL_ARRAY_SIZE(signers_seeds))); break; } @@ -189,8 +189,7 @@ extern uint64_t entrypoint(const uint8_t *input) { data, SOL_ARRAY_SIZE(data)}; sol_log("Invoke again"); - sol_assert(SUCCESS == - sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + sol_assert(SUCCESS == sol_invoke(&instruction, accounts, params.ka_num)); } else { sol_log("Last invoked"); for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) { diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 6bc3f2f1fd..a526ea8fdf 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -804,7 +804,7 @@ fn test_program_bpf_invoke() { .iter() .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) .collect(); - assert_eq!(invoked_programs, vec![argument_keypair.pubkey().clone()]); + assert_eq!(invoked_programs, vec![]); assert_eq!( result.unwrap_err(), TransactionError::InstructionError(0, InstructionError::AccountNotExecutable) diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 2f74aff038..2def312bae 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -19,7 +19,8 @@ use solana_sdk::{ feature_set::{ abort_on_all_cpi_failures, limit_cpi_loader_invoke, pubkey_log_syscall_enabled, ristretto_mul_syscall_enabled, sha256_syscall_enabled, sol_log_compute_units_syscall, - try_find_program_address_syscall_enabled, use_loaded_program_accounts, + try_find_program_address_syscall_enabled, use_loaded_executables, + use_loaded_program_accounts, }, hash::{Hasher, HASH_BYTES}, instruction::{AccountMeta, Instruction, InstructionError}, @@ -31,7 +32,7 @@ use solana_sdk::{ }; use std::{ alloc::Layout, - cell::{RefCell, RefMut}, + cell::{Ref, RefCell, RefMut}, convert::TryFrom, mem::{align_of, size_of}, rc::Rc, @@ -834,6 +835,7 @@ struct AccountReferences<'a> { ref_to_len_in_vm: &'a mut u64, serialized_len_ptr: &'a mut u64, } +type TranslatedAccount<'a> = (Rc>, Option>); type TranslatedAccounts<'a> = ( Vec>>, Vec>>, @@ -842,16 +844,15 @@ type TranslatedAccounts<'a> = ( /// Implemented by language specific data structure translators trait SyscallInvokeSigned<'a> { fn get_context_mut(&self) -> Result, EbpfError>; + fn get_context(&self) -> Result, EbpfError>; fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>]; fn translate_instruction( &self, addr: u64, - max_size: usize, memory_mapping: &MemoryMapping, ) -> Result>; fn translate_accounts( &self, - skip_program: bool, account_keys: &[Pubkey], program_account_index: usize, account_infos_addr: u64, @@ -879,17 +880,29 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { .try_borrow_mut() .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } + fn get_context(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { self.callers_keyed_accounts } fn translate_instruction( &self, addr: u64, - max_size: usize, memory_mapping: &MemoryMapping, ) -> Result> { let ix = translate_type::(memory_mapping, addr, self.loader_id)?; - check_instruction_size(ix.accounts.len(), ix.data.len(), max_size)?; + + check_instruction_size( + ix.accounts.len(), + ix.data.len(), + self.invoke_context + .borrow() + .get_bpf_compute_budget() + .max_cpi_instruction_size, + )?; let accounts = translate_slice::( memory_mapping, @@ -914,108 +927,110 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { fn translate_accounts( &self, - skip_program: bool, account_keys: &[Pubkey], program_account_index: usize, account_infos_addr: u64, account_infos_len: u64, memory_mapping: &MemoryMapping, ) -> Result, EbpfError> { - let account_infos = if account_infos_len > 0 { - translate_slice::( - memory_mapping, - account_infos_addr, - account_infos_len, - self.loader_id, - )? - } else { - &[] - }; + let invoke_context = self.invoke_context.borrow(); - let mut accounts = Vec::with_capacity(account_keys.len()); - let mut refs = Vec::with_capacity(account_keys.len()); - 'root: for (i, account_key) in account_keys.iter().enumerate() { - if skip_program && i == program_account_index { - // Don't look for caller passed executable, runtime already has it - continue 'root; - } - for account_info in account_infos.iter() { - let key = translate_type::( + let account_infos = translate_slice::( + memory_mapping, + account_infos_addr, + account_infos_len, + self.loader_id, + )?; + 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, self.loader_id, + ) + }) + .collect::, EbpfError>>()?; + + let translate = |account_info: &AccountInfo| { + // 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, + self.loader_id, )?; - if account_key == key { - let lamports = { - // Double translate lamports out of RefCell - let ptr = translate_type::( - memory_mapping, - account_info.lamports.as_ptr() as u64, - self.loader_id, - )?; - translate_type_mut::(memory_mapping, *ptr, self.loader_id)? - }; - let owner = translate_type_mut::( + translate_type_mut::(memory_mapping, *ptr, self.loader_id)? + }; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner as *const _ as u64, + self.loader_id, + )?; + 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, + self.loader_id, + )?; + let translated = translate( + memory_mapping, + AccessType::Store, + unsafe { (account_info.data.as_ptr() as *const u64).offset(1) as u64 }, + 8, + )? as *mut u64; + let ref_to_len_in_vm = unsafe { &mut *translated }; + let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) }; + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer as *const _ as u64, + self.loader_id, + )?; + let vm_data_addr = data.as_ptr() as u64; + ( + translate_slice_mut::( memory_mapping, - account_info.owner as *const _ as u64, - self.loader_id, - )?; - 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, - self.loader_id, - )?; - let translated = translate( - memory_mapping, - AccessType::Store, - unsafe { (account_info.data.as_ptr() as *const u64).offset(1) as u64 }, - 8, - )? as *mut u64; - let ref_to_len_in_vm = unsafe { &mut *translated }; - let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) }; - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - ref_of_len_in_input_buffer as *const _ as u64, - self.loader_id, - )?; - let vm_data_addr = data.as_ptr() as u64; - ( - translate_slice_mut::( - memory_mapping, - vm_data_addr, - data.len() as u64, - self.loader_id, - )?, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - ) - }; - - accounts.push(Rc::new(RefCell::new(Account { - lamports: *lamports, - data: data.to_vec(), - executable: account_info.executable, - owner: *owner, - rent_epoch: account_info.rent_epoch, - }))); - refs.push(Some(AccountReferences { - lamports, - owner, - data, vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - })); - continue 'root; - } - } - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); - } + data.len() as u64, + self.loader_id, + )?, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + ) + }; - Ok((accounts, refs)) + Ok(( + Rc::new(RefCell::new(Account { + lamports: *lamports, + data: data.to_vec(), + executable: account_info.executable, + owner: *owner, + rent_epoch: account_info.rent_epoch, + })), + Some(AccountReferences { + lamports, + owner, + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + }), + )) + }; + + get_translated_accounts( + account_keys, + program_account_index, + &account_info_keys, + account_infos, + &invoke_context, + translate, + ) } fn translate_signers( @@ -1151,6 +1166,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { .try_borrow_mut() .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } + fn get_context(&self) -> Result, EbpfError> { + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) + } fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { self.callers_keyed_accounts @@ -1159,11 +1179,18 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { fn translate_instruction( &self, addr: u64, - max_size: usize, memory_mapping: &MemoryMapping, ) -> Result> { let ix_c = translate_type::(memory_mapping, addr, self.loader_id)?; - check_instruction_size(ix_c.accounts_len, ix_c.data_len, max_size)?; + + check_instruction_size( + ix_c.accounts_len, + ix_c.data_len, + self.invoke_context + .borrow() + .get_bpf_compute_budget() + .max_cpi_instruction_size, + )?; let program_id = translate_type::(memory_mapping, ix_c.program_id_addr, self.loader_id)?; let meta_cs = translate_slice::( @@ -1201,92 +1228,95 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { fn translate_accounts( &self, - skip_program: bool, account_keys: &[Pubkey], program_account_index: usize, account_infos_addr: u64, account_infos_len: u64, memory_mapping: &MemoryMapping, ) -> Result, EbpfError> { + let invoke_context = self.invoke_context.borrow(); + let account_infos = translate_slice::( memory_mapping, account_infos_addr, account_infos_len, self.loader_id, )?; - let mut accounts = Vec::with_capacity(account_keys.len()); - let mut refs = Vec::with_capacity(account_keys.len()); - 'root: for (i, account_key) in account_keys.iter().enumerate() { - if skip_program && i == program_account_index { - // Don't look for caller passed executable, runtime already has it - continue 'root; - } - for account_info in account_infos.iter() { - let key = translate_type::( - memory_mapping, - account_info.key_addr, - self.loader_id, - )?; - if account_key == key { - let lamports = translate_type_mut::( - memory_mapping, - account_info.lamports_addr, - self.loader_id, - )?; - let owner = translate_type_mut::( - memory_mapping, - account_info.owner_addr, - self.loader_id, - )?; - let vm_data_addr = account_info.data_addr; - let data = translate_slice_mut::( - memory_mapping, - vm_data_addr, - account_info.data_len, - self.loader_id, - )?; + 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, self.loader_id) + }) + .collect::, EbpfError>>()?; - let first_info_addr = &account_infos[0] as *const _ as u64; - let addr = &account_info.data_len as *const u64 as u64; - let vm_addr = 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 translate = |account_info: &SolAccountInfo| { + // Translate the account from user space - let ref_of_len_in_input_buffer = - unsafe { (account_info.data_addr as *mut u8).offset(-8) }; - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - ref_of_len_in_input_buffer as *const _ as u64, - self.loader_id, - )?; + let lamports = translate_type_mut::( + memory_mapping, + account_info.lamports_addr, + self.loader_id, + )?; + let owner = translate_type_mut::( + memory_mapping, + account_info.owner_addr, + self.loader_id, + )?; + let vm_data_addr = account_info.data_addr; + let data = translate_slice_mut::( + memory_mapping, + vm_data_addr, + account_info.data_len, + self.loader_id, + )?; - accounts.push(Rc::new(RefCell::new(Account { - lamports: *lamports, - data: data.to_vec(), - executable: account_info.executable, - owner: *owner, - rent_epoch: account_info.rent_epoch, - }))); - refs.push(Some(AccountReferences { - lamports, - owner, - data, - vm_data_addr, - ref_to_len_in_vm, - serialized_len_ptr, - })); - continue 'root; - } - } - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); - } + let first_info_addr = &account_infos[0] as *const _ as u64; + let addr = &account_info.data_len as *const u64 as u64; + let vm_addr = 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((accounts, refs)) + let ref_of_len_in_input_buffer = + unsafe { (account_info.data_addr as *mut u8).offset(-8) }; + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer as *const _ as u64, + self.loader_id, + )?; + + Ok(( + Rc::new(RefCell::new(Account { + lamports: *lamports, + data: data.to_vec(), + executable: account_info.executable, + owner: *owner, + rent_epoch: account_info.rent_epoch, + })), + Some(AccountReferences { + lamports, + owner, + data, + vm_data_addr, + ref_to_len_in_vm, + serialized_len_ptr, + }), + )) + }; + + get_translated_accounts( + account_keys, + program_account_index, + &account_info_keys, + account_infos, + &invoke_context, + translate, + ) } fn translate_signers( @@ -1364,6 +1394,58 @@ impl<'a> SyscallObject for SyscallInvokeSignedC<'a> { } } +fn get_translated_accounts<'a, T, F>( + account_keys: &[Pubkey], + program_account_index: usize, + account_info_keys: &[&Pubkey], + account_infos: &[T], + invoke_context: &Ref<&mut dyn InvokeContext>, + do_translate: F, +) -> Result, EbpfError> +where + F: Fn(&T) -> Result, EbpfError>, +{ + let mut accounts = Vec::with_capacity(account_keys.len()); + let mut refs = Vec::with_capacity(account_keys.len()); + for (i, ref account_key) in account_keys.iter().enumerate() { + let account = + invoke_context + .get_account(&account_key) + .ok_or(SyscallError::InstructionError( + InstructionError::MissingAccount, + ))?; + + if (invoke_context.is_feature_active(&use_loaded_program_accounts::id()) + && i == program_account_index) + || (invoke_context.is_feature_active(&use_loaded_executables::id()) + && account.borrow().executable) + { + // Use the known executable + accounts.push(Rc::new(account)); + refs.push(None); + } else if let Some(account_info) = + account_info_keys + .iter() + .zip(account_infos) + .find_map(|(key, account_info)| { + if key == account_key { + Some(account_info) + } else { + None + } + }) + { + let (account, account_ref) = do_translate(account_info)?; + accounts.push(account); + refs.push(account_ref); + } else { + return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); + } + } + + Ok((accounts, refs)) +} + fn check_instruction_size( num_accounts: usize, data_len: usize, @@ -1377,6 +1459,25 @@ fn check_instruction_size( Ok(()) } +fn check_account_infos( + len: usize, + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result<(), EbpfError> { + if len * size_of::() + > invoke_context + .get_bpf_compute_budget() + .max_cpi_instruction_size + && invoke_context.is_feature_active(&use_loaded_program_accounts::id()) + { + // 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::InstructionError(InstructionError::ComputationalBudgetExceeded).into(), + ); + }; + Ok(()) +} + fn check_authorized_program( program_id: &Pubkey, instruction_data: &[u8], @@ -1392,6 +1493,31 @@ fn check_authorized_program( Ok(()) } +fn get_upgradeable_executable( + program_account: &RefCell, + invoke_context: &Ref<&mut dyn InvokeContext>, +) -> Result)>, EbpfError> { + if program_account.borrow().owner == bpf_loader_upgradeable::id() { + if let UpgradeableLoaderState::Program { + programdata_address, + } = program_account + .borrow() + .state() + .map_err(SyscallError::InstructionError)? + { + if let Some(account) = invoke_context.get_account(&programdata_address) { + Ok(Some((programdata_address, account))) + } else { + Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()) + } + } else { + Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()) + } + } else { + Ok(None) + } +} + /// Call process instruction, common to both Rust and C fn call<'a>( syscall: &mut dyn SyscallInvokeSigned<'a>, @@ -1402,109 +1528,84 @@ fn call<'a>( signers_seeds_len: u64, memory_mapping: &MemoryMapping, ) -> Result> { - let mut invoke_context = syscall.get_context_mut()?; - invoke_context - .get_compute_meter() - .consume(invoke_context.get_bpf_compute_budget().invoke_units)?; + let (message, executables, accounts, account_refs, abort_on_all_cpi_failures) = { + let invoke_context = syscall.get_context()?; - let use_loaded_program_accounts = - invoke_context.is_feature_active(&use_loaded_program_accounts::id()); - - // Translate and verify caller's data - - let instruction = syscall.translate_instruction( - instruction_addr, invoke_context - .get_bpf_compute_budget() - .max_cpi_instruction_size, - &memory_mapping, - )?; - let caller_program_id = invoke_context - .get_caller() - .map_err(SyscallError::InstructionError)?; - let signers = syscall.translate_signers( - caller_program_id, - signers_seeds_addr, - signers_seeds_len, - memory_mapping, - )?; - let keyed_account_refs = syscall - .get_callers_keyed_accounts() - .iter() - .collect::>(); - let (message, callee_program_id, callee_program_id_index) = - MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers) + .get_compute_meter() + .consume(invoke_context.get_bpf_compute_budget().invoke_units)?; + + let caller_program_id = invoke_context + .get_caller() .map_err(SyscallError::InstructionError)?; - if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) { - check_authorized_program(&callee_program_id, &instruction.data)?; - } - let (mut accounts, mut account_refs) = syscall.translate_accounts( - use_loaded_program_accounts, - &message.account_keys, - callee_program_id_index, - account_infos_addr, - account_infos_len, - memory_mapping, - )?; + + // Translate and verify caller's data + + let instruction = syscall.translate_instruction(instruction_addr, &memory_mapping)?; + let signers = syscall.translate_signers( + caller_program_id, + signers_seeds_addr, + signers_seeds_len, + memory_mapping, + )?; + let keyed_account_refs = syscall + .get_callers_keyed_accounts() + .iter() + .collect::>(); + let (message, callee_program_id, callee_program_id_index) = + MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers) + .map_err(SyscallError::InstructionError)?; + if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) { + check_authorized_program(&callee_program_id, &instruction.data)?; + } + let (accounts, account_refs) = syscall.translate_accounts( + &message.account_keys, + callee_program_id_index, + account_infos_addr, + account_infos_len, + memory_mapping, + )?; + + // Construct executables + + let program_account = + (**accounts + .get(callee_program_id_index) + .ok_or(SyscallError::InstructionError( + InstructionError::MissingAccount, + ))?) + .clone(); + let programdata_executable = get_upgradeable_executable(&program_account, &invoke_context)?; + let mut executables = vec![(callee_program_id, program_account)]; + if let Some(executable) = programdata_executable { + executables.push(executable); + } + + // Record the instruction + + invoke_context.record_instruction(&instruction); + + ( + message, + executables, + accounts, + account_refs, + invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()), + ) + }; // Process instruction - invoke_context.record_instruction(&instruction); - - let program_account = if use_loaded_program_accounts { - let program_account = invoke_context.get_account(&callee_program_id).ok_or( - SyscallError::InstructionError(InstructionError::MissingAccount), - )?; - accounts.insert(callee_program_id_index, Rc::new(program_account.clone())); - account_refs.insert(callee_program_id_index, None); - program_account - } else { - (**accounts - .get(callee_program_id_index) - .ok_or(SyscallError::InstructionError( - InstructionError::MissingAccount, - ))?) - .clone() - }; - if !program_account.borrow().executable { - return Err(SyscallError::InstructionError(InstructionError::AccountNotExecutable).into()); - } - let programdata_executable = if program_account.borrow().owner == bpf_loader_upgradeable::id() { - if let UpgradeableLoaderState::Program { - programdata_address, - } = program_account - .borrow() - .state() - .map_err(SyscallError::InstructionError)? - { - if let Some(account) = invoke_context.get_account(&programdata_address) { - Some((programdata_address, account)) - } else { - return Err( - SyscallError::InstructionError(InstructionError::MissingAccount).into(), - ); - } - } else { - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); - } - } else { - None - }; - let mut executable_accounts = vec![(callee_program_id, program_account)]; - if let Some(programdata) = programdata_executable { - executable_accounts.push(programdata); - } - #[allow(clippy::deref_addrof)] match MessageProcessor::process_cross_program_instruction( &message, - &executable_accounts, + &executables, &accounts, - *(&mut *invoke_context), + *(&mut *(syscall.get_context_mut()?)), ) { Ok(()) => (), Err(err) => { - if invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()) { + if abort_on_all_cpi_failures { return Err(SyscallError::InstructionError(err).into()); } else { match ProgramError::try_from(err) { @@ -2118,7 +2219,7 @@ mod tests { addr: 8192, len: bytes2.len(), }; - let bytes_to_hash = [mock_slice1, mock_slice2]; // TODO + let bytes_to_hash = [mock_slice1, mock_slice2]; let hash_result = [0; HASH_BYTES]; let ro_len = bytes_to_hash.len() as u64; let ro_va = 96; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index fa64e23a12..e402ef5230 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -541,18 +541,19 @@ impl MessageProcessor { } } - // validate the caller has access to the program account + // validate the caller has access to the program account and that it is executable let program_id = instruction.program_id; - let _ = keyed_accounts + match keyed_accounts .iter() - .find_map(|keyed_account| { - if &program_id == keyed_account.unsigned_key() { - Some(keyed_account) - } else { - None + .find(|keyed_account| &program_id == keyed_account.unsigned_key()) + { + Some(keyed_account) => { + if !keyed_account.executable()? { + return Err(InstructionError::AccountNotExecutable); } - }) - .ok_or(InstructionError::MissingAccount)?; + } + None => return Err(InstructionError::MissingAccount), + } let message = Message::new(&[instruction.clone()], None); let program_id_index = message.instructions[0].program_id_index as usize; diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 191a7dc6d8..cc1bccdea9 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -134,6 +134,10 @@ pub mod abort_on_all_cpi_failures { solana_sdk::declare_id!("ED5D5a2hQaECHaMmKpnU48GdsfafdCjkb3pgAw5RKbb2"); } +pub mod use_loaded_executables { + solana_sdk::declare_id!("2SLL2KLakB83YAnF1TwFb1hpycrWeHAfHYyLhwk2JRGn"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -169,6 +173,7 @@ lazy_static! { (limit_cpi_loader_invoke::id(), "Loader not authorized via CPI"), (use_loaded_program_accounts::id(), "Use loaded program accounts"), (abort_on_all_cpi_failures::id(), "Abort on all CPI failures"), + (use_loaded_executables::id(), "Use loaded executable accounts"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()