clone_solana_bpf_loader_program/syscalls/
cpi.rs

1use {
2    super::*,
3    crate::serialization::account_data_region_memory_state,
4    clone_agave_feature_set::enable_bpf_loader_set_authority_checked_ix,
5    clone_solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
6    clone_solana_measure::measure::Measure,
7    clone_solana_program_runtime::invoke_context::SerializedAccountMetadata,
8    clone_solana_stable_layout::stable_instruction::StableInstruction,
9    clone_solana_transaction_context::BorrowedAccount,
10    scopeguard::defer,
11    solana_sbpf::{
12        ebpf,
13        memory_region::{MemoryRegion, MemoryState},
14    },
15    std::{mem, ptr},
16};
17// consts inlined to avoid solana-program dep
18const MAX_CPI_INSTRUCTION_DATA_LEN: u64 = 10 * 1024;
19#[cfg(test)]
20static_assertions::const_assert_eq!(
21    MAX_CPI_INSTRUCTION_DATA_LEN,
22    clone_solana_program::syscalls::MAX_CPI_INSTRUCTION_DATA_LEN
23);
24const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX;
25#[cfg(test)]
26static_assertions::const_assert_eq!(
27    MAX_CPI_INSTRUCTION_ACCOUNTS,
28    clone_solana_program::syscalls::MAX_CPI_INSTRUCTION_ACCOUNTS
29);
30const MAX_CPI_ACCOUNT_INFOS: usize = 128;
31#[cfg(test)]
32static_assertions::const_assert_eq!(
33    MAX_CPI_ACCOUNT_INFOS,
34    clone_solana_program::syscalls::MAX_CPI_ACCOUNT_INFOS
35);
36
37fn check_account_info_pointer(
38    invoke_context: &InvokeContext,
39    vm_addr: u64,
40    expected_vm_addr: u64,
41    field: &str,
42) -> Result<(), Error> {
43    if vm_addr != expected_vm_addr {
44        ic_msg!(
45            invoke_context,
46            "Invalid account info pointer `{}': {:#x} != {:#x}",
47            field,
48            vm_addr,
49            expected_vm_addr
50        );
51        return Err(SyscallError::InvalidPointer.into());
52    }
53    Ok(())
54}
55
56enum VmValue<'a, 'b, T> {
57    VmAddress {
58        vm_addr: u64,
59        memory_mapping: &'b MemoryMapping<'a>,
60        check_aligned: bool,
61    },
62    // Once direct mapping is activated, this variant can be removed and the
63    // enum can be made a struct.
64    Translated(&'a mut T),
65}
66
67impl<T> VmValue<'_, '_, T> {
68    fn get(&self) -> Result<&T, Error> {
69        match self {
70            VmValue::VmAddress {
71                vm_addr,
72                memory_mapping,
73                check_aligned,
74            } => translate_type(memory_mapping, *vm_addr, *check_aligned),
75            VmValue::Translated(addr) => Ok(*addr),
76        }
77    }
78
79    fn get_mut(&mut self) -> Result<&mut T, Error> {
80        match self {
81            VmValue::VmAddress {
82                vm_addr,
83                memory_mapping,
84                check_aligned,
85            } => translate_type_mut(memory_mapping, *vm_addr, *check_aligned),
86            VmValue::Translated(addr) => Ok(*addr),
87        }
88    }
89}
90
91/// Host side representation of AccountInfo or SolAccountInfo passed to the CPI syscall.
92///
93/// At the start of a CPI, this can be different from the data stored in the
94/// corresponding BorrowedAccount, and needs to be synched.
95struct CallerAccount<'a, 'b> {
96    lamports: &'a mut u64,
97    owner: &'a mut Pubkey,
98    // The original data length of the account at the start of the current
99    // instruction. We use this to determine wether an account was shrunk or
100    // grown before or after CPI, and to derive the vm address of the realloc
101    // region.
102    original_data_len: usize,
103    // This points to the data section for this account, as serialized and
104    // mapped inside the vm (see serialize_parameters() in
105    // BpfExecutor::execute).
106    //
107    // This is only set when direct mapping is off (see the relevant comment in
108    // CallerAccount::from_account_info).
109    serialized_data: &'a mut [u8],
110    // Given the corresponding input AccountInfo::data, vm_data_addr points to
111    // the pointer field and ref_to_len_in_vm points to the length field.
112    vm_data_addr: u64,
113    ref_to_len_in_vm: VmValue<'b, 'a, u64>,
114}
115
116impl<'a, 'b> CallerAccount<'a, 'b> {
117    // Create a CallerAccount given an AccountInfo.
118    fn from_account_info(
119        invoke_context: &InvokeContext,
120        memory_mapping: &'b MemoryMapping<'a>,
121        _vm_addr: u64,
122        account_info: &AccountInfo,
123        account_metadata: &SerializedAccountMetadata,
124    ) -> Result<CallerAccount<'a, 'b>, Error> {
125        let direct_mapping = invoke_context
126            .get_feature_set()
127            .is_active(&feature_set::bpf_account_data_direct_mapping::id());
128
129        if direct_mapping {
130            check_account_info_pointer(
131                invoke_context,
132                account_info.key as *const _ as u64,
133                account_metadata.vm_key_addr,
134                "key",
135            )?;
136            check_account_info_pointer(
137                invoke_context,
138                account_info.owner as *const _ as u64,
139                account_metadata.vm_owner_addr,
140                "owner",
141            )?;
142        }
143
144        // account_info points to host memory. The addresses used internally are
145        // in vm space so they need to be translated.
146        let lamports = {
147            // Double translate lamports out of RefCell
148            let ptr = translate_type::<u64>(
149                memory_mapping,
150                account_info.lamports.as_ptr() as u64,
151                invoke_context.get_check_aligned(),
152            )?;
153            if direct_mapping {
154                if account_info.lamports.as_ptr() as u64 >= ebpf::MM_INPUT_START {
155                    return Err(SyscallError::InvalidPointer.into());
156                }
157
158                check_account_info_pointer(
159                    invoke_context,
160                    *ptr,
161                    account_metadata.vm_lamports_addr,
162                    "lamports",
163                )?;
164            }
165            translate_type_mut::<u64>(memory_mapping, *ptr, invoke_context.get_check_aligned())?
166        };
167
168        let owner = translate_type_mut::<Pubkey>(
169            memory_mapping,
170            account_info.owner as *const _ as u64,
171            invoke_context.get_check_aligned(),
172        )?;
173
174        let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
175            if direct_mapping && account_info.data.as_ptr() as u64 >= ebpf::MM_INPUT_START {
176                return Err(SyscallError::InvalidPointer.into());
177            }
178
179            // Double translate data out of RefCell
180            let data = *translate_type::<&[u8]>(
181                memory_mapping,
182                account_info.data.as_ptr() as *const _ as u64,
183                invoke_context.get_check_aligned(),
184            )?;
185            if direct_mapping {
186                check_account_info_pointer(
187                    invoke_context,
188                    data.as_ptr() as u64,
189                    account_metadata.vm_data_addr,
190                    "data",
191                )?;
192            }
193
194            consume_compute_meter(
195                invoke_context,
196                (data.len() as u64)
197                    .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
198                    .unwrap_or(u64::MAX),
199            )?;
200
201            let ref_to_len_in_vm = if direct_mapping {
202                let vm_addr = (account_info.data.as_ptr() as *const u64 as u64)
203                    .saturating_add(size_of::<u64>() as u64);
204                // In the same vein as the other check_account_info_pointer() checks, we don't lock
205                // this pointer to a specific address but we don't want it to be inside accounts, or
206                // callees might be able to write to the pointed memory.
207                if vm_addr >= ebpf::MM_INPUT_START {
208                    return Err(SyscallError::InvalidPointer.into());
209                }
210                VmValue::VmAddress {
211                    vm_addr,
212                    memory_mapping,
213                    check_aligned: invoke_context.get_check_aligned(),
214                }
215            } else {
216                let translated = translate(
217                    memory_mapping,
218                    AccessType::Store,
219                    (account_info.data.as_ptr() as *const u64 as u64)
220                        .saturating_add(size_of::<u64>() as u64),
221                    8,
222                )? as *mut u64;
223                VmValue::Translated(unsafe { &mut *translated })
224            };
225            let vm_data_addr = data.as_ptr() as u64;
226
227            let serialized_data = if direct_mapping {
228                // when direct mapping is enabled, the permissions on the
229                // realloc region can change during CPI so we must delay
230                // translating until when we know whether we're going to mutate
231                // the realloc region or not. Consider this case:
232                //
233                // [caller can't write to an account] <- we are here
234                // [callee grows and assigns account to the caller]
235                // [caller can now write to the account]
236                //
237                // If we always translated the realloc area here, we'd get a
238                // memory access violation since we can't write to the account
239                // _yet_, but we will be able to once the caller returns.
240                &mut []
241            } else {
242                translate_slice_mut::<u8>(
243                    memory_mapping,
244                    vm_data_addr,
245                    data.len() as u64,
246                    invoke_context.get_check_aligned(),
247                )?
248            };
249            (serialized_data, vm_data_addr, ref_to_len_in_vm)
250        };
251
252        Ok(CallerAccount {
253            lamports,
254            owner,
255            original_data_len: account_metadata.original_data_len,
256            serialized_data,
257            vm_data_addr,
258            ref_to_len_in_vm,
259        })
260    }
261
262    // Create a CallerAccount given a SolAccountInfo.
263    fn from_sol_account_info(
264        invoke_context: &InvokeContext,
265        memory_mapping: &'b MemoryMapping<'a>,
266        vm_addr: u64,
267        account_info: &SolAccountInfo,
268        account_metadata: &SerializedAccountMetadata,
269    ) -> Result<CallerAccount<'a, 'b>, Error> {
270        let direct_mapping = invoke_context
271            .get_feature_set()
272            .is_active(&feature_set::bpf_account_data_direct_mapping::id());
273
274        if direct_mapping {
275            check_account_info_pointer(
276                invoke_context,
277                account_info.key_addr,
278                account_metadata.vm_key_addr,
279                "key",
280            )?;
281
282            check_account_info_pointer(
283                invoke_context,
284                account_info.owner_addr,
285                account_metadata.vm_owner_addr,
286                "owner",
287            )?;
288
289            check_account_info_pointer(
290                invoke_context,
291                account_info.lamports_addr,
292                account_metadata.vm_lamports_addr,
293                "lamports",
294            )?;
295
296            check_account_info_pointer(
297                invoke_context,
298                account_info.data_addr,
299                account_metadata.vm_data_addr,
300                "data",
301            )?;
302        }
303
304        // account_info points to host memory. The addresses used internally are
305        // in vm space so they need to be translated.
306        let lamports = translate_type_mut::<u64>(
307            memory_mapping,
308            account_info.lamports_addr,
309            invoke_context.get_check_aligned(),
310        )?;
311        let owner = translate_type_mut::<Pubkey>(
312            memory_mapping,
313            account_info.owner_addr,
314            invoke_context.get_check_aligned(),
315        )?;
316
317        consume_compute_meter(
318            invoke_context,
319            account_info
320                .data_len
321                .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
322                .unwrap_or(u64::MAX),
323        )?;
324
325        let serialized_data = if direct_mapping {
326            // See comment in CallerAccount::from_account_info()
327            &mut []
328        } else {
329            translate_slice_mut::<u8>(
330                memory_mapping,
331                account_info.data_addr,
332                account_info.data_len,
333                invoke_context.get_check_aligned(),
334            )?
335        };
336
337        // we already have the host addr we want: &mut account_info.data_len.
338        // The account info might be read only in the vm though, so we translate
339        // to ensure we can write. This is tested by programs/sbf/rust/ro_modify
340        // which puts SolAccountInfo in rodata.
341        let data_len_vm_addr = vm_addr
342            .saturating_add(&account_info.data_len as *const u64 as u64)
343            .saturating_sub(account_info as *const _ as *const u64 as u64);
344
345        let ref_to_len_in_vm = if direct_mapping {
346            VmValue::VmAddress {
347                vm_addr: data_len_vm_addr,
348                memory_mapping,
349                check_aligned: invoke_context.get_check_aligned(),
350            }
351        } else {
352            let data_len_addr = translate(
353                memory_mapping,
354                AccessType::Store,
355                data_len_vm_addr,
356                size_of::<u64>() as u64,
357            )?;
358            VmValue::Translated(unsafe { &mut *(data_len_addr as *mut u64) })
359        };
360
361        Ok(CallerAccount {
362            lamports,
363            owner,
364            original_data_len: account_metadata.original_data_len,
365            serialized_data,
366            vm_data_addr: account_info.data_addr,
367            ref_to_len_in_vm,
368        })
369    }
370
371    fn realloc_region(
372        &self,
373        memory_mapping: &'b MemoryMapping<'_>,
374        is_loader_deprecated: bool,
375    ) -> Result<Option<&'a MemoryRegion>, Error> {
376        account_realloc_region(
377            memory_mapping,
378            self.vm_data_addr,
379            self.original_data_len,
380            is_loader_deprecated,
381        )
382    }
383}
384
385type TranslatedAccounts<'a, 'b> = Vec<(IndexOfAccount, Option<CallerAccount<'a, 'b>>)>;
386
387/// Implemented by language specific data structure translators
388trait SyscallInvokeSigned {
389    fn translate_instruction(
390        addr: u64,
391        memory_mapping: &MemoryMapping,
392        invoke_context: &mut InvokeContext,
393    ) -> Result<StableInstruction, Error>;
394    fn translate_accounts<'a, 'b>(
395        instruction_accounts: &[InstructionAccount],
396        program_indices: &[IndexOfAccount],
397        account_infos_addr: u64,
398        account_infos_len: u64,
399        is_loader_deprecated: bool,
400        memory_mapping: &'b MemoryMapping<'a>,
401        invoke_context: &mut InvokeContext,
402    ) -> Result<TranslatedAccounts<'a, 'b>, Error>;
403    fn translate_signers(
404        program_id: &Pubkey,
405        signers_seeds_addr: u64,
406        signers_seeds_len: u64,
407        memory_mapping: &MemoryMapping,
408        invoke_context: &InvokeContext,
409    ) -> Result<Vec<Pubkey>, Error>;
410}
411
412declare_builtin_function!(
413    /// Cross-program invocation called from Rust
414    SyscallInvokeSignedRust,
415    fn rust(
416        invoke_context: &mut InvokeContext,
417        instruction_addr: u64,
418        account_infos_addr: u64,
419        account_infos_len: u64,
420        signers_seeds_addr: u64,
421        signers_seeds_len: u64,
422        memory_mapping: &mut MemoryMapping,
423    ) -> Result<u64, Error> {
424        cpi_common::<Self>(
425            invoke_context,
426            instruction_addr,
427            account_infos_addr,
428            account_infos_len,
429            signers_seeds_addr,
430            signers_seeds_len,
431            memory_mapping,
432        )
433    }
434);
435
436impl SyscallInvokeSigned for SyscallInvokeSignedRust {
437    fn translate_instruction(
438        addr: u64,
439        memory_mapping: &MemoryMapping,
440        invoke_context: &mut InvokeContext,
441    ) -> Result<StableInstruction, Error> {
442        let ix = translate_type::<StableInstruction>(
443            memory_mapping,
444            addr,
445            invoke_context.get_check_aligned(),
446        )?;
447        let account_metas = translate_slice::<AccountMeta>(
448            memory_mapping,
449            ix.accounts.as_vaddr(),
450            ix.accounts.len(),
451            invoke_context.get_check_aligned(),
452        )?;
453        let data = translate_slice::<u8>(
454            memory_mapping,
455            ix.data.as_vaddr(),
456            ix.data.len(),
457            invoke_context.get_check_aligned(),
458        )?
459        .to_vec();
460
461        check_instruction_size(account_metas.len(), data.len(), invoke_context)?;
462
463        if invoke_context
464            .get_feature_set()
465            .is_active(&feature_set::loosen_cpi_size_restriction::id())
466        {
467            consume_compute_meter(
468                invoke_context,
469                (data.len() as u64)
470                    .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
471                    .unwrap_or(u64::MAX),
472            )?;
473        }
474
475        let mut accounts = Vec::with_capacity(account_metas.len());
476        #[allow(clippy::needless_range_loop)]
477        for account_index in 0..account_metas.len() {
478            #[allow(clippy::indexing_slicing)]
479            let account_meta = &account_metas[account_index];
480            if unsafe {
481                std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
482                    || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
483                        > 1
484            } {
485                return Err(Box::new(InstructionError::InvalidArgument));
486            }
487            accounts.push(account_meta.clone());
488        }
489
490        Ok(StableInstruction {
491            accounts: accounts.into(),
492            data: data.into(),
493            program_id: ix.program_id,
494        })
495    }
496
497    fn translate_accounts<'a, 'b>(
498        instruction_accounts: &[InstructionAccount],
499        program_indices: &[IndexOfAccount],
500        account_infos_addr: u64,
501        account_infos_len: u64,
502        is_loader_deprecated: bool,
503        memory_mapping: &'b MemoryMapping<'a>,
504        invoke_context: &mut InvokeContext,
505    ) -> Result<TranslatedAccounts<'a, 'b>, Error> {
506        let (account_infos, account_info_keys) = translate_account_infos(
507            account_infos_addr,
508            account_infos_len,
509            |account_info: &AccountInfo| account_info.key as *const _ as u64,
510            memory_mapping,
511            invoke_context,
512        )?;
513
514        translate_and_update_accounts(
515            instruction_accounts,
516            program_indices,
517            &account_info_keys,
518            account_infos,
519            account_infos_addr,
520            is_loader_deprecated,
521            invoke_context,
522            memory_mapping,
523            CallerAccount::from_account_info,
524        )
525    }
526
527    fn translate_signers(
528        program_id: &Pubkey,
529        signers_seeds_addr: u64,
530        signers_seeds_len: u64,
531        memory_mapping: &MemoryMapping,
532        invoke_context: &InvokeContext,
533    ) -> Result<Vec<Pubkey>, Error> {
534        let mut signers = Vec::new();
535        if signers_seeds_len > 0 {
536            let signers_seeds = translate_slice_of_slices::<VmSlice<u8>>(
537                memory_mapping,
538                signers_seeds_addr,
539                signers_seeds_len,
540                invoke_context.get_check_aligned(),
541            )?;
542            if signers_seeds.len() > MAX_SIGNERS {
543                return Err(Box::new(SyscallError::TooManySigners));
544            }
545            for signer_seeds in signers_seeds.iter() {
546                let untranslated_seeds = translate_slice_of_slices::<u8>(
547                    memory_mapping,
548                    signer_seeds.ptr(),
549                    signer_seeds.len(),
550                    invoke_context.get_check_aligned(),
551                )?;
552                if untranslated_seeds.len() > MAX_SEEDS {
553                    return Err(Box::new(InstructionError::MaxSeedLengthExceeded));
554                }
555                let seeds = untranslated_seeds
556                    .iter()
557                    .map(|untranslated_seed| {
558                        untranslated_seed
559                            .translate(memory_mapping, invoke_context.get_check_aligned())
560                    })
561                    .collect::<Result<Vec<_>, Error>>()?;
562                let signer = Pubkey::create_program_address(&seeds, program_id)
563                    .map_err(SyscallError::BadSeeds)?;
564                signers.push(signer);
565            }
566            Ok(signers)
567        } else {
568            Ok(vec![])
569        }
570    }
571}
572
573/// Rust representation of C's SolInstruction
574#[derive(Debug)]
575#[repr(C)]
576struct SolInstruction {
577    program_id_addr: u64,
578    accounts_addr: u64,
579    accounts_len: u64,
580    data_addr: u64,
581    data_len: u64,
582}
583
584/// Rust representation of C's SolAccountMeta
585#[derive(Debug)]
586#[repr(C)]
587struct SolAccountMeta {
588    pubkey_addr: u64,
589    is_writable: bool,
590    is_signer: bool,
591}
592
593/// Rust representation of C's SolAccountInfo
594#[derive(Debug)]
595#[repr(C)]
596struct SolAccountInfo {
597    key_addr: u64,
598    lamports_addr: u64,
599    data_len: u64,
600    data_addr: u64,
601    owner_addr: u64,
602    rent_epoch: u64,
603    is_signer: bool,
604    is_writable: bool,
605    executable: bool,
606}
607
608/// Rust representation of C's SolSignerSeed
609#[derive(Debug)]
610#[repr(C)]
611struct SolSignerSeedC {
612    addr: u64,
613    len: u64,
614}
615
616/// Rust representation of C's SolSignerSeeds
617#[derive(Debug)]
618#[repr(C)]
619struct SolSignerSeedsC {
620    addr: u64,
621    len: u64,
622}
623
624declare_builtin_function!(
625    /// Cross-program invocation called from C
626    SyscallInvokeSignedC,
627    fn rust(
628        invoke_context: &mut InvokeContext,
629        instruction_addr: u64,
630        account_infos_addr: u64,
631        account_infos_len: u64,
632        signers_seeds_addr: u64,
633        signers_seeds_len: u64,
634        memory_mapping: &mut MemoryMapping,
635    ) -> Result<u64, Error> {
636        cpi_common::<Self>(
637            invoke_context,
638            instruction_addr,
639            account_infos_addr,
640            account_infos_len,
641            signers_seeds_addr,
642            signers_seeds_len,
643            memory_mapping,
644        )
645    }
646);
647
648impl SyscallInvokeSigned for SyscallInvokeSignedC {
649    fn translate_instruction(
650        addr: u64,
651        memory_mapping: &MemoryMapping,
652        invoke_context: &mut InvokeContext,
653    ) -> Result<StableInstruction, Error> {
654        let ix_c = translate_type::<SolInstruction>(
655            memory_mapping,
656            addr,
657            invoke_context.get_check_aligned(),
658        )?;
659
660        let program_id = translate_type::<Pubkey>(
661            memory_mapping,
662            ix_c.program_id_addr,
663            invoke_context.get_check_aligned(),
664        )?;
665        let account_metas = translate_slice::<SolAccountMeta>(
666            memory_mapping,
667            ix_c.accounts_addr,
668            ix_c.accounts_len,
669            invoke_context.get_check_aligned(),
670        )?;
671        let data = translate_slice::<u8>(
672            memory_mapping,
673            ix_c.data_addr,
674            ix_c.data_len,
675            invoke_context.get_check_aligned(),
676        )?
677        .to_vec();
678
679        check_instruction_size(ix_c.accounts_len as usize, data.len(), invoke_context)?;
680
681        if invoke_context
682            .get_feature_set()
683            .is_active(&feature_set::loosen_cpi_size_restriction::id())
684        {
685            consume_compute_meter(
686                invoke_context,
687                (data.len() as u64)
688                    .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
689                    .unwrap_or(u64::MAX),
690            )?;
691        }
692
693        let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
694        #[allow(clippy::needless_range_loop)]
695        for account_index in 0..ix_c.accounts_len as usize {
696            #[allow(clippy::indexing_slicing)]
697            let account_meta = &account_metas[account_index];
698            if unsafe {
699                std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
700                    || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
701                        > 1
702            } {
703                return Err(Box::new(InstructionError::InvalidArgument));
704            }
705            let pubkey = translate_type::<Pubkey>(
706                memory_mapping,
707                account_meta.pubkey_addr,
708                invoke_context.get_check_aligned(),
709            )?;
710            accounts.push(AccountMeta {
711                pubkey: *pubkey,
712                is_signer: account_meta.is_signer,
713                is_writable: account_meta.is_writable,
714            });
715        }
716
717        Ok(StableInstruction {
718            accounts: accounts.into(),
719            data: data.into(),
720            program_id: *program_id,
721        })
722    }
723
724    fn translate_accounts<'a, 'b>(
725        instruction_accounts: &[InstructionAccount],
726        program_indices: &[IndexOfAccount],
727        account_infos_addr: u64,
728        account_infos_len: u64,
729        is_loader_deprecated: bool,
730        memory_mapping: &'b MemoryMapping<'a>,
731        invoke_context: &mut InvokeContext,
732    ) -> Result<TranslatedAccounts<'a, 'b>, Error> {
733        let (account_infos, account_info_keys) = translate_account_infos(
734            account_infos_addr,
735            account_infos_len,
736            |account_info: &SolAccountInfo| account_info.key_addr,
737            memory_mapping,
738            invoke_context,
739        )?;
740
741        translate_and_update_accounts(
742            instruction_accounts,
743            program_indices,
744            &account_info_keys,
745            account_infos,
746            account_infos_addr,
747            is_loader_deprecated,
748            invoke_context,
749            memory_mapping,
750            CallerAccount::from_sol_account_info,
751        )
752    }
753
754    fn translate_signers(
755        program_id: &Pubkey,
756        signers_seeds_addr: u64,
757        signers_seeds_len: u64,
758        memory_mapping: &MemoryMapping,
759        invoke_context: &InvokeContext,
760    ) -> Result<Vec<Pubkey>, Error> {
761        if signers_seeds_len > 0 {
762            let signers_seeds = translate_slice::<SolSignerSeedsC>(
763                memory_mapping,
764                signers_seeds_addr,
765                signers_seeds_len,
766                invoke_context.get_check_aligned(),
767            )?;
768            if signers_seeds.len() > MAX_SIGNERS {
769                return Err(Box::new(SyscallError::TooManySigners));
770            }
771            Ok(signers_seeds
772                .iter()
773                .map(|signer_seeds| {
774                    let seeds = translate_slice::<SolSignerSeedC>(
775                        memory_mapping,
776                        signer_seeds.addr,
777                        signer_seeds.len,
778                        invoke_context.get_check_aligned(),
779                    )?;
780                    if seeds.len() > MAX_SEEDS {
781                        return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
782                    }
783                    let seeds_bytes = seeds
784                        .iter()
785                        .map(|seed| {
786                            translate_slice::<u8>(
787                                memory_mapping,
788                                seed.addr,
789                                seed.len,
790                                invoke_context.get_check_aligned(),
791                            )
792                        })
793                        .collect::<Result<Vec<_>, Error>>()?;
794                    Pubkey::create_program_address(&seeds_bytes, program_id)
795                        .map_err(|err| Box::new(SyscallError::BadSeeds(err)) as Error)
796                })
797                .collect::<Result<Vec<_>, Error>>()?)
798        } else {
799            Ok(vec![])
800        }
801    }
802}
803
804fn translate_account_infos<'a, T, F>(
805    account_infos_addr: u64,
806    account_infos_len: u64,
807    key_addr: F,
808    memory_mapping: &MemoryMapping,
809    invoke_context: &mut InvokeContext,
810) -> Result<(&'a [T], Vec<&'a Pubkey>), Error>
811where
812    F: Fn(&T) -> u64,
813{
814    let direct_mapping = invoke_context
815        .get_feature_set()
816        .is_active(&feature_set::bpf_account_data_direct_mapping::id());
817
818    // In the same vein as the other check_account_info_pointer() checks, we don't lock
819    // this pointer to a specific address but we don't want it to be inside accounts, or
820    // callees might be able to write to the pointed memory.
821    if direct_mapping
822        && account_infos_addr
823            .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
824            >= ebpf::MM_INPUT_START
825    {
826        return Err(SyscallError::InvalidPointer.into());
827    }
828
829    let account_infos = translate_slice::<T>(
830        memory_mapping,
831        account_infos_addr,
832        account_infos_len,
833        invoke_context.get_check_aligned(),
834    )?;
835    check_account_infos(account_infos.len(), invoke_context)?;
836    let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
837    #[allow(clippy::needless_range_loop)]
838    for account_index in 0..account_infos_len as usize {
839        #[allow(clippy::indexing_slicing)]
840        let account_info = &account_infos[account_index];
841        account_info_keys.push(translate_type::<Pubkey>(
842            memory_mapping,
843            key_addr(account_info),
844            invoke_context.get_check_aligned(),
845        )?);
846    }
847    Ok((account_infos, account_info_keys))
848}
849
850// Finish translating accounts, build CallerAccount values and update callee
851// accounts in preparation of executing the callee.
852fn translate_and_update_accounts<'a, 'b, T, F>(
853    instruction_accounts: &[InstructionAccount],
854    program_indices: &[IndexOfAccount],
855    account_info_keys: &[&Pubkey],
856    account_infos: &[T],
857    account_infos_addr: u64,
858    is_loader_deprecated: bool,
859    invoke_context: &mut InvokeContext,
860    memory_mapping: &'b MemoryMapping<'a>,
861    do_translate: F,
862) -> Result<TranslatedAccounts<'a, 'b>, Error>
863where
864    F: Fn(
865        &InvokeContext,
866        &'b MemoryMapping<'a>,
867        u64,
868        &T,
869        &SerializedAccountMetadata,
870    ) -> Result<CallerAccount<'a, 'b>, Error>,
871{
872    let transaction_context = &invoke_context.transaction_context;
873    let instruction_context = transaction_context.get_current_instruction_context()?;
874    let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
875
876    let program_account_index = program_indices
877        .last()
878        .ok_or_else(|| Box::new(InstructionError::MissingAccount))?;
879    accounts.push((*program_account_index, None));
880
881    // unwrapping here is fine: we're in a syscall and the method below fails
882    // only outside syscalls
883    let accounts_metadata = &invoke_context
884        .get_syscall_context()
885        .unwrap()
886        .accounts_metadata;
887
888    let direct_mapping = invoke_context
889        .get_feature_set()
890        .is_active(&feature_set::bpf_account_data_direct_mapping::id());
891
892    for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate()
893    {
894        if instruction_account_index as IndexOfAccount != instruction_account.index_in_callee {
895            continue; // Skip duplicate account
896        }
897
898        let callee_account = instruction_context.try_borrow_instruction_account(
899            transaction_context,
900            instruction_account.index_in_caller,
901        )?;
902        let account_key = invoke_context
903            .transaction_context
904            .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
905
906        #[allow(deprecated)]
907        if callee_account.is_executable() {
908            // Use the known account
909            consume_compute_meter(
910                invoke_context,
911                (callee_account.get_data().len() as u64)
912                    .checked_div(invoke_context.get_compute_budget().cpi_bytes_per_unit)
913                    .unwrap_or(u64::MAX),
914            )?;
915
916            accounts.push((instruction_account.index_in_caller, None));
917        } else if let Some(caller_account_index) =
918            account_info_keys.iter().position(|key| *key == account_key)
919        {
920            let serialized_metadata = accounts_metadata
921                .get(instruction_account.index_in_caller as usize)
922                .ok_or_else(|| {
923                    ic_msg!(
924                        invoke_context,
925                        "Internal error: index mismatch for account {}",
926                        account_key
927                    );
928                    Box::new(InstructionError::MissingAccount)
929                })?;
930
931            // build the CallerAccount corresponding to this account.
932            if caller_account_index >= account_infos.len() {
933                return Err(Box::new(SyscallError::InvalidLength));
934            }
935            #[allow(clippy::indexing_slicing)]
936            let caller_account =
937                do_translate(
938                    invoke_context,
939                    memory_mapping,
940                    account_infos_addr.saturating_add(
941                        caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
942                    ),
943                    &account_infos[caller_account_index],
944                    serialized_metadata,
945                )?;
946
947            // before initiating CPI, the caller may have modified the
948            // account (caller_account). We need to update the corresponding
949            // BorrowedAccount (callee_account) so the callee can see the
950            // changes.
951            let update_caller = update_callee_account(
952                invoke_context,
953                memory_mapping,
954                is_loader_deprecated,
955                &caller_account,
956                callee_account,
957                direct_mapping,
958            )?;
959
960            let caller_account = if instruction_account.is_writable || update_caller {
961                Some(caller_account)
962            } else {
963                None
964            };
965            accounts.push((instruction_account.index_in_caller, caller_account));
966        } else {
967            ic_msg!(
968                invoke_context,
969                "Instruction references an unknown account {}",
970                account_key
971            );
972            return Err(Box::new(InstructionError::MissingAccount));
973        }
974    }
975
976    Ok(accounts)
977}
978
979fn check_instruction_size(
980    num_accounts: usize,
981    data_len: usize,
982    invoke_context: &mut InvokeContext,
983) -> Result<(), Error> {
984    if invoke_context
985        .get_feature_set()
986        .is_active(&feature_set::loosen_cpi_size_restriction::id())
987    {
988        let data_len = data_len as u64;
989        let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN;
990        if data_len > max_data_len {
991            return Err(Box::new(SyscallError::MaxInstructionDataLenExceeded {
992                data_len,
993                max_data_len,
994            }));
995        }
996
997        let num_accounts = num_accounts as u64;
998        let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64;
999        if num_accounts > max_accounts {
1000            return Err(Box::new(SyscallError::MaxInstructionAccountsExceeded {
1001                num_accounts,
1002                max_accounts,
1003            }));
1004        }
1005    } else {
1006        let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size;
1007        let size = num_accounts
1008            .saturating_mul(size_of::<AccountMeta>())
1009            .saturating_add(data_len);
1010        if size > max_size {
1011            return Err(Box::new(SyscallError::InstructionTooLarge(size, max_size)));
1012        }
1013    }
1014    Ok(())
1015}
1016
1017fn check_account_infos(
1018    num_account_infos: usize,
1019    invoke_context: &mut InvokeContext,
1020) -> Result<(), Error> {
1021    if invoke_context
1022        .get_feature_set()
1023        .is_active(&feature_set::loosen_cpi_size_restriction::id())
1024    {
1025        let max_cpi_account_infos = if invoke_context
1026            .get_feature_set()
1027            .is_active(&feature_set::increase_tx_account_lock_limit::id())
1028        {
1029            MAX_CPI_ACCOUNT_INFOS
1030        } else {
1031            64
1032        };
1033        let num_account_infos = num_account_infos as u64;
1034        let max_account_infos = max_cpi_account_infos as u64;
1035        if num_account_infos > max_account_infos {
1036            return Err(Box::new(SyscallError::MaxInstructionAccountInfosExceeded {
1037                num_account_infos,
1038                max_account_infos,
1039            }));
1040        }
1041    } else {
1042        let adjusted_len = num_account_infos.saturating_mul(size_of::<Pubkey>());
1043
1044        if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size {
1045            // Cap the number of account_infos a caller can pass to approximate
1046            // maximum that accounts that could be passed in an instruction
1047            return Err(Box::new(SyscallError::TooManyAccounts));
1048        };
1049    }
1050    Ok(())
1051}
1052
1053fn check_authorized_program(
1054    program_id: &Pubkey,
1055    instruction_data: &[u8],
1056    invoke_context: &InvokeContext,
1057) -> Result<(), Error> {
1058    if native_loader::check_id(program_id)
1059        || bpf_loader::check_id(program_id)
1060        || bpf_loader_deprecated::check_id(program_id)
1061        || (clone_solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
1062            && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
1063                || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
1064                || (invoke_context
1065                    .get_feature_set()
1066                    .is_active(&enable_bpf_loader_set_authority_checked_ix::id())
1067                    && bpf_loader_upgradeable::is_set_authority_checked_instruction(
1068                        instruction_data,
1069                    ))
1070                || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
1071        || is_precompile(program_id, |feature_id: &Pubkey| {
1072            invoke_context.get_feature_set().is_active(feature_id)
1073        })
1074    {
1075        return Err(Box::new(SyscallError::ProgramNotSupported(*program_id)));
1076    }
1077    Ok(())
1078}
1079
1080/// Call process instruction, common to both Rust and C
1081fn cpi_common<S: SyscallInvokeSigned>(
1082    invoke_context: &mut InvokeContext,
1083    instruction_addr: u64,
1084    account_infos_addr: u64,
1085    account_infos_len: u64,
1086    signers_seeds_addr: u64,
1087    signers_seeds_len: u64,
1088    memory_mapping: &MemoryMapping,
1089) -> Result<u64, Error> {
1090    // CPI entry.
1091    //
1092    // Translate the inputs to the syscall and synchronize the caller's account
1093    // changes so the callee can see them.
1094    consume_compute_meter(
1095        invoke_context,
1096        invoke_context.get_compute_budget().invoke_units,
1097    )?;
1098    if let Some(execute_time) = invoke_context.execute_time.as_mut() {
1099        execute_time.stop();
1100        invoke_context.timings.execute_us += execute_time.as_us();
1101    }
1102
1103    let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?;
1104    let transaction_context = &invoke_context.transaction_context;
1105    let instruction_context = transaction_context.get_current_instruction_context()?;
1106    let caller_program_id = instruction_context.get_last_program_key(transaction_context)?;
1107    let signers = S::translate_signers(
1108        caller_program_id,
1109        signers_seeds_addr,
1110        signers_seeds_len,
1111        memory_mapping,
1112        invoke_context,
1113    )?;
1114    let is_loader_deprecated = *instruction_context
1115        .try_borrow_last_program_account(transaction_context)?
1116        .get_owner()
1117        == bpf_loader_deprecated::id();
1118    let (instruction_accounts, program_indices) =
1119        invoke_context.prepare_instruction(&instruction, &signers)?;
1120    check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
1121
1122    let mut accounts = S::translate_accounts(
1123        &instruction_accounts,
1124        &program_indices,
1125        account_infos_addr,
1126        account_infos_len,
1127        is_loader_deprecated,
1128        memory_mapping,
1129        invoke_context,
1130    )?;
1131
1132    // Process the callee instruction
1133    let mut compute_units_consumed = 0;
1134    invoke_context.process_instruction(
1135        &instruction.data,
1136        &instruction_accounts,
1137        &program_indices,
1138        &mut compute_units_consumed,
1139        &mut ExecuteTimings::default(),
1140    )?;
1141
1142    // re-bind to please the borrow checker
1143    let transaction_context = &invoke_context.transaction_context;
1144    let instruction_context = transaction_context.get_current_instruction_context()?;
1145
1146    // CPI exit.
1147    //
1148    // Synchronize the callee's account changes so the caller can see them.
1149    let direct_mapping = invoke_context
1150        .get_feature_set()
1151        .is_active(&feature_set::bpf_account_data_direct_mapping::id());
1152
1153    if direct_mapping {
1154        // Update all perms at once before doing account data updates. This
1155        // isn't strictly required as we forbid updates to an account to touch
1156        // other accounts, but since we did have bugs around this in the past,
1157        // it's better to be safe than sorry.
1158        for (index_in_caller, caller_account) in accounts.iter() {
1159            if let Some(caller_account) = caller_account {
1160                let callee_account = instruction_context
1161                    .try_borrow_instruction_account(transaction_context, *index_in_caller)?;
1162                update_caller_account_perms(
1163                    memory_mapping,
1164                    caller_account,
1165                    &callee_account,
1166                    is_loader_deprecated,
1167                )?;
1168            }
1169        }
1170    }
1171
1172    for (index_in_caller, caller_account) in accounts.iter_mut() {
1173        if let Some(caller_account) = caller_account {
1174            let mut callee_account = instruction_context
1175                .try_borrow_instruction_account(transaction_context, *index_in_caller)?;
1176            update_caller_account(
1177                invoke_context,
1178                memory_mapping,
1179                is_loader_deprecated,
1180                caller_account,
1181                &mut callee_account,
1182                direct_mapping,
1183            )?;
1184        }
1185    }
1186
1187    invoke_context.execute_time = Some(Measure::start("execute"));
1188    Ok(SUCCESS)
1189}
1190
1191// Update the given account before executing CPI.
1192//
1193// caller_account and callee_account describe the same account. At CPI entry
1194// caller_account might include changes the caller has made to the account
1195// before executing CPI.
1196//
1197// This method updates callee_account so the CPI callee can see the caller's
1198// changes.
1199//
1200// When true is returned, the caller account must be updated after CPI. This
1201// is only set for direct mapping when the pointer may have changed.
1202fn update_callee_account(
1203    invoke_context: &InvokeContext,
1204    memory_mapping: &MemoryMapping,
1205    is_loader_deprecated: bool,
1206    caller_account: &CallerAccount,
1207    mut callee_account: BorrowedAccount<'_>,
1208    direct_mapping: bool,
1209) -> Result<bool, Error> {
1210    let mut must_update_caller = false;
1211
1212    if callee_account.get_lamports() != *caller_account.lamports {
1213        callee_account.set_lamports(*caller_account.lamports)?;
1214    }
1215
1216    if direct_mapping {
1217        let prev_len = callee_account.get_data().len();
1218        let post_len = *caller_account.ref_to_len_in_vm.get()? as usize;
1219        match callee_account
1220            .can_data_be_resized(post_len)
1221            .and_then(|_| callee_account.can_data_be_changed())
1222        {
1223            Ok(()) => {
1224                let realloc_bytes_used = post_len.saturating_sub(caller_account.original_data_len);
1225                // bpf_loader_deprecated programs don't have a realloc region
1226                if is_loader_deprecated && realloc_bytes_used > 0 {
1227                    return Err(InstructionError::InvalidRealloc.into());
1228                }
1229                if prev_len != post_len {
1230                    callee_account.set_data_length(post_len)?;
1231                    // pointer to data may have changed, so caller must be updated
1232                    must_update_caller = true;
1233                }
1234                if realloc_bytes_used > 0 {
1235                    let serialized_data = translate_slice::<u8>(
1236                        memory_mapping,
1237                        caller_account
1238                            .vm_data_addr
1239                            .saturating_add(caller_account.original_data_len as u64),
1240                        realloc_bytes_used as u64,
1241                        invoke_context.get_check_aligned(),
1242                    )?;
1243                    callee_account
1244                        .get_data_mut()?
1245                        .get_mut(caller_account.original_data_len..post_len)
1246                        .ok_or(SyscallError::InvalidLength)?
1247                        .copy_from_slice(serialized_data);
1248                }
1249            }
1250            Err(err) if prev_len != post_len => {
1251                return Err(Box::new(err));
1252            }
1253            _ => {}
1254        }
1255    } else {
1256        // The redundant check helps to avoid the expensive data comparison if we can
1257        match callee_account
1258            .can_data_be_resized(caller_account.serialized_data.len())
1259            .and_then(|_| callee_account.can_data_be_changed())
1260        {
1261            Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1262            Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1263                return Err(Box::new(err));
1264            }
1265            _ => {}
1266        }
1267    }
1268
1269    // Change the owner at the end so that we are allowed to change the lamports and data before
1270    if callee_account.get_owner() != caller_account.owner {
1271        callee_account.set_owner(caller_account.owner.as_ref())?;
1272    }
1273
1274    Ok(must_update_caller)
1275}
1276
1277fn update_caller_account_perms(
1278    memory_mapping: &MemoryMapping,
1279    caller_account: &CallerAccount,
1280    callee_account: &BorrowedAccount<'_>,
1281    is_loader_deprecated: bool,
1282) -> Result<(), Error> {
1283    let CallerAccount {
1284        original_data_len,
1285        vm_data_addr,
1286        ..
1287    } = caller_account;
1288
1289    let data_region = account_data_region(memory_mapping, *vm_data_addr, *original_data_len)?;
1290    if let Some(region) = data_region {
1291        region
1292            .state
1293            .set(account_data_region_memory_state(callee_account));
1294    }
1295    let realloc_region = account_realloc_region(
1296        memory_mapping,
1297        *vm_data_addr,
1298        *original_data_len,
1299        is_loader_deprecated,
1300    )?;
1301    if let Some(region) = realloc_region {
1302        region
1303            .state
1304            .set(if callee_account.can_data_be_changed().is_ok() {
1305                MemoryState::Writable
1306            } else {
1307                MemoryState::Readable
1308            });
1309    }
1310
1311    Ok(())
1312}
1313
1314// Update the given account after executing CPI.
1315//
1316// caller_account and callee_account describe to the same account. At CPI exit
1317// callee_account might include changes the callee has made to the account
1318// after executing.
1319//
1320// This method updates caller_account so the CPI caller can see the callee's
1321// changes.
1322fn update_caller_account(
1323    invoke_context: &InvokeContext,
1324    memory_mapping: &MemoryMapping,
1325    is_loader_deprecated: bool,
1326    caller_account: &mut CallerAccount,
1327    callee_account: &mut BorrowedAccount<'_>,
1328    direct_mapping: bool,
1329) -> Result<(), Error> {
1330    *caller_account.lamports = callee_account.get_lamports();
1331    *caller_account.owner = *callee_account.get_owner();
1332
1333    let mut zero_all_mapped_spare_capacity = false;
1334    if direct_mapping {
1335        if let Some(region) = account_data_region(
1336            memory_mapping,
1337            caller_account.vm_data_addr,
1338            caller_account.original_data_len,
1339        )? {
1340            // Since each instruction account is directly mapped in a memory region with a *fixed*
1341            // length, upon returning from CPI we must ensure that the current capacity is at least
1342            // the original length (what is mapped in memory), so that the account's memory region
1343            // never points to an invalid address.
1344            //
1345            // Note that the capacity can be smaller than the original length only if the account is
1346            // reallocated using the AccountSharedData API directly (deprecated) or using
1347            // BorrowedAccount::set_data_from_slice(), which implements an optimization to avoid an
1348            // extra allocation.
1349            let min_capacity = caller_account.original_data_len;
1350            if callee_account.capacity() < min_capacity {
1351                callee_account
1352                    .reserve(min_capacity.saturating_sub(callee_account.get_data().len()))?;
1353                zero_all_mapped_spare_capacity = true;
1354            }
1355
1356            // If an account's data pointer has changed we must update the corresponding
1357            // MemoryRegion in the caller's address space. Address spaces are fixed so we don't need
1358            // to update the MemoryRegion's length.
1359            //
1360            // An account's data pointer can change if the account is reallocated because of CoW,
1361            // because of BorrowedAccount::make_data_mut or by a program that uses the
1362            // AccountSharedData API directly (deprecated).
1363            let callee_ptr = callee_account.get_data().as_ptr() as u64;
1364            if region.host_addr.get() != callee_ptr {
1365                region.host_addr.set(callee_ptr);
1366                zero_all_mapped_spare_capacity = true;
1367            }
1368        }
1369    }
1370
1371    let prev_len = *caller_account.ref_to_len_in_vm.get()? as usize;
1372    let post_len = callee_account.get_data().len();
1373    if prev_len != post_len {
1374        let max_increase = if direct_mapping && !invoke_context.get_check_aligned() {
1375            0
1376        } else {
1377            MAX_PERMITTED_DATA_INCREASE
1378        };
1379        let data_overflow = post_len
1380            > caller_account
1381                .original_data_len
1382                .saturating_add(max_increase);
1383        if data_overflow {
1384            ic_msg!(
1385                invoke_context,
1386                "Account data size realloc limited to {max_increase} in inner instructions",
1387            );
1388            return Err(Box::new(InstructionError::InvalidRealloc));
1389        }
1390
1391        // If the account has been shrunk, we're going to zero the unused memory
1392        // *that was previously used*.
1393        if post_len < prev_len {
1394            if direct_mapping {
1395                // We have two separate regions to zero out: the account data
1396                // and the realloc region. Here we zero the realloc region, the
1397                // data region is zeroed further down below.
1398                //
1399                // This is done for compatibility but really only necessary for
1400                // the fringe case of a program calling itself, see
1401                // TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS.
1402                //
1403                // Zeroing the realloc region isn't necessary in the normal
1404                // invoke case because consider the following scenario:
1405                //
1406                // 1. Caller grows an account (prev_len > original_data_len)
1407                // 2. Caller assigns the account to the callee (needed for 3 to
1408                //    work)
1409                // 3. Callee shrinks the account (post_len < prev_len)
1410                //
1411                // In order for the caller to assign the account to the callee,
1412                // the caller _must_ either set the account length to zero,
1413                // therefore making prev_len > original_data_len impossible,
1414                // or it must zero the account data, therefore making the
1415                // zeroing we do here redundant.
1416                if prev_len > caller_account.original_data_len {
1417                    // If we get here and prev_len > original_data_len, then
1418                    // we've already returned InvalidRealloc for the
1419                    // bpf_loader_deprecated case.
1420                    debug_assert!(!is_loader_deprecated);
1421
1422                    // Temporarily configure the realloc region as writable then set it back to
1423                    // whatever state it had.
1424                    let realloc_region = caller_account
1425                        .realloc_region(memory_mapping, is_loader_deprecated)?
1426                        .unwrap(); // unwrapping here is fine, we already asserted !is_loader_deprecated
1427                    let original_state = realloc_region.state.replace(MemoryState::Writable);
1428                    defer! {
1429                        realloc_region.state.set(original_state);
1430                    };
1431
1432                    // We need to zero the unused space in the realloc region, starting after the
1433                    // last byte of the new data which might be > original_data_len.
1434                    let dirty_realloc_start = caller_account.original_data_len.max(post_len);
1435                    // and we want to zero up to the old length
1436                    let dirty_realloc_len = prev_len.saturating_sub(dirty_realloc_start);
1437                    let serialized_data = translate_slice_mut::<u8>(
1438                        memory_mapping,
1439                        caller_account
1440                            .vm_data_addr
1441                            .saturating_add(dirty_realloc_start as u64),
1442                        dirty_realloc_len as u64,
1443                        invoke_context.get_check_aligned(),
1444                    )?;
1445                    serialized_data.fill(0);
1446                }
1447            } else {
1448                caller_account
1449                    .serialized_data
1450                    .get_mut(post_len..)
1451                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1452                    .fill(0);
1453            }
1454        }
1455
1456        // when direct mapping is enabled we don't cache the serialized data in
1457        // caller_account.serialized_data. See CallerAccount::from_account_info.
1458        if !direct_mapping {
1459            caller_account.serialized_data = translate_slice_mut::<u8>(
1460                memory_mapping,
1461                caller_account.vm_data_addr,
1462                post_len as u64,
1463                false, // Don't care since it is byte aligned
1464            )?;
1465        }
1466        // this is the len field in the AccountInfo::data slice
1467        *caller_account.ref_to_len_in_vm.get_mut()? = post_len as u64;
1468
1469        // this is the len field in the serialized parameters
1470        let serialized_len_ptr = translate_type_mut::<u64>(
1471            memory_mapping,
1472            caller_account
1473                .vm_data_addr
1474                .saturating_sub(std::mem::size_of::<u64>() as u64),
1475            invoke_context.get_check_aligned(),
1476        )?;
1477        *serialized_len_ptr = post_len as u64;
1478    }
1479
1480    if direct_mapping {
1481        // Here we zero the account data region.
1482        //
1483        // If zero_all_mapped_spare_capacity=true, we need to zero regardless of whether the account
1484        // size changed, because the underlying vector holding the account might have been
1485        // reallocated and contain uninitialized memory in the spare capacity.
1486        //
1487        // See TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION for an example of
1488        // this case.
1489        let spare_len = if zero_all_mapped_spare_capacity {
1490            // In the unlikely case where the account data vector has
1491            // changed - which can happen during CoW - we zero the whole
1492            // extra capacity up to the original data length.
1493            //
1494            // The extra capacity up to original data length is
1495            // accessible from the vm and since it's uninitialized
1496            // memory, it could be a source of non determinism.
1497            caller_account.original_data_len
1498        } else {
1499            // If the allocation has not changed, we only zero the
1500            // difference between the previous and current lengths. The
1501            // rest of the memory contains whatever it contained before,
1502            // which is deterministic.
1503            prev_len
1504        }
1505        .saturating_sub(post_len);
1506
1507        if spare_len > 0 {
1508            let dst = callee_account
1509                .spare_data_capacity_mut()?
1510                .get_mut(..spare_len)
1511                .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1512                .as_mut_ptr();
1513            // Safety: we check bounds above
1514            unsafe { ptr::write_bytes(dst, 0, spare_len) };
1515        }
1516
1517        // Propagate changes to the realloc region in the callee up to the caller.
1518        let realloc_bytes_used = post_len.saturating_sub(caller_account.original_data_len);
1519        if realloc_bytes_used > 0 {
1520            // In the is_loader_deprecated case, we must have failed with
1521            // InvalidRealloc by now.
1522            debug_assert!(!is_loader_deprecated);
1523
1524            let to_slice = {
1525                // If a callee reallocs an account, we write into the caller's
1526                // realloc region regardless of whether the caller has write
1527                // permissions to the account or not. If the callee has been able to
1528                // make changes, it means they had permissions to do so, and here
1529                // we're just going to reflect those changes to the caller's frame.
1530                //
1531                // Therefore we temporarily configure the realloc region as writable
1532                // then set it back to whatever state it had.
1533                let realloc_region = caller_account
1534                    .realloc_region(memory_mapping, is_loader_deprecated)?
1535                    .unwrap(); // unwrapping here is fine, we asserted !is_loader_deprecated
1536                let original_state = realloc_region.state.replace(MemoryState::Writable);
1537                defer! {
1538                    realloc_region.state.set(original_state);
1539                };
1540
1541                translate_slice_mut::<u8>(
1542                    memory_mapping,
1543                    caller_account
1544                        .vm_data_addr
1545                        .saturating_add(caller_account.original_data_len as u64),
1546                    realloc_bytes_used as u64,
1547                    invoke_context.get_check_aligned(),
1548                )?
1549            };
1550            let from_slice = callee_account
1551                .get_data()
1552                .get(caller_account.original_data_len..post_len)
1553                .ok_or(SyscallError::InvalidLength)?;
1554            if to_slice.len() != from_slice.len() {
1555                return Err(Box::new(InstructionError::AccountDataTooSmall));
1556            }
1557            to_slice.copy_from_slice(from_slice);
1558        }
1559    } else {
1560        let to_slice = &mut caller_account.serialized_data;
1561        let from_slice = callee_account
1562            .get_data()
1563            .get(0..post_len)
1564            .ok_or(SyscallError::InvalidLength)?;
1565        if to_slice.len() != from_slice.len() {
1566            return Err(Box::new(InstructionError::AccountDataTooSmall));
1567        }
1568        to_slice.copy_from_slice(from_slice);
1569    }
1570
1571    Ok(())
1572}
1573
1574fn account_data_region<'a>(
1575    memory_mapping: &'a MemoryMapping<'_>,
1576    vm_data_addr: u64,
1577    original_data_len: usize,
1578) -> Result<Option<&'a MemoryRegion>, Error> {
1579    if original_data_len == 0 {
1580        return Ok(None);
1581    }
1582
1583    // We can trust vm_data_addr to point to the correct region because we
1584    // enforce that in CallerAccount::from_(sol_)account_info.
1585    let data_region = memory_mapping.region(AccessType::Load, vm_data_addr)?;
1586    // vm_data_addr must always point to the beginning of the region
1587    debug_assert_eq!(data_region.vm_addr, vm_data_addr);
1588    Ok(Some(data_region))
1589}
1590
1591fn account_realloc_region<'a>(
1592    memory_mapping: &'a MemoryMapping<'_>,
1593    vm_data_addr: u64,
1594    original_data_len: usize,
1595    is_loader_deprecated: bool,
1596) -> Result<Option<&'a MemoryRegion>, Error> {
1597    if is_loader_deprecated {
1598        return Ok(None);
1599    }
1600
1601    let realloc_vm_addr = vm_data_addr.saturating_add(original_data_len as u64);
1602    let realloc_region = memory_mapping.region(AccessType::Load, realloc_vm_addr)?;
1603    debug_assert_eq!(realloc_region.vm_addr, realloc_vm_addr);
1604    debug_assert!((MAX_PERMITTED_DATA_INCREASE
1605        ..MAX_PERMITTED_DATA_INCREASE.saturating_add(BPF_ALIGN_OF_U128))
1606        .contains(&(realloc_region.len as usize)));
1607    debug_assert!(!matches!(realloc_region.state.get(), MemoryState::Cow(_)));
1608    Ok(Some(realloc_region))
1609}
1610
1611#[allow(clippy::indexing_slicing)]
1612#[allow(clippy::arithmetic_side_effects)]
1613#[cfg(test)]
1614mod tests {
1615    use {
1616        super::*,
1617        crate::mock_create_vm,
1618        assert_matches::assert_matches,
1619        clone_agave_feature_set::bpf_account_data_direct_mapping,
1620        clone_solana_account::{Account, AccountSharedData, ReadableAccount},
1621        clone_solana_clock::Epoch,
1622        clone_solana_instruction::Instruction,
1623        clone_solana_program_runtime::{
1624            invoke_context::SerializedAccountMetadata, with_mock_invoke_context,
1625        },
1626        clone_solana_sdk_ids::system_program,
1627        clone_solana_transaction_context::TransactionAccount,
1628        solana_sbpf::{
1629            ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1630        },
1631        std::{
1632            cell::{Cell, RefCell},
1633            mem, ptr,
1634            rc::Rc,
1635            slice,
1636        },
1637    };
1638
1639    macro_rules! mock_invoke_context {
1640        ($invoke_context:ident,
1641         $transaction_context:ident,
1642         $instruction_data:expr,
1643         $transaction_accounts:expr,
1644         $program_accounts:expr,
1645         $instruction_accounts:expr) => {
1646            let program_accounts = $program_accounts;
1647            let instruction_data = $instruction_data;
1648            let instruction_accounts = $instruction_accounts
1649                .iter()
1650                .enumerate()
1651                .map(
1652                    |(index_in_callee, index_in_transaction)| InstructionAccount {
1653                        index_in_transaction: *index_in_transaction as IndexOfAccount,
1654                        index_in_caller: *index_in_transaction as IndexOfAccount,
1655                        index_in_callee: index_in_callee as IndexOfAccount,
1656                        is_signer: false,
1657                        is_writable: $transaction_accounts[*index_in_transaction as usize].2,
1658                    },
1659                )
1660                .collect::<Vec<_>>();
1661            let transaction_accounts = $transaction_accounts
1662                .into_iter()
1663                .map(|a| (a.0, a.1))
1664                .collect::<Vec<TransactionAccount>>();
1665            with_mock_invoke_context!($invoke_context, $transaction_context, transaction_accounts);
1666            let mut feature_set = $invoke_context.get_feature_set().clone();
1667            feature_set.deactivate(&bpf_account_data_direct_mapping::id());
1668            $invoke_context.mock_set_feature_set(Arc::new(feature_set));
1669            $invoke_context
1670                .transaction_context
1671                .get_next_instruction_context()
1672                .unwrap()
1673                .configure(program_accounts, &instruction_accounts, instruction_data);
1674            $invoke_context.push().unwrap();
1675        };
1676    }
1677
1678    macro_rules! borrow_instruction_account {
1679        ($invoke_context:expr, $index:expr) => {{
1680            let instruction_context = $invoke_context
1681                .transaction_context
1682                .get_current_instruction_context()
1683                .unwrap();
1684            instruction_context
1685                .try_borrow_instruction_account($invoke_context.transaction_context, $index)
1686                .unwrap()
1687        }};
1688    }
1689
1690    #[test]
1691    fn test_translate_instruction() {
1692        let transaction_accounts =
1693            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1694        mock_invoke_context!(
1695            invoke_context,
1696            transaction_context,
1697            b"instruction data",
1698            transaction_accounts,
1699            &[0],
1700            &[1]
1701        );
1702
1703        let program_id = Pubkey::new_unique();
1704        let accounts = vec![AccountMeta {
1705            pubkey: Pubkey::new_unique(),
1706            is_signer: true,
1707            is_writable: false,
1708        }];
1709        let data = b"ins data".to_vec();
1710        let vm_addr = MM_INPUT_START;
1711        let (_mem, region) = MockInstruction {
1712            program_id,
1713            accounts: accounts.clone(),
1714            data: data.clone(),
1715        }
1716        .into_region(vm_addr);
1717
1718        let config = Config {
1719            aligned_memory_mapping: false,
1720            ..Config::default()
1721        };
1722        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1723
1724        let ins = SyscallInvokeSignedRust::translate_instruction(
1725            vm_addr,
1726            &memory_mapping,
1727            &mut invoke_context,
1728        )
1729        .unwrap();
1730        assert_eq!(ins.program_id, program_id);
1731        assert_eq!(ins.accounts, accounts);
1732        assert_eq!(ins.data, data);
1733    }
1734
1735    #[test]
1736    fn test_translate_signers() {
1737        let transaction_accounts =
1738            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1739        mock_invoke_context!(
1740            invoke_context,
1741            transaction_context,
1742            b"instruction data",
1743            transaction_accounts,
1744            &[0],
1745            &[1]
1746        );
1747
1748        let program_id = Pubkey::new_unique();
1749        let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1750
1751        let vm_addr = MM_INPUT_START;
1752        let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1753
1754        let config = Config {
1755            aligned_memory_mapping: false,
1756            ..Config::default()
1757        };
1758        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1759
1760        let signers = SyscallInvokeSignedRust::translate_signers(
1761            &program_id,
1762            vm_addr,
1763            1,
1764            &memory_mapping,
1765            &invoke_context,
1766        )
1767        .unwrap();
1768        assert_eq!(signers[0], derived_key);
1769    }
1770
1771    #[test]
1772    fn test_caller_account_from_account_info() {
1773        let transaction_accounts =
1774            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1775        let account = transaction_accounts[1].1.clone();
1776        mock_invoke_context!(
1777            invoke_context,
1778            transaction_context,
1779            b"instruction data",
1780            transaction_accounts,
1781            &[0],
1782            &[1]
1783        );
1784
1785        let key = Pubkey::new_unique();
1786        let vm_addr = MM_INPUT_START;
1787        let (_mem, region, account_metadata) =
1788            MockAccountInfo::new(key, &account).into_region(vm_addr);
1789
1790        let config = Config {
1791            aligned_memory_mapping: false,
1792            ..Config::default()
1793        };
1794        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1795
1796        let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1797
1798        let caller_account = CallerAccount::from_account_info(
1799            &invoke_context,
1800            &memory_mapping,
1801            vm_addr,
1802            account_info,
1803            &account_metadata,
1804        )
1805        .unwrap();
1806        assert_eq!(*caller_account.lamports, account.lamports());
1807        assert_eq!(caller_account.owner, account.owner());
1808        assert_eq!(caller_account.original_data_len, account.data().len());
1809        assert_eq!(
1810            *caller_account.ref_to_len_in_vm.get().unwrap() as usize,
1811            account.data().len()
1812        );
1813        assert_eq!(caller_account.serialized_data, account.data());
1814    }
1815
1816    #[test]
1817    fn test_update_caller_account_lamports_owner() {
1818        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1819        let account = transaction_accounts[1].1.clone();
1820        mock_invoke_context!(
1821            invoke_context,
1822            transaction_context,
1823            b"instruction data",
1824            transaction_accounts,
1825            &[0],
1826            &[1]
1827        );
1828
1829        let mut mock_caller_account = MockCallerAccount::new(
1830            1234,
1831            *account.owner(),
1832            0xFFFFFFFF00000000,
1833            account.data(),
1834            false,
1835        );
1836
1837        let config = Config {
1838            aligned_memory_mapping: false,
1839            ..Config::default()
1840        };
1841        let memory_mapping = MemoryMapping::new(
1842            mock_caller_account.regions.split_off(0),
1843            &config,
1844            SBPFVersion::V3,
1845        )
1846        .unwrap();
1847
1848        let mut caller_account = mock_caller_account.caller_account();
1849
1850        let mut callee_account = borrow_instruction_account!(invoke_context, 0);
1851
1852        callee_account.set_lamports(42).unwrap();
1853        callee_account
1854            .set_owner(Pubkey::new_unique().as_ref())
1855            .unwrap();
1856
1857        update_caller_account(
1858            &invoke_context,
1859            &memory_mapping,
1860            false,
1861            &mut caller_account,
1862            &mut callee_account,
1863            false,
1864        )
1865        .unwrap();
1866
1867        assert_eq!(*caller_account.lamports, 42);
1868        assert_eq!(caller_account.owner, callee_account.get_owner());
1869    }
1870
1871    #[test]
1872    fn test_update_caller_account_data() {
1873        let transaction_accounts =
1874            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1875        let account = transaction_accounts[1].1.clone();
1876        let original_data_len = account.data().len();
1877
1878        mock_invoke_context!(
1879            invoke_context,
1880            transaction_context,
1881            b"instruction data",
1882            transaction_accounts,
1883            &[0],
1884            &[1]
1885        );
1886
1887        let mut mock_caller_account = MockCallerAccount::new(
1888            account.lamports(),
1889            *account.owner(),
1890            0xFFFFFFFF00000000,
1891            account.data(),
1892            false,
1893        );
1894
1895        let config = Config {
1896            aligned_memory_mapping: false,
1897            ..Config::default()
1898        };
1899        let memory_mapping = MemoryMapping::new(
1900            mock_caller_account.regions.split_off(0),
1901            &config,
1902            SBPFVersion::V3,
1903        )
1904        .unwrap();
1905
1906        let data_slice = mock_caller_account.data_slice();
1907        let len_ptr = unsafe {
1908            data_slice
1909                .as_ptr()
1910                .offset(-(mem::size_of::<u64>() as isize))
1911        };
1912        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1913        let mut caller_account = mock_caller_account.caller_account();
1914
1915        let mut callee_account = borrow_instruction_account!(invoke_context, 0);
1916
1917        for (new_value, expected_realloc_size) in [
1918            (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1919            (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1920            (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1921        ] {
1922            assert_eq!(caller_account.serialized_data, callee_account.get_data());
1923            callee_account.set_data_from_slice(&new_value).unwrap();
1924
1925            update_caller_account(
1926                &invoke_context,
1927                &memory_mapping,
1928                false,
1929                &mut caller_account,
1930                &mut callee_account,
1931                false,
1932            )
1933            .unwrap();
1934
1935            let data_len = callee_account.get_data().len();
1936            assert_eq!(
1937                data_len,
1938                *caller_account.ref_to_len_in_vm.get().unwrap() as usize
1939            );
1940            assert_eq!(data_len, serialized_len());
1941            assert_eq!(data_len, caller_account.serialized_data.len());
1942            assert_eq!(
1943                callee_account.get_data(),
1944                &caller_account.serialized_data[..data_len]
1945            );
1946            assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1947            assert!(is_zeroed(&data_slice[data_len..]));
1948        }
1949
1950        callee_account
1951            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1952            .unwrap();
1953        update_caller_account(
1954            &invoke_context,
1955            &memory_mapping,
1956            false,
1957            &mut caller_account,
1958            &mut callee_account,
1959            false,
1960        )
1961        .unwrap();
1962        let data_len = callee_account.get_data().len();
1963        assert_eq!(data_slice[data_len..].len(), 0);
1964        assert!(is_zeroed(&data_slice[data_len..]));
1965
1966        callee_account
1967            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1968            .unwrap();
1969        assert_matches!(
1970            update_caller_account(
1971                &invoke_context,
1972                &memory_mapping,
1973                false,
1974                &mut caller_account,
1975                &mut callee_account,
1976                false,
1977            ),
1978            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1979        );
1980
1981        // close the account
1982        callee_account.set_data_length(0).unwrap();
1983        callee_account
1984            .set_owner(system_program::id().as_ref())
1985            .unwrap();
1986        update_caller_account(
1987            &invoke_context,
1988            &memory_mapping,
1989            false,
1990            &mut caller_account,
1991            &mut callee_account,
1992            false,
1993        )
1994        .unwrap();
1995        let data_len = callee_account.get_data().len();
1996        assert_eq!(data_len, 0);
1997    }
1998
1999    #[test]
2000    fn test_update_caller_account_data_direct_mapping() {
2001        let transaction_accounts =
2002            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2003        let account = transaction_accounts[1].1.clone();
2004        let original_data_len = account.data().len();
2005
2006        mock_invoke_context!(
2007            invoke_context,
2008            transaction_context,
2009            b"instruction data",
2010            transaction_accounts,
2011            &[0],
2012            &[1]
2013        );
2014
2015        let mut mock_caller_account = MockCallerAccount::new(
2016            account.lamports(),
2017            *account.owner(),
2018            0xFFFFFFFF00000000,
2019            account.data(),
2020            true,
2021        );
2022
2023        let config = Config {
2024            aligned_memory_mapping: false,
2025            ..Config::default()
2026        };
2027        let memory_mapping = MemoryMapping::new(
2028            mock_caller_account.regions.split_off(0),
2029            &config,
2030            SBPFVersion::V3,
2031        )
2032        .unwrap();
2033
2034        let data_slice = mock_caller_account.data_slice();
2035        let len_ptr = unsafe {
2036            data_slice
2037                .as_ptr()
2038                .offset(-(mem::size_of::<u64>() as isize))
2039        };
2040        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
2041        let mut caller_account = mock_caller_account.caller_account();
2042
2043        let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2044
2045        for change_ptr in [false, true] {
2046            for (new_value, expected_realloc_used) in [
2047                (b"foobazbad".to_vec(), 3), // > original_data_len, writes into realloc
2048                (b"foo".to_vec(), 0), // < original_data_len, zeroes account capacity + realloc capacity
2049                (b"foobaz".to_vec(), 0), // = original_data_len
2050                (vec![], 0),          // check lower bound
2051            ] {
2052                if change_ptr {
2053                    callee_account.set_data(new_value).unwrap();
2054                } else {
2055                    callee_account.set_data_from_slice(&new_value).unwrap();
2056                }
2057
2058                update_caller_account(
2059                    &invoke_context,
2060                    &memory_mapping,
2061                    false,
2062                    &mut caller_account,
2063                    &mut callee_account,
2064                    true,
2065                )
2066                .unwrap();
2067
2068                // check that the caller account data pointer always matches the callee account data pointer
2069                assert_eq!(
2070                    translate_slice::<u8>(&memory_mapping, caller_account.vm_data_addr, 1, true,)
2071                        .unwrap()
2072                        .as_ptr(),
2073                    callee_account.get_data().as_ptr()
2074                );
2075
2076                let data_len = callee_account.get_data().len();
2077                // the account info length must get updated
2078                assert_eq!(
2079                    data_len,
2080                    *caller_account.ref_to_len_in_vm.get().unwrap() as usize
2081                );
2082                // the length slot in the serialization parameters must be updated
2083                assert_eq!(data_len, serialized_len());
2084
2085                let realloc_area = translate_slice::<u8>(
2086                    &memory_mapping,
2087                    caller_account
2088                        .vm_data_addr
2089                        .saturating_add(caller_account.original_data_len as u64),
2090                    MAX_PERMITTED_DATA_INCREASE as u64,
2091                    invoke_context.get_check_aligned(),
2092                )
2093                .unwrap();
2094
2095                if data_len < original_data_len {
2096                    // if an account gets resized below its original data length,
2097                    // the spare capacity is zeroed
2098                    let original_data_slice = unsafe {
2099                        slice::from_raw_parts(callee_account.get_data().as_ptr(), original_data_len)
2100                    };
2101
2102                    let spare_capacity = &original_data_slice[original_data_len - data_len..];
2103                    assert!(
2104                        is_zeroed(spare_capacity),
2105                        "dirty account spare capacity {spare_capacity:?}",
2106                    );
2107                }
2108
2109                // if an account gets extended past its original length, the end
2110                // gets written in the realloc padding
2111                assert_eq!(
2112                    &realloc_area[..expected_realloc_used],
2113                    &callee_account.get_data()[data_len - expected_realloc_used..]
2114                );
2115
2116                // the unused realloc padding is always zeroed
2117                assert!(
2118                    is_zeroed(&realloc_area[expected_realloc_used..]),
2119                    "dirty realloc padding {realloc_area:?}",
2120                );
2121            }
2122        }
2123
2124        callee_account
2125            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
2126            .unwrap();
2127        update_caller_account(
2128            &invoke_context,
2129            &memory_mapping,
2130            false,
2131            &mut caller_account,
2132            &mut callee_account,
2133            true,
2134        )
2135        .unwrap();
2136        assert!(
2137            is_zeroed(caller_account.serialized_data),
2138            "dirty realloc padding {:?}",
2139            caller_account.serialized_data
2140        );
2141
2142        callee_account
2143            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
2144            .unwrap();
2145        assert_matches!(
2146            update_caller_account(
2147                &invoke_context,
2148                &memory_mapping,
2149                false,
2150                &mut caller_account,
2151                &mut callee_account,
2152                false,
2153            ),
2154            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
2155        );
2156
2157        // close the account
2158        callee_account.set_data_length(0).unwrap();
2159        callee_account
2160            .set_owner(system_program::id().as_ref())
2161            .unwrap();
2162        update_caller_account(
2163            &invoke_context,
2164            &memory_mapping,
2165            false,
2166            &mut caller_account,
2167            &mut callee_account,
2168            true,
2169        )
2170        .unwrap();
2171        let data_len = callee_account.get_data().len();
2172        assert_eq!(data_len, 0);
2173    }
2174
2175    #[test]
2176    fn test_update_caller_account_data_capacity_direct_mapping() {
2177        let transaction_accounts =
2178            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2179        let account = transaction_accounts[1].1.clone();
2180
2181        mock_invoke_context!(
2182            invoke_context,
2183            transaction_context,
2184            b"instruction data",
2185            transaction_accounts,
2186            &[0],
2187            &[1]
2188        );
2189
2190        let mut mock_caller_account = MockCallerAccount::new(
2191            account.lamports(),
2192            *account.owner(),
2193            0xFFFFFFFF00000000,
2194            account.data(),
2195            true,
2196        );
2197
2198        let config = Config {
2199            aligned_memory_mapping: false,
2200            ..Config::default()
2201        };
2202        let memory_mapping = MemoryMapping::new(
2203            mock_caller_account.regions.split_off(0),
2204            &config,
2205            SBPFVersion::V3,
2206        )
2207        .unwrap();
2208
2209        let mut caller_account = mock_caller_account.caller_account();
2210
2211        {
2212            let mut account = invoke_context
2213                .transaction_context
2214                .get_account_at_index(1)
2215                .unwrap()
2216                .try_borrow_mut()
2217                .unwrap();
2218            account.set_data(b"baz".to_vec());
2219        }
2220
2221        let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2222        assert_eq!(callee_account.get_data().len(), 3);
2223        assert_eq!(callee_account.capacity(), 3);
2224
2225        update_caller_account(
2226            &invoke_context,
2227            &memory_mapping,
2228            false,
2229            &mut caller_account,
2230            &mut callee_account,
2231            true,
2232        )
2233        .unwrap();
2234
2235        assert_eq!(callee_account.get_data().len(), 3);
2236        assert!(callee_account.capacity() >= caller_account.original_data_len);
2237        let data = translate_slice::<u8>(
2238            &memory_mapping,
2239            caller_account.vm_data_addr,
2240            callee_account.get_data().len() as u64,
2241            true,
2242        )
2243        .unwrap();
2244        assert_eq!(data, callee_account.get_data());
2245    }
2246
2247    #[test]
2248    fn test_update_callee_account_lamports_owner() {
2249        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
2250        let account = transaction_accounts[1].1.clone();
2251
2252        mock_invoke_context!(
2253            invoke_context,
2254            transaction_context,
2255            b"instruction data",
2256            transaction_accounts,
2257            &[0],
2258            &[1]
2259        );
2260
2261        let mut mock_caller_account = MockCallerAccount::new(
2262            1234,
2263            *account.owner(),
2264            0xFFFFFFFF00000000,
2265            account.data(),
2266            false,
2267        );
2268
2269        let config = Config {
2270            aligned_memory_mapping: false,
2271            ..Config::default()
2272        };
2273        let memory_mapping = MemoryMapping::new(
2274            mock_caller_account.regions.split_off(0),
2275            &config,
2276            SBPFVersion::V3,
2277        )
2278        .unwrap();
2279
2280        let caller_account = mock_caller_account.caller_account();
2281
2282        let callee_account = borrow_instruction_account!(invoke_context, 0);
2283
2284        *caller_account.lamports = 42;
2285        *caller_account.owner = Pubkey::new_unique();
2286
2287        update_callee_account(
2288            &invoke_context,
2289            &memory_mapping,
2290            false,
2291            &caller_account,
2292            callee_account,
2293            false,
2294        )
2295        .unwrap();
2296
2297        let callee_account = borrow_instruction_account!(invoke_context, 0);
2298        assert_eq!(callee_account.get_lamports(), 42);
2299        assert_eq!(caller_account.owner, callee_account.get_owner());
2300    }
2301
2302    #[test]
2303    fn test_update_callee_account_data() {
2304        let transaction_accounts =
2305            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2306        let account = transaction_accounts[1].1.clone();
2307
2308        mock_invoke_context!(
2309            invoke_context,
2310            transaction_context,
2311            b"instruction data",
2312            transaction_accounts,
2313            &[0],
2314            &[1]
2315        );
2316
2317        let mut mock_caller_account = MockCallerAccount::new(
2318            1234,
2319            *account.owner(),
2320            0xFFFFFFFF00000000,
2321            account.data(),
2322            false,
2323        );
2324
2325        let config = Config {
2326            aligned_memory_mapping: false,
2327            ..Config::default()
2328        };
2329        let memory_mapping = MemoryMapping::new(
2330            mock_caller_account.regions.split_off(0),
2331            &config,
2332            SBPFVersion::V3,
2333        )
2334        .unwrap();
2335
2336        let mut caller_account = mock_caller_account.caller_account();
2337
2338        let callee_account = borrow_instruction_account!(invoke_context, 0);
2339
2340        let mut data = b"foo".to_vec();
2341        caller_account.serialized_data = &mut data;
2342
2343        update_callee_account(
2344            &invoke_context,
2345            &memory_mapping,
2346            false,
2347            &caller_account,
2348            callee_account,
2349            false,
2350        )
2351        .unwrap();
2352
2353        let callee_account = borrow_instruction_account!(invoke_context, 0);
2354        assert_eq!(callee_account.get_data(), caller_account.serialized_data);
2355
2356        // close the account
2357        let mut data = Vec::new();
2358        caller_account.serialized_data = &mut data;
2359        *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 0;
2360        let mut owner = system_program::id();
2361        caller_account.owner = &mut owner;
2362        update_callee_account(
2363            &invoke_context,
2364            &memory_mapping,
2365            false,
2366            &caller_account,
2367            callee_account,
2368            false,
2369        )
2370        .unwrap();
2371        let callee_account = borrow_instruction_account!(invoke_context, 0);
2372        assert_eq!(callee_account.get_data(), b"");
2373    }
2374
2375    #[test]
2376    fn test_update_callee_account_data_readonly() {
2377        let transaction_accounts =
2378            transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
2379        let account = transaction_accounts[1].1.clone();
2380
2381        mock_invoke_context!(
2382            invoke_context,
2383            transaction_context,
2384            b"instruction data",
2385            transaction_accounts,
2386            &[0],
2387            &[1]
2388        );
2389
2390        let mut mock_caller_account = MockCallerAccount::new(
2391            1234,
2392            *account.owner(),
2393            0xFFFFFFFF00000000,
2394            account.data(),
2395            false,
2396        );
2397
2398        let config = Config {
2399            aligned_memory_mapping: false,
2400            ..Config::default()
2401        };
2402        let memory_mapping = MemoryMapping::new(
2403            mock_caller_account.regions.split_off(0),
2404            &config,
2405            SBPFVersion::V3,
2406        )
2407        .unwrap();
2408
2409        let mut caller_account = mock_caller_account.caller_account();
2410
2411        let callee_account = borrow_instruction_account!(invoke_context, 0);
2412
2413        caller_account.serialized_data[0] = b'b';
2414        assert_matches!(
2415            update_callee_account(
2416                &invoke_context,
2417                &memory_mapping,
2418                false,
2419                &caller_account,
2420                callee_account,
2421                false,
2422            ),
2423            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
2424        );
2425
2426        // without direct mapping
2427        let mut data = b"foobarbaz".to_vec();
2428        *caller_account.ref_to_len_in_vm.get_mut().unwrap() = data.len() as u64;
2429        caller_account.serialized_data = &mut data;
2430
2431        let callee_account = borrow_instruction_account!(invoke_context, 0);
2432        assert_matches!(
2433            update_callee_account(
2434                &invoke_context,
2435                &memory_mapping,
2436                false,
2437                &caller_account,
2438                callee_account,
2439                false,
2440            ),
2441            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
2442        );
2443
2444        // with direct mapping
2445        let mut data = b"baz".to_vec();
2446        *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 9;
2447        caller_account.serialized_data = &mut data;
2448
2449        let callee_account = borrow_instruction_account!(invoke_context, 0);
2450        assert_matches!(
2451            update_callee_account(
2452                &invoke_context,
2453                &memory_mapping,
2454                false,
2455                &caller_account,
2456                callee_account,
2457                true,
2458            ),
2459            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
2460        );
2461    }
2462
2463    #[test]
2464    fn test_update_callee_account_data_direct_mapping() {
2465        let transaction_accounts =
2466            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2467        let account = transaction_accounts[1].1.clone();
2468
2469        mock_invoke_context!(
2470            invoke_context,
2471            transaction_context,
2472            b"instruction data",
2473            transaction_accounts,
2474            &[0],
2475            &[1]
2476        );
2477
2478        let mut mock_caller_account = MockCallerAccount::new(
2479            1234,
2480            *account.owner(),
2481            0xFFFFFFFF00000000,
2482            account.data(),
2483            true,
2484        );
2485
2486        let config = Config {
2487            aligned_memory_mapping: false,
2488            ..Config::default()
2489        };
2490        let memory_mapping = MemoryMapping::new(
2491            mock_caller_account.regions.split_off(0),
2492            &config,
2493            SBPFVersion::V3,
2494        )
2495        .unwrap();
2496
2497        let mut caller_account = mock_caller_account.caller_account();
2498
2499        let mut callee_account = borrow_instruction_account!(invoke_context, 0);
2500
2501        // this is done when a writable account is mapped, and it ensures
2502        // through make_data_mut() that the account is made writable and resized
2503        // with enough padding to hold the realloc padding
2504        callee_account.get_data_mut().unwrap();
2505
2506        let serialized_data = translate_slice_mut::<u8>(
2507            &memory_mapping,
2508            caller_account
2509                .vm_data_addr
2510                .saturating_add(caller_account.original_data_len as u64),
2511            3,
2512            invoke_context.get_check_aligned(),
2513        )
2514        .unwrap();
2515        serialized_data.copy_from_slice(b"baz");
2516
2517        for (len, expected) in [
2518            (9, b"foobarbaz".to_vec()), // > original_data_len, copies from realloc region
2519            (6, b"foobar".to_vec()),    // == original_data_len, truncates
2520            (3, b"foo".to_vec()),       // < original_data_len, truncates
2521        ] {
2522            *caller_account.ref_to_len_in_vm.get_mut().unwrap() = len as u64;
2523            update_callee_account(
2524                &invoke_context,
2525                &memory_mapping,
2526                false,
2527                &caller_account,
2528                callee_account,
2529                true,
2530            )
2531            .unwrap();
2532            callee_account = borrow_instruction_account!(invoke_context, 0);
2533            assert_eq!(callee_account.get_data(), expected);
2534        }
2535
2536        // close the account
2537        let mut data = Vec::new();
2538        caller_account.serialized_data = &mut data;
2539        *caller_account.ref_to_len_in_vm.get_mut().unwrap() = 0;
2540        let mut owner = system_program::id();
2541        caller_account.owner = &mut owner;
2542        update_callee_account(
2543            &invoke_context,
2544            &memory_mapping,
2545            false,
2546            &caller_account,
2547            callee_account,
2548            true,
2549        )
2550        .unwrap();
2551        callee_account = borrow_instruction_account!(invoke_context, 0);
2552        assert_eq!(callee_account.get_data(), b"");
2553    }
2554
2555    #[test]
2556    fn test_translate_accounts_rust() {
2557        let transaction_accounts =
2558            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
2559        let account = transaction_accounts[1].1.clone();
2560        let key = transaction_accounts[1].0;
2561        let original_data_len = account.data().len();
2562
2563        let vm_addr = MM_INPUT_START;
2564        let (_mem, region, account_metadata) =
2565            MockAccountInfo::new(key, &account).into_region(vm_addr);
2566
2567        let config = Config {
2568            aligned_memory_mapping: false,
2569            ..Config::default()
2570        };
2571        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
2572
2573        mock_invoke_context!(
2574            invoke_context,
2575            transaction_context,
2576            b"instruction data",
2577            transaction_accounts,
2578            &[0],
2579            &[1, 1]
2580        );
2581
2582        mock_create_vm!(_vm, Vec::new(), vec![account_metadata], &mut invoke_context);
2583
2584        let accounts = SyscallInvokeSignedRust::translate_accounts(
2585            &[
2586                InstructionAccount {
2587                    index_in_transaction: 1,
2588                    index_in_caller: 0,
2589                    index_in_callee: 0,
2590                    is_signer: false,
2591                    is_writable: true,
2592                },
2593                InstructionAccount {
2594                    index_in_transaction: 1,
2595                    index_in_caller: 0,
2596                    index_in_callee: 0,
2597                    is_signer: false,
2598                    is_writable: true,
2599                },
2600            ],
2601            &[0],
2602            vm_addr,
2603            1,
2604            false,
2605            &memory_mapping,
2606            &mut invoke_context,
2607        )
2608        .unwrap();
2609        assert_eq!(accounts.len(), 2);
2610        assert!(accounts[0].1.is_none());
2611        let caller_account = accounts[1].1.as_ref().unwrap();
2612        assert_eq!(caller_account.serialized_data, account.data());
2613        assert_eq!(caller_account.original_data_len, original_data_len);
2614    }
2615
2616    type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
2617    struct MockCallerAccount {
2618        lamports: u64,
2619        owner: Pubkey,
2620        vm_addr: u64,
2621        data: Vec<u8>,
2622        len: u64,
2623        regions: Vec<MemoryRegion>,
2624        direct_mapping: bool,
2625    }
2626
2627    impl MockCallerAccount {
2628        fn new(
2629            lamports: u64,
2630            owner: Pubkey,
2631            vm_addr: u64,
2632            data: &[u8],
2633            direct_mapping: bool,
2634        ) -> MockCallerAccount {
2635            let mut regions = vec![];
2636
2637            let mut d = vec![
2638                0;
2639                mem::size_of::<u64>()
2640                    + if direct_mapping { 0 } else { data.len() }
2641                    + MAX_PERMITTED_DATA_INCREASE
2642            ];
2643            // always write the [len] part even when direct mapping
2644            unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
2645
2646            // write the account data when not direct mapping
2647            if !direct_mapping {
2648                d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
2649            }
2650
2651            // create a region for [len][data+realloc if !direct_mapping]
2652            let mut region_addr = vm_addr;
2653            let region_len = mem::size_of::<u64>()
2654                + if direct_mapping {
2655                    0
2656                } else {
2657                    data.len() + MAX_PERMITTED_DATA_INCREASE
2658                };
2659            regions.push(MemoryRegion::new_writable(&mut d[..region_len], vm_addr));
2660            region_addr += region_len as u64;
2661
2662            if direct_mapping {
2663                // create a region for the directly mapped data
2664                regions.push(MemoryRegion::new_readonly(data, region_addr));
2665                region_addr += data.len() as u64;
2666
2667                // create a region for the realloc padding
2668                regions.push(MemoryRegion::new_writable(
2669                    &mut d[mem::size_of::<u64>()..],
2670                    region_addr,
2671                ));
2672            } else {
2673                // caller_account.serialized_data must have the actual data length
2674                d.truncate(mem::size_of::<u64>() + data.len());
2675            }
2676
2677            MockCallerAccount {
2678                lamports,
2679                owner,
2680                vm_addr,
2681                data: d,
2682                len: data.len() as u64,
2683                regions,
2684                direct_mapping,
2685            }
2686        }
2687
2688        fn data_slice<'a>(&self) -> &'a [u8] {
2689            // lifetime crimes
2690            unsafe {
2691                slice::from_raw_parts(
2692                    self.data[mem::size_of::<u64>()..].as_ptr(),
2693                    self.data.capacity() - mem::size_of::<u64>(),
2694                )
2695            }
2696        }
2697
2698        fn caller_account(&mut self) -> CallerAccount<'_, '_> {
2699            let data = if self.direct_mapping {
2700                &mut []
2701            } else {
2702                &mut self.data[mem::size_of::<u64>()..]
2703            };
2704            CallerAccount {
2705                lamports: &mut self.lamports,
2706                owner: &mut self.owner,
2707                original_data_len: self.len as usize,
2708                serialized_data: data,
2709                vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
2710                ref_to_len_in_vm: VmValue::Translated(&mut self.len),
2711            }
2712        }
2713    }
2714
2715    fn transaction_with_one_writable_instruction_account(
2716        data: Vec<u8>,
2717    ) -> Vec<TestTransactionAccount> {
2718        let program_id = Pubkey::new_unique();
2719        let account = AccountSharedData::from(Account {
2720            lamports: 1,
2721            data,
2722            owner: program_id,
2723            executable: false,
2724            rent_epoch: 100,
2725        });
2726        vec![
2727            (
2728                program_id,
2729                AccountSharedData::from(Account {
2730                    lamports: 0,
2731                    data: vec![],
2732                    owner: bpf_loader::id(),
2733                    executable: true,
2734                    rent_epoch: 0,
2735                }),
2736                false,
2737            ),
2738            (Pubkey::new_unique(), account, true),
2739        ]
2740    }
2741
2742    fn transaction_with_one_readonly_instruction_account(
2743        data: Vec<u8>,
2744    ) -> Vec<TestTransactionAccount> {
2745        let program_id = Pubkey::new_unique();
2746        let account_owner = Pubkey::new_unique();
2747        let account = AccountSharedData::from(Account {
2748            lamports: 1,
2749            data,
2750            owner: account_owner,
2751            executable: false,
2752            rent_epoch: 100,
2753        });
2754        vec![
2755            (
2756                program_id,
2757                AccountSharedData::from(Account {
2758                    lamports: 0,
2759                    data: vec![],
2760                    owner: bpf_loader::id(),
2761                    executable: true,
2762                    rent_epoch: 0,
2763                }),
2764                false,
2765            ),
2766            (Pubkey::new_unique(), account, true),
2767        ]
2768    }
2769
2770    struct MockInstruction {
2771        program_id: Pubkey,
2772        accounts: Vec<AccountMeta>,
2773        data: Vec<u8>,
2774    }
2775
2776    impl MockInstruction {
2777        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2778            let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
2779
2780            let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
2781
2782            let mut data = vec![0; size];
2783
2784            let vm_addr = vm_addr as usize;
2785            let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
2786            let data_addr = accounts_addr + accounts_len;
2787
2788            let ins = Instruction {
2789                program_id: self.program_id,
2790                accounts: unsafe {
2791                    Vec::from_raw_parts(
2792                        accounts_addr as *mut _,
2793                        self.accounts.len(),
2794                        self.accounts.len(),
2795                    )
2796                },
2797                data: unsafe {
2798                    Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
2799                },
2800            };
2801            let ins = StableInstruction::from(ins);
2802
2803            unsafe {
2804                ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
2805                data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
2806                    slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
2807                );
2808                data[data_addr - vm_addr..].copy_from_slice(&self.data);
2809            }
2810
2811            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2812            (data, region)
2813        }
2814    }
2815
2816    fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2817        let vm_addr = vm_addr as usize;
2818
2819        // calculate size
2820        let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); // pointer size + length size
2821        let singers_length = signers.len();
2822        let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
2823
2824        // init data vec
2825        let total_size = fat_ptr_size_of_slice
2826            + singers_length * fat_ptr_size_of_slice
2827            + sum_signers_data_length;
2828        let mut data = vec![0; total_size];
2829
2830        // data is composed by 3 parts
2831        // A.
2832        // [ singers address, singers length, ...,
2833        // B.                                      |
2834        //                                         signer1 address, signer1 length, signer2 address ...,
2835        //                                         ^ p1 --->
2836        // C.                                                                                           |
2837        //                                                                                              signer1 data, signer2 data, ... ]
2838        //                                                                                              ^ p2 --->
2839
2840        // A.
2841        data[..fat_ptr_size_of_slice / 2]
2842            .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
2843        data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
2844            .clone_from_slice(&(singers_length).to_le_bytes());
2845
2846        // B. + C.
2847        let (mut p1, mut p2) = (
2848            fat_ptr_size_of_slice,
2849            fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
2850        );
2851        for signer in signers.iter() {
2852            let signer_length = signer.len();
2853
2854            // B.
2855            data[p1..p1 + fat_ptr_size_of_slice / 2]
2856                .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
2857            data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
2858                .clone_from_slice(&(signer_length).to_le_bytes());
2859            p1 += fat_ptr_size_of_slice;
2860
2861            // C.
2862            data[p2..p2 + signer_length].clone_from_slice(signer);
2863            p2 += signer_length;
2864        }
2865
2866        let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2867        (data, region)
2868    }
2869
2870    struct MockAccountInfo<'a> {
2871        key: Pubkey,
2872        is_signer: bool,
2873        is_writable: bool,
2874        lamports: u64,
2875        data: &'a [u8],
2876        owner: Pubkey,
2877        executable: bool,
2878        rent_epoch: Epoch,
2879    }
2880
2881    impl MockAccountInfo<'_> {
2882        fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
2883            MockAccountInfo {
2884                key,
2885                is_signer: false,
2886                is_writable: false,
2887                lamports: account.lamports(),
2888                data: account.data(),
2889                owner: *account.owner(),
2890                executable: account.executable(),
2891                rent_epoch: account.rent_epoch(),
2892            }
2893        }
2894
2895        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
2896            let size = mem::size_of::<AccountInfo>()
2897                + mem::size_of::<Pubkey>() * 2
2898                + mem::size_of::<RcBox<RefCell<&mut u64>>>()
2899                + mem::size_of::<u64>()
2900                + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
2901                + self.data.len();
2902            let mut data = vec![0; size];
2903
2904            let vm_addr = vm_addr as usize;
2905            let key_addr = vm_addr + mem::size_of::<AccountInfo>();
2906            let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
2907            let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
2908            let owner_addr = lamports_addr + mem::size_of::<u64>();
2909            let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
2910            let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
2911
2912            let info = AccountInfo {
2913                key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
2914                is_signer: self.is_signer,
2915                is_writable: self.is_writable,
2916                lamports: unsafe {
2917                    Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
2918                },
2919                data: unsafe {
2920                    Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
2921                },
2922                owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
2923                executable: self.executable,
2924                rent_epoch: self.rent_epoch,
2925            };
2926
2927            unsafe {
2928                ptr::write_unaligned(data.as_mut_ptr().cast(), info);
2929                ptr::write_unaligned(
2930                    (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
2931                    self.key,
2932                );
2933                ptr::write_unaligned(
2934                    (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
2935                    RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
2936                );
2937                ptr::write_unaligned(
2938                    (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
2939                    self.lamports,
2940                );
2941                ptr::write_unaligned(
2942                    (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
2943                    self.owner,
2944                );
2945                ptr::write_unaligned(
2946                    (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
2947                    RcBox::new(RefCell::new(slice::from_raw_parts_mut(
2948                        data_addr as *mut u8,
2949                        self.data.len(),
2950                    ))),
2951                );
2952                data[data_addr - vm_addr..].copy_from_slice(self.data);
2953            }
2954
2955            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2956            (
2957                data,
2958                region,
2959                SerializedAccountMetadata {
2960                    original_data_len: self.data.len(),
2961                    vm_key_addr: key_addr as u64,
2962                    vm_lamports_addr: lamports_addr as u64,
2963                    vm_owner_addr: owner_addr as u64,
2964                    vm_data_addr: data_addr as u64,
2965                },
2966            )
2967        }
2968    }
2969
2970    #[repr(C)]
2971    struct RcBox<T> {
2972        strong: Cell<usize>,
2973        weak: Cell<usize>,
2974        value: T,
2975    }
2976
2977    impl<T> RcBox<T> {
2978        const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
2979        fn new(value: T) -> RcBox<T> {
2980            RcBox {
2981                strong: Cell::new(0),
2982                weak: Cell::new(0),
2983                value,
2984            }
2985        }
2986    }
2987
2988    fn is_zeroed(data: &[u8]) -> bool {
2989        data.iter().all(|b| *b == 0)
2990    }
2991}