diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index c7b27e730f..0e511b82ab 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -7,7 +7,6 @@ use solana_sdk::{ feature_set::{demote_program_write_locks, fix_write_privs}, ic_logger_msg, ic_msg, instruction::{CompiledInstruction, Instruction, InstructionError}, - keyed_account::{keyed_account_at_index, KeyedAccount}, message::Message, process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext}, pubkey::Pubkey, @@ -15,7 +14,7 @@ use solana_sdk::{ system_program, }; use std::{ - cell::{Ref, RefCell}, + cell::{Ref, RefCell, RefMut}, collections::HashMap, rc::Rc, sync::Arc, @@ -375,13 +374,34 @@ impl InstructionProcessor { pub fn create_message( instruction: &Instruction, - keyed_accounts: &[&KeyedAccount], signers: &[Pubkey], - invoke_context: &Ref<&mut dyn InvokeContext>, - ) -> Result<(Message, Pubkey, usize), InstructionError> { + invoke_context: &RefMut<&mut dyn InvokeContext>, + ) -> Result<(Message, Vec, Vec), InstructionError> { + let message = Message::new(&[instruction.clone()], None); + + // Gather keyed_accounts in the order of message.account_keys + let caller_keyed_accounts = invoke_context.get_keyed_accounts()?; + let callee_keyed_accounts = message + .account_keys + .iter() + .map(|account_key| { + caller_keyed_accounts + .iter() + .find(|keyed_account| keyed_account.unsigned_key() == account_key) + .ok_or_else(|| { + ic_msg!( + *invoke_context, + "Instruction references an unknown account {}", + account_key + ); + InstructionError::MissingAccount + }) + }) + .collect::, InstructionError>>()?; + // Check for privilege escalation for account in instruction.accounts.iter() { - let keyed_account = keyed_accounts + let keyed_account = callee_keyed_accounts .iter() .find_map(|keyed_account| { if &account.pubkey == keyed_account.unsigned_key() { @@ -421,215 +441,118 @@ impl InstructionProcessor { return Err(InstructionError::PrivilegeEscalation); } } - - // validate the caller has access to the program account and that it is executable - let program_id = instruction.program_id; - match keyed_accounts + let caller_write_privileges = callee_keyed_accounts .iter() - .find(|keyed_account| &program_id == keyed_account.unsigned_key()) - { - Some(keyed_account) => { - if !keyed_account.executable()? { + .map(|keyed_account| keyed_account.is_writable()) + .collect::>(); + + // Find and validate executables / program accounts + let callee_program_id = instruction.program_id; + let (program_account_index, program_account) = callee_keyed_accounts + .iter() + .find(|keyed_account| &callee_program_id == keyed_account.unsigned_key()) + .and_then(|_keyed_account| invoke_context.get_account(&callee_program_id)) + .ok_or_else(|| { + ic_msg!(invoke_context, "Unknown program {}", callee_program_id); + InstructionError::MissingAccount + })?; + if !program_account.borrow().executable() { + ic_msg!( + invoke_context, + "Account {} is not executable", + callee_program_id + ); + return Err(InstructionError::AccountNotExecutable); + } + let mut program_indices = vec![program_account_index]; + if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { + if let UpgradeableLoaderState::Program { + programdata_address, + } = program_account.borrow().state()? + { + if let Some((programdata_account_index, _programdata_account)) = + invoke_context.get_account(&programdata_address) + { + program_indices.push(programdata_account_index); + } else { ic_msg!( invoke_context, - "Account {} is not executable", - keyed_account.unsigned_key() + "Unknown upgradeable programdata account {}", + programdata_address, ); - return Err(InstructionError::AccountNotExecutable); + return Err(InstructionError::MissingAccount); } - } - None => { - ic_msg!(invoke_context, "Unknown program {}", program_id); + } else { + ic_msg!( + invoke_context, + "Invalid upgradeable program account {}", + callee_program_id, + ); return Err(InstructionError::MissingAccount); } } - let message = Message::new(&[instruction.clone()], None); - let program_id_index = message.instructions[0].program_id_index as usize; - - Ok((message, program_id, program_id_index)) + Ok((message, caller_write_privileges, program_indices)) } /// Entrypoint for a cross-program invocation from a native program pub fn native_invoke( invoke_context: &mut dyn InvokeContext, instruction: Instruction, - keyed_account_indices: &[usize], + keyed_account_indices_obsolete: &[usize], signers: &[Pubkey], ) -> Result<(), InstructionError> { let invoke_context = RefCell::new(invoke_context); + let mut invoke_context = invoke_context.borrow_mut(); - let ( - message, - program_indices, - accounts, - keyed_account_indices_reordered, - caller_write_privileges, - ) = { - let invoke_context = invoke_context.borrow(); - + // Translate and verify caller's data + let (message, mut caller_write_privileges, program_indices) = + Self::create_message(&instruction, signers, &invoke_context)?; + if !invoke_context.is_feature_active(&fix_write_privs::id()) { let caller_keyed_accounts = invoke_context.get_keyed_accounts()?; - let callee_keyed_accounts = keyed_account_indices - .iter() - .map(|index| keyed_account_at_index(caller_keyed_accounts, *index)) - .collect::, InstructionError>>()?; - let (message, callee_program_id, _) = Self::create_message( - &instruction, - &callee_keyed_accounts, - signers, - &invoke_context, - )?; - let mut keyed_account_indices_reordered = - Vec::with_capacity(message.account_keys.len()); - let mut accounts = Vec::with_capacity(message.account_keys.len()); - let mut caller_write_privileges = Vec::with_capacity(message.account_keys.len()); - - // Translate and verify caller's data - if invoke_context.is_feature_active(&fix_write_privs::id()) { - 'root: for account_key in message.account_keys.iter() { - for keyed_account_index in keyed_account_indices { - let keyed_account = &caller_keyed_accounts[*keyed_account_index]; - if account_key == keyed_account.unsigned_key() { - accounts.push((*account_key, Rc::new(keyed_account.account.clone()))); - caller_write_privileges.push(keyed_account.is_writable()); - keyed_account_indices_reordered.push(*keyed_account_index); - continue 'root; - } - } - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - return Err(InstructionError::MissingAccount); - } - } else { - let keyed_accounts = invoke_context.get_keyed_accounts()?; - for index in keyed_account_indices.iter() { - caller_write_privileges.push(keyed_accounts[*index].is_writable()); - } - caller_write_privileges.insert(0, false); - let keyed_accounts = invoke_context.get_keyed_accounts()?; - 'root2: for account_key in message.account_keys.iter() { - for keyed_account_index in keyed_account_indices { - let keyed_account = &keyed_accounts[*keyed_account_index]; - if account_key == keyed_account.unsigned_key() { - accounts.push((*account_key, Rc::new(keyed_account.account.clone()))); - keyed_account_indices_reordered.push(*keyed_account_index); - continue 'root2; - } - } - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - return Err(InstructionError::MissingAccount); - } + caller_write_privileges = Vec::with_capacity(1 + keyed_account_indices_obsolete.len()); + caller_write_privileges.push(false); + for index in keyed_account_indices_obsolete.iter() { + caller_write_privileges.push(caller_keyed_accounts[*index].is_writable()); } - - // Process instruction - - invoke_context.record_instruction(&instruction); - - let (program_account_index, program_account) = invoke_context - .get_account(&callee_program_id) - .ok_or_else(|| { - ic_msg!(invoke_context, "Unknown program {}", callee_program_id); - InstructionError::MissingAccount - })?; - if !program_account.borrow().executable() { - ic_msg!( - invoke_context, - "Account {} is not executable", - callee_program_id - ); - return Err(InstructionError::AccountNotExecutable); - } - let mut program_indices = vec![]; - if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { - if let UpgradeableLoaderState::Program { - programdata_address, - } = program_account.borrow().state()? - { - if let Some((programdata_account_index, _programdata_account)) = - invoke_context.get_account(&programdata_address) - { - program_indices.push(programdata_account_index); - } else { - ic_msg!( - invoke_context, - "Unknown upgradeable programdata account {}", - programdata_address, - ); - return Err(InstructionError::MissingAccount); - } - } else { - ic_msg!( - invoke_context, - "Upgradeable program account state not valid {}", - callee_program_id, - ); - return Err(InstructionError::MissingAccount); - } - } - program_indices.insert(0, program_account_index); - ( - message, - program_indices, - accounts, - keyed_account_indices_reordered, - caller_write_privileges, - ) }; + let accounts = message + .account_keys + .iter() + .map(|account_key| { + invoke_context + .get_account(account_key) + .ok_or(InstructionError::MissingAccount) + .map(|(_account_index, account)| (*account_key, account)) + }) + .collect::, InstructionError>>()?; + let account_sizes = accounts + .iter() + .map(|(_key, account)| account.borrow().data().len()) + .collect::>(); - #[allow(clippy::deref_addrof)] + // Record the instruction + invoke_context.record_instruction(&instruction); + + // Process instruction InstructionProcessor::process_cross_program_instruction( &message, &program_indices, &accounts, &caller_write_privileges, - *(&mut *(invoke_context.borrow_mut())), + *invoke_context, )?; - // Copy results back to caller - - { - let invoke_context = invoke_context.borrow(); - let demote_program_write_locks = - invoke_context.is_feature_active(&demote_program_write_locks::id()); - let keyed_accounts = invoke_context.get_keyed_accounts()?; - for (src_keyed_account_index, ((_key, account), dst_keyed_account_index)) in accounts - .iter() - .zip(keyed_account_indices_reordered) - .enumerate() - { - let dst_keyed_account = &keyed_accounts[dst_keyed_account_index]; - let src_keyed_account = account.borrow(); - if message.is_writable(src_keyed_account_index, demote_program_write_locks) - && !src_keyed_account.executable() - { - if dst_keyed_account.data_len()? != src_keyed_account.data().len() - && dst_keyed_account.data_len()? != 0 - { - // Only support for `CreateAccount` at this time. - // Need a way to limit total realloc size across multiple CPI calls - ic_msg!( - invoke_context, - "Inner instructions do not support realloc, only SystemProgram::CreateAccount", - ); - return Err(InstructionError::InvalidRealloc); - } - dst_keyed_account - .try_account_ref_mut()? - .set_lamports(src_keyed_account.lamports()); - dst_keyed_account - .try_account_ref_mut()? - .set_owner(*src_keyed_account.owner()); - dst_keyed_account - .try_account_ref_mut()? - .set_data(src_keyed_account.data().to_vec()); - } + // Verify the called program has not misbehaved + for ((_key, account), prev_size) in accounts.iter().zip(account_sizes.iter()) { + if *prev_size != account.borrow().data().len() && *prev_size != 0 { + // Only support for `CreateAccount` at this time. + // Need a way to limit total realloc size across multiple CPI calls + ic_msg!( + invoke_context, + "Inner instructions do not support realloc, only SystemProgram::CreateAccount", + ); + return Err(InstructionError::InvalidRealloc); } } @@ -645,45 +568,46 @@ impl InstructionProcessor { caller_write_privileges: &[bool], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - if let Some(instruction) = message.instructions.get(0) { - let program_id = instruction.program_id(&message.account_keys); + // This function is always called with a valid instruction, if that changes return an error + let instruction = message + .instructions + .get(0) + .ok_or(InstructionError::GenericError)?; - // Verify the calling program hasn't misbehaved - invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?; + let program_id = instruction.program_id(&message.account_keys); - // clear the return data - invoke_context.set_return_data(None); + // Verify the calling program hasn't misbehaved + invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?; - // Invoke callee - invoke_context.push(program_id, message, instruction, program_indices, accounts)?; + // clear the return data + invoke_context.set_return_data(None); - let mut instruction_processor = InstructionProcessor::default(); - for (program_id, process_instruction) in invoke_context.get_programs().iter() { - instruction_processor.add_program(*program_id, *process_instruction); - } + // Invoke callee + invoke_context.push(program_id, message, instruction, program_indices, accounts)?; - let mut result = instruction_processor.process_instruction( - program_id, - &instruction.data, - invoke_context, - ); - if result.is_ok() { - // Verify the called program has not misbehaved - let demote_program_write_locks = - invoke_context.is_feature_active(&demote_program_write_locks::id()); - let write_privileges: Vec = (0..message.account_keys.len()) - .map(|i| message.is_writable(i, demote_program_write_locks)) - .collect(); - result = invoke_context.verify_and_update(instruction, accounts, &write_privileges); - } - - // Restore previous state - invoke_context.pop(); - result - } else { - // This function is always called with a valid instruction, if that changes return an error - Err(InstructionError::GenericError) + let mut instruction_processor = InstructionProcessor::default(); + for (program_id, process_instruction) in invoke_context.get_programs().iter() { + instruction_processor.add_program(*program_id, *process_instruction); } + + let mut result = instruction_processor.process_instruction( + program_id, + &instruction.data, + invoke_context, + ); + if result.is_ok() { + // Verify the called program has not misbehaved + let demote_program_write_locks = + invoke_context.is_feature_active(&demote_program_write_locks::id()); + let write_privileges: Vec = (0..message.account_keys.len()) + .map(|i| message.is_writable(i, demote_program_write_locks)) + .collect(); + result = invoke_context.verify_and_update(instruction, accounts, &write_privileges); + } + + // Restore previous state + invoke_context.pop(); + result } /// Verify the results of a cross-program instruction diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index d0a7601c27..980f1f433c 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -220,7 +220,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { // Serialize account data let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - let mut serialized = serialize_parameters( + let (mut serialized, _account_lengths) = serialize_parameters( &bpf_loader::id(), &solana_sdk::pubkey::new_rand(), keyed_accounts, diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index b73c284ebd..70b311da08 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -198,7 +198,7 @@ fn run_program( let mut data = vec![]; file.read_to_end(&mut data).unwrap(); let loader_id = bpf_loader::id(); - let parameter_bytes = serialize_parameters( + let (parameter_bytes, account_lengths) = serialize_parameters( &bpf_loader::id(), program_id, ¶meter_accounts, @@ -282,6 +282,7 @@ fn run_program( &bpf_loader::id(), parameter_accounts, parameter_bytes.as_slice(), + &account_lengths, ) .unwrap(); } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 5bceb64844..2a5278e2c4 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -888,7 +888,7 @@ impl Executor for BpfExecutor { let mut serialize_time = Measure::start("serialize"); let keyed_accounts = invoke_context.get_keyed_accounts()?; - let mut parameter_bytes = + let (mut parameter_bytes, account_lengths) = serialize_parameters(loader_id, program_id, keyed_accounts, instruction_data)?; serialize_time.stop(); let mut create_vm_time = Measure::start("create_vm"); @@ -971,7 +971,12 @@ impl Executor for BpfExecutor { } let mut deserialize_time = Measure::start("deserialize"); let keyed_accounts = invoke_context.get_keyed_accounts()?; - deserialize_parameters(loader_id, keyed_accounts, parameter_bytes.as_slice())?; + deserialize_parameters( + loader_id, + keyed_accounts, + parameter_bytes.as_slice(), + &account_lengths, + )?; deserialize_time.stop(); invoke_context.update_timing( serialize_time.as_us(), diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index 4f842abebb..0d5f222303 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -28,23 +28,31 @@ pub fn serialize_parameters( program_id: &Pubkey, keyed_accounts: &[KeyedAccount], data: &[u8], -) -> Result { +) -> Result<(AlignedMemory, Vec), InstructionError> { if *loader_id == bpf_loader_deprecated::id() { serialize_parameters_unaligned(program_id, keyed_accounts, data) } else { serialize_parameters_aligned(program_id, keyed_accounts, data) } + .and_then(|buffer| { + let account_lengths = keyed_accounts + .iter() + .map(|keyed_account| keyed_account.data_len()) + .collect::, InstructionError>>()?; + Ok((buffer, account_lengths)) + }) } pub fn deserialize_parameters( loader_id: &Pubkey, keyed_accounts: &[KeyedAccount], buffer: &[u8], + account_lengths: &[usize], ) -> Result<(), InstructionError> { if *loader_id == bpf_loader_deprecated::id() { - deserialize_parameters_unaligned(keyed_accounts, buffer) + deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths) } else { - deserialize_parameters_aligned(keyed_accounts, buffer) + deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths) } } @@ -126,9 +134,14 @@ pub fn serialize_parameters_unaligned( pub fn deserialize_parameters_unaligned( keyed_accounts: &[KeyedAccount], buffer: &[u8], + account_lengths: &[usize], ) -> Result<(), InstructionError> { let mut start = size_of::(); // number of accounts - for (i, keyed_account) in keyed_accounts.iter().enumerate() { + for (i, (keyed_account, _pre_len)) in keyed_accounts + .iter() + .zip(account_lengths.iter()) + .enumerate() + { let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); start += 1; // is_dup if !is_dup { @@ -247,9 +260,14 @@ pub fn serialize_parameters_aligned( pub fn deserialize_parameters_aligned( keyed_accounts: &[KeyedAccount], buffer: &[u8], + account_lengths: &[usize], ) -> Result<(), InstructionError> { let mut start = size_of::(); // number of accounts - for (i, keyed_account) in keyed_accounts.iter().enumerate() { + for (i, (keyed_account, pre_len)) in keyed_accounts + .iter() + .zip(account_lengths.iter()) + .enumerate() + { let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); start += size_of::(); // position if is_dup { @@ -265,18 +283,17 @@ pub fn deserialize_parameters_aligned( start += size_of::(); // owner account.set_lamports(LittleEndian::read_u64(&buffer[start..])); start += size_of::(); // lamports - let pre_len = account.data().len(); let post_len = LittleEndian::read_u64(&buffer[start..]) as usize; start += size_of::(); // data length - let mut data_end = start + pre_len; - if post_len != pre_len - && (post_len.saturating_sub(pre_len)) <= MAX_PERMITTED_DATA_INCREASE + let mut data_end = start + *pre_len; + if post_len != *pre_len + && (post_len.saturating_sub(*pre_len)) <= MAX_PERMITTED_DATA_INCREASE { data_end = start + post_len; } account.set_data_from_slice(&buffer[start..data_end]); - start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data + start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data start += (start as *const u8).align_offset(align_of::()); start += size_of::(); // rent_epoch } @@ -392,7 +409,7 @@ mod tests { // check serialize_parameters_aligned - let mut serialized = serialize_parameters( + let (mut serialized, account_lengths) = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, @@ -445,8 +462,13 @@ mod tests { } }) .collect(); - deserialize_parameters(&bpf_loader::id(), &de_keyed_accounts, serialized.as_slice()) - .unwrap(); + deserialize_parameters( + &bpf_loader::id(), + &de_keyed_accounts, + serialized.as_slice(), + &account_lengths, + ) + .unwrap(); for ((account, de_keyed_account), key) in accounts.iter().zip(de_keyed_accounts).zip(keys.clone()) { @@ -458,7 +480,7 @@ mod tests { // check serialize_parameters_unaligned - let mut serialized = serialize_parameters( + let (mut serialized, account_lengths) = serialize_parameters( &bpf_loader_deprecated::id(), &program_id, &keyed_accounts, @@ -497,6 +519,7 @@ mod tests { &bpf_loader_deprecated::id(), &de_keyed_accounts, serialized.as_slice(), + &account_lengths, ) .unwrap(); for ((account, de_keyed_account), key) in diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index d031bef673..cf03ea9fcc 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -12,11 +12,9 @@ use solana_rbpf::{ #[allow(deprecated)] use solana_sdk::sysvar::fees::Fees; use solana_sdk::{ - account::{Account, AccountSharedData, ReadableAccount}, + account::{AccountSharedData, ReadableAccount, WritableAccount}, account_info::AccountInfo, - account_utils::StateMut, - blake3, bpf_loader, bpf_loader_deprecated, - bpf_loader_upgradeable::{self, UpgradeableLoaderState}, + blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, clock::Clock, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, epoch_schedule::EpochSchedule, @@ -30,7 +28,7 @@ use solana_sdk::{ ic_msg, instruction::{AccountMeta, Instruction, InstructionError}, keccak, - keyed_account::KeyedAccount, + message::Message, native_loader, process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger}, program::MAX_RETURN_DATA, @@ -1511,11 +1509,9 @@ struct AccountReferences<'a> { vm_data_addr: u64, ref_to_len_in_vm: &'a mut u64, serialized_len_ptr: &'a mut u64, + executable: bool, + rent_epoch: u64, } -type TranslatedAccount<'a> = ( - Rc>, - Option>, -); type TranslatedAccounts<'a> = ( Vec<(Pubkey, Rc>)>, Vec>>, @@ -1529,14 +1525,15 @@ trait SyscallInvokeSigned<'a> { &self, addr: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> Result>; fn translate_accounts( &self, - account_keys: &[Pubkey], - program_account_index: usize, + message: &Message, account_infos_addr: u64, account_infos_len: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> Result, EbpfError>; fn translate_signers( &self, @@ -1567,14 +1564,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { &self, addr: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> Result> { let ix = translate_type::(memory_mapping, addr, self.loader_id)?; - check_instruction_size( - ix.accounts.len(), - ix.data.len(), - &self.invoke_context.borrow(), - )?; + check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?; let accounts = translate_slice::( memory_mapping, @@ -1599,21 +1593,19 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { fn translate_accounts( &self, - account_keys: &[Pubkey], - program_account_index: usize, + message: &Message, account_infos_addr: u64, account_infos_len: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> 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, )?; - check_account_infos(account_infos.len(), &invoke_context)?; + check_account_infos(account_infos.len(), invoke_context)?; let account_info_keys = account_infos .iter() .map(|account_info| { @@ -1625,8 +1617,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { }) .collect::, EbpfError>>()?; - let translate = |account_info: &AccountInfo, - invoke_context: &Ref<&mut dyn InvokeContext>| { + let translate = |account_info: &AccountInfo, invoke_context: &mut dyn InvokeContext| { // Translate the account from user space let lamports = { @@ -1683,31 +1674,23 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { ) }; - Ok(( - Rc::new(RefCell::new(AccountSharedData::from(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, - }), - )) + Ok(AccountReferences { + lamports, + owner, + 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( - account_keys, - program_account_index, + message, &account_info_keys, account_infos, - &invoke_context, + invoke_context, translate, ) } @@ -1854,14 +1837,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { &self, addr: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> Result> { let ix_c = translate_type::(memory_mapping, addr, self.loader_id)?; - check_instruction_size( - ix_c.accounts_len, - ix_c.data_len, - &self.invoke_context.borrow(), - )?; + check_instruction_size(ix_c.accounts_len, ix_c.data_len, invoke_context)?; let program_id = translate_type::(memory_mapping, ix_c.program_id_addr, self.loader_id)?; let meta_cs = translate_slice::( @@ -1899,21 +1879,19 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { fn translate_accounts( &self, - account_keys: &[Pubkey], - program_account_index: usize, + message: &Message, account_infos_addr: u64, account_infos_len: u64, memory_mapping: &MemoryMapping, + invoke_context: &mut dyn InvokeContext, ) -> 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, )?; - check_account_infos(account_infos.len(), &invoke_context)?; + check_account_infos(account_infos.len(), invoke_context)?; let account_info_keys = account_infos .iter() .map(|account_info| { @@ -1921,8 +1899,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { }) .collect::, EbpfError>>()?; - let translate = |account_info: &SolAccountInfo, - invoke_context: &Ref<&mut dyn InvokeContext>| { + let translate = |account_info: &SolAccountInfo, invoke_context: &mut dyn InvokeContext| { // Translate the account from user space let lamports = translate_type_mut::( @@ -1967,31 +1944,23 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { self.loader_id, )?; - Ok(( - Rc::new(RefCell::new(AccountSharedData::from(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, - }), - )) + Ok(AccountReferences { + lamports, + owner, + 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( - account_keys, - program_account_index, + message, &account_info_keys, account_infos, - &invoke_context, + invoke_context, translate, ) } @@ -2072,56 +2041,55 @@ impl<'a> SyscallObject for SyscallInvokeSignedC<'a> { } fn get_translated_accounts<'a, T, F>( - account_keys: &[Pubkey], - program_account_index: usize, + message: &Message, account_info_keys: &[&Pubkey], account_infos: &[T], - invoke_context: &Ref<&mut dyn InvokeContext>, + invoke_context: &mut dyn InvokeContext, do_translate: F, ) -> Result, EbpfError> where - F: Fn(&T, &Ref<&mut dyn InvokeContext>) -> Result, EbpfError>, + F: Fn(&T, &mut dyn InvokeContext) -> 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_index, account) = - invoke_context.get_account(account_key).ok_or_else(|| { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - SyscallError::InstructionError(InstructionError::MissingAccount) - })?; - - if i == program_account_index || account.borrow().executable() { - // Use the known account - accounts.push((**account_key, 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, invoke_context)?; - accounts.push((**account_key, account)); - refs.push(account_ref); - } else { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); + let demote_program_write_locks = + invoke_context.is_feature_active(&demote_program_write_locks::id()); + let mut accounts = Vec::with_capacity(message.account_keys.len()); + let mut refs = Vec::with_capacity(message.account_keys.len()); + for (i, account_key) in message.account_keys.iter().enumerate() { + if let Some((_account_index, account)) = invoke_context.get_account(account_key) { + if i == message.instructions[0].program_id_index as usize + || account.borrow().executable() + { + // Use the known account + accounts.push((*account_key, account)); + refs.push(None); + continue; + } else if let Some(account_ref_index) = + account_info_keys.iter().position(|key| *key == account_key) + { + let account_ref = do_translate(&account_infos[account_ref_index], invoke_context)?; + { + let mut account = account.borrow_mut(); + account.copy_into_owner_from_slice(account_ref.owner.as_ref()); + account.set_data_from_slice(account_ref.data); + account.set_lamports(*account_ref.lamports); + account.set_executable(account_ref.executable); + account.set_rent_epoch(account_ref.rent_epoch); + } + accounts.push((*account_key, account)); + refs.push(if message.is_writable(i, demote_program_write_locks) { + Some(account_ref) + } else { + None + }); + continue; + } } + ic_msg!( + invoke_context, + "Instruction references an unknown account {}", + account_key + ); + return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()); } Ok((accounts, refs)) @@ -2130,7 +2098,7 @@ where fn check_instruction_size( num_accounts: usize, data_len: usize, - invoke_context: &Ref<&mut dyn InvokeContext>, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), EbpfError> { let size = num_accounts .saturating_mul(size_of::()) @@ -2144,7 +2112,7 @@ fn check_instruction_size( fn check_account_infos( len: usize, - invoke_context: &Ref<&mut dyn InvokeContext>, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), EbpfError> { if len * size_of::() > invoke_context.get_compute_budget().max_cpi_instruction_size { // Cap the number of account_infos a caller can pass to approximate @@ -2173,44 +2141,6 @@ fn check_authorized_program( Ok(()) } -#[allow(clippy::type_complexity)] -fn get_upgradeable_executable( - callee_program_id: &Pubkey, - program_account: &Rc>, - invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result, EbpfError> { - if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { - match program_account.borrow().state() { - Ok(UpgradeableLoaderState::Program { - programdata_address, - }) => { - if let Some((programdata_account_index, _programdata_account)) = - invoke_context.get_account(&programdata_address) - { - Ok(Some(programdata_account_index)) - } else { - ic_msg!( - invoke_context, - "Unknown upgradeable programdata account {}", - programdata_address, - ); - Err(SyscallError::InstructionError(InstructionError::MissingAccount).into()) - } - } - _ => { - ic_msg!( - invoke_context, - "Invalid upgradeable program account {}", - callee_program_id, - ); - Err(SyscallError::InstructionError(InstructionError::InvalidAccountData).into()) - } - } - } else { - Ok(None) - } -} - /// Call process instruction, common to both Rust and C fn call<'a>( syscall: &mut dyn SyscallInvokeSigned<'a>, @@ -2221,174 +2151,92 @@ fn call<'a>( signers_seeds_len: u64, memory_mapping: &MemoryMapping, ) -> Result> { - let ( - message, - program_indices, - accounts, - account_refs, - caller_write_privileges, - demote_program_write_locks, - ) = { - let invoke_context = syscall.get_context()?; + let mut invoke_context = syscall.get_context_mut()?; + invoke_context + .get_compute_meter() + .consume(invoke_context.get_compute_budget().invoke_units)?; - invoke_context - .get_compute_meter() - .consume(invoke_context.get_compute_budget().invoke_units)?; - - let caller_program_id = invoke_context - .get_caller() + // Translate and verify caller's data + let instruction = + syscall.translate_instruction(instruction_addr, memory_mapping, *invoke_context)?; + 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 (message, caller_write_privileges, program_indices) = + InstructionProcessor::create_message(&instruction, &signers, &invoke_context) .map_err(SyscallError::InstructionError)?; + check_authorized_program( + &instruction.program_id, + &instruction.data, + invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()), + )?; + let (accounts, account_refs) = syscall.translate_accounts( + &message, + account_infos_addr, + account_infos_len, + memory_mapping, + *invoke_context, + )?; - // 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 = invoke_context - .get_keyed_accounts() - .map_err(SyscallError::InstructionError)? - .iter() - .collect::>(); - let (message, callee_program_id, callee_program_id_index) = - InstructionProcessor::create_message( - &instruction, - &keyed_account_refs, - &signers, - &invoke_context, - ) - .map_err(SyscallError::InstructionError)?; - let caller_write_privileges = message - .account_keys - .iter() - .map(|key| { - if let Some(keyed_account) = keyed_account_refs - .iter() - .find(|keyed_account| key == keyed_account.unsigned_key()) - { - keyed_account.is_writable() - } else { - false - } - }) - .collect::>(); - check_authorized_program( - &callee_program_id, - &instruction.data, - invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()), - )?; - 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_else(|| { - ic_msg!(invoke_context, "Unknown program {}", callee_program_id); - SyscallError::InstructionError(InstructionError::MissingAccount) - })? - .1 - .clone(); - let (program_account_index, _program_account) = - invoke_context.get_account(&callee_program_id).ok_or( - SyscallError::InstructionError(InstructionError::MissingAccount), - )?; - - let mut program_indices = vec![program_account_index]; - if let Some(programdata_account_index) = - get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)? - { - program_indices.push(programdata_account_index); - } - - // Record the instruction - - invoke_context.record_instruction(&instruction); - - ( - message, - program_indices, - accounts, - account_refs, - caller_write_privileges, - invoke_context.is_feature_active(&demote_program_write_locks::id()), - ) - }; + // Record the instruction + invoke_context.record_instruction(&instruction); // Process instruction - - #[allow(clippy::deref_addrof)] - match InstructionProcessor::process_cross_program_instruction( + InstructionProcessor::process_cross_program_instruction( &message, &program_indices, &accounts, &caller_write_privileges, - *(&mut *(syscall.get_context_mut()?)), - ) { - Ok(()) => (), - Err(err) => { - return Err(SyscallError::InstructionError(err).into()); - } - } + *invoke_context, + ) + .map_err(SyscallError::InstructionError)?; // Copy results back to caller - { - let invoke_context = syscall.get_context()?; - for (i, ((_key, account), account_ref)) in accounts.iter().zip(account_refs).enumerate() { - let account = account.borrow(); - if let Some(mut account_ref) = account_ref { - if message.is_writable(i, demote_program_write_locks) && !account.executable() { - *account_ref.lamports = account.lamports(); - *account_ref.owner = *account.owner(); - if account_ref.data.len() != account.data().len() { - if !account_ref.data.is_empty() { - // Only support for `CreateAccount` at this time. - // Need a way to limit total realloc size across multiple CPI calls - ic_msg!( - invoke_context, - "Inner instructions do not support realloc, only SystemProgram::CreateAccount", - ); - return Err(SyscallError::InstructionError( - InstructionError::InvalidRealloc, - ) - .into()); - } - if account.data().len() - > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE - { - ic_msg!( - invoke_context, - "SystemProgram::CreateAccount data size limited to {} in inner instructions", - MAX_PERMITTED_DATA_INCREASE - ); - return Err(SyscallError::InstructionError( - InstructionError::InvalidRealloc, - ) - .into()); - } - account_ref.data = translate_slice_mut::( - memory_mapping, - account_ref.vm_data_addr, - account.data().len() as u64, - &bpf_loader_deprecated::id(), // Don't care since it is byte aligned - )?; - *account_ref.ref_to_len_in_vm = account.data().len() as u64; - *account_ref.serialized_len_ptr = account.data().len() as u64; - } - account_ref - .data - .copy_from_slice(&account.data()[0..account_ref.data.len()]); + for ((_key, account), account_ref) in accounts.iter().zip(account_refs) { + let account = account.borrow(); + if let Some(mut account_ref) = account_ref { + *account_ref.lamports = account.lamports(); + *account_ref.owner = *account.owner(); + if account_ref.data.len() != account.data().len() { + if !account_ref.data.is_empty() { + // Only support for `CreateAccount` at this time. + // Need a way to limit total realloc size across multiple CPI calls + ic_msg!( + invoke_context, + "Inner instructions do not support realloc, only SystemProgram::CreateAccount", + ); + return Err( + SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), + ); } + if account.data().len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE { + ic_msg!( + invoke_context, + "SystemProgram::CreateAccount data size limited to {} in inner instructions", + MAX_PERMITTED_DATA_INCREASE + ); + return Err( + SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), + ); + } + account_ref.data = translate_slice_mut::( + memory_mapping, + account_ref.vm_data_addr, + account.data().len() as u64, + &bpf_loader_deprecated::id(), // Don't care since it is byte aligned + )?; + *account_ref.ref_to_len_in_vm = account.data().len() as u64; + *account_ref.serialized_len_ptr = account.data().len() as u64; } + account_ref + .data + .copy_from_slice(&account.data()[0..account_ref.data.len()]); } } diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index b8f4ed4e1f..12006e4a81 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -170,7 +170,8 @@ native machine code before execting it in the virtual machine.", } let lid = bpf_loader::id(); let pid = Pubkey::new(&[0u8; 32]); - let mut bytes = serialize_parameters(&lid, &pid, &accounts, &input.insndata).unwrap(); + let (mut bytes, _account_lenghts) = + serialize_parameters(&lid, &pid, &accounts, &input.insndata).unwrap(); Vec::from(bytes.as_slice_mut()) } };