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