1use {
4 crate::{
5 invoke_context::InvokeContext,
6 memory::{translate_slice, translate_type, translate_type_mut_for_cpi, translate_vm_slice},
7 memory_context::SerializedAccountMetadata,
8 serialization::{create_memory_region_of_account, modify_memory_region_of_account},
9 },
10 solana_instruction::{AccountMeta, Instruction},
11 solana_instruction_error::InstructionError,
12 solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
13 solana_program_entrypoint::MAX_PERMITTED_DATA_INCREASE,
14 solana_pubkey::{MAX_SEEDS, Pubkey, PubkeyError},
15 solana_sbpf::{ebpf, memory_region::MemoryMapping},
16 solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, native_loader},
17 solana_stable_layout::stable_instruction::StableInstruction,
18 solana_svm_log_collector::ic_msg,
19 solana_svm_timings::ExecuteTimings,
20 solana_transaction_context::{
21 IndexOfAccount, MAX_ACCOUNTS_PER_INSTRUCTION, MAX_INSTRUCTION_DATA_LEN,
22 instruction_accounts::BorrowedInstructionAccount, vm_slice::VmSlice,
23 },
24 std::mem,
25 thiserror::Error,
26};
27
28#[derive(Clone, Debug, Error, PartialEq, Eq)]
30pub enum CpiError {
31 #[error("Invalid pointer")]
32 InvalidPointer,
33 #[error("Too many signers")]
34 TooManySigners,
35 #[error("Could not create program address with signer seeds: {0}")]
36 BadSeeds(PubkeyError),
37 #[error("InvalidLength")]
38 InvalidLength,
39 #[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")]
40 MaxInstructionAccountsExceeded {
41 num_accounts: u64,
42 max_accounts: u64,
43 },
44 #[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")]
45 MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 },
46 #[error(
47 "Invoked an instruction with too many account info's ({num_account_infos} > \
48 {max_account_infos})"
49 )]
50 MaxInstructionAccountInfosExceeded {
51 num_account_infos: u64,
52 max_account_infos: u64,
53 },
54 #[error("Program {0} not supported by inner instructions")]
55 ProgramNotSupported(Pubkey),
56}
57
58type Error = Box<dyn std::error::Error>;
59
60const SUCCESS: u64 = 0;
61const MAX_SIGNERS: usize = 16;
63const ACCOUNT_INFO_BYTE_SIZE: usize = 80;
69
70#[derive(Debug)]
72#[repr(C)]
73struct SolInstruction {
74 pub program_id_addr: u64,
75 pub accounts_addr: u64,
76 pub accounts_len: u64,
77 pub data_addr: u64,
78 pub data_len: u64,
79}
80
81#[derive(Debug)]
83#[repr(C)]
84struct SolAccountMeta {
85 pub pubkey_addr: u64,
86 pub is_writable: bool,
87 pub is_signer: bool,
88}
89
90#[derive(Debug)]
92#[repr(C)]
93struct SolAccountInfo {
94 pub key_addr: u64,
95 pub lamports_addr: u64,
96 pub data_len: u64,
97 pub data_addr: u64,
98 pub owner_addr: u64,
99 pub rent_epoch: u64,
100 pub is_signer: bool,
101 pub is_writable: bool,
102 pub executable: bool,
103}
104
105mod stable {
106 #[derive(Debug)]
108 #[repr(C)]
109 pub struct AccountInfo {
110 key_addr: u64,
111 lamports_addr: u64,
114 data_addr: u64,
117 owner_addr: u64,
118 _unused: u64,
119 _is_signer: u8,
120 _is_writable: u8,
121 _executable: u8,
122 }
123
124 impl AccountInfo {
125 const LAMPORTS_DATA_OFFSET: u64 = 24;
126 const DATA_DATA_OFFSET: u64 = 24;
127 const DATA_LEN_OFFSET: u64 = 32;
128 pub(crate) fn owner_addr(&self) -> u64 {
129 self.owner_addr
130 }
131 pub(crate) fn key_addr(&self) -> u64 {
132 self.key_addr
133 }
134 pub(crate) fn lamports_addr(&self) -> u64 {
135 self.lamports_addr.wrapping_add(Self::LAMPORTS_DATA_OFFSET)
136 }
137 pub(crate) fn data_addr(&self) -> u64 {
138 self.data_addr.wrapping_add(Self::DATA_DATA_OFFSET)
139 }
140 pub(crate) fn data_len_addr(&self) -> u64 {
141 self.data_addr.wrapping_add(Self::DATA_LEN_OFFSET)
142 }
143 }
144
145 const _FOR_NOW_THESE_ARE_THE_SAME_BUT_IF_ACCOUNT_INFO_CHANGES_SDK_HAS_TO_FIX_IT: () = const {
146 use {
147 solana_account_info::AccountInfo as SdkAccountInfo,
148 std::mem::{align_of, offset_of, size_of},
149 };
150 assert!(offset_of!(AccountInfo, key_addr) == offset_of!(SdkAccountInfo, key));
151 assert!(offset_of!(AccountInfo, lamports_addr) == offset_of!(SdkAccountInfo, lamports));
152 assert!(offset_of!(AccountInfo, data_addr) == offset_of!(SdkAccountInfo, data));
153 assert!(offset_of!(AccountInfo, owner_addr) == offset_of!(SdkAccountInfo, owner));
154 assert!(offset_of!(AccountInfo, _is_signer) == offset_of!(SdkAccountInfo, is_signer));
155 assert!(offset_of!(AccountInfo, _is_writable) == offset_of!(SdkAccountInfo, is_writable));
156 assert!(offset_of!(AccountInfo, _executable) == offset_of!(SdkAccountInfo, executable));
157 assert!(size_of::<AccountInfo>() == size_of::<SdkAccountInfo>());
158 assert!(align_of::<SdkAccountInfo>() >= align_of::<AccountInfo>());
159 };
160}
161
162const MAX_CPI_ACCOUNT_INFOS: usize = 255;
164
165fn check_account_info_pointer(
167 invoke_context: &InvokeContext,
168 vm_addr: u64,
169 expected_vm_addr: u64,
170 field: &str,
171) -> Result<(), Error> {
172 if vm_addr != expected_vm_addr {
173 ic_msg!(
174 invoke_context,
175 "Invalid account info pointer `{}': {:#x} != {:#x}",
176 field,
177 vm_addr,
178 expected_vm_addr
179 );
180 return Err(Box::new(CpiError::InvalidPointer));
181 }
182 Ok(())
183}
184
185fn check_instruction_size(num_accounts: usize, data_len: usize) -> Result<(), Error> {
187 if num_accounts > MAX_ACCOUNTS_PER_INSTRUCTION {
188 return Err(Box::new(CpiError::MaxInstructionAccountsExceeded {
189 num_accounts: num_accounts as u64,
190 max_accounts: MAX_ACCOUNTS_PER_INSTRUCTION as u64,
191 }));
192 }
193 if data_len > MAX_INSTRUCTION_DATA_LEN {
194 return Err(Box::new(CpiError::MaxInstructionDataLenExceeded {
195 data_len: data_len as u64,
196 max_data_len: MAX_INSTRUCTION_DATA_LEN as u64,
197 }));
198 }
199 Ok(())
200}
201
202fn check_account_infos(num_account_infos: usize) -> Result<(), Error> {
204 let num_account_infos = num_account_infos as u64;
205 let max_account_infos = MAX_CPI_ACCOUNT_INFOS as u64;
206 if num_account_infos > max_account_infos {
207 return Err(Box::new(CpiError::MaxInstructionAccountInfosExceeded {
208 num_account_infos,
209 max_account_infos,
210 }));
211 }
212 Ok(())
213}
214
215fn check_authorized_program(
217 program_id: &Pubkey,
218 instruction_data: &[u8],
219 invoke_context: &InvokeContext,
220) -> Result<(), Error> {
221 if native_loader::check_id(program_id)
222 || bpf_loader::check_id(program_id)
223 || bpf_loader_deprecated::check_id(program_id)
224 || (solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
225 && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
226 || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
227 || (invoke_context
228 .get_feature_set()
229 .enable_bpf_loader_set_authority_checked_ix
230 && bpf_loader_upgradeable::is_set_authority_checked_instruction(
231 instruction_data,
232 ))
233 || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
234 || invoke_context.is_precompile(program_id)
235 {
236 return Err(Box::new(CpiError::ProgramNotSupported(*program_id)));
237 }
238 Ok(())
239}
240
241#[derive(Debug)]
246pub struct CallerAccount<'a> {
247 pub lamports: &'a mut u64,
248 pub owner: &'a mut Pubkey,
249 pub original_data_len: usize,
254 pub serialized_data: &'a mut [u8],
260 pub vm_data_addr: u64,
263 pub ref_to_len_in_vm: &'a mut u64,
264}
265
266impl<'a> CallerAccount<'a> {
267 pub fn address_space_reserved_for_account(&self, is_caller_loader_deprecated: bool) -> usize {
269 if is_caller_loader_deprecated {
270 self.original_data_len
271 } else {
272 self.original_data_len
273 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
274 }
275 }
276
277 pub unsafe fn get_serialized_data(
287 memory_mapping: &solana_sbpf::memory_region::MemoryMapping,
288 check_aligned: bool,
289 vm_addr: u64,
290 original_data_len: usize,
291 len: usize,
292 virtual_address_space_adjustments: bool,
293 account_data_direct_mapping: bool,
294 ) -> Result<&'a mut [u8], Error> {
295 use crate::memory::translate_slice_mut_for_cpi;
296
297 let is_caller_loader_deprecated = !check_aligned;
298 let address_space_reserved_for_account = if is_caller_loader_deprecated {
299 original_data_len
300 } else {
301 original_data_len.saturating_add(MAX_PERMITTED_DATA_INCREASE)
302 };
303 if len > address_space_reserved_for_account {
304 return Err(InstructionError::InvalidRealloc.into());
305 }
306 if virtual_address_space_adjustments && account_data_direct_mapping {
307 Ok(&mut [])
308 } else if virtual_address_space_adjustments {
309 unsafe {
311 let serialization_ptr: &'a mut [u8] = translate_slice_mut_for_cpi::<u8>(
313 memory_mapping,
314 solana_sbpf::ebpf::MM_INPUT_START,
315 1,
316 false, )?;
318 Ok(std::slice::from_raw_parts_mut(
319 serialization_ptr
320 .as_mut_ptr()
321 .add(vm_addr.saturating_sub(solana_sbpf::ebpf::MM_INPUT_START) as usize),
322 len,
323 ))
324 }
325 } else {
326 unsafe {
327 translate_slice_mut_for_cpi::<u8>(
329 memory_mapping,
330 vm_addr,
331 len as u64,
332 false, )
334 }
335 }
336 }
337
338 pub fn from_account_info(
340 invoke_context: &InvokeContext,
341 memory_mapping: &MemoryMapping,
342 check_aligned: bool,
343 _vm_addr: u64,
344 account_info: &stable::AccountInfo,
345 account_metadata: &crate::memory_context::SerializedAccountMetadata,
346 ) -> Result<CallerAccount<'a>, Error> {
347 use crate::memory::{translate_type, translate_type_mut_for_cpi};
348
349 let virtual_address_space_adjustments = invoke_context
350 .get_feature_set()
351 .virtual_address_space_adjustments;
352 let account_data_direct_mapping =
353 invoke_context.get_feature_set().account_data_direct_mapping;
354
355 check_account_info_pointer(
356 invoke_context,
357 account_info.key_addr(),
358 account_metadata.vm_key_addr,
359 "key",
360 )?;
361 check_account_info_pointer(
362 invoke_context,
363 account_info.owner_addr(),
364 account_metadata.vm_owner_addr,
365 "owner",
366 )?;
367
368 let lamports = {
371 let ptr =
373 translate_type::<u64>(memory_mapping, account_info.lamports_addr(), check_aligned)?;
374 if account_info.lamports_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
375 return Err(Box::new(CpiError::InvalidPointer));
376 }
377
378 check_account_info_pointer(
379 invoke_context,
380 *ptr,
381 account_metadata.vm_lamports_addr,
382 "lamports",
383 )?;
384
385 translate_type_mut_for_cpi::<u64>(memory_mapping, *ptr, check_aligned)?
386 };
387
388 let owner = translate_type_mut_for_cpi::<Pubkey>(
389 memory_mapping,
390 account_info.owner_addr(),
391 check_aligned,
392 )?;
393
394 let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
395 if account_info.data_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
396 return Err(Box::new(CpiError::InvalidPointer));
397 }
398
399 let data_slice = *translate_type::<VmSlice<u8>>(
403 memory_mapping,
404 account_info.data_addr(),
405 check_aligned,
406 )?;
407 check_account_info_pointer(
408 invoke_context,
409 data_slice.ptr(),
410 account_metadata.vm_data_addr,
411 "data",
412 )?;
413
414 if account_info.data_len_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
418 return Err(Box::new(CpiError::InvalidPointer));
419 }
420 let ref_to_len_in_vm = translate_type_mut_for_cpi::<u64>(
421 memory_mapping,
422 account_info.data_len_addr(),
423 false,
424 )?;
425 let serialized_data = unsafe {
426 CallerAccount::get_serialized_data(
427 memory_mapping,
428 check_aligned,
429 data_slice.ptr(),
430 account_metadata.original_data_len,
431 *ref_to_len_in_vm as usize,
432 virtual_address_space_adjustments,
433 account_data_direct_mapping,
434 )?
435 };
436 (serialized_data, data_slice.ptr(), ref_to_len_in_vm)
437 };
438
439 Ok(CallerAccount {
440 lamports,
441 owner,
442 original_data_len: account_metadata.original_data_len,
443 serialized_data,
444 vm_data_addr,
445 ref_to_len_in_vm,
446 })
447 }
448
449 fn from_sol_account_info(
451 invoke_context: &InvokeContext,
452 memory_mapping: &MemoryMapping,
453 check_aligned: bool,
454 vm_addr: u64,
455 account_info: &SolAccountInfo,
456 account_metadata: &crate::memory_context::SerializedAccountMetadata,
457 ) -> Result<CallerAccount<'a>, Error> {
458 use crate::memory::translate_type_mut_for_cpi;
459
460 let virtual_address_space_adjustments = invoke_context
461 .get_feature_set()
462 .virtual_address_space_adjustments;
463 let account_data_direct_mapping =
464 invoke_context.get_feature_set().account_data_direct_mapping;
465
466 check_account_info_pointer(
467 invoke_context,
468 account_info.key_addr,
469 account_metadata.vm_key_addr,
470 "key",
471 )?;
472
473 check_account_info_pointer(
474 invoke_context,
475 account_info.owner_addr,
476 account_metadata.vm_owner_addr,
477 "owner",
478 )?;
479
480 check_account_info_pointer(
481 invoke_context,
482 account_info.lamports_addr,
483 account_metadata.vm_lamports_addr,
484 "lamports",
485 )?;
486
487 check_account_info_pointer(
488 invoke_context,
489 account_info.data_addr,
490 account_metadata.vm_data_addr,
491 "data",
492 )?;
493
494 let lamports = translate_type_mut_for_cpi::<u64>(
497 memory_mapping,
498 account_info.lamports_addr,
499 check_aligned,
500 )?;
501 let owner = translate_type_mut_for_cpi::<Pubkey>(
502 memory_mapping,
503 account_info.owner_addr,
504 check_aligned,
505 )?;
506
507 let vm_len_addr = vm_addr
512 .saturating_add(&account_info.data_len as *const u64 as u64)
513 .saturating_sub(account_info as *const _ as *const u64 as u64);
514 if vm_len_addr >= solana_sbpf::ebpf::MM_INPUT_START {
518 return Err(Box::new(CpiError::InvalidPointer));
519 }
520 let ref_to_len_in_vm =
521 translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
522 let serialized_data = unsafe {
523 CallerAccount::get_serialized_data(
524 memory_mapping,
525 check_aligned,
526 account_info.data_addr,
527 account_metadata.original_data_len,
528 *ref_to_len_in_vm as usize,
529 virtual_address_space_adjustments,
530 account_data_direct_mapping,
531 )?
532 };
533
534 Ok(CallerAccount {
535 lamports,
536 owner,
537 original_data_len: account_metadata.original_data_len,
538 serialized_data,
539 vm_data_addr: account_info.data_addr,
540 ref_to_len_in_vm,
541 })
542 }
543}
544
545pub trait SyscallInvokeSigned {
547 fn translate_instruction(
548 addr: u64,
549 invoke_context: &InvokeContext,
550 ) -> Result<Instruction, Error>;
551 fn translate_accounts<'a>(
552 account_infos_addr: u64,
553 account_infos_len: u64,
554 invoke_context: &InvokeContext,
555 ) -> Result<Vec<TranslatedAccount<'a>>, Error>;
556}
557
558pub fn translate_instruction_rust(
559 addr: u64,
560 invoke_context: &InvokeContext,
561) -> Result<Instruction, Error> {
562 let check_aligned = invoke_context.get_check_aligned();
563 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
564 let ix = translate_type::<StableInstruction>(memory_mapping, addr, check_aligned)?;
565 let account_metas = translate_slice::<mem::MaybeUninit<AccountMeta>>(
566 memory_mapping,
567 ix.accounts.as_vaddr(),
568 ix.accounts.len(),
569 check_aligned,
570 )?;
571 let data = translate_slice::<u8>(
572 memory_mapping,
573 ix.data.as_vaddr(),
574 ix.data.len(),
575 check_aligned,
576 )?;
577
578 check_instruction_size(account_metas.len(), data.len())?;
579
580 let mut total_cu_translation_cost: u64 = (data.len() as u64)
581 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
582 .unwrap_or(u64::MAX);
583
584 let account_meta_translation_cost =
586 (account_metas.len().saturating_mul(size_of::<AccountMeta>()) as u64)
587 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
588 .unwrap_or(u64::MAX);
589
590 total_cu_translation_cost =
591 total_cu_translation_cost.saturating_add(account_meta_translation_cost);
592
593 invoke_context.consume_checked(total_cu_translation_cost)?;
594
595 let mut accounts = Vec::with_capacity(account_metas.len());
596 for account_meta in account_metas {
597 let account_meta = unsafe {
600 let ptr = account_meta.as_ptr();
601 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
602 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
603 {
604 return Err(Box::new(InstructionError::InvalidArgument));
605 }
606 account_meta.assume_init_ref()
609 };
610
611 accounts.push(account_meta.clone());
612 }
613
614 Ok(Instruction {
615 accounts,
616 data: data.to_vec(),
617 program_id: ix.program_id,
618 })
619}
620
621pub fn translate_accounts_rust<'a>(
622 account_infos_addr: u64,
623 account_infos_len: u64,
624 invoke_context: &InvokeContext,
625) -> Result<Vec<TranslatedAccount<'a>>, Error> {
626 let check_aligned = invoke_context.get_check_aligned();
627 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
628 translate_account_infos(
629 account_infos_addr,
630 account_infos_len,
631 |account_info: &stable::AccountInfo| account_info.key_addr(),
632 invoke_context,
633 memory_mapping,
634 check_aligned,
635 |account_infos, account_info_keys| {
636 translate_accounts_common(
637 &account_info_keys,
638 account_infos,
639 account_infos_addr,
640 invoke_context,
641 memory_mapping,
642 check_aligned,
643 CallerAccount::from_account_info,
644 )
645 },
646 )?
647}
648
649pub fn translate_signers(
650 program_id: &Pubkey,
651 signers_seeds_addr: u64,
652 signers_seeds_len: u64,
653 invoke_context: &InvokeContext,
654) -> Result<Vec<Pubkey>, Error> {
655 let check_aligned = invoke_context.get_check_aligned();
656 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
657 if signers_seeds_len > 0 {
658 let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
659 memory_mapping,
660 signers_seeds_addr,
661 signers_seeds_len,
662 check_aligned,
663 )?;
664 if signers_seeds.len() > MAX_SIGNERS {
665 return Err(Box::new(CpiError::TooManySigners));
666 }
667 Ok(signers_seeds
668 .iter()
669 .map(|signer_seeds| {
670 let untranslated_seeds = translate_slice::<VmSlice<u8>>(
671 memory_mapping,
672 signer_seeds.ptr(),
673 signer_seeds.len(),
674 check_aligned,
675 )?;
676 if untranslated_seeds.len() > MAX_SEEDS {
677 return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
678 }
679 let seeds_bytes = untranslated_seeds
680 .iter()
681 .map(|untranslated_seed| {
682 translate_vm_slice(untranslated_seed, memory_mapping, check_aligned)
683 })
684 .collect::<Result<Vec<_>, Error>>()?;
685 Pubkey::create_program_address(&seeds_bytes, program_id)
686 .map_err(|err| Box::new(CpiError::BadSeeds(err)) as Error)
687 })
688 .collect::<Result<Vec<_>, Error>>()?)
689 } else {
690 Ok(vec![])
691 }
692}
693
694pub fn translate_instruction_c(
695 addr: u64,
696 invoke_context: &InvokeContext,
697) -> Result<Instruction, Error> {
698 let check_aligned = invoke_context.get_check_aligned();
699 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
700 let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
701
702 let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
703 let account_metas = translate_slice::<mem::MaybeUninit<SolAccountMeta>>(
704 memory_mapping,
705 ix_c.accounts_addr,
706 ix_c.accounts_len,
707 check_aligned,
708 )?;
709 let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
710
711 check_instruction_size(ix_c.accounts_len as usize, data.len())?;
712
713 let mut total_cu_translation_cost: u64 = (data.len() as u64)
714 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
715 .unwrap_or(u64::MAX);
716
717 let account_meta_translation_cost = (ix_c
719 .accounts_len
720 .saturating_mul(size_of::<AccountMeta>() as u64))
721 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
722 .unwrap_or(u64::MAX);
723
724 total_cu_translation_cost =
725 total_cu_translation_cost.saturating_add(account_meta_translation_cost);
726
727 invoke_context.consume_checked(total_cu_translation_cost)?;
728
729 let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
730 for account_meta in account_metas {
731 let account_meta = unsafe {
734 let ptr = account_meta.as_ptr();
735 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
736 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
737 {
738 return Err(Box::new(InstructionError::InvalidArgument));
739 }
740 account_meta.assume_init_ref()
743 };
744 let pubkey =
745 translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
746 accounts.push(AccountMeta {
747 pubkey: *pubkey,
748 is_signer: account_meta.is_signer,
749 is_writable: account_meta.is_writable,
750 });
751 }
752
753 Ok(Instruction {
754 accounts,
755 data: data.to_vec(),
756 program_id: *program_id,
757 })
758}
759
760pub fn translate_accounts_c<'a>(
761 account_infos_addr: u64,
762 account_infos_len: u64,
763 invoke_context: &InvokeContext,
764) -> Result<Vec<TranslatedAccount<'a>>, Error> {
765 let check_aligned = invoke_context.get_check_aligned();
766 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
767 translate_account_infos(
768 account_infos_addr,
769 account_infos_len,
770 |account_info: &SolAccountInfo| account_info.key_addr,
771 invoke_context,
772 memory_mapping,
773 check_aligned,
774 |account_infos, account_info_keys| {
775 translate_accounts_common(
776 &account_info_keys,
777 account_infos,
778 account_infos_addr,
779 invoke_context,
780 memory_mapping,
781 check_aligned,
782 CallerAccount::from_sol_account_info,
783 )
784 },
785 )?
786}
787
788pub fn cpi_common<S: SyscallInvokeSigned>(
790 invoke_context: &mut InvokeContext,
791 instruction_addr: u64,
792 account_infos_addr: u64,
793 account_infos_len: u64,
794 signers_seeds_addr: u64,
795 signers_seeds_len: u64,
796) -> Result<u64, Error> {
797 let amount = invoke_context.get_execution_cost().invoke_units;
802 invoke_context.consume_checked(amount)?;
803 let virtual_address_space_adjustments = invoke_context
804 .get_feature_set()
805 .virtual_address_space_adjustments;
806 let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
807 let check_aligned = invoke_context.get_check_aligned();
808
809 let instruction = S::translate_instruction(instruction_addr, invoke_context)?;
810 let instruction_context = invoke_context
811 .transaction_context
812 .get_current_instruction_context()?;
813 let caller_program_id = instruction_context.get_program_key()?;
814 let signers = translate_signers(
815 caller_program_id,
816 signers_seeds_addr,
817 signers_seeds_len,
818 invoke_context,
819 )?;
820 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
821 invoke_context.build_instruction_frame(instruction)?;
822 invoke_context.verify_instruction_accounts(&signers)?;
823
824 let mut accounts =
825 S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
826
827 let transaction_context = &invoke_context.transaction_context;
832 let instruction_context = transaction_context.get_current_instruction_context()?;
833 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
834 for translated_account in accounts.iter_mut() {
835 let callee_account = instruction_context
836 .try_borrow_instruction_account(translated_account.index_in_caller)?;
837 let update_caller = update_callee_account(
838 memory_mapping,
839 check_aligned,
840 &translated_account.caller_account,
841 callee_account,
842 virtual_address_space_adjustments,
843 account_data_direct_mapping,
844 )?;
845 translated_account.update_caller_account_region =
846 translated_account.update_caller_account_info || update_caller;
847 }
848
849 let mut compute_units_consumed = 0;
851 invoke_context
852 .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
853
854 let transaction_context = &invoke_context.transaction_context;
856 let instruction_context = transaction_context.get_current_instruction_context()?;
857
858 for translated_account in accounts.iter_mut() {
862 let mut callee_account = instruction_context
863 .try_borrow_instruction_account(translated_account.index_in_caller)?;
864 if translated_account.update_caller_account_info {
865 update_caller_account(
866 invoke_context,
867 check_aligned,
868 &mut translated_account.caller_account,
869 &mut callee_account,
870 virtual_address_space_adjustments,
871 account_data_direct_mapping,
872 )?;
873 }
874 }
875
876 if virtual_address_space_adjustments {
877 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
878 for translated_account in accounts.iter() {
879 let mut callee_account = instruction_context
880 .try_borrow_instruction_account(translated_account.index_in_caller)?;
881 if translated_account.update_caller_account_region {
882 unsafe {
883 update_caller_account_region(
888 memory_mapping,
889 check_aligned,
890 &translated_account.caller_account,
891 &mut callee_account,
892 account_data_direct_mapping,
893 )?;
894 }
895 }
896 }
897 }
898
899 Ok(SUCCESS)
900}
901
902pub struct TranslatedAccount<'a> {
904 pub index_in_caller: IndexOfAccount,
905 pub caller_account: CallerAccount<'a>,
906 pub update_caller_account_region: bool,
907 pub update_caller_account_info: bool,
908}
909
910fn translate_account_infos<T, R>(
911 account_infos_addr: u64,
912 account_infos_len: u64,
913 key_addr: impl Fn(&T) -> u64,
914 invoke_context: &InvokeContext,
915 memory_mapping: &MemoryMapping,
916 check_aligned: bool,
917 cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
918) -> Result<R, Error> {
919 if account_infos_addr
923 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
924 >= ebpf::MM_INPUT_START
925 {
926 return Err(CpiError::InvalidPointer.into());
927 }
928
929 let account_infos = translate_slice::<T>(
930 memory_mapping,
931 account_infos_addr,
932 account_infos_len,
933 check_aligned,
934 )?;
935 check_account_infos(account_infos.len())?;
936
937 let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
938
939 let amount = (account_infos_bytes as u64)
940 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
941 .unwrap_or(u64::MAX);
942 invoke_context.consume_checked(amount)?;
943
944 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
945 #[expect(clippy::needless_range_loop)]
946 for account_index in 0..account_infos_len as usize {
947 #[expect(clippy::indexing_slicing)]
948 let account_info = &account_infos[account_index];
949 account_info_keys.push(translate_type::<Pubkey>(
950 memory_mapping,
951 key_addr(account_info),
952 check_aligned,
953 )?);
954 }
955 Ok(cb(account_infos, account_info_keys))
956}
957
958fn translate_accounts_common<'a, T, F>(
960 account_info_keys: &[&Pubkey],
961 account_infos: &[T],
962 account_infos_addr: u64,
963 invoke_context: &InvokeContext,
964 memory_mapping: &MemoryMapping,
965 check_aligned: bool,
966 do_translate: F,
967) -> Result<Vec<TranslatedAccount<'a>>, Error>
968where
969 F: Fn(
970 &InvokeContext,
971 &MemoryMapping,
972 bool,
973 u64,
974 &T,
975 &SerializedAccountMetadata,
976 ) -> Result<CallerAccount<'a>, Error>,
977{
978 let transaction_context = &invoke_context.transaction_context;
979 let next_instruction_context = transaction_context.get_next_instruction_context()?;
980 let next_instruction_accounts = next_instruction_context.instruction_accounts();
981 let instruction_context = transaction_context.get_current_instruction_context()?;
982 let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
983
984 let accounts_metadata = &invoke_context
987 .memory_contexts
988 .memory_context_abi_v1()
989 .unwrap()
990 .accounts_metadata;
991
992 for (instruction_account_index, instruction_account) in
993 next_instruction_accounts.iter().enumerate()
994 {
995 if next_instruction_context
996 .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
997 .is_some()
998 {
999 continue; }
1001
1002 let index_in_caller = instruction_context
1003 .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
1004 let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
1005 let account_key = invoke_context
1006 .transaction_context
1007 .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
1008
1009 #[expect(deprecated)]
1010 if callee_account.is_executable() {
1011 let amount = (callee_account.get_data().len() as u64)
1013 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1014 .unwrap_or(u64::MAX);
1015 invoke_context.consume_checked(amount)?;
1016 } else if let Some(caller_account_index) =
1017 account_info_keys.iter().position(|key| *key == account_key)
1018 {
1019 let serialized_metadata =
1020 accounts_metadata
1021 .get(index_in_caller as usize)
1022 .ok_or_else(|| {
1023 ic_msg!(
1024 invoke_context,
1025 "Internal error: index mismatch for account {}",
1026 account_key
1027 );
1028 Box::new(InstructionError::MissingAccount) as Error
1029 })?;
1030
1031 if caller_account_index >= account_infos.len() {
1033 return Err(Box::new(CpiError::InvalidLength));
1034 }
1035 #[expect(clippy::indexing_slicing)]
1036 let caller_account =
1037 do_translate(
1038 invoke_context,
1039 memory_mapping,
1040 check_aligned,
1041 account_infos_addr.saturating_add(
1042 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
1043 ),
1044 &account_infos[caller_account_index],
1045 serialized_metadata,
1046 )?;
1047
1048 let amount = (*caller_account.ref_to_len_in_vm)
1049 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1050 .unwrap_or(u64::MAX);
1051 invoke_context.consume_checked(amount)?;
1052
1053 accounts.push(TranslatedAccount {
1054 index_in_caller,
1055 caller_account,
1056 update_caller_account_region: true, update_caller_account_info: instruction_account.is_writable(),
1058 });
1059 } else {
1060 ic_msg!(
1061 invoke_context,
1062 "Instruction references an unknown account {}",
1063 account_key
1064 );
1065 return Err(Box::new(InstructionError::MissingAccount));
1066 }
1067 }
1068
1069 Ok(accounts)
1070}
1071
1072fn update_callee_account(
1084 memory_mapping: &MemoryMapping,
1085 check_aligned: bool,
1086 caller_account: &CallerAccount,
1087 mut callee_account: BorrowedInstructionAccount<'_, '_>,
1088 virtual_address_space_adjustments: bool,
1089 account_data_direct_mapping: bool,
1090) -> Result<bool, Error> {
1091 let mut must_update_caller = false;
1092
1093 if callee_account.get_lamports() != *caller_account.lamports {
1094 callee_account.set_lamports(*caller_account.lamports)?;
1095 }
1096
1097 if virtual_address_space_adjustments {
1098 let prev_len = callee_account.get_data().len();
1099 let post_len = *caller_account.ref_to_len_in_vm as usize;
1100 if prev_len != post_len {
1101 if !account_data_direct_mapping && post_len < prev_len {
1102 let serialized_data = unsafe {
1105 CallerAccount::get_serialized_data(
1106 memory_mapping,
1107 check_aligned,
1108 caller_account.vm_data_addr,
1109 caller_account.original_data_len,
1110 prev_len,
1111 virtual_address_space_adjustments,
1112 account_data_direct_mapping,
1113 )?
1114 };
1115 serialized_data
1116 .get_mut(post_len..)
1117 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1118 .fill(0);
1119 }
1120 callee_account.set_data_length(post_len)?;
1121 must_update_caller = true;
1123 }
1124 if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1125 callee_account.set_data_from_slice(caller_account.serialized_data)?;
1126 }
1127 } else {
1128 match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1130 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1131 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1132 return Err(Box::new(err));
1133 }
1134 _ => {}
1135 }
1136 }
1137
1138 if callee_account.get_owner() != caller_account.owner {
1140 callee_account.set_owner(caller_account.owner.as_ref())?;
1141 must_update_caller = true;
1143 }
1144
1145 Ok(must_update_caller)
1146}
1147
1148unsafe fn update_caller_account_region(
1153 memory_mapping: &mut MemoryMapping,
1154 check_aligned: bool,
1155 caller_account: &CallerAccount,
1156 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1157 account_data_direct_mapping: bool,
1158) -> Result<(), Error> {
1159 let is_caller_loader_deprecated = !check_aligned;
1160 let address_space_reserved_for_account =
1161 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1162
1163 if address_space_reserved_for_account > 0 {
1164 let (region_index, region) = memory_mapping
1167 .find_region(caller_account.vm_data_addr)
1168 .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1169 let region_start_vm_addr = region.vm_addr_range().start;
1171 debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1172 let mut new_region;
1173 if !account_data_direct_mapping {
1174 new_region = region.clone();
1175 modify_memory_region_of_account(callee_account, &mut new_region);
1176 } else {
1177 new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1178 }
1179 unsafe {
1180 memory_mapping.replace_region(region_index, new_region)?;
1185 }
1186 }
1187
1188 Ok(())
1189}
1190
1191fn update_caller_account(
1200 invoke_context: &InvokeContext,
1201 check_aligned: bool,
1202 caller_account: &mut CallerAccount<'_>,
1203 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1204 virtual_address_space_adjustments: bool,
1205 account_data_direct_mapping: bool,
1206) -> Result<(), Error> {
1207 *caller_account.lamports = callee_account.get_lamports();
1208 *caller_account.owner = *callee_account.get_owner();
1209
1210 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1211 let post_len = callee_account.get_data().len();
1212 let is_caller_loader_deprecated = !check_aligned;
1213 let address_space_reserved_for_account =
1214 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1215
1216 if post_len > address_space_reserved_for_account {
1217 let max_increase =
1218 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1219 ic_msg!(
1220 invoke_context,
1221 "Account data size realloc limited to {max_increase} in inner instructions",
1222 );
1223 return Err(Box::new(InstructionError::InvalidRealloc));
1224 }
1225
1226 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1227 if prev_len != post_len {
1228 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1231 if post_len < prev_len {
1234 caller_account
1235 .serialized_data
1236 .get_mut(post_len..)
1237 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1238 .fill(0);
1239 }
1240 unsafe {
1242 caller_account.serialized_data = CallerAccount::get_serialized_data(
1243 memory_mapping,
1244 check_aligned,
1245 caller_account.vm_data_addr,
1246 caller_account.original_data_len,
1247 post_len,
1248 virtual_address_space_adjustments,
1249 account_data_direct_mapping,
1250 )?;
1251 }
1252 }
1253 *caller_account.ref_to_len_in_vm = post_len as u64;
1255
1256 let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1258 memory_mapping,
1259 caller_account
1260 .vm_data_addr
1261 .saturating_sub(std::mem::size_of::<u64>() as u64),
1262 check_aligned,
1263 )?;
1264 *serialized_len_ptr = post_len as u64;
1265 }
1266
1267 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1268 let to_slice = &mut caller_account.serialized_data;
1270 let from_slice = callee_account
1271 .get_data()
1272 .get(0..post_len)
1273 .ok_or(CpiError::InvalidLength)?;
1274 if to_slice.len() != from_slice.len() {
1275 return Err(Box::new(InstructionError::AccountDataTooSmall));
1276 }
1277 to_slice.copy_from_slice(from_slice);
1278 }
1279
1280 Ok(())
1281}
1282
1283#[allow(clippy::indexing_slicing)]
1284#[allow(clippy::arithmetic_side_effects)]
1285#[cfg(test)]
1286mod tests {
1287 use {
1288 super::*,
1289 crate::{
1290 invoke_context::BpfAllocator,
1291 memory::translate_type,
1292 memory_context::{MemoryContext, SerializedAccountMetadata},
1293 with_mock_invoke_context_with_feature_set,
1294 },
1295 assert_matches::assert_matches,
1296 solana_account::{Account, AccountSharedData, ReadableAccount},
1297 solana_account_info::AccountInfo,
1298 solana_sbpf::{
1299 ebpf::{MM_INPUT_START, MM_STACK_START},
1300 memory_region::MemoryRegion,
1301 program::SBPFVersion,
1302 vm::Config,
1303 },
1304 solana_sdk_ids::{bpf_loader, system_program},
1305 solana_svm_feature_set::SVMFeatureSet,
1306 solana_transaction_context::{
1307 IndexOfAccount, instruction_accounts::InstructionAccount,
1308 transaction_accounts::KeyedAccountSharedData,
1309 },
1310 std::{
1311 cell::{Cell, RefCell},
1312 mem, ptr,
1313 rc::Rc,
1314 slice,
1315 },
1316 test_case::case,
1317 };
1318
1319 macro_rules! mock_invoke_context {
1320 ($invoke_context:ident,
1321 $transaction_context:ident,
1322 $instruction_data:expr,
1323 $transaction_accounts:expr,
1324 $program_account:expr,
1325 $instruction_accounts:expr) => {
1326 let instruction_data = $instruction_data;
1327 let instruction_accounts = $instruction_accounts
1328 .iter()
1329 .map(|index_in_transaction| {
1330 InstructionAccount::new(
1331 *index_in_transaction as IndexOfAccount,
1332 false,
1333 $transaction_accounts[*index_in_transaction as usize].2,
1334 )
1335 })
1336 .collect::<Vec<_>>();
1337 let transaction_accounts = $transaction_accounts
1338 .into_iter()
1339 .map(|a| (a.0, a.1))
1340 .collect::<Vec<KeyedAccountSharedData>>();
1341 let mut feature_set = SVMFeatureSet::all_enabled();
1342 feature_set.virtual_address_space_adjustments = false;
1343 feature_set.account_data_direct_mapping = false;
1344 let feature_set = &feature_set;
1345 with_mock_invoke_context_with_feature_set!(
1346 $invoke_context,
1347 $transaction_context,
1348 feature_set,
1349 transaction_accounts
1350 );
1351 $invoke_context
1352 .transaction_context
1353 .configure_top_level_instruction_for_tests(
1354 $program_account,
1355 instruction_accounts,
1356 instruction_data.to_vec(),
1357 )
1358 .unwrap();
1359 $invoke_context.push().unwrap();
1360 };
1361 }
1362
1363 macro_rules! borrow_instruction_account {
1364 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1365 let instruction_context = $invoke_context
1366 .transaction_context
1367 .get_current_instruction_context()
1368 .unwrap();
1369 let $borrowed_account = instruction_context
1370 .try_borrow_instruction_account($index)
1371 .unwrap();
1372 };
1373 }
1374
1375 fn is_zeroed(data: &[u8]) -> bool {
1376 data.iter().all(|b| *b == 0)
1377 }
1378
1379 struct MockCallerAccount {
1380 lamports: u64,
1381 owner: Pubkey,
1382 vm_addr: u64,
1383 data: Vec<u8>,
1384 len: u64,
1385 regions: Vec<MemoryRegion>,
1386 virtual_address_space_adjustments: bool,
1387 }
1388
1389 impl MockCallerAccount {
1390 fn new(
1391 lamports: u64,
1392 owner: Pubkey,
1393 data: &[u8],
1394 virtual_address_space_adjustments: bool,
1395 ) -> MockCallerAccount {
1396 let vm_addr = MM_INPUT_START;
1397 let mut region_addr = vm_addr;
1398 let region_len = mem::size_of::<u64>()
1399 + if virtual_address_space_adjustments {
1400 0
1401 } else {
1402 data.len() + MAX_PERMITTED_DATA_INCREASE
1403 };
1404 let mut d = vec![0; region_len];
1405 let mut regions = vec![];
1406
1407 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1409
1410 if !virtual_address_space_adjustments {
1412 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1413 }
1414
1415 regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1417 region_addr += region_len as u64;
1418
1419 if virtual_address_space_adjustments {
1420 regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1422 region_addr += data.len() as u64;
1423
1424 regions.push(MemoryRegion::new(
1426 &raw mut d[mem::size_of::<u64>()..],
1427 region_addr,
1428 ));
1429 } else {
1430 d.truncate(mem::size_of::<u64>() + data.len());
1432 }
1433
1434 MockCallerAccount {
1435 lamports,
1436 owner,
1437 vm_addr,
1438 data: d,
1439 len: data.len() as u64,
1440 regions,
1441 virtual_address_space_adjustments,
1442 }
1443 }
1444
1445 fn data_slice<'a>(&self) -> &'a [u8] {
1446 unsafe {
1448 slice::from_raw_parts(
1449 self.data[mem::size_of::<u64>()..].as_ptr(),
1450 self.data.capacity() - mem::size_of::<u64>(),
1451 )
1452 }
1453 }
1454
1455 fn caller_account(&mut self) -> CallerAccount<'_> {
1456 let data = if self.virtual_address_space_adjustments {
1457 &mut []
1458 } else {
1459 &mut self.data[mem::size_of::<u64>()..]
1460 };
1461 CallerAccount {
1462 lamports: &mut self.lamports,
1463 owner: &mut self.owner,
1464 original_data_len: self.len as usize,
1465 serialized_data: data,
1466 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1467 ref_to_len_in_vm: &mut self.len,
1468 }
1469 }
1470 }
1471
1472 struct MockAccountInfo<'a> {
1473 key: Pubkey,
1474 is_signer: bool,
1475 is_writable: bool,
1476 lamports: u64,
1477 data: &'a [u8],
1478 owner: Pubkey,
1479 executable: bool,
1480 _unused: u64,
1481 }
1482
1483 impl MockAccountInfo<'_> {
1484 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1485 MockAccountInfo {
1486 key,
1487 is_signer: false,
1488 is_writable: false,
1489 lamports: account.lamports(),
1490 data: account.data(),
1491 owner: *account.owner(),
1492 executable: account.executable(),
1493 _unused: account.rent_epoch(),
1494 }
1495 }
1496
1497 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1498 let size = mem::size_of::<AccountInfo>()
1499 + mem::size_of::<Pubkey>() * 2
1500 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1501 + mem::size_of::<u64>()
1502 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1503 + self.data.len();
1504 let mut data = vec![0; size];
1505
1506 let vm_addr = vm_addr as usize;
1507 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1508 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1509 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1510 let owner_addr = lamports_addr + mem::size_of::<u64>();
1511 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1512 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1513
1514 #[allow(deprecated)]
1515 #[allow(clippy::used_underscore_binding)]
1516 let info = AccountInfo {
1517 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1518 is_signer: self.is_signer,
1519 is_writable: self.is_writable,
1520 lamports: unsafe {
1521 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1522 },
1523 data: unsafe {
1524 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1525 },
1526 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1527 executable: self.executable,
1528 _unused: self._unused,
1529 };
1530
1531 unsafe {
1532 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1533 ptr::write_unaligned(
1534 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1535 self.key,
1536 );
1537 ptr::write_unaligned(
1538 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1539 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1540 );
1541 ptr::write_unaligned(
1542 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1543 self.lamports,
1544 );
1545 ptr::write_unaligned(
1546 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1547 self.owner,
1548 );
1549 ptr::write_unaligned(
1550 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1551 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1552 data_addr as *mut u8,
1553 self.data.len(),
1554 ))),
1555 );
1556 data[data_addr - vm_addr..].copy_from_slice(self.data);
1557 }
1558
1559 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1560 (
1561 data,
1562 region,
1563 SerializedAccountMetadata {
1564 vm_addr: vm_addr as u64,
1565 original_data_len: self.data.len(),
1566 vm_key_addr: key_addr as u64,
1567 vm_lamports_addr: lamports_addr as u64,
1568 vm_owner_addr: owner_addr as u64,
1569 vm_data_addr: data_addr as u64,
1570 },
1571 )
1572 }
1573 }
1574
1575 struct MockInstruction {
1576 program_id: Pubkey,
1577 accounts: Vec<AccountMeta>,
1578 data: Vec<u8>,
1579 }
1580
1581 impl MockInstruction {
1582 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1583 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1584
1585 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1586
1587 let mut data = vec![0; size];
1588
1589 let vm_addr = vm_addr as usize;
1590 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1591 let data_addr = accounts_addr + accounts_len;
1592
1593 let ins = Instruction {
1594 program_id: self.program_id,
1595 accounts: unsafe {
1596 Vec::from_raw_parts(
1597 accounts_addr as *mut _,
1598 self.accounts.len(),
1599 self.accounts.len(),
1600 )
1601 },
1602 data: unsafe {
1603 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1604 },
1605 };
1606 let ins = StableInstruction::from(ins);
1607
1608 unsafe {
1609 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1610 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1611 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1612 );
1613 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1614 }
1615
1616 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1617 (data, region)
1618 }
1619 }
1620
1621 #[repr(C)]
1622 struct RcBox<T> {
1623 strong: Cell<usize>,
1624 weak: Cell<usize>,
1625 value: T,
1626 }
1627
1628 impl<T> RcBox<T> {
1629 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1630 fn new(value: T) -> RcBox<T> {
1631 RcBox {
1632 strong: Cell::new(0),
1633 weak: Cell::new(0),
1634 value,
1635 }
1636 }
1637 }
1638
1639 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1640
1641 fn transaction_with_one_writable_instruction_account(
1642 data: Vec<u8>,
1643 ) -> Vec<TestTransactionAccount> {
1644 let program_id = Pubkey::new_unique();
1645 let account = AccountSharedData::from(Account {
1646 lamports: 1,
1647 data,
1648 owner: program_id,
1649 executable: false,
1650 rent_epoch: 100,
1651 });
1652 vec![
1653 (
1654 program_id,
1655 AccountSharedData::from(Account {
1656 lamports: 0,
1657 data: vec![],
1658 owner: bpf_loader::id(),
1659 executable: true,
1660 rent_epoch: 0,
1661 }),
1662 false,
1663 ),
1664 (Pubkey::new_unique(), account, true),
1665 ]
1666 }
1667
1668 fn transaction_with_one_readonly_instruction_account(
1669 data: Vec<u8>,
1670 ) -> Vec<TestTransactionAccount> {
1671 let program_id = Pubkey::new_unique();
1672 let account_owner = Pubkey::new_unique();
1673 let account = AccountSharedData::from(Account {
1674 lamports: 1,
1675 data,
1676 owner: account_owner,
1677 executable: false,
1678 rent_epoch: 100,
1679 });
1680 vec![
1681 (
1682 program_id,
1683 AccountSharedData::from(Account {
1684 lamports: 0,
1685 data: vec![],
1686 owner: bpf_loader::id(),
1687 executable: true,
1688 rent_epoch: 0,
1689 }),
1690 false,
1691 ),
1692 (Pubkey::new_unique(), account, true),
1693 ]
1694 }
1695
1696 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1697 let vm_addr = vm_addr as usize;
1698
1699 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
1702 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1703
1704 let total_size = fat_ptr_size_of_slice
1706 + singers_length * fat_ptr_size_of_slice
1707 + sum_signers_data_length;
1708 let mut data = vec![0; total_size];
1709
1710 data[..fat_ptr_size_of_slice / 2]
1722 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1723 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1724 .clone_from_slice(&(singers_length).to_le_bytes());
1725
1726 let (mut p1, mut p2) = (
1728 fat_ptr_size_of_slice,
1729 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1730 );
1731 for signer in signers.iter() {
1732 let signer_length = signer.len();
1733
1734 data[p1..p1 + fat_ptr_size_of_slice / 2]
1736 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1737 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1738 .clone_from_slice(&(signer_length).to_le_bytes());
1739 p1 += fat_ptr_size_of_slice;
1740
1741 data[p2..p2 + signer_length].clone_from_slice(signer);
1743 p2 += signer_length;
1744 }
1745
1746 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1747 (data, region)
1748 }
1749
1750 #[test]
1751 fn test_translate_instruction() {
1752 let transaction_accounts =
1753 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1754 mock_invoke_context!(
1755 invoke_context,
1756 transaction_context,
1757 b"instruction data",
1758 transaction_accounts,
1759 0,
1760 &[1]
1761 );
1762
1763 let program_id = Pubkey::new_unique();
1764 let accounts = vec![AccountMeta {
1765 pubkey: Pubkey::new_unique(),
1766 is_signer: true,
1767 is_writable: false,
1768 }];
1769 let data = b"ins data".to_vec();
1770 let vm_addr = MM_INPUT_START;
1771 let (_mem, region) = MockInstruction {
1772 program_id,
1773 accounts: accounts.clone(),
1774 data: data.clone(),
1775 }
1776 .into_region(vm_addr);
1777
1778 let config = Config {
1779 aligned_memory_mapping: false,
1780 ..Config::default()
1781 };
1782 let memory_mapping =
1783 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1784 invoke_context
1785 .memory_contexts
1786 .mock_set_mapping_abi_v1(memory_mapping);
1787
1788 let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1789 assert_eq!(ins.program_id, program_id);
1790 assert_eq!(ins.accounts, accounts);
1791 assert_eq!(ins.data, data);
1792 }
1793
1794 #[test]
1795 fn test_translate_signers() {
1796 let transaction_accounts =
1797 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1798 mock_invoke_context!(
1799 invoke_context,
1800 transaction_context,
1801 b"instruction data",
1802 transaction_accounts,
1803 0,
1804 &[1]
1805 );
1806
1807 let program_id = Pubkey::new_unique();
1808 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1809
1810 let vm_addr = MM_INPUT_START;
1811 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1812
1813 let config = Config {
1814 aligned_memory_mapping: false,
1815 ..Config::default()
1816 };
1817 let mapping =
1818 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1819 invoke_context
1820 .memory_contexts
1821 .set_memory_context_abi_v1(MemoryContext::new(
1822 BpfAllocator::new(0),
1823 Vec::new(),
1824 mapping,
1825 ))
1826 .unwrap();
1827
1828 let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1829 assert_eq!(signers[0], derived_key);
1830 }
1831
1832 #[test]
1833 fn test_translate_accounts_rust() {
1834 let transaction_accounts =
1835 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1836 let account = transaction_accounts[1].1.clone();
1837 let key = transaction_accounts[1].0;
1838 let original_data_len = account.data().len();
1839
1840 let vm_addr = MM_STACK_START;
1841 let (_mem, region, account_metadata) =
1842 MockAccountInfo::new(key, &account).into_region(vm_addr);
1843
1844 let config = Config {
1845 aligned_memory_mapping: false,
1846 ..Config::default()
1847 };
1848 let memory_mapping =
1849 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1850
1851 mock_invoke_context!(
1852 invoke_context,
1853 transaction_context,
1854 b"instruction data",
1855 transaction_accounts,
1856 0,
1857 &[1, 1]
1858 );
1859
1860 invoke_context
1861 .memory_contexts
1862 .set_memory_context_abi_v1(MemoryContext::new(
1863 BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1864 vec![account_metadata],
1865 memory_mapping,
1866 ))
1867 .unwrap();
1868
1869 invoke_context
1870 .transaction_context
1871 .configure_next_cpi_for_tests(
1872 0,
1873 vec![
1874 InstructionAccount::new(1, false, true),
1875 InstructionAccount::new(1, false, true),
1876 ],
1877 vec![],
1878 )
1879 .unwrap();
1880
1881 let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1882 assert_eq!(accounts.len(), 1);
1883 let caller_account = &accounts[0].caller_account;
1884 assert_eq!(caller_account.serialized_data, account.data());
1885 assert_eq!(caller_account.original_data_len, original_data_len);
1886 }
1887
1888 #[test]
1889 fn test_get_serialized_data() {
1890 let transaction_accounts =
1891 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1892 let account = transaction_accounts[1].1.clone();
1893 mock_invoke_context!(
1894 invoke_context,
1895 transaction_context,
1896 b"instruction data",
1897 transaction_accounts,
1898 0,
1899 &[1]
1900 );
1901
1902 let config = Config {
1903 aligned_memory_mapping: false,
1904 ..Config::default()
1905 };
1906 let memory_mapping =
1907 unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1908 let serialized_data = unsafe {
1909 CallerAccount::get_serialized_data(
1910 &memory_mapping,
1911 true, MM_INPUT_START,
1913 account.data().len(),
1914 account
1915 .data()
1916 .len()
1917 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1918 .saturating_add(1),
1919 true, false, )
1922 };
1923
1924 assert_matches!(
1925 serialized_data,
1926 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1927 );
1928 }
1929
1930 #[test]
1931 fn test_caller_account_from_account_info() {
1932 let transaction_accounts =
1933 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1934 let account = transaction_accounts[1].1.clone();
1935 mock_invoke_context!(
1936 invoke_context,
1937 transaction_context,
1938 b"instruction data",
1939 transaction_accounts,
1940 0,
1941 &[1]
1942 );
1943
1944 let key = Pubkey::new_unique();
1945 let vm_addr = MM_STACK_START;
1946 let (_mem, region, account_metadata) =
1947 MockAccountInfo::new(key, &account).into_region(vm_addr);
1948
1949 let config = Config {
1950 aligned_memory_mapping: false,
1951 ..Config::default()
1952 };
1953 let memory_mapping =
1954 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1955
1956 let account_info =
1957 translate_type::<stable::AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1958
1959 invoke_context
1960 .memory_contexts
1961 .mock_set_mapping_abi_v1(memory_mapping);
1962 let check_aligned = invoke_context.get_check_aligned();
1963 let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
1964 let caller_account = CallerAccount::from_account_info(
1965 &invoke_context,
1966 memory_mapping,
1967 check_aligned,
1968 vm_addr,
1969 account_info,
1970 &account_metadata,
1971 )
1972 .unwrap();
1973 assert_eq!(*caller_account.lamports, account.lamports());
1974 assert_eq!(caller_account.owner, account.owner());
1975 assert_eq!(caller_account.original_data_len, account.data().len());
1976 assert_eq!(
1977 *caller_account.ref_to_len_in_vm as usize,
1978 account.data().len()
1979 );
1980 assert_eq!(caller_account.serialized_data, account.data());
1981 }
1982
1983 #[case(false, false)]
1984 #[case(true, false)]
1985 #[case(true, true)]
1986 fn test_update_caller_account_lamports_owner(
1987 virtual_address_space_adjustments: bool,
1988 account_data_direct_mapping: bool,
1989 ) {
1990 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1991 let account = transaction_accounts[1].1.clone();
1992 mock_invoke_context!(
1993 invoke_context,
1994 transaction_context,
1995 b"instruction data",
1996 transaction_accounts,
1997 0,
1998 &[1]
1999 );
2000
2001 let mut mock_caller_account =
2002 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2003
2004 let config = Config {
2005 aligned_memory_mapping: false,
2006 ..Config::default()
2007 };
2008 let memory_mapping = unsafe {
2009 MemoryMapping::new(
2010 mock_caller_account.regions.split_off(0),
2011 &config,
2012 SBPFVersion::V3,
2013 )
2014 .unwrap()
2015 };
2016 invoke_context
2017 .memory_contexts
2018 .mock_set_mapping_abi_v1(memory_mapping);
2019
2020 let mut caller_account = mock_caller_account.caller_account();
2021 let instruction_context = invoke_context
2022 .transaction_context
2023 .get_current_instruction_context()
2024 .unwrap();
2025 let mut callee_account = instruction_context
2026 .try_borrow_instruction_account(0)
2027 .unwrap();
2028 callee_account.set_lamports(42).unwrap();
2029 callee_account
2030 .set_owner(Pubkey::new_unique().as_ref())
2031 .unwrap();
2032
2033 update_caller_account(
2034 &invoke_context,
2035 true, &mut caller_account,
2037 &mut callee_account,
2038 virtual_address_space_adjustments,
2039 account_data_direct_mapping,
2040 )
2041 .unwrap();
2042
2043 assert_eq!(*caller_account.lamports, 42);
2044 assert_eq!(caller_account.owner, callee_account.get_owner());
2045 }
2046
2047 #[test]
2048 fn test_update_caller_account_data() {
2049 let transaction_accounts =
2050 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2051 let account = transaction_accounts[1].1.clone();
2052 let original_data_len = account.data().len();
2053
2054 mock_invoke_context!(
2055 invoke_context,
2056 transaction_context,
2057 b"instruction data",
2058 transaction_accounts,
2059 0,
2060 &[1]
2061 );
2062
2063 let mut mock_caller_account =
2064 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2065
2066 let config = Config {
2067 aligned_memory_mapping: false,
2068 ..Config::default()
2069 };
2070 let memory_mapping = unsafe {
2071 MemoryMapping::new(
2072 mock_caller_account.regions.clone(),
2073 &config,
2074 SBPFVersion::V3,
2075 )
2076 .unwrap()
2077 };
2078 invoke_context
2079 .memory_contexts
2080 .mock_set_mapping_abi_v1(memory_mapping);
2081
2082 let data_slice = mock_caller_account.data_slice();
2083 let len_ptr = unsafe {
2084 data_slice
2085 .as_ptr()
2086 .offset(-(mem::size_of::<u64>() as isize))
2087 };
2088 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2089 let mut caller_account = mock_caller_account.caller_account();
2090 let instruction_context = invoke_context
2091 .transaction_context
2092 .get_current_instruction_context()
2093 .unwrap();
2094 let mut callee_account = instruction_context
2095 .try_borrow_instruction_account(0)
2096 .unwrap();
2097
2098 for (new_value, expected_realloc_size) in [
2099 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2100 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2101 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2102 ] {
2103 assert_eq!(caller_account.serialized_data, callee_account.get_data());
2104 callee_account.set_data_from_slice(&new_value).unwrap();
2105
2106 update_caller_account(
2107 &invoke_context,
2108 true, &mut caller_account,
2110 &mut callee_account,
2111 false, false, )
2114 .unwrap();
2115
2116 let data_len = callee_account.get_data().len();
2117 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2118 assert_eq!(data_len, serialized_len());
2119 assert_eq!(data_len, caller_account.serialized_data.len());
2120 assert_eq!(
2121 callee_account.get_data(),
2122 &caller_account.serialized_data[..data_len]
2123 );
2124 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2125 assert!(is_zeroed(&data_slice[data_len..]));
2126 }
2127
2128 callee_account
2129 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2130 .unwrap();
2131 update_caller_account(
2132 &invoke_context,
2133 true, &mut caller_account,
2135 &mut callee_account,
2136 false, false, )
2139 .unwrap();
2140 let data_len = callee_account.get_data().len();
2141 assert_eq!(data_slice[data_len..].len(), 0);
2142 assert!(is_zeroed(&data_slice[data_len..]));
2143
2144 callee_account
2145 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2146 .unwrap();
2147 assert_matches!(
2148 update_caller_account(
2149 &invoke_context,
2150 true, &mut caller_account,
2152 &mut callee_account,
2153 false, false, ),
2156 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2157 );
2158
2159 callee_account.set_data_length(0).unwrap();
2161 callee_account
2162 .set_owner(system_program::id().as_ref())
2163 .unwrap();
2164 update_caller_account(
2165 &invoke_context,
2166 true, &mut caller_account,
2168 &mut callee_account,
2169 false, false, )
2172 .unwrap();
2173 let data_len = callee_account.get_data().len();
2174 assert_eq!(data_len, 0);
2175 }
2176
2177 #[case(false, false)]
2178 #[case(true, false)]
2179 #[case(true, true)]
2180 fn test_update_callee_account_lamports_owner(
2181 virtual_address_space_adjustments: bool,
2182 account_data_direct_mapping: bool,
2183 ) {
2184 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2185 let account = transaction_accounts[1].1.clone();
2186
2187 mock_invoke_context!(
2188 invoke_context,
2189 transaction_context,
2190 b"instruction data",
2191 transaction_accounts,
2192 0,
2193 &[1]
2194 );
2195
2196 let mut mock_caller_account =
2197 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2198 let config = Config {
2199 aligned_memory_mapping: false,
2200 ..Config::default()
2201 };
2202 let memory_mapping = unsafe {
2203 MemoryMapping::new(
2204 mock_caller_account.regions.clone(),
2205 &config,
2206 SBPFVersion::V3,
2207 )
2208 .unwrap()
2209 };
2210 let caller_account = mock_caller_account.caller_account();
2211
2212 borrow_instruction_account!(callee_account, invoke_context, 0);
2213
2214 *caller_account.lamports = 42;
2215 *caller_account.owner = Pubkey::new_unique();
2216
2217 update_callee_account(
2218 &memory_mapping,
2219 true, &caller_account,
2221 callee_account,
2222 virtual_address_space_adjustments,
2223 account_data_direct_mapping,
2224 )
2225 .unwrap();
2226
2227 borrow_instruction_account!(callee_account, invoke_context, 0);
2228 assert_eq!(callee_account.get_lamports(), 42);
2229 assert_eq!(caller_account.owner, callee_account.get_owner());
2230 }
2231
2232 #[case(false, false)]
2233 #[case(true, false)]
2234 #[case(true, true)]
2235 fn test_update_callee_account_data_writable(
2236 virtual_address_space_adjustments: bool,
2237 account_data_direct_mapping: bool,
2238 ) {
2239 let transaction_accounts =
2240 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2241 let account = transaction_accounts[1].1.clone();
2242
2243 mock_invoke_context!(
2244 invoke_context,
2245 transaction_context,
2246 b"instruction data",
2247 transaction_accounts,
2248 0,
2249 &[1]
2250 );
2251
2252 let mut mock_caller_account =
2253 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2254 let config = Config {
2255 aligned_memory_mapping: false,
2256 ..Config::default()
2257 };
2258 let memory_mapping = unsafe {
2259 MemoryMapping::new(
2260 mock_caller_account.regions.clone(),
2261 &config,
2262 SBPFVersion::V3,
2263 )
2264 .unwrap()
2265 };
2266 let mut caller_account = mock_caller_account.caller_account();
2267 borrow_instruction_account!(callee_account, invoke_context, 0);
2268
2269 caller_account.serialized_data[0] = b'b';
2271 update_callee_account(
2272 &memory_mapping,
2273 true, &caller_account,
2275 callee_account,
2276 false, false, )
2279 .unwrap();
2280 borrow_instruction_account!(callee_account, invoke_context, 0);
2281 assert_eq!(callee_account.get_data(), b"boobar");
2282
2283 let mut data = b"foobarbaz".to_vec();
2285 *caller_account.ref_to_len_in_vm = data.len() as u64;
2286 caller_account.serialized_data = &mut data;
2287 assert_eq!(
2288 update_callee_account(
2289 &memory_mapping,
2290 true, &caller_account,
2292 callee_account,
2293 virtual_address_space_adjustments,
2294 account_data_direct_mapping,
2295 )
2296 .unwrap(),
2297 virtual_address_space_adjustments,
2298 );
2299
2300 let mut data = b"baz".to_vec();
2302 *caller_account.ref_to_len_in_vm = data.len() as u64;
2303 caller_account.serialized_data = &mut data;
2304 borrow_instruction_account!(callee_account, invoke_context, 0);
2305 assert_eq!(
2306 update_callee_account(
2307 &memory_mapping,
2308 true, &caller_account,
2310 callee_account,
2311 virtual_address_space_adjustments,
2312 account_data_direct_mapping,
2313 )
2314 .unwrap(),
2315 virtual_address_space_adjustments,
2316 );
2317
2318 let mut data = Vec::new();
2320 caller_account.serialized_data = &mut data;
2321 *caller_account.ref_to_len_in_vm = 0;
2322 let mut owner = system_program::id();
2323 caller_account.owner = &mut owner;
2324 borrow_instruction_account!(callee_account, invoke_context, 0);
2325 update_callee_account(
2326 &memory_mapping,
2327 true, &caller_account,
2329 callee_account,
2330 virtual_address_space_adjustments,
2331 account_data_direct_mapping,
2332 )
2333 .unwrap();
2334 borrow_instruction_account!(callee_account, invoke_context, 0);
2335 assert_eq!(callee_account.get_data(), b"");
2336 }
2337
2338 #[case(false, false)]
2339 #[case(true, false)]
2340 #[case(true, true)]
2341 fn test_update_callee_account_data_readonly(
2342 virtual_address_space_adjustments: bool,
2343 account_data_direct_mapping: bool,
2344 ) {
2345 let transaction_accounts =
2346 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2347 let account = transaction_accounts[1].1.clone();
2348
2349 mock_invoke_context!(
2350 invoke_context,
2351 transaction_context,
2352 b"instruction data",
2353 transaction_accounts,
2354 0,
2355 &[1]
2356 );
2357
2358 let mut mock_caller_account =
2359 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2360 let config = Config {
2361 aligned_memory_mapping: false,
2362 ..Config::default()
2363 };
2364 let memory_mapping = unsafe {
2365 MemoryMapping::new(
2366 mock_caller_account.regions.clone(),
2367 &config,
2368 SBPFVersion::V3,
2369 )
2370 .unwrap()
2371 };
2372 let mut caller_account = mock_caller_account.caller_account();
2373 borrow_instruction_account!(callee_account, invoke_context, 0);
2374
2375 caller_account.serialized_data[0] = b'b';
2377 assert_matches!(
2378 update_callee_account(
2379 &memory_mapping,
2380 true, &caller_account,
2382 callee_account,
2383 false, false, ),
2386 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2387 );
2388
2389 let mut data = b"foobarbaz".to_vec();
2391 *caller_account.ref_to_len_in_vm = data.len() as u64;
2392 caller_account.serialized_data = &mut data;
2393 borrow_instruction_account!(callee_account, invoke_context, 0);
2394 assert_matches!(
2395 update_callee_account(
2396 &memory_mapping,
2397 true, &caller_account,
2399 callee_account,
2400 virtual_address_space_adjustments,
2401 account_data_direct_mapping,
2402 ),
2403 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2404 );
2405
2406 let mut data = b"baz".to_vec();
2408 *caller_account.ref_to_len_in_vm = data.len() as u64;
2409 caller_account.serialized_data = &mut data;
2410 borrow_instruction_account!(callee_account, invoke_context, 0);
2411 assert_matches!(
2412 update_callee_account(
2413 &memory_mapping,
2414 true, &caller_account,
2416 callee_account,
2417 virtual_address_space_adjustments,
2418 account_data_direct_mapping,
2419 ),
2420 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2421 );
2422 }
2423}