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