solana_bpf_loader_program/syscalls/
cpi.rs

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