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