solana_bpf_loader_program/syscalls/
cpi.rs

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