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