Skip to main content

rialo_s_bpf_loader_program/syscalls/
cpi.rs

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