1use {
2 super::*,
3 crate::declare_syscall,
4 solana_sdk::{
5 feature_set::enable_bpf_loader_set_authority_checked_ix,
6 stable_layout::stable_instruction::StableInstruction,
7 syscalls::{
8 MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
9 },
10 transaction_context::BorrowedAccount,
11 },
12 std::mem,
13};
14
15struct CallerAccount<'a> {
20 lamports: &'a mut u64,
21 owner: &'a mut Pubkey,
22 original_data_len: usize,
23 data: &'a mut [u8],
24 vm_data_addr: u64,
27 ref_to_len_in_vm: &'a mut u64,
28 serialized_len_ptr: *mut u64,
30 executable: bool,
31 rent_epoch: u64,
32}
33
34impl<'a> CallerAccount<'a> {
35 fn from_account_info(
37 invoke_context: &InvokeContext,
38 memory_mapping: &MemoryMapping,
39 _vm_addr: u64,
40 account_info: &AccountInfo,
41 original_data_len: usize,
42 ) -> Result<CallerAccount<'a>, EbpfError> {
43 let lamports = {
47 let ptr = translate_type::<u64>(
49 memory_mapping,
50 account_info.lamports.as_ptr() as u64,
51 invoke_context.get_check_aligned(),
52 )?;
53 translate_type_mut::<u64>(memory_mapping, *ptr, invoke_context.get_check_aligned())?
54 };
55 let owner = translate_type_mut::<Pubkey>(
56 memory_mapping,
57 account_info.owner as *const _ as u64,
58 invoke_context.get_check_aligned(),
59 )?;
60
61 let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = {
62 let data = *translate_type::<&[u8]>(
64 memory_mapping,
65 account_info.data.as_ptr() as *const _ as u64,
66 invoke_context.get_check_aligned(),
67 )?;
68
69 consume_compute_meter(
70 invoke_context,
71 (data.len() as u64)
72 .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
73 )?;
74
75 let translated = translate(
76 memory_mapping,
77 AccessType::Store,
78 (account_info.data.as_ptr() as *const u64 as u64)
79 .saturating_add(size_of::<u64>() as u64),
80 8,
81 )? as *mut u64;
82 let ref_to_len_in_vm = unsafe { &mut *translated };
83 let serialized_len_ptr = if invoke_context
84 .feature_set
85 .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
86 {
87 std::ptr::null_mut()
88 } else {
89 let ref_of_len_in_input_buffer =
90 (data.as_ptr() as *const _ as u64).saturating_sub(8);
91 translate_type_mut::<u64>(
92 memory_mapping,
93 ref_of_len_in_input_buffer,
94 invoke_context.get_check_aligned(),
95 )?
96 };
97 let vm_data_addr = data.as_ptr() as u64;
98 (
99 translate_slice_mut::<u8>(
100 memory_mapping,
101 vm_data_addr,
102 data.len() as u64,
103 invoke_context.get_check_aligned(),
104 invoke_context.get_check_size(),
105 )?,
106 vm_data_addr,
107 ref_to_len_in_vm,
108 serialized_len_ptr,
109 )
110 };
111
112 Ok(CallerAccount {
113 lamports,
114 owner,
115 original_data_len,
116 data,
117 vm_data_addr,
118 ref_to_len_in_vm,
119 serialized_len_ptr,
120 executable: account_info.executable,
121 rent_epoch: account_info.rent_epoch,
122 })
123 }
124
125 fn from_sol_account_info(
127 invoke_context: &InvokeContext,
128 memory_mapping: &MemoryMapping,
129 vm_addr: u64,
130 account_info: &SolAccountInfo,
131 original_data_len: usize,
132 ) -> Result<CallerAccount<'a>, EbpfError> {
133 let lamports = translate_type_mut::<u64>(
137 memory_mapping,
138 account_info.lamports_addr,
139 invoke_context.get_check_aligned(),
140 )?;
141 let owner = translate_type_mut::<Pubkey>(
142 memory_mapping,
143 account_info.owner_addr,
144 invoke_context.get_check_aligned(),
145 )?;
146 let vm_data_addr = account_info.data_addr;
147
148 consume_compute_meter(
149 invoke_context,
150 account_info
151 .data_len
152 .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
153 )?;
154
155 let data = translate_slice_mut::<u8>(
156 memory_mapping,
157 vm_data_addr,
158 account_info.data_len,
159 invoke_context.get_check_aligned(),
160 invoke_context.get_check_size(),
161 )?;
162
163 let data_len_vm_addr = vm_addr
168 .saturating_add(&account_info.data_len as *const u64 as u64)
169 .saturating_sub(account_info as *const _ as *const u64 as u64);
170 let data_len_addr = translate(
171 memory_mapping,
172 AccessType::Store,
173 data_len_vm_addr,
174 size_of::<u64>() as u64,
175 )?;
176 let ref_to_len_in_vm = unsafe { &mut *(data_len_addr as *mut u64) };
177
178 let ref_of_len_in_input_buffer =
179 (account_info.data_addr as *mut u8 as u64).saturating_sub(8);
180 let serialized_len_ptr = if invoke_context
181 .feature_set
182 .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
183 {
184 std::ptr::null_mut()
185 } else {
186 translate_type_mut::<u64>(
187 memory_mapping,
188 ref_of_len_in_input_buffer,
189 invoke_context.get_check_aligned(),
190 )?
191 };
192
193 Ok(CallerAccount {
194 lamports,
195 owner,
196 original_data_len,
197 data,
198 vm_data_addr,
199 ref_to_len_in_vm,
200 serialized_len_ptr,
201 executable: account_info.executable,
202 rent_epoch: account_info.rent_epoch,
203 })
204 }
205}
206
207type TranslatedAccounts<'a> = Vec<(IndexOfAccount, Option<CallerAccount<'a>>)>;
208
209trait SyscallInvokeSigned {
211 fn translate_instruction(
212 addr: u64,
213 memory_mapping: &mut MemoryMapping,
214 invoke_context: &mut InvokeContext,
215 ) -> Result<StableInstruction, EbpfError>;
216 fn translate_accounts<'a>(
217 instruction_accounts: &[InstructionAccount],
218 program_indices: &[IndexOfAccount],
219 account_infos_addr: u64,
220 account_infos_len: u64,
221 memory_mapping: &mut MemoryMapping,
222 invoke_context: &mut InvokeContext,
223 ) -> Result<TranslatedAccounts<'a>, EbpfError>;
224 fn translate_signers(
225 program_id: &Pubkey,
226 signers_seeds_addr: u64,
227 signers_seeds_len: u64,
228 memory_mapping: &mut MemoryMapping,
229 invoke_context: &InvokeContext,
230 ) -> Result<Vec<Pubkey>, EbpfError>;
231}
232
233declare_syscall!(
234 SyscallInvokeSignedRust,
236 fn inner_call(
237 invoke_context: &mut InvokeContext,
238 instruction_addr: u64,
239 account_infos_addr: u64,
240 account_infos_len: u64,
241 signers_seeds_addr: u64,
242 signers_seeds_len: u64,
243 memory_mapping: &mut MemoryMapping,
244 ) -> Result<u64, EbpfError> {
245 cpi_common::<Self>(
246 invoke_context,
247 instruction_addr,
248 account_infos_addr,
249 account_infos_len,
250 signers_seeds_addr,
251 signers_seeds_len,
252 memory_mapping,
253 )
254 }
255);
256
257impl SyscallInvokeSigned for SyscallInvokeSignedRust {
258 fn translate_instruction(
259 addr: u64,
260 memory_mapping: &mut MemoryMapping,
261 invoke_context: &mut InvokeContext,
262 ) -> Result<StableInstruction, EbpfError> {
263 let ix = translate_type::<StableInstruction>(
264 memory_mapping,
265 addr,
266 invoke_context.get_check_aligned(),
267 )?;
268
269 check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?;
270
271 let accounts = translate_slice::<AccountMeta>(
272 memory_mapping,
273 ix.accounts.as_ptr() as u64,
274 ix.accounts.len() as u64,
275 invoke_context.get_check_aligned(),
276 invoke_context.get_check_size(),
277 )?
278 .to_vec();
279
280 let ix_data_len = ix.data.len() as u64;
281 if invoke_context
282 .feature_set
283 .is_active(&feature_set::loosen_cpi_size_restriction::id())
284 {
285 consume_compute_meter(
286 invoke_context,
287 (ix_data_len)
288 .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
289 )?;
290 }
291
292 let data = translate_slice::<u8>(
293 memory_mapping,
294 ix.data.as_ptr() as u64,
295 ix_data_len,
296 invoke_context.get_check_aligned(),
297 invoke_context.get_check_size(),
298 )?
299 .to_vec();
300
301 Ok(StableInstruction {
302 accounts: accounts.into(),
303 data: data.into(),
304 program_id: ix.program_id,
305 })
306 }
307
308 fn translate_accounts<'a>(
309 instruction_accounts: &[InstructionAccount],
310 program_indices: &[IndexOfAccount],
311 account_infos_addr: u64,
312 account_infos_len: u64,
313 memory_mapping: &mut MemoryMapping,
314 invoke_context: &mut InvokeContext,
315 ) -> Result<TranslatedAccounts<'a>, EbpfError> {
316 let (account_infos, account_info_keys) = translate_account_infos(
317 account_infos_addr,
318 account_infos_len,
319 |account_info: &AccountInfo| account_info.key as *const _ as u64,
320 memory_mapping,
321 invoke_context,
322 )?;
323
324 translate_and_update_accounts(
325 instruction_accounts,
326 program_indices,
327 &account_info_keys,
328 account_infos,
329 account_infos_addr,
330 invoke_context,
331 memory_mapping,
332 CallerAccount::from_account_info,
333 )
334 }
335
336 fn translate_signers(
337 program_id: &Pubkey,
338 signers_seeds_addr: u64,
339 signers_seeds_len: u64,
340 memory_mapping: &mut MemoryMapping,
341 invoke_context: &InvokeContext,
342 ) -> Result<Vec<Pubkey>, EbpfError> {
343 let mut signers = Vec::new();
344 if signers_seeds_len > 0 {
345 let signers_seeds = translate_slice::<&[&[u8]]>(
346 memory_mapping,
347 signers_seeds_addr,
348 signers_seeds_len,
349 invoke_context.get_check_aligned(),
350 invoke_context.get_check_size(),
351 )?;
352 if signers_seeds.len() > MAX_SIGNERS {
353 return Err(SyscallError::TooManySigners.into());
354 }
355 for signer_seeds in signers_seeds.iter() {
356 let untranslated_seeds = translate_slice::<&[u8]>(
357 memory_mapping,
358 signer_seeds.as_ptr() as *const _ as u64,
359 signer_seeds.len() as u64,
360 invoke_context.get_check_aligned(),
361 invoke_context.get_check_size(),
362 )?;
363 if untranslated_seeds.len() > MAX_SEEDS {
364 return Err(SyscallError::InstructionError(
365 InstructionError::MaxSeedLengthExceeded,
366 )
367 .into());
368 }
369 let seeds = untranslated_seeds
370 .iter()
371 .map(|untranslated_seed| {
372 translate_slice::<u8>(
373 memory_mapping,
374 untranslated_seed.as_ptr() as *const _ as u64,
375 untranslated_seed.len() as u64,
376 invoke_context.get_check_aligned(),
377 invoke_context.get_check_size(),
378 )
379 })
380 .collect::<Result<Vec<_>, EbpfError>>()?;
381 let signer = Pubkey::create_program_address(&seeds, program_id)
382 .map_err(SyscallError::BadSeeds)?;
383 signers.push(signer);
384 }
385 Ok(signers)
386 } else {
387 Ok(vec![])
388 }
389 }
390}
391
392#[derive(Debug)]
394#[repr(C)]
395struct SolInstruction {
396 program_id_addr: u64,
397 accounts_addr: u64,
398 accounts_len: u64,
399 data_addr: u64,
400 data_len: u64,
401}
402
403#[derive(Debug)]
405#[repr(C)]
406struct SolAccountMeta {
407 pubkey_addr: u64,
408 is_writable: bool,
409 is_signer: bool,
410}
411
412#[derive(Debug)]
414#[repr(C)]
415struct SolAccountInfo {
416 key_addr: u64,
417 lamports_addr: u64,
418 data_len: u64,
419 data_addr: u64,
420 owner_addr: u64,
421 rent_epoch: u64,
422 #[allow(dead_code)]
423 is_signer: bool,
424 #[allow(dead_code)]
425 is_writable: bool,
426 executable: bool,
427}
428
429#[derive(Debug)]
431#[repr(C)]
432struct SolSignerSeedC {
433 addr: u64,
434 len: u64,
435}
436
437#[derive(Debug)]
439#[repr(C)]
440struct SolSignerSeedsC {
441 addr: u64,
442 len: u64,
443}
444
445declare_syscall!(
446 SyscallInvokeSignedC,
448 fn inner_call(
449 invoke_context: &mut InvokeContext,
450 instruction_addr: u64,
451 account_infos_addr: u64,
452 account_infos_len: u64,
453 signers_seeds_addr: u64,
454 signers_seeds_len: u64,
455 memory_mapping: &mut MemoryMapping,
456 ) -> Result<u64, EbpfError> {
457 cpi_common::<Self>(
458 invoke_context,
459 instruction_addr,
460 account_infos_addr,
461 account_infos_len,
462 signers_seeds_addr,
463 signers_seeds_len,
464 memory_mapping,
465 )
466 }
467);
468
469impl SyscallInvokeSigned for SyscallInvokeSignedC {
470 fn translate_instruction(
471 addr: u64,
472 memory_mapping: &mut MemoryMapping,
473 invoke_context: &mut InvokeContext,
474 ) -> Result<StableInstruction, EbpfError> {
475 let ix_c = translate_type::<SolInstruction>(
476 memory_mapping,
477 addr,
478 invoke_context.get_check_aligned(),
479 )?;
480
481 check_instruction_size(
482 ix_c.accounts_len as usize,
483 ix_c.data_len as usize,
484 invoke_context,
485 )?;
486 let program_id = translate_type::<Pubkey>(
487 memory_mapping,
488 ix_c.program_id_addr,
489 invoke_context.get_check_aligned(),
490 )?;
491 let meta_cs = translate_slice::<SolAccountMeta>(
492 memory_mapping,
493 ix_c.accounts_addr,
494 ix_c.accounts_len,
495 invoke_context.get_check_aligned(),
496 invoke_context.get_check_size(),
497 )?;
498
499 let ix_data_len = ix_c.data_len;
500 if invoke_context
501 .feature_set
502 .is_active(&feature_set::loosen_cpi_size_restriction::id())
503 {
504 consume_compute_meter(
505 invoke_context,
506 (ix_data_len)
507 .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
508 )?;
509 }
510
511 let data = translate_slice::<u8>(
512 memory_mapping,
513 ix_c.data_addr,
514 ix_data_len,
515 invoke_context.get_check_aligned(),
516 invoke_context.get_check_size(),
517 )?
518 .to_vec();
519 let accounts = meta_cs
520 .iter()
521 .map(|meta_c| {
522 let pubkey = translate_type::<Pubkey>(
523 memory_mapping,
524 meta_c.pubkey_addr,
525 invoke_context.get_check_aligned(),
526 )?;
527 Ok(AccountMeta {
528 pubkey: *pubkey,
529 is_signer: meta_c.is_signer,
530 is_writable: meta_c.is_writable,
531 })
532 })
533 .collect::<Result<Vec<AccountMeta>, EbpfError>>()?;
534
535 Ok(StableInstruction {
536 accounts: accounts.into(),
537 data: data.into(),
538 program_id: *program_id,
539 })
540 }
541
542 fn translate_accounts<'a>(
543 instruction_accounts: &[InstructionAccount],
544 program_indices: &[IndexOfAccount],
545 account_infos_addr: u64,
546 account_infos_len: u64,
547 memory_mapping: &mut MemoryMapping,
548 invoke_context: &mut InvokeContext,
549 ) -> Result<TranslatedAccounts<'a>, EbpfError> {
550 let (account_infos, account_info_keys) = translate_account_infos(
551 account_infos_addr,
552 account_infos_len,
553 |account_info: &SolAccountInfo| account_info.key_addr,
554 memory_mapping,
555 invoke_context,
556 )?;
557
558 translate_and_update_accounts(
559 instruction_accounts,
560 program_indices,
561 &account_info_keys,
562 account_infos,
563 account_infos_addr,
564 invoke_context,
565 memory_mapping,
566 CallerAccount::from_sol_account_info,
567 )
568 }
569
570 fn translate_signers(
571 program_id: &Pubkey,
572 signers_seeds_addr: u64,
573 signers_seeds_len: u64,
574 memory_mapping: &mut MemoryMapping,
575 invoke_context: &InvokeContext,
576 ) -> Result<Vec<Pubkey>, EbpfError> {
577 if signers_seeds_len > 0 {
578 let signers_seeds = translate_slice::<SolSignerSeedsC>(
579 memory_mapping,
580 signers_seeds_addr,
581 signers_seeds_len,
582 invoke_context.get_check_aligned(),
583 invoke_context.get_check_size(),
584 )?;
585 if signers_seeds.len() > MAX_SIGNERS {
586 return Err(SyscallError::TooManySigners.into());
587 }
588 Ok(signers_seeds
589 .iter()
590 .map(|signer_seeds| {
591 let seeds = translate_slice::<SolSignerSeedC>(
592 memory_mapping,
593 signer_seeds.addr,
594 signer_seeds.len,
595 invoke_context.get_check_aligned(),
596 invoke_context.get_check_size(),
597 )?;
598 if seeds.len() > MAX_SEEDS {
599 return Err(SyscallError::InstructionError(
600 InstructionError::MaxSeedLengthExceeded,
601 )
602 .into());
603 }
604 let seeds_bytes = seeds
605 .iter()
606 .map(|seed| {
607 translate_slice::<u8>(
608 memory_mapping,
609 seed.addr,
610 seed.len,
611 invoke_context.get_check_aligned(),
612 invoke_context.get_check_size(),
613 )
614 })
615 .collect::<Result<Vec<_>, EbpfError>>()?;
616 Pubkey::create_program_address(&seeds_bytes, program_id)
617 .map_err(|err| SyscallError::BadSeeds(err).into())
618 })
619 .collect::<Result<Vec<_>, EbpfError>>()?)
620 } else {
621 Ok(vec![])
622 }
623 }
624}
625
626fn translate_account_infos<'a, T, F>(
627 account_infos_addr: u64,
628 account_infos_len: u64,
629 key_addr: F,
630 memory_mapping: &mut MemoryMapping,
631 invoke_context: &mut InvokeContext,
632) -> Result<(&'a [T], Vec<&'a Pubkey>), EbpfError>
633where
634 F: Fn(&T) -> u64,
635{
636 let account_infos = translate_slice::<T>(
637 memory_mapping,
638 account_infos_addr,
639 account_infos_len,
640 invoke_context.get_check_aligned(),
641 invoke_context.get_check_size(),
642 )?;
643 check_account_infos(account_infos.len(), invoke_context)?;
644 let account_info_keys = account_infos
645 .iter()
646 .map(|account_info| {
647 translate_type::<Pubkey>(
648 memory_mapping,
649 key_addr(account_info),
650 invoke_context.get_check_aligned(),
651 )
652 })
653 .collect::<Result<Vec<_>, EbpfError>>()?;
654
655 Ok((account_infos, account_info_keys))
656}
657
658fn translate_and_update_accounts<'a, T, F>(
661 instruction_accounts: &[InstructionAccount],
662 program_indices: &[IndexOfAccount],
663 account_info_keys: &[&Pubkey],
664 account_infos: &[T],
665 account_infos_addr: u64,
666 invoke_context: &mut InvokeContext,
667 memory_mapping: &MemoryMapping,
668 do_translate: F,
669) -> Result<TranslatedAccounts<'a>, EbpfError>
670where
671 F: Fn(&InvokeContext, &MemoryMapping, u64, &T, usize) -> Result<CallerAccount<'a>, EbpfError>,
672{
673 let transaction_context = &invoke_context.transaction_context;
674 let instruction_context = transaction_context
675 .get_current_instruction_context()
676 .map_err(SyscallError::InstructionError)?;
677 let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
678
679 let program_account_index = program_indices
680 .last()
681 .ok_or(SyscallError::InstructionError(
682 InstructionError::MissingAccount,
683 ))?;
684 accounts.push((*program_account_index, None));
685
686 let orig_data_lens = invoke_context.get_orig_account_lengths().unwrap();
689
690 for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate()
691 {
692 if instruction_account_index as IndexOfAccount != instruction_account.index_in_callee {
693 continue; }
695
696 let callee_account = instruction_context
697 .try_borrow_instruction_account(
698 transaction_context,
699 instruction_account.index_in_caller,
700 )
701 .map_err(SyscallError::InstructionError)?;
702 let account_key = invoke_context
703 .transaction_context
704 .get_key_of_account_at_index(instruction_account.index_in_transaction)
705 .map_err(SyscallError::InstructionError)?;
706
707 if callee_account.is_executable() {
708 consume_compute_meter(
710 invoke_context,
711 (callee_account.get_data().len() as u64)
712 .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
713 )?;
714
715 accounts.push((instruction_account.index_in_caller, None));
716 } else if let Some(caller_account_index) =
717 account_info_keys.iter().position(|key| *key == account_key)
718 {
719 let original_data_len = *orig_data_lens
720 .get(instruction_account.index_in_caller as usize)
721 .ok_or_else(|| {
722 ic_msg!(
723 invoke_context,
724 "Internal error: index mismatch for account {}",
725 account_key
726 );
727 SyscallError::InstructionError(InstructionError::MissingAccount)
728 })?;
729
730 let caller_account =
732 do_translate(
733 invoke_context,
734 memory_mapping,
735 account_infos_addr.saturating_add(
736 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
737 ),
738 account_infos
739 .get(caller_account_index)
740 .ok_or(SyscallError::InvalidLength)?,
741 original_data_len,
742 )?;
743
744 update_callee_account(invoke_context, &caller_account, callee_account)?;
749
750 let caller_account = if instruction_account.is_writable {
751 Some(caller_account)
752 } else {
753 None
754 };
755 accounts.push((instruction_account.index_in_caller, caller_account));
756 } else {
757 ic_msg!(
758 invoke_context,
759 "Instruction references an unknown account {}",
760 account_key
761 );
762 return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
763 }
764 }
765
766 Ok(accounts)
767}
768
769fn check_instruction_size(
770 num_accounts: usize,
771 data_len: usize,
772 invoke_context: &mut InvokeContext,
773) -> Result<(), EbpfError> {
774 if invoke_context
775 .feature_set
776 .is_active(&feature_set::loosen_cpi_size_restriction::id())
777 {
778 let data_len = data_len as u64;
779 let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN;
780 if data_len > max_data_len {
781 return Err(SyscallError::MaxInstructionDataLenExceeded {
782 data_len,
783 max_data_len,
784 }
785 .into());
786 }
787
788 let num_accounts = num_accounts as u64;
789 let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64;
790 if num_accounts > max_accounts {
791 return Err(SyscallError::MaxInstructionAccountsExceeded {
792 num_accounts,
793 max_accounts,
794 }
795 .into());
796 }
797 } else {
798 let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size;
799 let size = num_accounts
800 .saturating_mul(size_of::<AccountMeta>())
801 .saturating_add(data_len);
802 if size > max_size {
803 return Err(SyscallError::InstructionTooLarge(size, max_size).into());
804 }
805 }
806 Ok(())
807}
808
809fn check_account_infos(
810 num_account_infos: usize,
811 invoke_context: &mut InvokeContext,
812) -> Result<(), EbpfError> {
813 if invoke_context
814 .feature_set
815 .is_active(&feature_set::loosen_cpi_size_restriction::id())
816 {
817 let max_cpi_account_infos = if invoke_context
818 .feature_set
819 .is_active(&feature_set::increase_tx_account_lock_limit::id())
820 {
821 MAX_CPI_ACCOUNT_INFOS
822 } else {
823 64
824 };
825 let num_account_infos = num_account_infos as u64;
826 let max_account_infos = max_cpi_account_infos as u64;
827 if num_account_infos > max_account_infos {
828 return Err(SyscallError::MaxInstructionAccountInfosExceeded {
829 num_account_infos,
830 max_account_infos,
831 }
832 .into());
833 }
834 } else {
835 let adjusted_len = num_account_infos.saturating_mul(size_of::<Pubkey>());
836
837 if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size {
838 return Err(SyscallError::TooManyAccounts.into());
841 };
842 }
843 Ok(())
844}
845
846fn check_authorized_program(
847 program_id: &Pubkey,
848 instruction_data: &[u8],
849 invoke_context: &InvokeContext,
850) -> Result<(), EbpfError> {
851 if native_loader::check_id(program_id)
852 || bpf_loader::check_id(program_id)
853 || bpf_loader_deprecated::check_id(program_id)
854 || (bpf_loader_upgradeable::check_id(program_id)
855 && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
856 || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
857 || (invoke_context
858 .feature_set
859 .is_active(&enable_bpf_loader_set_authority_checked_ix::id())
860 && bpf_loader_upgradeable::is_set_authority_checked_instruction(
861 instruction_data,
862 ))
863 || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
864 || is_precompile(program_id, |feature_id: &Pubkey| {
865 invoke_context.feature_set.is_active(feature_id)
866 })
867 {
868 return Err(SyscallError::ProgramNotSupported(*program_id).into());
869 }
870 Ok(())
871}
872
873fn cpi_common<S: SyscallInvokeSigned>(
875 invoke_context: &mut InvokeContext,
876 instruction_addr: u64,
877 account_infos_addr: u64,
878 account_infos_len: u64,
879 signers_seeds_addr: u64,
880 signers_seeds_len: u64,
881 memory_mapping: &mut MemoryMapping,
882) -> Result<u64, EbpfError> {
883 consume_compute_meter(
888 invoke_context,
889 invoke_context.get_compute_budget().invoke_units,
890 )?;
891
892 let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?;
893 let transaction_context = &invoke_context.transaction_context;
894 let instruction_context = transaction_context
895 .get_current_instruction_context()
896 .map_err(SyscallError::InstructionError)?;
897 let caller_program_id = instruction_context
898 .get_last_program_key(transaction_context)
899 .map_err(SyscallError::InstructionError)?;
900 let signers = S::translate_signers(
901 caller_program_id,
902 signers_seeds_addr,
903 signers_seeds_len,
904 memory_mapping,
905 invoke_context,
906 )?;
907 let (instruction_accounts, program_indices) = invoke_context
908 .prepare_instruction(&instruction, &signers)
909 .map_err(SyscallError::InstructionError)?;
910 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
911 let mut accounts = S::translate_accounts(
912 &instruction_accounts,
913 &program_indices,
914 account_infos_addr,
915 account_infos_len,
916 memory_mapping,
917 invoke_context,
918 )?;
919
920 let mut compute_units_consumed = 0;
922 invoke_context
923 .process_instruction(
924 &instruction.data,
925 &instruction_accounts,
926 &program_indices,
927 &mut compute_units_consumed,
928 &mut ExecuteTimings::default(),
929 )
930 .map_err(SyscallError::InstructionError)?;
931
932 let transaction_context = &invoke_context.transaction_context;
934 let instruction_context = transaction_context
935 .get_current_instruction_context()
936 .map_err(SyscallError::InstructionError)?;
937
938 for (index_in_caller, caller_account) in accounts.iter_mut() {
942 if let Some(caller_account) = caller_account {
943 let callee_account = instruction_context
944 .try_borrow_instruction_account(transaction_context, *index_in_caller)
945 .map_err(SyscallError::InstructionError)?;
946 update_caller_account(
947 invoke_context,
948 memory_mapping,
949 caller_account,
950 &callee_account,
951 )?;
952 }
953 }
954
955 Ok(SUCCESS)
956}
957
958fn update_callee_account(
967 invoke_context: &InvokeContext,
968 caller_account: &CallerAccount,
969 mut callee_account: BorrowedAccount<'_>,
970) -> Result<(), EbpfError> {
971 let is_disable_cpi_setting_executable_and_rent_epoch_active = invoke_context
972 .feature_set
973 .is_active(&disable_cpi_setting_executable_and_rent_epoch::id());
974
975 if callee_account.get_lamports() != *caller_account.lamports {
976 callee_account
977 .set_lamports(*caller_account.lamports)
978 .map_err(SyscallError::InstructionError)?;
979 }
980
981 match callee_account
983 .can_data_be_resized(caller_account.data.len())
984 .and_then(|_| callee_account.can_data_be_changed())
985 {
986 Ok(()) => callee_account
987 .set_data_from_slice(caller_account.data)
988 .map_err(SyscallError::InstructionError)?,
989 Err(err) if callee_account.get_data() != caller_account.data => {
990 return Err(EbpfError::UserError(Box::new(BpfError::SyscallError(
991 SyscallError::InstructionError(err),
992 ))));
993 }
994 _ => {}
995 }
996
997 if !is_disable_cpi_setting_executable_and_rent_epoch_active
998 && callee_account.is_executable() != caller_account.executable
999 {
1000 callee_account
1001 .set_executable(caller_account.executable)
1002 .map_err(SyscallError::InstructionError)?;
1003 }
1004
1005 if callee_account.get_owner() != caller_account.owner {
1007 callee_account
1008 .set_owner(caller_account.owner.as_ref())
1009 .map_err(SyscallError::InstructionError)?;
1010 }
1011
1012 let index_in_transaction = callee_account.get_index_in_transaction();
1015 drop(callee_account);
1016 let callee_account = invoke_context
1017 .transaction_context
1018 .get_account_at_index(index_in_transaction)
1019 .map_err(SyscallError::InstructionError)?;
1020 if !is_disable_cpi_setting_executable_and_rent_epoch_active
1021 && callee_account.borrow().rent_epoch() != caller_account.rent_epoch
1022 {
1023 if invoke_context
1024 .feature_set
1025 .is_active(&enable_early_verification_of_account_modifications::id())
1026 {
1027 return Err(SyscallError::InstructionError(InstructionError::RentEpochModified).into());
1028 } else {
1029 callee_account
1030 .borrow_mut()
1031 .set_rent_epoch(caller_account.rent_epoch);
1032 }
1033 }
1034
1035 Ok(())
1036}
1037
1038fn update_caller_account(
1047 invoke_context: &InvokeContext,
1048 memory_mapping: &MemoryMapping,
1049 caller_account: &mut CallerAccount,
1050 callee_account: &BorrowedAccount<'_>,
1051) -> Result<(), EbpfError> {
1052 *caller_account.lamports = callee_account.get_lamports();
1053 *caller_account.owner = *callee_account.get_owner();
1054 let new_len = callee_account.get_data().len();
1055 if caller_account.data.len() != new_len {
1056 let data_overflow = new_len
1057 > caller_account
1058 .original_data_len
1059 .saturating_add(MAX_PERMITTED_DATA_INCREASE);
1060 if data_overflow {
1061 ic_msg!(
1062 invoke_context,
1063 "Account data size realloc limited to {} in inner instructions",
1064 MAX_PERMITTED_DATA_INCREASE
1065 );
1066 return Err(SyscallError::InstructionError(InstructionError::InvalidRealloc).into());
1067 }
1068 if new_len < caller_account.data.len() {
1069 caller_account
1070 .data
1071 .get_mut(new_len..)
1072 .ok_or(SyscallError::InstructionError(
1073 InstructionError::AccountDataTooSmall,
1074 ))?
1075 .fill(0);
1076 }
1077 caller_account.data = translate_slice_mut::<u8>(
1078 memory_mapping,
1079 caller_account.vm_data_addr,
1080 new_len as u64,
1081 false, invoke_context.get_check_size(),
1083 )?;
1084 *caller_account.ref_to_len_in_vm = new_len as u64;
1086
1087 if invoke_context
1089 .feature_set
1090 .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
1091 {
1092 let serialized_len_ptr = translate_type_mut::<u64>(
1093 memory_mapping,
1094 caller_account
1095 .vm_data_addr
1096 .saturating_sub(std::mem::size_of::<u64>() as u64),
1097 invoke_context.get_check_aligned(),
1098 )?;
1099 *serialized_len_ptr = new_len as u64;
1100 } else {
1101 unsafe {
1102 *caller_account.serialized_len_ptr = new_len as u64;
1103 }
1104 }
1105 }
1106 let to_slice = &mut caller_account.data;
1107 let from_slice = callee_account
1108 .get_data()
1109 .get(0..new_len)
1110 .ok_or(SyscallError::InvalidLength)?;
1111 if to_slice.len() != from_slice.len() {
1112 return Err(SyscallError::InstructionError(InstructionError::AccountDataTooSmall).into());
1113 }
1114 to_slice.copy_from_slice(from_slice);
1115
1116 Ok(())
1117}
1118
1119#[allow(clippy::indexing_slicing)]
1120#[allow(clippy::integer_arithmetic)]
1121#[cfg(test)]
1122mod tests {
1123 use {
1124 super::*,
1125 crate::allocator_bump::BpfAllocator,
1126 solana_rbpf::{
1127 aligned_memory::AlignedMemory, ebpf::MM_INPUT_START, memory_region::MemoryRegion,
1128 vm::Config,
1129 },
1130 solana_sdk::{
1131 account::{Account, AccountSharedData},
1132 clock::Epoch,
1133 instruction::Instruction,
1134 rent::Rent,
1135 transaction_context::{TransactionAccount, TransactionContext},
1136 },
1137 std::{
1138 cell::{Cell, RefCell},
1139 mem, ptr,
1140 rc::Rc,
1141 slice,
1142 },
1143 };
1144
1145 macro_rules! mock_invoke_context {
1146 ($invoke_context:ident,
1147 $transaction_context:ident,
1148 $instruction_data:expr,
1149 $transaction_accounts:expr,
1150 $program_accounts:expr,
1151 $instruction_accounts:expr) => {
1152 let program_accounts = $program_accounts;
1153 let instruction_data = $instruction_data;
1154 let instruction_accounts = $instruction_accounts
1155 .iter()
1156 .enumerate()
1157 .map(
1158 |(index_in_callee, index_in_transaction)| InstructionAccount {
1159 index_in_transaction: *index_in_transaction as IndexOfAccount,
1160 index_in_caller: *index_in_transaction as IndexOfAccount,
1161 index_in_callee: index_in_callee as IndexOfAccount,
1162 is_signer: false,
1163 is_writable: $transaction_accounts[*index_in_transaction as usize].2,
1164 },
1165 )
1166 .collect::<Vec<_>>();
1167 let transaction_accounts = $transaction_accounts
1168 .into_iter()
1169 .map(|a| (a.0, a.1))
1170 .collect::<Vec<TransactionAccount>>();
1171
1172 let program_accounts = program_accounts;
1173 let mut $transaction_context =
1174 TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
1175
1176 let mut $invoke_context = InvokeContext::new_mock(&mut $transaction_context, &[]);
1177 $invoke_context
1178 .transaction_context
1179 .get_next_instruction_context()
1180 .unwrap()
1181 .configure(program_accounts, &instruction_accounts, instruction_data);
1182 $invoke_context.push().unwrap();
1183 };
1184 }
1185
1186 #[test]
1187 fn test_translate_instruction() {
1188 let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1189 mock_invoke_context!(
1190 invoke_context,
1191 transaction_context,
1192 b"instruction data",
1193 transaction_accounts,
1194 &[0],
1195 &[1]
1196 );
1197
1198 let program_id = Pubkey::new_unique();
1199 let accounts = vec![AccountMeta {
1200 pubkey: Pubkey::new_unique(),
1201 is_signer: true,
1202 is_writable: false,
1203 }];
1204 let data = b"ins data".to_vec();
1205 let vm_addr = MM_INPUT_START;
1206 let (_mem, region) = MockInstruction {
1207 program_id,
1208 accounts: accounts.clone(),
1209 data: data.clone(),
1210 }
1211 .into_region(vm_addr);
1212
1213 let config = Config {
1214 aligned_memory_mapping: false,
1215 ..Config::default()
1216 };
1217 let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1218
1219 let ins = SyscallInvokeSignedRust::translate_instruction(
1220 vm_addr,
1221 &mut memory_mapping,
1222 &mut invoke_context,
1223 )
1224 .unwrap();
1225 assert_eq!(ins.program_id, program_id);
1226 assert_eq!(ins.accounts, accounts);
1227 assert_eq!(ins.data, data);
1228 }
1229
1230 #[test]
1231 fn test_translate_signers() {
1232 let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1233 mock_invoke_context!(
1234 invoke_context,
1235 transaction_context,
1236 b"instruction data",
1237 transaction_accounts,
1238 &[0],
1239 &[1]
1240 );
1241
1242 let program_id = Pubkey::new_unique();
1243 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1244
1245 let vm_addr = MM_INPUT_START;
1246 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1247
1248 let config = Config {
1249 aligned_memory_mapping: false,
1250 ..Config::default()
1251 };
1252 let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1253
1254 let signers = SyscallInvokeSignedRust::translate_signers(
1255 &program_id,
1256 vm_addr,
1257 1,
1258 &mut memory_mapping,
1259 &invoke_context,
1260 )
1261 .unwrap();
1262 assert_eq!(signers[0], derived_key);
1263 }
1264
1265 #[test]
1266 fn test_caller_account_from_account_info() {
1267 let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1268 let account = transaction_accounts[1].1.clone();
1269 mock_invoke_context!(
1270 invoke_context,
1271 transaction_context,
1272 b"instruction data",
1273 transaction_accounts,
1274 &[0],
1275 &[1]
1276 );
1277
1278 let key = Pubkey::new_unique();
1279 let vm_addr = MM_INPUT_START;
1280 let (_mem, region) = MockAccountInfo::new(key, &account).into_region(vm_addr);
1281
1282 let config = Config {
1283 aligned_memory_mapping: false,
1284 ..Config::default()
1285 };
1286 let memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1287
1288 let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1289
1290 let caller_account = CallerAccount::from_account_info(
1291 &invoke_context,
1292 &memory_mapping,
1293 vm_addr,
1294 account_info,
1295 account.data().len(),
1296 )
1297 .unwrap();
1298 assert_eq!(*caller_account.lamports, account.lamports());
1299 assert_eq!(caller_account.owner, account.owner());
1300 assert_eq!(caller_account.original_data_len, account.data().len());
1301 assert_eq!(
1302 *caller_account.ref_to_len_in_vm as usize,
1303 account.data().len()
1304 );
1305 assert_eq!(caller_account.data, account.data());
1306 assert_eq!(caller_account.executable, account.executable());
1307 assert_eq!(caller_account.rent_epoch, account.rent_epoch());
1308 }
1309
1310 #[test]
1311 fn test_update_caller_account_lamports_owner() {
1312 let transaction_accounts = transaction_with_one_instruction_account(vec![]);
1313 let account = transaction_accounts[1].1.clone();
1314 mock_invoke_context!(
1315 invoke_context,
1316 transaction_context,
1317 b"instruction data",
1318 transaction_accounts,
1319 &[0],
1320 &[1]
1321 );
1322
1323 let instruction_context = invoke_context
1324 .transaction_context
1325 .get_current_instruction_context()
1326 .unwrap();
1327
1328 let mut mock_caller_account =
1329 MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1330
1331 let config = Config {
1332 aligned_memory_mapping: false,
1333 ..Config::default()
1334 };
1335 let memory_mapping =
1336 MemoryMapping::new(vec![mock_caller_account.region.clone()], &config).unwrap();
1337
1338 let mut caller_account = mock_caller_account.caller_account();
1339
1340 let mut callee_account = instruction_context
1341 .try_borrow_instruction_account(invoke_context.transaction_context, 0)
1342 .unwrap();
1343
1344 callee_account.set_lamports(42).unwrap();
1345 callee_account
1346 .set_owner(Pubkey::new_unique().as_ref())
1347 .unwrap();
1348
1349 update_caller_account(
1350 &invoke_context,
1351 &memory_mapping,
1352 &mut caller_account,
1353 &callee_account,
1354 )
1355 .unwrap();
1356
1357 assert_eq!(*caller_account.lamports, 42);
1358 assert_eq!(caller_account.owner, callee_account.get_owner());
1359 }
1360
1361 #[test]
1362 fn test_update_caller_account_data() {
1363 let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1364 let account = transaction_accounts[1].1.clone();
1365 let original_data_len = account.data().len();
1366
1367 mock_invoke_context!(
1368 invoke_context,
1369 transaction_context,
1370 b"instruction data",
1371 transaction_accounts,
1372 &[0],
1373 &[1]
1374 );
1375
1376 let instruction_context = invoke_context
1377 .transaction_context
1378 .get_current_instruction_context()
1379 .unwrap();
1380
1381 let mut mock_caller_account = MockCallerAccount::new(
1382 account.lamports(),
1383 *account.owner(),
1384 0xFFFFFFFF00000000,
1385 account.data(),
1386 );
1387
1388 let config = Config {
1389 aligned_memory_mapping: false,
1390 ..Config::default()
1391 };
1392 let memory_mapping =
1393 MemoryMapping::new(vec![mock_caller_account.region.clone()], &config).unwrap();
1394
1395 let data_slice = mock_caller_account.data_slice();
1396 let len_ptr = unsafe {
1397 data_slice
1398 .as_ptr()
1399 .offset(-(mem::size_of::<u64>() as isize))
1400 };
1401 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1402 let mut caller_account = mock_caller_account.caller_account();
1403
1404 let mut callee_account = instruction_context
1405 .try_borrow_instruction_account(invoke_context.transaction_context, 0)
1406 .unwrap();
1407
1408 for (new_value, expected_realloc_size) in [
1409 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1410 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1411 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1412 ] {
1413 assert_eq!(caller_account.data, callee_account.get_data());
1414 callee_account.set_data_from_slice(&new_value).unwrap();
1415
1416 update_caller_account(
1417 &invoke_context,
1418 &memory_mapping,
1419 &mut caller_account,
1420 &callee_account,
1421 )
1422 .unwrap();
1423
1424 let data_len = callee_account.get_data().len();
1425 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
1426 assert_eq!(data_len, serialized_len());
1427 assert_eq!(data_len, caller_account.data.len());
1428 assert_eq!(callee_account.get_data(), &caller_account.data[..data_len]);
1429 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1430 assert!(is_zeroed(&data_slice[data_len..]));
1431 }
1432
1433 callee_account
1434 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1435 .unwrap();
1436 update_caller_account(
1437 &invoke_context,
1438 &memory_mapping,
1439 &mut caller_account,
1440 &callee_account,
1441 )
1442 .unwrap();
1443 let data_len = callee_account.get_data().len();
1444 assert_eq!(data_slice[data_len..].len(), 0);
1445 assert!(is_zeroed(&data_slice[data_len..]));
1446
1447 callee_account
1448 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1449 .unwrap();
1450 assert!(matches!(
1451 update_caller_account(
1452 &invoke_context,
1453 &memory_mapping,
1454 &mut caller_account,
1455 &callee_account,
1456 ),
1457 Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(
1458 SyscallError::InstructionError(InstructionError::InvalidRealloc)
1459 )
1460 ));
1461 }
1462
1463 macro_rules! borrow_instruction_account {
1464 ($invoke_context:expr, $index:expr) => {{
1465 let instruction_context = $invoke_context
1466 .transaction_context
1467 .get_current_instruction_context()
1468 .unwrap();
1469 instruction_context
1470 .try_borrow_instruction_account($invoke_context.transaction_context, $index)
1471 .unwrap()
1472 }};
1473 }
1474
1475 #[test]
1476 fn test_update_callee_account_lamports_owner() {
1477 let transaction_accounts = transaction_with_one_instruction_account(vec![]);
1478 let account = transaction_accounts[1].1.clone();
1479
1480 mock_invoke_context!(
1481 invoke_context,
1482 transaction_context,
1483 b"instruction data",
1484 transaction_accounts,
1485 &[0],
1486 &[1]
1487 );
1488
1489 let mut mock_caller_account =
1490 MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1491
1492 let caller_account = mock_caller_account.caller_account();
1493
1494 let callee_account = borrow_instruction_account!(invoke_context, 0);
1495
1496 *caller_account.lamports = 42;
1497 *caller_account.owner = Pubkey::new_unique();
1498
1499 update_callee_account(&invoke_context, &caller_account, callee_account).unwrap();
1500
1501 let callee_account = borrow_instruction_account!(invoke_context, 0);
1502 assert_eq!(callee_account.get_lamports(), 42);
1503 assert_eq!(caller_account.owner, callee_account.get_owner());
1504 }
1505
1506 #[test]
1507 fn test_update_callee_account_data() {
1508 let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1509 let account = transaction_accounts[1].1.clone();
1510
1511 mock_invoke_context!(
1512 invoke_context,
1513 transaction_context,
1514 b"instruction data",
1515 transaction_accounts,
1516 &[0],
1517 &[1]
1518 );
1519
1520 let mut mock_caller_account =
1521 MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1522
1523 let mut caller_account = mock_caller_account.caller_account();
1524
1525 let callee_account = borrow_instruction_account!(invoke_context, 0);
1526
1527 let mut data = b"foo".to_vec();
1528 caller_account.data = &mut data;
1529
1530 update_callee_account(&invoke_context, &caller_account, callee_account).unwrap();
1531
1532 let callee_account = borrow_instruction_account!(invoke_context, 0);
1533 assert_eq!(callee_account.get_data(), caller_account.data);
1534 }
1535
1536 #[test]
1537 fn test_translate_accounts_rust() {
1538 let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1539 let account = transaction_accounts[1].1.clone();
1540 let key = transaction_accounts[1].0;
1541 let original_data_len = account.data().len();
1542
1543 mock_invoke_context!(
1544 invoke_context,
1545 transaction_context,
1546 b"instruction data",
1547 transaction_accounts,
1548 &[0],
1549 &[1, 1]
1550 );
1551 invoke_context
1552 .set_syscall_context(
1553 true,
1554 true,
1555 vec![original_data_len],
1556 Rc::new(RefCell::new(BpfAllocator::new(
1557 AlignedMemory::with_capacity(0),
1558 0,
1559 ))),
1560 )
1561 .unwrap();
1562
1563 let vm_addr = MM_INPUT_START;
1564 let (_mem, region) = MockAccountInfo::new(key, &account).into_region(vm_addr);
1565
1566 let config = Config {
1567 aligned_memory_mapping: false,
1568 ..Config::default()
1569 };
1570 let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1571
1572 let accounts = SyscallInvokeSignedRust::translate_accounts(
1573 &[
1574 InstructionAccount {
1575 index_in_transaction: 1,
1576 index_in_caller: 0,
1577 index_in_callee: 0,
1578 is_signer: false,
1579 is_writable: true,
1580 },
1581 InstructionAccount {
1582 index_in_transaction: 1,
1583 index_in_caller: 0,
1584 index_in_callee: 0,
1585 is_signer: false,
1586 is_writable: true,
1587 },
1588 ],
1589 &[0],
1590 vm_addr,
1591 1,
1592 &mut memory_mapping,
1593 &mut invoke_context,
1594 )
1595 .unwrap();
1596 assert_eq!(accounts.len(), 2);
1597 assert!(accounts[0].1.is_none());
1598 let caller_account = accounts[1].1.as_ref().unwrap();
1599 assert_eq!(caller_account.data, account.data());
1600 assert_eq!(caller_account.original_data_len, original_data_len);
1601 }
1602
1603 pub type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1604
1605 struct MockCallerAccount {
1606 lamports: u64,
1607 owner: Pubkey,
1608 vm_addr: u64,
1609 data: Vec<u8>,
1610 len: u64,
1611 region: MemoryRegion,
1612 }
1613
1614 impl MockCallerAccount {
1615 fn new(lamports: u64, owner: Pubkey, vm_addr: u64, data: &[u8]) -> MockCallerAccount {
1616 let mut d = vec![0; mem::size_of::<u64>() + data.len() + MAX_PERMITTED_DATA_INCREASE];
1619 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1620 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1621
1622 let region = MemoryRegion::new_writable(d.as_mut_slice(), vm_addr);
1624
1625 d.truncate(mem::size_of::<u64>() + data.len());
1627
1628 MockCallerAccount {
1629 lamports,
1630 owner,
1631 vm_addr,
1632 data: d,
1633 len: data.len() as u64,
1634 region,
1635 }
1636 }
1637
1638 fn data_slice<'a>(&self) -> &'a [u8] {
1639 unsafe {
1641 slice::from_raw_parts(
1642 self.data[mem::size_of::<u64>()..].as_ptr(),
1643 self.data.capacity() - mem::size_of::<u64>(),
1644 )
1645 }
1646 }
1647
1648 fn caller_account(&mut self) -> CallerAccount<'_> {
1649 let data = &mut self.data[mem::size_of::<u64>()..];
1650 CallerAccount {
1651 lamports: &mut self.lamports,
1652 owner: &mut self.owner,
1653 original_data_len: data.len(),
1654 data,
1655 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1656 ref_to_len_in_vm: &mut self.len,
1657 serialized_len_ptr: std::ptr::null_mut(),
1658 executable: false,
1659 rent_epoch: 0,
1660 }
1661 }
1662 }
1663
1664 fn transaction_with_one_instruction_account(data: Vec<u8>) -> Vec<TestTransactionAccount> {
1665 let program_id = Pubkey::new_unique();
1666 let account = AccountSharedData::from(Account {
1667 lamports: 1,
1668 data,
1669 owner: program_id,
1670 executable: false,
1671 rent_epoch: 100,
1672 });
1673 vec![
1674 (
1675 program_id,
1676 AccountSharedData::from(Account {
1677 lamports: 0,
1678 data: vec![],
1679 owner: bpf_loader::id(),
1680 executable: true,
1681 rent_epoch: 0,
1682 }),
1683 false,
1684 ),
1685 (Pubkey::new_unique(), account, true),
1686 ]
1687 }
1688
1689 struct MockInstruction {
1690 program_id: Pubkey,
1691 accounts: Vec<AccountMeta>,
1692 data: Vec<u8>,
1693 }
1694
1695 impl MockInstruction {
1696 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1697 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1698
1699 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1700
1701 let mut data = vec![0; size];
1702
1703 let vm_addr = vm_addr as usize;
1704 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1705 let data_addr = accounts_addr + accounts_len;
1706
1707 let ins = Instruction {
1708 program_id: self.program_id,
1709 accounts: unsafe {
1710 Vec::from_raw_parts(
1711 accounts_addr as *mut _,
1712 self.accounts.len(),
1713 self.accounts.len(),
1714 )
1715 },
1716 data: unsafe {
1717 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1718 },
1719 };
1720 let ins = StableInstruction::from(ins);
1721
1722 unsafe {
1723 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1724 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1725 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1726 );
1727 data[data_addr - vm_addr..].copy_from_slice(&self.data);
1728 }
1729
1730 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1731 (data, region)
1732 }
1733 }
1734
1735 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1736 let slice_size = mem::size_of::<&[()]>();
1737 let size = signers
1738 .iter()
1739 .fold(slice_size, |size, signer| size + slice_size + signer.len());
1740
1741 let vm_addr = vm_addr as usize;
1742 let mut slices_addr = vm_addr + slice_size;
1743
1744 let mut data = vec![0; size];
1745 unsafe {
1746 ptr::write_unaligned(
1747 data.as_mut_ptr().cast(),
1748 slice::from_raw_parts::<&[&[u8]]>(slices_addr as *const _, signers.len()),
1749 );
1750 }
1751
1752 let mut signers_addr = slices_addr + signers.len() * slice_size;
1753
1754 for signer in signers {
1755 unsafe {
1756 ptr::write_unaligned(
1757 (data.as_mut_ptr() as usize + slices_addr - vm_addr) as *mut _,
1758 slice::from_raw_parts::<&[u8]>(signers_addr as *const _, signer.len()),
1759 );
1760 }
1761 slices_addr += slice_size;
1762 signers_addr += signer.len();
1763 }
1764
1765 let slices_addr = vm_addr + slice_size;
1766 let mut signers_addr = slices_addr + signers.len() * slice_size;
1767 for signer in signers {
1768 data[signers_addr - vm_addr..][..signer.len()].copy_from_slice(signer);
1769 signers_addr += signer.len();
1770 }
1771
1772 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1773 (data, region)
1774 }
1775
1776 struct MockAccountInfo<'a> {
1777 key: Pubkey,
1778 is_signer: bool,
1779 is_writable: bool,
1780 lamports: u64,
1781 data: &'a [u8],
1782 owner: Pubkey,
1783 executable: bool,
1784 rent_epoch: Epoch,
1785 }
1786
1787 impl<'a> MockAccountInfo<'a> {
1788 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
1789 MockAccountInfo {
1790 key,
1791 is_signer: false,
1792 is_writable: false,
1793 lamports: account.lamports(),
1794 data: account.data(),
1795 owner: *account.owner(),
1796 executable: account.executable(),
1797 rent_epoch: account.rent_epoch(),
1798 }
1799 }
1800
1801 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1802 let size = mem::size_of::<AccountInfo>()
1803 + mem::size_of::<Pubkey>() * 2
1804 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1805 + mem::size_of::<u64>()
1806 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1807 + self.data.len();
1808 let mut data = vec![0; size];
1809
1810 let vm_addr = vm_addr as usize;
1811 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1812 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1813 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1814 let owner_addr = lamports_addr + mem::size_of::<u64>();
1815 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1816 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1817
1818 let info = AccountInfo {
1819 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1820 is_signer: self.is_signer,
1821 is_writable: self.is_writable,
1822 lamports: unsafe {
1823 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1824 },
1825 data: unsafe {
1826 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1827 },
1828 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1829 executable: self.executable,
1830 rent_epoch: self.rent_epoch,
1831 };
1832
1833 unsafe {
1834 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1835 ptr::write_unaligned(
1836 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1837 self.key,
1838 );
1839 ptr::write_unaligned(
1840 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1841 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1842 );
1843 ptr::write_unaligned(
1844 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1845 self.lamports,
1846 );
1847 ptr::write_unaligned(
1848 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1849 self.owner,
1850 );
1851 ptr::write_unaligned(
1852 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1853 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1854 data_addr as *mut u8,
1855 self.data.len(),
1856 ))),
1857 );
1858 data[data_addr - vm_addr..].copy_from_slice(self.data);
1859 }
1860
1861 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1862 (data, region)
1863 }
1864 }
1865
1866 #[repr(C)]
1867 struct RcBox<T> {
1868 strong: Cell<usize>,
1869 weak: Cell<usize>,
1870 value: T,
1871 }
1872
1873 impl<T> RcBox<T> {
1874 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1875 fn new(value: T) -> RcBox<T> {
1876 RcBox {
1877 strong: Cell::new(0),
1878 weak: Cell::new(0),
1879 value,
1880 }
1881 }
1882 }
1883
1884 fn is_zeroed(data: &[u8]) -> bool {
1885 data.iter().all(|b| *b == 0)
1886 }
1887}