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.prepare_next_cpi_instruction(instruction, &signers)?;
822
823 let mut accounts =
824 S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
825
826 let transaction_context = &invoke_context.transaction_context;
831 let instruction_context = transaction_context.get_current_instruction_context()?;
832 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
833 for translated_account in accounts.iter_mut() {
834 let callee_account = instruction_context
835 .try_borrow_instruction_account(translated_account.index_in_caller)?;
836 let update_caller = update_callee_account(
837 memory_mapping,
838 check_aligned,
839 &translated_account.caller_account,
840 callee_account,
841 virtual_address_space_adjustments,
842 account_data_direct_mapping,
843 )?;
844 translated_account.update_caller_account_region =
845 translated_account.update_caller_account_info || update_caller;
846 }
847
848 let mut compute_units_consumed = 0;
850 invoke_context
851 .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
852
853 let transaction_context = &invoke_context.transaction_context;
855 let instruction_context = transaction_context.get_current_instruction_context()?;
856
857 for translated_account in accounts.iter_mut() {
861 let mut callee_account = instruction_context
862 .try_borrow_instruction_account(translated_account.index_in_caller)?;
863 if translated_account.update_caller_account_info {
864 update_caller_account(
865 invoke_context,
866 check_aligned,
867 &mut translated_account.caller_account,
868 &mut callee_account,
869 virtual_address_space_adjustments,
870 account_data_direct_mapping,
871 )?;
872 }
873 }
874
875 if virtual_address_space_adjustments {
876 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
877 for translated_account in accounts.iter() {
878 let mut callee_account = instruction_context
879 .try_borrow_instruction_account(translated_account.index_in_caller)?;
880 if translated_account.update_caller_account_region {
881 unsafe {
882 update_caller_account_region(
887 memory_mapping,
888 check_aligned,
889 &translated_account.caller_account,
890 &mut callee_account,
891 account_data_direct_mapping,
892 )?;
893 }
894 }
895 }
896 }
897
898 Ok(SUCCESS)
899}
900
901pub struct TranslatedAccount<'a> {
903 pub index_in_caller: IndexOfAccount,
904 pub caller_account: CallerAccount<'a>,
905 pub update_caller_account_region: bool,
906 pub update_caller_account_info: bool,
907}
908
909fn translate_account_infos<T, R>(
910 account_infos_addr: u64,
911 account_infos_len: u64,
912 key_addr: impl Fn(&T) -> u64,
913 invoke_context: &InvokeContext,
914 memory_mapping: &MemoryMapping,
915 check_aligned: bool,
916 cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
917) -> Result<R, Error> {
918 if account_infos_addr
922 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
923 >= ebpf::MM_INPUT_START
924 {
925 return Err(CpiError::InvalidPointer.into());
926 }
927
928 let account_infos = translate_slice::<T>(
929 memory_mapping,
930 account_infos_addr,
931 account_infos_len,
932 check_aligned,
933 )?;
934 check_account_infos(account_infos.len())?;
935
936 let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
937
938 let amount = (account_infos_bytes as u64)
939 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
940 .unwrap_or(u64::MAX);
941 invoke_context.consume_checked(amount)?;
942
943 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
944 #[expect(clippy::needless_range_loop)]
945 for account_index in 0..account_infos_len as usize {
946 #[expect(clippy::indexing_slicing)]
947 let account_info = &account_infos[account_index];
948 account_info_keys.push(translate_type::<Pubkey>(
949 memory_mapping,
950 key_addr(account_info),
951 check_aligned,
952 )?);
953 }
954 Ok(cb(account_infos, account_info_keys))
955}
956
957fn translate_accounts_common<'a, T, F>(
959 account_info_keys: &[&Pubkey],
960 account_infos: &[T],
961 account_infos_addr: u64,
962 invoke_context: &InvokeContext,
963 memory_mapping: &MemoryMapping,
964 check_aligned: bool,
965 do_translate: F,
966) -> Result<Vec<TranslatedAccount<'a>>, Error>
967where
968 F: Fn(
969 &InvokeContext,
970 &MemoryMapping,
971 bool,
972 u64,
973 &T,
974 &SerializedAccountMetadata,
975 ) -> Result<CallerAccount<'a>, Error>,
976{
977 let transaction_context = &invoke_context.transaction_context;
978 let next_instruction_context = transaction_context.get_next_instruction_context()?;
979 let next_instruction_accounts = next_instruction_context.instruction_accounts();
980 let instruction_context = transaction_context.get_current_instruction_context()?;
981 let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
982
983 let accounts_metadata = &invoke_context
986 .memory_contexts
987 .memory_context_abi_v1()
988 .unwrap()
989 .accounts_metadata;
990
991 for (instruction_account_index, instruction_account) in
992 next_instruction_accounts.iter().enumerate()
993 {
994 if next_instruction_context
995 .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
996 .is_some()
997 {
998 continue; }
1000
1001 let index_in_caller = instruction_context
1002 .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
1003 let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
1004 let account_key = invoke_context
1005 .transaction_context
1006 .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
1007
1008 #[expect(deprecated)]
1009 if callee_account.is_executable() {
1010 let amount = (callee_account.get_data().len() as u64)
1012 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1013 .unwrap_or(u64::MAX);
1014 invoke_context.consume_checked(amount)?;
1015 } else if let Some(caller_account_index) =
1016 account_info_keys.iter().position(|key| *key == account_key)
1017 {
1018 let serialized_metadata =
1019 accounts_metadata
1020 .get(index_in_caller as usize)
1021 .ok_or_else(|| {
1022 ic_msg!(
1023 invoke_context,
1024 "Internal error: index mismatch for account {}",
1025 account_key
1026 );
1027 Box::new(InstructionError::MissingAccount) as Error
1028 })?;
1029
1030 if caller_account_index >= account_infos.len() {
1032 return Err(Box::new(CpiError::InvalidLength));
1033 }
1034 #[expect(clippy::indexing_slicing)]
1035 let caller_account =
1036 do_translate(
1037 invoke_context,
1038 memory_mapping,
1039 check_aligned,
1040 account_infos_addr.saturating_add(
1041 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
1042 ),
1043 &account_infos[caller_account_index],
1044 serialized_metadata,
1045 )?;
1046
1047 let amount = (*caller_account.ref_to_len_in_vm)
1048 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1049 .unwrap_or(u64::MAX);
1050 invoke_context.consume_checked(amount)?;
1051
1052 accounts.push(TranslatedAccount {
1053 index_in_caller,
1054 caller_account,
1055 update_caller_account_region: true, update_caller_account_info: instruction_account.is_writable(),
1057 });
1058 } else {
1059 ic_msg!(
1060 invoke_context,
1061 "Instruction references an unknown account {}",
1062 account_key
1063 );
1064 return Err(Box::new(InstructionError::MissingAccount));
1065 }
1066 }
1067
1068 Ok(accounts)
1069}
1070
1071fn update_callee_account(
1083 memory_mapping: &MemoryMapping,
1084 check_aligned: bool,
1085 caller_account: &CallerAccount,
1086 mut callee_account: BorrowedInstructionAccount<'_, '_>,
1087 virtual_address_space_adjustments: bool,
1088 account_data_direct_mapping: bool,
1089) -> Result<bool, Error> {
1090 let mut must_update_caller = false;
1091
1092 if callee_account.get_lamports() != *caller_account.lamports {
1093 callee_account.set_lamports(*caller_account.lamports)?;
1094 }
1095
1096 if virtual_address_space_adjustments {
1097 let prev_len = callee_account.get_data().len();
1098 let post_len = *caller_account.ref_to_len_in_vm as usize;
1099 if prev_len != post_len {
1100 if !account_data_direct_mapping && post_len < prev_len {
1101 let serialized_data = unsafe {
1104 CallerAccount::get_serialized_data(
1105 memory_mapping,
1106 check_aligned,
1107 caller_account.vm_data_addr,
1108 caller_account.original_data_len,
1109 prev_len,
1110 virtual_address_space_adjustments,
1111 account_data_direct_mapping,
1112 )?
1113 };
1114 serialized_data
1115 .get_mut(post_len..)
1116 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1117 .fill(0);
1118 }
1119 callee_account.set_data_length(post_len)?;
1120 must_update_caller = true;
1122 }
1123 if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1124 callee_account.set_data_from_slice(caller_account.serialized_data)?;
1125 }
1126 } else {
1127 match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1129 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1130 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1131 return Err(Box::new(err));
1132 }
1133 _ => {}
1134 }
1135 }
1136
1137 if callee_account.get_owner() != caller_account.owner {
1139 callee_account.set_owner(caller_account.owner.as_ref())?;
1140 must_update_caller = true;
1142 }
1143
1144 Ok(must_update_caller)
1145}
1146
1147unsafe fn update_caller_account_region(
1152 memory_mapping: &mut MemoryMapping,
1153 check_aligned: bool,
1154 caller_account: &CallerAccount,
1155 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1156 account_data_direct_mapping: bool,
1157) -> Result<(), Error> {
1158 let is_caller_loader_deprecated = !check_aligned;
1159 let address_space_reserved_for_account =
1160 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1161
1162 if address_space_reserved_for_account > 0 {
1163 let (region_index, region) = memory_mapping
1166 .find_region(caller_account.vm_data_addr)
1167 .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1168 let region_start_vm_addr = region.vm_addr_range().start;
1170 debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1171 let mut new_region;
1172 if !account_data_direct_mapping {
1173 new_region = region.clone();
1174 modify_memory_region_of_account(callee_account, &mut new_region);
1175 } else {
1176 new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1177 }
1178 unsafe {
1179 memory_mapping.replace_region(region_index, new_region)?;
1184 }
1185 }
1186
1187 Ok(())
1188}
1189
1190fn update_caller_account(
1199 invoke_context: &InvokeContext,
1200 check_aligned: bool,
1201 caller_account: &mut CallerAccount<'_>,
1202 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1203 virtual_address_space_adjustments: bool,
1204 account_data_direct_mapping: bool,
1205) -> Result<(), Error> {
1206 *caller_account.lamports = callee_account.get_lamports();
1207 *caller_account.owner = *callee_account.get_owner();
1208
1209 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1210 let post_len = callee_account.get_data().len();
1211 let is_caller_loader_deprecated = !check_aligned;
1212 let address_space_reserved_for_account =
1213 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1214
1215 if post_len > address_space_reserved_for_account {
1216 let max_increase =
1217 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1218 ic_msg!(
1219 invoke_context,
1220 "Account data size realloc limited to {max_increase} in inner instructions",
1221 );
1222 return Err(Box::new(InstructionError::InvalidRealloc));
1223 }
1224
1225 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1226 if prev_len != post_len {
1227 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1230 if post_len < prev_len {
1233 caller_account
1234 .serialized_data
1235 .get_mut(post_len..)
1236 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1237 .fill(0);
1238 }
1239 unsafe {
1241 caller_account.serialized_data = CallerAccount::get_serialized_data(
1242 memory_mapping,
1243 check_aligned,
1244 caller_account.vm_data_addr,
1245 caller_account.original_data_len,
1246 post_len,
1247 virtual_address_space_adjustments,
1248 account_data_direct_mapping,
1249 )?;
1250 }
1251 }
1252 *caller_account.ref_to_len_in_vm = post_len as u64;
1254
1255 let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1257 memory_mapping,
1258 caller_account
1259 .vm_data_addr
1260 .saturating_sub(std::mem::size_of::<u64>() as u64),
1261 check_aligned,
1262 )?;
1263 *serialized_len_ptr = post_len as u64;
1264 }
1265
1266 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1267 let to_slice = &mut caller_account.serialized_data;
1269 let from_slice = callee_account
1270 .get_data()
1271 .get(0..post_len)
1272 .ok_or(CpiError::InvalidLength)?;
1273 if to_slice.len() != from_slice.len() {
1274 return Err(Box::new(InstructionError::AccountDataTooSmall));
1275 }
1276 to_slice.copy_from_slice(from_slice);
1277 }
1278
1279 Ok(())
1280}
1281
1282#[allow(clippy::indexing_slicing)]
1283#[allow(clippy::arithmetic_side_effects)]
1284#[cfg(test)]
1285mod tests {
1286 use {
1287 super::*,
1288 crate::{
1289 invoke_context::BpfAllocator,
1290 memory::translate_type,
1291 memory_context::{MemoryContext, SerializedAccountMetadata},
1292 with_mock_invoke_context_with_feature_set,
1293 },
1294 assert_matches::assert_matches,
1295 solana_account::{Account, AccountSharedData, ReadableAccount},
1296 solana_account_info::AccountInfo,
1297 solana_sbpf::{
1298 ebpf::{MM_INPUT_START, MM_STACK_START},
1299 memory_region::MemoryRegion,
1300 program::SBPFVersion,
1301 vm::Config,
1302 },
1303 solana_sdk_ids::{bpf_loader, system_program},
1304 solana_svm_feature_set::SVMFeatureSet,
1305 solana_transaction_context::{
1306 IndexOfAccount, instruction_accounts::InstructionAccount,
1307 transaction_accounts::KeyedAccountSharedData,
1308 },
1309 std::{
1310 cell::{Cell, RefCell},
1311 mem, ptr,
1312 rc::Rc,
1313 slice,
1314 },
1315 test_case::case,
1316 };
1317
1318 macro_rules! mock_invoke_context {
1319 ($invoke_context:ident,
1320 $transaction_context:ident,
1321 $instruction_data:expr,
1322 $transaction_accounts:expr,
1323 $program_account:expr,
1324 $instruction_accounts:expr) => {
1325 let instruction_data = $instruction_data;
1326 let instruction_accounts = $instruction_accounts
1327 .iter()
1328 .map(|index_in_transaction| {
1329 InstructionAccount::new(
1330 *index_in_transaction as IndexOfAccount,
1331 false,
1332 $transaction_accounts[*index_in_transaction as usize].2,
1333 )
1334 })
1335 .collect::<Vec<_>>();
1336 let transaction_accounts = $transaction_accounts
1337 .into_iter()
1338 .map(|a| (a.0, a.1))
1339 .collect::<Vec<KeyedAccountSharedData>>();
1340 let mut feature_set = SVMFeatureSet::all_enabled();
1341 feature_set.virtual_address_space_adjustments = false;
1342 feature_set.account_data_direct_mapping = false;
1343 let feature_set = &feature_set;
1344 with_mock_invoke_context_with_feature_set!(
1345 $invoke_context,
1346 $transaction_context,
1347 feature_set,
1348 transaction_accounts
1349 );
1350 $invoke_context
1351 .transaction_context
1352 .configure_top_level_instruction_for_tests(
1353 $program_account,
1354 instruction_accounts,
1355 instruction_data.to_vec(),
1356 )
1357 .unwrap();
1358 $invoke_context.push().unwrap();
1359 };
1360 }
1361
1362 macro_rules! borrow_instruction_account {
1363 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1364 let instruction_context = $invoke_context
1365 .transaction_context
1366 .get_current_instruction_context()
1367 .unwrap();
1368 let $borrowed_account = instruction_context
1369 .try_borrow_instruction_account($index)
1370 .unwrap();
1371 };
1372 }
1373
1374 fn is_zeroed(data: &[u8]) -> bool {
1375 data.iter().all(|b| *b == 0)
1376 }
1377
1378 struct MockCallerAccount {
1379 lamports: u64,
1380 owner: Pubkey,
1381 vm_addr: u64,
1382 data: Vec<u8>,
1383 len: u64,
1384 regions: Vec<MemoryRegion>,
1385 virtual_address_space_adjustments: bool,
1386 }
1387
1388 impl MockCallerAccount {
1389 fn new(
1390 lamports: u64,
1391 owner: Pubkey,
1392 data: &[u8],
1393 virtual_address_space_adjustments: bool,
1394 ) -> MockCallerAccount {
1395 let vm_addr = MM_INPUT_START;
1396 let mut region_addr = vm_addr;
1397 let region_len = mem::size_of::<u64>()
1398 + if virtual_address_space_adjustments {
1399 0
1400 } else {
1401 data.len() + MAX_PERMITTED_DATA_INCREASE
1402 };
1403 let mut d = vec![0; region_len];
1404 let mut regions = vec![];
1405
1406 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1408
1409 if !virtual_address_space_adjustments {
1411 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1412 }
1413
1414 regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1416 region_addr += region_len as u64;
1417
1418 if virtual_address_space_adjustments {
1419 regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1421 region_addr += data.len() as u64;
1422
1423 regions.push(MemoryRegion::new(
1425 &raw mut d[mem::size_of::<u64>()..],
1426 region_addr,
1427 ));
1428 } else {
1429 d.truncate(mem::size_of::<u64>() + data.len());
1431 }
1432
1433 MockCallerAccount {
1434 lamports,
1435 owner,
1436 vm_addr,
1437 data: d,
1438 len: data.len() as u64,
1439 regions,
1440 virtual_address_space_adjustments,
1441 }
1442 }
1443
1444 fn data_slice<'a>(&self) -> &'a [u8] {
1445 unsafe {
1447 slice::from_raw_parts(
1448 self.data[mem::size_of::<u64>()..].as_ptr(),
1449 self.data.capacity() - mem::size_of::<u64>(),
1450 )
1451 }
1452 }
1453
1454 fn caller_account(&mut self) -> CallerAccount<'_> {
1455 let data = if self.virtual_address_space_adjustments {
1456 &mut []
1457 } else {
1458 &mut self.data[mem::size_of::<u64>()..]
1459 };
1460 CallerAccount {
1461 lamports: &mut self.lamports,
1462 owner: &mut self.owner,
1463 original_data_len: self.len as usize,
1464 serialized_data: data,
1465 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1466 ref_to_len_in_vm: &mut self.len,
1467 }
1468 }
1469 }
1470
1471 struct MockAccountInfo<'a> {
1472 key: Pubkey,
1473 is_signer: bool,
1474 is_writable: bool,
1475 lamports: u64,
1476 data: &'a [u8],
1477 owner: Pubkey,
1478 executable: bool,
1479 _unused: u64,
1480 }
1481
1482 impl MockAccountInfo<'_> {
1483 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1484 MockAccountInfo {
1485 key,
1486 is_signer: false,
1487 is_writable: false,
1488 lamports: account.lamports(),
1489 data: account.data(),
1490 owner: *account.owner(),
1491 executable: account.executable(),
1492 _unused: account.rent_epoch(),
1493 }
1494 }
1495
1496 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1497 let size = mem::size_of::<AccountInfo>()
1498 + mem::size_of::<Pubkey>() * 2
1499 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1500 + mem::size_of::<u64>()
1501 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1502 + self.data.len();
1503 let mut data = vec![0; size];
1504
1505 let vm_addr = vm_addr as usize;
1506 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1507 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1508 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1509 let owner_addr = lamports_addr + mem::size_of::<u64>();
1510 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1511 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1512
1513 #[allow(deprecated)]
1514 #[allow(clippy::used_underscore_binding)]
1515 let info = AccountInfo {
1516 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1517 is_signer: self.is_signer,
1518 is_writable: self.is_writable,
1519 lamports: unsafe {
1520 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1521 },
1522 data: unsafe {
1523 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1524 },
1525 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1526 executable: self.executable,
1527 _unused: self._unused,
1528 };
1529
1530 unsafe {
1531 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1532 ptr::write_unaligned(
1533 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1534 self.key,
1535 );
1536 ptr::write_unaligned(
1537 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1538 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1539 );
1540 ptr::write_unaligned(
1541 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1542 self.lamports,
1543 );
1544 ptr::write_unaligned(
1545 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1546 self.owner,
1547 );
1548 ptr::write_unaligned(
1549 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1550 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1551 data_addr as *mut u8,
1552 self.data.len(),
1553 ))),
1554 );
1555 data[data_addr - vm_addr..].copy_from_slice(self.data);
1556 }
1557
1558 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1559 (
1560 data,
1561 region,
1562 SerializedAccountMetadata {
1563 vm_addr: vm_addr as u64,
1564 original_data_len: self.data.len(),
1565 vm_key_addr: key_addr as u64,
1566 vm_lamports_addr: lamports_addr as u64,
1567 vm_owner_addr: owner_addr as u64,
1568 vm_data_addr: data_addr as u64,
1569 },
1570 )
1571 }
1572 }
1573
1574 struct MockInstruction {
1575 program_id: Pubkey,
1576 accounts: Vec<AccountMeta>,
1577 data: Vec<u8>,
1578 }
1579
1580 impl MockInstruction {
1581 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1582 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1583
1584 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1585
1586 let mut data = vec![0; size];
1587
1588 let vm_addr = vm_addr as usize;
1589 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1590 let data_addr = accounts_addr + accounts_len;
1591
1592 let ins = Instruction {
1593 program_id: self.program_id,
1594 accounts: unsafe {
1595 Vec::from_raw_parts(
1596 accounts_addr as *mut _,
1597 self.accounts.len(),
1598 self.accounts.len(),
1599 )
1600 },
1601 data: unsafe {
1602 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1603 },
1604 };
1605 let ins = StableInstruction::from(ins);
1606
1607 unsafe {
1608 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1609 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1610 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1611 );
1612 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1613 }
1614
1615 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1616 (data, region)
1617 }
1618 }
1619
1620 #[repr(C)]
1621 struct RcBox<T> {
1622 strong: Cell<usize>,
1623 weak: Cell<usize>,
1624 value: T,
1625 }
1626
1627 impl<T> RcBox<T> {
1628 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1629 fn new(value: T) -> RcBox<T> {
1630 RcBox {
1631 strong: Cell::new(0),
1632 weak: Cell::new(0),
1633 value,
1634 }
1635 }
1636 }
1637
1638 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1639
1640 fn transaction_with_one_writable_instruction_account(
1641 data: Vec<u8>,
1642 ) -> Vec<TestTransactionAccount> {
1643 let program_id = Pubkey::new_unique();
1644 let account = AccountSharedData::from(Account {
1645 lamports: 1,
1646 data,
1647 owner: program_id,
1648 executable: false,
1649 rent_epoch: 100,
1650 });
1651 vec![
1652 (
1653 program_id,
1654 AccountSharedData::from(Account {
1655 lamports: 0,
1656 data: vec![],
1657 owner: bpf_loader::id(),
1658 executable: true,
1659 rent_epoch: 0,
1660 }),
1661 false,
1662 ),
1663 (Pubkey::new_unique(), account, true),
1664 ]
1665 }
1666
1667 fn transaction_with_one_readonly_instruction_account(
1668 data: Vec<u8>,
1669 ) -> Vec<TestTransactionAccount> {
1670 let program_id = Pubkey::new_unique();
1671 let account_owner = Pubkey::new_unique();
1672 let account = AccountSharedData::from(Account {
1673 lamports: 1,
1674 data,
1675 owner: account_owner,
1676 executable: false,
1677 rent_epoch: 100,
1678 });
1679 vec![
1680 (
1681 program_id,
1682 AccountSharedData::from(Account {
1683 lamports: 0,
1684 data: vec![],
1685 owner: bpf_loader::id(),
1686 executable: true,
1687 rent_epoch: 0,
1688 }),
1689 false,
1690 ),
1691 (Pubkey::new_unique(), account, true),
1692 ]
1693 }
1694
1695 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1696 let vm_addr = vm_addr as usize;
1697
1698 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
1701 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1702
1703 let total_size = fat_ptr_size_of_slice
1705 + singers_length * fat_ptr_size_of_slice
1706 + sum_signers_data_length;
1707 let mut data = vec![0; total_size];
1708
1709 data[..fat_ptr_size_of_slice / 2]
1721 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1722 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1723 .clone_from_slice(&(singers_length).to_le_bytes());
1724
1725 let (mut p1, mut p2) = (
1727 fat_ptr_size_of_slice,
1728 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1729 );
1730 for signer in signers.iter() {
1731 let signer_length = signer.len();
1732
1733 data[p1..p1 + fat_ptr_size_of_slice / 2]
1735 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1736 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1737 .clone_from_slice(&(signer_length).to_le_bytes());
1738 p1 += fat_ptr_size_of_slice;
1739
1740 data[p2..p2 + signer_length].clone_from_slice(signer);
1742 p2 += signer_length;
1743 }
1744
1745 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1746 (data, region)
1747 }
1748
1749 #[test]
1750 fn test_translate_instruction() {
1751 let transaction_accounts =
1752 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1753 mock_invoke_context!(
1754 invoke_context,
1755 transaction_context,
1756 b"instruction data",
1757 transaction_accounts,
1758 0,
1759 &[1]
1760 );
1761
1762 let program_id = Pubkey::new_unique();
1763 let accounts = vec![AccountMeta {
1764 pubkey: Pubkey::new_unique(),
1765 is_signer: true,
1766 is_writable: false,
1767 }];
1768 let data = b"ins data".to_vec();
1769 let vm_addr = MM_INPUT_START;
1770 let (_mem, region) = MockInstruction {
1771 program_id,
1772 accounts: accounts.clone(),
1773 data: data.clone(),
1774 }
1775 .into_region(vm_addr);
1776
1777 let config = Config {
1778 aligned_memory_mapping: false,
1779 ..Config::default()
1780 };
1781 let memory_mapping =
1782 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1783 invoke_context
1784 .memory_contexts
1785 .mock_set_mapping_abi_v1(memory_mapping);
1786
1787 let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1788 assert_eq!(ins.program_id, program_id);
1789 assert_eq!(ins.accounts, accounts);
1790 assert_eq!(ins.data, data);
1791 }
1792
1793 #[test]
1794 fn test_translate_signers() {
1795 let transaction_accounts =
1796 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1797 mock_invoke_context!(
1798 invoke_context,
1799 transaction_context,
1800 b"instruction data",
1801 transaction_accounts,
1802 0,
1803 &[1]
1804 );
1805
1806 let program_id = Pubkey::new_unique();
1807 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1808
1809 let vm_addr = MM_INPUT_START;
1810 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1811
1812 let config = Config {
1813 aligned_memory_mapping: false,
1814 ..Config::default()
1815 };
1816 let mapping =
1817 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1818 invoke_context
1819 .memory_contexts
1820 .set_memory_context_abi_v1(MemoryContext::new(
1821 BpfAllocator::new(0),
1822 Vec::new(),
1823 mapping,
1824 ))
1825 .unwrap();
1826
1827 let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1828 assert_eq!(signers[0], derived_key);
1829 }
1830
1831 #[test]
1832 fn test_translate_accounts_rust() {
1833 let transaction_accounts =
1834 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1835 let account = transaction_accounts[1].1.clone();
1836 let key = transaction_accounts[1].0;
1837 let original_data_len = account.data().len();
1838
1839 let vm_addr = MM_STACK_START;
1840 let (_mem, region, account_metadata) =
1841 MockAccountInfo::new(key, &account).into_region(vm_addr);
1842
1843 let config = Config {
1844 aligned_memory_mapping: false,
1845 ..Config::default()
1846 };
1847 let memory_mapping =
1848 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1849
1850 mock_invoke_context!(
1851 invoke_context,
1852 transaction_context,
1853 b"instruction data",
1854 transaction_accounts,
1855 0,
1856 &[1, 1]
1857 );
1858
1859 invoke_context
1860 .memory_contexts
1861 .set_memory_context_abi_v1(MemoryContext::new(
1862 BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1863 vec![account_metadata],
1864 memory_mapping,
1865 ))
1866 .unwrap();
1867
1868 invoke_context
1869 .transaction_context
1870 .configure_next_cpi_for_tests(
1871 0,
1872 vec![
1873 InstructionAccount::new(1, false, true),
1874 InstructionAccount::new(1, false, true),
1875 ],
1876 vec![],
1877 )
1878 .unwrap();
1879
1880 let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1881 assert_eq!(accounts.len(), 1);
1882 let caller_account = &accounts[0].caller_account;
1883 assert_eq!(caller_account.serialized_data, account.data());
1884 assert_eq!(caller_account.original_data_len, original_data_len);
1885 }
1886
1887 #[test]
1888 fn test_get_serialized_data() {
1889 let transaction_accounts =
1890 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1891 let account = transaction_accounts[1].1.clone();
1892 mock_invoke_context!(
1893 invoke_context,
1894 transaction_context,
1895 b"instruction data",
1896 transaction_accounts,
1897 0,
1898 &[1]
1899 );
1900
1901 let config = Config {
1902 aligned_memory_mapping: false,
1903 ..Config::default()
1904 };
1905 let memory_mapping =
1906 unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1907 let serialized_data = unsafe {
1908 CallerAccount::get_serialized_data(
1909 &memory_mapping,
1910 true, MM_INPUT_START,
1912 account.data().len(),
1913 account
1914 .data()
1915 .len()
1916 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1917 .saturating_add(1),
1918 true, false, )
1921 };
1922
1923 assert_matches!(
1924 serialized_data,
1925 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1926 );
1927 }
1928
1929 #[test]
1930 fn test_caller_account_from_account_info() {
1931 let transaction_accounts =
1932 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1933 let account = transaction_accounts[1].1.clone();
1934 mock_invoke_context!(
1935 invoke_context,
1936 transaction_context,
1937 b"instruction data",
1938 transaction_accounts,
1939 0,
1940 &[1]
1941 );
1942
1943 let key = Pubkey::new_unique();
1944 let vm_addr = MM_STACK_START;
1945 let (_mem, region, account_metadata) =
1946 MockAccountInfo::new(key, &account).into_region(vm_addr);
1947
1948 let config = Config {
1949 aligned_memory_mapping: false,
1950 ..Config::default()
1951 };
1952 let memory_mapping =
1953 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1954
1955 let account_info =
1956 translate_type::<stable::AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1957
1958 invoke_context
1959 .memory_contexts
1960 .mock_set_mapping_abi_v1(memory_mapping);
1961 let check_aligned = invoke_context.get_check_aligned();
1962 let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
1963 let caller_account = CallerAccount::from_account_info(
1964 &invoke_context,
1965 memory_mapping,
1966 check_aligned,
1967 vm_addr,
1968 account_info,
1969 &account_metadata,
1970 )
1971 .unwrap();
1972 assert_eq!(*caller_account.lamports, account.lamports());
1973 assert_eq!(caller_account.owner, account.owner());
1974 assert_eq!(caller_account.original_data_len, account.data().len());
1975 assert_eq!(
1976 *caller_account.ref_to_len_in_vm as usize,
1977 account.data().len()
1978 );
1979 assert_eq!(caller_account.serialized_data, account.data());
1980 }
1981
1982 #[case(false, false)]
1983 #[case(true, false)]
1984 #[case(true, true)]
1985 fn test_update_caller_account_lamports_owner(
1986 virtual_address_space_adjustments: bool,
1987 account_data_direct_mapping: bool,
1988 ) {
1989 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1990 let account = transaction_accounts[1].1.clone();
1991 mock_invoke_context!(
1992 invoke_context,
1993 transaction_context,
1994 b"instruction data",
1995 transaction_accounts,
1996 0,
1997 &[1]
1998 );
1999
2000 let mut mock_caller_account =
2001 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2002
2003 let config = Config {
2004 aligned_memory_mapping: false,
2005 ..Config::default()
2006 };
2007 let memory_mapping = unsafe {
2008 MemoryMapping::new(
2009 mock_caller_account.regions.split_off(0),
2010 &config,
2011 SBPFVersion::V3,
2012 )
2013 .unwrap()
2014 };
2015 invoke_context
2016 .memory_contexts
2017 .mock_set_mapping_abi_v1(memory_mapping);
2018
2019 let mut caller_account = mock_caller_account.caller_account();
2020 let instruction_context = invoke_context
2021 .transaction_context
2022 .get_current_instruction_context()
2023 .unwrap();
2024 let mut callee_account = instruction_context
2025 .try_borrow_instruction_account(0)
2026 .unwrap();
2027 callee_account.set_lamports(42).unwrap();
2028 callee_account
2029 .set_owner(Pubkey::new_unique().as_ref())
2030 .unwrap();
2031
2032 update_caller_account(
2033 &invoke_context,
2034 true, &mut caller_account,
2036 &mut callee_account,
2037 virtual_address_space_adjustments,
2038 account_data_direct_mapping,
2039 )
2040 .unwrap();
2041
2042 assert_eq!(*caller_account.lamports, 42);
2043 assert_eq!(caller_account.owner, callee_account.get_owner());
2044 }
2045
2046 #[test]
2047 fn test_update_caller_account_data() {
2048 let transaction_accounts =
2049 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2050 let account = transaction_accounts[1].1.clone();
2051 let original_data_len = account.data().len();
2052
2053 mock_invoke_context!(
2054 invoke_context,
2055 transaction_context,
2056 b"instruction data",
2057 transaction_accounts,
2058 0,
2059 &[1]
2060 );
2061
2062 let mut mock_caller_account =
2063 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2064
2065 let config = Config {
2066 aligned_memory_mapping: false,
2067 ..Config::default()
2068 };
2069 let memory_mapping = unsafe {
2070 MemoryMapping::new(
2071 mock_caller_account.regions.clone(),
2072 &config,
2073 SBPFVersion::V3,
2074 )
2075 .unwrap()
2076 };
2077 invoke_context
2078 .memory_contexts
2079 .mock_set_mapping_abi_v1(memory_mapping);
2080
2081 let data_slice = mock_caller_account.data_slice();
2082 let len_ptr = unsafe {
2083 data_slice
2084 .as_ptr()
2085 .offset(-(mem::size_of::<u64>() as isize))
2086 };
2087 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2088 let mut caller_account = mock_caller_account.caller_account();
2089 let instruction_context = invoke_context
2090 .transaction_context
2091 .get_current_instruction_context()
2092 .unwrap();
2093 let mut callee_account = instruction_context
2094 .try_borrow_instruction_account(0)
2095 .unwrap();
2096
2097 for (new_value, expected_realloc_size) in [
2098 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2099 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2100 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2101 ] {
2102 assert_eq!(caller_account.serialized_data, callee_account.get_data());
2103 callee_account.set_data_from_slice(&new_value).unwrap();
2104
2105 update_caller_account(
2106 &invoke_context,
2107 true, &mut caller_account,
2109 &mut callee_account,
2110 false, false, )
2113 .unwrap();
2114
2115 let data_len = callee_account.get_data().len();
2116 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2117 assert_eq!(data_len, serialized_len());
2118 assert_eq!(data_len, caller_account.serialized_data.len());
2119 assert_eq!(
2120 callee_account.get_data(),
2121 &caller_account.serialized_data[..data_len]
2122 );
2123 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2124 assert!(is_zeroed(&data_slice[data_len..]));
2125 }
2126
2127 callee_account
2128 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2129 .unwrap();
2130 update_caller_account(
2131 &invoke_context,
2132 true, &mut caller_account,
2134 &mut callee_account,
2135 false, false, )
2138 .unwrap();
2139 let data_len = callee_account.get_data().len();
2140 assert_eq!(data_slice[data_len..].len(), 0);
2141 assert!(is_zeroed(&data_slice[data_len..]));
2142
2143 callee_account
2144 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2145 .unwrap();
2146 assert_matches!(
2147 update_caller_account(
2148 &invoke_context,
2149 true, &mut caller_account,
2151 &mut callee_account,
2152 false, false, ),
2155 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2156 );
2157
2158 callee_account.set_data_length(0).unwrap();
2160 callee_account
2161 .set_owner(system_program::id().as_ref())
2162 .unwrap();
2163 update_caller_account(
2164 &invoke_context,
2165 true, &mut caller_account,
2167 &mut callee_account,
2168 false, false, )
2171 .unwrap();
2172 let data_len = callee_account.get_data().len();
2173 assert_eq!(data_len, 0);
2174 }
2175
2176 #[case(false, false)]
2177 #[case(true, false)]
2178 #[case(true, true)]
2179 fn test_update_callee_account_lamports_owner(
2180 virtual_address_space_adjustments: bool,
2181 account_data_direct_mapping: bool,
2182 ) {
2183 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2184 let account = transaction_accounts[1].1.clone();
2185
2186 mock_invoke_context!(
2187 invoke_context,
2188 transaction_context,
2189 b"instruction data",
2190 transaction_accounts,
2191 0,
2192 &[1]
2193 );
2194
2195 let mut mock_caller_account =
2196 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2197 let config = Config {
2198 aligned_memory_mapping: false,
2199 ..Config::default()
2200 };
2201 let memory_mapping = unsafe {
2202 MemoryMapping::new(
2203 mock_caller_account.regions.clone(),
2204 &config,
2205 SBPFVersion::V3,
2206 )
2207 .unwrap()
2208 };
2209 let caller_account = mock_caller_account.caller_account();
2210
2211 borrow_instruction_account!(callee_account, invoke_context, 0);
2212
2213 *caller_account.lamports = 42;
2214 *caller_account.owner = Pubkey::new_unique();
2215
2216 update_callee_account(
2217 &memory_mapping,
2218 true, &caller_account,
2220 callee_account,
2221 virtual_address_space_adjustments,
2222 account_data_direct_mapping,
2223 )
2224 .unwrap();
2225
2226 borrow_instruction_account!(callee_account, invoke_context, 0);
2227 assert_eq!(callee_account.get_lamports(), 42);
2228 assert_eq!(caller_account.owner, callee_account.get_owner());
2229 }
2230
2231 #[case(false, false)]
2232 #[case(true, false)]
2233 #[case(true, true)]
2234 fn test_update_callee_account_data_writable(
2235 virtual_address_space_adjustments: bool,
2236 account_data_direct_mapping: bool,
2237 ) {
2238 let transaction_accounts =
2239 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2240 let account = transaction_accounts[1].1.clone();
2241
2242 mock_invoke_context!(
2243 invoke_context,
2244 transaction_context,
2245 b"instruction data",
2246 transaction_accounts,
2247 0,
2248 &[1]
2249 );
2250
2251 let mut mock_caller_account =
2252 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2253 let config = Config {
2254 aligned_memory_mapping: false,
2255 ..Config::default()
2256 };
2257 let memory_mapping = unsafe {
2258 MemoryMapping::new(
2259 mock_caller_account.regions.clone(),
2260 &config,
2261 SBPFVersion::V3,
2262 )
2263 .unwrap()
2264 };
2265 let mut caller_account = mock_caller_account.caller_account();
2266 borrow_instruction_account!(callee_account, invoke_context, 0);
2267
2268 caller_account.serialized_data[0] = b'b';
2270 update_callee_account(
2271 &memory_mapping,
2272 true, &caller_account,
2274 callee_account,
2275 false, false, )
2278 .unwrap();
2279 borrow_instruction_account!(callee_account, invoke_context, 0);
2280 assert_eq!(callee_account.get_data(), b"boobar");
2281
2282 let mut data = b"foobarbaz".to_vec();
2284 *caller_account.ref_to_len_in_vm = data.len() as u64;
2285 caller_account.serialized_data = &mut data;
2286 assert_eq!(
2287 update_callee_account(
2288 &memory_mapping,
2289 true, &caller_account,
2291 callee_account,
2292 virtual_address_space_adjustments,
2293 account_data_direct_mapping,
2294 )
2295 .unwrap(),
2296 virtual_address_space_adjustments,
2297 );
2298
2299 let mut data = b"baz".to_vec();
2301 *caller_account.ref_to_len_in_vm = data.len() as u64;
2302 caller_account.serialized_data = &mut data;
2303 borrow_instruction_account!(callee_account, invoke_context, 0);
2304 assert_eq!(
2305 update_callee_account(
2306 &memory_mapping,
2307 true, &caller_account,
2309 callee_account,
2310 virtual_address_space_adjustments,
2311 account_data_direct_mapping,
2312 )
2313 .unwrap(),
2314 virtual_address_space_adjustments,
2315 );
2316
2317 let mut data = Vec::new();
2319 caller_account.serialized_data = &mut data;
2320 *caller_account.ref_to_len_in_vm = 0;
2321 let mut owner = system_program::id();
2322 caller_account.owner = &mut owner;
2323 borrow_instruction_account!(callee_account, invoke_context, 0);
2324 update_callee_account(
2325 &memory_mapping,
2326 true, &caller_account,
2328 callee_account,
2329 virtual_address_space_adjustments,
2330 account_data_direct_mapping,
2331 )
2332 .unwrap();
2333 borrow_instruction_account!(callee_account, invoke_context, 0);
2334 assert_eq!(callee_account.get_data(), b"");
2335 }
2336
2337 #[case(false, false)]
2338 #[case(true, false)]
2339 #[case(true, true)]
2340 fn test_update_callee_account_data_readonly(
2341 virtual_address_space_adjustments: bool,
2342 account_data_direct_mapping: bool,
2343 ) {
2344 let transaction_accounts =
2345 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2346 let account = transaction_accounts[1].1.clone();
2347
2348 mock_invoke_context!(
2349 invoke_context,
2350 transaction_context,
2351 b"instruction data",
2352 transaction_accounts,
2353 0,
2354 &[1]
2355 );
2356
2357 let mut mock_caller_account =
2358 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2359 let config = Config {
2360 aligned_memory_mapping: false,
2361 ..Config::default()
2362 };
2363 let memory_mapping = unsafe {
2364 MemoryMapping::new(
2365 mock_caller_account.regions.clone(),
2366 &config,
2367 SBPFVersion::V3,
2368 )
2369 .unwrap()
2370 };
2371 let mut caller_account = mock_caller_account.caller_account();
2372 borrow_instruction_account!(callee_account, invoke_context, 0);
2373
2374 caller_account.serialized_data[0] = b'b';
2376 assert_matches!(
2377 update_callee_account(
2378 &memory_mapping,
2379 true, &caller_account,
2381 callee_account,
2382 false, false, ),
2385 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2386 );
2387
2388 let mut data = b"foobarbaz".to_vec();
2390 *caller_account.ref_to_len_in_vm = data.len() as u64;
2391 caller_account.serialized_data = &mut data;
2392 borrow_instruction_account!(callee_account, invoke_context, 0);
2393 assert_matches!(
2394 update_callee_account(
2395 &memory_mapping,
2396 true, &caller_account,
2398 callee_account,
2399 virtual_address_space_adjustments,
2400 account_data_direct_mapping,
2401 ),
2402 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2403 );
2404
2405 let mut data = b"baz".to_vec();
2407 *caller_account.ref_to_len_in_vm = data.len() as u64;
2408 caller_account.serialized_data = &mut data;
2409 borrow_instruction_account!(callee_account, invoke_context, 0);
2410 assert_matches!(
2411 update_callee_account(
2412 &memory_mapping,
2413 true, &caller_account,
2415 callee_account,
2416 virtual_address_space_adjustments,
2417 account_data_direct_mapping,
2418 ),
2419 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2420 );
2421 }
2422}