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
594        .compute_meter
595        .consume_checked(total_cu_translation_cost)?;
596
597    let mut accounts = Vec::with_capacity(account_metas.len());
598    for account_meta in account_metas {
599        // Before using `account_meta` directly, verify that `is_signer` and `is_writable`
600        // contain valid boolean values to prevent UB.
601        let account_meta = unsafe {
602            let ptr = account_meta.as_ptr();
603            if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
604                || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
605            {
606                return Err(Box::new(InstructionError::InvalidArgument));
607            }
608            // SAFETY: VM memory is initialized, and we have validated that the boolean fields
609            // contain valid data.
610            account_meta.assume_init_ref()
611        };
612
613        accounts.push(account_meta.clone());
614    }
615
616    Ok(Instruction {
617        accounts,
618        data: data.to_vec(),
619        program_id: ix.program_id,
620    })
621}
622
623pub fn translate_accounts_rust<'a>(
624    account_infos_addr: u64,
625    account_infos_len: u64,
626    invoke_context: &InvokeContext,
627) -> Result<Vec<TranslatedAccount<'a>>, Error> {
628    let check_aligned = invoke_context.get_check_aligned();
629    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
630    translate_account_infos(
631        account_infos_addr,
632        account_infos_len,
633        |account_info: &stable::AccountInfo| account_info.key_addr(),
634        invoke_context,
635        memory_mapping,
636        check_aligned,
637        |account_infos, account_info_keys| {
638            translate_accounts_common(
639                &account_info_keys,
640                account_infos,
641                account_infos_addr,
642                invoke_context,
643                memory_mapping,
644                check_aligned,
645                CallerAccount::from_account_info,
646            )
647        },
648    )?
649}
650
651pub fn translate_signers(
652    program_id: &Pubkey,
653    signers_seeds_addr: u64,
654    signers_seeds_len: u64,
655    invoke_context: &InvokeContext,
656) -> Result<Vec<Pubkey>, Error> {
657    let check_aligned = invoke_context.get_check_aligned();
658    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
659    if signers_seeds_len > 0 {
660        let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
661            memory_mapping,
662            signers_seeds_addr,
663            signers_seeds_len,
664            check_aligned,
665        )?;
666        if signers_seeds.len() > MAX_SIGNERS {
667            return Err(Box::new(CpiError::TooManySigners));
668        }
669        Ok(signers_seeds
670            .iter()
671            .map(|signer_seeds| {
672                let untranslated_seeds = translate_slice::<VmSlice<u8>>(
673                    memory_mapping,
674                    signer_seeds.ptr(),
675                    signer_seeds.len(),
676                    check_aligned,
677                )?;
678                if untranslated_seeds.len() > MAX_SEEDS {
679                    return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
680                }
681                let seeds_bytes = untranslated_seeds
682                    .iter()
683                    .map(|untranslated_seed| {
684                        translate_vm_slice(untranslated_seed, memory_mapping, check_aligned)
685                    })
686                    .collect::<Result<Vec<_>, Error>>()?;
687                Pubkey::create_program_address(&seeds_bytes, program_id)
688                    .map_err(|err| Box::new(CpiError::BadSeeds(err)) as Error)
689            })
690            .collect::<Result<Vec<_>, Error>>()?)
691    } else {
692        Ok(vec![])
693    }
694}
695
696pub fn translate_instruction_c(
697    addr: u64,
698    invoke_context: &InvokeContext,
699) -> Result<Instruction, Error> {
700    let check_aligned = invoke_context.get_check_aligned();
701    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
702    let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
703
704    let program_id = translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
705    let account_metas = translate_slice::<mem::MaybeUninit<SolAccountMeta>>(
706        memory_mapping,
707        ix_c.accounts_addr,
708        ix_c.accounts_len,
709        check_aligned,
710    )?;
711    let data = translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?;
712
713    check_instruction_size(ix_c.accounts_len as usize, data.len())?;
714
715    let mut total_cu_translation_cost: u64 = (data.len() as u64)
716        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
717        .unwrap_or(u64::MAX);
718
719    // Each account meta is 34 bytes (32 for pubkey, 1 for is_signer, 1 for is_writable)
720    let account_meta_translation_cost = (ix_c
721        .accounts_len
722        .saturating_mul(size_of::<AccountMeta>() as u64))
723    .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
724    .unwrap_or(u64::MAX);
725
726    total_cu_translation_cost =
727        total_cu_translation_cost.saturating_add(account_meta_translation_cost);
728
729    invoke_context
730        .compute_meter
731        .consume_checked(total_cu_translation_cost)?;
732
733    let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
734    for account_meta in account_metas {
735        // Before using `account_meta` directly, verify that `is_signer` and `is_writable`
736        // contain valid boolean values to prevent UB.
737        let account_meta = unsafe {
738            let ptr = account_meta.as_ptr();
739            if (&raw const (*ptr).is_signer).cast::<u8>().read_volatile() > 1
740                || (&raw const (*ptr).is_writable).cast::<u8>().read_volatile() > 1
741            {
742                return Err(Box::new(InstructionError::InvalidArgument));
743            }
744            // SAFETY: VM memory is initialized, and we have validated that the boolean fields
745            // contain valid data.
746            account_meta.assume_init_ref()
747        };
748        let pubkey =
749            translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
750        accounts.push(AccountMeta {
751            pubkey: *pubkey,
752            is_signer: account_meta.is_signer,
753            is_writable: account_meta.is_writable,
754        });
755    }
756
757    Ok(Instruction {
758        accounts,
759        data: data.to_vec(),
760        program_id: *program_id,
761    })
762}
763
764pub fn translate_accounts_c<'a>(
765    account_infos_addr: u64,
766    account_infos_len: u64,
767    invoke_context: &InvokeContext,
768) -> Result<Vec<TranslatedAccount<'a>>, Error> {
769    let check_aligned = invoke_context.get_check_aligned();
770    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
771    translate_account_infos(
772        account_infos_addr,
773        account_infos_len,
774        |account_info: &SolAccountInfo| account_info.key_addr,
775        invoke_context,
776        memory_mapping,
777        check_aligned,
778        |account_infos, account_info_keys| {
779            translate_accounts_common(
780                &account_info_keys,
781                account_infos,
782                account_infos_addr,
783                invoke_context,
784                memory_mapping,
785                check_aligned,
786                CallerAccount::from_sol_account_info,
787            )
788        },
789    )?
790}
791
792/// Call process instruction, common to both Rust and C
793pub fn cpi_common<S: SyscallInvokeSigned>(
794    invoke_context: &mut InvokeContext,
795    instruction_addr: u64,
796    account_infos_addr: u64,
797    account_infos_len: u64,
798    signers_seeds_addr: u64,
799    signers_seeds_len: u64,
800) -> Result<u64, Error> {
801    // CPI entry.
802    //
803    // Translate the inputs to the syscall and synchronize the caller's account
804    // changes so the callee can see them.
805    let amount = invoke_context.get_execution_cost().invoke_units;
806    invoke_context.compute_meter.consume_checked(amount)?;
807    let virtual_address_space_adjustments = invoke_context
808        .get_feature_set()
809        .virtual_address_space_adjustments;
810    let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
811    let check_aligned = invoke_context.get_check_aligned();
812
813    let instruction = S::translate_instruction(instruction_addr, invoke_context)?;
814    let instruction_context = invoke_context
815        .transaction_context
816        .get_current_instruction_context()?;
817    let caller_program_id = instruction_context.get_program_key()?;
818    let signers = translate_signers(
819        caller_program_id,
820        signers_seeds_addr,
821        signers_seeds_len,
822        invoke_context,
823    )?;
824    check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
825    invoke_context.build_instruction_frame(instruction)?;
826    invoke_context.verify_instruction_accounts(&signers)?;
827
828    let mut accounts =
829        S::translate_accounts(account_infos_addr, account_infos_len, invoke_context)?;
830
831    // before initiating CPI, the caller may have modified the
832    // account (caller_account). We need to update the corresponding
833    // BorrowedAccount (callee_account) so the callee can see the
834    // changes.
835    let transaction_context = &invoke_context.transaction_context;
836    let instruction_context = transaction_context.get_current_instruction_context()?;
837    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
838    for translated_account in accounts.iter_mut() {
839        let callee_account = instruction_context
840            .try_borrow_instruction_account(translated_account.index_in_caller)?;
841        let update_caller = update_callee_account(
842            memory_mapping,
843            check_aligned,
844            &translated_account.caller_account,
845            callee_account,
846            virtual_address_space_adjustments,
847            account_data_direct_mapping,
848        )?;
849        translated_account.update_caller_account_region =
850            translated_account.update_caller_account_info || update_caller;
851    }
852
853    // Process the callee instruction
854    let mut compute_units_consumed = 0;
855    invoke_context
856        .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
857
858    // re-bind to please the borrow checker
859    let transaction_context = &invoke_context.transaction_context;
860    let instruction_context = transaction_context.get_current_instruction_context()?;
861
862    // CPI exit.
863    //
864    // Synchronize the callee's account changes so the caller can see them.
865    for translated_account in accounts.iter_mut() {
866        let mut callee_account = instruction_context
867            .try_borrow_instruction_account(translated_account.index_in_caller)?;
868        if translated_account.update_caller_account_info {
869            update_caller_account(
870                invoke_context,
871                check_aligned,
872                &mut translated_account.caller_account,
873                &mut callee_account,
874                virtual_address_space_adjustments,
875                account_data_direct_mapping,
876            )?;
877        }
878    }
879
880    if virtual_address_space_adjustments {
881        let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
882        for translated_account in accounts.iter() {
883            let mut callee_account = instruction_context
884                .try_borrow_instruction_account(translated_account.index_in_caller)?;
885            if translated_account.update_caller_account_region {
886                unsafe {
887                    // SAFETY: lifetime is valid by construction: we're resetting the caller memory
888                    // region back to the account that was here before the CPI call, meaning that
889                    // the memory region was guaranteed to be live for sufficient duration upon
890                    // call of this function.
891                    update_caller_account_region(
892                        memory_mapping,
893                        check_aligned,
894                        &translated_account.caller_account,
895                        &mut callee_account,
896                        account_data_direct_mapping,
897                    )?;
898                }
899            }
900        }
901    }
902
903    Ok(SUCCESS)
904}
905
906/// Account data and metadata that has been translated from caller space.
907pub struct TranslatedAccount<'a> {
908    pub index_in_caller: IndexOfAccount,
909    pub caller_account: CallerAccount<'a>,
910    pub update_caller_account_region: bool,
911    pub update_caller_account_info: bool,
912}
913
914fn translate_account_infos<T, R>(
915    account_infos_addr: u64,
916    account_infos_len: u64,
917    key_addr: impl Fn(&T) -> u64,
918    invoke_context: &InvokeContext,
919    memory_mapping: &MemoryMapping,
920    check_aligned: bool,
921    cb: impl FnOnce(&[T], Vec<&Pubkey>) -> R,
922) -> Result<R, Error> {
923    // In the same vein as the other check_account_info_pointer() checks, we don't lock
924    // this pointer to a specific address but we don't want it to be inside accounts, or
925    // callees might be able to write to the pointed memory.
926    if account_infos_addr
927        .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
928        >= ebpf::MM_INPUT_START
929    {
930        return Err(CpiError::InvalidPointer.into());
931    }
932
933    let account_infos = translate_slice::<T>(
934        memory_mapping,
935        account_infos_addr,
936        account_infos_len,
937        check_aligned,
938    )?;
939    check_account_infos(account_infos.len())?;
940
941    let account_infos_bytes = account_infos.len().saturating_mul(ACCOUNT_INFO_BYTE_SIZE);
942
943    let amount = (account_infos_bytes as u64)
944        .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
945        .unwrap_or(u64::MAX);
946    invoke_context.compute_meter.consume_checked(amount)?;
947
948    let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
949    #[expect(clippy::needless_range_loop)]
950    for account_index in 0..account_infos_len as usize {
951        #[expect(clippy::indexing_slicing)]
952        let account_info = &account_infos[account_index];
953        account_info_keys.push(translate_type::<Pubkey>(
954            memory_mapping,
955            key_addr(account_info),
956            check_aligned,
957        )?);
958    }
959    Ok(cb(account_infos, account_info_keys))
960}
961
962// Finish translating accounts and build TranslatedAccount from CallerAccount.
963fn translate_accounts_common<'a, T, F>(
964    account_info_keys: &[&Pubkey],
965    account_infos: &[T],
966    account_infos_addr: u64,
967    invoke_context: &InvokeContext,
968    memory_mapping: &MemoryMapping,
969    check_aligned: bool,
970    do_translate: F,
971) -> Result<Vec<TranslatedAccount<'a>>, Error>
972where
973    F: Fn(
974        &InvokeContext,
975        &MemoryMapping,
976        bool,
977        u64,
978        &T,
979        &SerializedAccountMetadata,
980    ) -> Result<CallerAccount<'a>, Error>,
981{
982    let transaction_context = &invoke_context.transaction_context;
983    let next_instruction_context = transaction_context.get_next_instruction_context()?;
984    let next_instruction_accounts = next_instruction_context.instruction_accounts();
985    let instruction_context = transaction_context.get_current_instruction_context()?;
986    let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
987
988    // unwrapping here is fine: we're in a syscall and the method below fails
989    // only outside syscalls
990    let accounts_metadata = &invoke_context
991        .memory_contexts
992        .memory_context_abi_v1()
993        .unwrap()
994        .accounts_metadata;
995
996    for (instruction_account_index, instruction_account) in
997        next_instruction_accounts.iter().enumerate()
998    {
999        if next_instruction_context
1000            .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
1001            .is_some()
1002        {
1003            continue; // Skip duplicate account
1004        }
1005
1006        let index_in_caller = instruction_context
1007            .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
1008        let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
1009        let account_key = invoke_context
1010            .transaction_context
1011            .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
1012
1013        #[expect(deprecated)]
1014        if callee_account.is_executable() {
1015            // Use the known account
1016            let amount = (callee_account.get_data().len() as u64)
1017                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1018                .unwrap_or(u64::MAX);
1019            invoke_context.compute_meter.consume_checked(amount)?;
1020        } else if let Some(caller_account_index) =
1021            account_info_keys.iter().position(|key| *key == account_key)
1022        {
1023            let serialized_metadata =
1024                accounts_metadata
1025                    .get(index_in_caller as usize)
1026                    .ok_or_else(|| {
1027                        ic_msg!(
1028                            invoke_context,
1029                            "Internal error: index mismatch for account {}",
1030                            account_key
1031                        );
1032                        Box::new(InstructionError::MissingAccount) as Error
1033                    })?;
1034
1035            // build the CallerAccount corresponding to this account.
1036            if caller_account_index >= account_infos.len() {
1037                return Err(Box::new(CpiError::InvalidLength));
1038            }
1039            #[expect(clippy::indexing_slicing)]
1040            let caller_account =
1041                do_translate(
1042                    invoke_context,
1043                    memory_mapping,
1044                    check_aligned,
1045                    account_infos_addr.saturating_add(
1046                        caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
1047                    ),
1048                    &account_infos[caller_account_index],
1049                    serialized_metadata,
1050                )?;
1051
1052            let amount = (*caller_account.ref_to_len_in_vm)
1053                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
1054                .unwrap_or(u64::MAX);
1055            invoke_context.compute_meter.consume_checked(amount)?;
1056
1057            accounts.push(TranslatedAccount {
1058                index_in_caller,
1059                caller_account,
1060                update_caller_account_region: true, // overwritten in `cpi_common` via `update_callee_acccount()`
1061                update_caller_account_info: instruction_account.is_writable(),
1062            });
1063        } else {
1064            ic_msg!(
1065                invoke_context,
1066                "Instruction references an unknown account {}",
1067                account_key
1068            );
1069            return Err(Box::new(InstructionError::MissingAccount));
1070        }
1071    }
1072
1073    Ok(accounts)
1074}
1075
1076// Update the given account before executing CPI.
1077//
1078// caller_account and callee_account describe the same account. At CPI entry
1079// caller_account might include changes the caller has made to the account
1080// before executing CPI.
1081//
1082// This method updates callee_account so the CPI callee can see the caller's
1083// changes.
1084//
1085// When true is returned, the caller account must be updated after CPI. This
1086// is only set for virtual_address_space_adjustments when the pointer may have changed.
1087fn update_callee_account(
1088    memory_mapping: &MemoryMapping,
1089    check_aligned: bool,
1090    caller_account: &CallerAccount,
1091    mut callee_account: BorrowedInstructionAccount<'_, '_>,
1092    virtual_address_space_adjustments: bool,
1093    account_data_direct_mapping: bool,
1094) -> Result<bool, Error> {
1095    let mut must_update_caller = false;
1096
1097    if callee_account.get_lamports() != *caller_account.lamports {
1098        callee_account.set_lamports(*caller_account.lamports)?;
1099    }
1100
1101    if virtual_address_space_adjustments {
1102        let prev_len = callee_account.get_data().len();
1103        let post_len = *caller_account.ref_to_len_in_vm as usize;
1104        if prev_len != post_len {
1105            if !account_data_direct_mapping && post_len < prev_len {
1106                // If the account has been shrunk, we're going to zero the unused memory
1107                // *that was previously used*.
1108                let serialized_data = unsafe {
1109                    CallerAccount::get_serialized_data(
1110                        memory_mapping,
1111                        check_aligned,
1112                        caller_account.vm_data_addr,
1113                        caller_account.original_data_len,
1114                        prev_len,
1115                        virtual_address_space_adjustments,
1116                        account_data_direct_mapping,
1117                    )?
1118                };
1119                serialized_data
1120                    .get_mut(post_len..)
1121                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1122                    .fill(0);
1123            }
1124            callee_account.set_data_length(post_len)?;
1125            // pointer to data may have changed, so caller must be updated
1126            must_update_caller = true;
1127        }
1128        if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1129            callee_account.set_data_from_slice(caller_account.serialized_data)?;
1130        }
1131    } else {
1132        // The redundant check helps to avoid the expensive data comparison if we can
1133        match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1134            Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1135            Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1136                return Err(Box::new(err));
1137            }
1138            _ => {}
1139        }
1140    }
1141
1142    // Change the owner at the end so that we are allowed to change the lamports and data before
1143    if callee_account.get_owner() != caller_account.owner {
1144        callee_account.set_owner(caller_account.owner.as_ref())?;
1145        // caller gave ownership and thus write access away, so caller must be updated
1146        must_update_caller = true;
1147    }
1148
1149    Ok(must_update_caller)
1150}
1151
1152/// # Safety
1153///
1154/// The the account data pointed to by `callee_account` must outlive the uses of the
1155/// [`MemoryMapping`].
1156unsafe fn update_caller_account_region(
1157    memory_mapping: &mut MemoryMapping,
1158    check_aligned: bool,
1159    caller_account: &CallerAccount,
1160    callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1161    account_data_direct_mapping: bool,
1162) -> Result<(), Error> {
1163    let is_caller_loader_deprecated = !check_aligned;
1164    let address_space_reserved_for_account =
1165        caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1166
1167    if address_space_reserved_for_account > 0 {
1168        // We can trust vm_data_addr to point to the correct region because we
1169        // enforce that in CallerAccount::from_(sol_)account_info.
1170        let (region_index, region) = memory_mapping
1171            .find_region(caller_account.vm_data_addr)
1172            .ok_or_else(|| Box::new(InstructionError::MissingAccount) as Error)?;
1173        // vm_data_addr must always point to the beginning of the region
1174        let region_start_vm_addr = region.vm_addr_range().start;
1175        debug_assert_eq!(region_start_vm_addr, caller_account.vm_data_addr);
1176        let mut new_region;
1177        if !account_data_direct_mapping {
1178            new_region = region.clone();
1179            modify_memory_region_of_account(callee_account, &mut new_region);
1180        } else {
1181            new_region = create_memory_region_of_account(callee_account, region_start_vm_addr)?;
1182        }
1183        unsafe {
1184            // SAFETY: the lifetime invariants are delegated to the callers of this function. Both
1185            // `modify_memory_region_of_account` and `create_memory_region_of_account` create memory
1186            // regions pointing to valid buffers by the virtue of the region being produced out of
1187            // an intermediate slice, which itself must be wholly valid.
1188            memory_mapping.replace_region(region_index, new_region)?;
1189        }
1190    }
1191
1192    Ok(())
1193}
1194
1195// Update the given account after executing CPI.
1196//
1197// caller_account and callee_account describe to the same account. At CPI exit
1198// callee_account might include changes the callee has made to the account
1199// after executing.
1200//
1201// This method updates caller_account so the CPI caller can see the callee's
1202// changes.
1203fn update_caller_account(
1204    invoke_context: &InvokeContext,
1205    check_aligned: bool,
1206    caller_account: &mut CallerAccount<'_>,
1207    callee_account: &mut BorrowedInstructionAccount<'_, '_>,
1208    virtual_address_space_adjustments: bool,
1209    account_data_direct_mapping: bool,
1210) -> Result<(), Error> {
1211    *caller_account.lamports = callee_account.get_lamports();
1212    *caller_account.owner = *callee_account.get_owner();
1213
1214    let prev_len = *caller_account.ref_to_len_in_vm as usize;
1215    let post_len = callee_account.get_data().len();
1216    let is_caller_loader_deprecated = !check_aligned;
1217    let address_space_reserved_for_account =
1218        caller_account.address_space_reserved_for_account(is_caller_loader_deprecated);
1219
1220    if post_len > address_space_reserved_for_account {
1221        let max_increase =
1222            address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1223        ic_msg!(
1224            invoke_context,
1225            "Account data size realloc limited to {max_increase} in inner instructions",
1226        );
1227        return Err(Box::new(InstructionError::InvalidRealloc));
1228    }
1229
1230    let memory_mapping = invoke_context.memory_contexts.memory_mapping()?;
1231    if prev_len != post_len {
1232        // when virtual_address_space_adjustments is enabled we don't cache the serialized data in
1233        // caller_account.serialized_data. See CallerAccount::from_account_info.
1234        if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1235            // If the account has been shrunk, we're going to zero the unused memory
1236            // *that was previously used*.
1237            if post_len < prev_len {
1238                caller_account
1239                    .serialized_data
1240                    .get_mut(post_len..)
1241                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall) as Error)?
1242                    .fill(0);
1243            }
1244            // Set the length of caller_account.serialized_data to post_len.
1245            unsafe {
1246                caller_account.serialized_data = CallerAccount::get_serialized_data(
1247                    memory_mapping,
1248                    check_aligned,
1249                    caller_account.vm_data_addr,
1250                    caller_account.original_data_len,
1251                    post_len,
1252                    virtual_address_space_adjustments,
1253                    account_data_direct_mapping,
1254                )?;
1255            }
1256        }
1257        // this is the len field in the AccountInfo::data slice
1258        *caller_account.ref_to_len_in_vm = post_len as u64;
1259
1260        // this is the len field in the serialized parameters
1261        let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
1262            memory_mapping,
1263            caller_account
1264                .vm_data_addr
1265                .saturating_sub(std::mem::size_of::<u64>() as u64),
1266            check_aligned,
1267        )?;
1268        *serialized_len_ptr = post_len as u64;
1269    }
1270
1271    if !(virtual_address_space_adjustments && account_data_direct_mapping) {
1272        // Propagate changes in the callee up to the caller.
1273        let to_slice = &mut caller_account.serialized_data;
1274        let from_slice = callee_account
1275            .get_data()
1276            .get(0..post_len)
1277            .ok_or(CpiError::InvalidLength)?;
1278        if to_slice.len() != from_slice.len() {
1279            return Err(Box::new(InstructionError::AccountDataTooSmall));
1280        }
1281        to_slice.copy_from_slice(from_slice);
1282    }
1283
1284    Ok(())
1285}
1286
1287#[allow(clippy::indexing_slicing)]
1288#[allow(clippy::arithmetic_side_effects)]
1289#[cfg(test)]
1290mod tests {
1291    use {
1292        super::*,
1293        crate::{
1294            invoke_context::BpfAllocator,
1295            memory::translate_type,
1296            memory_context::{MemoryContext, SerializedAccountMetadata},
1297            with_mock_invoke_context_with_feature_set,
1298        },
1299        assert_matches::assert_matches,
1300        solana_account::{Account, AccountSharedData, ReadableAccount},
1301        solana_account_info::AccountInfo,
1302        solana_sbpf::{
1303            ebpf::{MM_INPUT_START, MM_STACK_START},
1304            memory_region::MemoryRegion,
1305            program::SBPFVersion,
1306            vm::Config,
1307        },
1308        solana_sdk_ids::{bpf_loader, system_program},
1309        solana_svm_feature_set::SVMFeatureSet,
1310        solana_transaction_context::{
1311            IndexOfAccount, instruction_accounts::InstructionAccount,
1312            transaction_accounts::KeyedAccountSharedData,
1313        },
1314        std::{
1315            cell::{Cell, RefCell},
1316            mem, ptr,
1317            rc::Rc,
1318            slice,
1319        },
1320        test_case::case,
1321    };
1322
1323    macro_rules! mock_invoke_context {
1324        ($invoke_context:ident,
1325         $transaction_context:ident,
1326         $instruction_data:expr,
1327         $transaction_accounts:expr,
1328         $program_account:expr,
1329         $instruction_accounts:expr) => {
1330            let instruction_data = $instruction_data;
1331            let instruction_accounts = $instruction_accounts
1332                .iter()
1333                .map(|index_in_transaction| {
1334                    InstructionAccount::new(
1335                        *index_in_transaction as IndexOfAccount,
1336                        false,
1337                        $transaction_accounts[*index_in_transaction as usize].2,
1338                    )
1339                })
1340                .collect::<Vec<_>>();
1341            let transaction_accounts = $transaction_accounts
1342                .into_iter()
1343                .map(|a| (a.0, a.1))
1344                .collect::<Vec<KeyedAccountSharedData>>();
1345            let mut feature_set = SVMFeatureSet::all_enabled();
1346            feature_set.virtual_address_space_adjustments = false;
1347            feature_set.account_data_direct_mapping = false;
1348            let feature_set = &feature_set;
1349            with_mock_invoke_context_with_feature_set!(
1350                $invoke_context,
1351                $transaction_context,
1352                feature_set,
1353                transaction_accounts
1354            );
1355            $invoke_context
1356                .transaction_context
1357                .configure_top_level_instruction_for_tests(
1358                    $program_account,
1359                    instruction_accounts,
1360                    instruction_data.to_vec(),
1361                )
1362                .unwrap();
1363            $invoke_context.push().unwrap();
1364        };
1365    }
1366
1367    macro_rules! borrow_instruction_account {
1368        ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1369            let instruction_context = $invoke_context
1370                .transaction_context
1371                .get_current_instruction_context()
1372                .unwrap();
1373            let $borrowed_account = instruction_context
1374                .try_borrow_instruction_account($index)
1375                .unwrap();
1376        };
1377    }
1378
1379    fn is_zeroed(data: &[u8]) -> bool {
1380        data.iter().all(|b| *b == 0)
1381    }
1382
1383    struct MockCallerAccount {
1384        lamports: u64,
1385        owner: Pubkey,
1386        vm_addr: u64,
1387        data: Vec<u8>,
1388        len: u64,
1389        regions: Vec<MemoryRegion>,
1390        virtual_address_space_adjustments: bool,
1391    }
1392
1393    impl MockCallerAccount {
1394        fn new(
1395            lamports: u64,
1396            owner: Pubkey,
1397            data: &[u8],
1398            virtual_address_space_adjustments: bool,
1399        ) -> MockCallerAccount {
1400            let vm_addr = MM_INPUT_START;
1401            let mut region_addr = vm_addr;
1402            let region_len = mem::size_of::<u64>()
1403                + if virtual_address_space_adjustments {
1404                    0
1405                } else {
1406                    data.len() + MAX_PERMITTED_DATA_INCREASE
1407                };
1408            let mut d = vec![0; region_len];
1409            let mut regions = vec![];
1410
1411            // always write the [len] part even when virtual_address_space_adjustments
1412            unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1413
1414            // write the account data when not virtual_address_space_adjustments
1415            if !virtual_address_space_adjustments {
1416                d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1417            }
1418
1419            // create a region for [len][data+realloc if !virtual_address_space_adjustments]
1420            regions.push(MemoryRegion::new(&raw mut d[..region_len], vm_addr));
1421            region_addr += region_len as u64;
1422
1423            if virtual_address_space_adjustments {
1424                // create a region for the directly mapped data
1425                regions.push(MemoryRegion::new(&raw const data[..], region_addr));
1426                region_addr += data.len() as u64;
1427
1428                // create a region for the realloc padding
1429                regions.push(MemoryRegion::new(
1430                    &raw mut d[mem::size_of::<u64>()..],
1431                    region_addr,
1432                ));
1433            } else {
1434                // caller_account.serialized_data must have the actual data length
1435                d.truncate(mem::size_of::<u64>() + data.len());
1436            }
1437
1438            MockCallerAccount {
1439                lamports,
1440                owner,
1441                vm_addr,
1442                data: d,
1443                len: data.len() as u64,
1444                regions,
1445                virtual_address_space_adjustments,
1446            }
1447        }
1448
1449        fn data_slice<'a>(&self) -> &'a [u8] {
1450            // lifetime crimes
1451            unsafe {
1452                slice::from_raw_parts(
1453                    self.data[mem::size_of::<u64>()..].as_ptr(),
1454                    self.data.capacity() - mem::size_of::<u64>(),
1455                )
1456            }
1457        }
1458
1459        fn caller_account(&mut self) -> CallerAccount<'_> {
1460            let data = if self.virtual_address_space_adjustments {
1461                &mut []
1462            } else {
1463                &mut self.data[mem::size_of::<u64>()..]
1464            };
1465            CallerAccount {
1466                lamports: &mut self.lamports,
1467                owner: &mut self.owner,
1468                original_data_len: self.len as usize,
1469                serialized_data: data,
1470                vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1471                ref_to_len_in_vm: &mut self.len,
1472            }
1473        }
1474    }
1475
1476    struct MockAccountInfo<'a> {
1477        key: Pubkey,
1478        is_signer: bool,
1479        is_writable: bool,
1480        lamports: u64,
1481        data: &'a [u8],
1482        owner: Pubkey,
1483        executable: bool,
1484        _unused: u64,
1485    }
1486
1487    impl MockAccountInfo<'_> {
1488        fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo<'_> {
1489            MockAccountInfo {
1490                key,
1491                is_signer: false,
1492                is_writable: false,
1493                lamports: account.lamports(),
1494                data: account.data(),
1495                owner: *account.owner(),
1496                executable: account.executable(),
1497                _unused: account.rent_epoch(),
1498            }
1499        }
1500
1501        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
1502            let size = mem::size_of::<AccountInfo>()
1503                + mem::size_of::<Pubkey>() * 2
1504                + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1505                + mem::size_of::<u64>()
1506                + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1507                + self.data.len();
1508            let mut data = vec![0; size];
1509
1510            let vm_addr = vm_addr as usize;
1511            let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1512            let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1513            let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1514            let owner_addr = lamports_addr + mem::size_of::<u64>();
1515            let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1516            let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1517
1518            #[allow(deprecated)]
1519            #[allow(clippy::used_underscore_binding)]
1520            let info = AccountInfo {
1521                key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1522                is_signer: self.is_signer,
1523                is_writable: self.is_writable,
1524                lamports: unsafe {
1525                    Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1526                },
1527                data: unsafe {
1528                    Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1529                },
1530                owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1531                executable: self.executable,
1532                _unused: self._unused,
1533            };
1534
1535            unsafe {
1536                ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1537                ptr::write_unaligned(
1538                    (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1539                    self.key,
1540                );
1541                ptr::write_unaligned(
1542                    (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1543                    RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1544                );
1545                ptr::write_unaligned(
1546                    (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1547                    self.lamports,
1548                );
1549                ptr::write_unaligned(
1550                    (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1551                    self.owner,
1552                );
1553                ptr::write_unaligned(
1554                    (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1555                    RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1556                        data_addr as *mut u8,
1557                        self.data.len(),
1558                    ))),
1559                );
1560                data[data_addr - vm_addr..].copy_from_slice(self.data);
1561            }
1562
1563            let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1564            (
1565                data,
1566                region,
1567                SerializedAccountMetadata {
1568                    vm_addr: vm_addr as u64,
1569                    original_data_len: self.data.len(),
1570                    vm_key_addr: key_addr as u64,
1571                    vm_lamports_addr: lamports_addr as u64,
1572                    vm_owner_addr: owner_addr as u64,
1573                    vm_data_addr: data_addr as u64,
1574                },
1575            )
1576        }
1577    }
1578
1579    struct MockInstruction {
1580        program_id: Pubkey,
1581        accounts: Vec<AccountMeta>,
1582        data: Vec<u8>,
1583    }
1584
1585    impl MockInstruction {
1586        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1587            let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1588
1589            let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1590
1591            let mut data = vec![0; size];
1592
1593            let vm_addr = vm_addr as usize;
1594            let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1595            let data_addr = accounts_addr + accounts_len;
1596
1597            let ins = Instruction {
1598                program_id: self.program_id,
1599                accounts: unsafe {
1600                    Vec::from_raw_parts(
1601                        accounts_addr as *mut _,
1602                        self.accounts.len(),
1603                        self.accounts.len(),
1604                    )
1605                },
1606                data: unsafe {
1607                    Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1608                },
1609            };
1610            let ins = StableInstruction::from(ins);
1611
1612            unsafe {
1613                ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1614                data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1615                    slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1616                );
1617                data[data_addr - vm_addr..].copy_from_slice(&self.data);
1618            }
1619
1620            let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1621            (data, region)
1622        }
1623    }
1624
1625    #[repr(C)]
1626    struct RcBox<T> {
1627        strong: Cell<usize>,
1628        weak: Cell<usize>,
1629        value: T,
1630    }
1631
1632    impl<T> RcBox<T> {
1633        const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1634        fn new(value: T) -> RcBox<T> {
1635            RcBox {
1636                strong: Cell::new(0),
1637                weak: Cell::new(0),
1638                value,
1639            }
1640        }
1641    }
1642
1643    type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1644
1645    fn transaction_with_one_writable_instruction_account(
1646        data: Vec<u8>,
1647    ) -> Vec<TestTransactionAccount> {
1648        let program_id = Pubkey::new_unique();
1649        let account = AccountSharedData::from(Account {
1650            lamports: 1,
1651            data,
1652            owner: program_id,
1653            executable: false,
1654            rent_epoch: 100,
1655        });
1656        vec![
1657            (
1658                program_id,
1659                AccountSharedData::from(Account {
1660                    lamports: 0,
1661                    data: vec![],
1662                    owner: bpf_loader::id(),
1663                    executable: true,
1664                    rent_epoch: 0,
1665                }),
1666                false,
1667            ),
1668            (Pubkey::new_unique(), account, true),
1669        ]
1670    }
1671
1672    fn transaction_with_one_readonly_instruction_account(
1673        data: Vec<u8>,
1674    ) -> Vec<TestTransactionAccount> {
1675        let program_id = Pubkey::new_unique();
1676        let account_owner = Pubkey::new_unique();
1677        let account = AccountSharedData::from(Account {
1678            lamports: 1,
1679            data,
1680            owner: account_owner,
1681            executable: false,
1682            rent_epoch: 100,
1683        });
1684        vec![
1685            (
1686                program_id,
1687                AccountSharedData::from(Account {
1688                    lamports: 0,
1689                    data: vec![],
1690                    owner: bpf_loader::id(),
1691                    executable: true,
1692                    rent_epoch: 0,
1693                }),
1694                false,
1695            ),
1696            (Pubkey::new_unique(), account, true),
1697        ]
1698    }
1699
1700    fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1701        let vm_addr = vm_addr as usize;
1702
1703        // calculate size
1704        let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); // pointer size + length size
1705        let singers_length = signers.len();
1706        let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
1707
1708        // init data vec
1709        let total_size = fat_ptr_size_of_slice
1710            + singers_length * fat_ptr_size_of_slice
1711            + sum_signers_data_length;
1712        let mut data = vec![0; total_size];
1713
1714        // data is composed by 3 parts
1715        // A.
1716        // [ singers address, singers length, ...,
1717        // B.                                      |
1718        //                                         signer1 address, signer1 length, signer2 address ...,
1719        //                                         ^ p1 --->
1720        // C.                                                                                           |
1721        //                                                                                              signer1 data, signer2 data, ... ]
1722        //                                                                                              ^ p2 --->
1723
1724        // A.
1725        data[..fat_ptr_size_of_slice / 2]
1726            .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
1727        data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
1728            .clone_from_slice(&(singers_length).to_le_bytes());
1729
1730        // B. + C.
1731        let (mut p1, mut p2) = (
1732            fat_ptr_size_of_slice,
1733            fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
1734        );
1735        for signer in signers.iter() {
1736            let signer_length = signer.len();
1737
1738            // B.
1739            data[p1..p1 + fat_ptr_size_of_slice / 2]
1740                .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
1741            data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
1742                .clone_from_slice(&(signer_length).to_le_bytes());
1743            p1 += fat_ptr_size_of_slice;
1744
1745            // C.
1746            data[p2..p2 + signer_length].clone_from_slice(signer);
1747            p2 += signer_length;
1748        }
1749
1750        let region = MemoryRegion::new(&raw mut data[..], vm_addr as u64);
1751        (data, region)
1752    }
1753
1754    #[test]
1755    fn test_translate_instruction() {
1756        let transaction_accounts =
1757            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1758        mock_invoke_context!(
1759            invoke_context,
1760            transaction_context,
1761            b"instruction data",
1762            transaction_accounts,
1763            0,
1764            &[1]
1765        );
1766
1767        let program_id = Pubkey::new_unique();
1768        let accounts = vec![AccountMeta {
1769            pubkey: Pubkey::new_unique(),
1770            is_signer: true,
1771            is_writable: false,
1772        }];
1773        let data = b"ins data".to_vec();
1774        let vm_addr = MM_INPUT_START;
1775        let (_mem, region) = MockInstruction {
1776            program_id,
1777            accounts: accounts.clone(),
1778            data: data.clone(),
1779        }
1780        .into_region(vm_addr);
1781
1782        let config = Config {
1783            aligned_memory_mapping: false,
1784            ..Config::default()
1785        };
1786        let memory_mapping =
1787            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1788        invoke_context
1789            .memory_contexts
1790            .mock_set_mapping_abi_v1(memory_mapping);
1791
1792        let ins = translate_instruction_rust(vm_addr, &invoke_context).unwrap();
1793        assert_eq!(ins.program_id, program_id);
1794        assert_eq!(ins.accounts, accounts);
1795        assert_eq!(ins.data, data);
1796    }
1797
1798    #[test]
1799    fn test_translate_signers() {
1800        let transaction_accounts =
1801            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1802        mock_invoke_context!(
1803            invoke_context,
1804            transaction_context,
1805            b"instruction data",
1806            transaction_accounts,
1807            0,
1808            &[1]
1809        );
1810
1811        let program_id = Pubkey::new_unique();
1812        let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1813
1814        let vm_addr = MM_INPUT_START;
1815        let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1816
1817        let config = Config {
1818            aligned_memory_mapping: false,
1819            ..Config::default()
1820        };
1821        let mapping =
1822            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1823        invoke_context
1824            .memory_contexts
1825            .set_memory_context_abi_v1(MemoryContext::new(
1826                BpfAllocator::new(0),
1827                Vec::new(),
1828                mapping,
1829            ))
1830            .unwrap();
1831
1832        let signers = translate_signers(&program_id, vm_addr, 1, &invoke_context).unwrap();
1833        assert_eq!(signers[0], derived_key);
1834    }
1835
1836    #[test]
1837    fn test_translate_accounts_rust() {
1838        let transaction_accounts =
1839            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1840        let account = transaction_accounts[1].1.clone();
1841        let key = transaction_accounts[1].0;
1842        let original_data_len = account.data().len();
1843
1844        let vm_addr = MM_STACK_START;
1845        let (_mem, region, account_metadata) =
1846            MockAccountInfo::new(key, &account).into_region(vm_addr);
1847
1848        let config = Config {
1849            aligned_memory_mapping: false,
1850            ..Config::default()
1851        };
1852        let memory_mapping =
1853            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1854
1855        mock_invoke_context!(
1856            invoke_context,
1857            transaction_context,
1858            b"instruction data",
1859            transaction_accounts,
1860            0,
1861            &[1, 1]
1862        );
1863
1864        invoke_context
1865            .memory_contexts
1866            .set_memory_context_abi_v1(MemoryContext::new(
1867                BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1868                vec![account_metadata],
1869                memory_mapping,
1870            ))
1871            .unwrap();
1872
1873        invoke_context
1874            .transaction_context
1875            .configure_next_cpi_for_tests(
1876                0,
1877                vec![
1878                    InstructionAccount::new(1, false, true),
1879                    InstructionAccount::new(1, false, true),
1880                ],
1881                vec![],
1882            )
1883            .unwrap();
1884
1885        let accounts = translate_accounts_rust(vm_addr, 1, &invoke_context).unwrap();
1886        assert_eq!(accounts.len(), 1);
1887        let caller_account = &accounts[0].caller_account;
1888        assert_eq!(caller_account.serialized_data, account.data());
1889        assert_eq!(caller_account.original_data_len, original_data_len);
1890    }
1891
1892    #[test]
1893    fn test_get_serialized_data() {
1894        let transaction_accounts =
1895            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1896        let account = transaction_accounts[1].1.clone();
1897        mock_invoke_context!(
1898            invoke_context,
1899            transaction_context,
1900            b"instruction data",
1901            transaction_accounts,
1902            0,
1903            &[1]
1904        );
1905
1906        let config = Config {
1907            aligned_memory_mapping: false,
1908            ..Config::default()
1909        };
1910        let memory_mapping =
1911            unsafe { MemoryMapping::new(vec![], &config, SBPFVersion::V3).unwrap() };
1912        let serialized_data = unsafe {
1913            CallerAccount::get_serialized_data(
1914                &memory_mapping,
1915                true, // check_aligned
1916                MM_INPUT_START,
1917                account.data().len(),
1918                account
1919                    .data()
1920                    .len()
1921                    .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1922                    .saturating_add(1),
1923                true,  // virtual_address_space_adjustments
1924                false, // account_data_direct_mapping
1925            )
1926        };
1927
1928        assert_matches!(
1929            serialized_data,
1930            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1931        );
1932    }
1933
1934    #[test]
1935    fn test_caller_account_from_account_info() {
1936        let transaction_accounts =
1937            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1938        let account = transaction_accounts[1].1.clone();
1939        mock_invoke_context!(
1940            invoke_context,
1941            transaction_context,
1942            b"instruction data",
1943            transaction_accounts,
1944            0,
1945            &[1]
1946        );
1947
1948        let key = Pubkey::new_unique();
1949        let vm_addr = MM_STACK_START;
1950        let (_mem, region, account_metadata) =
1951            MockAccountInfo::new(key, &account).into_region(vm_addr);
1952
1953        let config = Config {
1954            aligned_memory_mapping: false,
1955            ..Config::default()
1956        };
1957        let memory_mapping =
1958            unsafe { MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap() };
1959
1960        let account_info =
1961            translate_type::<stable::AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1962
1963        invoke_context
1964            .memory_contexts
1965            .mock_set_mapping_abi_v1(memory_mapping);
1966        let check_aligned = invoke_context.get_check_aligned();
1967        let memory_mapping = invoke_context.memory_contexts.memory_mapping().unwrap();
1968        let caller_account = CallerAccount::from_account_info(
1969            &invoke_context,
1970            memory_mapping,
1971            check_aligned,
1972            vm_addr,
1973            account_info,
1974            &account_metadata,
1975        )
1976        .unwrap();
1977        assert_eq!(*caller_account.lamports, account.lamports());
1978        assert_eq!(caller_account.owner, account.owner());
1979        assert_eq!(caller_account.original_data_len, account.data().len());
1980        assert_eq!(
1981            *caller_account.ref_to_len_in_vm as usize,
1982            account.data().len()
1983        );
1984        assert_eq!(caller_account.serialized_data, account.data());
1985    }
1986
1987    #[case(false, false)]
1988    #[case(true, false)]
1989    #[case(true, true)]
1990    fn test_update_caller_account_lamports_owner(
1991        virtual_address_space_adjustments: bool,
1992        account_data_direct_mapping: bool,
1993    ) {
1994        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1995        let account = transaction_accounts[1].1.clone();
1996        mock_invoke_context!(
1997            invoke_context,
1998            transaction_context,
1999            b"instruction data",
2000            transaction_accounts,
2001            0,
2002            &[1]
2003        );
2004
2005        let mut mock_caller_account =
2006            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2007
2008        let config = Config {
2009            aligned_memory_mapping: false,
2010            ..Config::default()
2011        };
2012        let memory_mapping = unsafe {
2013            MemoryMapping::new(
2014                mock_caller_account.regions.split_off(0),
2015                &config,
2016                SBPFVersion::V3,
2017            )
2018            .unwrap()
2019        };
2020        invoke_context
2021            .memory_contexts
2022            .mock_set_mapping_abi_v1(memory_mapping);
2023
2024        let mut caller_account = mock_caller_account.caller_account();
2025        let instruction_context = invoke_context
2026            .transaction_context
2027            .get_current_instruction_context()
2028            .unwrap();
2029        let mut callee_account = instruction_context
2030            .try_borrow_instruction_account(0)
2031            .unwrap();
2032        callee_account.set_lamports(42).unwrap();
2033        callee_account
2034            .set_owner(Pubkey::new_unique().as_ref())
2035            .unwrap();
2036
2037        update_caller_account(
2038            &invoke_context,
2039            true, // check_aligned
2040            &mut caller_account,
2041            &mut callee_account,
2042            virtual_address_space_adjustments,
2043            account_data_direct_mapping,
2044        )
2045        .unwrap();
2046
2047        assert_eq!(*caller_account.lamports, 42);
2048        assert_eq!(caller_account.owner, callee_account.get_owner());
2049    }
2050
2051    #[test]
2052    fn test_update_caller_account_data() {
2053        let transaction_accounts =
2054            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2055        let account = transaction_accounts[1].1.clone();
2056        let original_data_len = account.data().len();
2057
2058        mock_invoke_context!(
2059            invoke_context,
2060            transaction_context,
2061            b"instruction data",
2062            transaction_accounts,
2063            0,
2064            &[1]
2065        );
2066
2067        let mut mock_caller_account =
2068            MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
2069
2070        let config = Config {
2071            aligned_memory_mapping: false,
2072            ..Config::default()
2073        };
2074        let memory_mapping = unsafe {
2075            MemoryMapping::new(
2076                mock_caller_account.regions.clone(),
2077                &config,
2078                SBPFVersion::V3,
2079            )
2080            .unwrap()
2081        };
2082        invoke_context
2083            .memory_contexts
2084            .mock_set_mapping_abi_v1(memory_mapping);
2085
2086        let data_slice = mock_caller_account.data_slice();
2087        let len_ptr = unsafe {
2088            data_slice
2089                .as_ptr()
2090                .offset(-(mem::size_of::<u64>() as isize))
2091        };
2092        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2093        let mut caller_account = mock_caller_account.caller_account();
2094        let instruction_context = invoke_context
2095            .transaction_context
2096            .get_current_instruction_context()
2097            .unwrap();
2098        let mut callee_account = instruction_context
2099            .try_borrow_instruction_account(0)
2100            .unwrap();
2101
2102        for (new_value, expected_realloc_size) in [
2103            (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
2104            (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
2105            (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
2106        ] {
2107            assert_eq!(caller_account.serialized_data, callee_account.get_data());
2108            callee_account.set_data_from_slice(&new_value).unwrap();
2109
2110            update_caller_account(
2111                &invoke_context,
2112                true, // check_aligned
2113                &mut caller_account,
2114                &mut callee_account,
2115                false, // virtual_address_space_adjustments
2116                false, // account_data_direct_mapping
2117            )
2118            .unwrap();
2119
2120            let data_len = callee_account.get_data().len();
2121            assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
2122            assert_eq!(data_len, serialized_len());
2123            assert_eq!(data_len, caller_account.serialized_data.len());
2124            assert_eq!(
2125                callee_account.get_data(),
2126                &caller_account.serialized_data[..data_len]
2127            );
2128            assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
2129            assert!(is_zeroed(&data_slice[data_len..]));
2130        }
2131
2132        callee_account
2133            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2134            .unwrap();
2135        update_caller_account(
2136            &invoke_context,
2137            true, // check_aligned
2138            &mut caller_account,
2139            &mut callee_account,
2140            false, // virtual_address_space_adjustments
2141            false, // account_data_direct_mapping
2142        )
2143        .unwrap();
2144        let data_len = callee_account.get_data().len();
2145        assert_eq!(data_slice[data_len..].len(), 0);
2146        assert!(is_zeroed(&data_slice[data_len..]));
2147
2148        callee_account
2149            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2150            .unwrap();
2151        assert_matches!(
2152            update_caller_account(
2153                &invoke_context,
2154                true, // check_aligned
2155                &mut caller_account,
2156                &mut callee_account,
2157                false, // virtual_address_space_adjustments
2158                false, // account_data_direct_mapping
2159            ),
2160            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2161        );
2162
2163        // close the account
2164        callee_account.set_data_length(0).unwrap();
2165        callee_account
2166            .set_owner(system_program::id().as_ref())
2167            .unwrap();
2168        update_caller_account(
2169            &invoke_context,
2170            true, // check_aligned
2171            &mut caller_account,
2172            &mut callee_account,
2173            false, // virtual_address_space_adjustments
2174            false, // account_data_direct_mapping
2175        )
2176        .unwrap();
2177        let data_len = callee_account.get_data().len();
2178        assert_eq!(data_len, 0);
2179    }
2180
2181    #[case(false, false)]
2182    #[case(true, false)]
2183    #[case(true, true)]
2184    fn test_update_callee_account_lamports_owner(
2185        virtual_address_space_adjustments: bool,
2186        account_data_direct_mapping: bool,
2187    ) {
2188        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2189        let account = transaction_accounts[1].1.clone();
2190
2191        mock_invoke_context!(
2192            invoke_context,
2193            transaction_context,
2194            b"instruction data",
2195            transaction_accounts,
2196            0,
2197            &[1]
2198        );
2199
2200        let mut mock_caller_account =
2201            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2202        let config = Config {
2203            aligned_memory_mapping: false,
2204            ..Config::default()
2205        };
2206        let memory_mapping = unsafe {
2207            MemoryMapping::new(
2208                mock_caller_account.regions.clone(),
2209                &config,
2210                SBPFVersion::V3,
2211            )
2212            .unwrap()
2213        };
2214        let caller_account = mock_caller_account.caller_account();
2215
2216        borrow_instruction_account!(callee_account, invoke_context, 0);
2217
2218        *caller_account.lamports = 42;
2219        *caller_account.owner = Pubkey::new_unique();
2220
2221        update_callee_account(
2222            &memory_mapping,
2223            true, // check_aligned
2224            &caller_account,
2225            callee_account,
2226            virtual_address_space_adjustments,
2227            account_data_direct_mapping,
2228        )
2229        .unwrap();
2230
2231        borrow_instruction_account!(callee_account, invoke_context, 0);
2232        assert_eq!(callee_account.get_lamports(), 42);
2233        assert_eq!(caller_account.owner, callee_account.get_owner());
2234    }
2235
2236    #[case(false, false)]
2237    #[case(true, false)]
2238    #[case(true, true)]
2239    fn test_update_callee_account_data_writable(
2240        virtual_address_space_adjustments: bool,
2241        account_data_direct_mapping: bool,
2242    ) {
2243        let transaction_accounts =
2244            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2245        let account = transaction_accounts[1].1.clone();
2246
2247        mock_invoke_context!(
2248            invoke_context,
2249            transaction_context,
2250            b"instruction data",
2251            transaction_accounts,
2252            0,
2253            &[1]
2254        );
2255
2256        let mut mock_caller_account =
2257            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2258        let config = Config {
2259            aligned_memory_mapping: false,
2260            ..Config::default()
2261        };
2262        let memory_mapping = unsafe {
2263            MemoryMapping::new(
2264                mock_caller_account.regions.clone(),
2265                &config,
2266                SBPFVersion::V3,
2267            )
2268            .unwrap()
2269        };
2270        let mut caller_account = mock_caller_account.caller_account();
2271        borrow_instruction_account!(callee_account, invoke_context, 0);
2272
2273        // Data is not copied in update_callee_account() with virtual_address_space_adjustments
2274        caller_account.serialized_data[0] = b'b';
2275        update_callee_account(
2276            &memory_mapping,
2277            true, // check_aligned
2278            &caller_account,
2279            callee_account,
2280            false, // virtual_address_space_adjustments,
2281            false, // account_data_direct_mapping
2282        )
2283        .unwrap();
2284        borrow_instruction_account!(callee_account, invoke_context, 0);
2285        assert_eq!(callee_account.get_data(), b"boobar");
2286
2287        // growing resize
2288        let mut data = b"foobarbaz".to_vec();
2289        *caller_account.ref_to_len_in_vm = data.len() as u64;
2290        caller_account.serialized_data = &mut data;
2291        assert_eq!(
2292            update_callee_account(
2293                &memory_mapping,
2294                true, // check_aligned
2295                &caller_account,
2296                callee_account,
2297                virtual_address_space_adjustments,
2298                account_data_direct_mapping,
2299            )
2300            .unwrap(),
2301            virtual_address_space_adjustments,
2302        );
2303
2304        // truncating resize
2305        let mut data = b"baz".to_vec();
2306        *caller_account.ref_to_len_in_vm = data.len() as u64;
2307        caller_account.serialized_data = &mut data;
2308        borrow_instruction_account!(callee_account, invoke_context, 0);
2309        assert_eq!(
2310            update_callee_account(
2311                &memory_mapping,
2312                true, // check_aligned
2313                &caller_account,
2314                callee_account,
2315                virtual_address_space_adjustments,
2316                account_data_direct_mapping,
2317            )
2318            .unwrap(),
2319            virtual_address_space_adjustments,
2320        );
2321
2322        // close the account
2323        let mut data = Vec::new();
2324        caller_account.serialized_data = &mut data;
2325        *caller_account.ref_to_len_in_vm = 0;
2326        let mut owner = system_program::id();
2327        caller_account.owner = &mut owner;
2328        borrow_instruction_account!(callee_account, invoke_context, 0);
2329        update_callee_account(
2330            &memory_mapping,
2331            true, // check_aligned
2332            &caller_account,
2333            callee_account,
2334            virtual_address_space_adjustments,
2335            account_data_direct_mapping,
2336        )
2337        .unwrap();
2338        borrow_instruction_account!(callee_account, invoke_context, 0);
2339        assert_eq!(callee_account.get_data(), b"");
2340    }
2341
2342    #[case(false, false)]
2343    #[case(true, false)]
2344    #[case(true, true)]
2345    fn test_update_callee_account_data_readonly(
2346        virtual_address_space_adjustments: bool,
2347        account_data_direct_mapping: bool,
2348    ) {
2349        let transaction_accounts =
2350            transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2351        let account = transaction_accounts[1].1.clone();
2352
2353        mock_invoke_context!(
2354            invoke_context,
2355            transaction_context,
2356            b"instruction data",
2357            transaction_accounts,
2358            0,
2359            &[1]
2360        );
2361
2362        let mut mock_caller_account =
2363            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
2364        let config = Config {
2365            aligned_memory_mapping: false,
2366            ..Config::default()
2367        };
2368        let memory_mapping = unsafe {
2369            MemoryMapping::new(
2370                mock_caller_account.regions.clone(),
2371                &config,
2372                SBPFVersion::V3,
2373            )
2374            .unwrap()
2375        };
2376        let mut caller_account = mock_caller_account.caller_account();
2377        borrow_instruction_account!(callee_account, invoke_context, 0);
2378
2379        // Data is not copied in update_callee_account() with virtual_address_space_adjustments
2380        caller_account.serialized_data[0] = b'b';
2381        assert_matches!(
2382            update_callee_account(
2383                &memory_mapping,
2384                true, // check_aligned
2385                &caller_account,
2386                callee_account,
2387                false, // virtual_address_space_adjustments,
2388                false, // account_data_direct_mapping
2389            ),
2390            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2391        );
2392
2393        // growing resize
2394        let mut data = b"foobarbaz".to_vec();
2395        *caller_account.ref_to_len_in_vm = data.len() as u64;
2396        caller_account.serialized_data = &mut data;
2397        borrow_instruction_account!(callee_account, invoke_context, 0);
2398        assert_matches!(
2399            update_callee_account(
2400                &memory_mapping,
2401                true, // check_aligned
2402                &caller_account,
2403                callee_account,
2404                virtual_address_space_adjustments,
2405                account_data_direct_mapping,
2406            ),
2407            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2408        );
2409
2410        // truncating resize
2411        let mut data = b"baz".to_vec();
2412        *caller_account.ref_to_len_in_vm = data.len() as u64;
2413        caller_account.serialized_data = &mut data;
2414        borrow_instruction_account!(callee_account, invoke_context, 0);
2415        assert_matches!(
2416            update_callee_account(
2417                &memory_mapping,
2418                true, // check_aligned
2419                &caller_account,
2420                callee_account,
2421                virtual_address_space_adjustments,
2422                account_data_direct_mapping,
2423            ),
2424            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2425        );
2426    }
2427}