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