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_account_info::AccountInfo,
11 solana_instruction::{AccountMeta, 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
105const MAX_CPI_ACCOUNT_INFOS: usize = 255;
107
108fn check_account_info_pointer(
110 invoke_context: &InvokeContext,
111 vm_addr: u64,
112 expected_vm_addr: u64,
113 field: &str,
114) -> Result<(), Error> {
115 if vm_addr != expected_vm_addr {
116 ic_msg!(
117 invoke_context,
118 "Invalid account info pointer `{}': {:#x} != {:#x}",
119 field,
120 vm_addr,
121 expected_vm_addr
122 );
123 return Err(Box::new(CpiError::InvalidPointer));
124 }
125 Ok(())
126}
127
128fn check_instruction_size(num_accounts: usize, data_len: usize) -> Result<(), Error> {
130 if num_accounts > MAX_ACCOUNTS_PER_INSTRUCTION {
131 return Err(Box::new(CpiError::MaxInstructionAccountsExceeded {
132 num_accounts: num_accounts as u64,
133 max_accounts: MAX_ACCOUNTS_PER_INSTRUCTION as u64,
134 }));
135 }
136 if data_len > MAX_INSTRUCTION_DATA_LEN {
137 return Err(Box::new(CpiError::MaxInstructionDataLenExceeded {
138 data_len: data_len as u64,
139 max_data_len: MAX_INSTRUCTION_DATA_LEN as u64,
140 }));
141 }
142 Ok(())
143}
144
145fn check_account_infos(num_account_infos: usize) -> Result<(), Error> {
147 let num_account_infos = num_account_infos as u64;
148 let max_account_infos = MAX_CPI_ACCOUNT_INFOS as u64;
149 if num_account_infos > max_account_infos {
150 return Err(Box::new(CpiError::MaxInstructionAccountInfosExceeded {
151 num_account_infos,
152 max_account_infos,
153 }));
154 }
155 Ok(())
156}
157
158fn check_authorized_program(
160 program_id: &Pubkey,
161 instruction_data: &[u8],
162 invoke_context: &InvokeContext,
163) -> Result<(), Error> {
164 if native_loader::check_id(program_id)
165 || bpf_loader::check_id(program_id)
166 || bpf_loader_deprecated::check_id(program_id)
167 || (solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
168 && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
169 || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
170 || (invoke_context
171 .get_feature_set()
172 .enable_bpf_loader_set_authority_checked_ix
173 && bpf_loader_upgradeable::is_set_authority_checked_instruction(
174 instruction_data,
175 ))
176 || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
177 || invoke_context.is_precompile(program_id)
178 {
179 return Err(Box::new(CpiError::ProgramNotSupported(*program_id)));
180 }
181 Ok(())
182}
183
184#[derive(Debug)]
189pub struct CallerAccount<'a> {
190 pub lamports: &'a mut u64,
191 pub owner: &'a mut Pubkey,
192 pub original_data_len: usize,
197 pub serialized_data: &'a mut [u8],
203 pub vm_data_addr: u64,
206 pub ref_to_len_in_vm: &'a mut u64,
207}
208
209impl<'a> CallerAccount<'a> {
210 pub unsafe fn get_serialized_data(
217 memory_mapping: &solana_sbpf::memory_region::MemoryMapping,
218 check_aligned: bool,
219 vm_addr: u64,
220 original_data_len: usize,
221 len: usize,
222 syscall_parameter_address_restrictions: bool,
223 virtual_address_space_adjustments: bool,
224 account_data_direct_mapping: bool,
225 ) -> Result<&'a mut [u8], Error> {
226 use crate::memory::translate_slice_mut_for_cpi;
227
228 if syscall_parameter_address_restrictions {
229 let is_caller_loader_deprecated = !check_aligned;
230 let address_space_reserved_for_account = if is_caller_loader_deprecated {
231 original_data_len
232 } else {
233 original_data_len.saturating_add(MAX_PERMITTED_DATA_INCREASE)
234 };
235 if len > address_space_reserved_for_account {
236 return Err(InstructionError::InvalidRealloc.into());
237 }
238 }
239 if virtual_address_space_adjustments && account_data_direct_mapping {
240 Ok(&mut [])
241 } else if virtual_address_space_adjustments {
242 unsafe {
244 let serialization_ptr: &'a mut [u8] = translate_slice_mut_for_cpi::<u8>(
246 memory_mapping,
247 solana_sbpf::ebpf::MM_INPUT_START,
248 1,
249 false, )?;
251 Ok(std::slice::from_raw_parts_mut(
252 serialization_ptr
253 .as_mut_ptr()
254 .add(vm_addr.saturating_sub(solana_sbpf::ebpf::MM_INPUT_START) as usize),
255 len,
256 ))
257 }
258 } else {
259 unsafe {
260 translate_slice_mut_for_cpi::<u8>(
262 memory_mapping,
263 vm_addr,
264 len as u64,
265 false, )
267 }
268 }
269 }
270
271 pub fn from_account_info(
273 invoke_context: &InvokeContext,
274 memory_mapping: &MemoryMapping,
275 check_aligned: bool,
276 _vm_addr: u64,
277 account_info: &solana_account_info::AccountInfo,
278 account_metadata: &crate::memory_context::SerializedAccountMetadata,
279 ) -> Result<CallerAccount<'a>, Error> {
280 use crate::memory::{translate_type, translate_type_mut_for_cpi};
281
282 let syscall_parameter_address_restrictions = invoke_context
283 .get_feature_set()
284 .syscall_parameter_address_restrictions;
285 let virtual_address_space_adjustments = invoke_context
286 .get_feature_set()
287 .virtual_address_space_adjustments;
288 let account_data_direct_mapping =
289 invoke_context.get_feature_set().account_data_direct_mapping;
290
291 if syscall_parameter_address_restrictions {
292 check_account_info_pointer(
293 invoke_context,
294 account_info.key as *const _ as u64,
295 account_metadata.vm_key_addr,
296 "key",
297 )?;
298 check_account_info_pointer(
299 invoke_context,
300 account_info.owner as *const _ as u64,
301 account_metadata.vm_owner_addr,
302 "owner",
303 )?;
304 }
305
306 let lamports = {
309 let ptr = translate_type::<u64>(
311 memory_mapping,
312 account_info.lamports.as_ptr() as u64,
313 check_aligned,
314 )?;
315 if syscall_parameter_address_restrictions {
316 if account_info.lamports.as_ptr() as u64 >= solana_sbpf::ebpf::MM_INPUT_START {
317 return Err(Box::new(CpiError::InvalidPointer));
318 }
319
320 check_account_info_pointer(
321 invoke_context,
322 *ptr,
323 account_metadata.vm_lamports_addr,
324 "lamports",
325 )?;
326 }
327 translate_type_mut_for_cpi::<u64>(memory_mapping, *ptr, check_aligned)?
328 };
329
330 let owner = translate_type_mut_for_cpi::<Pubkey>(
331 memory_mapping,
332 account_info.owner as *const _ as u64,
333 check_aligned,
334 )?;
335
336 let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
337 if syscall_parameter_address_restrictions
338 && account_info.data.as_ptr() as u64 >= solana_sbpf::ebpf::MM_INPUT_START
339 {
340 return Err(Box::new(CpiError::InvalidPointer));
341 }
342
343 let data = *translate_type::<&[u8]>(
345 memory_mapping,
346 account_info.data.as_ptr() as *const _ as u64,
347 check_aligned,
348 )?;
349 if syscall_parameter_address_restrictions {
350 check_account_info_pointer(
351 invoke_context,
352 data.as_ptr() as u64,
353 account_metadata.vm_data_addr,
354 "data",
355 )?;
356 } else {
357 invoke_context.compute_meter.consume_checked(
359 (data.len() as u64)
360 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
361 .unwrap_or(u64::MAX),
362 )?;
363 }
364
365 let vm_len_addr = (account_info.data.as_ptr() as *const u64 as u64)
366 .saturating_add(std::mem::size_of::<u64>() as u64);
367 if syscall_parameter_address_restrictions {
368 if vm_len_addr >= solana_sbpf::ebpf::MM_INPUT_START {
372 return Err(Box::new(CpiError::InvalidPointer));
373 }
374 }
375 let ref_to_len_in_vm =
376 translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
377 let vm_data_addr = data.as_ptr() as u64;
378 let serialized_data = unsafe {
379 CallerAccount::get_serialized_data(
380 memory_mapping,
381 check_aligned,
382 vm_data_addr,
383 account_metadata.original_data_len,
384 if syscall_parameter_address_restrictions {
385 *ref_to_len_in_vm as usize
386 } else {
387 data.len()
388 },
389 syscall_parameter_address_restrictions,
390 virtual_address_space_adjustments,
391 account_data_direct_mapping,
392 )?
393 };
394 (serialized_data, vm_data_addr, ref_to_len_in_vm)
395 };
396
397 Ok(CallerAccount {
398 lamports,
399 owner,
400 original_data_len: account_metadata.original_data_len,
401 serialized_data,
402 vm_data_addr,
403 ref_to_len_in_vm,
404 })
405 }
406
407 fn from_sol_account_info(
409 invoke_context: &InvokeContext,
410 memory_mapping: &MemoryMapping,
411 check_aligned: bool,
412 vm_addr: u64,
413 account_info: &SolAccountInfo,
414 account_metadata: &crate::memory_context::SerializedAccountMetadata,
415 ) -> Result<CallerAccount<'a>, Error> {
416 use crate::memory::translate_type_mut_for_cpi;
417
418 let syscall_parameter_address_restrictions = invoke_context
419 .get_feature_set()
420 .syscall_parameter_address_restrictions;
421 let virtual_address_space_adjustments = invoke_context
422 .get_feature_set()
423 .virtual_address_space_adjustments;
424 let account_data_direct_mapping =
425 invoke_context.get_feature_set().account_data_direct_mapping;
426
427 if syscall_parameter_address_restrictions {
428 check_account_info_pointer(
429 invoke_context,
430 account_info.key_addr,
431 account_metadata.vm_key_addr,
432 "key",
433 )?;
434
435 check_account_info_pointer(
436 invoke_context,
437 account_info.owner_addr,
438 account_metadata.vm_owner_addr,
439 "owner",
440 )?;
441
442 check_account_info_pointer(
443 invoke_context,
444 account_info.lamports_addr,
445 account_metadata.vm_lamports_addr,
446 "lamports",
447 )?;
448
449 check_account_info_pointer(
450 invoke_context,
451 account_info.data_addr,
452 account_metadata.vm_data_addr,
453 "data",
454 )?;
455 }
456
457 let lamports = translate_type_mut_for_cpi::<u64>(
460 memory_mapping,
461 account_info.lamports_addr,
462 check_aligned,
463 )?;
464 let owner = translate_type_mut_for_cpi::<Pubkey>(
465 memory_mapping,
466 account_info.owner_addr,
467 check_aligned,
468 )?;
469
470 if !syscall_parameter_address_restrictions {
471 invoke_context.compute_meter.consume_checked(
473 account_info
474 .data_len
475 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
476 .unwrap_or(u64::MAX),
477 )?;
478 }
479
480 let vm_len_addr = vm_addr
485 .saturating_add(&account_info.data_len as *const u64 as u64)
486 .saturating_sub(account_info as *const _ as *const u64 as u64);
487 if syscall_parameter_address_restrictions {
488 if vm_len_addr >= solana_sbpf::ebpf::MM_INPUT_START {
492 return Err(Box::new(CpiError::InvalidPointer));
493 }
494 }
495 let ref_to_len_in_vm =
496 translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
497 let serialized_data = unsafe {
498 CallerAccount::get_serialized_data(
499 memory_mapping,
500 check_aligned,
501 account_info.data_addr,
502 account_metadata.original_data_len,
503 if syscall_parameter_address_restrictions {
504 *ref_to_len_in_vm as usize
505 } else {
506 account_info.data_len as usize
507 },
508 syscall_parameter_address_restrictions,
509 virtual_address_space_adjustments,
510 account_data_direct_mapping,
511 )?
512 };
513
514 Ok(CallerAccount {
515 lamports,
516 owner,
517 original_data_len: account_metadata.original_data_len,
518 serialized_data,
519 vm_data_addr: account_info.data_addr,
520 ref_to_len_in_vm,
521 })
522 }
523}
524
525pub trait SyscallInvokeSigned {
527 fn translate_instruction(
528 addr: u64,
529 invoke_context: &InvokeContext,
530 ) -> Result<Instruction, Error>;
531 fn translate_accounts<'a>(
532 account_infos_addr: u64,
533 account_infos_len: u64,
534 invoke_context: &InvokeContext,
535 ) -> Result<Vec<TranslatedAccount<'a>>, Error>;
536}
537
538pub fn translate_instruction_rust(
539 addr: u64,
540 invoke_context: &InvokeContext,
541) -> Result<Instruction, Error> {
542 let check_aligned = invoke_context.get_check_aligned();
543 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
544 let ix = translate_type::<StableInstruction>(memory_mapping, addr, check_aligned)?;
545 let account_metas = translate_slice::<mem::MaybeUninit<AccountMeta>>(
546 memory_mapping,
547 ix.accounts.as_vaddr(),
548 ix.accounts.len(),
549 check_aligned,
550 )?;
551 let data = translate_slice::<u8>(
552 memory_mapping,
553 ix.data.as_vaddr(),
554 ix.data.len(),
555 check_aligned,
556 )?;
557
558 check_instruction_size(account_metas.len(), data.len())?;
559
560 let mut total_cu_translation_cost: u64 = (data.len() as u64)
561 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
562 .unwrap_or(u64::MAX);
563
564 let account_meta_translation_cost =
566 (account_metas.len().saturating_mul(size_of::<AccountMeta>()) as u64)
567 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
568 .unwrap_or(u64::MAX);
569
570 total_cu_translation_cost =
571 total_cu_translation_cost.saturating_add(account_meta_translation_cost);
572
573 invoke_context
574 .compute_meter
575 .consume_checked(total_cu_translation_cost)?;
576
577 let mut accounts = Vec::with_capacity(account_metas.len());
578 for account_meta in account_metas {
579 let account_meta = unsafe {
582 let ptr = account_meta.as_ptr();
583 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
584 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
585 {
586 return Err(Box::new(InstructionError::InvalidArgument));
587 }
588 account_meta.assume_init_ref()
591 };
592
593 accounts.push(account_meta.clone());
594 }
595
596 Ok(Instruction {
597 accounts,
598 data: data.to_vec(),
599 program_id: ix.program_id,
600 })
601}
602
603pub fn translate_accounts_rust<'a>(
604 account_infos_addr: u64,
605 account_infos_len: u64,
606 invoke_context: &InvokeContext,
607) -> Result<Vec<TranslatedAccount<'a>>, Error> {
608 let check_aligned = invoke_context.get_check_aligned();
609 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
610 translate_account_infos(
611 account_infos_addr,
612 account_infos_len,
613 |account_info: &AccountInfo| account_info.key as *const _ as u64,
614 invoke_context,
615 memory_mapping,
616 check_aligned,
617 |account_infos, account_info_keys| {
618 translate_accounts_common(
619 &account_info_keys,
620 account_infos,
621 account_infos_addr,
622 invoke_context,
623 memory_mapping,
624 check_aligned,
625 CallerAccount::from_account_info,
626 )
627 },
628 )?
629}
630
631pub fn translate_signers(
632 program_id: &Pubkey,
633 signers_seeds_addr: u64,
634 signers_seeds_len: u64,
635 invoke_context: &InvokeContext,
636) -> Result<Vec<Pubkey>, Error> {
637 let check_aligned = invoke_context.get_check_aligned();
638 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
639 if signers_seeds_len > 0 {
640 let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
641 memory_mapping,
642 signers_seeds_addr,
643 signers_seeds_len,
644 check_aligned,
645 )?;
646 if signers_seeds.len() > MAX_SIGNERS {
647 return Err(Box::new(CpiError::TooManySigners));
648 }
649 Ok(signers_seeds
650 .iter()
651 .map(|signer_seeds| {
652 let untranslated_seeds = translate_slice::<VmSlice<u8>>(
653 memory_mapping,
654 signer_seeds.ptr(),
655 signer_seeds.len(),
656 check_aligned,
657 )?;
658 if untranslated_seeds.len() > MAX_SEEDS {
659 return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
660 }
661 let seeds_bytes = untranslated_seeds
662 .iter()
663 .map(|untranslated_seed| {
664 translate_vm_slice(untranslated_seed, memory_mapping, check_aligned)
665 })
666 .collect::<Result<Vec<_>, Error>>()?;
667 Pubkey::create_program_address(&seeds_bytes, program_id)
668 .map_err(|err| Box::new(CpiError::BadSeeds(err)) as Error)
669 })
670 .collect::<Result<Vec<_>, Error>>()?)
671 } else {
672 Ok(vec![])
673 }
674}
675
676pub fn translate_instruction_c(
677 addr: u64,
678 invoke_context: &InvokeContext,
679) -> Result<Instruction, Error> {
680 let check_aligned = invoke_context.get_check_aligned();
681 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
682 let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
683
684 let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
685 let account_metas = translate_slice::<mem::MaybeUninit<SolAccountMeta>>(
686 memory_mapping,
687 ix_c.accounts_addr,
688 ix_c.accounts_len,
689 check_aligned,
690 )?;
691 let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
692
693 check_instruction_size(ix_c.accounts_len as usize, data.len())?;
694
695 let mut total_cu_translation_cost: u64 = (data.len() as u64)
696 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
697 .unwrap_or(u64::MAX);
698
699 let account_meta_translation_cost = (ix_c
701 .accounts_len
702 .saturating_mul(size_of::<AccountMeta>() as u64))
703 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
704 .unwrap_or(u64::MAX);
705
706 total_cu_translation_cost =
707 total_cu_translation_cost.saturating_add(account_meta_translation_cost);
708
709 invoke_context
710 .compute_meter
711 .consume_checked(total_cu_translation_cost)?;
712
713 let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
714 for account_meta in account_metas {
715 let account_meta = unsafe {
718 let ptr = account_meta.as_ptr();
719 if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
720 || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
721 {
722 return Err(Box::new(InstructionError::InvalidArgument));
723 }
724 account_meta.assume_init_ref()
727 };
728 let pubkey =
729 translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
730 accounts.push(AccountMeta {
731 pubkey: *pubkey,
732 is_signer: account_meta.is_signer,
733 is_writable: account_meta.is_writable,
734 });
735 }
736
737 Ok(Instruction {
738 accounts,
739 data: data.to_vec(),
740 program_id: *program_id,
741 })
742}
743
744pub fn translate_accounts_c<'a>(
745 account_infos_addr: u64,
746 account_infos_len: u64,
747 invoke_context: &InvokeContext,
748) -> Result<Vec<TranslatedAccount<'a>>, Error> {
749 let check_aligned = invoke_context.get_check_aligned();
750 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
751 translate_account_infos(
752 account_infos_addr,
753 account_infos_len,
754 |account_info: &SolAccountInfo| account_info.key_addr,
755 invoke_context,
756 memory_mapping,
757 check_aligned,
758 |account_infos, account_info_keys| {
759 translate_accounts_common(
760 &account_info_keys,
761 account_infos,
762 account_infos_addr,
763 invoke_context,
764 memory_mapping,
765 check_aligned,
766 CallerAccount::from_sol_account_info,
767 )
768 },
769 )?
770}
771
772pub fn cpi_common<S: SyscallInvokeSigned>(
774 invoke_context: &mut InvokeContext,
775 instruction_addr: u64,
776 account_infos_addr: u64,
777 account_infos_len: u64,
778 signers_seeds_addr: u64,
779 signers_seeds_len: u64,
780) -> Result<u64, Error> {
781 let amount = invoke_context.get_execution_cost().invoke_units;
786 invoke_context.compute_meter.consume_checked(amount)?;
787 let syscall_parameter_address_restrictions = invoke_context
788 .get_feature_set()
789 .syscall_parameter_address_restrictions;
790 let virtual_address_space_adjustments = invoke_context
791 .get_feature_set()
792 .virtual_address_space_adjustments;
793 let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
794 let check_aligned = invoke_context.get_check_aligned();
795
796 let instruction = S::translate_instruction(instruction_addr, invoke_context)?;
797 let instruction_context = invoke_context
798 .transaction_context
799 .get_current_instruction_context()?;
800 let caller_program_id = instruction_context.get_program_key()?;
801 let signers = translate_signers(
802 caller_program_id,
803 signers_seeds_addr,
804 signers_seeds_len,
805 invoke_context,
806 )?;
807 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
808 invoke_context.prepare_next_cpi_instruction(instruction, &signers)?;
809
810 let mut accounts =
811 S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
812
813 if syscall_parameter_address_restrictions {
814 let transaction_context = &invoke_context.transaction_context;
819 let instruction_context = transaction_context.get_current_instruction_context()?;
820 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
821 for translated_account in accounts.iter_mut() {
822 let callee_account = instruction_context
823 .try_borrow_instruction_account(translated_account.index_in_caller)?;
824 let update_caller = update_callee_account(
826 memory_mapping,
827 check_aligned,
828 &translated_account.caller_account,
829 callee_account,
830 syscall_parameter_address_restrictions,
831 virtual_address_space_adjustments,
832 account_data_direct_mapping,
833 )?;
834 translated_account.update_caller_account_region =
835 translated_account.update_caller_account_info || update_caller;
836 }
837 }
838
839 let mut compute_units_consumed = 0;
841 invoke_context
842 .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
843
844 let transaction_context = &invoke_context.transaction_context;
846 let instruction_context = transaction_context.get_current_instruction_context()?;
847
848 for translated_account in accounts.iter_mut() {
852 let mut callee_account = instruction_context
853 .try_borrow_instruction_account(translated_account.index_in_caller)?;
854 if translated_account.update_caller_account_info {
855 update_caller_account(
856 invoke_context,
857 check_aligned,
858 &mut translated_account.caller_account,
859 &mut callee_account,
860 syscall_parameter_address_restrictions,
861 virtual_address_space_adjustments,
862 account_data_direct_mapping,
863 )?;
864 }
865 }
866
867 if virtual_address_space_adjustments {
868 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
869 for translated_account in accounts.iter() {
870 let mut callee_account = instruction_context
871 .try_borrow_instruction_account(translated_account.index_in_caller)?;
872 if translated_account.update_caller_account_region {
873 unsafe {
874 update_caller_account_region(
879 memory_mapping,
880 check_aligned,
881 &translated_account.caller_account,
882 &mut callee_account,
883 account_data_direct_mapping,
884 )?;
885 }
886 }
887 }
888 }
889
890 Ok(SUCCESS)
891}
892
893pub struct TranslatedAccount<'a> {
895 pub index_in_caller: IndexOfAccount,
896 pub caller_account: CallerAccount<'a>,
897 pub update_caller_account_region: bool,
898 pub update_caller_account_info: bool,
899}
900
901fn translate_account_infos<T, R>(
902 account_infos_addr: u64,
903 account_infos_len: u64,
904 key_addr: impl Fn(&T) -> u64,
905 invoke_context: &InvokeContext,
906 memory_mapping: &MemoryMapping,
907 check_aligned: bool,
908 cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
909) -> Result<R, Error> {
910 let syscall_parameter_address_restrictions = invoke_context
911 .get_feature_set()
912 .syscall_parameter_address_restrictions;
913
914 if syscall_parameter_address_restrictions
918 && account_infos_addr
919 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
920 >= ebpf::MM_INPUT_START
921 {
922 return Err(CpiError::InvalidPointer.into());
923 }
924
925 let account_infos = translate_slice::<T>(
926 memory_mapping,
927 account_infos_addr,
928 account_infos_len,
929 check_aligned,
930 )?;
931 check_account_infos(account_infos.len())?;
932
933 let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
934
935 let amount = (account_infos_bytes as u64)
936 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
937 .unwrap_or(u64::MAX);
938 invoke_context.compute_meter.consume_checked(amount)?;
939
940 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
941 #[expect(clippy::needless_range_loop)]
942 for account_index in 0..account_infos_len as usize {
943 #[expect(clippy::indexing_slicing)]
944 let account_info = &account_infos[account_index];
945 account_info_keys.push(translate_type::<Pubkey>(
946 memory_mapping,
947 key_addr(account_info),
948 check_aligned,
949 )?);
950 }
951 Ok(cb(account_infos, account_info_keys))
952}
953
954fn translate_accounts_common<'a, T, F>(
956 account_info_keys: &[&Pubkey],
957 account_infos: &[T],
958 account_infos_addr: u64,
959 invoke_context: &InvokeContext,
960 memory_mapping: &MemoryMapping,
961 check_aligned: bool,
962 do_translate: F,
963) -> Result<Vec<TranslatedAccount<'a>>, Error>
964where
965 F: Fn(
966 &InvokeContext,
967 &MemoryMapping,
968 bool,
969 u64,
970 &T,
971 &SerializedAccountMetadata,
972 ) -> Result<CallerAccount<'a>, Error>,
973{
974 let transaction_context = &invoke_context.transaction_context;
975 let next_instruction_context = transaction_context.get_next_instruction_context()?;
976 let next_instruction_accounts = next_instruction_context.instruction_accounts();
977 let instruction_context = transaction_context.get_current_instruction_context()?;
978 let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
979
980 let accounts_metadata = &invoke_context
983 .memory_contexts
984 .memory_context_abi_v1()
985 .unwrap()
986 .accounts_metadata;
987
988 let syscall_parameter_address_restrictions = invoke_context
989 .get_feature_set()
990 .syscall_parameter_address_restrictions;
991 let virtual_address_space_adjustments = invoke_context
992 .get_feature_set()
993 .virtual_address_space_adjustments;
994 let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
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 if syscall_parameter_address_restrictions {
1053 let amount = (*caller_account.ref_to_len_in_vm)
1055 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1056 .unwrap_or(u64::MAX);
1057 invoke_context.compute_meter.consume_checked(amount)?;
1058 }
1059 let update_caller = if syscall_parameter_address_restrictions {
1060 true
1062 } else {
1063 update_callee_account(
1068 memory_mapping,
1069 check_aligned,
1070 &caller_account,
1071 callee_account,
1072 syscall_parameter_address_restrictions,
1073 virtual_address_space_adjustments,
1074 account_data_direct_mapping,
1075 )?
1076 };
1077
1078 accounts.push(TranslatedAccount {
1079 index_in_caller,
1080 caller_account,
1081 update_caller_account_region: instruction_account.is_writable() || update_caller,
1082 update_caller_account_info: instruction_account.is_writable(),
1083 });
1084 } else {
1085 ic_msg!(
1086 invoke_context,
1087 "Instruction references an unknown account {}",
1088 account_key
1089 );
1090 return Err(Box::new(InstructionError::MissingAccount));
1091 }
1092 }
1093
1094 Ok(accounts)
1095}
1096
1097fn update_callee_account(
1109 memory_mapping: &MemoryMapping,
1110 check_aligned: bool,
1111 caller_account: &CallerAccount,
1112 mut callee_account: BorrowedInstructionAccount<'_, '_>,
1113 syscall_parameter_address_restrictions: bool,
1114 virtual_address_space_adjustments: bool,
1115 account_data_direct_mapping: bool,
1116) -> Result<bool, Error> {
1117 let mut must_update_caller = false;
1118
1119 if callee_account.get_lamports() != *caller_account.lamports {
1120 callee_account.set_lamports(*caller_account.lamports)?;
1121 }
1122
1123 if virtual_address_space_adjustments {
1124 let prev_len = callee_account.get_data().len();
1125 let post_len = *caller_account.ref_to_len_in_vm as usize;
1126 if prev_len != post_len {
1127 if !account_data_direct_mapping && post_len < prev_len {
1128 let serialized_data = unsafe {
1131 CallerAccount::get_serialized_data(
1132 memory_mapping,
1133 check_aligned,
1134 caller_account.vm_data_addr,
1135 caller_account.original_data_len,
1136 prev_len,
1137 syscall_parameter_address_restrictions,
1138 virtual_address_space_adjustments,
1139 account_data_direct_mapping,
1140 )?
1141 };
1142 serialized_data
1143 .get_mut(post_len..)
1144 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1145 .fill(0);
1146 }
1147 callee_account.set_data_length(post_len)?;
1148 must_update_caller = true;
1150 }
1151 if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1152 callee_account.set_data_from_slice(caller_account.serialized_data)?;
1153 }
1154 } else {
1155 match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1157 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1158 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1159 return Err(Box::new(err));
1160 }
1161 _ => {}
1162 }
1163 }
1164
1165 if callee_account.get_owner() != caller_account.owner {
1167 callee_account.set_owner(caller_account.owner.as_ref())?;
1168 must_update_caller = true;
1170 }
1171
1172 Ok(must_update_caller)
1173}
1174
1175unsafe fn update_caller_account_region(
1180 memory_mapping: &mut MemoryMapping,
1181 check_aligned: bool,
1182 caller_account: &CallerAccount,
1183 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1184 account_data_direct_mapping: bool,
1185) -> Result<(), Error> {
1186 let is_caller_loader_deprecated = !check_aligned;
1187 let address_space_reserved_for_account = if is_caller_loader_deprecated {
1188 caller_account.original_data_len
1189 } else {
1190 caller_account
1191 .original_data_len
1192 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1193 };
1194
1195 if address_space_reserved_for_account > 0 {
1196 let (region_index, region) = memory_mapping
1199 .find_region(caller_account.vm_data_addr)
1200 .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1201 debug_assert_eq!(region.vm_addr, caller_account.vm_data_addr);
1203 let mut new_region;
1204 if !account_data_direct_mapping {
1205 new_region = region.clone();
1206 modify_memory_region_of_account(callee_account, &mut new_region);
1207 } else {
1208 new_region = create_memory_region_of_account(callee_account, region.vm_addr)?;
1209 }
1210 unsafe {
1211 memory_mapping.replace_region(region_index, new_region)?;
1216 }
1217 }
1218
1219 Ok(())
1220}
1221
1222fn update_caller_account(
1235 invoke_context: &InvokeContext,
1236 check_aligned: bool,
1237 caller_account: &mut CallerAccount<'_>,
1238 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1239 syscall_parameter_address_restrictions: bool,
1240 virtual_address_space_adjustments: bool,
1241 account_data_direct_mapping: bool,
1242) -> Result<(), Error> {
1243 *caller_account.lamports = callee_account.get_lamports();
1244 *caller_account.owner = *callee_account.get_owner();
1245
1246 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1247 let post_len = callee_account.get_data().len();
1248 let is_caller_loader_deprecated = !check_aligned;
1249 let address_space_reserved_for_account =
1250 if syscall_parameter_address_restrictions && is_caller_loader_deprecated {
1251 caller_account.original_data_len
1252 } else {
1253 caller_account
1254 .original_data_len
1255 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1256 };
1257
1258 if post_len > address_space_reserved_for_account
1259 && (syscall_parameter_address_restrictions || prev_len != post_len)
1260 {
1261 let max_increase =
1262 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1263 ic_msg!(
1264 invoke_context,
1265 "Account data size realloc limited to {max_increase} in inner instructions",
1266 );
1267 return Err(Box::new(InstructionError::InvalidRealloc));
1268 }
1269
1270 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1271 if prev_len != post_len {
1272 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1275 if post_len < prev_len {
1278 caller_account
1279 .serialized_data
1280 .get_mut(post_len..)
1281 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1282 .fill(0);
1283 }
1284 unsafe {
1286 caller_account.serialized_data = CallerAccount::get_serialized_data(
1287 memory_mapping,
1288 check_aligned,
1289 caller_account.vm_data_addr,
1290 caller_account.original_data_len,
1291 post_len,
1292 syscall_parameter_address_restrictions,
1293 virtual_address_space_adjustments,
1294 account_data_direct_mapping,
1295 )?;
1296 }
1297 }
1298 *caller_account.ref_to_len_in_vm = post_len as u64;
1300
1301 let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1303 memory_mapping,
1304 caller_account
1305 .vm_data_addr
1306 .saturating_sub(std::mem::size_of::<u64>() as u64),
1307 check_aligned,
1308 )?;
1309 *serialized_len_ptr = post_len as u64;
1310 }
1311
1312 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1313 let to_slice = &mut caller_account.serialized_data;
1315 let from_slice = callee_account
1316 .get_data()
1317 .get(0..post_len)
1318 .ok_or(CpiError::InvalidLength)?;
1319 if to_slice.len() != from_slice.len() {
1320 return Err(Box::new(InstructionError::AccountDataTooSmall));
1321 }
1322 to_slice.copy_from_slice(from_slice);
1323 }
1324
1325 Ok(())
1326}
1327
1328#[allow(clippy::indexing_slicing)]
1329#[allow(clippy::arithmetic_side_effects)]
1330#[cfg(test)]
1331mod tests {
1332 use {
1333 super::*,
1334 crate::{
1335 invoke_context::BpfAllocator,
1336 memory::translate_type,
1337 memory_context::{MemoryContext, SerializedAccountMetadata},
1338 with_mock_invoke_context_with_feature_set,
1339 },
1340 assert_matches::assert_matches,
1341 solana_account::{Account, AccountSharedData, ReadableAccount},
1342 solana_account_info::AccountInfo,
1343 solana_sbpf::{
1344 ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1345 },
1346 solana_sdk_ids::{bpf_loader, system_program},
1347 solana_svm_feature_set::SVMFeatureSet,
1348 solana_transaction_context::{
1349 IndexOfAccount, instruction_accounts::InstructionAccount,
1350 transaction_accounts::KeyedAccountSharedData,
1351 },
1352 std::{
1353 cell::{Cell, RefCell},
1354 mem, ptr,
1355 rc::Rc,
1356 slice,
1357 },
1358 test_case::case,
1359 };
1360
1361 macro_rules! mock_invoke_context {
1362 ($invoke_context:ident,
1363 $transaction_context:ident,
1364 $instruction_data:expr,
1365 $transaction_accounts:expr,
1366 $program_account:expr,
1367 $instruction_accounts:expr) => {
1368 let instruction_data = $instruction_data;
1369 let instruction_accounts = $instruction_accounts
1370 .iter()
1371 .map(|index_in_transaction| {
1372 InstructionAccount::new(
1373 *index_in_transaction as IndexOfAccount,
1374 false,
1375 $transaction_accounts[*index_in_transaction as usize].2,
1376 )
1377 })
1378 .collect::<Vec<_>>();
1379 let transaction_accounts = $transaction_accounts
1380 .into_iter()
1381 .map(|a| (a.0, a.1))
1382 .collect::<Vec<KeyedAccountSharedData>>();
1383 let mut feature_set = SVMFeatureSet::all_enabled();
1384 feature_set.syscall_parameter_address_restrictions = false;
1385 feature_set.virtual_address_space_adjustments = false;
1386 feature_set.account_data_direct_mapping = false;
1387 let feature_set = &feature_set;
1388 with_mock_invoke_context_with_feature_set!(
1389 $invoke_context,
1390 $transaction_context,
1391 feature_set,
1392 transaction_accounts
1393 );
1394 $invoke_context
1395 .transaction_context
1396 .configure_top_level_instruction_for_tests(
1397 $program_account,
1398 instruction_accounts,
1399 instruction_data.to_vec(),
1400 )
1401 .unwrap();
1402 $invoke_context.push().unwrap();
1403 };
1404 }
1405
1406 macro_rules! borrow_instruction_account {
1407 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1408 let instruction_context = $invoke_context
1409 .transaction_context
1410 .get_current_instruction_context()
1411 .unwrap();
1412 let $borrowed_account = instruction_context
1413 .try_borrow_instruction_account($index)
1414 .unwrap();
1415 };
1416 }
1417
1418 fn is_zeroed(data: &[u8]) -> bool {
1419 data.iter().all(|b| *b == 0)
1420 }
1421
1422 struct MockCallerAccount {
1423 lamports: u64,
1424 owner: Pubkey,
1425 vm_addr: u64,
1426 data: Vec<u8>,
1427 len: u64,
1428 regions: Vec<MemoryRegion>,
1429 virtual_address_space_adjustments: bool,
1430 }
1431
1432 impl MockCallerAccount {
1433 fn new(
1434 lamports: u64,
1435 owner: Pubkey,
1436 data: &[u8],
1437 virtual_address_space_adjustments: bool,
1438 ) -> MockCallerAccount {
1439 let vm_addr = MM_INPUT_START;
1440 let mut region_addr = vm_addr;
1441 let region_len = mem::size_of::<u64>()
1442 + if virtual_address_space_adjustments {
1443 0
1444 } else {
1445 data.len() + MAX_PERMITTED_DATA_INCREASE
1446 };
1447 let mut d = vec![0; region_len];
1448 let mut regions = vec![];
1449
1450 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1452
1453 if !virtual_address_space_adjustments {
1455 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1456 }
1457
1458 regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1460 region_addr += region_len as u64;
1461
1462 if virtual_address_space_adjustments {
1463 regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1465 region_addr += data.len() as u64;
1466
1467 regions.push(MemoryRegion::new(
1469 &raw mut d[mem::size_of::<u64>()..],
1470 region_addr,
1471 ));
1472 } else {
1473 d.truncate(mem::size_of::<u64>() + data.len());
1475 }
1476
1477 MockCallerAccount {
1478 lamports,
1479 owner,
1480 vm_addr,
1481 data: d,
1482 len: data.len() as u64,
1483 regions,
1484 virtual_address_space_adjustments,
1485 }
1486 }
1487
1488 fn data_slice<'a>(&self) -> &'a [u8] {
1489 unsafe {
1491 slice::from_raw_parts(
1492 self.data[mem::size_of::<u64>()..].as_ptr(),
1493 self.data.capacity() - mem::size_of::<u64>(),
1494 )
1495 }
1496 }
1497
1498 fn caller_account(&mut self) -> CallerAccount<'_> {
1499 let data = if self.virtual_address_space_adjustments {
1500 &mut []
1501 } else {
1502 &mut self.data[mem::size_of::<u64>()..]
1503 };
1504 CallerAccount {
1505 lamports: &mut self.lamports,
1506 owner: &mut self.owner,
1507 original_data_len: self.len as usize,
1508 serialized_data: data,
1509 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1510 ref_to_len_in_vm: &mut self.len,
1511 }
1512 }
1513 }
1514
1515 struct MockAccountInfo<'a> {
1516 key: Pubkey,
1517 is_signer: bool,
1518 is_writable: bool,
1519 lamports: u64,
1520 data: &'a [u8],
1521 owner: Pubkey,
1522 executable: bool,
1523 _unused: u64,
1524 }
1525
1526 impl MockAccountInfo<'_> {
1527 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1528 MockAccountInfo {
1529 key,
1530 is_signer: false,
1531 is_writable: false,
1532 lamports: account.lamports(),
1533 data: account.data(),
1534 owner: *account.owner(),
1535 executable: account.executable(),
1536 _unused: account.rent_epoch(),
1537 }
1538 }
1539
1540 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1541 let size = mem::size_of::<AccountInfo>()
1542 + mem::size_of::<Pubkey>() * 2
1543 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1544 + mem::size_of::<u64>()
1545 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1546 + self.data.len();
1547 let mut data = vec![0; size];
1548
1549 let vm_addr = vm_addr as usize;
1550 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1551 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1552 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1553 let owner_addr = lamports_addr + mem::size_of::<u64>();
1554 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1555 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1556
1557 #[allow(deprecated)]
1558 #[allow(clippy::used_underscore_binding)]
1559 let info = AccountInfo {
1560 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1561 is_signer: self.is_signer,
1562 is_writable: self.is_writable,
1563 lamports: unsafe {
1564 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1565 },
1566 data: unsafe {
1567 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1568 },
1569 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1570 executable: self.executable,
1571 _unused: self._unused,
1572 };
1573
1574 unsafe {
1575 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1576 ptr::write_unaligned(
1577 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1578 self.key,
1579 );
1580 ptr::write_unaligned(
1581 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1582 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1583 );
1584 ptr::write_unaligned(
1585 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1586 self.lamports,
1587 );
1588 ptr::write_unaligned(
1589 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1590 self.owner,
1591 );
1592 ptr::write_unaligned(
1593 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1594 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1595 data_addr as *mut u8,
1596 self.data.len(),
1597 ))),
1598 );
1599 data[data_addr - vm_addr..].copy_from_slice(self.data);
1600 }
1601
1602 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1603 (
1604 data,
1605 region,
1606 SerializedAccountMetadata {
1607 vm_addr: vm_addr as u64,
1608 original_data_len: self.data.len(),
1609 vm_key_addr: key_addr as u64,
1610 vm_lamports_addr: lamports_addr as u64,
1611 vm_owner_addr: owner_addr as u64,
1612 vm_data_addr: data_addr as u64,
1613 },
1614 )
1615 }
1616 }
1617
1618 struct MockInstruction {
1619 program_id: Pubkey,
1620 accounts: Vec<AccountMeta>,
1621 data: Vec<u8>,
1622 }
1623
1624 impl MockInstruction {
1625 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1626 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1627
1628 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1629
1630 let mut data = vec![0; size];
1631
1632 let vm_addr = vm_addr as usize;
1633 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1634 let data_addr = accounts_addr + accounts_len;
1635
1636 let ins = Instruction {
1637 program_id: self.program_id,
1638 accounts: unsafe {
1639 Vec::from_raw_parts(
1640 accounts_addr as *mut _,
1641 self.accounts.len(),
1642 self.accounts.len(),
1643 )
1644 },
1645 data: unsafe {
1646 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1647 },
1648 };
1649 let ins = StableInstruction::from(ins);
1650
1651 unsafe {
1652 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1653 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1654 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1655 );
1656 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1657 }
1658
1659 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1660 (data, region)
1661 }
1662 }
1663
1664 #[repr(C)]
1665 struct RcBox<T> {
1666 strong: Cell<usize>,
1667 weak: Cell<usize>,
1668 value: T,
1669 }
1670
1671 impl<T> RcBox<T> {
1672 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1673 fn new(value: T) -> RcBox<T> {
1674 RcBox {
1675 strong: Cell::new(0),
1676 weak: Cell::new(0),
1677 value,
1678 }
1679 }
1680 }
1681
1682 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1683
1684 fn transaction_with_one_writable_instruction_account(
1685 data: Vec<u8>,
1686 ) -> Vec<TestTransactionAccount> {
1687 let program_id = Pubkey::new_unique();
1688 let account = AccountSharedData::from(Account {
1689 lamports: 1,
1690 data,
1691 owner: program_id,
1692 executable: false,
1693 rent_epoch: 100,
1694 });
1695 vec![
1696 (
1697 program_id,
1698 AccountSharedData::from(Account {
1699 lamports: 0,
1700 data: vec![],
1701 owner: bpf_loader::id(),
1702 executable: true,
1703 rent_epoch: 0,
1704 }),
1705 false,
1706 ),
1707 (Pubkey::new_unique(), account, true),
1708 ]
1709 }
1710
1711 fn transaction_with_one_readonly_instruction_account(
1712 data: Vec<u8>,
1713 ) -> Vec<TestTransactionAccount> {
1714 let program_id = Pubkey::new_unique();
1715 let account_owner = Pubkey::new_unique();
1716 let account = AccountSharedData::from(Account {
1717 lamports: 1,
1718 data,
1719 owner: account_owner,
1720 executable: false,
1721 rent_epoch: 100,
1722 });
1723 vec![
1724 (
1725 program_id,
1726 AccountSharedData::from(Account {
1727 lamports: 0,
1728 data: vec![],
1729 owner: bpf_loader::id(),
1730 executable: true,
1731 rent_epoch: 0,
1732 }),
1733 false,
1734 ),
1735 (Pubkey::new_unique(), account, true),
1736 ]
1737 }
1738
1739 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1740 let vm_addr = vm_addr as usize;
1741
1742 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
1745 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1746
1747 let total_size = fat_ptr_size_of_slice
1749 + singers_length * fat_ptr_size_of_slice
1750 + sum_signers_data_length;
1751 let mut data = vec![0; total_size];
1752
1753 data[..fat_ptr_size_of_slice / 2]
1765 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1766 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1767 .clone_from_slice(&(singers_length).to_le_bytes());
1768
1769 let (mut p1, mut p2) = (
1771 fat_ptr_size_of_slice,
1772 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1773 );
1774 for signer in signers.iter() {
1775 let signer_length = signer.len();
1776
1777 data[p1..p1 + fat_ptr_size_of_slice / 2]
1779 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1780 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1781 .clone_from_slice(&(signer_length).to_le_bytes());
1782 p1 += fat_ptr_size_of_slice;
1783
1784 data[p2..p2 + signer_length].clone_from_slice(signer);
1786 p2 += signer_length;
1787 }
1788
1789 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1790 (data, region)
1791 }
1792
1793 #[test]
1794 fn test_translate_instruction() {
1795 let transaction_accounts =
1796 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1797 mock_invoke_context!(
1798 invoke_context,
1799 transaction_context,
1800 b"instruction data",
1801 transaction_accounts,
1802 0,
1803 &[1]
1804 );
1805
1806 let program_id = Pubkey::new_unique();
1807 let accounts = vec![AccountMeta {
1808 pubkey: Pubkey::new_unique(),
1809 is_signer: true,
1810 is_writable: false,
1811 }];
1812 let data = b"ins data".to_vec();
1813 let vm_addr = MM_INPUT_START;
1814 let (_mem, region) = MockInstruction {
1815 program_id,
1816 accounts: accounts.clone(),
1817 data: data.clone(),
1818 }
1819 .into_region(vm_addr);
1820
1821 let config = Config {
1822 aligned_memory_mapping: false,
1823 ..Config::default()
1824 };
1825 let memory_mapping =
1826 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1827 invoke_context
1828 .memory_contexts
1829 .mock_set_mapping_abi_v1(memory_mapping);
1830
1831 let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1832 assert_eq!(ins.program_id, program_id);
1833 assert_eq!(ins.accounts, accounts);
1834 assert_eq!(ins.data, data);
1835 }
1836
1837 #[test]
1838 fn test_translate_signers() {
1839 let transaction_accounts =
1840 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1841 mock_invoke_context!(
1842 invoke_context,
1843 transaction_context,
1844 b"instruction data",
1845 transaction_accounts,
1846 0,
1847 &[1]
1848 );
1849
1850 let program_id = Pubkey::new_unique();
1851 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1852
1853 let vm_addr = MM_INPUT_START;
1854 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1855
1856 let config = Config {
1857 aligned_memory_mapping: false,
1858 ..Config::default()
1859 };
1860 let mapping =
1861 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1862 invoke_context
1863 .memory_contexts
1864 .set_memory_context_abi_v1(MemoryContext::new(
1865 BpfAllocator::new(0),
1866 Vec::new(),
1867 mapping,
1868 ))
1869 .unwrap();
1870
1871 let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1872 assert_eq!(signers[0], derived_key);
1873 }
1874
1875 #[test]
1876 fn test_translate_accounts_rust() {
1877 let transaction_accounts =
1878 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1879 let account = transaction_accounts[1].1.clone();
1880 let key = transaction_accounts[1].0;
1881 let original_data_len = account.data().len();
1882
1883 let vm_addr = MM_INPUT_START;
1884 let (_mem, region, account_metadata) =
1885 MockAccountInfo::new(key, &account).into_region(vm_addr);
1886
1887 let config = Config {
1888 aligned_memory_mapping: false,
1889 ..Config::default()
1890 };
1891 let memory_mapping =
1892 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1893
1894 mock_invoke_context!(
1895 invoke_context,
1896 transaction_context,
1897 b"instruction data",
1898 transaction_accounts,
1899 0,
1900 &[1, 1]
1901 );
1902
1903 invoke_context
1904 .memory_contexts
1905 .set_memory_context_abi_v1(MemoryContext::new(
1906 BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1907 vec![account_metadata],
1908 memory_mapping,
1909 ))
1910 .unwrap();
1911
1912 invoke_context
1913 .transaction_context
1914 .configure_next_cpi_for_tests(
1915 0,
1916 vec![
1917 InstructionAccount::new(1, false, true),
1918 InstructionAccount::new(1, false, true),
1919 ],
1920 vec![],
1921 )
1922 .unwrap();
1923
1924 let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1925 assert_eq!(accounts.len(), 1);
1926 let caller_account = &accounts[0].caller_account;
1927 assert_eq!(caller_account.serialized_data, account.data());
1928 assert_eq!(caller_account.original_data_len, original_data_len);
1929 }
1930
1931 #[test]
1932 fn test_get_serialized_data() {
1933 let transaction_accounts =
1934 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1935 let account = transaction_accounts[1].1.clone();
1936 mock_invoke_context!(
1937 invoke_context,
1938 transaction_context,
1939 b"instruction data",
1940 transaction_accounts,
1941 0,
1942 &[1]
1943 );
1944
1945 let config = Config {
1946 aligned_memory_mapping: false,
1947 ..Config::default()
1948 };
1949 let memory_mapping =
1950 unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1951 let serialized_data = unsafe {
1952 CallerAccount::get_serialized_data(
1953 &memory_mapping,
1954 true, MM_INPUT_START,
1956 account.data().len(),
1957 account
1958 .data()
1959 .len()
1960 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1961 .saturating_add(1),
1962 true, true, false, )
1966 };
1967
1968 assert_matches!(
1969 serialized_data,
1970 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1971 );
1972 }
1973
1974 #[test]
1975 fn test_caller_account_from_account_info() {
1976 let transaction_accounts =
1977 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1978 let account = transaction_accounts[1].1.clone();
1979 mock_invoke_context!(
1980 invoke_context,
1981 transaction_context,
1982 b"instruction data",
1983 transaction_accounts,
1984 0,
1985 &[1]
1986 );
1987
1988 let key = Pubkey::new_unique();
1989 let vm_addr = MM_INPUT_START;
1990 let (_mem, region, account_metadata) =
1991 MockAccountInfo::new(key, &account).into_region(vm_addr);
1992
1993 let config = Config {
1994 aligned_memory_mapping: false,
1995 ..Config::default()
1996 };
1997 let memory_mapping =
1998 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1999
2000 let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
2001
2002 invoke_context
2003 .memory_contexts
2004 .mock_set_mapping_abi_v1(memory_mapping);
2005 let check_aligned = invoke_context.get_check_aligned();
2006 let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
2007 let caller_account = CallerAccount::from_account_info(
2008 &invoke_context,
2009 memory_mapping,
2010 check_aligned,
2011 vm_addr,
2012 account_info,
2013 &account_metadata,
2014 )
2015 .unwrap();
2016 assert_eq!(*caller_account.lamports, account.lamports());
2017 assert_eq!(caller_account.owner, account.owner());
2018 assert_eq!(caller_account.original_data_len, account.data().len());
2019 assert_eq!(
2020 *caller_account.ref_to_len_in_vm as usize,
2021 account.data().len()
2022 );
2023 assert_eq!(caller_account.serialized_data, account.data());
2024 }
2025
2026 #[case(false, false, false)]
2027 #[case(true, false, false)]
2028 #[case(true, true, false)]
2029 #[case(true, true, true)]
2030 fn test_update_caller_account_lamports_owner(
2031 syscall_parameter_address_restrictions: bool,
2032 virtual_address_space_adjustments: bool,
2033 account_data_direct_mapping: bool,
2034 ) {
2035 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2036 let account = transaction_accounts[1].1.clone();
2037 mock_invoke_context!(
2038 invoke_context,
2039 transaction_context,
2040 b"instruction data",
2041 transaction_accounts,
2042 0,
2043 &[1]
2044 );
2045
2046 let mut mock_caller_account =
2047 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2048
2049 let config = Config {
2050 aligned_memory_mapping: false,
2051 ..Config::default()
2052 };
2053 let memory_mapping = unsafe {
2054 MemoryMapping::new(
2055 mock_caller_account.regions.split_off(0),
2056 &config,
2057 SBPFVersion::V3,
2058 )
2059 .unwrap()
2060 };
2061 invoke_context
2062 .memory_contexts
2063 .mock_set_mapping_abi_v1(memory_mapping);
2064
2065 let mut caller_account = mock_caller_account.caller_account();
2066 let instruction_context = invoke_context
2067 .transaction_context
2068 .get_current_instruction_context()
2069 .unwrap();
2070 let mut callee_account = instruction_context
2071 .try_borrow_instruction_account(0)
2072 .unwrap();
2073 callee_account.set_lamports(42).unwrap();
2074 callee_account
2075 .set_owner(Pubkey::new_unique().as_ref())
2076 .unwrap();
2077
2078 update_caller_account(
2079 &invoke_context,
2080 true, &mut caller_account,
2082 &mut callee_account,
2083 syscall_parameter_address_restrictions,
2084 virtual_address_space_adjustments,
2085 account_data_direct_mapping,
2086 )
2087 .unwrap();
2088
2089 assert_eq!(*caller_account.lamports, 42);
2090 assert_eq!(caller_account.owner, callee_account.get_owner());
2091 }
2092
2093 #[test]
2094 fn test_update_caller_account_data() {
2095 let transaction_accounts =
2096 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2097 let account = transaction_accounts[1].1.clone();
2098 let original_data_len = account.data().len();
2099
2100 mock_invoke_context!(
2101 invoke_context,
2102 transaction_context,
2103 b"instruction data",
2104 transaction_accounts,
2105 0,
2106 &[1]
2107 );
2108
2109 let mut mock_caller_account =
2110 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2111
2112 let config = Config {
2113 aligned_memory_mapping: false,
2114 ..Config::default()
2115 };
2116 let memory_mapping = unsafe {
2117 MemoryMapping::new(
2118 mock_caller_account.regions.clone(),
2119 &config,
2120 SBPFVersion::V3,
2121 )
2122 .unwrap()
2123 };
2124 invoke_context
2125 .memory_contexts
2126 .mock_set_mapping_abi_v1(memory_mapping);
2127
2128 let data_slice = mock_caller_account.data_slice();
2129 let len_ptr = unsafe {
2130 data_slice
2131 .as_ptr()
2132 .offset(-(mem::size_of::<u64>() as isize))
2133 };
2134 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2135 let mut caller_account = mock_caller_account.caller_account();
2136 let instruction_context = invoke_context
2137 .transaction_context
2138 .get_current_instruction_context()
2139 .unwrap();
2140 let mut callee_account = instruction_context
2141 .try_borrow_instruction_account(0)
2142 .unwrap();
2143
2144 for (new_value, expected_realloc_size) in [
2145 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2146 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2147 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2148 ] {
2149 assert_eq!(caller_account.serialized_data, callee_account.get_data());
2150 callee_account.set_data_from_slice(&new_value).unwrap();
2151
2152 update_caller_account(
2153 &invoke_context,
2154 true, &mut caller_account,
2156 &mut callee_account,
2157 false, false, false, )
2161 .unwrap();
2162
2163 let data_len = callee_account.get_data().len();
2164 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2165 assert_eq!(data_len, serialized_len());
2166 assert_eq!(data_len, caller_account.serialized_data.len());
2167 assert_eq!(
2168 callee_account.get_data(),
2169 &caller_account.serialized_data[..data_len]
2170 );
2171 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2172 assert!(is_zeroed(&data_slice[data_len..]));
2173 }
2174
2175 callee_account
2176 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2177 .unwrap();
2178 update_caller_account(
2179 &invoke_context,
2180 true, &mut caller_account,
2182 &mut callee_account,
2183 false, false, false, )
2187 .unwrap();
2188 let data_len = callee_account.get_data().len();
2189 assert_eq!(data_slice[data_len..].len(), 0);
2190 assert!(is_zeroed(&data_slice[data_len..]));
2191
2192 callee_account
2193 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2194 .unwrap();
2195 assert_matches!(
2196 update_caller_account(
2197 &invoke_context,
2198 true, &mut caller_account,
2200 &mut callee_account,
2201 false, false, false, ),
2205 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2206 );
2207
2208 callee_account.set_data_length(0).unwrap();
2210 callee_account
2211 .set_owner(system_program::id().as_ref())
2212 .unwrap();
2213 update_caller_account(
2214 &invoke_context,
2215 true, &mut caller_account,
2217 &mut callee_account,
2218 false, false, false, )
2222 .unwrap();
2223 let data_len = callee_account.get_data().len();
2224 assert_eq!(data_len, 0);
2225 }
2226
2227 #[case(false, false, false)]
2228 #[case(true, false, false)]
2229 #[case(true, true, false)]
2230 #[case(true, true, true)]
2231 fn test_update_callee_account_lamports_owner(
2232 syscall_parameter_address_restrictions: bool,
2233 virtual_address_space_adjustments: bool,
2234 account_data_direct_mapping: bool,
2235 ) {
2236 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2237 let account = transaction_accounts[1].1.clone();
2238
2239 mock_invoke_context!(
2240 invoke_context,
2241 transaction_context,
2242 b"instruction data",
2243 transaction_accounts,
2244 0,
2245 &[1]
2246 );
2247
2248 let mut mock_caller_account =
2249 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2250 let config = Config {
2251 aligned_memory_mapping: false,
2252 ..Config::default()
2253 };
2254 let memory_mapping = unsafe {
2255 MemoryMapping::new(
2256 mock_caller_account.regions.clone(),
2257 &config,
2258 SBPFVersion::V3,
2259 )
2260 .unwrap()
2261 };
2262 let caller_account = mock_caller_account.caller_account();
2263
2264 borrow_instruction_account!(callee_account, invoke_context, 0);
2265
2266 *caller_account.lamports = 42;
2267 *caller_account.owner = Pubkey::new_unique();
2268
2269 update_callee_account(
2270 &memory_mapping,
2271 true, &caller_account,
2273 callee_account,
2274 syscall_parameter_address_restrictions,
2275 virtual_address_space_adjustments,
2276 account_data_direct_mapping,
2277 )
2278 .unwrap();
2279
2280 borrow_instruction_account!(callee_account, invoke_context, 0);
2281 assert_eq!(callee_account.get_lamports(), 42);
2282 assert_eq!(caller_account.owner, callee_account.get_owner());
2283 }
2284
2285 #[case(false, false, false)]
2286 #[case(true, false, false)]
2287 #[case(true, true, false)]
2288 #[case(true, true, true)]
2289 fn test_update_callee_account_data_writable(
2290 syscall_parameter_address_restrictions: bool,
2291 virtual_address_space_adjustments: bool,
2292 account_data_direct_mapping: bool,
2293 ) {
2294 let transaction_accounts =
2295 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2296 let account = transaction_accounts[1].1.clone();
2297
2298 mock_invoke_context!(
2299 invoke_context,
2300 transaction_context,
2301 b"instruction data",
2302 transaction_accounts,
2303 0,
2304 &[1]
2305 );
2306
2307 let mut mock_caller_account =
2308 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2309 let config = Config {
2310 aligned_memory_mapping: false,
2311 ..Config::default()
2312 };
2313 let memory_mapping = unsafe {
2314 MemoryMapping::new(
2315 mock_caller_account.regions.clone(),
2316 &config,
2317 SBPFVersion::V3,
2318 )
2319 .unwrap()
2320 };
2321 let mut caller_account = mock_caller_account.caller_account();
2322 borrow_instruction_account!(callee_account, invoke_context, 0);
2323
2324 caller_account.serialized_data[0] = b'b';
2326 update_callee_account(
2327 &memory_mapping,
2328 true, &caller_account,
2330 callee_account,
2331 false, false, false, )
2335 .unwrap();
2336 borrow_instruction_account!(callee_account, invoke_context, 0);
2337 assert_eq!(callee_account.get_data(), b"boobar");
2338
2339 let mut data = b"foobarbaz".to_vec();
2341 *caller_account.ref_to_len_in_vm = data.len() as u64;
2342 caller_account.serialized_data = &mut data;
2343 assert_eq!(
2344 update_callee_account(
2345 &memory_mapping,
2346 true, &caller_account,
2348 callee_account,
2349 syscall_parameter_address_restrictions,
2350 virtual_address_space_adjustments,
2351 account_data_direct_mapping,
2352 )
2353 .unwrap(),
2354 virtual_address_space_adjustments,
2355 );
2356
2357 let mut data = b"baz".to_vec();
2359 *caller_account.ref_to_len_in_vm = data.len() as u64;
2360 caller_account.serialized_data = &mut data;
2361 borrow_instruction_account!(callee_account, invoke_context, 0);
2362 assert_eq!(
2363 update_callee_account(
2364 &memory_mapping,
2365 true, &caller_account,
2367 callee_account,
2368 syscall_parameter_address_restrictions,
2369 virtual_address_space_adjustments,
2370 account_data_direct_mapping,
2371 )
2372 .unwrap(),
2373 virtual_address_space_adjustments,
2374 );
2375
2376 let mut data = Vec::new();
2378 caller_account.serialized_data = &mut data;
2379 *caller_account.ref_to_len_in_vm = 0;
2380 let mut owner = system_program::id();
2381 caller_account.owner = &mut owner;
2382 borrow_instruction_account!(callee_account, invoke_context, 0);
2383 update_callee_account(
2384 &memory_mapping,
2385 true, &caller_account,
2387 callee_account,
2388 syscall_parameter_address_restrictions,
2389 virtual_address_space_adjustments,
2390 account_data_direct_mapping,
2391 )
2392 .unwrap();
2393 borrow_instruction_account!(callee_account, invoke_context, 0);
2394 assert_eq!(callee_account.get_data(), b"");
2395 }
2396
2397 #[case(false, false, false)]
2398 #[case(true, false, false)]
2399 #[case(true, true, false)]
2400 #[case(true, true, true)]
2401 fn test_update_callee_account_data_readonly(
2402 syscall_parameter_address_restrictions: bool,
2403 virtual_address_space_adjustments: bool,
2404 account_data_direct_mapping: bool,
2405 ) {
2406 let transaction_accounts =
2407 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2408 let account = transaction_accounts[1].1.clone();
2409
2410 mock_invoke_context!(
2411 invoke_context,
2412 transaction_context,
2413 b"instruction data",
2414 transaction_accounts,
2415 0,
2416 &[1]
2417 );
2418
2419 let mut mock_caller_account =
2420 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2421 let config = Config {
2422 aligned_memory_mapping: false,
2423 ..Config::default()
2424 };
2425 let memory_mapping = unsafe {
2426 MemoryMapping::new(
2427 mock_caller_account.regions.clone(),
2428 &config,
2429 SBPFVersion::V3,
2430 )
2431 .unwrap()
2432 };
2433 let mut caller_account = mock_caller_account.caller_account();
2434 borrow_instruction_account!(callee_account, invoke_context, 0);
2435
2436 caller_account.serialized_data[0] = b'b';
2438 assert_matches!(
2439 update_callee_account(
2440 &memory_mapping,
2441 true, &caller_account,
2443 callee_account,
2444 false, false, false, ),
2448 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2449 );
2450
2451 let mut data = b"foobarbaz".to_vec();
2453 *caller_account.ref_to_len_in_vm = data.len() as u64;
2454 caller_account.serialized_data = &mut data;
2455 borrow_instruction_account!(callee_account, invoke_context, 0);
2456 assert_matches!(
2457 update_callee_account(
2458 &memory_mapping,
2459 true, &caller_account,
2461 callee_account,
2462 syscall_parameter_address_restrictions,
2463 virtual_address_space_adjustments,
2464 account_data_direct_mapping,
2465 ),
2466 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2467 );
2468
2469 let mut data = b"baz".to_vec();
2471 *caller_account.ref_to_len_in_vm = data.len() as u64;
2472 caller_account.serialized_data = &mut data;
2473 borrow_instruction_account!(callee_account, invoke_context, 0);
2474 assert_matches!(
2475 update_callee_account(
2476 &memory_mapping,
2477 true, &caller_account,
2479 callee_account,
2480 syscall_parameter_address_restrictions,
2481 virtual_address_space_adjustments,
2482 account_data_direct_mapping,
2483 ),
2484 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2485 );
2486 }
2487}