solana_program_runtime/
cpi.rs

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