Skip to main content

solana_program_runtime/
cpi.rs

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