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