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