Skip to main content

solana_program_runtime/
cpi.rs

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