atlas_program_runtime/
cpi.rs

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