1use {
2 super::*,
3 crate::serialization::account_data_region_memory_state,
4 clone_agave_feature_set::enable_bpf_loader_set_authority_checked_ix,
5 clone_solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
6 clone_solana_measure::measure::Measure,
7 clone_solana_program_runtime::invoke_context::SerializedAccountMetadata,
8 clone_solana_stable_layout::stable_instruction::StableInstruction,
9 clone_solana_transaction_context::BorrowedAccount,
10 scopeguard::defer,
11 solana_sbpf::{
12 ebpf,
13 memory_region::{MemoryRegion, MemoryState},
14 },
15 std::{mem, ptr},
16};
17const MAX_CPI_INSTRUCTION_DATA_LEN: u64 = 10 * 1024;
19#[cfg(test)]
20static_assertions::const_assert_eq!(
21 MAX_CPI_INSTRUCTION_DATA_LEN,
22 clone_solana_program::syscalls::MAX_CPI_INSTRUCTION_DATA_LEN
23);
24const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX;
25#[cfg(test)]
26static_assertions::const_assert_eq!(
27 MAX_CPI_INSTRUCTION_ACCOUNTS,
28 clone_solana_program::syscalls::MAX_CPI_INSTRUCTION_ACCOUNTS
29);
30const MAX_CPI_ACCOUNT_INFOS: usize = 128;
31#[cfg(test)]
32static_assertions::const_assert_eq!(
33 MAX_CPI_ACCOUNT_INFOS,
34 clone_solana_program::syscalls::MAX_CPI_ACCOUNT_INFOS
35);
36
37fn check_account_info_pointer(
38 invoke_context: &InvokeContext,
39 vm_addr: u64,
40 expected_vm_addr: u64,
41 field: &str,
42) -> Result<(), Error> {
43 if vm_addr != expected_vm_addr {
44 ic_msg!(
45 invoke_context,
46 "Invalid account info pointer `{}': {:#x} != {:#x}",
47 field,
48 vm_addr,
49 expected_vm_addr
50 );
51 return Err(SyscallError::InvalidPointer.into());
52 }
53 Ok(())
54}
55
56enum VmValue<'a, 'b, T> {
57 VmAddress {
58 vm_addr: u64,
59 memory_mapping: &'b MemoryMapping<'a>,
60 check_aligned: bool,
61 },
62 Translated(&'a mut T),
65}
66
67impl<T> VmValue<'_, '_, T> {
68 fn get(&self) -> Result<&T, Error> {
69 match self {
70 VmValue::VmAddress {
71 vm_addr,
72 memory_mapping,
73 check_aligned,
74 } => translate_type(memory_mapping, *vm_addr, *check_aligned),
75 VmValue::Translated(addr) => Ok(*addr),
76 }
77 }
78
79 fn get_mut(&mut self) -> Result<&mut T, Error> {
80 match self {
81 VmValue::VmAddress {
82 vm_addr,
83 memory_mapping,
84 check_aligned,
85 } => translate_type_mut(memory_mapping, *vm_addr, *check_aligned),
86 VmValue::Translated(addr) => Ok(*addr),
87 }
88 }
89}
90
91struct CallerAccount<'a, 'b> {
96 lamports: &'a mut u64,
97 owner: &'a mut Pubkey,
98 original_data_len: usize,
103 serialized_data: &'a mut [u8],
110 vm_data_addr: u64,
113 ref_to_len_in_vm: VmValue<'b, 'a, u64>,
114}
115
116impl<'a, 'b> CallerAccount<'a, 'b> {
117 fn from_account_info(
119 invoke_context: &InvokeContext,
120 memory_mapping: &'b MemoryMapping<'a>,
121 _vm_addr: u64,
122 account_info: &AccountInfo,
123 account_metadata: &SerializedAccountMetadata,
124 ) -> Result<CallerAccount<'a, 'b>, Error> {
125 let direct_mapping = invoke_context
126 .get_feature_set()
127 .is_active(&feature_set::bpf_account_data_direct_mapping::id());
128
129 if direct_mapping {
130 check_account_info_pointer(
131 invoke_context,
132 account_info.key as *const _ as u64,
133 account_metadata.vm_key_addr,
134 "key",
135 )?;
136 check_account_info_pointer(
137 invoke_context,
138 account_info.owner as *const _ as u64,
139 account_metadata.vm_owner_addr,
140 "owner",
141 )?;
142 }
143
144 let lamports = {
147 let ptr = translate_type::<u64>(
149 memory_mapping,
150 account_info.lamports.as_ptr() as u64,
151 invoke_context.get_check_aligned(),
152 )?;
153 if direct_mapping {
154 if account_info.lamports.as_ptr() as u64 >= ebpf::MM_INPUT_START {
155 return Err(SyscallError::InvalidPointer.into());
156 }
157
158 check_account_info_pointer(
159 invoke_context,
160 *ptr,
161 account_metadata.vm_lamports_addr,
162 "lamports",
163 )?;
164 }
165 translate_type_mut::<u64>(memory_mapping, *ptr, invoke_context.get_check_aligned())?
166 };
167
168 let owner = translate_type_mut::<Pubkey>(
169 memory_mapping,
170 account_info.owner as *const _ as u64,
171 invoke_context.get_check_aligned(),
172 )?;
173
174 let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
175 if direct_mapping && account_info.data.as_ptr() as u64 >= ebpf::MM_INPUT_START {
176 return Err(SyscallError::InvalidPointer.into());
177 }
178
179 let data = *translate_type::<&[u8]>(
181 memory_mapping,
182 account_info.data.as_ptr() as *const _ as u64,
183 invoke_context.get_check_aligned(),
184 )?;
185 if direct_mapping {
186 check_account_info_pointer(
187 invoke_context,
188 data.as_ptr() as u64,
189 account_metadata.vm_data_addr,
190 "data",
191 )?;
192 }
193
194 consume_compute_meter(
195 invoke_context,
196 (data.len() as u64)
197 .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
198 .unwrap_or(u64::MAX),
199 )?;
200
201 let ref_to_len_in_vm = if direct_mapping {
202 let vm_addr = (account_info.data.as_ptr() as *const u64 as u64)
203 .saturating_add(size_of::<u64>() as u64);
204 if vm_addr >= ebpf::MM_INPUT_START {
208 return Err(SyscallError::InvalidPointer.into());
209 }
210 VmValue::VmAddress {
211 vm_addr,
212 memory_mapping,
213 check_aligned: invoke_context.get_check_aligned(),
214 }
215 } else {
216 let translated = translate(
217 memory_mapping,
218 AccessType::Store,
219 (account_info.data.as_ptr() as *const u64 as u64)
220 .saturating_add(size_of::<u64>() as u64),
221 8,
222 )? as *mut u64;
223 VmValue::Translated(unsafe { &mut *translated })
224 };
225 let vm_data_addr = data.as_ptr() as u64;
226
227 let serialized_data = if direct_mapping {
228 &mut []
241 } else {
242 translate_slice_mut::<u8>(
243 memory_mapping,
244 vm_data_addr,
245 data.len() as u64,
246 invoke_context.get_check_aligned(),
247 )?
248 };
249 (serialized_data, vm_data_addr, ref_to_len_in_vm)
250 };
251
252 Ok(CallerAccount {
253 lamports,
254 owner,
255 original_data_len: account_metadata.original_data_len,
256 serialized_data,
257 vm_data_addr,
258 ref_to_len_in_vm,
259 })
260 }
261
262 fn from_sol_account_info(
264 invoke_context: &InvokeContext,
265 memory_mapping: &'b MemoryMapping<'a>,
266 vm_addr: u64,
267 account_info: &SolAccountInfo,
268 account_metadata: &SerializedAccountMetadata,
269 ) -> Result<CallerAccount<'a, 'b>, Error> {
270 let direct_mapping = invoke_context
271 .get_feature_set()
272 .is_active(&feature_set::bpf_account_data_direct_mapping::id());
273
274 if direct_mapping {
275 check_account_info_pointer(
276 invoke_context,
277 account_info.key_addr,
278 account_metadata.vm_key_addr,
279 "key",
280 )?;
281
282 check_account_info_pointer(
283 invoke_context,
284 account_info.owner_addr,
285 account_metadata.vm_owner_addr,
286 "owner",
287 )?;
288
289 check_account_info_pointer(
290 invoke_context,
291 account_info.lamports_addr,
292 account_metadata.vm_lamports_addr,
293 "lamports",
294 )?;
295
296 check_account_info_pointer(
297 invoke_context,
298 account_info.data_addr,
299 account_metadata.vm_data_addr,
300 "data",
301 )?;
302 }
303
304 let lamports = translate_type_mut::<u64>(
307 memory_mapping,
308 account_info.lamports_addr,
309 invoke_context.get_check_aligned(),
310 )?;
311 let owner = translate_type_mut::<Pubkey>(
312 memory_mapping,
313 account_info.owner_addr,
314 invoke_context.get_check_aligned(),
315 )?;
316
317 consume_compute_meter(
318 invoke_context,
319 account_info
320 .data_len
321 .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
322 .unwrap_or(u64::MAX),
323 )?;
324
325 let serialized_data = if direct_mapping {
326 &mut []
328 } else {
329 translate_slice_mut::<u8>(
330 memory_mapping,
331 account_info.data_addr,
332 account_info.data_len,
333 invoke_context.get_check_aligned(),
334 )?
335 };
336
337 let data_len_vm_addr = vm_addr
342 .saturating_add(&account_info.data_len as *const u64 as u64)
343 .saturating_sub(account_info as *const _ as *const u64 as u64);
344
345 let ref_to_len_in_vm = if direct_mapping {
346 VmValue::VmAddress {
347 vm_addr: data_len_vm_addr,
348 memory_mapping,
349 check_aligned: invoke_context.get_check_aligned(),
350 }
351 } else {
352 let data_len_addr = translate(
353 memory_mapping,
354 AccessType::Store,
355 data_len_vm_addr,
356 size_of::<u64>() as u64,
357 )?;
358 VmValue::Translated(unsafe { &mut *(data_len_addr as *mut u64) })
359 };
360
361 Ok(CallerAccount {
362 lamports,
363 owner,
364 original_data_len: account_metadata.original_data_len,
365 serialized_data,
366 vm_data_addr: account_info.data_addr,
367 ref_to_len_in_vm,
368 })
369 }
370
371 fn realloc_region(
372 &self,
373 memory_mapping: &'b MemoryMapping<'_>,
374 is_loader_deprecated: bool,
375 ) -> Result<Option<&'a MemoryRegion>, Error> {
376 account_realloc_region(
377 memory_mapping,
378 self.vm_data_addr,
379 self.original_data_len,
380 is_loader_deprecated,
381 )
382 }
383}
384
385type TranslatedAccounts<'a, 'b> = Vec<(IndexOfAccount, Option<CallerAccount<'a, 'b>>)>;
386
387trait SyscallInvokeSigned {
389 fn translate_instruction(
390 addr: u64,
391 memory_mapping: &MemoryMapping,
392 invoke_context: &mut InvokeContext,
393 ) -> Result<StableInstruction, Error>;
394 fn translate_accounts<'a, 'b>(
395 instruction_accounts: &[InstructionAccount],
396 program_indices: &[IndexOfAccount],
397 account_infos_addr: u64,
398 account_infos_len: u64,
399 is_loader_deprecated: bool,
400 memory_mapping: &'b MemoryMapping<'a>,
401 invoke_context: &mut InvokeContext,
402 ) -> Result<TranslatedAccounts<'a, 'b>, Error>;
403 fn translate_signers(
404 program_id: &Pubkey,
405 signers_seeds_addr: u64,
406 signers_seeds_len: u64,
407 memory_mapping: &MemoryMapping,
408 invoke_context: &InvokeContext,
409 ) -> Result<Vec<Pubkey>, Error>;
410}
411
412declare_builtin_function!(
413 SyscallInvokeSignedRust,
415 fn rust(
416 invoke_context: &mut InvokeContext,
417 instruction_addr: u64,
418 account_infos_addr: u64,
419 account_infos_len: u64,
420 signers_seeds_addr: u64,
421 signers_seeds_len: u64,
422 memory_mapping: &mut MemoryMapping,
423 ) -> Result<u64, Error> {
424 cpi_common::<Self>(
425 invoke_context,
426 instruction_addr,
427 account_infos_addr,
428 account_infos_len,
429 signers_seeds_addr,
430 signers_seeds_len,
431 memory_mapping,
432 )
433 }
434);
435
436impl SyscallInvokeSigned for SyscallInvokeSignedRust {
437 fn translate_instruction(
438 addr: u64,
439 memory_mapping: &MemoryMapping,
440 invoke_context: &mut InvokeContext,
441 ) -> Result<StableInstruction, Error> {
442 let ix = translate_type::<StableInstruction>(
443 memory_mapping,
444 addr,
445 invoke_context.get_check_aligned(),
446 )?;
447 let account_metas = translate_slice::<AccountMeta>(
448 memory_mapping,
449 ix.accounts.as_vaddr(),
450 ix.accounts.len(),
451 invoke_context.get_check_aligned(),
452 )?;
453 let data = translate_slice::<u8>(
454 memory_mapping,
455 ix.data.as_vaddr(),
456 ix.data.len(),
457 invoke_context.get_check_aligned(),
458 )?
459 .to_vec();
460
461 check_instruction_size(account_metas.len(), data.len(), invoke_context)?;
462
463 if invoke_context
464 .get_feature_set()
465 .is_active(&feature_set::loosen_cpi_size_restriction::id())
466 {
467 consume_compute_meter(
468 invoke_context,
469 (data.len() as u64)
470 .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
471 .unwrap_or(u64::MAX),
472 )?;
473 }
474
475 let mut accounts = Vec::with_capacity(account_metas.len());
476 #[allow(clippy::needless_range_loop)]
477 for account_index in 0..account_metas.len() {
478 #[allow(clippy::indexing_slicing)]
479 let account_meta = &account_metas[account_index];
480 if unsafe {
481 std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
482 || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
483 > 1
484 } {
485 return Err(Box::new(InstructionError::InvalidArgument));
486 }
487 accounts.push(account_meta.clone());
488 }
489
490 Ok(StableInstruction {
491 accounts: accounts.into(),
492 data: data.into(),
493 program_id: ix.program_id,
494 })
495 }
496
497 fn translate_accounts<'a, 'b>(
498 instruction_accounts: &[InstructionAccount],
499 program_indices: &[IndexOfAccount],
500 account_infos_addr: u64,
501 account_infos_len: u64,
502 is_loader_deprecated: bool,
503 memory_mapping: &'b MemoryMapping<'a>,
504 invoke_context: &mut InvokeContext,
505 ) -> Result<TranslatedAccounts<'a, 'b>, Error> {
506 let (account_infos, account_info_keys) = translate_account_infos(
507 account_infos_addr,
508 account_infos_len,
509 |account_info: &AccountInfo| account_info.key as *const _ as u64,
510 memory_mapping,
511 invoke_context,
512 )?;
513
514 translate_and_update_accounts(
515 instruction_accounts,
516 program_indices,
517 &account_info_keys,
518 account_infos,
519 account_infos_addr,
520 is_loader_deprecated,
521 invoke_context,
522 memory_mapping,
523 CallerAccount::from_account_info,
524 )
525 }
526
527 fn translate_signers(
528 program_id: &Pubkey,
529 signers_seeds_addr: u64,
530 signers_seeds_len: u64,
531 memory_mapping: &MemoryMapping,
532 invoke_context: &InvokeContext,
533 ) -> Result<Vec<Pubkey>, Error> {
534 let mut signers = Vec::new();
535 if signers_seeds_len > 0 {
536 let signers_seeds = translate_slice_of_slices::<VmSlice<u8>>(
537 memory_mapping,
538 signers_seeds_addr,
539 signers_seeds_len,
540 invoke_context.get_check_aligned(),
541 )?;
542 if signers_seeds.len() > MAX_SIGNERS {
543 return Err(Box::new(SyscallError::TooManySigners));
544 }
545 for signer_seeds in signers_seeds.iter() {
546 let untranslated_seeds = translate_slice_of_slices::<u8>(
547 memory_mapping,
548 signer_seeds.ptr(),
549 signer_seeds.len(),
550 invoke_context.get_check_aligned(),
551 )?;
552 if untranslated_seeds.len() > MAX_SEEDS {
553 return Err(Box::new(InstructionError::MaxSeedLengthExceeded));
554 }
555 let seeds = untranslated_seeds
556 .iter()
557 .map(|untranslated_seed| {
558 untranslated_seed
559 .translate(memory_mapping, invoke_context.get_check_aligned())
560 })
561 .collect::<Result<Vec<_>, Error>>()?;
562 let signer = Pubkey::create_program_address(&seeds, program_id)
563 .map_err(SyscallError::BadSeeds)?;
564 signers.push(signer);
565 }
566 Ok(signers)
567 } else {
568 Ok(vec![])
569 }
570 }
571}
572
573#[derive(Debug)]
575#[repr(C)]
576struct SolInstruction {
577 program_id_addr: u64,
578 accounts_addr: u64,
579 accounts_len: u64,
580 data_addr: u64,
581 data_len: u64,
582}
583
584#[derive(Debug)]
586#[repr(C)]
587struct SolAccountMeta {
588 pubkey_addr: u64,
589 is_writable: bool,
590 is_signer: bool,
591}
592
593#[derive(Debug)]
595#[repr(C)]
596struct SolAccountInfo {
597 key_addr: u64,
598 lamports_addr: u64,
599 data_len: u64,
600 data_addr: u64,
601 owner_addr: u64,
602 rent_epoch: u64,
603 is_signer: bool,
604 is_writable: bool,
605 executable: bool,
606}
607
608#[derive(Debug)]
610#[repr(C)]
611struct SolSignerSeedC {
612 addr: u64,
613 len: u64,
614}
615
616#[derive(Debug)]
618#[repr(C)]
619struct SolSignerSeedsC {
620 addr: u64,
621 len: u64,
622}
623
624declare_builtin_function!(
625 SyscallInvokeSignedC,
627 fn rust(
628 invoke_context: &mut InvokeContext,
629 instruction_addr: u64,
630 account_infos_addr: u64,
631 account_infos_len: u64,
632 signers_seeds_addr: u64,
633 signers_seeds_len: u64,
634 memory_mapping: &mut MemoryMapping,
635 ) -> Result<u64, Error> {
636 cpi_common::<Self>(
637 invoke_context,
638 instruction_addr,
639 account_infos_addr,
640 account_infos_len,
641 signers_seeds_addr,
642 signers_seeds_len,
643 memory_mapping,
644 )
645 }
646);
647
648impl SyscallInvokeSigned for SyscallInvokeSignedC {
649 fn translate_instruction(
650 addr: u64,
651 memory_mapping: &MemoryMapping,
652 invoke_context: &mut InvokeContext,
653 ) -> Result<StableInstruction, Error> {
654 let ix_c = translate_type::<SolInstruction>(
655 memory_mapping,
656 addr,
657 invoke_context.get_check_aligned(),
658 )?;
659
660 let program_id = translate_type::<Pubkey>(
661 memory_mapping,
662 ix_c.program_id_addr,
663 invoke_context.get_check_aligned(),
664 )?;
665 let account_metas = translate_slice::<SolAccountMeta>(
666 memory_mapping,
667 ix_c.accounts_addr,
668 ix_c.accounts_len,
669 invoke_context.get_check_aligned(),
670 )?;
671 let data = translate_slice::<u8>(
672 memory_mapping,
673 ix_c.data_addr,
674 ix_c.data_len,
675 invoke_context.get_check_aligned(),
676 )?
677 .to_vec();
678
679 check_instruction_size(ix_c.accounts_len as usize, data.len(), invoke_context)?;
680
681 if invoke_context
682 .get_feature_set()
683 .is_active(&feature_set::loosen_cpi_size_restriction::id())
684 {
685 consume_compute_meter(
686 invoke_context,
687 (data.len() as u64)
688 .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
689 .unwrap_or(u64::MAX),
690 )?;
691 }
692
693 let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
694 #[allow(clippy::needless_range_loop)]
695 for account_index in 0..ix_c.accounts_len as usize {
696 #[allow(clippy::indexing_slicing)]
697 let account_meta = &account_metas[account_index];
698 if unsafe {
699 std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
700 || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
701 > 1
702 } {
703 return Err(Box::new(InstructionError::InvalidArgument));
704 }
705 let pubkey = translate_type::<Pubkey>(
706 memory_mapping,
707 account_meta.pubkey_addr,
708 invoke_context.get_check_aligned(),
709 )?;
710 accounts.push(AccountMeta {
711 pubkey: *pubkey,
712 is_signer: account_meta.is_signer,
713 is_writable: account_meta.is_writable,
714 });
715 }
716
717 Ok(StableInstruction {
718 accounts: accounts.into(),
719 data: data.into(),
720 program_id: *program_id,
721 })
722 }
723
724 fn translate_accounts<'a, 'b>(
725 instruction_accounts: &[InstructionAccount],
726 program_indices: &[IndexOfAccount],
727 account_infos_addr: u64,
728 account_infos_len: u64,
729 is_loader_deprecated: bool,
730 memory_mapping: &'b MemoryMapping<'a>,
731 invoke_context: &mut InvokeContext,
732 ) -> Result<TranslatedAccounts<'a, 'b>, Error> {
733 let (account_infos, account_info_keys) = translate_account_infos(
734 account_infos_addr,
735 account_infos_len,
736 |account_info: &SolAccountInfo| account_info.key_addr,
737 memory_mapping,
738 invoke_context,
739 )?;
740
741 translate_and_update_accounts(
742 instruction_accounts,
743 program_indices,
744 &account_info_keys,
745 account_infos,
746 account_infos_addr,
747 is_loader_deprecated,
748 invoke_context,
749 memory_mapping,
750 CallerAccount::from_sol_account_info,
751 )
752 }
753
754 fn translate_signers(
755 program_id: &Pubkey,
756 signers_seeds_addr: u64,
757 signers_seeds_len: u64,
758 memory_mapping: &MemoryMapping,
759 invoke_context: &InvokeContext,
760 ) -> Result<Vec<Pubkey>, Error> {
761 if signers_seeds_len > 0 {
762 let signers_seeds = translate_slice::<SolSignerSeedsC>(
763 memory_mapping,
764 signers_seeds_addr,
765 signers_seeds_len,
766 invoke_context.get_check_aligned(),
767 )?;
768 if signers_seeds.len() > MAX_SIGNERS {
769 return Err(Box::new(SyscallError::TooManySigners));
770 }
771 Ok(signers_seeds
772 .iter()
773 .map(|signer_seeds| {
774 let seeds = translate_slice::<SolSignerSeedC>(
775 memory_mapping,
776 signer_seeds.addr,
777 signer_seeds.len,
778 invoke_context.get_check_aligned(),
779 )?;
780 if seeds.len() > MAX_SEEDS {
781 return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
782 }
783 let seeds_bytes = seeds
784 .iter()
785 .map(|seed| {
786 translate_slice::<u8>(
787 memory_mapping,
788 seed.addr,
789 seed.len,
790 invoke_context.get_check_aligned(),
791 )
792 })
793 .collect::<Result<Vec<_>, Error>>()?;
794 Pubkey::create_program_address(&seeds_bytes, program_id)
795 .map_err(|err| Box::new(SyscallError::BadSeeds(err)) as Error)
796 })
797 .collect::<Result<Vec<_>, Error>>()?)
798 } else {
799 Ok(vec![])
800 }
801 }
802}
803
804fn translate_account_infos<'a, T, F>(
805 account_infos_addr: u64,
806 account_infos_len: u64,
807 key_addr: F,
808 memory_mapping: &MemoryMapping,
809 invoke_context: &mut InvokeContext,
810) -> Result<(&'a [T], Vec<&'a Pubkey>), Error>
811where
812 F: Fn(&T) -> u64,
813{
814 let direct_mapping = invoke_context
815 .get_feature_set()
816 .is_active(&feature_set::bpf_account_data_direct_mapping::id());
817
818 if direct_mapping
822 && account_infos_addr
823 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
824 >= ebpf::MM_INPUT_START
825 {
826 return Err(SyscallError::InvalidPointer.into());
827 }
828
829 let account_infos = translate_slice::<T>(
830 memory_mapping,
831 account_infos_addr,
832 account_infos_len,
833 invoke_context.get_check_aligned(),
834 )?;
835 check_account_infos(account_infos.len(), invoke_context)?;
836 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
837 #[allow(clippy::needless_range_loop)]
838 for account_index in 0..account_infos_len as usize {
839 #[allow(clippy::indexing_slicing)]
840 let account_info = &account_infos[account_index];
841 account_info_keys.push(translate_type::<Pubkey>(
842 memory_mapping,
843 key_addr(account_info),
844 invoke_context.get_check_aligned(),
845 )?);
846 }
847 Ok((account_infos, account_info_keys))
848}
849
850fn translate_and_update_accounts<'a, 'b, T, F>(
853 instruction_accounts: &[InstructionAccount],
854 program_indices: &[IndexOfAccount],
855 account_info_keys: &[&Pubkey],
856 account_infos: &[T],
857 account_infos_addr: u64,
858 is_loader_deprecated: bool,
859 invoke_context: &mut InvokeContext,
860 memory_mapping: &'b MemoryMapping<'a>,
861 do_translate: F,
862) -> Result<TranslatedAccounts<'a, 'b>, Error>
863where
864 F: Fn(
865 &InvokeContext,
866 &'b MemoryMapping<'a>,
867 u64,
868 &T,
869 &SerializedAccountMetadata,
870 ) -> Result<CallerAccount<'a, 'b>, Error>,
871{
872 let transaction_context = &invoke_context.transaction_context;
873 let instruction_context = transaction_context.get_current_instruction_context()?;
874 let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
875
876 let program_account_index = program_indices
877 .last()
878 .ok_or_else(|| Box::new(InstructionError::MissingAccount))?;
879 accounts.push((*program_account_index, None));
880
881 let accounts_metadata = &invoke_context
884 .get_syscall_context()
885 .unwrap()
886 .accounts_metadata;
887
888 let direct_mapping = invoke_context
889 .get_feature_set()
890 .is_active(&feature_set::bpf_account_data_direct_mapping::id());
891
892 for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate()
893 {
894 if instruction_account_index as IndexOfAccount != instruction_account.index_in_callee {
895 continue; }
897
898 let callee_account = instruction_context.try_borrow_instruction_account(
899 transaction_context,
900 instruction_account.index_in_caller,
901 )?;
902 let account_key = invoke_context
903 .transaction_context
904 .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
905
906 #[allow(deprecated)]
907 if callee_account.is_executable() {
908 consume_compute_meter(
910 invoke_context,
911 (callee_account.get_data().len() as u64)
912 .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
913 .unwrap_or(u64::MAX),
914 )?;
915
916 accounts.push((instruction_account.index_in_caller, None));
917 } else if let Some(caller_account_index) =
918 account_info_keys.iter().position(|key| *key == account_key)
919 {
920 let serialized_metadata = accounts_metadata
921 .get(instruction_account.index_in_caller as usize)
922 .ok_or_else(|| {
923 ic_msg!(
924 invoke_context,
925 "Internal error: index mismatch for account {}",
926 account_key
927 );
928 Box::new(InstructionError::MissingAccount)
929 })?;
930
931 if caller_account_index >= account_infos.len() {
933 return Err(Box::new(SyscallError::InvalidLength));
934 }
935 #[allow(clippy::indexing_slicing)]
936 let caller_account =
937 do_translate(
938 invoke_context,
939 memory_mapping,
940 account_infos_addr.saturating_add(
941 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
942 ),
943 &account_infos[caller_account_index],
944 serialized_metadata,
945 )?;
946
947 let update_caller = update_callee_account(
952 invoke_context,
953 memory_mapping,
954 is_loader_deprecated,
955 &caller_account,
956 callee_account,
957 direct_mapping,
958 )?;
959
960 let caller_account = if instruction_account.is_writable || update_caller {
961 Some(caller_account)
962 } else {
963 None
964 };
965 accounts.push((instruction_account.index_in_caller, caller_account));
966 } else {
967 ic_msg!(
968 invoke_context,
969 "Instruction references an unknown account {}",
970 account_key
971 );
972 return Err(Box::new(InstructionError::MissingAccount));
973 }
974 }
975
976 Ok(accounts)
977}
978
979fn check_instruction_size(
980 num_accounts: usize,
981 data_len: usize,
982 invoke_context: &mut InvokeContext,
983) -> Result<(), Error> {
984 if invoke_context
985 .get_feature_set()
986 .is_active(&feature_set::loosen_cpi_size_restriction::id())
987 {
988 let data_len = data_len as u64;
989 let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN;
990 if data_len > max_data_len {
991 return Err(Box::new(SyscallError::MaxInstructionDataLenExceeded {
992 data_len,
993 max_data_len,
994 }));
995 }
996
997 let num_accounts = num_accounts as u64;
998 let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64;
999 if num_accounts > max_accounts {
1000 return Err(Box::new(SyscallError::MaxInstructionAccountsExceeded {
1001 num_accounts,
1002 max_accounts,
1003 }));
1004 }
1005 } else {
1006 let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size;
1007 let size = num_accounts
1008 .saturating_mul(size_of::<AccountMeta>())
1009 .saturating_add(data_len);
1010 if size > max_size {
1011 return Err(Box::new(SyscallError::InstructionTooLarge(size, max_size)));
1012 }
1013 }
1014 Ok(())
1015}
1016
1017fn check_account_infos(
1018 num_account_infos: usize,
1019 invoke_context: &mut InvokeContext,
1020) -> Result<(), Error> {
1021 if invoke_context
1022 .get_feature_set()
1023 .is_active(&feature_set::loosen_cpi_size_restriction::id())
1024 {
1025 let max_cpi_account_infos = if invoke_context
1026 .get_feature_set()
1027 .is_active(&feature_set::increase_tx_account_lock_limit::id())
1028 {
1029 MAX_CPI_ACCOUNT_INFOS
1030 } else {
1031 64
1032 };
1033 let num_account_infos = num_account_infos as u64;
1034 let max_account_infos = max_cpi_account_infos as u64;
1035 if num_account_infos > max_account_infos {
1036 return Err(Box::new(SyscallError::MaxInstructionAccountInfosExceeded {
1037 num_account_infos,
1038 max_account_infos,
1039 }));
1040 }
1041 } else {
1042 let adjusted_len = num_account_infos.saturating_mul(size_of::<Pubkey>());
1043
1044 if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size {
1045 return Err(Box::new(SyscallError::TooManyAccounts));
1048 };
1049 }
1050 Ok(())
1051}
1052
1053fn check_authorized_program(
1054 program_id: &Pubkey,
1055 instruction_data: &[u8],
1056 invoke_context: &InvokeContext,
1057) -> Result<(), Error> {
1058 if native_loader::check_id(program_id)
1059 || bpf_loader::check_id(program_id)
1060 || bpf_loader_deprecated::check_id(program_id)
1061 || (clone_solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
1062 && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
1063 || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
1064 || (invoke_context
1065 .get_feature_set()
1066 .is_active(&enable_bpf_loader_set_authority_checked_ix::id())
1067 && bpf_loader_upgradeable::is_set_authority_checked_instruction(
1068 instruction_data,
1069 ))
1070 || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
1071 || is_precompile(program_id, |feature_id: &Pubkey| {
1072 invoke_context.get_feature_set().is_active(feature_id)
1073 })
1074 {
1075 return Err(Box::new(SyscallError::ProgramNotSupported(*program_id)));
1076 }
1077 Ok(())
1078}
1079
1080fn cpi_common<S: SyscallInvokeSigned>(
1082 invoke_context: &mut InvokeContext,
1083 instruction_addr: u64,
1084 account_infos_addr: u64,
1085 account_infos_len: u64,
1086 signers_seeds_addr: u64,
1087 signers_seeds_len: u64,
1088 memory_mapping: &MemoryMapping,
1089) -> Result<u64, Error> {
1090 consume_compute_meter(
1095 invoke_context,
1096 invoke_context.get_compute_budget().invoke_units,
1097 )?;
1098 if let Some(execute_time) = invoke_context.execute_time.as_mut() {
1099 execute_time.stop();
1100 invoke_context.timings.execute_us += execute_time.as_us();
1101 }
1102
1103 let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?;
1104 let transaction_context = &invoke_context.transaction_context;
1105 let instruction_context = transaction_context.get_current_instruction_context()?;
1106 let caller_program_id = instruction_context.get_last_program_key(transaction_context)?;
1107 let signers = S::translate_signers(
1108 caller_program_id,
1109 signers_seeds_addr,
1110 signers_seeds_len,
1111 memory_mapping,
1112 invoke_context,
1113 )?;
1114 let is_loader_deprecated = *instruction_context
1115 .try_borrow_last_program_account(transaction_context)?
1116 .get_owner()
1117 == bpf_loader_deprecated::id();
1118 let (instruction_accounts, program_indices) =
1119 invoke_context.prepare_instruction(&instruction, &signers)?;
1120 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
1121
1122 let mut accounts = S::translate_accounts(
1123 &instruction_accounts,
1124 &program_indices,
1125 account_infos_addr,
1126 account_infos_len,
1127 is_loader_deprecated,
1128 memory_mapping,
1129 invoke_context,
1130 )?;
1131
1132 let mut compute_units_consumed = 0;
1134 invoke_context.process_instruction(
1135 &instruction.data,
1136 &instruction_accounts,
1137 &program_indices,
1138 &mut compute_units_consumed,
1139 &mut ExecuteTimings::default(),
1140 )?;
1141
1142 let transaction_context = &invoke_context.transaction_context;
1144 let instruction_context = transaction_context.get_current_instruction_context()?;
1145
1146 let direct_mapping = invoke_context
1150 .get_feature_set()
1151 .is_active(&feature_set::bpf_account_data_direct_mapping::id());
1152
1153 if direct_mapping {
1154 for (index_in_caller, caller_account) in accounts.iter() {
1159 if let Some(caller_account) = caller_account {
1160 let callee_account = instruction_context
1161 .try_borrow_instruction_account(transaction_context, *index_in_caller)?;
1162 update_caller_account_perms(
1163 memory_mapping,
1164 caller_account,
1165 &callee_account,
1166 is_loader_deprecated,
1167 )?;
1168 }
1169 }
1170 }
1171
1172 for (index_in_caller, caller_account) in accounts.iter_mut() {
1173 if let Some(caller_account) = caller_account {
1174 let mut callee_account = instruction_context
1175 .try_borrow_instruction_account(transaction_context, *index_in_caller)?;
1176 update_caller_account(
1177 invoke_context,
1178 memory_mapping,
1179 is_loader_deprecated,
1180 caller_account,
1181 &mut callee_account,
1182 direct_mapping,
1183 )?;
1184 }
1185 }
1186
1187 invoke_context.execute_time = Some(Measure::start("execute"));
1188 Ok(SUCCESS)
1189}
1190
1191fn update_callee_account(
1203 invoke_context: &InvokeContext,
1204 memory_mapping: &MemoryMapping,
1205 is_loader_deprecated: bool,
1206 caller_account: &CallerAccount,
1207 mut callee_account: BorrowedAccount<'_>,
1208 direct_mapping: bool,
1209) -> Result<bool, Error> {
1210 let mut must_update_caller = false;
1211
1212 if callee_account.get_lamports() != *caller_account.lamports {
1213 callee_account.set_lamports(*caller_account.lamports)?;
1214 }
1215
1216 if direct_mapping {
1217 let prev_len = callee_account.get_data().len();
1218 let post_len = *caller_account.ref_to_len_in_vm.get()? as usize;
1219 match callee_account
1220 .can_data_be_resized(post_len)
1221 .and_then(|_| callee_account.can_data_be_changed())
1222 {
1223 Ok(()) => {
1224 let realloc_bytes_used = post_len.saturating_sub(caller_account.original_data_len);
1225 if is_loader_deprecated && realloc_bytes_used > 0 {
1227 return Err(InstructionError::InvalidRealloc.into());
1228 }
1229 if prev_len != post_len {
1230 callee_account.set_data_length(post_len)?;
1231 must_update_caller = true;
1233 }
1234 if realloc_bytes_used > 0 {
1235 let serialized_data = translate_slice::<u8>(
1236 memory_mapping,
1237 caller_account
1238 .vm_data_addr
1239 .saturating_add(caller_account.original_data_len as u64),
1240 realloc_bytes_used as u64,
1241 invoke_context.get_check_aligned(),
1242 )?;
1243 callee_account
1244 .get_data_mut()?
1245 .get_mut(caller_account.original_data_len..post_len)
1246 .ok_or(SyscallError::InvalidLength)?
1247 .copy_from_slice(serialized_data);
1248 }
1249 }
1250 Err(err) if prev_len != post_len => {
1251 return Err(Box::new(err));
1252 }
1253 _ => {}
1254 }
1255 } else {
1256 match callee_account
1258 .can_data_be_resized(caller_account.serialized_data.len())
1259 .and_then(|_| callee_account.can_data_be_changed())
1260 {
1261 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1262 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1263 return Err(Box::new(err));
1264 }
1265 _ => {}
1266 }
1267 }
1268
1269 if callee_account.get_owner() != caller_account.owner {
1271 callee_account.set_owner(caller_account.owner.as_ref())?;
1272 }
1273
1274 Ok(must_update_caller)
1275}
1276
1277fn update_caller_account_perms(
1278 memory_mapping: &MemoryMapping,
1279 caller_account: &CallerAccount,
1280 callee_account: &BorrowedAccount<'_>,
1281 is_loader_deprecated: bool,
1282) -> Result<(), Error> {
1283 let CallerAccount {
1284 original_data_len,
1285 vm_data_addr,
1286 ..
1287 } = caller_account;
1288
1289 let data_region = account_data_region(memory_mapping, *vm_data_addr, *original_data_len)?;
1290 if let Some(region) = data_region {
1291 region
1292 .state
1293 .set(account_data_region_memory_state(callee_account));
1294 }
1295 let realloc_region = account_realloc_region(
1296 memory_mapping,
1297 *vm_data_addr,
1298 *original_data_len,
1299 is_loader_deprecated,
1300 )?;
1301 if let Some(region) = realloc_region {
1302 region
1303 .state
1304 .set(if callee_account.can_data_be_changed().is_ok() {
1305 MemoryState::Writable
1306 } else {
1307 MemoryState::Readable
1308 });
1309 }
1310
1311 Ok(())
1312}
1313
1314fn update_caller_account(
1323 invoke_context: &InvokeContext,
1324 memory_mapping: &MemoryMapping,
1325 is_loader_deprecated: bool,
1326 caller_account: &mut CallerAccount,
1327 callee_account: &mut BorrowedAccount<'_>,
1328 direct_mapping: bool,
1329) -> Result<(), Error> {
1330 *caller_account.lamports = callee_account.get_lamports();
1331 *caller_account.owner = *callee_account.get_owner();
1332
1333 let mut zero_all_mapped_spare_capacity = false;
1334 if direct_mapping {
1335 if let Some(region) = account_data_region(
1336 memory_mapping,
1337 caller_account.vm_data_addr,
1338 caller_account.original_data_len,
1339 )? {
1340 let min_capacity = caller_account.original_data_len;
1350 if callee_account.capacity() < min_capacity {
1351 callee_account
1352 .reserve(min_capacity.saturating_sub(callee_account.get_data().len()))?;
1353 zero_all_mapped_spare_capacity = true;
1354 }
1355
1356 let callee_ptr = callee_account.get_data().as_ptr() as u64;
1364 if region.host_addr.get() != callee_ptr {
1365 region.host_addr.set(callee_ptr);
1366 zero_all_mapped_spare_capacity = true;
1367 }
1368 }
1369 }
1370
1371 let prev_len = *caller_account.ref_to_len_in_vm.get()? as usize;
1372 let post_len = callee_account.get_data().len();
1373 if prev_len != post_len {
1374 let max_increase = if direct_mapping && !invoke_context.get_check_aligned() {
1375 0
1376 } else {
1377 MAX_PERMITTED_DATA_INCREASE
1378 };
1379 let data_overflow = post_len
1380 > caller_account
1381 .original_data_len
1382 .saturating_add(max_increase);
1383 if data_overflow {
1384 ic_msg!(
1385 invoke_context,
1386 "Account data size realloc limited to {max_increase} in inner instructions",
1387 );
1388 return Err(Box::new(InstructionError::InvalidRealloc));
1389 }
1390
1391 if post_len < prev_len {
1394 if direct_mapping {
1395 if prev_len > caller_account.original_data_len {
1417 debug_assert!(!is_loader_deprecated);
1421
1422 let realloc_region = caller_account
1425 .realloc_region(memory_mapping, is_loader_deprecated)?
1426 .unwrap(); let original_state = realloc_region.state.replace(MemoryState::Writable);
1428 defer! {
1429 realloc_region.state.set(original_state);
1430 };
1431
1432 let dirty_realloc_start = caller_account.original_data_len.max(post_len);
1435 let dirty_realloc_len = prev_len.saturating_sub(dirty_realloc_start);
1437 let serialized_data = translate_slice_mut::<u8>(
1438 memory_mapping,
1439 caller_account
1440 .vm_data_addr
1441 .saturating_add(dirty_realloc_start as u64),
1442 dirty_realloc_len as u64,
1443 invoke_context.get_check_aligned(),
1444 )?;
1445 serialized_data.fill(0);
1446 }
1447 } else {
1448 caller_account
1449 .serialized_data
1450 .get_mut(post_len..)
1451 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1452 .fill(0);
1453 }
1454 }
1455
1456 if !direct_mapping {
1459 caller_account.serialized_data = translate_slice_mut::<u8>(
1460 memory_mapping,
1461 caller_account.vm_data_addr,
1462 post_len as u64,
1463 false, )?;
1465 }
1466 *caller_account.ref_to_len_in_vm.get_mut()? = post_len as u64;
1468
1469 let serialized_len_ptr = translate_type_mut::<u64>(
1471 memory_mapping,
1472 caller_account
1473 .vm_data_addr
1474 .saturating_sub(std::mem::size_of::<u64>() as u64),
1475 invoke_context.get_check_aligned(),
1476 )?;
1477 *serialized_len_ptr = post_len as u64;
1478 }
1479
1480 if direct_mapping {
1481 let spare_len = if zero_all_mapped_spare_capacity {
1490 caller_account.original_data_len
1498 } else {
1499 prev_len
1504 }
1505 .saturating_sub(post_len);
1506
1507 if spare_len > 0 {
1508 let dst = callee_account
1509 .spare_data_capacity_mut()?
1510 .get_mut(..spare_len)
1511 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1512 .as_mut_ptr();
1513 unsafe { ptr::write_bytes(dst, 0, spare_len) };
1515 }
1516
1517 let realloc_bytes_used = post_len.saturating_sub(caller_account.original_data_len);
1519 if realloc_bytes_used > 0 {
1520 debug_assert!(!is_loader_deprecated);
1523
1524 let to_slice = {
1525 let realloc_region = caller_account
1534 .realloc_region(memory_mapping, is_loader_deprecated)?
1535 .unwrap(); let original_state = realloc_region.state.replace(MemoryState::Writable);
1537 defer! {
1538 realloc_region.state.set(original_state);
1539 };
1540
1541 translate_slice_mut::<u8>(
1542 memory_mapping,
1543 caller_account
1544 .vm_data_addr
1545 .saturating_add(caller_account.original_data_len as u64),
1546 realloc_bytes_used as u64,
1547 invoke_context.get_check_aligned(),
1548 )?
1549 };
1550 let from_slice = callee_account
1551 .get_data()
1552 .get(caller_account.original_data_len..post_len)
1553 .ok_or(SyscallError::InvalidLength)?;
1554 if to_slice.len() != from_slice.len() {
1555 return Err(Box::new(InstructionError::AccountDataTooSmall));
1556 }
1557 to_slice.copy_from_slice(from_slice);
1558 }
1559 } else {
1560 let to_slice = &mut caller_account.serialized_data;
1561 let from_slice = callee_account
1562 .get_data()
1563 .get(0..post_len)
1564 .ok_or(SyscallError::InvalidLength)?;
1565 if to_slice.len() != from_slice.len() {
1566 return Err(Box::new(InstructionError::AccountDataTooSmall));
1567 }
1568 to_slice.copy_from_slice(from_slice);
1569 }
1570
1571 Ok(())
1572}
1573
1574fn account_data_region<'a>(
1575 memory_mapping: &'a MemoryMapping<'_>,
1576 vm_data_addr: u64,
1577 original_data_len: usize,
1578) -> Result<Option<&'a MemoryRegion>, Error> {
1579 if original_data_len == 0 {
1580 return Ok(None);
1581 }
1582
1583 let data_region = memory_mapping.region(AccessType::Load, vm_data_addr)?;
1586 debug_assert_eq!(data_region.vm_addr, vm_data_addr);
1588 Ok(Some(data_region))
1589}
1590
1591fn account_realloc_region<'a>(
1592 memory_mapping: &'a MemoryMapping<'_>,
1593 vm_data_addr: u64,
1594 original_data_len: usize,
1595 is_loader_deprecated: bool,
1596) -> Result<Option<&'a MemoryRegion>, Error> {
1597 if is_loader_deprecated {
1598 return Ok(None);
1599 }
1600
1601 let realloc_vm_addr = vm_data_addr.saturating_add(original_data_len as u64);
1602 let realloc_region = memory_mapping.region(AccessType::Load, realloc_vm_addr)?;
1603 debug_assert_eq!(realloc_region.vm_addr, realloc_vm_addr);
1604 debug_assert!((MAX_PERMITTED_DATA_INCREASE
1605 ..MAX_PERMITTED_DATA_INCREASE.saturating_add(BPF_ALIGN_OF_U128))
1606 .contains(&(realloc_region.len as usize)));
1607 debug_assert!(!matches!(realloc_region.state.get(), MemoryState::Cow(_)));
1608 Ok(Some(realloc_region))
1609}
1610
1611#[allow(clippy::indexing_slicing)]
1612#[allow(clippy::arithmetic_side_effects)]
1613#[cfg(test)]
1614mod tests {
1615 use {
1616 super::*,
1617 crate::mock_create_vm,
1618 assert_matches::assert_matches,
1619 clone_agave_feature_set::bpf_account_data_direct_mapping,
1620 clone_solana_account::{Account, AccountSharedData, ReadableAccount},
1621 clone_solana_clock::Epoch,
1622 clone_solana_instruction::Instruction,
1623 clone_solana_program_runtime::{
1624 invoke_context::SerializedAccountMetadata, with_mock_invoke_context,
1625 },
1626 clone_solana_sdk_ids::system_program,
1627 clone_solana_transaction_context::TransactionAccount,
1628 solana_sbpf::{
1629 ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1630 },
1631 std::{
1632 cell::{Cell, RefCell},
1633 mem, ptr,
1634 rc::Rc,
1635 slice,
1636 },
1637 };
1638
1639 macro_rules! mock_invoke_context {
1640 ($invoke_context:ident,
1641 $transaction_context:ident,
1642 $instruction_data:expr,
1643 $transaction_accounts:expr,
1644 $program_accounts:expr,
1645 $instruction_accounts:expr) => {
1646 let program_accounts = $program_accounts;
1647 let instruction_data = $instruction_data;
1648 let instruction_accounts = $instruction_accounts
1649 .iter()
1650 .enumerate()
1651 .map(
1652 |(index_in_callee, index_in_transaction)| InstructionAccount {
1653 index_in_transaction: *index_in_transaction as IndexOfAccount,
1654 index_in_caller: *index_in_transaction as IndexOfAccount,
1655 index_in_callee: index_in_callee as IndexOfAccount,
1656 is_signer: false,
1657 is_writable: $transaction_accounts[*index_in_transaction as usize].2,
1658 },
1659 )
1660 .collect::<Vec<_>>();
1661 let transaction_accounts = $transaction_accounts
1662 .into_iter()
1663 .map(|a| (a.0, a.1))
1664 .collect::<Vec<TransactionAccount>>();
1665 with_mock_invoke_context!($invoke_context, $transaction_context, transaction_accounts);
1666 let mut feature_set = $invoke_context.get_feature_set().clone();
1667 feature_set.deactivate(&bpf_account_data_direct_mapping::id());
1668 $invoke_context.mock_set_feature_set(Arc::new(feature_set));
1669 $invoke_context
1670 .transaction_context
1671 .get_next_instruction_context()
1672 .unwrap()
1673 .configure(program_accounts, &instruction_accounts, instruction_data);
1674 $invoke_context.push().unwrap();
1675 };
1676 }
1677
1678 macro_rules! borrow_instruction_account {
1679 ($invoke_context:expr, $index:expr) => {{
1680 let instruction_context = $invoke_context
1681 .transaction_context
1682 .get_current_instruction_context()
1683 .unwrap();
1684 instruction_context
1685 .try_borrow_instruction_account($invoke_context.transaction_context, $index)
1686 .unwrap()
1687 }};
1688 }
1689
1690 #[test]
1691 fn test_translate_instruction() {
1692 let transaction_accounts =
1693 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1694 mock_invoke_context!(
1695 invoke_context,
1696 transaction_context,
1697 b"instruction data",
1698 transaction_accounts,
1699 &[0],
1700 &[1]
1701 );
1702
1703 let program_id = Pubkey::new_unique();
1704 let accounts = vec![AccountMeta {
1705 pubkey: Pubkey::new_unique(),
1706 is_signer: true,
1707 is_writable: false,
1708 }];
1709 let data = b"ins data".to_vec();
1710 let vm_addr = MM_INPUT_START;
1711 let (_mem, region) = MockInstruction {
1712 program_id,
1713 accounts: accounts.clone(),
1714 data: data.clone(),
1715 }
1716 .into_region(vm_addr);
1717
1718 let config = Config {
1719 aligned_memory_mapping: false,
1720 ..Config::default()
1721 };
1722 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1723
1724 let ins = SyscallInvokeSignedRust::translate_instruction(
1725 vm_addr,
1726 &memory_mapping,
1727 &mut invoke_context,
1728 )
1729 .unwrap();
1730 assert_eq!(ins.program_id, program_id);
1731 assert_eq!(ins.accounts, accounts);
1732 assert_eq!(ins.data, data);
1733 }
1734
1735 #[test]
1736 fn test_translate_signers() {
1737 let transaction_accounts =
1738 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1739 mock_invoke_context!(
1740 invoke_context,
1741 transaction_context,
1742 b"instruction data",
1743 transaction_accounts,
1744 &[0],
1745 &[1]
1746 );
1747
1748 let program_id = Pubkey::new_unique();
1749 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1750
1751 let vm_addr = MM_INPUT_START;
1752 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1753
1754 let config = Config {
1755 aligned_memory_mapping: false,
1756 ..Config::default()
1757 };
1758 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1759
1760 let signers = SyscallInvokeSignedRust::translate_signers(
1761 &program_id,
1762 vm_addr,
1763 1,
1764 &memory_mapping,
1765 &invoke_context,
1766 )
1767 .unwrap();
1768 assert_eq!(signers[0], derived_key);
1769 }
1770
1771 #[test]
1772 fn test_caller_account_from_account_info() {
1773 let transaction_accounts =
1774 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1775 let account = transaction_accounts[1].1.clone();
1776 mock_invoke_context!(
1777 invoke_context,
1778 transaction_context,
1779 b"instruction data",
1780 transaction_accounts,
1781 &[0],
1782 &[1]
1783 );
1784
1785 let key = Pubkey::new_unique();
1786 let vm_addr = MM_INPUT_START;
1787 let (_mem, region, account_metadata) =
1788 MockAccountInfo::new(key, &account).into_region(vm_addr);
1789
1790 let config = Config {
1791 aligned_memory_mapping: false,
1792 ..Config::default()
1793 };
1794 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1795
1796 let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1797
1798 let caller_account = CallerAccount::from_account_info(
1799 &invoke_context,
1800 &memory_mapping,
1801 vm_addr,
1802 account_info,
1803 &account_metadata,
1804 )
1805 .unwrap();
1806 assert_eq!(*caller_account.lamports, account.lamports());
1807 assert_eq!(caller_account.owner, account.owner());
1808 assert_eq!(caller_account.original_data_len, account.data().len());
1809 assert_eq!(
1810 *caller_account.ref_to_len_in_vm.get().unwrap() as usize,
1811 account.data().len()
1812 );
1813 assert_eq!(caller_account.serialized_data, account.data());
1814 }
1815
1816 #[test]
1817 fn test_update_caller_account_lamports_owner() {
1818 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1819 let account = transaction_accounts[1].1.clone();
1820 mock_invoke_context!(
1821 invoke_context,
1822 transaction_context,
1823 b"instruction data",
1824 transaction_accounts,
1825 &[0],
1826 &[1]
1827 );
1828
1829 let mut mock_caller_account = MockCallerAccount::new(
1830 1234,
1831 *account.owner(),
1832 0xFFFFFFFF00000000,
1833 account.data(),
1834 false,
1835 );
1836
1837 let config = Config {
1838 aligned_memory_mapping: false,
1839 ..Config::default()
1840 };
1841 let memory_mapping = MemoryMapping::new(
1842 mock_caller_account.regions.split_off(0),
1843 &config,
1844 SBPFVersion::V3,
1845 )
1846 .unwrap();
1847
1848 let mut caller_account = mock_caller_account.caller_account();
1849
1850 let mut callee_account = borrow_instruction_account!(invoke_context, 0);
1851
1852 callee_account.set_lamports(42).unwrap();
1853 callee_account
1854 .set_owner(Pubkey::new_unique().as_ref())
1855 .unwrap();
1856
1857 update_caller_account(
1858 &invoke_context,
1859 &memory_mapping,
1860 false,
1861 &mut caller_account,
1862 &mut callee_account,
1863 false,
1864 )
1865 .unwrap();
1866
1867 assert_eq!(*caller_account.lamports, 42);
1868 assert_eq!(caller_account.owner, callee_account.get_owner());
1869 }
1870
1871 #[test]
1872 fn test_update_caller_account_data() {
1873 let transaction_accounts =
1874 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1875 let account = transaction_accounts[1].1.clone();
1876 let original_data_len = account.data().len();
1877
1878 mock_invoke_context!(
1879 invoke_context,
1880 transaction_context,
1881 b"instruction data",
1882 transaction_accounts,
1883 &[0],
1884 &[1]
1885 );
1886
1887 let mut mock_caller_account = MockCallerAccount::new(
1888 account.lamports(),
1889 *account.owner(),
1890 0xFFFFFFFF00000000,
1891 account.data(),
1892 false,
1893 );
1894
1895 let config = Config {
1896 aligned_memory_mapping: false,
1897 ..Config::default()
1898 };
1899 let memory_mapping = MemoryMapping::new(
1900 mock_caller_account.regions.split_off(0),
1901 &config,
1902 SBPFVersion::V3,
1903 )
1904 .unwrap();
1905
1906 let data_slice = mock_caller_account.data_slice();
1907 let len_ptr = unsafe {
1908 data_slice
1909 .as_ptr()
1910 .offset(-(mem::size_of::<u64>() as isize))
1911 };
1912 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1913 let mut caller_account = mock_caller_account.caller_account();
1914
1915 let mut callee_account = borrow_instruction_account!(invoke_context, 0);
1916
1917 for (new_value, expected_realloc_size) in [
1918 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1919 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1920 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1921 ] {
1922 assert_eq!(caller_account.serialized_data, callee_account.get_data());
1923 callee_account.set_data_from_slice(&new_value).unwrap();
1924
1925 update_caller_account(
1926 &invoke_context,
1927 &memory_mapping,
1928 false,
1929 &mut caller_account,
1930 &mut callee_account,
1931 false,
1932 )
1933 .unwrap();
1934
1935 let data_len = callee_account.get_data().len();
1936 assert_eq!(
1937 data_len,
1938 *caller_account.ref_to_len_in_vm.get().unwrap() as usize
1939 );
1940 assert_eq!(data_len, serialized_len());
1941 assert_eq!(data_len, caller_account.serialized_data.len());
1942 assert_eq!(
1943 callee_account.get_data(),
1944 &caller_account.serialized_data[..data_len]
1945 );
1946 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1947 assert!(is_zeroed(&data_slice[data_len..]));
1948 }
1949
1950 callee_account
1951 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1952 .unwrap();
1953 update_caller_account(
1954 &invoke_context,
1955 &memory_mapping,
1956 false,
1957 &mut caller_account,
1958 &mut callee_account,
1959 false,
1960 )
1961 .unwrap();
1962 let data_len = callee_account.get_data().len();
1963 assert_eq!(data_slice[data_len..].len(), 0);
1964 assert!(is_zeroed(&data_slice[data_len..]));
1965
1966 callee_account
1967 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1968 .unwrap();
1969 assert_matches!(
1970 update_caller_account(
1971 &invoke_context,
1972 &memory_mapping,
1973 false,
1974 &mut caller_account,
1975 &mut callee_account,
1976 false,
1977 ),
1978 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1979 );
1980
1981 callee_account.set_data_length(0).unwrap();
1983 callee_account
1984 .set_owner(system_program::id().as_ref())
1985 .unwrap();
1986 update_caller_account(
1987 &invoke_context,
1988 &memory_mapping,
1989 false,
1990 &mut caller_account,
1991 &mut callee_account,
1992 false,
1993 )
1994 .unwrap();
1995 let data_len = callee_account.get_data().len();
1996 assert_eq!(data_len, 0);
1997 }
1998
1999 #[test]
2000 fn test_update_caller_account_data_direct_mapping() {
2001 let transaction_accounts =
2002 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2003 let account = transaction_accounts[1].1.clone();
2004 let original_data_len = account.data().len();
2005
2006 mock_invoke_context!(
2007 invoke_context,
2008 transaction_context,
2009 b"instruction data",
2010 transaction_accounts,
2011 &[0],
2012 &[1]
2013 );
2014
2015 let mut mock_caller_account = MockCallerAccount::new(
2016 account.lamports(),
2017 *account.owner(),
2018 0xFFFFFFFF00000000,
2019 account.data(),
2020 true,
2021 );
2022
2023 let config = Config {
2024 aligned_memory_mapping: false,
2025 ..Config::default()
2026 };
2027 let memory_mapping = MemoryMapping::new(
2028 mock_caller_account.regions.split_off(0),
2029 &config,
2030 SBPFVersion::V3,
2031 )
2032 .unwrap();
2033
2034 let data_slice = mock_caller_account.data_slice();
2035 let len_ptr = unsafe {
2036 data_slice
2037 .as_ptr()
2038 .offset(-(mem::size_of::<u64>() as isize))
2039 };
2040 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2041 let mut caller_account = mock_caller_account.caller_account();
2042
2043 let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2044
2045 for change_ptr in [false, true] {
2046 for (new_value, expected_realloc_used) in [
2047 (b"foobazbad".to_vec(), 3), (b"foo".to_vec(), 0), (b"foobaz".to_vec(), 0), (vec![], 0), ] {
2052 if change_ptr {
2053 callee_account.set_data(new_value).unwrap();
2054 } else {
2055 callee_account.set_data_from_slice(&new_value).unwrap();
2056 }
2057
2058 update_caller_account(
2059 &invoke_context,
2060 &memory_mapping,
2061 false,
2062 &mut caller_account,
2063 &mut callee_account,
2064 true,
2065 )
2066 .unwrap();
2067
2068 assert_eq!(
2070 translate_slice::<u8>(&memory_mapping, caller_account.vm_data_addr, 1, true,)
2071 .unwrap()
2072 .as_ptr(),
2073 callee_account.get_data().as_ptr()
2074 );
2075
2076 let data_len = callee_account.get_data().len();
2077 assert_eq!(
2079 data_len,
2080 *caller_account.ref_to_len_in_vm.get().unwrap() as usize
2081 );
2082 assert_eq!(data_len, serialized_len());
2084
2085 let realloc_area = translate_slice::<u8>(
2086 &memory_mapping,
2087 caller_account
2088 .vm_data_addr
2089 .saturating_add(caller_account.original_data_len as u64),
2090 MAX_PERMITTED_DATA_INCREASE as u64,
2091 invoke_context.get_check_aligned(),
2092 )
2093 .unwrap();
2094
2095 if data_len < original_data_len {
2096 let original_data_slice = unsafe {
2099 slice::from_raw_parts(callee_account.get_data().as_ptr(), original_data_len)
2100 };
2101
2102 let spare_capacity = &original_data_slice[original_data_len - data_len..];
2103 assert!(
2104 is_zeroed(spare_capacity),
2105 "dirty account spare capacity {spare_capacity:?}",
2106 );
2107 }
2108
2109 assert_eq!(
2112 &realloc_area[..expected_realloc_used],
2113 &callee_account.get_data()[data_len - expected_realloc_used..]
2114 );
2115
2116 assert!(
2118 is_zeroed(&realloc_area[expected_realloc_used..]),
2119 "dirty realloc padding {realloc_area:?}",
2120 );
2121 }
2122 }
2123
2124 callee_account
2125 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2126 .unwrap();
2127 update_caller_account(
2128 &invoke_context,
2129 &memory_mapping,
2130 false,
2131 &mut caller_account,
2132 &mut callee_account,
2133 true,
2134 )
2135 .unwrap();
2136 assert!(
2137 is_zeroed(caller_account.serialized_data),
2138 "dirty realloc padding {:?}",
2139 caller_account.serialized_data
2140 );
2141
2142 callee_account
2143 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2144 .unwrap();
2145 assert_matches!(
2146 update_caller_account(
2147 &invoke_context,
2148 &memory_mapping,
2149 false,
2150 &mut caller_account,
2151 &mut callee_account,
2152 false,
2153 ),
2154 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2155 );
2156
2157 callee_account.set_data_length(0).unwrap();
2159 callee_account
2160 .set_owner(system_program::id().as_ref())
2161 .unwrap();
2162 update_caller_account(
2163 &invoke_context,
2164 &memory_mapping,
2165 false,
2166 &mut caller_account,
2167 &mut callee_account,
2168 true,
2169 )
2170 .unwrap();
2171 let data_len = callee_account.get_data().len();
2172 assert_eq!(data_len, 0);
2173 }
2174
2175 #[test]
2176 fn test_update_caller_account_data_capacity_direct_mapping() {
2177 let transaction_accounts =
2178 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2179 let account = transaction_accounts[1].1.clone();
2180
2181 mock_invoke_context!(
2182 invoke_context,
2183 transaction_context,
2184 b"instruction data",
2185 transaction_accounts,
2186 &[0],
2187 &[1]
2188 );
2189
2190 let mut mock_caller_account = MockCallerAccount::new(
2191 account.lamports(),
2192 *account.owner(),
2193 0xFFFFFFFF00000000,
2194 account.data(),
2195 true,
2196 );
2197
2198 let config = Config {
2199 aligned_memory_mapping: false,
2200 ..Config::default()
2201 };
2202 let memory_mapping = MemoryMapping::new(
2203 mock_caller_account.regions.split_off(0),
2204 &config,
2205 SBPFVersion::V3,
2206 )
2207 .unwrap();
2208
2209 let mut caller_account = mock_caller_account.caller_account();
2210
2211 {
2212 let mut account = invoke_context
2213 .transaction_context
2214 .get_account_at_index(1)
2215 .unwrap()
2216 .try_borrow_mut()
2217 .unwrap();
2218 account.set_data(b"baz".to_vec());
2219 }
2220
2221 let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2222 assert_eq!(callee_account.get_data().len(), 3);
2223 assert_eq!(callee_account.capacity(), 3);
2224
2225 update_caller_account(
2226 &invoke_context,
2227 &memory_mapping,
2228 false,
2229 &mut caller_account,
2230 &mut callee_account,
2231 true,
2232 )
2233 .unwrap();
2234
2235 assert_eq!(callee_account.get_data().len(), 3);
2236 assert!(callee_account.capacity() >= caller_account.original_data_len);
2237 let data = translate_slice::<u8>(
2238 &memory_mapping,
2239 caller_account.vm_data_addr,
2240 callee_account.get_data().len() as u64,
2241 true,
2242 )
2243 .unwrap();
2244 assert_eq!(data, callee_account.get_data());
2245 }
2246
2247 #[test]
2248 fn test_update_callee_account_lamports_owner() {
2249 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2250 let account = transaction_accounts[1].1.clone();
2251
2252 mock_invoke_context!(
2253 invoke_context,
2254 transaction_context,
2255 b"instruction data",
2256 transaction_accounts,
2257 &[0],
2258 &[1]
2259 );
2260
2261 let mut mock_caller_account = MockCallerAccount::new(
2262 1234,
2263 *account.owner(),
2264 0xFFFFFFFF00000000,
2265 account.data(),
2266 false,
2267 );
2268
2269 let config = Config {
2270 aligned_memory_mapping: false,
2271 ..Config::default()
2272 };
2273 let memory_mapping = MemoryMapping::new(
2274 mock_caller_account.regions.split_off(0),
2275 &config,
2276 SBPFVersion::V3,
2277 )
2278 .unwrap();
2279
2280 let caller_account = mock_caller_account.caller_account();
2281
2282 let callee_account = borrow_instruction_account!(invoke_context, 0);
2283
2284 *caller_account.lamports = 42;
2285 *caller_account.owner = Pubkey::new_unique();
2286
2287 update_callee_account(
2288 &invoke_context,
2289 &memory_mapping,
2290 false,
2291 &caller_account,
2292 callee_account,
2293 false,
2294 )
2295 .unwrap();
2296
2297 let callee_account = borrow_instruction_account!(invoke_context, 0);
2298 assert_eq!(callee_account.get_lamports(), 42);
2299 assert_eq!(caller_account.owner, callee_account.get_owner());
2300 }
2301
2302 #[test]
2303 fn test_update_callee_account_data() {
2304 let transaction_accounts =
2305 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2306 let account = transaction_accounts[1].1.clone();
2307
2308 mock_invoke_context!(
2309 invoke_context,
2310 transaction_context,
2311 b"instruction data",
2312 transaction_accounts,
2313 &[0],
2314 &[1]
2315 );
2316
2317 let mut mock_caller_account = MockCallerAccount::new(
2318 1234,
2319 *account.owner(),
2320 0xFFFFFFFF00000000,
2321 account.data(),
2322 false,
2323 );
2324
2325 let config = Config {
2326 aligned_memory_mapping: false,
2327 ..Config::default()
2328 };
2329 let memory_mapping = MemoryMapping::new(
2330 mock_caller_account.regions.split_off(0),
2331 &config,
2332 SBPFVersion::V3,
2333 )
2334 .unwrap();
2335
2336 let mut caller_account = mock_caller_account.caller_account();
2337
2338 let callee_account = borrow_instruction_account!(invoke_context, 0);
2339
2340 let mut data = b"foo".to_vec();
2341 caller_account.serialized_data = &mut data;
2342
2343 update_callee_account(
2344 &invoke_context,
2345 &memory_mapping,
2346 false,
2347 &caller_account,
2348 callee_account,
2349 false,
2350 )
2351 .unwrap();
2352
2353 let callee_account = borrow_instruction_account!(invoke_context, 0);
2354 assert_eq!(callee_account.get_data(), caller_account.serialized_data);
2355
2356 let mut data = Vec::new();
2358 caller_account.serialized_data = &mut data;
2359 *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 0;
2360 let mut owner = system_program::id();
2361 caller_account.owner = &mut owner;
2362 update_callee_account(
2363 &invoke_context,
2364 &memory_mapping,
2365 false,
2366 &caller_account,
2367 callee_account,
2368 false,
2369 )
2370 .unwrap();
2371 let callee_account = borrow_instruction_account!(invoke_context, 0);
2372 assert_eq!(callee_account.get_data(), b"");
2373 }
2374
2375 #[test]
2376 fn test_update_callee_account_data_readonly() {
2377 let transaction_accounts =
2378 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2379 let account = transaction_accounts[1].1.clone();
2380
2381 mock_invoke_context!(
2382 invoke_context,
2383 transaction_context,
2384 b"instruction data",
2385 transaction_accounts,
2386 &[0],
2387 &[1]
2388 );
2389
2390 let mut mock_caller_account = MockCallerAccount::new(
2391 1234,
2392 *account.owner(),
2393 0xFFFFFFFF00000000,
2394 account.data(),
2395 false,
2396 );
2397
2398 let config = Config {
2399 aligned_memory_mapping: false,
2400 ..Config::default()
2401 };
2402 let memory_mapping = MemoryMapping::new(
2403 mock_caller_account.regions.split_off(0),
2404 &config,
2405 SBPFVersion::V3,
2406 )
2407 .unwrap();
2408
2409 let mut caller_account = mock_caller_account.caller_account();
2410
2411 let callee_account = borrow_instruction_account!(invoke_context, 0);
2412
2413 caller_account.serialized_data[0] = b'b';
2414 assert_matches!(
2415 update_callee_account(
2416 &invoke_context,
2417 &memory_mapping,
2418 false,
2419 &caller_account,
2420 callee_account,
2421 false,
2422 ),
2423 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2424 );
2425
2426 let mut data = b"foobarbaz".to_vec();
2428 *caller_account.ref_to_len_in_vm.get_mut().unwrap() = data.len() as u64;
2429 caller_account.serialized_data = &mut data;
2430
2431 let callee_account = borrow_instruction_account!(invoke_context, 0);
2432 assert_matches!(
2433 update_callee_account(
2434 &invoke_context,
2435 &memory_mapping,
2436 false,
2437 &caller_account,
2438 callee_account,
2439 false,
2440 ),
2441 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
2442 );
2443
2444 let mut data = b"baz".to_vec();
2446 *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 9;
2447 caller_account.serialized_data = &mut data;
2448
2449 let callee_account = borrow_instruction_account!(invoke_context, 0);
2450 assert_matches!(
2451 update_callee_account(
2452 &invoke_context,
2453 &memory_mapping,
2454 false,
2455 &caller_account,
2456 callee_account,
2457 true,
2458 ),
2459 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
2460 );
2461 }
2462
2463 #[test]
2464 fn test_update_callee_account_data_direct_mapping() {
2465 let transaction_accounts =
2466 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2467 let account = transaction_accounts[1].1.clone();
2468
2469 mock_invoke_context!(
2470 invoke_context,
2471 transaction_context,
2472 b"instruction data",
2473 transaction_accounts,
2474 &[0],
2475 &[1]
2476 );
2477
2478 let mut mock_caller_account = MockCallerAccount::new(
2479 1234,
2480 *account.owner(),
2481 0xFFFFFFFF00000000,
2482 account.data(),
2483 true,
2484 );
2485
2486 let config = Config {
2487 aligned_memory_mapping: false,
2488 ..Config::default()
2489 };
2490 let memory_mapping = MemoryMapping::new(
2491 mock_caller_account.regions.split_off(0),
2492 &config,
2493 SBPFVersion::V3,
2494 )
2495 .unwrap();
2496
2497 let mut caller_account = mock_caller_account.caller_account();
2498
2499 let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2500
2501 callee_account.get_data_mut().unwrap();
2505
2506 let serialized_data = translate_slice_mut::<u8>(
2507 &memory_mapping,
2508 caller_account
2509 .vm_data_addr
2510 .saturating_add(caller_account.original_data_len as u64),
2511 3,
2512 invoke_context.get_check_aligned(),
2513 )
2514 .unwrap();
2515 serialized_data.copy_from_slice(b"baz");
2516
2517 for (len, expected) in [
2518 (9, b"foobarbaz".to_vec()), (6, b"foobar".to_vec()), (3, b"foo".to_vec()), ] {
2522 *caller_account.ref_to_len_in_vm.get_mut().unwrap() = len as u64;
2523 update_callee_account(
2524 &invoke_context,
2525 &memory_mapping,
2526 false,
2527 &caller_account,
2528 callee_account,
2529 true,
2530 )
2531 .unwrap();
2532 callee_account = borrow_instruction_account!(invoke_context, 0);
2533 assert_eq!(callee_account.get_data(), expected);
2534 }
2535
2536 let mut data = Vec::new();
2538 caller_account.serialized_data = &mut data;
2539 *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 0;
2540 let mut owner = system_program::id();
2541 caller_account.owner = &mut owner;
2542 update_callee_account(
2543 &invoke_context,
2544 &memory_mapping,
2545 false,
2546 &caller_account,
2547 callee_account,
2548 true,
2549 )
2550 .unwrap();
2551 callee_account = borrow_instruction_account!(invoke_context, 0);
2552 assert_eq!(callee_account.get_data(), b"");
2553 }
2554
2555 #[test]
2556 fn test_translate_accounts_rust() {
2557 let transaction_accounts =
2558 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2559 let account = transaction_accounts[1].1.clone();
2560 let key = transaction_accounts[1].0;
2561 let original_data_len = account.data().len();
2562
2563 let vm_addr = MM_INPUT_START;
2564 let (_mem, region, account_metadata) =
2565 MockAccountInfo::new(key, &account).into_region(vm_addr);
2566
2567 let config = Config {
2568 aligned_memory_mapping: false,
2569 ..Config::default()
2570 };
2571 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
2572
2573 mock_invoke_context!(
2574 invoke_context,
2575 transaction_context,
2576 b"instruction data",
2577 transaction_accounts,
2578 &[0],
2579 &[1, 1]
2580 );
2581
2582 mock_create_vm!(_vm, Vec::new(), vec![account_metadata], &mut invoke_context);
2583
2584 let accounts = SyscallInvokeSignedRust::translate_accounts(
2585 &[
2586 InstructionAccount {
2587 index_in_transaction: 1,
2588 index_in_caller: 0,
2589 index_in_callee: 0,
2590 is_signer: false,
2591 is_writable: true,
2592 },
2593 InstructionAccount {
2594 index_in_transaction: 1,
2595 index_in_caller: 0,
2596 index_in_callee: 0,
2597 is_signer: false,
2598 is_writable: true,
2599 },
2600 ],
2601 &[0],
2602 vm_addr,
2603 1,
2604 false,
2605 &memory_mapping,
2606 &mut invoke_context,
2607 )
2608 .unwrap();
2609 assert_eq!(accounts.len(), 2);
2610 assert!(accounts[0].1.is_none());
2611 let caller_account = accounts[1].1.as_ref().unwrap();
2612 assert_eq!(caller_account.serialized_data, account.data());
2613 assert_eq!(caller_account.original_data_len, original_data_len);
2614 }
2615
2616 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
2617 struct MockCallerAccount {
2618 lamports: u64,
2619 owner: Pubkey,
2620 vm_addr: u64,
2621 data: Vec<u8>,
2622 len: u64,
2623 regions: Vec<MemoryRegion>,
2624 direct_mapping: bool,
2625 }
2626
2627 impl MockCallerAccount {
2628 fn new(
2629 lamports: u64,
2630 owner: Pubkey,
2631 vm_addr: u64,
2632 data: &[u8],
2633 direct_mapping: bool,
2634 ) -> MockCallerAccount {
2635 let mut regions = vec![];
2636
2637 let mut d = vec![
2638 0;
2639 mem::size_of::<u64>()
2640 + if direct_mapping { 0 } else { data.len() }
2641 + MAX_PERMITTED_DATA_INCREASE
2642 ];
2643 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
2645
2646 if !direct_mapping {
2648 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
2649 }
2650
2651 let mut region_addr = vm_addr;
2653 let region_len = mem::size_of::<u64>()
2654 + if direct_mapping {
2655 0
2656 } else {
2657 data.len() + MAX_PERMITTED_DATA_INCREASE
2658 };
2659 regions.push(MemoryRegion::new_writable(&mut d[..region_len], vm_addr));
2660 region_addr += region_len as u64;
2661
2662 if direct_mapping {
2663 regions.push(MemoryRegion::new_readonly(data, region_addr));
2665 region_addr += data.len() as u64;
2666
2667 regions.push(MemoryRegion::new_writable(
2669 &mut d[mem::size_of::<u64>()..],
2670 region_addr,
2671 ));
2672 } else {
2673 d.truncate(mem::size_of::<u64>() + data.len());
2675 }
2676
2677 MockCallerAccount {
2678 lamports,
2679 owner,
2680 vm_addr,
2681 data: d,
2682 len: data.len() as u64,
2683 regions,
2684 direct_mapping,
2685 }
2686 }
2687
2688 fn data_slice<'a>(&self) -> &'a [u8] {
2689 unsafe {
2691 slice::from_raw_parts(
2692 self.data[mem::size_of::<u64>()..].as_ptr(),
2693 self.data.capacity() - mem::size_of::<u64>(),
2694 )
2695 }
2696 }
2697
2698 fn caller_account(&mut self) -> CallerAccount<'_, '_> {
2699 let data = if self.direct_mapping {
2700 &mut []
2701 } else {
2702 &mut self.data[mem::size_of::<u64>()..]
2703 };
2704 CallerAccount {
2705 lamports: &mut self.lamports,
2706 owner: &mut self.owner,
2707 original_data_len: self.len as usize,
2708 serialized_data: data,
2709 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
2710 ref_to_len_in_vm: VmValue::Translated(&mut self.len),
2711 }
2712 }
2713 }
2714
2715 fn transaction_with_one_writable_instruction_account(
2716 data: Vec<u8>,
2717 ) -> Vec<TestTransactionAccount> {
2718 let program_id = Pubkey::new_unique();
2719 let account = AccountSharedData::from(Account {
2720 lamports: 1,
2721 data,
2722 owner: program_id,
2723 executable: false,
2724 rent_epoch: 100,
2725 });
2726 vec![
2727 (
2728 program_id,
2729 AccountSharedData::from(Account {
2730 lamports: 0,
2731 data: vec![],
2732 owner: bpf_loader::id(),
2733 executable: true,
2734 rent_epoch: 0,
2735 }),
2736 false,
2737 ),
2738 (Pubkey::new_unique(), account, true),
2739 ]
2740 }
2741
2742 fn transaction_with_one_readonly_instruction_account(
2743 data: Vec<u8>,
2744 ) -> Vec<TestTransactionAccount> {
2745 let program_id = Pubkey::new_unique();
2746 let account_owner = Pubkey::new_unique();
2747 let account = AccountSharedData::from(Account {
2748 lamports: 1,
2749 data,
2750 owner: account_owner,
2751 executable: false,
2752 rent_epoch: 100,
2753 });
2754 vec![
2755 (
2756 program_id,
2757 AccountSharedData::from(Account {
2758 lamports: 0,
2759 data: vec![],
2760 owner: bpf_loader::id(),
2761 executable: true,
2762 rent_epoch: 0,
2763 }),
2764 false,
2765 ),
2766 (Pubkey::new_unique(), account, true),
2767 ]
2768 }
2769
2770 struct MockInstruction {
2771 program_id: Pubkey,
2772 accounts: Vec<AccountMeta>,
2773 data: Vec<u8>,
2774 }
2775
2776 impl MockInstruction {
2777 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2778 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
2779
2780 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
2781
2782 let mut data = vec![0; size];
2783
2784 let vm_addr = vm_addr as usize;
2785 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
2786 let data_addr = accounts_addr + accounts_len;
2787
2788 let ins = Instruction {
2789 program_id: self.program_id,
2790 accounts: unsafe {
2791 Vec::from_raw_parts(
2792 accounts_addr as *mut _,
2793 self.accounts.len(),
2794 self.accounts.len(),
2795 )
2796 },
2797 data: unsafe {
2798 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
2799 },
2800 };
2801 let ins = StableInstruction::from(ins);
2802
2803 unsafe {
2804 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
2805 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
2806 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
2807 );
2808 data[data_addr - vm_addr..].copy_from_slice(&self.data);
2809 }
2810
2811 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2812 (data, region)
2813 }
2814 }
2815
2816 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2817 let vm_addr = vm_addr as usize;
2818
2819 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
2822 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
2823
2824 let total_size = fat_ptr_size_of_slice
2826 + singers_length * fat_ptr_size_of_slice
2827 + sum_signers_data_length;
2828 let mut data = vec![0; total_size];
2829
2830 data[..fat_ptr_size_of_slice / 2]
2842 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
2843 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
2844 .clone_from_slice(&(singers_length).to_le_bytes());
2845
2846 let (mut p1, mut p2) = (
2848 fat_ptr_size_of_slice,
2849 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
2850 );
2851 for signer in signers.iter() {
2852 let signer_length = signer.len();
2853
2854 data[p1..p1 + fat_ptr_size_of_slice / 2]
2856 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
2857 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
2858 .clone_from_slice(&(signer_length).to_le_bytes());
2859 p1 += fat_ptr_size_of_slice;
2860
2861 data[p2..p2 + signer_length].clone_from_slice(signer);
2863 p2 += signer_length;
2864 }
2865
2866 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2867 (data, region)
2868 }
2869
2870 struct MockAccountInfo<'a> {
2871 key: Pubkey,
2872 is_signer: bool,
2873 is_writable: bool,
2874 lamports: u64,
2875 data: &'a [u8],
2876 owner: Pubkey,
2877 executable: bool,
2878 rent_epoch: Epoch,
2879 }
2880
2881 impl MockAccountInfo<'_> {
2882 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
2883 MockAccountInfo {
2884 key,
2885 is_signer: false,
2886 is_writable: false,
2887 lamports: account.lamports(),
2888 data: account.data(),
2889 owner: *account.owner(),
2890 executable: account.executable(),
2891 rent_epoch: account.rent_epoch(),
2892 }
2893 }
2894
2895 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
2896 let size = mem::size_of::<AccountInfo>()
2897 + mem::size_of::<Pubkey>() * 2
2898 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
2899 + mem::size_of::<u64>()
2900 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
2901 + self.data.len();
2902 let mut data = vec![0; size];
2903
2904 let vm_addr = vm_addr as usize;
2905 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
2906 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
2907 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
2908 let owner_addr = lamports_addr + mem::size_of::<u64>();
2909 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
2910 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
2911
2912 let info = AccountInfo {
2913 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
2914 is_signer: self.is_signer,
2915 is_writable: self.is_writable,
2916 lamports: unsafe {
2917 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
2918 },
2919 data: unsafe {
2920 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
2921 },
2922 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
2923 executable: self.executable,
2924 rent_epoch: self.rent_epoch,
2925 };
2926
2927 unsafe {
2928 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
2929 ptr::write_unaligned(
2930 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
2931 self.key,
2932 );
2933 ptr::write_unaligned(
2934 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
2935 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
2936 );
2937 ptr::write_unaligned(
2938 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
2939 self.lamports,
2940 );
2941 ptr::write_unaligned(
2942 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
2943 self.owner,
2944 );
2945 ptr::write_unaligned(
2946 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
2947 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
2948 data_addr as *mut u8,
2949 self.data.len(),
2950 ))),
2951 );
2952 data[data_addr - vm_addr..].copy_from_slice(self.data);
2953 }
2954
2955 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2956 (
2957 data,
2958 region,
2959 SerializedAccountMetadata {
2960 original_data_len: self.data.len(),
2961 vm_key_addr: key_addr as u64,
2962 vm_lamports_addr: lamports_addr as u64,
2963 vm_owner_addr: owner_addr as u64,
2964 vm_data_addr: data_addr as u64,
2965 },
2966 )
2967 }
2968 }
2969
2970 #[repr(C)]
2971 struct RcBox<T> {
2972 strong: Cell<usize>,
2973 weak: Cell<usize>,
2974 value: T,
2975 }
2976
2977 impl<T> RcBox<T> {
2978 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
2979 fn new(value: T) -> RcBox<T> {
2980 RcBox {
2981 strong: Cell::new(0),
2982 weak: Cell::new(0),
2983 value,
2984 }
2985 }
2986 }
2987
2988 fn is_zeroed(data: &[u8]) -> bool {
2989 data.iter().all(|b| *b == 0)
2990 }
2991}