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