Skip to main content

solana_program_runtime/
cpi.rs

1//! Cross-Program Invocation (CPI) error types
2
3use {
4    crate::{
5        invoke_context::InvokeContext,
6        memory::{translate_slice, translate_type, translate_type_mut_for_cpi, translate_vm_slice},
7        memory_context::SerializedAccountMetadata,
8        serialization::{create_memory_region_of_account, modify_memory_region_of_account},
9    },
10    solana_instruction::{AccountMeta, Instruction, error::InstructionError},
11    solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
12    solana_program_entrypoint::MAX_PERMITTED_DATA_INCREASE,
13    solana_pubkey::{MAX_SEEDS, Pubkey, PubkeyError},
14    solana_sbpf::{ebpf, memory_region::MemoryMapping},
15    solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, native_loader},
16    solana_stable_layout::stable_instruction::StableInstruction,
17    solana_svm_log_collector::ic_msg,
18    solana_svm_timings::ExecuteTimings,
19    solana_transaction_context::{
20        IndexOfAccount, MAX_ACCOUNTS_PER_INSTRUCTION, MAX_INSTRUCTION_DATA_LEN,
21        instruction_accounts::BorrowedInstructionAccount, vm_slice::VmSlice,
22    },
23    std::mem,
24    thiserror::Error,
25};
26
27/// CPI-specific error types
28#[derive(Clone, Debug, Error, PartialEq, Eq)]
29pub enum CpiError {
30    #[error("Invalid pointer")]
31    InvalidPointer,
32    #[error("Too many signers")]
33    TooManySigners,
34    #[error("Could not create program address with signer seeds: {0}")]
35    BadSeeds(PubkeyError),
36    #[error("InvalidLength")]
37    InvalidLength,
38    #[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")]
39    MaxInstructionAccountsExceeded {
40        num_accounts: u64,
41        max_accounts: u64,
42    },
43    #[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")]
44    MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 },
45    #[error(
46        "Invoked an instruction with too many account info's ({num_account_infos} > \
47         {max_account_infos})"
48    )]
49    MaxInstructionAccountInfosExceeded {
50        num_account_infos: u64,
51        max_account_infos: u64,
52    },
53    #[error("Program {0} not supported by inner instructions")]
54    ProgramNotSupported(Pubkey),
55}
56
57type Error = Box<dyn std::error::Error>;
58
59const SUCCESS: u64 = 0;
60/// Maximum signers
61const MAX_SIGNERS: usize = 16;
62///SIMD-0339 based calculation of AccountInfo translation byte size. Fixed size of **80 bytes** for each AccountInfo broken down as:
63/// - 32 bytes for account address
64/// - 32 bytes for owner address
65/// - 8 bytes for lamport balance
66/// - 8 bytes for data length
67const ACCOUNT_INFO_BYTE_SIZE: usize = 80;
68
69/// Rust representation of C's SolInstruction
70#[derive(Debug)]
71#[repr(C)]
72struct SolInstruction {
73    pub program_id_addr: u64,
74    pub accounts_addr: u64,
75    pub accounts_len: u64,
76    pub data_addr: u64,
77    pub data_len: u64,
78}
79
80/// Rust representation of C's SolAccountMeta
81#[derive(Debug)]
82#[repr(C)]
83struct SolAccountMeta {
84    pub pubkey_addr: u64,
85    pub is_writable: bool,
86    pub is_signer: bool,
87}
88
89/// Rust representation of C's SolAccountInfo
90#[derive(Debug)]
91#[repr(C)]
92struct SolAccountInfo {
93    pub key_addr: u64,
94    pub lamports_addr: u64,
95    pub data_len: u64,
96    pub data_addr: u64,
97    pub owner_addr: u64,
98    pub rent_epoch: u64,
99    pub is_signer: bool,
100    pub is_writable: bool,
101    pub executable: bool,
102}
103
104mod stable {
105    /// Stable BPF representation of Rust [`solana_account_info::AccountInfo`].
106    #[derive(Debug)]
107    #[repr(C)]
108    pub struct AccountInfo {
109        key_addr: u64,
110        /// This address is pointing at `Rc<RefCell<T>>`'s internal data first. Use the
111        /// [`AccountInfo::lamports_addr()`] method to get the pointer to contained `T`.
112        lamports_addr: u64,
113        /// This address is pointing at `Rc<RefCell<T>>`'s internal data first. Use the
114        /// [`AccountInfo::data_addr()`] method to get the pointer to contained `T`.
115        data_addr: u64,
116        owner_addr: u64,
117        _unused: u64,
118        _is_signer: u8,
119        _is_writable: u8,
120        _executable: u8,
121    }
122
123    impl AccountInfo {
124        const LAMPORTS_DATA_OFFSET: u64 = 24;
125        const DATA_DATA_OFFSET: u64 = 24;
126        const DATA_LEN_OFFSET: u64 = 32;
127        pub(crate) fn owner_addr(&self) -> u64 {
128            self.owner_addr
129        }
130        pub(crate) fn key_addr(&self) -> u64 {
131            self.key_addr
132        }
133        pub(crate) fn lamports_addr(&self) -> u64 {
134            self.lamports_addr.wrapping_add(Self::LAMPORTS_DATA_OFFSET)
135        }
136        pub(crate) fn data_addr(&self) -> u64 {
137            self.data_addr.wrapping_add(Self::DATA_DATA_OFFSET)
138        }
139        pub(crate) fn data_len_addr(&self) -> u64 {
140            self.data_addr.wrapping_add(Self::DATA_LEN_OFFSET)
141        }
142    }
143
144    const _FOR_NOW_THESE_ARE_THE_SAME_BUT_IF_ACCOUNT_INFO_CHANGES_SDK_HAS_TO_FIX_IT: () = const {
145        use {
146            solana_account_info::AccountInfo as SdkAccountInfo,
147            std::mem::{align_of, offset_of, size_of},
148        };
149        assert!(offset_of!(AccountInfo, key_addr) == offset_of!(SdkAccountInfo, key));
150        assert!(offset_of!(AccountInfo, lamports_addr) == offset_of!(SdkAccountInfo, lamports));
151        assert!(offset_of!(AccountInfo, data_addr) == offset_of!(SdkAccountInfo, data));
152        assert!(offset_of!(AccountInfo, owner_addr) == offset_of!(SdkAccountInfo, owner));
153        assert!(offset_of!(AccountInfo, _is_signer) == offset_of!(SdkAccountInfo, is_signer));
154        assert!(offset_of!(AccountInfo, _is_writable) == offset_of!(SdkAccountInfo, is_writable));
155        assert!(offset_of!(AccountInfo, _executable) == offset_of!(SdkAccountInfo, executable));
156        assert!(size_of::<AccountInfo>() == size_of::<SdkAccountInfo>());
157        assert!(align_of::<SdkAccountInfo>() >= align_of::<AccountInfo>());
158    };
159}
160
161/// Maximum number of account info structs that can be used in a single CPI invocation
162const MAX_CPI_ACCOUNT_INFOS: usize = 255;
163
164/// Check that an account info pointer field points to the expected address
165fn check_account_info_pointer(
166    invoke_context: &InvokeContext,
167    vm_addr: u64,
168    expected_vm_addr: u64,
169    field: &str,
170) -> Result<(), Error> {
171    if vm_addr != expected_vm_addr {
172        ic_msg!(
173            invoke_context,
174            "Invalid account info pointer `{}': {:#x} != {:#x}",
175            field,
176            vm_addr,
177            expected_vm_addr
178        );
179        return Err(Box::new(CpiError::InvalidPointer));
180    }
181    Ok(())
182}
183
184/// Check that an instruction's account and data lengths are within limits
185fn check_instruction_size(num_accounts: usize, data_len: usize) -> Result<(), Error> {
186    if num_accounts > MAX_ACCOUNTS_PER_INSTRUCTION {
187        return Err(Box::new(CpiError::MaxInstructionAccountsExceeded {
188            num_accounts: num_accounts as u64,
189            max_accounts: MAX_ACCOUNTS_PER_INSTRUCTION as u64,
190        }));
191    }
192    if data_len > MAX_INSTRUCTION_DATA_LEN {
193        return Err(Box::new(CpiError::MaxInstructionDataLenExceeded {
194            data_len: data_len as u64,
195            max_data_len: MAX_INSTRUCTION_DATA_LEN as u64,
196        }));
197    }
198    Ok(())
199}
200
201/// Check that the number of account infos is within the CPI limit
202fn check_account_infos(num_account_infos: usize) -> Result<(), Error> {
203    let num_account_infos = num_account_infos as u64;
204    let max_account_infos = MAX_CPI_ACCOUNT_INFOS as u64;
205    if num_account_infos > max_account_infos {
206        return Err(Box::new(CpiError::MaxInstructionAccountInfosExceeded {
207            num_account_infos,
208            max_account_infos,
209        }));
210    }
211    Ok(())
212}
213
214/// Check whether a program is authorized for CPI
215fn check_authorized_program(
216    program_id: &Pubkey,
217    instruction_data: &[u8],
218    invoke_context: &InvokeContext,
219) -> Result<(), Error> {
220    if native_loader::check_id(program_id)
221        || bpf_loader::check_id(program_id)
222        || bpf_loader_deprecated::check_id(program_id)
223        || (solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
224            && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
225                || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
226                || (invoke_context
227                    .get_feature_set()
228                    .enable_bpf_loader_set_authority_checked_ix
229                    && bpf_loader_upgradeable::is_set_authority_checked_instruction(
230                        instruction_data,
231                    ))
232                || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
233        || invoke_context.is_precompile(program_id)
234    {
235        return Err(Box::new(CpiError::ProgramNotSupported(*program_id)));
236    }
237    Ok(())
238}
239
240/// Host side representation of AccountInfo or SolAccountInfo passed to the CPI syscall.
241///
242/// At the start of a CPI, this can be different from the data stored in the
243/// corresponding BorrowedAccount, and needs to be synched.
244#[derive(Debug)]
245pub struct CallerAccount<'a> {
246    pub lamports: &'a mut u64,
247    pub owner: &'a mut Pubkey,
248    // The original data length of the account at the start of the current
249    // instruction. We use this to determine whether an account was shrunk or
250    // grown before or after CPI, and to derive the vm address of the realloc
251    // region.
252    pub original_data_len: usize,
253    // This points to the data section for this account, as serialized and
254    // mapped inside the vm (see serialize_parameters() in
255    // BpfExecutor::execute).
256    //
257    // This is only set when account_data_direct_mapping is off.
258    pub serialized_data: &'a mut [u8],
259    // Given the corresponding input AccountInfo::data, vm_data_addr points to
260    // the pointer field and ref_to_len_in_vm points to the length field.
261    pub vm_data_addr: u64,
262    pub ref_to_len_in_vm: &'a mut u64,
263}
264
265impl<'a> CallerAccount<'a> {
266    /// Returns the length of the addres space reserved depending on the ABI version
267    pub fn address_space_reserved_for_account(&self, is_caller_loader_deprecated: bool) -> usize {
268        if is_caller_loader_deprecated {
269            self.original_data_len
270        } else {
271            self.original_data_len
272                .saturating_add(MAX_PERMITTED_DATA_INCREASE)
273        }
274    }
275
276    /// # Safety
277    ///
278    /// * The caller must ensure that this function does not violate mutable reference uniqueness
279    ///   constraints;
280    /// * The caller must ensure that the lifetime of the returned slice does not outlive the
281    ///   backing data;
282    /// * If `virtual_address_space_adjustments` is enabled and
283    ///   `account_data_direct_mapping` is disabled, the caller must ensure that the full
284    ///   `[vm_addr, vm_addr + len)` range is valid for the account.
285    pub unsafe fn get_serialized_data(
286        memory_mapping: &solana_sbpf::memory_region::MemoryMapping,
287        check_aligned: bool,
288        vm_addr: u64,
289        original_data_len: usize,
290        len: usize,
291        virtual_address_space_adjustments: bool,
292        account_data_direct_mapping: bool,
293    ) -> Result<&'a mut [u8], Error> {
294        use crate::memory::translate_slice_mut_for_cpi;
295
296        let is_caller_loader_deprecated = !check_aligned;
297        let address_space_reserved_for_account = if is_caller_loader_deprecated {
298            original_data_len
299        } else {
300            original_data_len.saturating_add(MAX_PERMITTED_DATA_INCREASE)
301        };
302        if len > address_space_reserved_for_account {
303            return Err(InstructionError::InvalidRealloc.into());
304        }
305        if virtual_address_space_adjustments && account_data_direct_mapping {
306            Ok(&mut [])
307        } else if virtual_address_space_adjustments {
308            // Workaround the memory permissions (as these are from the PoV of being inside the VM)
309            unsafe {
310                // SAFETY: Invariants for constructing a mutable reference delegated to the caller.
311                let serialization_ptr: &'a mut [u8] = translate_slice_mut_for_cpi::<u8>(
312                    memory_mapping,
313                    solana_sbpf::ebpf::MM_INPUT_START,
314                    1,
315                    false, // Don't care since it is byte aligned
316                )?;
317                Ok(std::slice::from_raw_parts_mut(
318                    serialization_ptr
319                        .as_mut_ptr()
320                        .add(vm_addr.saturating_sub(solana_sbpf::ebpf::MM_INPUT_START) as usize),
321                    len,
322                ))
323            }
324        } else {
325            unsafe {
326                // SAFETY: Invariants for constructing a mutable reference delegated to the caller.
327                translate_slice_mut_for_cpi::<u8>(
328                    memory_mapping,
329                    vm_addr,
330                    len as u64,
331                    false, // Don't care since it is byte aligned
332                )
333            }
334        }
335    }
336
337    // Create a CallerAccount given an AccountInfo.
338    pub fn from_account_info(
339        invoke_context: &InvokeContext,
340        memory_mapping: &MemoryMapping,
341        check_aligned: bool,
342        _vm_addr: u64,
343        account_info: &stable::AccountInfo,
344        account_metadata: &crate::memory_context::SerializedAccountMetadata,
345    ) -> Result<CallerAccount<'a>, Error> {
346        use crate::memory::{translate_type, translate_type_mut_for_cpi};
347
348        let virtual_address_space_adjustments = invoke_context
349            .get_feature_set()
350            .virtual_address_space_adjustments;
351        let account_data_direct_mapping =
352            invoke_context.get_feature_set().account_data_direct_mapping;
353
354        check_account_info_pointer(
355            invoke_context,
356            account_info.key_addr(),
357            account_metadata.vm_key_addr,
358            "key",
359        )?;
360        check_account_info_pointer(
361            invoke_context,
362            account_info.owner_addr(),
363            account_metadata.vm_owner_addr,
364            "owner",
365        )?;
366
367        // account_info points to host memory. The addresses used internally are
368        // in vm space so they need to be translated.
369        let lamports = {
370            // Double dereference lamports out
371            let ptr =
372                translate_type::<u64>(memory_mapping, account_info.lamports_addr(), check_aligned)?;
373            if account_info.lamports_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
374                return Err(Box::new(CpiError::InvalidPointer));
375            }
376
377            check_account_info_pointer(
378                invoke_context,
379                *ptr,
380                account_metadata.vm_lamports_addr,
381                "lamports",
382            )?;
383
384            translate_type_mut_for_cpi::<u64>(memory_mapping, *ptr, check_aligned)?
385        };
386
387        let owner = translate_type_mut_for_cpi::<Pubkey>(
388            memory_mapping,
389            account_info.owner_addr(),
390            check_aligned,
391        )?;
392
393        let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
394            if account_info.data_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
395                return Err(Box::new(CpiError::InvalidPointer));
396            }
397
398            // Double dereference data pointer out
399            // NOTE: we must obtain an owned copy to VmSlice<u8> right away in order to make
400            // the mutable reference to the length sound.
401            let data_slice = *translate_type::<VmSlice<u8>>(
402                memory_mapping,
403                account_info.data_addr(),
404                check_aligned,
405            )?;
406            check_account_info_pointer(
407                invoke_context,
408                data_slice.ptr(),
409                account_metadata.vm_data_addr,
410                "data",
411            )?;
412
413            // In the same vein as the other check_account_info_pointer() checks, we don't lock
414            // this pointer to a specific address but we don't want it to be inside accounts, or
415            // callees might be able to write to the pointed memory.
416            if account_info.data_len_addr() >= solana_sbpf::ebpf::MM_INPUT_START {
417                return Err(Box::new(CpiError::InvalidPointer));
418            }
419            let ref_to_len_in_vm = translate_type_mut_for_cpi::<u64>(
420                memory_mapping,
421                account_info.data_len_addr(),
422                false,
423            )?;
424            let serialized_data = unsafe {
425                CallerAccount::get_serialized_data(
426                    memory_mapping,
427                    check_aligned,
428                    data_slice.ptr(),
429                    account_metadata.original_data_len,
430                    *ref_to_len_in_vm as usize,
431                    virtual_address_space_adjustments,
432                    account_data_direct_mapping,
433                )?
434            };
435            (serialized_data, data_slice.ptr(), ref_to_len_in_vm)
436        };
437
438        Ok(CallerAccount {
439            lamports,
440            owner,
441            original_data_len: account_metadata.original_data_len,
442            serialized_data,
443            vm_data_addr,
444            ref_to_len_in_vm,
445        })
446    }
447
448    // Create a CallerAccount given a SolAccountInfo.
449    fn from_sol_account_info(
450        invoke_context: &InvokeContext,
451        memory_mapping: &MemoryMapping,
452        check_aligned: bool,
453        vm_addr: u64,
454        account_info: &SolAccountInfo,
455        account_metadata: &crate::memory_context::SerializedAccountMetadata,
456    ) -> Result<CallerAccount<'a>, Error> {
457        use crate::memory::translate_type_mut_for_cpi;
458
459        let virtual_address_space_adjustments = invoke_context
460            .get_feature_set()
461            .virtual_address_space_adjustments;
462        let account_data_direct_mapping =
463            invoke_context.get_feature_set().account_data_direct_mapping;
464
465        check_account_info_pointer(
466            invoke_context,
467            account_info.key_addr,
468            account_metadata.vm_key_addr,
469            "key",
470        )?;
471
472        check_account_info_pointer(
473            invoke_context,
474            account_info.owner_addr,
475            account_metadata.vm_owner_addr,
476            "owner",
477        )?;
478
479        check_account_info_pointer(
480            invoke_context,
481            account_info.lamports_addr,
482            account_metadata.vm_lamports_addr,
483            "lamports",
484        )?;
485
486        check_account_info_pointer(
487            invoke_context,
488            account_info.data_addr,
489            account_metadata.vm_data_addr,
490            "data",
491        )?;
492
493        // account_info points to host memory. The addresses used internally are
494        // in vm space so they need to be translated.
495        let lamports = translate_type_mut_for_cpi::<u64>(
496            memory_mapping,
497            account_info.lamports_addr,
498            check_aligned,
499        )?;
500        let owner = translate_type_mut_for_cpi::<Pubkey>(
501            memory_mapping,
502            account_info.owner_addr,
503            check_aligned,
504        )?;
505
506        // we already have the host addr we want: &mut account_info.data_len.
507        // The account info might be read only in the vm though, so we translate
508        // to ensure we can write. This is tested by programs/sbf/rust/ro_modify
509        // which puts SolAccountInfo in rodata.
510        let vm_len_addr = vm_addr
511            .saturating_add(&account_info.data_len as *const u64 as u64)
512            .saturating_sub(account_info as *const _ as *const u64 as u64);
513        // In the same vein as the other check_account_info_pointer() checks, we don't lock
514        // this pointer to a specific address but we don't want it to be inside accounts, or
515        // callees might be able to write to the pointed memory.
516        if vm_len_addr >= solana_sbpf::ebpf::MM_INPUT_START {
517            return Err(Box::new(CpiError::InvalidPointer));
518        }
519        let ref_to_len_in_vm =
520            translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
521        let serialized_data = unsafe {
522            CallerAccount::get_serialized_data(
523                memory_mapping,
524                check_aligned,
525                account_info.data_addr,
526                account_metadata.original_data_len,
527                *ref_to_len_in_vm as usize,
528                virtual_address_space_adjustments,
529                account_data_direct_mapping,
530            )?
531        };
532
533        Ok(CallerAccount {
534            lamports,
535            owner,
536            original_data_len: account_metadata.original_data_len,
537            serialized_data,
538            vm_data_addr: account_info.data_addr,
539            ref_to_len_in_vm,
540        })
541    }
542}
543
544/// Implemented by language specific data structure translators
545pub trait SyscallInvokeSigned {
546    fn translate_instruction(
547        addr: u64,
548        invoke_context: &InvokeContext,
549    ) -> Result<Instruction, Error>;
550    fn translate_accounts<'a>(
551        account_infos_addr: u64,
552        account_infos_len: u64,
553        invoke_context: &InvokeContext,
554    ) -> Result<Vec<TranslatedAccount<'a>>, Error>;
555}
556
557pub fn translate_instruction_rust(
558    addr: u64,
559    invoke_context: &InvokeContext,
560) -> Result<Instruction, Error> {
561    let check_aligned = invoke_context.get_check_aligned();
562    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
563    let ix = translate_type::<StableInstruction>(memory_mapping, addr, check_aligned)?;
564    let account_metas = translate_slice::<mem::MaybeUninit<AccountMeta>>(
565        memory_mapping,
566        ix.accounts.as_vaddr(),
567        ix.accounts.len(),
568        check_aligned,
569    )?;
570    let data = translate_slice::<u8>(
571        memory_mapping,
572        ix.data.as_vaddr(),
573        ix.data.len(),
574        check_aligned,
575    )?;
576
577    check_instruction_size(account_metas.len(), data.len())?;
578
579    let mut total_cu_translation_cost: u64 = (data.len() as u64)
580        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
581        .unwrap_or(u64::MAX);
582
583    // Each account meta is 34 bytes (32 for pubkey, 1 for is_signer, 1 for is_writable)
584    let account_meta_translation_cost =
585        (account_metas.len().saturating_mul(size_of::<AccountMeta>()) as u64)
586            .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
587            .unwrap_or(u64::MAX);
588
589    total_cu_translation_cost =
590        total_cu_translation_cost.saturating_add(account_meta_translation_cost);
591
592    invoke_context
593        .compute_meter
594        .consume_checked(total_cu_translation_cost)?;
595
596    let mut accounts = Vec::with_capacity(account_metas.len());
597    for account_meta in account_metas {
598        // Before using `account_meta` directly, verify that `is_signer` and `is_writable`
599        // contain valid boolean values to prevent UB.
600        let account_meta = unsafe {
601            let ptr = account_meta.as_ptr();
602            if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
603                || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
604            {
605                return Err(Box::new(InstructionError::InvalidArgument));
606            }
607            // SAFETY: VM memory is initialized, and we have validated that the boolean fields
608            // contain valid data.
609            account_meta.assume_init_ref()
610        };
611
612        accounts.push(account_meta.clone());
613    }
614
615    Ok(Instruction {
616        accounts,
617        data: data.to_vec(),
618        program_id: ix.program_id,
619    })
620}
621
622pub fn translate_accounts_rust<'a>(
623    account_infos_addr: u64,
624    account_infos_len: u64,
625    invoke_context: &InvokeContext,
626) -> Result<Vec<TranslatedAccount<'a>>, Error> {
627    let check_aligned = invoke_context.get_check_aligned();
628    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
629    translate_account_infos(
630        account_infos_addr,
631        account_infos_len,
632        |account_info: &stable::AccountInfo| account_info.key_addr(),
633        invoke_context,
634        memory_mapping,
635        check_aligned,
636        |account_infos, account_info_keys| {
637            translate_accounts_common(
638                &account_info_keys,
639                account_infos,
640                account_infos_addr,
641                invoke_context,
642                memory_mapping,
643                check_aligned,
644                CallerAccount::from_account_info,
645            )
646        },
647    )?
648}
649
650pub fn translate_signers(
651    program_id: &Pubkey,
652    signers_seeds_addr: u64,
653    signers_seeds_len: u64,
654    invoke_context: &InvokeContext,
655) -> Result<Vec<Pubkey>, Error> {
656    let check_aligned = invoke_context.get_check_aligned();
657    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
658    if signers_seeds_len > 0 {
659        let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
660            memory_mapping,
661            signers_seeds_addr,
662            signers_seeds_len,
663            check_aligned,
664        )?;
665        if signers_seeds.len() > MAX_SIGNERS {
666            return Err(Box::new(CpiError::TooManySigners));
667        }
668        Ok(signers_seeds
669            .iter()
670            .map(|signer_seeds| {
671                let untranslated_seeds = translate_slice::<VmSlice<u8>>(
672                    memory_mapping,
673                    signer_seeds.ptr(),
674                    signer_seeds.len(),
675                    check_aligned,
676                )?;
677                if untranslated_seeds.len() > MAX_SEEDS {
678                    return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
679                }
680                let seeds_bytes = untranslated_seeds
681                    .iter()
682                    .map(|untranslated_seed| {
683                        translate_vm_slice(untranslated_seed, memory_mapping, check_aligned)
684                    })
685                    .collect::<Result<Vec<_>, Error>>()?;
686                Pubkey::create_program_address(&seeds_bytes, program_id)
687                    .map_err(|err| Box::new(CpiError::BadSeeds(err)) as Error)
688            })
689            .collect::<Result<Vec<_>, Error>>()?)
690    } else {
691        Ok(vec![])
692    }
693}
694
695pub fn translate_instruction_c(
696    addr: u64,
697    invoke_context: &InvokeContext,
698) -> Result<Instruction, Error> {
699    let check_aligned = invoke_context.get_check_aligned();
700    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
701    let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
702
703    let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
704    let account_metas = translate_slice::<mem::MaybeUninit<SolAccountMeta>>(
705        memory_mapping,
706        ix_c.accounts_addr,
707        ix_c.accounts_len,
708        check_aligned,
709    )?;
710    let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
711
712    check_instruction_size(ix_c.accounts_len as usize, data.len())?;
713
714    let mut total_cu_translation_cost: u64 = (data.len() as u64)
715        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
716        .unwrap_or(u64::MAX);
717
718    // Each account meta is 34 bytes (32 for pubkey, 1 for is_signer, 1 for is_writable)
719    let account_meta_translation_cost = (ix_c
720        .accounts_len
721        .saturating_mul(size_of::<AccountMeta>() as u64))
722    .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
723    .unwrap_or(u64::MAX);
724
725    total_cu_translation_cost =
726        total_cu_translation_cost.saturating_add(account_meta_translation_cost);
727
728    invoke_context
729        .compute_meter
730        .consume_checked(total_cu_translation_cost)?;
731
732    let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
733    for account_meta in account_metas {
734        // Before using `account_meta` directly, verify that `is_signer` and `is_writable`
735        // contain valid boolean values to prevent UB.
736        let account_meta = unsafe {
737            let ptr = account_meta.as_ptr();
738            if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
739                || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
740            {
741                return Err(Box::new(InstructionError::InvalidArgument));
742            }
743            // SAFETY: VM memory is initialized, and we have validated that the boolean fields
744            // contain valid data.
745            account_meta.assume_init_ref()
746        };
747        let pubkey =
748            translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
749        accounts.push(AccountMeta {
750            pubkey: *pubkey,
751            is_signer: account_meta.is_signer,
752            is_writable: account_meta.is_writable,
753        });
754    }
755
756    Ok(Instruction {
757        accounts,
758        data: data.to_vec(),
759        program_id: *program_id,
760    })
761}
762
763pub fn translate_accounts_c<'a>(
764    account_infos_addr: u64,
765    account_infos_len: u64,
766    invoke_context: &InvokeContext,
767) -> Result<Vec<TranslatedAccount<'a>>, Error> {
768    let check_aligned = invoke_context.get_check_aligned();
769    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
770    translate_account_infos(
771        account_infos_addr,
772        account_infos_len,
773        |account_info: &SolAccountInfo| account_info.key_addr,
774        invoke_context,
775        memory_mapping,
776        check_aligned,
777        |account_infos, account_info_keys| {
778            translate_accounts_common(
779                &account_info_keys,
780                account_infos,
781                account_infos_addr,
782                invoke_context,
783                memory_mapping,
784                check_aligned,
785                CallerAccount::from_sol_account_info,
786            )
787        },
788    )?
789}
790
791/// Call process instruction, common to both Rust and C
792pub fn cpi_common<S: SyscallInvokeSigned>(
793    invoke_context: &mut InvokeContext,
794    instruction_addr: u64,
795    account_infos_addr: u64,
796    account_infos_len: u64,
797    signers_seeds_addr: u64,
798    signers_seeds_len: u64,
799) -> Result<u64, Error> {
800    // CPI entry.
801    //
802    // Translate the inputs to the syscall and synchronize the caller's account
803    // changes so the callee can see them.
804    let amount = invoke_context.get_execution_cost().invoke_units;
805    invoke_context.compute_meter.consume_checked(amount)?;
806    let virtual_address_space_adjustments = invoke_context
807        .get_feature_set()
808        .virtual_address_space_adjustments;
809    let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
810    let check_aligned = invoke_context.get_check_aligned();
811
812    let instruction = S::translate_instruction(instruction_addr, invoke_context)?;
813    let instruction_context = invoke_context
814        .transaction_context
815        .get_current_instruction_context()?;
816    let caller_program_id = instruction_context.get_program_key()?;
817    let signers = translate_signers(
818        caller_program_id,
819        signers_seeds_addr,
820        signers_seeds_len,
821        invoke_context,
822    )?;
823    check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
824    invoke_context.prepare_next_cpi_instruction(instruction, &signers)?;
825
826    let mut accounts =
827        S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
828
829    // before initiating CPI, the caller may have modified the
830    // account (caller_account). We need to update the corresponding
831    // BorrowedAccount (callee_account) so the callee can see the
832    // changes.
833    let transaction_context = &invoke_context.transaction_context;
834    let instruction_context = transaction_context.get_current_instruction_context()?;
835    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
836    for translated_account in accounts.iter_mut() {
837        let callee_account = instruction_context
838            .try_borrow_instruction_account(translated_account.index_in_caller)?;
839        let update_caller = update_callee_account(
840            memory_mapping,
841            check_aligned,
842            &translated_account.caller_account,
843            callee_account,
844            virtual_address_space_adjustments,
845            account_data_direct_mapping,
846        )?;
847        translated_account.update_caller_account_region =
848            translated_account.update_caller_account_info || update_caller;
849    }
850
851    // Process the callee instruction
852    let mut compute_units_consumed = 0;
853    invoke_context
854        .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
855
856    // re-bind to please the borrow checker
857    let transaction_context = &invoke_context.transaction_context;
858    let instruction_context = transaction_context.get_current_instruction_context()?;
859
860    // CPI exit.
861    //
862    // Synchronize the callee's account changes so the caller can see them.
863    for translated_account in accounts.iter_mut() {
864        let mut callee_account = instruction_context
865            .try_borrow_instruction_account(translated_account.index_in_caller)?;
866        if translated_account.update_caller_account_info {
867            update_caller_account(
868                invoke_context,
869                check_aligned,
870                &mut translated_account.caller_account,
871                &mut callee_account,
872                virtual_address_space_adjustments,
873                account_data_direct_mapping,
874            )?;
875        }
876    }
877
878    if virtual_address_space_adjustments {
879        let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
880        for translated_account in accounts.iter() {
881            let mut callee_account = instruction_context
882                .try_borrow_instruction_account(translated_account.index_in_caller)?;
883            if translated_account.update_caller_account_region {
884                unsafe {
885                    // SAFETY: lifetime is valid by construction: we're resetting the caller memory
886                    // region back to the account that was here before the CPI call, meaning that
887                    // the memory region was guaranteed to be live for sufficient duration upon
888                    // call of this function.
889                    update_caller_account_region(
890                        memory_mapping,
891                        check_aligned,
892                        &translated_account.caller_account,
893                        &mut callee_account,
894                        account_data_direct_mapping,
895                    )?;
896                }
897            }
898        }
899    }
900
901    Ok(SUCCESS)
902}
903
904/// Account data and metadata that has been translated from caller space.
905pub struct TranslatedAccount<'a> {
906    pub index_in_caller: IndexOfAccount,
907    pub caller_account: CallerAccount<'a>,
908    pub update_caller_account_region: bool,
909    pub update_caller_account_info: bool,
910}
911
912fn translate_account_infos<T, R>(
913    account_infos_addr: u64,
914    account_infos_len: u64,
915    key_addr: impl Fn(&T) -> u64,
916    invoke_context: &InvokeContext,
917    memory_mapping: &MemoryMapping,
918    check_aligned: bool,
919    cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
920) -> Result<R, Error> {
921    // In the same vein as the other check_account_info_pointer() checks, we don't lock
922    // this pointer to a specific address but we don't want it to be inside accounts, or
923    // callees might be able to write to the pointed memory.
924    if account_infos_addr
925        .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
926        >= ebpf::MM_INPUT_START
927    {
928        return Err(CpiError::InvalidPointer.into());
929    }
930
931    let account_infos = translate_slice::<T>(
932        memory_mapping,
933        account_infos_addr,
934        account_infos_len,
935        check_aligned,
936    )?;
937    check_account_infos(account_infos.len())?;
938
939    let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
940
941    let amount = (account_infos_bytes as u64)
942        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
943        .unwrap_or(u64::MAX);
944    invoke_context.compute_meter.consume_checked(amount)?;
945
946    let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
947    #[expect(clippy::needless_range_loop)]
948    for account_index in 0..account_infos_len as usize {
949        #[expect(clippy::indexing_slicing)]
950        let account_info = &account_infos[account_index];
951        account_info_keys.push(translate_type::<Pubkey>(
952            memory_mapping,
953            key_addr(account_info),
954            check_aligned,
955        )?);
956    }
957    Ok(cb(account_infos, account_info_keys))
958}
959
960// Finish translating accounts and build TranslatedAccount from CallerAccount.
961fn translate_accounts_common<'a, T, F>(
962    account_info_keys: &[&Pubkey],
963    account_infos: &[T],
964    account_infos_addr: u64,
965    invoke_context: &InvokeContext,
966    memory_mapping: &MemoryMapping,
967    check_aligned: bool,
968    do_translate: F,
969) -> Result<Vec<TranslatedAccount<'a>>, Error>
970where
971    F: Fn(
972        &InvokeContext,
973        &MemoryMapping,
974        bool,
975        u64,
976        &T,
977        &SerializedAccountMetadata,
978    ) -> Result<CallerAccount<'a>, Error>,
979{
980    let transaction_context = &invoke_context.transaction_context;
981    let next_instruction_context = transaction_context.get_next_instruction_context()?;
982    let next_instruction_accounts = next_instruction_context.instruction_accounts();
983    let instruction_context = transaction_context.get_current_instruction_context()?;
984    let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
985
986    // unwrapping here is fine: we're in a syscall and the method below fails
987    // only outside syscalls
988    let accounts_metadata = &invoke_context
989        .memory_contexts
990        .memory_context_abi_v1()
991        .unwrap()
992        .accounts_metadata;
993
994    for (instruction_account_index, instruction_account) in
995        next_instruction_accounts.iter().enumerate()
996    {
997        if next_instruction_context
998            .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
999            .is_some()
1000        {
1001            continue; // Skip duplicate account
1002        }
1003
1004        let index_in_caller = instruction_context
1005            .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
1006        let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
1007        let account_key = invoke_context
1008            .transaction_context
1009            .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
1010
1011        #[expect(deprecated)]
1012        if callee_account.is_executable() {
1013            // Use the known account
1014            let amount = (callee_account.get_data().len() as u64)
1015                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1016                .unwrap_or(u64::MAX);
1017            invoke_context.compute_meter.consume_checked(amount)?;
1018        } else if let Some(caller_account_index) =
1019            account_info_keys.iter().position(|key| *key == account_key)
1020        {
1021            let serialized_metadata =
1022                accounts_metadata
1023                    .get(index_in_caller as usize)
1024                    .ok_or_else(|| {
1025                        ic_msg!(
1026                            invoke_context,
1027                            "Internal error: index mismatch for account {}",
1028                            account_key
1029                        );
1030                        Box::new(InstructionError::MissingAccount) as Error
1031                    })?;
1032
1033            // build the CallerAccount corresponding to this account.
1034            if caller_account_index >= account_infos.len() {
1035                return Err(Box::new(CpiError::InvalidLength));
1036            }
1037            #[expect(clippy::indexing_slicing)]
1038            let caller_account =
1039                do_translate(
1040                    invoke_context,
1041                    memory_mapping,
1042                    check_aligned,
1043                    account_infos_addr.saturating_add(
1044                        caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
1045                    ),
1046                    &account_infos[caller_account_index],
1047                    serialized_metadata,
1048                )?;
1049
1050            let amount = (*caller_account.ref_to_len_in_vm)
1051                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1052                .unwrap_or(u64::MAX);
1053            invoke_context.compute_meter.consume_checked(amount)?;
1054
1055            accounts.push(TranslatedAccount {
1056                index_in_caller,
1057                caller_account,
1058                update_caller_account_region: true, // overwritten in `cpi_common` via `update_callee_acccount()`
1059                update_caller_account_info: instruction_account.is_writable(),
1060            });
1061        } else {
1062            ic_msg!(
1063                invoke_context,
1064                "Instruction references an unknown account {}",
1065                account_key
1066            );
1067            return Err(Box::new(InstructionError::MissingAccount));
1068        }
1069    }
1070
1071    Ok(accounts)
1072}
1073
1074// Update the given account before executing CPI.
1075//
1076// caller_account and callee_account describe the same account. At CPI entry
1077// caller_account might include changes the caller has made to the account
1078// before executing CPI.
1079//
1080// This method updates callee_account so the CPI callee can see the caller's
1081// changes.
1082//
1083// When true is returned, the caller account must be updated after CPI. This
1084// is only set for virtual_address_space_adjustments when the pointer may have changed.
1085fn update_callee_account(
1086    memory_mapping: &MemoryMapping,
1087    check_aligned: bool,
1088    caller_account: &CallerAccount,
1089    mut callee_account: BorrowedInstructionAccount<'_, '_>,
1090    virtual_address_space_adjustments: bool,
1091    account_data_direct_mapping: bool,
1092) -> Result<bool, Error> {
1093    let mut must_update_caller = false;
1094
1095    if callee_account.get_lamports() != *caller_account.lamports {
1096        callee_account.set_lamports(*caller_account.lamports)?;
1097    }
1098
1099    if virtual_address_space_adjustments {
1100        let prev_len = callee_account.get_data().len();
1101        let post_len = *caller_account.ref_to_len_in_vm as usize;
1102        if prev_len != post_len {
1103            if !account_data_direct_mapping && post_len < prev_len {
1104                // If the account has been shrunk, we're going to zero the unused memory
1105                // *that was previously used*.
1106                let serialized_data = unsafe {
1107                    CallerAccount::get_serialized_data(
1108                        memory_mapping,
1109                        check_aligned,
1110                        caller_account.vm_data_addr,
1111                        caller_account.original_data_len,
1112                        prev_len,
1113                        virtual_address_space_adjustments,
1114                        account_data_direct_mapping,
1115                    )?
1116                };
1117                serialized_data
1118                    .get_mut(post_len..)
1119                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1120                    .fill(0);
1121            }
1122            callee_account.set_data_length(post_len)?;
1123            // pointer to data may have changed, so caller must be updated
1124            must_update_caller = true;
1125        }
1126        if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1127            callee_account.set_data_from_slice(caller_account.serialized_data)?;
1128        }
1129    } else {
1130        // The redundant check helps to avoid the expensive data comparison if we can
1131        match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1132            Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1133            Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1134                return Err(Box::new(err));
1135            }
1136            _ => {}
1137        }
1138    }
1139
1140    // Change the owner at the end so that we are allowed to change the lamports and data before
1141    if callee_account.get_owner() != caller_account.owner {
1142        callee_account.set_owner(caller_account.owner.as_ref())?;
1143        // caller gave ownership and thus write access away, so caller must be updated
1144        must_update_caller = true;
1145    }
1146
1147    Ok(must_update_caller)
1148}
1149
1150/// # Safety
1151///
1152/// The the account data pointed to by `callee_account` must outlive the uses of the
1153/// [`MemoryMapping`].
1154unsafe fn update_caller_account_region(
1155    memory_mapping: &mut MemoryMapping,
1156    check_aligned: bool,
1157    caller_account: &CallerAccount,
1158    callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1159    account_data_direct_mapping: bool,
1160) -> Result<(), Error> {
1161    let is_caller_loader_deprecated = !check_aligned;
1162    let address_space_reserved_for_account =
1163        caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1164
1165    if address_space_reserved_for_account > 0 {
1166        // We can trust vm_data_addr to point to the correct region because we
1167        // enforce that in CallerAccount::from_(sol_)account_info.
1168        let (region_index, region) = memory_mapping
1169            .find_region(caller_account.vm_data_addr)
1170            .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1171        // vm_data_addr must always point to the beginning of the region
1172        let region_start_vm_addr = region.vm_addr_range().start;
1173        debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1174        let mut new_region;
1175        if !account_data_direct_mapping {
1176            new_region = region.clone();
1177            modify_memory_region_of_account(callee_account, &mut new_region);
1178        } else {
1179            new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1180        }
1181        unsafe {
1182            // SAFETY: the lifetime invariants are delegated to the callers of this function. Both
1183            // `modify_memory_region_of_account` and `create_memory_region_of_account` create memory
1184            // regions pointing to valid buffers by the virtue of the region being produced out of
1185            // an intermediate slice, which itself must be wholly valid.
1186            memory_mapping.replace_region(region_index, new_region)?;
1187        }
1188    }
1189
1190    Ok(())
1191}
1192
1193// Update the given account after executing CPI.
1194//
1195// caller_account and callee_account describe to the same account. At CPI exit
1196// callee_account might include changes the callee has made to the account
1197// after executing.
1198//
1199// This method updates caller_account so the CPI caller can see the callee's
1200// changes.
1201fn update_caller_account(
1202    invoke_context: &InvokeContext,
1203    check_aligned: bool,
1204    caller_account: &mut CallerAccount<'_>,
1205    callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1206    virtual_address_space_adjustments: bool,
1207    account_data_direct_mapping: bool,
1208) -> Result<(), Error> {
1209    *caller_account.lamports = callee_account.get_lamports();
1210    *caller_account.owner = *callee_account.get_owner();
1211
1212    let prev_len = *caller_account.ref_to_len_in_vm as usize;
1213    let post_len = callee_account.get_data().len();
1214    let is_caller_loader_deprecated = !check_aligned;
1215    let address_space_reserved_for_account =
1216        caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1217
1218    if post_len > address_space_reserved_for_account {
1219        let max_increase =
1220            address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1221        ic_msg!(
1222            invoke_context,
1223            "Account data size realloc limited to {max_increase} in inner instructions",
1224        );
1225        return Err(Box::new(InstructionError::InvalidRealloc));
1226    }
1227
1228    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1229    if prev_len != post_len {
1230        // when virtual_address_space_adjustments is enabled we don't cache the serialized data in
1231        // caller_account.serialized_data. See CallerAccount::from_account_info.
1232        if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1233            // If the account has been shrunk, we're going to zero the unused memory
1234            // *that was previously used*.
1235            if post_len < prev_len {
1236                caller_account
1237                    .serialized_data
1238                    .get_mut(post_len..)
1239                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1240                    .fill(0);
1241            }
1242            // Set the length of caller_account.serialized_data to post_len.
1243            unsafe {
1244                caller_account.serialized_data = CallerAccount::get_serialized_data(
1245                    memory_mapping,
1246                    check_aligned,
1247                    caller_account.vm_data_addr,
1248                    caller_account.original_data_len,
1249                    post_len,
1250                    virtual_address_space_adjustments,
1251                    account_data_direct_mapping,
1252                )?;
1253            }
1254        }
1255        // this is the len field in the AccountInfo::data slice
1256        *caller_account.ref_to_len_in_vm = post_len as u64;
1257
1258        // this is the len field in the serialized parameters
1259        let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1260            memory_mapping,
1261            caller_account
1262                .vm_data_addr
1263                .saturating_sub(std::mem::size_of::<u64>() as u64),
1264            check_aligned,
1265        )?;
1266        *serialized_len_ptr = post_len as u64;
1267    }
1268
1269    if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1270        // Propagate changes in the callee up to the caller.
1271        let to_slice = &mut caller_account.serialized_data;
1272        let from_slice = callee_account
1273            .get_data()
1274            .get(0..post_len)
1275            .ok_or(CpiError::InvalidLength)?;
1276        if to_slice.len() != from_slice.len() {
1277            return Err(Box::new(InstructionError::AccountDataTooSmall));
1278        }
1279        to_slice.copy_from_slice(from_slice);
1280    }
1281
1282    Ok(())
1283}
1284
1285#[allow(clippy::indexing_slicing)]
1286#[allow(clippy::arithmetic_side_effects)]
1287#[cfg(test)]
1288mod tests {
1289    use {
1290        super::*,
1291        crate::{
1292            invoke_context::BpfAllocator,
1293            memory::translate_type,
1294            memory_context::{MemoryContext, SerializedAccountMetadata},
1295            with_mock_invoke_context_with_feature_set,
1296        },
1297        assert_matches::assert_matches,
1298        solana_account::{Account, AccountSharedData, ReadableAccount},
1299        solana_account_info::AccountInfo,
1300        solana_sbpf::{
1301            ebpf::{MM_INPUT_START, MM_STACK_START},
1302            memory_region::MemoryRegion,
1303            program::SBPFVersion,
1304            vm::Config,
1305        },
1306        solana_sdk_ids::{bpf_loader, system_program},
1307        solana_svm_feature_set::SVMFeatureSet,
1308        solana_transaction_context::{
1309            IndexOfAccount, instruction_accounts::InstructionAccount,
1310            transaction_accounts::KeyedAccountSharedData,
1311        },
1312        std::{
1313            cell::{Cell, RefCell},
1314            mem, ptr,
1315            rc::Rc,
1316            slice,
1317        },
1318        test_case::case,
1319    };
1320
1321    macro_rules! mock_invoke_context {
1322        ($invoke_context:ident,
1323         $transaction_context:ident,
1324         $instruction_data:expr,
1325         $transaction_accounts:expr,
1326         $program_account:expr,
1327         $instruction_accounts:expr) => {
1328            let instruction_data = $instruction_data;
1329            let instruction_accounts = $instruction_accounts
1330                .iter()
1331                .map(|index_in_transaction| {
1332                    InstructionAccount::new(
1333                        *index_in_transaction as IndexOfAccount,
1334                        false,
1335                        $transaction_accounts[*index_in_transaction as usize].2,
1336                    )
1337                })
1338                .collect::<Vec<_>>();
1339            let transaction_accounts = $transaction_accounts
1340                .into_iter()
1341                .map(|a| (a.0, a.1))
1342                .collect::<Vec<KeyedAccountSharedData>>();
1343            let mut feature_set = SVMFeatureSet::all_enabled();
1344            feature_set.virtual_address_space_adjustments = false;
1345            feature_set.account_data_direct_mapping = false;
1346            let feature_set = &feature_set;
1347            with_mock_invoke_context_with_feature_set!(
1348                $invoke_context,
1349                $transaction_context,
1350                feature_set,
1351                transaction_accounts
1352            );
1353            $invoke_context
1354                .transaction_context
1355                .configure_top_level_instruction_for_tests(
1356                    $program_account,
1357                    instruction_accounts,
1358                    instruction_data.to_vec(),
1359                )
1360                .unwrap();
1361            $invoke_context.push().unwrap();
1362        };
1363    }
1364
1365    macro_rules! borrow_instruction_account {
1366        ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1367            let instruction_context = $invoke_context
1368                .transaction_context
1369                .get_current_instruction_context()
1370                .unwrap();
1371            let $borrowed_account = instruction_context
1372                .try_borrow_instruction_account($index)
1373                .unwrap();
1374        };
1375    }
1376
1377    fn is_zeroed(data: &[u8]) -> bool {
1378        data.iter().all(|b| *b == 0)
1379    }
1380
1381    struct MockCallerAccount {
1382        lamports: u64,
1383        owner: Pubkey,
1384        vm_addr: u64,
1385        data: Vec<u8>,
1386        len: u64,
1387        regions: Vec<MemoryRegion>,
1388        virtual_address_space_adjustments: bool,
1389    }
1390
1391    impl MockCallerAccount {
1392        fn new(
1393            lamports: u64,
1394            owner: Pubkey,
1395            data: &[u8],
1396            virtual_address_space_adjustments: bool,
1397        ) -> MockCallerAccount {
1398            let vm_addr = MM_INPUT_START;
1399            let mut region_addr = vm_addr;
1400            let region_len = mem::size_of::<u64>()
1401                + if virtual_address_space_adjustments {
1402                    0
1403                } else {
1404                    data.len() + MAX_PERMITTED_DATA_INCREASE
1405                };
1406            let mut d = vec![0; region_len];
1407            let mut regions = vec![];
1408
1409            // always write the [len] part even when virtual_address_space_adjustments
1410            unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1411
1412            // write the account data when not virtual_address_space_adjustments
1413            if !virtual_address_space_adjustments {
1414                d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1415            }
1416
1417            // create a region for [len][data+realloc if !virtual_address_space_adjustments]
1418            regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1419            region_addr += region_len as u64;
1420
1421            if virtual_address_space_adjustments {
1422                // create a region for the directly mapped data
1423                regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1424                region_addr += data.len() as u64;
1425
1426                // create a region for the realloc padding
1427                regions.push(MemoryRegion::new(
1428                    &raw mut d[mem::size_of::<u64>()..],
1429                    region_addr,
1430                ));
1431            } else {
1432                // caller_account.serialized_data must have the actual data length
1433                d.truncate(mem::size_of::<u64>() + data.len());
1434            }
1435
1436            MockCallerAccount {
1437                lamports,
1438                owner,
1439                vm_addr,
1440                data: d,
1441                len: data.len() as u64,
1442                regions,
1443                virtual_address_space_adjustments,
1444            }
1445        }
1446
1447        fn data_slice<'a>(&self) -> &'a [u8] {
1448            // lifetime crimes
1449            unsafe {
1450                slice::from_raw_parts(
1451                    self.data[mem::size_of::<u64>()..].as_ptr(),
1452                    self.data.capacity() - mem::size_of::<u64>(),
1453                )
1454            }
1455        }
1456
1457        fn caller_account(&mut self) -> CallerAccount<'_> {
1458            let data = if self.virtual_address_space_adjustments {
1459                &mut []
1460            } else {
1461                &mut self.data[mem::size_of::<u64>()..]
1462            };
1463            CallerAccount {
1464                lamports: &mut self.lamports,
1465                owner: &mut self.owner,
1466                original_data_len: self.len as usize,
1467                serialized_data: data,
1468                vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1469                ref_to_len_in_vm: &mut self.len,
1470            }
1471        }
1472    }
1473
1474    struct MockAccountInfo<'a> {
1475        key: Pubkey,
1476        is_signer: bool,
1477        is_writable: bool,
1478        lamports: u64,
1479        data: &'a [u8],
1480        owner: Pubkey,
1481        executable: bool,
1482        _unused: u64,
1483    }
1484
1485    impl MockAccountInfo<'_> {
1486        fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1487            MockAccountInfo {
1488                key,
1489                is_signer: false,
1490                is_writable: false,
1491                lamports: account.lamports(),
1492                data: account.data(),
1493                owner: *account.owner(),
1494                executable: account.executable(),
1495                _unused: account.rent_epoch(),
1496            }
1497        }
1498
1499        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1500            let size = mem::size_of::<AccountInfo>()
1501                + mem::size_of::<Pubkey>() * 2
1502                + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1503                + mem::size_of::<u64>()
1504                + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1505                + self.data.len();
1506            let mut data = vec![0; size];
1507
1508            let vm_addr = vm_addr as usize;
1509            let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1510            let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1511            let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1512            let owner_addr = lamports_addr + mem::size_of::<u64>();
1513            let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1514            let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1515
1516            #[allow(deprecated)]
1517            #[allow(clippy::used_underscore_binding)]
1518            let info = AccountInfo {
1519                key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1520                is_signer: self.is_signer,
1521                is_writable: self.is_writable,
1522                lamports: unsafe {
1523                    Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1524                },
1525                data: unsafe {
1526                    Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1527                },
1528                owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1529                executable: self.executable,
1530                _unused: self._unused,
1531            };
1532
1533            unsafe {
1534                ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1535                ptr::write_unaligned(
1536                    (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1537                    self.key,
1538                );
1539                ptr::write_unaligned(
1540                    (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1541                    RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1542                );
1543                ptr::write_unaligned(
1544                    (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1545                    self.lamports,
1546                );
1547                ptr::write_unaligned(
1548                    (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1549                    self.owner,
1550                );
1551                ptr::write_unaligned(
1552                    (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1553                    RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1554                        data_addr as *mut u8,
1555                        self.data.len(),
1556                    ))),
1557                );
1558                data[data_addr - vm_addr..].copy_from_slice(self.data);
1559            }
1560
1561            let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1562            (
1563                data,
1564                region,
1565                SerializedAccountMetadata {
1566                    vm_addr: vm_addr as u64,
1567                    original_data_len: self.data.len(),
1568                    vm_key_addr: key_addr as u64,
1569                    vm_lamports_addr: lamports_addr as u64,
1570                    vm_owner_addr: owner_addr as u64,
1571                    vm_data_addr: data_addr as u64,
1572                },
1573            )
1574        }
1575    }
1576
1577    struct MockInstruction {
1578        program_id: Pubkey,
1579        accounts: Vec<AccountMeta>,
1580        data: Vec<u8>,
1581    }
1582
1583    impl MockInstruction {
1584        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1585            let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1586
1587            let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1588
1589            let mut data = vec![0; size];
1590
1591            let vm_addr = vm_addr as usize;
1592            let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1593            let data_addr = accounts_addr + accounts_len;
1594
1595            let ins = Instruction {
1596                program_id: self.program_id,
1597                accounts: unsafe {
1598                    Vec::from_raw_parts(
1599                        accounts_addr as *mut _,
1600                        self.accounts.len(),
1601                        self.accounts.len(),
1602                    )
1603                },
1604                data: unsafe {
1605                    Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1606                },
1607            };
1608            let ins = StableInstruction::from(ins);
1609
1610            unsafe {
1611                ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1612                data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1613                    slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1614                );
1615                data[data_addr - vm_addr..].copy_from_slice(&self.data);
1616            }
1617
1618            let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1619            (data, region)
1620        }
1621    }
1622
1623    #[repr(C)]
1624    struct RcBox<T> {
1625        strong: Cell<usize>,
1626        weak: Cell<usize>,
1627        value: T,
1628    }
1629
1630    impl<T> RcBox<T> {
1631        const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1632        fn new(value: T) -> RcBox<T> {
1633            RcBox {
1634                strong: Cell::new(0),
1635                weak: Cell::new(0),
1636                value,
1637            }
1638        }
1639    }
1640
1641    type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1642
1643    fn transaction_with_one_writable_instruction_account(
1644        data: Vec<u8>,
1645    ) -> Vec<TestTransactionAccount> {
1646        let program_id = Pubkey::new_unique();
1647        let account = AccountSharedData::from(Account {
1648            lamports: 1,
1649            data,
1650            owner: program_id,
1651            executable: false,
1652            rent_epoch: 100,
1653        });
1654        vec![
1655            (
1656                program_id,
1657                AccountSharedData::from(Account {
1658                    lamports: 0,
1659                    data: vec![],
1660                    owner: bpf_loader::id(),
1661                    executable: true,
1662                    rent_epoch: 0,
1663                }),
1664                false,
1665            ),
1666            (Pubkey::new_unique(), account, true),
1667        ]
1668    }
1669
1670    fn transaction_with_one_readonly_instruction_account(
1671        data: Vec<u8>,
1672    ) -> Vec<TestTransactionAccount> {
1673        let program_id = Pubkey::new_unique();
1674        let account_owner = Pubkey::new_unique();
1675        let account = AccountSharedData::from(Account {
1676            lamports: 1,
1677            data,
1678            owner: account_owner,
1679            executable: false,
1680            rent_epoch: 100,
1681        });
1682        vec![
1683            (
1684                program_id,
1685                AccountSharedData::from(Account {
1686                    lamports: 0,
1687                    data: vec![],
1688                    owner: bpf_loader::id(),
1689                    executable: true,
1690                    rent_epoch: 0,
1691                }),
1692                false,
1693            ),
1694            (Pubkey::new_unique(), account, true),
1695        ]
1696    }
1697
1698    fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1699        let vm_addr = vm_addr as usize;
1700
1701        // calculate size
1702        let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); // pointer size + length size
1703        let singers_length = signers.len();
1704        let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1705
1706        // init data vec
1707        let total_size = fat_ptr_size_of_slice
1708            + singers_length * fat_ptr_size_of_slice
1709            + sum_signers_data_length;
1710        let mut data = vec![0; total_size];
1711
1712        // data is composed by 3 parts
1713        // A.
1714        // [ singers address, singers length, ...,
1715        // B.                                      |
1716        //                                         signer1 address, signer1 length, signer2 address ...,
1717        //                                         ^ p1 --->
1718        // C.                                                                                           |
1719        //                                                                                              signer1 data, signer2 data, ... ]
1720        //                                                                                              ^ p2 --->
1721
1722        // A.
1723        data[..fat_ptr_size_of_slice / 2]
1724            .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1725        data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1726            .clone_from_slice(&(singers_length).to_le_bytes());
1727
1728        // B. + C.
1729        let (mut p1, mut p2) = (
1730            fat_ptr_size_of_slice,
1731            fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1732        );
1733        for signer in signers.iter() {
1734            let signer_length = signer.len();
1735
1736            // B.
1737            data[p1..p1 + fat_ptr_size_of_slice / 2]
1738                .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1739            data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1740                .clone_from_slice(&(signer_length).to_le_bytes());
1741            p1 += fat_ptr_size_of_slice;
1742
1743            // C.
1744            data[p2..p2 + signer_length].clone_from_slice(signer);
1745            p2 += signer_length;
1746        }
1747
1748        let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1749        (data, region)
1750    }
1751
1752    #[test]
1753    fn test_translate_instruction() {
1754        let transaction_accounts =
1755            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1756        mock_invoke_context!(
1757            invoke_context,
1758            transaction_context,
1759            b"instruction data",
1760            transaction_accounts,
1761            0,
1762            &[1]
1763        );
1764
1765        let program_id = Pubkey::new_unique();
1766        let accounts = vec![AccountMeta {
1767            pubkey: Pubkey::new_unique(),
1768            is_signer: true,
1769            is_writable: false,
1770        }];
1771        let data = b"ins data".to_vec();
1772        let vm_addr = MM_INPUT_START;
1773        let (_mem, region) = MockInstruction {
1774            program_id,
1775            accounts: accounts.clone(),
1776            data: data.clone(),
1777        }
1778        .into_region(vm_addr);
1779
1780        let config = Config {
1781            aligned_memory_mapping: false,
1782            ..Config::default()
1783        };
1784        let memory_mapping =
1785            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1786        invoke_context
1787            .memory_contexts
1788            .mock_set_mapping_abi_v1(memory_mapping);
1789
1790        let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1791        assert_eq!(ins.program_id, program_id);
1792        assert_eq!(ins.accounts, accounts);
1793        assert_eq!(ins.data, data);
1794    }
1795
1796    #[test]
1797    fn test_translate_signers() {
1798        let transaction_accounts =
1799            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1800        mock_invoke_context!(
1801            invoke_context,
1802            transaction_context,
1803            b"instruction data",
1804            transaction_accounts,
1805            0,
1806            &[1]
1807        );
1808
1809        let program_id = Pubkey::new_unique();
1810        let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1811
1812        let vm_addr = MM_INPUT_START;
1813        let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1814
1815        let config = Config {
1816            aligned_memory_mapping: false,
1817            ..Config::default()
1818        };
1819        let mapping =
1820            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1821        invoke_context
1822            .memory_contexts
1823            .set_memory_context_abi_v1(MemoryContext::new(
1824                BpfAllocator::new(0),
1825                Vec::new(),
1826                mapping,
1827            ))
1828            .unwrap();
1829
1830        let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1831        assert_eq!(signers[0], derived_key);
1832    }
1833
1834    #[test]
1835    fn test_translate_accounts_rust() {
1836        let transaction_accounts =
1837            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1838        let account = transaction_accounts[1].1.clone();
1839        let key = transaction_accounts[1].0;
1840        let original_data_len = account.data().len();
1841
1842        let vm_addr = MM_STACK_START;
1843        let (_mem, region, account_metadata) =
1844            MockAccountInfo::new(key, &account).into_region(vm_addr);
1845
1846        let config = Config {
1847            aligned_memory_mapping: false,
1848            ..Config::default()
1849        };
1850        let memory_mapping =
1851            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1852
1853        mock_invoke_context!(
1854            invoke_context,
1855            transaction_context,
1856            b"instruction data",
1857            transaction_accounts,
1858            0,
1859            &[1, 1]
1860        );
1861
1862        invoke_context
1863            .memory_contexts
1864            .set_memory_context_abi_v1(MemoryContext::new(
1865                BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1866                vec![account_metadata],
1867                memory_mapping,
1868            ))
1869            .unwrap();
1870
1871        invoke_context
1872            .transaction_context
1873            .configure_next_cpi_for_tests(
1874                0,
1875                vec![
1876                    InstructionAccount::new(1, false, true),
1877                    InstructionAccount::new(1, false, true),
1878                ],
1879                vec![],
1880            )
1881            .unwrap();
1882
1883        let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1884        assert_eq!(accounts.len(), 1);
1885        let caller_account = &accounts[0].caller_account;
1886        assert_eq!(caller_account.serialized_data, account.data());
1887        assert_eq!(caller_account.original_data_len, original_data_len);
1888    }
1889
1890    #[test]
1891    fn test_get_serialized_data() {
1892        let transaction_accounts =
1893            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1894        let account = transaction_accounts[1].1.clone();
1895        mock_invoke_context!(
1896            invoke_context,
1897            transaction_context,
1898            b"instruction data",
1899            transaction_accounts,
1900            0,
1901            &[1]
1902        );
1903
1904        let config = Config {
1905            aligned_memory_mapping: false,
1906            ..Config::default()
1907        };
1908        let memory_mapping =
1909            unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1910        let serialized_data = unsafe {
1911            CallerAccount::get_serialized_data(
1912                &memory_mapping,
1913                true, // check_aligned
1914                MM_INPUT_START,
1915                account.data().len(),
1916                account
1917                    .data()
1918                    .len()
1919                    .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1920                    .saturating_add(1),
1921                true,  // virtual_address_space_adjustments
1922                false, // account_data_direct_mapping
1923            )
1924        };
1925
1926        assert_matches!(
1927            serialized_data,
1928            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1929        );
1930    }
1931
1932    #[test]
1933    fn test_caller_account_from_account_info() {
1934        let transaction_accounts =
1935            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1936        let account = transaction_accounts[1].1.clone();
1937        mock_invoke_context!(
1938            invoke_context,
1939            transaction_context,
1940            b"instruction data",
1941            transaction_accounts,
1942            0,
1943            &[1]
1944        );
1945
1946        let key = Pubkey::new_unique();
1947        let vm_addr = MM_STACK_START;
1948        let (_mem, region, account_metadata) =
1949            MockAccountInfo::new(key, &account).into_region(vm_addr);
1950
1951        let config = Config {
1952            aligned_memory_mapping: false,
1953            ..Config::default()
1954        };
1955        let memory_mapping =
1956            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1957
1958        let account_info =
1959            translate_type::<stable::AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1960
1961        invoke_context
1962            .memory_contexts
1963            .mock_set_mapping_abi_v1(memory_mapping);
1964        let check_aligned = invoke_context.get_check_aligned();
1965        let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
1966        let caller_account = CallerAccount::from_account_info(
1967            &invoke_context,
1968            memory_mapping,
1969            check_aligned,
1970            vm_addr,
1971            account_info,
1972            &account_metadata,
1973        )
1974        .unwrap();
1975        assert_eq!(*caller_account.lamports, account.lamports());
1976        assert_eq!(caller_account.owner, account.owner());
1977        assert_eq!(caller_account.original_data_len, account.data().len());
1978        assert_eq!(
1979            *caller_account.ref_to_len_in_vm as usize,
1980            account.data().len()
1981        );
1982        assert_eq!(caller_account.serialized_data, account.data());
1983    }
1984
1985    #[case(false, false)]
1986    #[case(true, false)]
1987    #[case(true, true)]
1988    fn test_update_caller_account_lamports_owner(
1989        virtual_address_space_adjustments: bool,
1990        account_data_direct_mapping: bool,
1991    ) {
1992        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1993        let account = transaction_accounts[1].1.clone();
1994        mock_invoke_context!(
1995            invoke_context,
1996            transaction_context,
1997            b"instruction data",
1998            transaction_accounts,
1999            0,
2000            &[1]
2001        );
2002
2003        let mut mock_caller_account =
2004            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2005
2006        let config = Config {
2007            aligned_memory_mapping: false,
2008            ..Config::default()
2009        };
2010        let memory_mapping = unsafe {
2011            MemoryMapping::new(
2012                mock_caller_account.regions.split_off(0),
2013                &config,
2014                SBPFVersion::V3,
2015            )
2016            .unwrap()
2017        };
2018        invoke_context
2019            .memory_contexts
2020            .mock_set_mapping_abi_v1(memory_mapping);
2021
2022        let mut caller_account = mock_caller_account.caller_account();
2023        let instruction_context = invoke_context
2024            .transaction_context
2025            .get_current_instruction_context()
2026            .unwrap();
2027        let mut callee_account = instruction_context
2028            .try_borrow_instruction_account(0)
2029            .unwrap();
2030        callee_account.set_lamports(42).unwrap();
2031        callee_account
2032            .set_owner(Pubkey::new_unique().as_ref())
2033            .unwrap();
2034
2035        update_caller_account(
2036            &invoke_context,
2037            true, // check_aligned
2038            &mut caller_account,
2039            &mut callee_account,
2040            virtual_address_space_adjustments,
2041            account_data_direct_mapping,
2042        )
2043        .unwrap();
2044
2045        assert_eq!(*caller_account.lamports, 42);
2046        assert_eq!(caller_account.owner, callee_account.get_owner());
2047    }
2048
2049    #[test]
2050    fn test_update_caller_account_data() {
2051        let transaction_accounts =
2052            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2053        let account = transaction_accounts[1].1.clone();
2054        let original_data_len = account.data().len();
2055
2056        mock_invoke_context!(
2057            invoke_context,
2058            transaction_context,
2059            b"instruction data",
2060            transaction_accounts,
2061            0,
2062            &[1]
2063        );
2064
2065        let mut mock_caller_account =
2066            MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2067
2068        let config = Config {
2069            aligned_memory_mapping: false,
2070            ..Config::default()
2071        };
2072        let memory_mapping = unsafe {
2073            MemoryMapping::new(
2074                mock_caller_account.regions.clone(),
2075                &config,
2076                SBPFVersion::V3,
2077            )
2078            .unwrap()
2079        };
2080        invoke_context
2081            .memory_contexts
2082            .mock_set_mapping_abi_v1(memory_mapping);
2083
2084        let data_slice = mock_caller_account.data_slice();
2085        let len_ptr = unsafe {
2086            data_slice
2087                .as_ptr()
2088                .offset(-(mem::size_of::<u64>() as isize))
2089        };
2090        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2091        let mut caller_account = mock_caller_account.caller_account();
2092        let instruction_context = invoke_context
2093            .transaction_context
2094            .get_current_instruction_context()
2095            .unwrap();
2096        let mut callee_account = instruction_context
2097            .try_borrow_instruction_account(0)
2098            .unwrap();
2099
2100        for (new_value, expected_realloc_size) in [
2101            (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2102            (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2103            (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2104        ] {
2105            assert_eq!(caller_account.serialized_data, callee_account.get_data());
2106            callee_account.set_data_from_slice(&new_value).unwrap();
2107
2108            update_caller_account(
2109                &invoke_context,
2110                true, // check_aligned
2111                &mut caller_account,
2112                &mut callee_account,
2113                false, // virtual_address_space_adjustments
2114                false, // account_data_direct_mapping
2115            )
2116            .unwrap();
2117
2118            let data_len = callee_account.get_data().len();
2119            assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2120            assert_eq!(data_len, serialized_len());
2121            assert_eq!(data_len, caller_account.serialized_data.len());
2122            assert_eq!(
2123                callee_account.get_data(),
2124                &caller_account.serialized_data[..data_len]
2125            );
2126            assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2127            assert!(is_zeroed(&data_slice[data_len..]));
2128        }
2129
2130        callee_account
2131            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2132            .unwrap();
2133        update_caller_account(
2134            &invoke_context,
2135            true, // check_aligned
2136            &mut caller_account,
2137            &mut callee_account,
2138            false, // virtual_address_space_adjustments
2139            false, // account_data_direct_mapping
2140        )
2141        .unwrap();
2142        let data_len = callee_account.get_data().len();
2143        assert_eq!(data_slice[data_len..].len(), 0);
2144        assert!(is_zeroed(&data_slice[data_len..]));
2145
2146        callee_account
2147            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2148            .unwrap();
2149        assert_matches!(
2150            update_caller_account(
2151                &invoke_context,
2152                true, // check_aligned
2153                &mut caller_account,
2154                &mut callee_account,
2155                false, // virtual_address_space_adjustments
2156                false, // account_data_direct_mapping
2157            ),
2158            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2159        );
2160
2161        // close the account
2162        callee_account.set_data_length(0).unwrap();
2163        callee_account
2164            .set_owner(system_program::id().as_ref())
2165            .unwrap();
2166        update_caller_account(
2167            &invoke_context,
2168            true, // check_aligned
2169            &mut caller_account,
2170            &mut callee_account,
2171            false, // virtual_address_space_adjustments
2172            false, // account_data_direct_mapping
2173        )
2174        .unwrap();
2175        let data_len = callee_account.get_data().len();
2176        assert_eq!(data_len, 0);
2177    }
2178
2179    #[case(false, false)]
2180    #[case(true, false)]
2181    #[case(true, true)]
2182    fn test_update_callee_account_lamports_owner(
2183        virtual_address_space_adjustments: bool,
2184        account_data_direct_mapping: bool,
2185    ) {
2186        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2187        let account = transaction_accounts[1].1.clone();
2188
2189        mock_invoke_context!(
2190            invoke_context,
2191            transaction_context,
2192            b"instruction data",
2193            transaction_accounts,
2194            0,
2195            &[1]
2196        );
2197
2198        let mut mock_caller_account =
2199            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2200        let config = Config {
2201            aligned_memory_mapping: false,
2202            ..Config::default()
2203        };
2204        let memory_mapping = unsafe {
2205            MemoryMapping::new(
2206                mock_caller_account.regions.clone(),
2207                &config,
2208                SBPFVersion::V3,
2209            )
2210            .unwrap()
2211        };
2212        let caller_account = mock_caller_account.caller_account();
2213
2214        borrow_instruction_account!(callee_account, invoke_context, 0);
2215
2216        *caller_account.lamports = 42;
2217        *caller_account.owner = Pubkey::new_unique();
2218
2219        update_callee_account(
2220            &memory_mapping,
2221            true, // check_aligned
2222            &caller_account,
2223            callee_account,
2224            virtual_address_space_adjustments,
2225            account_data_direct_mapping,
2226        )
2227        .unwrap();
2228
2229        borrow_instruction_account!(callee_account, invoke_context, 0);
2230        assert_eq!(callee_account.get_lamports(), 42);
2231        assert_eq!(caller_account.owner, callee_account.get_owner());
2232    }
2233
2234    #[case(false, false)]
2235    #[case(true, false)]
2236    #[case(true, true)]
2237    fn test_update_callee_account_data_writable(
2238        virtual_address_space_adjustments: bool,
2239        account_data_direct_mapping: bool,
2240    ) {
2241        let transaction_accounts =
2242            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2243        let account = transaction_accounts[1].1.clone();
2244
2245        mock_invoke_context!(
2246            invoke_context,
2247            transaction_context,
2248            b"instruction data",
2249            transaction_accounts,
2250            0,
2251            &[1]
2252        );
2253
2254        let mut mock_caller_account =
2255            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2256        let config = Config {
2257            aligned_memory_mapping: false,
2258            ..Config::default()
2259        };
2260        let memory_mapping = unsafe {
2261            MemoryMapping::new(
2262                mock_caller_account.regions.clone(),
2263                &config,
2264                SBPFVersion::V3,
2265            )
2266            .unwrap()
2267        };
2268        let mut caller_account = mock_caller_account.caller_account();
2269        borrow_instruction_account!(callee_account, invoke_context, 0);
2270
2271        // Data is not copied in update_callee_account() with virtual_address_space_adjustments
2272        caller_account.serialized_data[0] = b'b';
2273        update_callee_account(
2274            &memory_mapping,
2275            true, // check_aligned
2276            &caller_account,
2277            callee_account,
2278            false, // virtual_address_space_adjustments,
2279            false, // account_data_direct_mapping
2280        )
2281        .unwrap();
2282        borrow_instruction_account!(callee_account, invoke_context, 0);
2283        assert_eq!(callee_account.get_data(), b"boobar");
2284
2285        // growing resize
2286        let mut data = b"foobarbaz".to_vec();
2287        *caller_account.ref_to_len_in_vm = data.len() as u64;
2288        caller_account.serialized_data = &mut data;
2289        assert_eq!(
2290            update_callee_account(
2291                &memory_mapping,
2292                true, // check_aligned
2293                &caller_account,
2294                callee_account,
2295                virtual_address_space_adjustments,
2296                account_data_direct_mapping,
2297            )
2298            .unwrap(),
2299            virtual_address_space_adjustments,
2300        );
2301
2302        // truncating resize
2303        let mut data = b"baz".to_vec();
2304        *caller_account.ref_to_len_in_vm = data.len() as u64;
2305        caller_account.serialized_data = &mut data;
2306        borrow_instruction_account!(callee_account, invoke_context, 0);
2307        assert_eq!(
2308            update_callee_account(
2309                &memory_mapping,
2310                true, // check_aligned
2311                &caller_account,
2312                callee_account,
2313                virtual_address_space_adjustments,
2314                account_data_direct_mapping,
2315            )
2316            .unwrap(),
2317            virtual_address_space_adjustments,
2318        );
2319
2320        // close the account
2321        let mut data = Vec::new();
2322        caller_account.serialized_data = &mut data;
2323        *caller_account.ref_to_len_in_vm = 0;
2324        let mut owner = system_program::id();
2325        caller_account.owner = &mut owner;
2326        borrow_instruction_account!(callee_account, invoke_context, 0);
2327        update_callee_account(
2328            &memory_mapping,
2329            true, // check_aligned
2330            &caller_account,
2331            callee_account,
2332            virtual_address_space_adjustments,
2333            account_data_direct_mapping,
2334        )
2335        .unwrap();
2336        borrow_instruction_account!(callee_account, invoke_context, 0);
2337        assert_eq!(callee_account.get_data(), b"");
2338    }
2339
2340    #[case(false, false)]
2341    #[case(true, false)]
2342    #[case(true, true)]
2343    fn test_update_callee_account_data_readonly(
2344        virtual_address_space_adjustments: bool,
2345        account_data_direct_mapping: bool,
2346    ) {
2347        let transaction_accounts =
2348            transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2349        let account = transaction_accounts[1].1.clone();
2350
2351        mock_invoke_context!(
2352            invoke_context,
2353            transaction_context,
2354            b"instruction data",
2355            transaction_accounts,
2356            0,
2357            &[1]
2358        );
2359
2360        let mut mock_caller_account =
2361            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2362        let config = Config {
2363            aligned_memory_mapping: false,
2364            ..Config::default()
2365        };
2366        let memory_mapping = unsafe {
2367            MemoryMapping::new(
2368                mock_caller_account.regions.clone(),
2369                &config,
2370                SBPFVersion::V3,
2371            )
2372            .unwrap()
2373        };
2374        let mut caller_account = mock_caller_account.caller_account();
2375        borrow_instruction_account!(callee_account, invoke_context, 0);
2376
2377        // Data is not copied in update_callee_account() with virtual_address_space_adjustments
2378        caller_account.serialized_data[0] = b'b';
2379        assert_matches!(
2380            update_callee_account(
2381                &memory_mapping,
2382                true, // check_aligned
2383                &caller_account,
2384                callee_account,
2385                false, // virtual_address_space_adjustments,
2386                false, // account_data_direct_mapping
2387            ),
2388            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2389        );
2390
2391        // growing resize
2392        let mut data = b"foobarbaz".to_vec();
2393        *caller_account.ref_to_len_in_vm = data.len() as u64;
2394        caller_account.serialized_data = &mut data;
2395        borrow_instruction_account!(callee_account, invoke_context, 0);
2396        assert_matches!(
2397            update_callee_account(
2398                &memory_mapping,
2399                true, // check_aligned
2400                &caller_account,
2401                callee_account,
2402                virtual_address_space_adjustments,
2403                account_data_direct_mapping,
2404            ),
2405            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2406        );
2407
2408        // truncating resize
2409        let mut data = b"baz".to_vec();
2410        *caller_account.ref_to_len_in_vm = data.len() as u64;
2411        caller_account.serialized_data = &mut data;
2412        borrow_instruction_account!(callee_account, invoke_context, 0);
2413        assert_matches!(
2414            update_callee_account(
2415                &memory_mapping,
2416                true, // check_aligned
2417                &caller_account,
2418                callee_account,
2419                virtual_address_space_adjustments,
2420                account_data_direct_mapping,
2421            ),
2422            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2423        );
2424    }
2425}