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