miraland_bpf_loader_program/syscalls/
cpi.rs

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