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
594 .compute_meter
595 .consume_checked(total_cu_translation_cost)?;
596
597 let mut accounts = Vec::with_capacity(account_metas.len());
598 for account_meta in account_metas {
599 let account_meta = unsafe {
602 let ptr = account_meta.as_ptr();
603 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
604 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
605 {
606 return Err(Box::new(InstructionError::InvalidArgument));
607 }
608 account_meta.assume_init_ref()
611 };
612
613 accounts.push(account_meta.clone());
614 }
615
616 Ok(Instruction {
617 accounts,
618 data: data.to_vec(),
619 program_id: ix.program_id,
620 })
621}
622
623pub fn translate_accounts_rust<'a>(
624 account_infos_addr: u64,
625 account_infos_len: u64,
626 invoke_context: &InvokeContext,
627) -> Result<Vec<TranslatedAccount<'a>>, Error> {
628 let check_aligned = invoke_context.get_check_aligned();
629 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
630 translate_account_infos(
631 account_infos_addr,
632 account_infos_len,
633 |account_info: &stable::AccountInfo| account_info.key_addr(),
634 invoke_context,
635 memory_mapping,
636 check_aligned,
637 |account_infos, account_info_keys| {
638 translate_accounts_common(
639 &account_info_keys,
640 account_infos,
641 account_infos_addr,
642 invoke_context,
643 memory_mapping,
644 check_aligned,
645 CallerAccount::from_account_info,
646 )
647 },
648 )?
649}
650
651pub fn translate_signers(
652 program_id: &Pubkey,
653 signers_seeds_addr: u64,
654 signers_seeds_len: u64,
655 invoke_context: &InvokeContext,
656) -> Result<Vec<Pubkey>, Error> {
657 let check_aligned = invoke_context.get_check_aligned();
658 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
659 if signers_seeds_len > 0 {
660 let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
661 memory_mapping,
662 signers_seeds_addr,
663 signers_seeds_len,
664 check_aligned,
665 )?;
666 if signers_seeds.len() > MAX_SIGNERS {
667 return Err(Box::new(CpiError::TooManySigners));
668 }
669 Ok(signers_seeds
670 .iter()
671 .map(|signer_seeds| {
672 let untranslated_seeds = translate_slice::<VmSlice<u8>>(
673 memory_mapping,
674 signer_seeds.ptr(),
675 signer_seeds.len(),
676 check_aligned,
677 )?;
678 if untranslated_seeds.len() > MAX_SEEDS {
679 return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
680 }
681 let seeds_bytes = untranslated_seeds
682 .iter()
683 .map(|untranslated_seed| {
684 translate_vm_slice(untranslated_seed, memory_mapping, check_aligned)
685 })
686 .collect::<Result<Vec<_>, Error>>()?;
687 Pubkey::create_program_address(&seeds_bytes, program_id)
688 .map_err(|err| Box::new(CpiError::BadSeeds(err)) as Error)
689 })
690 .collect::<Result<Vec<_>, Error>>()?)
691 } else {
692 Ok(vec![])
693 }
694}
695
696pub fn translate_instruction_c(
697 addr: u64,
698 invoke_context: &InvokeContext,
699) -> Result<Instruction, Error> {
700 let check_aligned = invoke_context.get_check_aligned();
701 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
702 let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
703
704 let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
705 let account_metas = translate_slice::<mem::MaybeUninit<SolAccountMeta>>(
706 memory_mapping,
707 ix_c.accounts_addr,
708 ix_c.accounts_len,
709 check_aligned,
710 )?;
711 let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
712
713 check_instruction_size(ix_c.accounts_len as usize, data.len())?;
714
715 let mut total_cu_translation_cost: u64 = (data.len() as u64)
716 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
717 .unwrap_or(u64::MAX);
718
719 let account_meta_translation_cost = (ix_c
721 .accounts_len
722 .saturating_mul(size_of::<AccountMeta>() as u64))
723 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
724 .unwrap_or(u64::MAX);
725
726 total_cu_translation_cost =
727 total_cu_translation_cost.saturating_add(account_meta_translation_cost);
728
729 invoke_context
730 .compute_meter
731 .consume_checked(total_cu_translation_cost)?;
732
733 let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
734 for account_meta in account_metas {
735 let account_meta = unsafe {
738 let ptr = account_meta.as_ptr();
739 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
740 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
741 {
742 return Err(Box::new(InstructionError::InvalidArgument));
743 }
744 account_meta.assume_init_ref()
747 };
748 let pubkey =
749 translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
750 accounts.push(AccountMeta {
751 pubkey: *pubkey,
752 is_signer: account_meta.is_signer,
753 is_writable: account_meta.is_writable,
754 });
755 }
756
757 Ok(Instruction {
758 accounts,
759 data: data.to_vec(),
760 program_id: *program_id,
761 })
762}
763
764pub fn translate_accounts_c<'a>(
765 account_infos_addr: u64,
766 account_infos_len: u64,
767 invoke_context: &InvokeContext,
768) -> Result<Vec<TranslatedAccount<'a>>, Error> {
769 let check_aligned = invoke_context.get_check_aligned();
770 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
771 translate_account_infos(
772 account_infos_addr,
773 account_infos_len,
774 |account_info: &SolAccountInfo| account_info.key_addr,
775 invoke_context,
776 memory_mapping,
777 check_aligned,
778 |account_infos, account_info_keys| {
779 translate_accounts_common(
780 &account_info_keys,
781 account_infos,
782 account_infos_addr,
783 invoke_context,
784 memory_mapping,
785 check_aligned,
786 CallerAccount::from_sol_account_info,
787 )
788 },
789 )?
790}
791
792pub fn cpi_common<S: SyscallInvokeSigned>(
794 invoke_context: &mut InvokeContext,
795 instruction_addr: u64,
796 account_infos_addr: u64,
797 account_infos_len: u64,
798 signers_seeds_addr: u64,
799 signers_seeds_len: u64,
800) -> Result<u64, Error> {
801 let amount = invoke_context.get_execution_cost().invoke_units;
806 invoke_context.compute_meter.consume_checked(amount)?;
807 let virtual_address_space_adjustments = invoke_context
808 .get_feature_set()
809 .virtual_address_space_adjustments;
810 let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
811 let check_aligned = invoke_context.get_check_aligned();
812
813 let instruction = S::translate_instruction(instruction_addr, invoke_context)?;
814 let instruction_context = invoke_context
815 .transaction_context
816 .get_current_instruction_context()?;
817 let caller_program_id = instruction_context.get_program_key()?;
818 let signers = translate_signers(
819 caller_program_id,
820 signers_seeds_addr,
821 signers_seeds_len,
822 invoke_context,
823 )?;
824 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
825 invoke_context.build_instruction_frame(instruction)?;
826 invoke_context.verify_instruction_accounts(&signers)?;
827
828 let mut accounts =
829 S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
830
831 let transaction_context = &invoke_context.transaction_context;
836 let instruction_context = transaction_context.get_current_instruction_context()?;
837 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
838 for translated_account in accounts.iter_mut() {
839 let callee_account = instruction_context
840 .try_borrow_instruction_account(translated_account.index_in_caller)?;
841 let update_caller = update_callee_account(
842 memory_mapping,
843 check_aligned,
844 &translated_account.caller_account,
845 callee_account,
846 virtual_address_space_adjustments,
847 account_data_direct_mapping,
848 )?;
849 translated_account.update_caller_account_region =
850 translated_account.update_caller_account_info || update_caller;
851 }
852
853 let mut compute_units_consumed = 0;
855 invoke_context
856 .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
857
858 let transaction_context = &invoke_context.transaction_context;
860 let instruction_context = transaction_context.get_current_instruction_context()?;
861
862 for translated_account in accounts.iter_mut() {
866 let mut callee_account = instruction_context
867 .try_borrow_instruction_account(translated_account.index_in_caller)?;
868 if translated_account.update_caller_account_info {
869 update_caller_account(
870 invoke_context,
871 check_aligned,
872 &mut translated_account.caller_account,
873 &mut callee_account,
874 virtual_address_space_adjustments,
875 account_data_direct_mapping,
876 )?;
877 }
878 }
879
880 if virtual_address_space_adjustments {
881 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
882 for translated_account in accounts.iter() {
883 let mut callee_account = instruction_context
884 .try_borrow_instruction_account(translated_account.index_in_caller)?;
885 if translated_account.update_caller_account_region {
886 unsafe {
887 update_caller_account_region(
892 memory_mapping,
893 check_aligned,
894 &translated_account.caller_account,
895 &mut callee_account,
896 account_data_direct_mapping,
897 )?;
898 }
899 }
900 }
901 }
902
903 Ok(SUCCESS)
904}
905
906pub struct TranslatedAccount<'a> {
908 pub index_in_caller: IndexOfAccount,
909 pub caller_account: CallerAccount<'a>,
910 pub update_caller_account_region: bool,
911 pub update_caller_account_info: bool,
912}
913
914fn translate_account_infos<T, R>(
915 account_infos_addr: u64,
916 account_infos_len: u64,
917 key_addr: impl Fn(&T) -> u64,
918 invoke_context: &InvokeContext,
919 memory_mapping: &MemoryMapping,
920 check_aligned: bool,
921 cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
922) -> Result<R, Error> {
923 if account_infos_addr
927 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
928 >= ebpf::MM_INPUT_START
929 {
930 return Err(CpiError::InvalidPointer.into());
931 }
932
933 let account_infos = translate_slice::<T>(
934 memory_mapping,
935 account_infos_addr,
936 account_infos_len,
937 check_aligned,
938 )?;
939 check_account_infos(account_infos.len())?;
940
941 let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
942
943 let amount = (account_infos_bytes as u64)
944 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
945 .unwrap_or(u64::MAX);
946 invoke_context.compute_meter.consume_checked(amount)?;
947
948 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
949 #[expect(clippy::needless_range_loop)]
950 for account_index in 0..account_infos_len as usize {
951 #[expect(clippy::indexing_slicing)]
952 let account_info = &account_infos[account_index];
953 account_info_keys.push(translate_type::<Pubkey>(
954 memory_mapping,
955 key_addr(account_info),
956 check_aligned,
957 )?);
958 }
959 Ok(cb(account_infos, account_info_keys))
960}
961
962fn translate_accounts_common<'a, T, F>(
964 account_info_keys: &[&Pubkey],
965 account_infos: &[T],
966 account_infos_addr: u64,
967 invoke_context: &InvokeContext,
968 memory_mapping: &MemoryMapping,
969 check_aligned: bool,
970 do_translate: F,
971) -> Result<Vec<TranslatedAccount<'a>>, Error>
972where
973 F: Fn(
974 &InvokeContext,
975 &MemoryMapping,
976 bool,
977 u64,
978 &T,
979 &SerializedAccountMetadata,
980 ) -> Result<CallerAccount<'a>, Error>,
981{
982 let transaction_context = &invoke_context.transaction_context;
983 let next_instruction_context = transaction_context.get_next_instruction_context()?;
984 let next_instruction_accounts = next_instruction_context.instruction_accounts();
985 let instruction_context = transaction_context.get_current_instruction_context()?;
986 let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
987
988 let accounts_metadata = &invoke_context
991 .memory_contexts
992 .memory_context_abi_v1()
993 .unwrap()
994 .accounts_metadata;
995
996 for (instruction_account_index, instruction_account) in
997 next_instruction_accounts.iter().enumerate()
998 {
999 if next_instruction_context
1000 .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
1001 .is_some()
1002 {
1003 continue; }
1005
1006 let index_in_caller = instruction_context
1007 .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
1008 let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
1009 let account_key = invoke_context
1010 .transaction_context
1011 .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
1012
1013 #[expect(deprecated)]
1014 if callee_account.is_executable() {
1015 let amount = (callee_account.get_data().len() as u64)
1017 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1018 .unwrap_or(u64::MAX);
1019 invoke_context.compute_meter.consume_checked(amount)?;
1020 } else if let Some(caller_account_index) =
1021 account_info_keys.iter().position(|key| *key == account_key)
1022 {
1023 let serialized_metadata =
1024 accounts_metadata
1025 .get(index_in_caller as usize)
1026 .ok_or_else(|| {
1027 ic_msg!(
1028 invoke_context,
1029 "Internal error: index mismatch for account {}",
1030 account_key
1031 );
1032 Box::new(InstructionError::MissingAccount) as Error
1033 })?;
1034
1035 if caller_account_index >= account_infos.len() {
1037 return Err(Box::new(CpiError::InvalidLength));
1038 }
1039 #[expect(clippy::indexing_slicing)]
1040 let caller_account =
1041 do_translate(
1042 invoke_context,
1043 memory_mapping,
1044 check_aligned,
1045 account_infos_addr.saturating_add(
1046 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
1047 ),
1048 &account_infos[caller_account_index],
1049 serialized_metadata,
1050 )?;
1051
1052 let amount = (*caller_account.ref_to_len_in_vm)
1053 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1054 .unwrap_or(u64::MAX);
1055 invoke_context.compute_meter.consume_checked(amount)?;
1056
1057 accounts.push(TranslatedAccount {
1058 index_in_caller,
1059 caller_account,
1060 update_caller_account_region: true, update_caller_account_info: instruction_account.is_writable(),
1062 });
1063 } else {
1064 ic_msg!(
1065 invoke_context,
1066 "Instruction references an unknown account {}",
1067 account_key
1068 );
1069 return Err(Box::new(InstructionError::MissingAccount));
1070 }
1071 }
1072
1073 Ok(accounts)
1074}
1075
1076fn update_callee_account(
1088 memory_mapping: &MemoryMapping,
1089 check_aligned: bool,
1090 caller_account: &CallerAccount,
1091 mut callee_account: BorrowedInstructionAccount<'_, '_>,
1092 virtual_address_space_adjustments: bool,
1093 account_data_direct_mapping: bool,
1094) -> Result<bool, Error> {
1095 let mut must_update_caller = false;
1096
1097 if callee_account.get_lamports() != *caller_account.lamports {
1098 callee_account.set_lamports(*caller_account.lamports)?;
1099 }
1100
1101 if virtual_address_space_adjustments {
1102 let prev_len = callee_account.get_data().len();
1103 let post_len = *caller_account.ref_to_len_in_vm as usize;
1104 if prev_len != post_len {
1105 if !account_data_direct_mapping && post_len < prev_len {
1106 let serialized_data = unsafe {
1109 CallerAccount::get_serialized_data(
1110 memory_mapping,
1111 check_aligned,
1112 caller_account.vm_data_addr,
1113 caller_account.original_data_len,
1114 prev_len,
1115 virtual_address_space_adjustments,
1116 account_data_direct_mapping,
1117 )?
1118 };
1119 serialized_data
1120 .get_mut(post_len..)
1121 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1122 .fill(0);
1123 }
1124 callee_account.set_data_length(post_len)?;
1125 must_update_caller = true;
1127 }
1128 if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1129 callee_account.set_data_from_slice(caller_account.serialized_data)?;
1130 }
1131 } else {
1132 match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1134 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1135 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1136 return Err(Box::new(err));
1137 }
1138 _ => {}
1139 }
1140 }
1141
1142 if callee_account.get_owner() != caller_account.owner {
1144 callee_account.set_owner(caller_account.owner.as_ref())?;
1145 must_update_caller = true;
1147 }
1148
1149 Ok(must_update_caller)
1150}
1151
1152unsafe fn update_caller_account_region(
1157 memory_mapping: &mut MemoryMapping,
1158 check_aligned: bool,
1159 caller_account: &CallerAccount,
1160 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1161 account_data_direct_mapping: bool,
1162) -> Result<(), Error> {
1163 let is_caller_loader_deprecated = !check_aligned;
1164 let address_space_reserved_for_account =
1165 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1166
1167 if address_space_reserved_for_account > 0 {
1168 let (region_index, region) = memory_mapping
1171 .find_region(caller_account.vm_data_addr)
1172 .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1173 let region_start_vm_addr = region.vm_addr_range().start;
1175 debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1176 let mut new_region;
1177 if !account_data_direct_mapping {
1178 new_region = region.clone();
1179 modify_memory_region_of_account(callee_account, &mut new_region);
1180 } else {
1181 new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1182 }
1183 unsafe {
1184 memory_mapping.replace_region(region_index, new_region)?;
1189 }
1190 }
1191
1192 Ok(())
1193}
1194
1195fn update_caller_account(
1204 invoke_context: &InvokeContext,
1205 check_aligned: bool,
1206 caller_account: &mut CallerAccount<'_>,
1207 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1208 virtual_address_space_adjustments: bool,
1209 account_data_direct_mapping: bool,
1210) -> Result<(), Error> {
1211 *caller_account.lamports = callee_account.get_lamports();
1212 *caller_account.owner = *callee_account.get_owner();
1213
1214 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1215 let post_len = callee_account.get_data().len();
1216 let is_caller_loader_deprecated = !check_aligned;
1217 let address_space_reserved_for_account =
1218 caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1219
1220 if post_len > address_space_reserved_for_account {
1221 let max_increase =
1222 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1223 ic_msg!(
1224 invoke_context,
1225 "Account data size realloc limited to {max_increase} in inner instructions",
1226 );
1227 return Err(Box::new(InstructionError::InvalidRealloc));
1228 }
1229
1230 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1231 if prev_len != post_len {
1232 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1235 if post_len < prev_len {
1238 caller_account
1239 .serialized_data
1240 .get_mut(post_len..)
1241 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1242 .fill(0);
1243 }
1244 unsafe {
1246 caller_account.serialized_data = CallerAccount::get_serialized_data(
1247 memory_mapping,
1248 check_aligned,
1249 caller_account.vm_data_addr,
1250 caller_account.original_data_len,
1251 post_len,
1252 virtual_address_space_adjustments,
1253 account_data_direct_mapping,
1254 )?;
1255 }
1256 }
1257 *caller_account.ref_to_len_in_vm = post_len as u64;
1259
1260 let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1262 memory_mapping,
1263 caller_account
1264 .vm_data_addr
1265 .saturating_sub(std::mem::size_of::<u64>() as u64),
1266 check_aligned,
1267 )?;
1268 *serialized_len_ptr = post_len as u64;
1269 }
1270
1271 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1272 let to_slice = &mut caller_account.serialized_data;
1274 let from_slice = callee_account
1275 .get_data()
1276 .get(0..post_len)
1277 .ok_or(CpiError::InvalidLength)?;
1278 if to_slice.len() != from_slice.len() {
1279 return Err(Box::new(InstructionError::AccountDataTooSmall));
1280 }
1281 to_slice.copy_from_slice(from_slice);
1282 }
1283
1284 Ok(())
1285}
1286
1287#[allow(clippy::indexing_slicing)]
1288#[allow(clippy::arithmetic_side_effects)]
1289#[cfg(test)]
1290mod tests {
1291 use {
1292 super::*,
1293 crate::{
1294 invoke_context::BpfAllocator,
1295 memory::translate_type,
1296 memory_context::{MemoryContext, SerializedAccountMetadata},
1297 with_mock_invoke_context_with_feature_set,
1298 },
1299 assert_matches::assert_matches,
1300 solana_account::{Account, AccountSharedData, ReadableAccount},
1301 solana_account_info::AccountInfo,
1302 solana_sbpf::{
1303 ebpf::{MM_INPUT_START, MM_STACK_START},
1304 memory_region::MemoryRegion,
1305 program::SBPFVersion,
1306 vm::Config,
1307 },
1308 solana_sdk_ids::{bpf_loader, system_program},
1309 solana_svm_feature_set::SVMFeatureSet,
1310 solana_transaction_context::{
1311 IndexOfAccount, instruction_accounts::InstructionAccount,
1312 transaction_accounts::KeyedAccountSharedData,
1313 },
1314 std::{
1315 cell::{Cell, RefCell},
1316 mem, ptr,
1317 rc::Rc,
1318 slice,
1319 },
1320 test_case::case,
1321 };
1322
1323 macro_rules! mock_invoke_context {
1324 ($invoke_context:ident,
1325 $transaction_context:ident,
1326 $instruction_data:expr,
1327 $transaction_accounts:expr,
1328 $program_account:expr,
1329 $instruction_accounts:expr) => {
1330 let instruction_data = $instruction_data;
1331 let instruction_accounts = $instruction_accounts
1332 .iter()
1333 .map(|index_in_transaction| {
1334 InstructionAccount::new(
1335 *index_in_transaction as IndexOfAccount,
1336 false,
1337 $transaction_accounts[*index_in_transaction as usize].2,
1338 )
1339 })
1340 .collect::<Vec<_>>();
1341 let transaction_accounts = $transaction_accounts
1342 .into_iter()
1343 .map(|a| (a.0, a.1))
1344 .collect::<Vec<KeyedAccountSharedData>>();
1345 let mut feature_set = SVMFeatureSet::all_enabled();
1346 feature_set.virtual_address_space_adjustments = false;
1347 feature_set.account_data_direct_mapping = false;
1348 let feature_set = &feature_set;
1349 with_mock_invoke_context_with_feature_set!(
1350 $invoke_context,
1351 $transaction_context,
1352 feature_set,
1353 transaction_accounts
1354 );
1355 $invoke_context
1356 .transaction_context
1357 .configure_top_level_instruction_for_tests(
1358 $program_account,
1359 instruction_accounts,
1360 instruction_data.to_vec(),
1361 )
1362 .unwrap();
1363 $invoke_context.push().unwrap();
1364 };
1365 }
1366
1367 macro_rules! borrow_instruction_account {
1368 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1369 let instruction_context = $invoke_context
1370 .transaction_context
1371 .get_current_instruction_context()
1372 .unwrap();
1373 let $borrowed_account = instruction_context
1374 .try_borrow_instruction_account($index)
1375 .unwrap();
1376 };
1377 }
1378
1379 fn is_zeroed(data: &[u8]) -> bool {
1380 data.iter().all(|b| *b == 0)
1381 }
1382
1383 struct MockCallerAccount {
1384 lamports: u64,
1385 owner: Pubkey,
1386 vm_addr: u64,
1387 data: Vec<u8>,
1388 len: u64,
1389 regions: Vec<MemoryRegion>,
1390 virtual_address_space_adjustments: bool,
1391 }
1392
1393 impl MockCallerAccount {
1394 fn new(
1395 lamports: u64,
1396 owner: Pubkey,
1397 data: &[u8],
1398 virtual_address_space_adjustments: bool,
1399 ) -> MockCallerAccount {
1400 let vm_addr = MM_INPUT_START;
1401 let mut region_addr = vm_addr;
1402 let region_len = mem::size_of::<u64>()
1403 + if virtual_address_space_adjustments {
1404 0
1405 } else {
1406 data.len() + MAX_PERMITTED_DATA_INCREASE
1407 };
1408 let mut d = vec![0; region_len];
1409 let mut regions = vec![];
1410
1411 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1413
1414 if !virtual_address_space_adjustments {
1416 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1417 }
1418
1419 regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1421 region_addr += region_len as u64;
1422
1423 if virtual_address_space_adjustments {
1424 regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1426 region_addr += data.len() as u64;
1427
1428 regions.push(MemoryRegion::new(
1430 &raw mut d[mem::size_of::<u64>()..],
1431 region_addr,
1432 ));
1433 } else {
1434 d.truncate(mem::size_of::<u64>() + data.len());
1436 }
1437
1438 MockCallerAccount {
1439 lamports,
1440 owner,
1441 vm_addr,
1442 data: d,
1443 len: data.len() as u64,
1444 regions,
1445 virtual_address_space_adjustments,
1446 }
1447 }
1448
1449 fn data_slice<'a>(&self) -> &'a [u8] {
1450 unsafe {
1452 slice::from_raw_parts(
1453 self.data[mem::size_of::<u64>()..].as_ptr(),
1454 self.data.capacity() - mem::size_of::<u64>(),
1455 )
1456 }
1457 }
1458
1459 fn caller_account(&mut self) -> CallerAccount<'_> {
1460 let data = if self.virtual_address_space_adjustments {
1461 &mut []
1462 } else {
1463 &mut self.data[mem::size_of::<u64>()..]
1464 };
1465 CallerAccount {
1466 lamports: &mut self.lamports,
1467 owner: &mut self.owner,
1468 original_data_len: self.len as usize,
1469 serialized_data: data,
1470 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1471 ref_to_len_in_vm: &mut self.len,
1472 }
1473 }
1474 }
1475
1476 struct MockAccountInfo<'a> {
1477 key: Pubkey,
1478 is_signer: bool,
1479 is_writable: bool,
1480 lamports: u64,
1481 data: &'a [u8],
1482 owner: Pubkey,
1483 executable: bool,
1484 _unused: u64,
1485 }
1486
1487 impl MockAccountInfo<'_> {
1488 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1489 MockAccountInfo {
1490 key,
1491 is_signer: false,
1492 is_writable: false,
1493 lamports: account.lamports(),
1494 data: account.data(),
1495 owner: *account.owner(),
1496 executable: account.executable(),
1497 _unused: account.rent_epoch(),
1498 }
1499 }
1500
1501 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1502 let size = mem::size_of::<AccountInfo>()
1503 + mem::size_of::<Pubkey>() * 2
1504 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1505 + mem::size_of::<u64>()
1506 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1507 + self.data.len();
1508 let mut data = vec![0; size];
1509
1510 let vm_addr = vm_addr as usize;
1511 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1512 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1513 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1514 let owner_addr = lamports_addr + mem::size_of::<u64>();
1515 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1516 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1517
1518 #[allow(deprecated)]
1519 #[allow(clippy::used_underscore_binding)]
1520 let info = AccountInfo {
1521 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1522 is_signer: self.is_signer,
1523 is_writable: self.is_writable,
1524 lamports: unsafe {
1525 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1526 },
1527 data: unsafe {
1528 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1529 },
1530 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1531 executable: self.executable,
1532 _unused: self._unused,
1533 };
1534
1535 unsafe {
1536 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1537 ptr::write_unaligned(
1538 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1539 self.key,
1540 );
1541 ptr::write_unaligned(
1542 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1543 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1544 );
1545 ptr::write_unaligned(
1546 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1547 self.lamports,
1548 );
1549 ptr::write_unaligned(
1550 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1551 self.owner,
1552 );
1553 ptr::write_unaligned(
1554 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1555 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1556 data_addr as *mut u8,
1557 self.data.len(),
1558 ))),
1559 );
1560 data[data_addr - vm_addr..].copy_from_slice(self.data);
1561 }
1562
1563 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1564 (
1565 data,
1566 region,
1567 SerializedAccountMetadata {
1568 vm_addr: vm_addr as u64,
1569 original_data_len: self.data.len(),
1570 vm_key_addr: key_addr as u64,
1571 vm_lamports_addr: lamports_addr as u64,
1572 vm_owner_addr: owner_addr as u64,
1573 vm_data_addr: data_addr as u64,
1574 },
1575 )
1576 }
1577 }
1578
1579 struct MockInstruction {
1580 program_id: Pubkey,
1581 accounts: Vec<AccountMeta>,
1582 data: Vec<u8>,
1583 }
1584
1585 impl MockInstruction {
1586 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1587 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1588
1589 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1590
1591 let mut data = vec![0; size];
1592
1593 let vm_addr = vm_addr as usize;
1594 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1595 let data_addr = accounts_addr + accounts_len;
1596
1597 let ins = Instruction {
1598 program_id: self.program_id,
1599 accounts: unsafe {
1600 Vec::from_raw_parts(
1601 accounts_addr as *mut _,
1602 self.accounts.len(),
1603 self.accounts.len(),
1604 )
1605 },
1606 data: unsafe {
1607 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1608 },
1609 };
1610 let ins = StableInstruction::from(ins);
1611
1612 unsafe {
1613 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1614 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1615 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1616 );
1617 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1618 }
1619
1620 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1621 (data, region)
1622 }
1623 }
1624
1625 #[repr(C)]
1626 struct RcBox<T> {
1627 strong: Cell<usize>,
1628 weak: Cell<usize>,
1629 value: T,
1630 }
1631
1632 impl<T> RcBox<T> {
1633 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1634 fn new(value: T) -> RcBox<T> {
1635 RcBox {
1636 strong: Cell::new(0),
1637 weak: Cell::new(0),
1638 value,
1639 }
1640 }
1641 }
1642
1643 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1644
1645 fn transaction_with_one_writable_instruction_account(
1646 data: Vec<u8>,
1647 ) -> Vec<TestTransactionAccount> {
1648 let program_id = Pubkey::new_unique();
1649 let account = AccountSharedData::from(Account {
1650 lamports: 1,
1651 data,
1652 owner: program_id,
1653 executable: false,
1654 rent_epoch: 100,
1655 });
1656 vec![
1657 (
1658 program_id,
1659 AccountSharedData::from(Account {
1660 lamports: 0,
1661 data: vec![],
1662 owner: bpf_loader::id(),
1663 executable: true,
1664 rent_epoch: 0,
1665 }),
1666 false,
1667 ),
1668 (Pubkey::new_unique(), account, true),
1669 ]
1670 }
1671
1672 fn transaction_with_one_readonly_instruction_account(
1673 data: Vec<u8>,
1674 ) -> Vec<TestTransactionAccount> {
1675 let program_id = Pubkey::new_unique();
1676 let account_owner = Pubkey::new_unique();
1677 let account = AccountSharedData::from(Account {
1678 lamports: 1,
1679 data,
1680 owner: account_owner,
1681 executable: false,
1682 rent_epoch: 100,
1683 });
1684 vec![
1685 (
1686 program_id,
1687 AccountSharedData::from(Account {
1688 lamports: 0,
1689 data: vec![],
1690 owner: bpf_loader::id(),
1691 executable: true,
1692 rent_epoch: 0,
1693 }),
1694 false,
1695 ),
1696 (Pubkey::new_unique(), account, true),
1697 ]
1698 }
1699
1700 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1701 let vm_addr = vm_addr as usize;
1702
1703 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
1706 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1707
1708 let total_size = fat_ptr_size_of_slice
1710 + singers_length * fat_ptr_size_of_slice
1711 + sum_signers_data_length;
1712 let mut data = vec![0; total_size];
1713
1714 data[..fat_ptr_size_of_slice / 2]
1726 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1727 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1728 .clone_from_slice(&(singers_length).to_le_bytes());
1729
1730 let (mut p1, mut p2) = (
1732 fat_ptr_size_of_slice,
1733 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1734 );
1735 for signer in signers.iter() {
1736 let signer_length = signer.len();
1737
1738 data[p1..p1 + fat_ptr_size_of_slice / 2]
1740 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1741 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1742 .clone_from_slice(&(signer_length).to_le_bytes());
1743 p1 += fat_ptr_size_of_slice;
1744
1745 data[p2..p2 + signer_length].clone_from_slice(signer);
1747 p2 += signer_length;
1748 }
1749
1750 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1751 (data, region)
1752 }
1753
1754 #[test]
1755 fn test_translate_instruction() {
1756 let transaction_accounts =
1757 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1758 mock_invoke_context!(
1759 invoke_context,
1760 transaction_context,
1761 b"instruction data",
1762 transaction_accounts,
1763 0,
1764 &[1]
1765 );
1766
1767 let program_id = Pubkey::new_unique();
1768 let accounts = vec![AccountMeta {
1769 pubkey: Pubkey::new_unique(),
1770 is_signer: true,
1771 is_writable: false,
1772 }];
1773 let data = b"ins data".to_vec();
1774 let vm_addr = MM_INPUT_START;
1775 let (_mem, region) = MockInstruction {
1776 program_id,
1777 accounts: accounts.clone(),
1778 data: data.clone(),
1779 }
1780 .into_region(vm_addr);
1781
1782 let config = Config {
1783 aligned_memory_mapping: false,
1784 ..Config::default()
1785 };
1786 let memory_mapping =
1787 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1788 invoke_context
1789 .memory_contexts
1790 .mock_set_mapping_abi_v1(memory_mapping);
1791
1792 let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1793 assert_eq!(ins.program_id, program_id);
1794 assert_eq!(ins.accounts, accounts);
1795 assert_eq!(ins.data, data);
1796 }
1797
1798 #[test]
1799 fn test_translate_signers() {
1800 let transaction_accounts =
1801 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1802 mock_invoke_context!(
1803 invoke_context,
1804 transaction_context,
1805 b"instruction data",
1806 transaction_accounts,
1807 0,
1808 &[1]
1809 );
1810
1811 let program_id = Pubkey::new_unique();
1812 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1813
1814 let vm_addr = MM_INPUT_START;
1815 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1816
1817 let config = Config {
1818 aligned_memory_mapping: false,
1819 ..Config::default()
1820 };
1821 let mapping =
1822 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1823 invoke_context
1824 .memory_contexts
1825 .set_memory_context_abi_v1(MemoryContext::new(
1826 BpfAllocator::new(0),
1827 Vec::new(),
1828 mapping,
1829 ))
1830 .unwrap();
1831
1832 let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1833 assert_eq!(signers[0], derived_key);
1834 }
1835
1836 #[test]
1837 fn test_translate_accounts_rust() {
1838 let transaction_accounts =
1839 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1840 let account = transaction_accounts[1].1.clone();
1841 let key = transaction_accounts[1].0;
1842 let original_data_len = account.data().len();
1843
1844 let vm_addr = MM_STACK_START;
1845 let (_mem, region, account_metadata) =
1846 MockAccountInfo::new(key, &account).into_region(vm_addr);
1847
1848 let config = Config {
1849 aligned_memory_mapping: false,
1850 ..Config::default()
1851 };
1852 let memory_mapping =
1853 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1854
1855 mock_invoke_context!(
1856 invoke_context,
1857 transaction_context,
1858 b"instruction data",
1859 transaction_accounts,
1860 0,
1861 &[1, 1]
1862 );
1863
1864 invoke_context
1865 .memory_contexts
1866 .set_memory_context_abi_v1(MemoryContext::new(
1867 BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1868 vec![account_metadata],
1869 memory_mapping,
1870 ))
1871 .unwrap();
1872
1873 invoke_context
1874 .transaction_context
1875 .configure_next_cpi_for_tests(
1876 0,
1877 vec![
1878 InstructionAccount::new(1, false, true),
1879 InstructionAccount::new(1, false, true),
1880 ],
1881 vec![],
1882 )
1883 .unwrap();
1884
1885 let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1886 assert_eq!(accounts.len(), 1);
1887 let caller_account = &accounts[0].caller_account;
1888 assert_eq!(caller_account.serialized_data, account.data());
1889 assert_eq!(caller_account.original_data_len, original_data_len);
1890 }
1891
1892 #[test]
1893 fn test_get_serialized_data() {
1894 let transaction_accounts =
1895 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1896 let account = transaction_accounts[1].1.clone();
1897 mock_invoke_context!(
1898 invoke_context,
1899 transaction_context,
1900 b"instruction data",
1901 transaction_accounts,
1902 0,
1903 &[1]
1904 );
1905
1906 let config = Config {
1907 aligned_memory_mapping: false,
1908 ..Config::default()
1909 };
1910 let memory_mapping =
1911 unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1912 let serialized_data = unsafe {
1913 CallerAccount::get_serialized_data(
1914 &memory_mapping,
1915 true, MM_INPUT_START,
1917 account.data().len(),
1918 account
1919 .data()
1920 .len()
1921 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1922 .saturating_add(1),
1923 true, false, )
1926 };
1927
1928 assert_matches!(
1929 serialized_data,
1930 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1931 );
1932 }
1933
1934 #[test]
1935 fn test_caller_account_from_account_info() {
1936 let transaction_accounts =
1937 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1938 let account = transaction_accounts[1].1.clone();
1939 mock_invoke_context!(
1940 invoke_context,
1941 transaction_context,
1942 b"instruction data",
1943 transaction_accounts,
1944 0,
1945 &[1]
1946 );
1947
1948 let key = Pubkey::new_unique();
1949 let vm_addr = MM_STACK_START;
1950 let (_mem, region, account_metadata) =
1951 MockAccountInfo::new(key, &account).into_region(vm_addr);
1952
1953 let config = Config {
1954 aligned_memory_mapping: false,
1955 ..Config::default()
1956 };
1957 let memory_mapping =
1958 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1959
1960 let account_info =
1961 translate_type::<stable::AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1962
1963 invoke_context
1964 .memory_contexts
1965 .mock_set_mapping_abi_v1(memory_mapping);
1966 let check_aligned = invoke_context.get_check_aligned();
1967 let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
1968 let caller_account = CallerAccount::from_account_info(
1969 &invoke_context,
1970 memory_mapping,
1971 check_aligned,
1972 vm_addr,
1973 account_info,
1974 &account_metadata,
1975 )
1976 .unwrap();
1977 assert_eq!(*caller_account.lamports, account.lamports());
1978 assert_eq!(caller_account.owner, account.owner());
1979 assert_eq!(caller_account.original_data_len, account.data().len());
1980 assert_eq!(
1981 *caller_account.ref_to_len_in_vm as usize,
1982 account.data().len()
1983 );
1984 assert_eq!(caller_account.serialized_data, account.data());
1985 }
1986
1987 #[case(false, false)]
1988 #[case(true, false)]
1989 #[case(true, true)]
1990 fn test_update_caller_account_lamports_owner(
1991 virtual_address_space_adjustments: bool,
1992 account_data_direct_mapping: bool,
1993 ) {
1994 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1995 let account = transaction_accounts[1].1.clone();
1996 mock_invoke_context!(
1997 invoke_context,
1998 transaction_context,
1999 b"instruction data",
2000 transaction_accounts,
2001 0,
2002 &[1]
2003 );
2004
2005 let mut mock_caller_account =
2006 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2007
2008 let config = Config {
2009 aligned_memory_mapping: false,
2010 ..Config::default()
2011 };
2012 let memory_mapping = unsafe {
2013 MemoryMapping::new(
2014 mock_caller_account.regions.split_off(0),
2015 &config,
2016 SBPFVersion::V3,
2017 )
2018 .unwrap()
2019 };
2020 invoke_context
2021 .memory_contexts
2022 .mock_set_mapping_abi_v1(memory_mapping);
2023
2024 let mut caller_account = mock_caller_account.caller_account();
2025 let instruction_context = invoke_context
2026 .transaction_context
2027 .get_current_instruction_context()
2028 .unwrap();
2029 let mut callee_account = instruction_context
2030 .try_borrow_instruction_account(0)
2031 .unwrap();
2032 callee_account.set_lamports(42).unwrap();
2033 callee_account
2034 .set_owner(Pubkey::new_unique().as_ref())
2035 .unwrap();
2036
2037 update_caller_account(
2038 &invoke_context,
2039 true, &mut caller_account,
2041 &mut callee_account,
2042 virtual_address_space_adjustments,
2043 account_data_direct_mapping,
2044 )
2045 .unwrap();
2046
2047 assert_eq!(*caller_account.lamports, 42);
2048 assert_eq!(caller_account.owner, callee_account.get_owner());
2049 }
2050
2051 #[test]
2052 fn test_update_caller_account_data() {
2053 let transaction_accounts =
2054 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2055 let account = transaction_accounts[1].1.clone();
2056 let original_data_len = account.data().len();
2057
2058 mock_invoke_context!(
2059 invoke_context,
2060 transaction_context,
2061 b"instruction data",
2062 transaction_accounts,
2063 0,
2064 &[1]
2065 );
2066
2067 let mut mock_caller_account =
2068 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2069
2070 let config = Config {
2071 aligned_memory_mapping: false,
2072 ..Config::default()
2073 };
2074 let memory_mapping = unsafe {
2075 MemoryMapping::new(
2076 mock_caller_account.regions.clone(),
2077 &config,
2078 SBPFVersion::V3,
2079 )
2080 .unwrap()
2081 };
2082 invoke_context
2083 .memory_contexts
2084 .mock_set_mapping_abi_v1(memory_mapping);
2085
2086 let data_slice = mock_caller_account.data_slice();
2087 let len_ptr = unsafe {
2088 data_slice
2089 .as_ptr()
2090 .offset(-(mem::size_of::<u64>() as isize))
2091 };
2092 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2093 let mut caller_account = mock_caller_account.caller_account();
2094 let instruction_context = invoke_context
2095 .transaction_context
2096 .get_current_instruction_context()
2097 .unwrap();
2098 let mut callee_account = instruction_context
2099 .try_borrow_instruction_account(0)
2100 .unwrap();
2101
2102 for (new_value, expected_realloc_size) in [
2103 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2104 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2105 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2106 ] {
2107 assert_eq!(caller_account.serialized_data, callee_account.get_data());
2108 callee_account.set_data_from_slice(&new_value).unwrap();
2109
2110 update_caller_account(
2111 &invoke_context,
2112 true, &mut caller_account,
2114 &mut callee_account,
2115 false, false, )
2118 .unwrap();
2119
2120 let data_len = callee_account.get_data().len();
2121 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2122 assert_eq!(data_len, serialized_len());
2123 assert_eq!(data_len, caller_account.serialized_data.len());
2124 assert_eq!(
2125 callee_account.get_data(),
2126 &caller_account.serialized_data[..data_len]
2127 );
2128 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2129 assert!(is_zeroed(&data_slice[data_len..]));
2130 }
2131
2132 callee_account
2133 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2134 .unwrap();
2135 update_caller_account(
2136 &invoke_context,
2137 true, &mut caller_account,
2139 &mut callee_account,
2140 false, false, )
2143 .unwrap();
2144 let data_len = callee_account.get_data().len();
2145 assert_eq!(data_slice[data_len..].len(), 0);
2146 assert!(is_zeroed(&data_slice[data_len..]));
2147
2148 callee_account
2149 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2150 .unwrap();
2151 assert_matches!(
2152 update_caller_account(
2153 &invoke_context,
2154 true, &mut caller_account,
2156 &mut callee_account,
2157 false, false, ),
2160 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2161 );
2162
2163 callee_account.set_data_length(0).unwrap();
2165 callee_account
2166 .set_owner(system_program::id().as_ref())
2167 .unwrap();
2168 update_caller_account(
2169 &invoke_context,
2170 true, &mut caller_account,
2172 &mut callee_account,
2173 false, false, )
2176 .unwrap();
2177 let data_len = callee_account.get_data().len();
2178 assert_eq!(data_len, 0);
2179 }
2180
2181 #[case(false, false)]
2182 #[case(true, false)]
2183 #[case(true, true)]
2184 fn test_update_callee_account_lamports_owner(
2185 virtual_address_space_adjustments: bool,
2186 account_data_direct_mapping: bool,
2187 ) {
2188 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2189 let account = transaction_accounts[1].1.clone();
2190
2191 mock_invoke_context!(
2192 invoke_context,
2193 transaction_context,
2194 b"instruction data",
2195 transaction_accounts,
2196 0,
2197 &[1]
2198 );
2199
2200 let mut mock_caller_account =
2201 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2202 let config = Config {
2203 aligned_memory_mapping: false,
2204 ..Config::default()
2205 };
2206 let memory_mapping = unsafe {
2207 MemoryMapping::new(
2208 mock_caller_account.regions.clone(),
2209 &config,
2210 SBPFVersion::V3,
2211 )
2212 .unwrap()
2213 };
2214 let caller_account = mock_caller_account.caller_account();
2215
2216 borrow_instruction_account!(callee_account, invoke_context, 0);
2217
2218 *caller_account.lamports = 42;
2219 *caller_account.owner = Pubkey::new_unique();
2220
2221 update_callee_account(
2222 &memory_mapping,
2223 true, &caller_account,
2225 callee_account,
2226 virtual_address_space_adjustments,
2227 account_data_direct_mapping,
2228 )
2229 .unwrap();
2230
2231 borrow_instruction_account!(callee_account, invoke_context, 0);
2232 assert_eq!(callee_account.get_lamports(), 42);
2233 assert_eq!(caller_account.owner, callee_account.get_owner());
2234 }
2235
2236 #[case(false, false)]
2237 #[case(true, false)]
2238 #[case(true, true)]
2239 fn test_update_callee_account_data_writable(
2240 virtual_address_space_adjustments: bool,
2241 account_data_direct_mapping: bool,
2242 ) {
2243 let transaction_accounts =
2244 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2245 let account = transaction_accounts[1].1.clone();
2246
2247 mock_invoke_context!(
2248 invoke_context,
2249 transaction_context,
2250 b"instruction data",
2251 transaction_accounts,
2252 0,
2253 &[1]
2254 );
2255
2256 let mut mock_caller_account =
2257 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2258 let config = Config {
2259 aligned_memory_mapping: false,
2260 ..Config::default()
2261 };
2262 let memory_mapping = unsafe {
2263 MemoryMapping::new(
2264 mock_caller_account.regions.clone(),
2265 &config,
2266 SBPFVersion::V3,
2267 )
2268 .unwrap()
2269 };
2270 let mut caller_account = mock_caller_account.caller_account();
2271 borrow_instruction_account!(callee_account, invoke_context, 0);
2272
2273 caller_account.serialized_data[0] = b'b';
2275 update_callee_account(
2276 &memory_mapping,
2277 true, &caller_account,
2279 callee_account,
2280 false, false, )
2283 .unwrap();
2284 borrow_instruction_account!(callee_account, invoke_context, 0);
2285 assert_eq!(callee_account.get_data(), b"boobar");
2286
2287 let mut data = b"foobarbaz".to_vec();
2289 *caller_account.ref_to_len_in_vm = data.len() as u64;
2290 caller_account.serialized_data = &mut data;
2291 assert_eq!(
2292 update_callee_account(
2293 &memory_mapping,
2294 true, &caller_account,
2296 callee_account,
2297 virtual_address_space_adjustments,
2298 account_data_direct_mapping,
2299 )
2300 .unwrap(),
2301 virtual_address_space_adjustments,
2302 );
2303
2304 let mut data = b"baz".to_vec();
2306 *caller_account.ref_to_len_in_vm = data.len() as u64;
2307 caller_account.serialized_data = &mut data;
2308 borrow_instruction_account!(callee_account, invoke_context, 0);
2309 assert_eq!(
2310 update_callee_account(
2311 &memory_mapping,
2312 true, &caller_account,
2314 callee_account,
2315 virtual_address_space_adjustments,
2316 account_data_direct_mapping,
2317 )
2318 .unwrap(),
2319 virtual_address_space_adjustments,
2320 );
2321
2322 let mut data = Vec::new();
2324 caller_account.serialized_data = &mut data;
2325 *caller_account.ref_to_len_in_vm = 0;
2326 let mut owner = system_program::id();
2327 caller_account.owner = &mut owner;
2328 borrow_instruction_account!(callee_account, invoke_context, 0);
2329 update_callee_account(
2330 &memory_mapping,
2331 true, &caller_account,
2333 callee_account,
2334 virtual_address_space_adjustments,
2335 account_data_direct_mapping,
2336 )
2337 .unwrap();
2338 borrow_instruction_account!(callee_account, invoke_context, 0);
2339 assert_eq!(callee_account.get_data(), b"");
2340 }
2341
2342 #[case(false, false)]
2343 #[case(true, false)]
2344 #[case(true, true)]
2345 fn test_update_callee_account_data_readonly(
2346 virtual_address_space_adjustments: bool,
2347 account_data_direct_mapping: bool,
2348 ) {
2349 let transaction_accounts =
2350 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2351 let account = transaction_accounts[1].1.clone();
2352
2353 mock_invoke_context!(
2354 invoke_context,
2355 transaction_context,
2356 b"instruction data",
2357 transaction_accounts,
2358 0,
2359 &[1]
2360 );
2361
2362 let mut mock_caller_account =
2363 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2364 let config = Config {
2365 aligned_memory_mapping: false,
2366 ..Config::default()
2367 };
2368 let memory_mapping = unsafe {
2369 MemoryMapping::new(
2370 mock_caller_account.regions.clone(),
2371 &config,
2372 SBPFVersion::V3,
2373 )
2374 .unwrap()
2375 };
2376 let mut caller_account = mock_caller_account.caller_account();
2377 borrow_instruction_account!(callee_account, invoke_context, 0);
2378
2379 caller_account.serialized_data[0] = b'b';
2381 assert_matches!(
2382 update_callee_account(
2383 &memory_mapping,
2384 true, &caller_account,
2386 callee_account,
2387 false, false, ),
2390 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2391 );
2392
2393 let mut data = b"foobarbaz".to_vec();
2395 *caller_account.ref_to_len_in_vm = data.len() as u64;
2396 caller_account.serialized_data = &mut data;
2397 borrow_instruction_account!(callee_account, invoke_context, 0);
2398 assert_matches!(
2399 update_callee_account(
2400 &memory_mapping,
2401 true, &caller_account,
2403 callee_account,
2404 virtual_address_space_adjustments,
2405 account_data_direct_mapping,
2406 ),
2407 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2408 );
2409
2410 let mut data = b"baz".to_vec();
2412 *caller_account.ref_to_len_in_vm = data.len() as u64;
2413 caller_account.serialized_data = &mut data;
2414 borrow_instruction_account!(callee_account, invoke_context, 0);
2415 assert_matches!(
2416 update_callee_account(
2417 &memory_mapping,
2418 true, &caller_account,
2420 callee_account,
2421 virtual_address_space_adjustments,
2422 account_data_direct_mapping,
2423 ),
2424 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2425 );
2426 }
2427}