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