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