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