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 let region_start_vm_addr = region.vm_addr_range().start;
1203 debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1204 let mut new_region;
1205 if !account_data_direct_mapping {
1206 new_region = region.clone();
1207 modify_memory_region_of_account(callee_account, &mut new_region);
1208 } else {
1209 new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1210 }
1211 unsafe {
1212 memory_mapping.replace_region(region_index, new_region)?;
1217 }
1218 }
1219
1220 Ok(())
1221}
1222
1223fn update_caller_account(
1236 invoke_context: &InvokeContext,
1237 check_aligned: bool,
1238 caller_account: &mut CallerAccount<'_>,
1239 callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1240 syscall_parameter_address_restrictions: bool,
1241 virtual_address_space_adjustments: bool,
1242 account_data_direct_mapping: bool,
1243) -> Result<(), Error> {
1244 *caller_account.lamports = callee_account.get_lamports();
1245 *caller_account.owner = *callee_account.get_owner();
1246
1247 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1248 let post_len = callee_account.get_data().len();
1249 let is_caller_loader_deprecated = !check_aligned;
1250 let address_space_reserved_for_account =
1251 if syscall_parameter_address_restrictions && is_caller_loader_deprecated {
1252 caller_account.original_data_len
1253 } else {
1254 caller_account
1255 .original_data_len
1256 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1257 };
1258
1259 if post_len > address_space_reserved_for_account
1260 && (syscall_parameter_address_restrictions || prev_len != post_len)
1261 {
1262 let max_increase =
1263 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1264 ic_msg!(
1265 invoke_context,
1266 "Account data size realloc limited to {max_increase} in inner instructions",
1267 );
1268 return Err(Box::new(InstructionError::InvalidRealloc));
1269 }
1270
1271 let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1272 if prev_len != post_len {
1273 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1276 if post_len < prev_len {
1279 caller_account
1280 .serialized_data
1281 .get_mut(post_len..)
1282 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1283 .fill(0);
1284 }
1285 unsafe {
1287 caller_account.serialized_data = CallerAccount::get_serialized_data(
1288 memory_mapping,
1289 check_aligned,
1290 caller_account.vm_data_addr,
1291 caller_account.original_data_len,
1292 post_len,
1293 syscall_parameter_address_restrictions,
1294 virtual_address_space_adjustments,
1295 account_data_direct_mapping,
1296 )?;
1297 }
1298 }
1299 *caller_account.ref_to_len_in_vm = post_len as u64;
1301
1302 let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1304 memory_mapping,
1305 caller_account
1306 .vm_data_addr
1307 .saturating_sub(std::mem::size_of::<u64>() as u64),
1308 check_aligned,
1309 )?;
1310 *serialized_len_ptr = post_len as u64;
1311 }
1312
1313 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1314 let to_slice = &mut caller_account.serialized_data;
1316 let from_slice = callee_account
1317 .get_data()
1318 .get(0..post_len)
1319 .ok_or(CpiError::InvalidLength)?;
1320 if to_slice.len() != from_slice.len() {
1321 return Err(Box::new(InstructionError::AccountDataTooSmall));
1322 }
1323 to_slice.copy_from_slice(from_slice);
1324 }
1325
1326 Ok(())
1327}
1328
1329#[allow(clippy::indexing_slicing)]
1330#[allow(clippy::arithmetic_side_effects)]
1331#[cfg(test)]
1332mod tests {
1333 use {
1334 super::*,
1335 crate::{
1336 invoke_context::BpfAllocator,
1337 memory::translate_type,
1338 memory_context::{MemoryContext, SerializedAccountMetadata},
1339 with_mock_invoke_context_with_feature_set,
1340 },
1341 assert_matches::assert_matches,
1342 solana_account::{Account, AccountSharedData, ReadableAccount},
1343 solana_account_info::AccountInfo,
1344 solana_sbpf::{
1345 ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1346 },
1347 solana_sdk_ids::{bpf_loader, system_program},
1348 solana_svm_feature_set::SVMFeatureSet,
1349 solana_transaction_context::{
1350 IndexOfAccount, instruction_accounts::InstructionAccount,
1351 transaction_accounts::KeyedAccountSharedData,
1352 },
1353 std::{
1354 cell::{Cell, RefCell},
1355 mem, ptr,
1356 rc::Rc,
1357 slice,
1358 },
1359 test_case::case,
1360 };
1361
1362 macro_rules! mock_invoke_context {
1363 ($invoke_context:ident,
1364 $transaction_context:ident,
1365 $instruction_data:expr,
1366 $transaction_accounts:expr,
1367 $program_account:expr,
1368 $instruction_accounts:expr) => {
1369 let instruction_data = $instruction_data;
1370 let instruction_accounts = $instruction_accounts
1371 .iter()
1372 .map(|index_in_transaction| {
1373 InstructionAccount::new(
1374 *index_in_transaction as IndexOfAccount,
1375 false,
1376 $transaction_accounts[*index_in_transaction as usize].2,
1377 )
1378 })
1379 .collect::<Vec<_>>();
1380 let transaction_accounts = $transaction_accounts
1381 .into_iter()
1382 .map(|a| (a.0, a.1))
1383 .collect::<Vec<KeyedAccountSharedData>>();
1384 let mut feature_set = SVMFeatureSet::all_enabled();
1385 feature_set.syscall_parameter_address_restrictions = false;
1386 feature_set.virtual_address_space_adjustments = false;
1387 feature_set.account_data_direct_mapping = false;
1388 let feature_set = &feature_set;
1389 with_mock_invoke_context_with_feature_set!(
1390 $invoke_context,
1391 $transaction_context,
1392 feature_set,
1393 transaction_accounts
1394 );
1395 $invoke_context
1396 .transaction_context
1397 .configure_top_level_instruction_for_tests(
1398 $program_account,
1399 instruction_accounts,
1400 instruction_data.to_vec(),
1401 )
1402 .unwrap();
1403 $invoke_context.push().unwrap();
1404 };
1405 }
1406
1407 macro_rules! borrow_instruction_account {
1408 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1409 let instruction_context = $invoke_context
1410 .transaction_context
1411 .get_current_instruction_context()
1412 .unwrap();
1413 let $borrowed_account = instruction_context
1414 .try_borrow_instruction_account($index)
1415 .unwrap();
1416 };
1417 }
1418
1419 fn is_zeroed(data: &[u8]) -> bool {
1420 data.iter().all(|b| *b == 0)
1421 }
1422
1423 struct MockCallerAccount {
1424 lamports: u64,
1425 owner: Pubkey,
1426 vm_addr: u64,
1427 data: Vec<u8>,
1428 len: u64,
1429 regions: Vec<MemoryRegion>,
1430 virtual_address_space_adjustments: bool,
1431 }
1432
1433 impl MockCallerAccount {
1434 fn new(
1435 lamports: u64,
1436 owner: Pubkey,
1437 data: &[u8],
1438 virtual_address_space_adjustments: bool,
1439 ) -> MockCallerAccount {
1440 let vm_addr = MM_INPUT_START;
1441 let mut region_addr = vm_addr;
1442 let region_len = mem::size_of::<u64>()
1443 + if virtual_address_space_adjustments {
1444 0
1445 } else {
1446 data.len() + MAX_PERMITTED_DATA_INCREASE
1447 };
1448 let mut d = vec![0; region_len];
1449 let mut regions = vec![];
1450
1451 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1453
1454 if !virtual_address_space_adjustments {
1456 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1457 }
1458
1459 regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1461 region_addr += region_len as u64;
1462
1463 if virtual_address_space_adjustments {
1464 regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1466 region_addr += data.len() as u64;
1467
1468 regions.push(MemoryRegion::new(
1470 &raw mut d[mem::size_of::<u64>()..],
1471 region_addr,
1472 ));
1473 } else {
1474 d.truncate(mem::size_of::<u64>() + data.len());
1476 }
1477
1478 MockCallerAccount {
1479 lamports,
1480 owner,
1481 vm_addr,
1482 data: d,
1483 len: data.len() as u64,
1484 regions,
1485 virtual_address_space_adjustments,
1486 }
1487 }
1488
1489 fn data_slice<'a>(&self) -> &'a [u8] {
1490 unsafe {
1492 slice::from_raw_parts(
1493 self.data[mem::size_of::<u64>()..].as_ptr(),
1494 self.data.capacity() - mem::size_of::<u64>(),
1495 )
1496 }
1497 }
1498
1499 fn caller_account(&mut self) -> CallerAccount<'_> {
1500 let data = if self.virtual_address_space_adjustments {
1501 &mut []
1502 } else {
1503 &mut self.data[mem::size_of::<u64>()..]
1504 };
1505 CallerAccount {
1506 lamports: &mut self.lamports,
1507 owner: &mut self.owner,
1508 original_data_len: self.len as usize,
1509 serialized_data: data,
1510 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1511 ref_to_len_in_vm: &mut self.len,
1512 }
1513 }
1514 }
1515
1516 struct MockAccountInfo<'a> {
1517 key: Pubkey,
1518 is_signer: bool,
1519 is_writable: bool,
1520 lamports: u64,
1521 data: &'a [u8],
1522 owner: Pubkey,
1523 executable: bool,
1524 _unused: u64,
1525 }
1526
1527 impl MockAccountInfo<'_> {
1528 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1529 MockAccountInfo {
1530 key,
1531 is_signer: false,
1532 is_writable: false,
1533 lamports: account.lamports(),
1534 data: account.data(),
1535 owner: *account.owner(),
1536 executable: account.executable(),
1537 _unused: account.rent_epoch(),
1538 }
1539 }
1540
1541 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1542 let size = mem::size_of::<AccountInfo>()
1543 + mem::size_of::<Pubkey>() * 2
1544 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1545 + mem::size_of::<u64>()
1546 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1547 + self.data.len();
1548 let mut data = vec![0; size];
1549
1550 let vm_addr = vm_addr as usize;
1551 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1552 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1553 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1554 let owner_addr = lamports_addr + mem::size_of::<u64>();
1555 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1556 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1557
1558 #[allow(deprecated)]
1559 #[allow(clippy::used_underscore_binding)]
1560 let info = AccountInfo {
1561 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1562 is_signer: self.is_signer,
1563 is_writable: self.is_writable,
1564 lamports: unsafe {
1565 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1566 },
1567 data: unsafe {
1568 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1569 },
1570 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1571 executable: self.executable,
1572 _unused: self._unused,
1573 };
1574
1575 unsafe {
1576 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1577 ptr::write_unaligned(
1578 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1579 self.key,
1580 );
1581 ptr::write_unaligned(
1582 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1583 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1584 );
1585 ptr::write_unaligned(
1586 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1587 self.lamports,
1588 );
1589 ptr::write_unaligned(
1590 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1591 self.owner,
1592 );
1593 ptr::write_unaligned(
1594 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1595 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1596 data_addr as *mut u8,
1597 self.data.len(),
1598 ))),
1599 );
1600 data[data_addr - vm_addr..].copy_from_slice(self.data);
1601 }
1602
1603 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1604 (
1605 data,
1606 region,
1607 SerializedAccountMetadata {
1608 vm_addr: vm_addr as u64,
1609 original_data_len: self.data.len(),
1610 vm_key_addr: key_addr as u64,
1611 vm_lamports_addr: lamports_addr as u64,
1612 vm_owner_addr: owner_addr as u64,
1613 vm_data_addr: data_addr as u64,
1614 },
1615 )
1616 }
1617 }
1618
1619 struct MockInstruction {
1620 program_id: Pubkey,
1621 accounts: Vec<AccountMeta>,
1622 data: Vec<u8>,
1623 }
1624
1625 impl MockInstruction {
1626 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1627 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1628
1629 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1630
1631 let mut data = vec![0; size];
1632
1633 let vm_addr = vm_addr as usize;
1634 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1635 let data_addr = accounts_addr + accounts_len;
1636
1637 let ins = Instruction {
1638 program_id: self.program_id,
1639 accounts: unsafe {
1640 Vec::from_raw_parts(
1641 accounts_addr as *mut _,
1642 self.accounts.len(),
1643 self.accounts.len(),
1644 )
1645 },
1646 data: unsafe {
1647 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1648 },
1649 };
1650 let ins = StableInstruction::from(ins);
1651
1652 unsafe {
1653 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1654 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1655 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1656 );
1657 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1658 }
1659
1660 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1661 (data, region)
1662 }
1663 }
1664
1665 #[repr(C)]
1666 struct RcBox<T> {
1667 strong: Cell<usize>,
1668 weak: Cell<usize>,
1669 value: T,
1670 }
1671
1672 impl<T> RcBox<T> {
1673 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1674 fn new(value: T) -> RcBox<T> {
1675 RcBox {
1676 strong: Cell::new(0),
1677 weak: Cell::new(0),
1678 value,
1679 }
1680 }
1681 }
1682
1683 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1684
1685 fn transaction_with_one_writable_instruction_account(
1686 data: Vec<u8>,
1687 ) -> Vec<TestTransactionAccount> {
1688 let program_id = Pubkey::new_unique();
1689 let account = AccountSharedData::from(Account {
1690 lamports: 1,
1691 data,
1692 owner: program_id,
1693 executable: false,
1694 rent_epoch: 100,
1695 });
1696 vec![
1697 (
1698 program_id,
1699 AccountSharedData::from(Account {
1700 lamports: 0,
1701 data: vec![],
1702 owner: bpf_loader::id(),
1703 executable: true,
1704 rent_epoch: 0,
1705 }),
1706 false,
1707 ),
1708 (Pubkey::new_unique(), account, true),
1709 ]
1710 }
1711
1712 fn transaction_with_one_readonly_instruction_account(
1713 data: Vec<u8>,
1714 ) -> Vec<TestTransactionAccount> {
1715 let program_id = Pubkey::new_unique();
1716 let account_owner = Pubkey::new_unique();
1717 let account = AccountSharedData::from(Account {
1718 lamports: 1,
1719 data,
1720 owner: account_owner,
1721 executable: false,
1722 rent_epoch: 100,
1723 });
1724 vec![
1725 (
1726 program_id,
1727 AccountSharedData::from(Account {
1728 lamports: 0,
1729 data: vec![],
1730 owner: bpf_loader::id(),
1731 executable: true,
1732 rent_epoch: 0,
1733 }),
1734 false,
1735 ),
1736 (Pubkey::new_unique(), account, true),
1737 ]
1738 }
1739
1740 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1741 let vm_addr = vm_addr as usize;
1742
1743 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
1746 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1747
1748 let total_size = fat_ptr_size_of_slice
1750 + singers_length * fat_ptr_size_of_slice
1751 + sum_signers_data_length;
1752 let mut data = vec![0; total_size];
1753
1754 data[..fat_ptr_size_of_slice / 2]
1766 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1767 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1768 .clone_from_slice(&(singers_length).to_le_bytes());
1769
1770 let (mut p1, mut p2) = (
1772 fat_ptr_size_of_slice,
1773 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1774 );
1775 for signer in signers.iter() {
1776 let signer_length = signer.len();
1777
1778 data[p1..p1 + fat_ptr_size_of_slice / 2]
1780 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1781 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1782 .clone_from_slice(&(signer_length).to_le_bytes());
1783 p1 += fat_ptr_size_of_slice;
1784
1785 data[p2..p2 + signer_length].clone_from_slice(signer);
1787 p2 += signer_length;
1788 }
1789
1790 let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1791 (data, region)
1792 }
1793
1794 #[test]
1795 fn test_translate_instruction() {
1796 let transaction_accounts =
1797 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1798 mock_invoke_context!(
1799 invoke_context,
1800 transaction_context,
1801 b"instruction data",
1802 transaction_accounts,
1803 0,
1804 &[1]
1805 );
1806
1807 let program_id = Pubkey::new_unique();
1808 let accounts = vec![AccountMeta {
1809 pubkey: Pubkey::new_unique(),
1810 is_signer: true,
1811 is_writable: false,
1812 }];
1813 let data = b"ins data".to_vec();
1814 let vm_addr = MM_INPUT_START;
1815 let (_mem, region) = MockInstruction {
1816 program_id,
1817 accounts: accounts.clone(),
1818 data: data.clone(),
1819 }
1820 .into_region(vm_addr);
1821
1822 let config = Config {
1823 aligned_memory_mapping: false,
1824 ..Config::default()
1825 };
1826 let memory_mapping =
1827 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1828 invoke_context
1829 .memory_contexts
1830 .mock_set_mapping_abi_v1(memory_mapping);
1831
1832 let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1833 assert_eq!(ins.program_id, program_id);
1834 assert_eq!(ins.accounts, accounts);
1835 assert_eq!(ins.data, data);
1836 }
1837
1838 #[test]
1839 fn test_translate_signers() {
1840 let transaction_accounts =
1841 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1842 mock_invoke_context!(
1843 invoke_context,
1844 transaction_context,
1845 b"instruction data",
1846 transaction_accounts,
1847 0,
1848 &[1]
1849 );
1850
1851 let program_id = Pubkey::new_unique();
1852 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1853
1854 let vm_addr = MM_INPUT_START;
1855 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1856
1857 let config = Config {
1858 aligned_memory_mapping: false,
1859 ..Config::default()
1860 };
1861 let mapping =
1862 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1863 invoke_context
1864 .memory_contexts
1865 .set_memory_context_abi_v1(MemoryContext::new(
1866 BpfAllocator::new(0),
1867 Vec::new(),
1868 mapping,
1869 ))
1870 .unwrap();
1871
1872 let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1873 assert_eq!(signers[0], derived_key);
1874 }
1875
1876 #[test]
1877 fn test_translate_accounts_rust() {
1878 let transaction_accounts =
1879 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1880 let account = transaction_accounts[1].1.clone();
1881 let key = transaction_accounts[1].0;
1882 let original_data_len = account.data().len();
1883
1884 let vm_addr = MM_INPUT_START;
1885 let (_mem, region, account_metadata) =
1886 MockAccountInfo::new(key, &account).into_region(vm_addr);
1887
1888 let config = Config {
1889 aligned_memory_mapping: false,
1890 ..Config::default()
1891 };
1892 let memory_mapping =
1893 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1894
1895 mock_invoke_context!(
1896 invoke_context,
1897 transaction_context,
1898 b"instruction data",
1899 transaction_accounts,
1900 0,
1901 &[1, 1]
1902 );
1903
1904 invoke_context
1905 .memory_contexts
1906 .set_memory_context_abi_v1(MemoryContext::new(
1907 BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1908 vec![account_metadata],
1909 memory_mapping,
1910 ))
1911 .unwrap();
1912
1913 invoke_context
1914 .transaction_context
1915 .configure_next_cpi_for_tests(
1916 0,
1917 vec![
1918 InstructionAccount::new(1, false, true),
1919 InstructionAccount::new(1, false, true),
1920 ],
1921 vec![],
1922 )
1923 .unwrap();
1924
1925 let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1926 assert_eq!(accounts.len(), 1);
1927 let caller_account = &accounts[0].caller_account;
1928 assert_eq!(caller_account.serialized_data, account.data());
1929 assert_eq!(caller_account.original_data_len, original_data_len);
1930 }
1931
1932 #[test]
1933 fn test_get_serialized_data() {
1934 let transaction_accounts =
1935 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1936 let account = transaction_accounts[1].1.clone();
1937 mock_invoke_context!(
1938 invoke_context,
1939 transaction_context,
1940 b"instruction data",
1941 transaction_accounts,
1942 0,
1943 &[1]
1944 );
1945
1946 let config = Config {
1947 aligned_memory_mapping: false,
1948 ..Config::default()
1949 };
1950 let memory_mapping =
1951 unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1952 let serialized_data = unsafe {
1953 CallerAccount::get_serialized_data(
1954 &memory_mapping,
1955 true, MM_INPUT_START,
1957 account.data().len(),
1958 account
1959 .data()
1960 .len()
1961 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1962 .saturating_add(1),
1963 true, true, false, )
1967 };
1968
1969 assert_matches!(
1970 serialized_data,
1971 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1972 );
1973 }
1974
1975 #[test]
1976 fn test_caller_account_from_account_info() {
1977 let transaction_accounts =
1978 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1979 let account = transaction_accounts[1].1.clone();
1980 mock_invoke_context!(
1981 invoke_context,
1982 transaction_context,
1983 b"instruction data",
1984 transaction_accounts,
1985 0,
1986 &[1]
1987 );
1988
1989 let key = Pubkey::new_unique();
1990 let vm_addr = MM_INPUT_START;
1991 let (_mem, region, account_metadata) =
1992 MockAccountInfo::new(key, &account).into_region(vm_addr);
1993
1994 let config = Config {
1995 aligned_memory_mapping: false,
1996 ..Config::default()
1997 };
1998 let memory_mapping =
1999 unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
2000
2001 let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
2002
2003 invoke_context
2004 .memory_contexts
2005 .mock_set_mapping_abi_v1(memory_mapping);
2006 let check_aligned = invoke_context.get_check_aligned();
2007 let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
2008 let caller_account = CallerAccount::from_account_info(
2009 &invoke_context,
2010 memory_mapping,
2011 check_aligned,
2012 vm_addr,
2013 account_info,
2014 &account_metadata,
2015 )
2016 .unwrap();
2017 assert_eq!(*caller_account.lamports, account.lamports());
2018 assert_eq!(caller_account.owner, account.owner());
2019 assert_eq!(caller_account.original_data_len, account.data().len());
2020 assert_eq!(
2021 *caller_account.ref_to_len_in_vm as usize,
2022 account.data().len()
2023 );
2024 assert_eq!(caller_account.serialized_data, account.data());
2025 }
2026
2027 #[case(false, false, false)]
2028 #[case(true, false, false)]
2029 #[case(true, true, false)]
2030 #[case(true, true, true)]
2031 fn test_update_caller_account_lamports_owner(
2032 syscall_parameter_address_restrictions: bool,
2033 virtual_address_space_adjustments: bool,
2034 account_data_direct_mapping: bool,
2035 ) {
2036 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2037 let account = transaction_accounts[1].1.clone();
2038 mock_invoke_context!(
2039 invoke_context,
2040 transaction_context,
2041 b"instruction data",
2042 transaction_accounts,
2043 0,
2044 &[1]
2045 );
2046
2047 let mut mock_caller_account =
2048 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2049
2050 let config = Config {
2051 aligned_memory_mapping: false,
2052 ..Config::default()
2053 };
2054 let memory_mapping = unsafe {
2055 MemoryMapping::new(
2056 mock_caller_account.regions.split_off(0),
2057 &config,
2058 SBPFVersion::V3,
2059 )
2060 .unwrap()
2061 };
2062 invoke_context
2063 .memory_contexts
2064 .mock_set_mapping_abi_v1(memory_mapping);
2065
2066 let mut caller_account = mock_caller_account.caller_account();
2067 let instruction_context = invoke_context
2068 .transaction_context
2069 .get_current_instruction_context()
2070 .unwrap();
2071 let mut callee_account = instruction_context
2072 .try_borrow_instruction_account(0)
2073 .unwrap();
2074 callee_account.set_lamports(42).unwrap();
2075 callee_account
2076 .set_owner(Pubkey::new_unique().as_ref())
2077 .unwrap();
2078
2079 update_caller_account(
2080 &invoke_context,
2081 true, &mut caller_account,
2083 &mut callee_account,
2084 syscall_parameter_address_restrictions,
2085 virtual_address_space_adjustments,
2086 account_data_direct_mapping,
2087 )
2088 .unwrap();
2089
2090 assert_eq!(*caller_account.lamports, 42);
2091 assert_eq!(caller_account.owner, callee_account.get_owner());
2092 }
2093
2094 #[test]
2095 fn test_update_caller_account_data() {
2096 let transaction_accounts =
2097 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2098 let account = transaction_accounts[1].1.clone();
2099 let original_data_len = account.data().len();
2100
2101 mock_invoke_context!(
2102 invoke_context,
2103 transaction_context,
2104 b"instruction data",
2105 transaction_accounts,
2106 0,
2107 &[1]
2108 );
2109
2110 let mut mock_caller_account =
2111 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2112
2113 let config = Config {
2114 aligned_memory_mapping: false,
2115 ..Config::default()
2116 };
2117 let memory_mapping = unsafe {
2118 MemoryMapping::new(
2119 mock_caller_account.regions.clone(),
2120 &config,
2121 SBPFVersion::V3,
2122 )
2123 .unwrap()
2124 };
2125 invoke_context
2126 .memory_contexts
2127 .mock_set_mapping_abi_v1(memory_mapping);
2128
2129 let data_slice = mock_caller_account.data_slice();
2130 let len_ptr = unsafe {
2131 data_slice
2132 .as_ptr()
2133 .offset(-(mem::size_of::<u64>() as isize))
2134 };
2135 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2136 let mut caller_account = mock_caller_account.caller_account();
2137 let instruction_context = invoke_context
2138 .transaction_context
2139 .get_current_instruction_context()
2140 .unwrap();
2141 let mut callee_account = instruction_context
2142 .try_borrow_instruction_account(0)
2143 .unwrap();
2144
2145 for (new_value, expected_realloc_size) in [
2146 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2147 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2148 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2149 ] {
2150 assert_eq!(caller_account.serialized_data, callee_account.get_data());
2151 callee_account.set_data_from_slice(&new_value).unwrap();
2152
2153 update_caller_account(
2154 &invoke_context,
2155 true, &mut caller_account,
2157 &mut callee_account,
2158 false, false, false, )
2162 .unwrap();
2163
2164 let data_len = callee_account.get_data().len();
2165 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2166 assert_eq!(data_len, serialized_len());
2167 assert_eq!(data_len, caller_account.serialized_data.len());
2168 assert_eq!(
2169 callee_account.get_data(),
2170 &caller_account.serialized_data[..data_len]
2171 );
2172 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2173 assert!(is_zeroed(&data_slice[data_len..]));
2174 }
2175
2176 callee_account
2177 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2178 .unwrap();
2179 update_caller_account(
2180 &invoke_context,
2181 true, &mut caller_account,
2183 &mut callee_account,
2184 false, false, false, )
2188 .unwrap();
2189 let data_len = callee_account.get_data().len();
2190 assert_eq!(data_slice[data_len..].len(), 0);
2191 assert!(is_zeroed(&data_slice[data_len..]));
2192
2193 callee_account
2194 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2195 .unwrap();
2196 assert_matches!(
2197 update_caller_account(
2198 &invoke_context,
2199 true, &mut caller_account,
2201 &mut callee_account,
2202 false, false, false, ),
2206 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2207 );
2208
2209 callee_account.set_data_length(0).unwrap();
2211 callee_account
2212 .set_owner(system_program::id().as_ref())
2213 .unwrap();
2214 update_caller_account(
2215 &invoke_context,
2216 true, &mut caller_account,
2218 &mut callee_account,
2219 false, false, false, )
2223 .unwrap();
2224 let data_len = callee_account.get_data().len();
2225 assert_eq!(data_len, 0);
2226 }
2227
2228 #[case(false, false, false)]
2229 #[case(true, false, false)]
2230 #[case(true, true, false)]
2231 #[case(true, true, true)]
2232 fn test_update_callee_account_lamports_owner(
2233 syscall_parameter_address_restrictions: bool,
2234 virtual_address_space_adjustments: bool,
2235 account_data_direct_mapping: bool,
2236 ) {
2237 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2238 let account = transaction_accounts[1].1.clone();
2239
2240 mock_invoke_context!(
2241 invoke_context,
2242 transaction_context,
2243 b"instruction data",
2244 transaction_accounts,
2245 0,
2246 &[1]
2247 );
2248
2249 let mut mock_caller_account =
2250 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2251 let config = Config {
2252 aligned_memory_mapping: false,
2253 ..Config::default()
2254 };
2255 let memory_mapping = unsafe {
2256 MemoryMapping::new(
2257 mock_caller_account.regions.clone(),
2258 &config,
2259 SBPFVersion::V3,
2260 )
2261 .unwrap()
2262 };
2263 let caller_account = mock_caller_account.caller_account();
2264
2265 borrow_instruction_account!(callee_account, invoke_context, 0);
2266
2267 *caller_account.lamports = 42;
2268 *caller_account.owner = Pubkey::new_unique();
2269
2270 update_callee_account(
2271 &memory_mapping,
2272 true, &caller_account,
2274 callee_account,
2275 syscall_parameter_address_restrictions,
2276 virtual_address_space_adjustments,
2277 account_data_direct_mapping,
2278 )
2279 .unwrap();
2280
2281 borrow_instruction_account!(callee_account, invoke_context, 0);
2282 assert_eq!(callee_account.get_lamports(), 42);
2283 assert_eq!(caller_account.owner, callee_account.get_owner());
2284 }
2285
2286 #[case(false, false, false)]
2287 #[case(true, false, false)]
2288 #[case(true, true, false)]
2289 #[case(true, true, true)]
2290 fn test_update_callee_account_data_writable(
2291 syscall_parameter_address_restrictions: bool,
2292 virtual_address_space_adjustments: bool,
2293 account_data_direct_mapping: bool,
2294 ) {
2295 let transaction_accounts =
2296 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2297 let account = transaction_accounts[1].1.clone();
2298
2299 mock_invoke_context!(
2300 invoke_context,
2301 transaction_context,
2302 b"instruction data",
2303 transaction_accounts,
2304 0,
2305 &[1]
2306 );
2307
2308 let mut mock_caller_account =
2309 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2310 let config = Config {
2311 aligned_memory_mapping: false,
2312 ..Config::default()
2313 };
2314 let memory_mapping = unsafe {
2315 MemoryMapping::new(
2316 mock_caller_account.regions.clone(),
2317 &config,
2318 SBPFVersion::V3,
2319 )
2320 .unwrap()
2321 };
2322 let mut caller_account = mock_caller_account.caller_account();
2323 borrow_instruction_account!(callee_account, invoke_context, 0);
2324
2325 caller_account.serialized_data[0] = b'b';
2327 update_callee_account(
2328 &memory_mapping,
2329 true, &caller_account,
2331 callee_account,
2332 false, false, false, )
2336 .unwrap();
2337 borrow_instruction_account!(callee_account, invoke_context, 0);
2338 assert_eq!(callee_account.get_data(), b"boobar");
2339
2340 let mut data = b"foobarbaz".to_vec();
2342 *caller_account.ref_to_len_in_vm = data.len() as u64;
2343 caller_account.serialized_data = &mut data;
2344 assert_eq!(
2345 update_callee_account(
2346 &memory_mapping,
2347 true, &caller_account,
2349 callee_account,
2350 syscall_parameter_address_restrictions,
2351 virtual_address_space_adjustments,
2352 account_data_direct_mapping,
2353 )
2354 .unwrap(),
2355 virtual_address_space_adjustments,
2356 );
2357
2358 let mut data = b"baz".to_vec();
2360 *caller_account.ref_to_len_in_vm = data.len() as u64;
2361 caller_account.serialized_data = &mut data;
2362 borrow_instruction_account!(callee_account, invoke_context, 0);
2363 assert_eq!(
2364 update_callee_account(
2365 &memory_mapping,
2366 true, &caller_account,
2368 callee_account,
2369 syscall_parameter_address_restrictions,
2370 virtual_address_space_adjustments,
2371 account_data_direct_mapping,
2372 )
2373 .unwrap(),
2374 virtual_address_space_adjustments,
2375 );
2376
2377 let mut data = Vec::new();
2379 caller_account.serialized_data = &mut data;
2380 *caller_account.ref_to_len_in_vm = 0;
2381 let mut owner = system_program::id();
2382 caller_account.owner = &mut owner;
2383 borrow_instruction_account!(callee_account, invoke_context, 0);
2384 update_callee_account(
2385 &memory_mapping,
2386 true, &caller_account,
2388 callee_account,
2389 syscall_parameter_address_restrictions,
2390 virtual_address_space_adjustments,
2391 account_data_direct_mapping,
2392 )
2393 .unwrap();
2394 borrow_instruction_account!(callee_account, invoke_context, 0);
2395 assert_eq!(callee_account.get_data(), b"");
2396 }
2397
2398 #[case(false, false, false)]
2399 #[case(true, false, false)]
2400 #[case(true, true, false)]
2401 #[case(true, true, true)]
2402 fn test_update_callee_account_data_readonly(
2403 syscall_parameter_address_restrictions: bool,
2404 virtual_address_space_adjustments: bool,
2405 account_data_direct_mapping: bool,
2406 ) {
2407 let transaction_accounts =
2408 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2409 let account = transaction_accounts[1].1.clone();
2410
2411 mock_invoke_context!(
2412 invoke_context,
2413 transaction_context,
2414 b"instruction data",
2415 transaction_accounts,
2416 0,
2417 &[1]
2418 );
2419
2420 let mut mock_caller_account =
2421 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2422 let config = Config {
2423 aligned_memory_mapping: false,
2424 ..Config::default()
2425 };
2426 let memory_mapping = unsafe {
2427 MemoryMapping::new(
2428 mock_caller_account.regions.clone(),
2429 &config,
2430 SBPFVersion::V3,
2431 )
2432 .unwrap()
2433 };
2434 let mut caller_account = mock_caller_account.caller_account();
2435 borrow_instruction_account!(callee_account, invoke_context, 0);
2436
2437 caller_account.serialized_data[0] = b'b';
2439 assert_matches!(
2440 update_callee_account(
2441 &memory_mapping,
2442 true, &caller_account,
2444 callee_account,
2445 false, false, false, ),
2449 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2450 );
2451
2452 let mut data = b"foobarbaz".to_vec();
2454 *caller_account.ref_to_len_in_vm = data.len() as u64;
2455 caller_account.serialized_data = &mut data;
2456 borrow_instruction_account!(callee_account, invoke_context, 0);
2457 assert_matches!(
2458 update_callee_account(
2459 &memory_mapping,
2460 true, &caller_account,
2462 callee_account,
2463 syscall_parameter_address_restrictions,
2464 virtual_address_space_adjustments,
2465 account_data_direct_mapping,
2466 ),
2467 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2468 );
2469
2470 let mut data = b"baz".to_vec();
2472 *caller_account.ref_to_len_in_vm = data.len() as u64;
2473 caller_account.serialized_data = &mut data;
2474 borrow_instruction_account!(callee_account, invoke_context, 0);
2475 assert_matches!(
2476 update_callee_account(
2477 &memory_mapping,
2478 true, &caller_account,
2480 callee_account,
2481 syscall_parameter_address_restrictions,
2482 virtual_address_space_adjustments,
2483 account_data_direct_mapping,
2484 ),
2485 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2486 );
2487 }
2488}