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