solana_bpf_loader_program/syscalls/
cpi.rs

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