diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 5d37bfe29f081a..13d851ed5f190c 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -2939,6 +2939,10 @@ fn get_translated_accounts<'a, T, F>( where F: Fn(&T, &InvokeContext) -> Result, EbpfError>, { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context + .get_current_instruction_context() + .map_err(SyscallError::InstructionError)?; let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1)); let program_account_index = program_indices @@ -2952,22 +2956,24 @@ where if index_in_instruction != instruction_account.index_in_callee { continue; // Skip duplicate account } - let account = invoke_context - .transaction_context - .get_account_at_index(instruction_account.index_in_transaction) + let mut borrowed_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 account.borrow().executable() { + if borrowed_account.is_executable() { // Use the known account if invoke_context .feature_set .is_active(&executables_incur_cpi_data_cost::id()) { invoke_context.get_compute_meter().consume( - (account.borrow().data().len() as u64) + (borrowed_account.get_data().len() as u64) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; } @@ -2982,12 +2988,40 @@ where invoke_context, )?; { - let mut account = account.borrow_mut(); - account.copy_into_owner_from_slice(caller_account.owner.as_ref()); - account.set_data_from_slice(caller_account.data); - account.set_lamports(*caller_account.lamports); - account.set_executable(caller_account.executable); - account.set_rent_epoch(caller_account.rent_epoch); + if borrowed_account.get_lamports() != *caller_account.lamports { + borrowed_account + .set_lamports(*caller_account.lamports) + .map_err(SyscallError::InstructionError)?; + } + // The redundant `is_data_mutable` helps to avoid the expensive data comparison if we can + if borrowed_account + .is_data_mutable(caller_account.data.len()) + .is_ok() + || borrowed_account.get_data() != caller_account.data + { + borrowed_account + .set_data(caller_account.data) + .map_err(SyscallError::InstructionError)?; + } + if borrowed_account.is_executable() != caller_account.executable { + borrowed_account + .set_executable(caller_account.executable) + .map_err(SyscallError::InstructionError)?; + } + if borrowed_account.get_owner() != caller_account.owner { + borrowed_account + .set_owner(caller_account.owner.as_ref()) + .map_err(SyscallError::InstructionError)?; + } + drop(borrowed_account); + // TODO: What to do about the `rent_epoch` field? + let account = invoke_context + .transaction_context + .get_account_at_index(instruction_account.index_in_transaction) + .map_err(SyscallError::InstructionError)?; + account + .borrow_mut() + .set_rent_epoch(caller_account.rent_epoch); } let caller_account = if instruction_account.is_writable { let orig_data_lens = invoke_context