agave_syscalls/
cpi.rs

1use {
2    super::*,
3    crate::{translate_inner, translate_slice_inner, translate_type_inner},
4    solana_instruction::Instruction,
5    solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
6    solana_program_runtime::{
7        invoke_context::SerializedAccountMetadata,
8        serialization::{create_memory_region_of_account, modify_memory_region_of_account},
9    },
10    solana_sbpf::ebpf,
11    solana_stable_layout::stable_instruction::StableInstruction,
12    solana_svm_measure::measure::Measure,
13    solana_transaction_context::{
14        BorrowedAccount, MAX_ACCOUNTS_PER_INSTRUCTION, MAX_INSTRUCTION_DATA_LEN,
15    },
16    std::mem,
17};
18
19const MAX_CPI_ACCOUNT_INFOS: usize = 128;
20
21fn check_account_info_pointer(
22    invoke_context: &InvokeContext,
23    vm_addr: u64,
24    expected_vm_addr: u64,
25    field: &str,
26) -> Result<(), Error> {
27    if vm_addr != expected_vm_addr {
28        ic_msg!(
29            invoke_context,
30            "Invalid account info pointer `{}': {:#x} != {:#x}",
31            field,
32            vm_addr,
33            expected_vm_addr
34        );
35        return Err(SyscallError::InvalidPointer.into());
36    }
37    Ok(())
38}
39
40// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
41fn translate_type_mut<'a, T>(
42    memory_mapping: &MemoryMapping,
43    vm_addr: u64,
44    check_aligned: bool,
45) -> Result<&'a mut T, Error> {
46    translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
47}
48// This version is missing the lifetime 'a of the return type in the parameter &MemoryMapping.
49fn translate_slice_mut<'a, T>(
50    memory_mapping: &MemoryMapping,
51    vm_addr: u64,
52    len: u64,
53    check_aligned: bool,
54) -> Result<&'a mut [T], Error> {
55    translate_slice_inner!(
56        memory_mapping,
57        AccessType::Store,
58        vm_addr,
59        len,
60        T,
61        check_aligned,
62    )
63}
64
65/// Host side representation of AccountInfo or SolAccountInfo passed to the CPI syscall.
66///
67/// At the start of a CPI, this can be different from the data stored in the
68/// corresponding BorrowedAccount, and needs to be synched.
69struct CallerAccount<'a> {
70    lamports: &'a mut u64,
71    owner: &'a mut Pubkey,
72    // The original data length of the account at the start of the current
73    // instruction. We use this to determine wether an account was shrunk or
74    // grown before or after CPI, and to derive the vm address of the realloc
75    // region.
76    original_data_len: usize,
77    // This points to the data section for this account, as serialized and
78    // mapped inside the vm (see serialize_parameters() in
79    // BpfExecutor::execute).
80    //
81    // This is only set when account_data_direct_mapping is off.
82    serialized_data: &'a mut [u8],
83    // Given the corresponding input AccountInfo::data, vm_data_addr points to
84    // the pointer field and ref_to_len_in_vm points to the length field.
85    vm_data_addr: u64,
86    ref_to_len_in_vm: &'a mut u64,
87}
88
89impl<'a> CallerAccount<'a> {
90    fn get_serialized_data(
91        memory_mapping: &MemoryMapping<'_>,
92        vm_addr: u64,
93        len: u64,
94        stricter_abi_and_runtime_constraints: bool,
95        account_data_direct_mapping: bool,
96    ) -> Result<&'a mut [u8], Error> {
97        if stricter_abi_and_runtime_constraints && account_data_direct_mapping {
98            Ok(&mut [])
99        } else if stricter_abi_and_runtime_constraints {
100            // Workaround the memory permissions (as these are from the PoV of being inside the VM)
101            let serialization_ptr = translate_slice_mut::<u8>(
102                memory_mapping,
103                solana_sbpf::ebpf::MM_INPUT_START,
104                1,
105                false, // Don't care since it is byte aligned
106            )?
107            .as_mut_ptr();
108            unsafe {
109                Ok(std::slice::from_raw_parts_mut(
110                    serialization_ptr
111                        .add(vm_addr.saturating_sub(solana_sbpf::ebpf::MM_INPUT_START) as usize),
112                    len as usize,
113                ))
114            }
115        } else {
116            translate_slice_mut::<u8>(
117                memory_mapping,
118                vm_addr,
119                len,
120                false, // Don't care since it is byte aligned
121            )
122        }
123    }
124
125    // Create a CallerAccount given an AccountInfo.
126    fn from_account_info(
127        invoke_context: &InvokeContext,
128        memory_mapping: &MemoryMapping<'_>,
129        check_aligned: bool,
130        _vm_addr: u64,
131        account_info: &AccountInfo,
132        account_metadata: &SerializedAccountMetadata,
133    ) -> Result<CallerAccount<'a>, Error> {
134        let stricter_abi_and_runtime_constraints = invoke_context
135            .get_feature_set()
136            .stricter_abi_and_runtime_constraints;
137
138        if stricter_abi_and_runtime_constraints {
139            check_account_info_pointer(
140                invoke_context,
141                account_info.key as *const _ as u64,
142                account_metadata.vm_key_addr,
143                "key",
144            )?;
145            check_account_info_pointer(
146                invoke_context,
147                account_info.owner as *const _ as u64,
148                account_metadata.vm_owner_addr,
149                "owner",
150            )?;
151        }
152
153        // account_info points to host memory. The addresses used internally are
154        // in vm space so they need to be translated.
155        let lamports = {
156            // Double translate lamports out of RefCell
157            let ptr = translate_type::<u64>(
158                memory_mapping,
159                account_info.lamports.as_ptr() as u64,
160                check_aligned,
161            )?;
162            if stricter_abi_and_runtime_constraints {
163                if account_info.lamports.as_ptr() as u64 >= ebpf::MM_INPUT_START {
164                    return Err(SyscallError::InvalidPointer.into());
165                }
166
167                check_account_info_pointer(
168                    invoke_context,
169                    *ptr,
170                    account_metadata.vm_lamports_addr,
171                    "lamports",
172                )?;
173            }
174            translate_type_mut::<u64>(memory_mapping, *ptr, check_aligned)?
175        };
176
177        let owner = translate_type_mut::<Pubkey>(
178            memory_mapping,
179            account_info.owner as *const _ as u64,
180            check_aligned,
181        )?;
182
183        let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
184            if stricter_abi_and_runtime_constraints
185                && account_info.data.as_ptr() as u64 >= ebpf::MM_INPUT_START
186            {
187                return Err(SyscallError::InvalidPointer.into());
188            }
189
190            // Double translate data out of RefCell
191            let data = *translate_type::<&[u8]>(
192                memory_mapping,
193                account_info.data.as_ptr() as *const _ as u64,
194                check_aligned,
195            )?;
196            if stricter_abi_and_runtime_constraints {
197                check_account_info_pointer(
198                    invoke_context,
199                    data.as_ptr() as u64,
200                    account_metadata.vm_data_addr,
201                    "data",
202                )?;
203            }
204
205            consume_compute_meter(
206                invoke_context,
207                (data.len() as u64)
208                    .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
209                    .unwrap_or(u64::MAX),
210            )?;
211
212            let vm_len_addr = (account_info.data.as_ptr() as *const u64 as u64)
213                .saturating_add(size_of::<u64>() as u64);
214            if stricter_abi_and_runtime_constraints {
215                // In the same vein as the other check_account_info_pointer() checks, we don't lock
216                // this pointer to a specific address but we don't want it to be inside accounts, or
217                // callees might be able to write to the pointed memory.
218                if vm_len_addr >= ebpf::MM_INPUT_START {
219                    return Err(SyscallError::InvalidPointer.into());
220                }
221            }
222            let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
223            let vm_data_addr = data.as_ptr() as u64;
224            let serialized_data = CallerAccount::get_serialized_data(
225                memory_mapping,
226                vm_data_addr,
227                data.len() as u64,
228                stricter_abi_and_runtime_constraints,
229                invoke_context.account_data_direct_mapping,
230            )?;
231            (serialized_data, vm_data_addr, ref_to_len_in_vm)
232        };
233
234        Ok(CallerAccount {
235            lamports,
236            owner,
237            original_data_len: account_metadata.original_data_len,
238            serialized_data,
239            vm_data_addr,
240            ref_to_len_in_vm,
241        })
242    }
243
244    // Create a CallerAccount given a SolAccountInfo.
245    fn from_sol_account_info(
246        invoke_context: &InvokeContext,
247        memory_mapping: &MemoryMapping<'_>,
248        check_aligned: bool,
249        vm_addr: u64,
250        account_info: &SolAccountInfo,
251        account_metadata: &SerializedAccountMetadata,
252    ) -> Result<CallerAccount<'a>, Error> {
253        let stricter_abi_and_runtime_constraints = invoke_context
254            .get_feature_set()
255            .stricter_abi_and_runtime_constraints;
256
257        if stricter_abi_and_runtime_constraints {
258            check_account_info_pointer(
259                invoke_context,
260                account_info.key_addr,
261                account_metadata.vm_key_addr,
262                "key",
263            )?;
264
265            check_account_info_pointer(
266                invoke_context,
267                account_info.owner_addr,
268                account_metadata.vm_owner_addr,
269                "owner",
270            )?;
271
272            check_account_info_pointer(
273                invoke_context,
274                account_info.lamports_addr,
275                account_metadata.vm_lamports_addr,
276                "lamports",
277            )?;
278
279            check_account_info_pointer(
280                invoke_context,
281                account_info.data_addr,
282                account_metadata.vm_data_addr,
283                "data",
284            )?;
285        }
286
287        // account_info points to host memory. The addresses used internally are
288        // in vm space so they need to be translated.
289        let lamports =
290            translate_type_mut::<u64>(memory_mapping, account_info.lamports_addr, check_aligned)?;
291        let owner =
292            translate_type_mut::<Pubkey>(memory_mapping, account_info.owner_addr, check_aligned)?;
293
294        consume_compute_meter(
295            invoke_context,
296            account_info
297                .data_len
298                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
299                .unwrap_or(u64::MAX),
300        )?;
301
302        let serialized_data = CallerAccount::get_serialized_data(
303            memory_mapping,
304            account_info.data_addr,
305            account_info.data_len,
306            stricter_abi_and_runtime_constraints,
307            invoke_context.account_data_direct_mapping,
308        )?;
309
310        // we already have the host addr we want: &mut account_info.data_len.
311        // The account info might be read only in the vm though, so we translate
312        // to ensure we can write. This is tested by programs/sbf/rust/ro_modify
313        // which puts SolAccountInfo in rodata.
314        let vm_len_addr = vm_addr
315            .saturating_add(&account_info.data_len as *const u64 as u64)
316            .saturating_sub(account_info as *const _ as *const u64 as u64);
317        let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
318
319        Ok(CallerAccount {
320            lamports,
321            owner,
322            original_data_len: account_metadata.original_data_len,
323            serialized_data,
324            vm_data_addr: account_info.data_addr,
325            ref_to_len_in_vm,
326        })
327    }
328}
329
330struct TranslatedAccount<'a> {
331    index_in_caller: IndexOfAccount,
332    caller_account: CallerAccount<'a>,
333    update_caller_account_region: bool,
334    update_caller_account_info: bool,
335}
336
337/// Implemented by language specific data structure translators
338trait SyscallInvokeSigned {
339    fn translate_instruction(
340        addr: u64,
341        memory_mapping: &MemoryMapping,
342        invoke_context: &mut InvokeContext,
343        check_aligned: bool,
344    ) -> Result<Instruction, Error>;
345    fn translate_accounts<'a>(
346        account_infos_addr: u64,
347        account_infos_len: u64,
348        memory_mapping: &MemoryMapping<'_>,
349        invoke_context: &mut InvokeContext,
350        check_aligned: bool,
351    ) -> Result<Vec<TranslatedAccount<'a>>, Error>;
352    fn translate_signers(
353        program_id: &Pubkey,
354        signers_seeds_addr: u64,
355        signers_seeds_len: u64,
356        memory_mapping: &MemoryMapping,
357        check_aligned: bool,
358    ) -> Result<Vec<Pubkey>, Error>;
359}
360
361declare_builtin_function!(
362    /// Cross-program invocation called from Rust
363    SyscallInvokeSignedRust,
364    fn rust(
365        invoke_context: &mut InvokeContext,
366        instruction_addr: u64,
367        account_infos_addr: u64,
368        account_infos_len: u64,
369        signers_seeds_addr: u64,
370        signers_seeds_len: u64,
371        memory_mapping: &mut MemoryMapping,
372    ) -> Result<u64, Error> {
373        cpi_common::<Self>(
374            invoke_context,
375            instruction_addr,
376            account_infos_addr,
377            account_infos_len,
378            signers_seeds_addr,
379            signers_seeds_len,
380            memory_mapping,
381        )
382    }
383);
384
385impl SyscallInvokeSigned for SyscallInvokeSignedRust {
386    fn translate_instruction(
387        addr: u64,
388        memory_mapping: &MemoryMapping,
389        invoke_context: &mut InvokeContext,
390        check_aligned: bool,
391    ) -> Result<Instruction, Error> {
392        let ix = translate_type::<StableInstruction>(memory_mapping, addr, check_aligned)?;
393        let account_metas = translate_slice::<AccountMeta>(
394            memory_mapping,
395            ix.accounts.as_vaddr(),
396            ix.accounts.len(),
397            check_aligned,
398        )?;
399        let data = translate_slice::<u8>(
400            memory_mapping,
401            ix.data.as_vaddr(),
402            ix.data.len(),
403            check_aligned,
404        )?
405        .to_vec();
406
407        check_instruction_size(account_metas.len(), data.len())?;
408
409        consume_compute_meter(
410            invoke_context,
411            (data.len() as u64)
412                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
413                .unwrap_or(u64::MAX),
414        )?;
415
416        let mut accounts = Vec::with_capacity(account_metas.len());
417        #[allow(clippy::needless_range_loop)]
418        for account_index in 0..account_metas.len() {
419            #[allow(clippy::indexing_slicing)]
420            let account_meta = &account_metas[account_index];
421            if unsafe {
422                std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
423                    || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
424                        > 1
425            } {
426                return Err(Box::new(InstructionError::InvalidArgument));
427            }
428            accounts.push(account_meta.clone());
429        }
430
431        Ok(Instruction {
432            accounts,
433            data,
434            program_id: ix.program_id,
435        })
436    }
437
438    fn translate_accounts<'a>(
439        account_infos_addr: u64,
440        account_infos_len: u64,
441        memory_mapping: &MemoryMapping<'_>,
442        invoke_context: &mut InvokeContext,
443        check_aligned: bool,
444    ) -> Result<Vec<TranslatedAccount<'a>>, Error> {
445        let (account_infos, account_info_keys) = translate_account_infos(
446            account_infos_addr,
447            account_infos_len,
448            |account_info: &AccountInfo| account_info.key as *const _ as u64,
449            memory_mapping,
450            invoke_context,
451            check_aligned,
452        )?;
453
454        translate_and_update_accounts(
455            &account_info_keys,
456            account_infos,
457            account_infos_addr,
458            invoke_context,
459            memory_mapping,
460            check_aligned,
461            CallerAccount::from_account_info,
462        )
463    }
464
465    fn translate_signers(
466        program_id: &Pubkey,
467        signers_seeds_addr: u64,
468        signers_seeds_len: u64,
469        memory_mapping: &MemoryMapping,
470        check_aligned: bool,
471    ) -> Result<Vec<Pubkey>, Error> {
472        let mut signers = Vec::new();
473        if signers_seeds_len > 0 {
474            let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
475                memory_mapping,
476                signers_seeds_addr,
477                signers_seeds_len,
478                check_aligned,
479            )?;
480            if signers_seeds.len() > MAX_SIGNERS {
481                return Err(Box::new(SyscallError::TooManySigners));
482            }
483            for signer_seeds in signers_seeds.iter() {
484                let untranslated_seeds = translate_slice::<VmSlice<u8>>(
485                    memory_mapping,
486                    signer_seeds.ptr(),
487                    signer_seeds.len(),
488                    check_aligned,
489                )?;
490                if untranslated_seeds.len() > MAX_SEEDS {
491                    return Err(Box::new(InstructionError::MaxSeedLengthExceeded));
492                }
493                let seeds = untranslated_seeds
494                    .iter()
495                    .map(|untranslated_seed| {
496                        untranslated_seed.translate(memory_mapping, 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        check_aligned: bool,
591    ) -> Result<Instruction, Error> {
592        let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
593
594        let program_id =
595            translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
596        let account_metas = translate_slice::<SolAccountMeta>(
597            memory_mapping,
598            ix_c.accounts_addr,
599            ix_c.accounts_len,
600            check_aligned,
601        )?;
602        let data =
603            translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?
604                .to_vec();
605
606        check_instruction_size(ix_c.accounts_len as usize, data.len())?;
607
608        consume_compute_meter(
609            invoke_context,
610            (data.len() as u64)
611                .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
612                .unwrap_or(u64::MAX),
613        )?;
614
615        let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
616        #[allow(clippy::needless_range_loop)]
617        for account_index in 0..ix_c.accounts_len as usize {
618            #[allow(clippy::indexing_slicing)]
619            let account_meta = &account_metas[account_index];
620            if unsafe {
621                std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
622                    || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
623                        > 1
624            } {
625                return Err(Box::new(InstructionError::InvalidArgument));
626            }
627            let pubkey =
628                translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
629            accounts.push(AccountMeta {
630                pubkey: *pubkey,
631                is_signer: account_meta.is_signer,
632                is_writable: account_meta.is_writable,
633            });
634        }
635
636        Ok(Instruction {
637            accounts,
638            data,
639            program_id: *program_id,
640        })
641    }
642
643    fn translate_accounts<'a>(
644        account_infos_addr: u64,
645        account_infos_len: u64,
646        memory_mapping: &MemoryMapping<'_>,
647        invoke_context: &mut InvokeContext,
648        check_aligned: bool,
649    ) -> Result<Vec<TranslatedAccount<'a>>, Error> {
650        let (account_infos, account_info_keys) = translate_account_infos(
651            account_infos_addr,
652            account_infos_len,
653            |account_info: &SolAccountInfo| account_info.key_addr,
654            memory_mapping,
655            invoke_context,
656            check_aligned,
657        )?;
658
659        translate_and_update_accounts(
660            &account_info_keys,
661            account_infos,
662            account_infos_addr,
663            invoke_context,
664            memory_mapping,
665            check_aligned,
666            CallerAccount::from_sol_account_info,
667        )
668    }
669
670    fn translate_signers(
671        program_id: &Pubkey,
672        signers_seeds_addr: u64,
673        signers_seeds_len: u64,
674        memory_mapping: &MemoryMapping,
675        check_aligned: bool,
676    ) -> Result<Vec<Pubkey>, Error> {
677        if signers_seeds_len > 0 {
678            let signers_seeds = translate_slice::<SolSignerSeedsC>(
679                memory_mapping,
680                signers_seeds_addr,
681                signers_seeds_len,
682                check_aligned,
683            )?;
684            if signers_seeds.len() > MAX_SIGNERS {
685                return Err(Box::new(SyscallError::TooManySigners));
686            }
687            Ok(signers_seeds
688                .iter()
689                .map(|signer_seeds| {
690                    let seeds = translate_slice::<SolSignerSeedC>(
691                        memory_mapping,
692                        signer_seeds.addr,
693                        signer_seeds.len,
694                        check_aligned,
695                    )?;
696                    if seeds.len() > MAX_SEEDS {
697                        return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
698                    }
699                    let seeds_bytes = seeds
700                        .iter()
701                        .map(|seed| {
702                            translate_slice::<u8>(
703                                memory_mapping,
704                                seed.addr,
705                                seed.len,
706                                check_aligned,
707                            )
708                        })
709                        .collect::<Result<Vec<_>, Error>>()?;
710                    Pubkey::create_program_address(&seeds_bytes, program_id)
711                        .map_err(|err| Box::new(SyscallError::BadSeeds(err)) as Error)
712                })
713                .collect::<Result<Vec<_>, Error>>()?)
714        } else {
715            Ok(vec![])
716        }
717    }
718}
719
720fn translate_account_infos<'a, T, F>(
721    account_infos_addr: u64,
722    account_infos_len: u64,
723    key_addr: F,
724    memory_mapping: &'a MemoryMapping,
725    invoke_context: &mut InvokeContext,
726    check_aligned: bool,
727) -> Result<(&'a [T], Vec<&'a Pubkey>), Error>
728where
729    F: Fn(&T) -> u64,
730{
731    let stricter_abi_and_runtime_constraints = invoke_context
732        .get_feature_set()
733        .stricter_abi_and_runtime_constraints;
734
735    // In the same vein as the other check_account_info_pointer() checks, we don't lock
736    // this pointer to a specific address but we don't want it to be inside accounts, or
737    // callees might be able to write to the pointed memory.
738    if stricter_abi_and_runtime_constraints
739        && account_infos_addr
740            .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
741            >= ebpf::MM_INPUT_START
742    {
743        return Err(SyscallError::InvalidPointer.into());
744    }
745
746    let account_infos = translate_slice::<T>(
747        memory_mapping,
748        account_infos_addr,
749        account_infos_len,
750        check_aligned,
751    )?;
752    check_account_infos(account_infos.len(), invoke_context)?;
753    let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
754    #[allow(clippy::needless_range_loop)]
755    for account_index in 0..account_infos_len as usize {
756        #[allow(clippy::indexing_slicing)]
757        let account_info = &account_infos[account_index];
758        account_info_keys.push(translate_type::<Pubkey>(
759            memory_mapping,
760            key_addr(account_info),
761            check_aligned,
762        )?);
763    }
764    Ok((account_infos, account_info_keys))
765}
766
767// Finish translating accounts, build CallerAccount values and update callee
768// accounts in preparation of executing the callee.
769fn translate_and_update_accounts<'a, T, F>(
770    account_info_keys: &[&Pubkey],
771    account_infos: &[T],
772    account_infos_addr: u64,
773    invoke_context: &mut InvokeContext,
774    memory_mapping: &MemoryMapping<'_>,
775    check_aligned: bool,
776    do_translate: F,
777) -> Result<Vec<TranslatedAccount<'a>>, Error>
778where
779    F: Fn(
780        &InvokeContext,
781        &MemoryMapping<'_>,
782        bool,
783        u64,
784        &T,
785        &SerializedAccountMetadata,
786    ) -> Result<CallerAccount<'a>, Error>,
787{
788    let transaction_context = &invoke_context.transaction_context;
789    let next_instruction_context = transaction_context.get_next_instruction_context()?;
790    let next_instruction_accounts = next_instruction_context.instruction_accounts();
791    let instruction_context = transaction_context.get_current_instruction_context()?;
792    let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
793
794    // unwrapping here is fine: we're in a syscall and the method below fails
795    // only outside syscalls
796    let accounts_metadata = &invoke_context
797        .get_syscall_context()
798        .unwrap()
799        .accounts_metadata;
800
801    let stricter_abi_and_runtime_constraints = invoke_context
802        .get_feature_set()
803        .stricter_abi_and_runtime_constraints;
804
805    for (instruction_account_index, instruction_account) in
806        next_instruction_accounts.iter().enumerate()
807    {
808        if next_instruction_context
809            .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
810            .is_some()
811        {
812            continue; // Skip duplicate account
813        }
814
815        let index_in_caller = instruction_context
816            .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
817        let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
818        let account_key = invoke_context
819            .transaction_context
820            .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
821
822        #[allow(deprecated)]
823        if callee_account.is_executable() {
824            // Use the known account
825            consume_compute_meter(
826                invoke_context,
827                (callee_account.get_data().len() as u64)
828                    .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
829                    .unwrap_or(u64::MAX),
830            )?;
831        } else if let Some(caller_account_index) =
832            account_info_keys.iter().position(|key| *key == account_key)
833        {
834            let serialized_metadata =
835                accounts_metadata
836                    .get(index_in_caller as usize)
837                    .ok_or_else(|| {
838                        ic_msg!(
839                            invoke_context,
840                            "Internal error: index mismatch for account {}",
841                            account_key
842                        );
843                        Box::new(InstructionError::MissingAccount)
844                    })?;
845
846            // build the CallerAccount corresponding to this account.
847            if caller_account_index >= account_infos.len() {
848                return Err(Box::new(SyscallError::InvalidLength));
849            }
850            #[allow(clippy::indexing_slicing)]
851            let caller_account =
852                do_translate(
853                    invoke_context,
854                    memory_mapping,
855                    check_aligned,
856                    account_infos_addr.saturating_add(
857                        caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
858                    ),
859                    &account_infos[caller_account_index],
860                    serialized_metadata,
861                )?;
862
863            // before initiating CPI, the caller may have modified the
864            // account (caller_account). We need to update the corresponding
865            // BorrowedAccount (callee_account) so the callee can see the
866            // changes.
867            let update_caller = update_callee_account(
868                check_aligned,
869                &caller_account,
870                callee_account,
871                stricter_abi_and_runtime_constraints,
872                invoke_context.account_data_direct_mapping,
873            )?;
874
875            accounts.push(TranslatedAccount {
876                index_in_caller,
877                caller_account,
878                update_caller_account_region: instruction_account.is_writable() || update_caller,
879                update_caller_account_info: instruction_account.is_writable(),
880            });
881        } else {
882            ic_msg!(
883                invoke_context,
884                "Instruction references an unknown account {}",
885                account_key
886            );
887            return Err(Box::new(InstructionError::MissingAccount));
888        }
889    }
890
891    Ok(accounts)
892}
893
894fn check_instruction_size(num_accounts: usize, data_len: usize) -> Result<(), Error> {
895    if num_accounts > MAX_ACCOUNTS_PER_INSTRUCTION {
896        return Err(Box::new(SyscallError::MaxInstructionAccountsExceeded {
897            num_accounts: num_accounts as u64,
898            max_accounts: MAX_ACCOUNTS_PER_INSTRUCTION as u64,
899        }));
900    }
901    if data_len > MAX_INSTRUCTION_DATA_LEN {
902        return Err(Box::new(SyscallError::MaxInstructionDataLenExceeded {
903            data_len: data_len as u64,
904            max_data_len: MAX_INSTRUCTION_DATA_LEN as u64,
905        }));
906    }
907    Ok(())
908}
909
910fn check_account_infos(
911    num_account_infos: usize,
912    invoke_context: &mut InvokeContext,
913) -> Result<(), Error> {
914    let max_cpi_account_infos = if invoke_context
915        .get_feature_set()
916        .increase_tx_account_lock_limit
917    {
918        MAX_CPI_ACCOUNT_INFOS
919    } else {
920        64
921    };
922    let num_account_infos = num_account_infos as u64;
923    let max_account_infos = max_cpi_account_infos as u64;
924    if num_account_infos > max_account_infos {
925        return Err(Box::new(SyscallError::MaxInstructionAccountInfosExceeded {
926            num_account_infos,
927            max_account_infos,
928        }));
929    }
930    Ok(())
931}
932
933fn check_authorized_program(
934    program_id: &Pubkey,
935    instruction_data: &[u8],
936    invoke_context: &InvokeContext,
937) -> Result<(), Error> {
938    if native_loader::check_id(program_id)
939        || bpf_loader::check_id(program_id)
940        || bpf_loader_deprecated::check_id(program_id)
941        || (solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
942            && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
943                || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
944                || (invoke_context
945                    .get_feature_set()
946                    .enable_bpf_loader_set_authority_checked_ix
947                    && bpf_loader_upgradeable::is_set_authority_checked_instruction(
948                        instruction_data,
949                    ))
950                || (invoke_context
951                    .get_feature_set()
952                    .enable_extend_program_checked
953                    && bpf_loader_upgradeable::is_extend_program_checked_instruction(
954                        instruction_data,
955                    ))
956                || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
957        || invoke_context.is_precompile(program_id)
958    {
959        return Err(Box::new(SyscallError::ProgramNotSupported(*program_id)));
960    }
961    Ok(())
962}
963
964/// Call process instruction, common to both Rust and C
965fn cpi_common<S: SyscallInvokeSigned>(
966    invoke_context: &mut InvokeContext,
967    instruction_addr: u64,
968    account_infos_addr: u64,
969    account_infos_len: u64,
970    signers_seeds_addr: u64,
971    signers_seeds_len: u64,
972    memory_mapping: &mut MemoryMapping,
973) -> Result<u64, Error> {
974    let check_aligned = invoke_context.get_check_aligned();
975
976    // CPI entry.
977    //
978    // Translate the inputs to the syscall and synchronize the caller's account
979    // changes so the callee can see them.
980    consume_compute_meter(
981        invoke_context,
982        invoke_context.get_execution_cost().invoke_units,
983    )?;
984    if let Some(execute_time) = invoke_context.execute_time.as_mut() {
985        execute_time.stop();
986        invoke_context.timings.execute_us += execute_time.as_us();
987    }
988
989    let instruction = S::translate_instruction(
990        instruction_addr,
991        memory_mapping,
992        invoke_context,
993        check_aligned,
994    )?;
995    let transaction_context = &invoke_context.transaction_context;
996    let instruction_context = transaction_context.get_current_instruction_context()?;
997    let caller_program_id = instruction_context.get_program_key()?;
998    let signers = S::translate_signers(
999        caller_program_id,
1000        signers_seeds_addr,
1001        signers_seeds_len,
1002        memory_mapping,
1003        check_aligned,
1004    )?;
1005    check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
1006    invoke_context.prepare_next_instruction(&instruction, &signers)?;
1007
1008    let mut accounts = S::translate_accounts(
1009        account_infos_addr,
1010        account_infos_len,
1011        memory_mapping,
1012        invoke_context,
1013        check_aligned,
1014    )?;
1015
1016    // Process the callee instruction
1017    let mut compute_units_consumed = 0;
1018    invoke_context
1019        .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
1020
1021    // re-bind to please the borrow checker
1022    let transaction_context = &invoke_context.transaction_context;
1023    let instruction_context = transaction_context.get_current_instruction_context()?;
1024
1025    // CPI exit.
1026    //
1027    // Synchronize the callee's account changes so the caller can see them.
1028    let stricter_abi_and_runtime_constraints = invoke_context
1029        .get_feature_set()
1030        .stricter_abi_and_runtime_constraints;
1031
1032    for translate_account in accounts.iter_mut() {
1033        let mut callee_account = instruction_context
1034            .try_borrow_instruction_account(translate_account.index_in_caller)?;
1035        if translate_account.update_caller_account_info {
1036            update_caller_account(
1037                invoke_context,
1038                memory_mapping,
1039                check_aligned,
1040                &mut translate_account.caller_account,
1041                &mut callee_account,
1042                stricter_abi_and_runtime_constraints,
1043            )?;
1044        }
1045    }
1046
1047    if stricter_abi_and_runtime_constraints {
1048        for translate_account in accounts.iter() {
1049            let mut callee_account = instruction_context
1050                .try_borrow_instruction_account(translate_account.index_in_caller)?;
1051            if translate_account.update_caller_account_region {
1052                update_caller_account_region(
1053                    memory_mapping,
1054                    check_aligned,
1055                    &translate_account.caller_account,
1056                    &mut callee_account,
1057                    invoke_context.account_data_direct_mapping,
1058                )?;
1059            }
1060        }
1061    }
1062
1063    invoke_context.execute_time = Some(Measure::start("execute"));
1064    Ok(SUCCESS)
1065}
1066
1067// Update the given account before executing CPI.
1068//
1069// caller_account and callee_account describe the same account. At CPI entry
1070// caller_account might include changes the caller has made to the account
1071// before executing CPI.
1072//
1073// This method updates callee_account so the CPI callee can see the caller's
1074// changes.
1075//
1076// When true is returned, the caller account must be updated after CPI. This
1077// is only set for stricter_abi_and_runtime_constraints when the pointer may have changed.
1078fn update_callee_account(
1079    check_aligned: bool,
1080    caller_account: &CallerAccount,
1081    mut callee_account: BorrowedAccount<'_>,
1082    stricter_abi_and_runtime_constraints: bool,
1083    account_data_direct_mapping: bool,
1084) -> Result<bool, Error> {
1085    let mut must_update_caller = false;
1086
1087    if callee_account.get_lamports() != *caller_account.lamports {
1088        callee_account.set_lamports(*caller_account.lamports)?;
1089    }
1090
1091    if stricter_abi_and_runtime_constraints {
1092        let prev_len = callee_account.get_data().len();
1093        let post_len = *caller_account.ref_to_len_in_vm as usize;
1094        if prev_len != post_len {
1095            let is_caller_loader_deprecated = !check_aligned;
1096            let address_space_reserved_for_account = if is_caller_loader_deprecated {
1097                caller_account.original_data_len
1098            } else {
1099                caller_account
1100                    .original_data_len
1101                    .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1102            };
1103            if post_len > address_space_reserved_for_account {
1104                return Err(InstructionError::InvalidRealloc.into());
1105            }
1106            callee_account.set_data_length(post_len)?;
1107            // pointer to data may have changed, so caller must be updated
1108            must_update_caller = true;
1109        }
1110        if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1111            callee_account.set_data_from_slice(caller_account.serialized_data)?;
1112        }
1113    } else {
1114        // The redundant check helps to avoid the expensive data comparison if we can
1115        match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1116            Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1117            Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1118                return Err(Box::new(err));
1119            }
1120            _ => {}
1121        }
1122    }
1123
1124    // Change the owner at the end so that we are allowed to change the lamports and data before
1125    if callee_account.get_owner() != caller_account.owner {
1126        callee_account.set_owner(caller_account.owner.as_ref())?;
1127        // caller gave ownership and thus write access away, so caller must be updated
1128        must_update_caller = true;
1129    }
1130
1131    Ok(must_update_caller)
1132}
1133
1134fn update_caller_account_region(
1135    memory_mapping: &mut MemoryMapping,
1136    check_aligned: bool,
1137    caller_account: &CallerAccount,
1138    callee_account: &mut BorrowedAccount<'_>,
1139    account_data_direct_mapping: bool,
1140) -> Result<(), Error> {
1141    let is_caller_loader_deprecated = !check_aligned;
1142    let address_space_reserved_for_account = if is_caller_loader_deprecated {
1143        caller_account.original_data_len
1144    } else {
1145        caller_account
1146            .original_data_len
1147            .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1148    };
1149
1150    if address_space_reserved_for_account > 0 {
1151        // We can trust vm_data_addr to point to the correct region because we
1152        // enforce that in CallerAccount::from_(sol_)account_info.
1153        let (region_index, region) = memory_mapping
1154            .find_region(caller_account.vm_data_addr)
1155            .ok_or_else(|| Box::new(InstructionError::MissingAccount))?;
1156        // vm_data_addr must always point to the beginning of the region
1157        debug_assert_eq!(region.vm_addr, caller_account.vm_data_addr);
1158        let mut new_region;
1159        if !account_data_direct_mapping {
1160            new_region = region.clone();
1161            modify_memory_region_of_account(callee_account, &mut new_region);
1162        } else {
1163            new_region = create_memory_region_of_account(callee_account, region.vm_addr)?;
1164        }
1165        memory_mapping.replace_region(region_index, new_region)?;
1166    }
1167
1168    Ok(())
1169}
1170
1171// Update the given account after executing CPI.
1172//
1173// caller_account and callee_account describe to the same account. At CPI exit
1174// callee_account might include changes the callee has made to the account
1175// after executing.
1176//
1177// This method updates caller_account so the CPI caller can see the callee's
1178// changes.
1179//
1180// Safety: Once `stricter_abi_and_runtime_constraints` is enabled all fields of [CallerAccount] used
1181// in this function should never point inside the address space reserved for
1182// accounts (regardless of the current size of an account).
1183fn update_caller_account(
1184    invoke_context: &InvokeContext,
1185    memory_mapping: &MemoryMapping<'_>,
1186    check_aligned: bool,
1187    caller_account: &mut CallerAccount<'_>,
1188    callee_account: &mut BorrowedAccount<'_>,
1189    stricter_abi_and_runtime_constraints: bool,
1190) -> Result<(), Error> {
1191    *caller_account.lamports = callee_account.get_lamports();
1192    *caller_account.owner = *callee_account.get_owner();
1193
1194    let prev_len = *caller_account.ref_to_len_in_vm as usize;
1195    let post_len = callee_account.get_data().len();
1196    let is_caller_loader_deprecated = !check_aligned;
1197    let address_space_reserved_for_account =
1198        if stricter_abi_and_runtime_constraints && is_caller_loader_deprecated {
1199            caller_account.original_data_len
1200        } else {
1201            caller_account
1202                .original_data_len
1203                .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1204        };
1205
1206    if post_len > address_space_reserved_for_account
1207        && (stricter_abi_and_runtime_constraints || prev_len != post_len)
1208    {
1209        let max_increase =
1210            address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1211        ic_msg!(
1212            invoke_context,
1213            "Account data size realloc limited to {max_increase} in inner instructions",
1214        );
1215        return Err(Box::new(InstructionError::InvalidRealloc));
1216    }
1217
1218    if prev_len != post_len {
1219        // when stricter_abi_and_runtime_constraints is enabled we don't cache the serialized data in
1220        // caller_account.serialized_data. See CallerAccount::from_account_info.
1221        if !(stricter_abi_and_runtime_constraints && invoke_context.account_data_direct_mapping) {
1222            // If the account has been shrunk, we're going to zero the unused memory
1223            // *that was previously used*.
1224            if post_len < prev_len {
1225                caller_account
1226                    .serialized_data
1227                    .get_mut(post_len..)
1228                    .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1229                    .fill(0);
1230            }
1231            // Set the length of caller_account.serialized_data to post_len.
1232            caller_account.serialized_data = CallerAccount::get_serialized_data(
1233                memory_mapping,
1234                caller_account.vm_data_addr,
1235                post_len as u64,
1236                stricter_abi_and_runtime_constraints,
1237                invoke_context.account_data_direct_mapping,
1238            )?;
1239        }
1240        // this is the len field in the AccountInfo::data slice
1241        *caller_account.ref_to_len_in_vm = post_len as u64;
1242
1243        // this is the len field in the serialized parameters
1244        let serialized_len_ptr = translate_type_mut::<u64>(
1245            memory_mapping,
1246            caller_account
1247                .vm_data_addr
1248                .saturating_sub(std::mem::size_of::<u64>() as u64),
1249            check_aligned,
1250        )?;
1251        *serialized_len_ptr = post_len as u64;
1252    }
1253
1254    if !(stricter_abi_and_runtime_constraints && invoke_context.account_data_direct_mapping) {
1255        // Propagate changes in the callee up to the caller.
1256        let to_slice = &mut caller_account.serialized_data;
1257        let from_slice = callee_account
1258            .get_data()
1259            .get(0..post_len)
1260            .ok_or(SyscallError::InvalidLength)?;
1261        if to_slice.len() != from_slice.len() {
1262            return Err(Box::new(InstructionError::AccountDataTooSmall));
1263        }
1264        to_slice.copy_from_slice(from_slice);
1265    }
1266
1267    Ok(())
1268}
1269
1270#[allow(clippy::indexing_slicing)]
1271#[allow(clippy::arithmetic_side_effects)]
1272#[cfg(test)]
1273mod tests {
1274    use {
1275        super::*,
1276        assert_matches::assert_matches,
1277        solana_account::{Account, AccountSharedData, ReadableAccount},
1278        solana_instruction::Instruction,
1279        solana_program_runtime::{
1280            invoke_context::{BpfAllocator, SerializedAccountMetadata, SyscallContext},
1281            with_mock_invoke_context_with_feature_set,
1282        },
1283        solana_sbpf::{
1284            ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1285        },
1286        solana_sdk_ids::system_program,
1287        solana_transaction_context::{InstructionAccount, TransactionAccount},
1288        std::{
1289            cell::{Cell, RefCell},
1290            mem, ptr,
1291            rc::Rc,
1292            slice,
1293        },
1294        test_case::test_matrix,
1295    };
1296
1297    macro_rules! mock_invoke_context {
1298        ($invoke_context:ident,
1299         $transaction_context:ident,
1300         $instruction_data:expr,
1301         $transaction_accounts:expr,
1302         $program_account:expr,
1303         $instruction_accounts:expr) => {
1304            let instruction_data = $instruction_data;
1305            let instruction_accounts = $instruction_accounts
1306                .iter()
1307                .map(|index_in_transaction| {
1308                    InstructionAccount::new(
1309                        *index_in_transaction as IndexOfAccount,
1310                        false,
1311                        $transaction_accounts[*index_in_transaction as usize].2,
1312                    )
1313                })
1314                .collect::<Vec<_>>();
1315            let transaction_accounts = $transaction_accounts
1316                .into_iter()
1317                .map(|a| (a.0, a.1))
1318                .collect::<Vec<TransactionAccount>>();
1319            let mut feature_set = SVMFeatureSet::all_enabled();
1320            feature_set.stricter_abi_and_runtime_constraints = false;
1321            let feature_set = &feature_set;
1322            with_mock_invoke_context_with_feature_set!(
1323                $invoke_context,
1324                $transaction_context,
1325                feature_set,
1326                transaction_accounts
1327            );
1328            $invoke_context
1329                .transaction_context
1330                .configure_next_instruction_for_tests(
1331                    $program_account,
1332                    instruction_accounts,
1333                    instruction_data,
1334                )
1335                .unwrap();
1336            $invoke_context.push().unwrap();
1337        };
1338    }
1339
1340    macro_rules! borrow_instruction_account {
1341        ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1342            let instruction_context = $invoke_context
1343                .transaction_context
1344                .get_current_instruction_context()
1345                .unwrap();
1346            let $borrowed_account = instruction_context
1347                .try_borrow_instruction_account($index)
1348                .unwrap();
1349        };
1350    }
1351
1352    #[test]
1353    fn test_translate_instruction() {
1354        let transaction_accounts =
1355            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1356        mock_invoke_context!(
1357            invoke_context,
1358            transaction_context,
1359            b"instruction data",
1360            transaction_accounts,
1361            0,
1362            &[1]
1363        );
1364
1365        let program_id = Pubkey::new_unique();
1366        let accounts = vec![AccountMeta {
1367            pubkey: Pubkey::new_unique(),
1368            is_signer: true,
1369            is_writable: false,
1370        }];
1371        let data = b"ins data".to_vec();
1372        let vm_addr = MM_INPUT_START;
1373        let (_mem, region) = MockInstruction {
1374            program_id,
1375            accounts: accounts.clone(),
1376            data: data.clone(),
1377        }
1378        .into_region(vm_addr);
1379
1380        let config = Config {
1381            aligned_memory_mapping: false,
1382            ..Config::default()
1383        };
1384        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1385
1386        let ins = SyscallInvokeSignedRust::translate_instruction(
1387            vm_addr,
1388            &memory_mapping,
1389            &mut invoke_context,
1390            true, // check_aligned
1391        )
1392        .unwrap();
1393        assert_eq!(ins.program_id, program_id);
1394        assert_eq!(ins.accounts, accounts);
1395        assert_eq!(ins.data, data);
1396    }
1397
1398    #[test]
1399    fn test_translate_signers() {
1400        let transaction_accounts =
1401            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1402        mock_invoke_context!(
1403            invoke_context,
1404            transaction_context,
1405            b"instruction data",
1406            transaction_accounts,
1407            0,
1408            &[1]
1409        );
1410
1411        let program_id = Pubkey::new_unique();
1412        let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1413
1414        let vm_addr = MM_INPUT_START;
1415        let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1416
1417        let config = Config {
1418            aligned_memory_mapping: false,
1419            ..Config::default()
1420        };
1421        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1422
1423        let signers = SyscallInvokeSignedRust::translate_signers(
1424            &program_id,
1425            vm_addr,
1426            1,
1427            &memory_mapping,
1428            true, // check_aligned
1429        )
1430        .unwrap();
1431        assert_eq!(signers[0], derived_key);
1432    }
1433
1434    #[test]
1435    fn test_caller_account_from_account_info() {
1436        let transaction_accounts =
1437            transaction_with_one_writable_instruction_account(b"foo".to_vec());
1438        let account = transaction_accounts[1].1.clone();
1439        mock_invoke_context!(
1440            invoke_context,
1441            transaction_context,
1442            b"instruction data",
1443            transaction_accounts,
1444            0,
1445            &[1]
1446        );
1447
1448        let key = Pubkey::new_unique();
1449        let vm_addr = MM_INPUT_START;
1450        let (_mem, region, account_metadata) =
1451            MockAccountInfo::new(key, &account).into_region(vm_addr);
1452
1453        let config = Config {
1454            aligned_memory_mapping: false,
1455            ..Config::default()
1456        };
1457        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1458
1459        let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1460
1461        let caller_account = CallerAccount::from_account_info(
1462            &invoke_context,
1463            &memory_mapping,
1464            true, // check_aligned
1465            vm_addr,
1466            account_info,
1467            &account_metadata,
1468        )
1469        .unwrap();
1470        assert_eq!(*caller_account.lamports, account.lamports());
1471        assert_eq!(caller_account.owner, account.owner());
1472        assert_eq!(caller_account.original_data_len, account.data().len());
1473        assert_eq!(
1474            *caller_account.ref_to_len_in_vm as usize,
1475            account.data().len()
1476        );
1477        assert_eq!(caller_account.serialized_data, account.data());
1478    }
1479
1480    #[test_matrix([false, true])]
1481    fn test_update_caller_account_lamports_owner(stricter_abi_and_runtime_constraints: bool) {
1482        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1483        let account = transaction_accounts[1].1.clone();
1484        mock_invoke_context!(
1485            invoke_context,
1486            transaction_context,
1487            b"instruction data",
1488            transaction_accounts,
1489            0,
1490            &[1]
1491        );
1492
1493        let mut mock_caller_account =
1494            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1495
1496        let config = Config {
1497            aligned_memory_mapping: false,
1498            ..Config::default()
1499        };
1500        let memory_mapping = MemoryMapping::new(
1501            mock_caller_account.regions.split_off(0),
1502            &config,
1503            SBPFVersion::V3,
1504        )
1505        .unwrap();
1506
1507        let mut caller_account = mock_caller_account.caller_account();
1508        let instruction_context = invoke_context
1509            .transaction_context
1510            .get_current_instruction_context()
1511            .unwrap();
1512        let mut callee_account = instruction_context
1513            .try_borrow_instruction_account(0)
1514            .unwrap();
1515        callee_account.set_lamports(42).unwrap();
1516        callee_account
1517            .set_owner(Pubkey::new_unique().as_ref())
1518            .unwrap();
1519
1520        update_caller_account(
1521            &invoke_context,
1522            &memory_mapping,
1523            true, // check_aligned
1524            &mut caller_account,
1525            &mut callee_account,
1526            stricter_abi_and_runtime_constraints,
1527        )
1528        .unwrap();
1529
1530        assert_eq!(*caller_account.lamports, 42);
1531        assert_eq!(caller_account.owner, callee_account.get_owner());
1532    }
1533
1534    #[test]
1535    fn test_update_caller_account_data() {
1536        let transaction_accounts =
1537            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1538        let account = transaction_accounts[1].1.clone();
1539        let original_data_len = account.data().len();
1540
1541        mock_invoke_context!(
1542            invoke_context,
1543            transaction_context,
1544            b"instruction data",
1545            transaction_accounts,
1546            0,
1547            &[1]
1548        );
1549
1550        let mut mock_caller_account =
1551            MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
1552
1553        let config = Config {
1554            aligned_memory_mapping: false,
1555            ..Config::default()
1556        };
1557        let memory_mapping = MemoryMapping::new(
1558            mock_caller_account.regions.clone(),
1559            &config,
1560            SBPFVersion::V3,
1561        )
1562        .unwrap();
1563
1564        let data_slice = mock_caller_account.data_slice();
1565        let len_ptr = unsafe {
1566            data_slice
1567                .as_ptr()
1568                .offset(-(mem::size_of::<u64>() as isize))
1569        };
1570        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1571        let mut caller_account = mock_caller_account.caller_account();
1572        let instruction_context = invoke_context
1573            .transaction_context
1574            .get_current_instruction_context()
1575            .unwrap();
1576        let mut callee_account = instruction_context
1577            .try_borrow_instruction_account(0)
1578            .unwrap();
1579
1580        for (new_value, expected_realloc_size) in [
1581            (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1582            (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1583            (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1584        ] {
1585            assert_eq!(caller_account.serialized_data, callee_account.get_data());
1586            callee_account.set_data_from_slice(&new_value).unwrap();
1587
1588            update_caller_account(
1589                &invoke_context,
1590                &memory_mapping,
1591                true, // check_aligned
1592                &mut caller_account,
1593                &mut callee_account,
1594                false,
1595            )
1596            .unwrap();
1597
1598            let data_len = callee_account.get_data().len();
1599            assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
1600            assert_eq!(data_len, serialized_len());
1601            assert_eq!(data_len, caller_account.serialized_data.len());
1602            assert_eq!(
1603                callee_account.get_data(),
1604                &caller_account.serialized_data[..data_len]
1605            );
1606            assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1607            assert!(is_zeroed(&data_slice[data_len..]));
1608        }
1609
1610        callee_account
1611            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1612            .unwrap();
1613        update_caller_account(
1614            &invoke_context,
1615            &memory_mapping,
1616            true, // check_aligned
1617            &mut caller_account,
1618            &mut callee_account,
1619            false,
1620        )
1621        .unwrap();
1622        let data_len = callee_account.get_data().len();
1623        assert_eq!(data_slice[data_len..].len(), 0);
1624        assert!(is_zeroed(&data_slice[data_len..]));
1625
1626        callee_account
1627            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1628            .unwrap();
1629        assert_matches!(
1630            update_caller_account(
1631                &invoke_context,
1632                &memory_mapping,
1633                true, // check_aligned
1634                &mut caller_account,
1635                &mut callee_account,
1636                false,
1637            ),
1638            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1639        );
1640
1641        // close the account
1642        callee_account.set_data_length(0).unwrap();
1643        callee_account
1644            .set_owner(system_program::id().as_ref())
1645            .unwrap();
1646        update_caller_account(
1647            &invoke_context,
1648            &memory_mapping,
1649            true, // check_aligned
1650            &mut caller_account,
1651            &mut callee_account,
1652            false,
1653        )
1654        .unwrap();
1655        let data_len = callee_account.get_data().len();
1656        assert_eq!(data_len, 0);
1657    }
1658
1659    #[test_matrix([false, true])]
1660    fn test_update_callee_account_lamports_owner(stricter_abi_and_runtime_constraints: bool) {
1661        let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1662        let account = transaction_accounts[1].1.clone();
1663
1664        mock_invoke_context!(
1665            invoke_context,
1666            transaction_context,
1667            b"instruction data",
1668            transaction_accounts,
1669            0,
1670            &[1]
1671        );
1672
1673        let mut mock_caller_account =
1674            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1675
1676        let caller_account = mock_caller_account.caller_account();
1677
1678        borrow_instruction_account!(callee_account, invoke_context, 0);
1679
1680        *caller_account.lamports = 42;
1681        *caller_account.owner = Pubkey::new_unique();
1682
1683        update_callee_account(
1684            true, // check_aligned
1685            &caller_account,
1686            callee_account,
1687            stricter_abi_and_runtime_constraints,
1688            true, // account_data_direct_mapping
1689        )
1690        .unwrap();
1691
1692        borrow_instruction_account!(callee_account, invoke_context, 0);
1693        assert_eq!(callee_account.get_lamports(), 42);
1694        assert_eq!(caller_account.owner, callee_account.get_owner());
1695    }
1696
1697    #[test_matrix([false, true])]
1698    fn test_update_callee_account_data_writable(stricter_abi_and_runtime_constraints: bool) {
1699        let transaction_accounts =
1700            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1701        let account = transaction_accounts[1].1.clone();
1702
1703        mock_invoke_context!(
1704            invoke_context,
1705            transaction_context,
1706            b"instruction data",
1707            transaction_accounts,
1708            0,
1709            &[1]
1710        );
1711
1712        let mut mock_caller_account =
1713            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1714
1715        let mut caller_account = mock_caller_account.caller_account();
1716        borrow_instruction_account!(callee_account, invoke_context, 0);
1717
1718        // stricter_abi_and_runtime_constraints does not copy data in update_callee_account()
1719        caller_account.serialized_data[0] = b'b';
1720        update_callee_account(
1721            true, // check_aligned
1722            &caller_account,
1723            callee_account,
1724            false, // stricter_abi_and_runtime_constraints
1725            false, // account_data_direct_mapping
1726        )
1727        .unwrap();
1728        borrow_instruction_account!(callee_account, invoke_context, 0);
1729        assert_eq!(callee_account.get_data(), b"boobar");
1730
1731        // growing resize
1732        let mut data = b"foobarbaz".to_vec();
1733        *caller_account.ref_to_len_in_vm = data.len() as u64;
1734        caller_account.serialized_data = &mut data;
1735        assert_eq!(
1736            update_callee_account(
1737                true, // check_aligned
1738                &caller_account,
1739                callee_account,
1740                stricter_abi_and_runtime_constraints,
1741                true, // account_data_direct_mapping
1742            )
1743            .unwrap(),
1744            stricter_abi_and_runtime_constraints,
1745        );
1746
1747        // truncating resize
1748        let mut data = b"baz".to_vec();
1749        *caller_account.ref_to_len_in_vm = data.len() as u64;
1750        caller_account.serialized_data = &mut data;
1751        borrow_instruction_account!(callee_account, invoke_context, 0);
1752        assert_eq!(
1753            update_callee_account(
1754                true, // check_aligned
1755                &caller_account,
1756                callee_account,
1757                stricter_abi_and_runtime_constraints,
1758                true, // account_data_direct_mapping
1759            )
1760            .unwrap(),
1761            stricter_abi_and_runtime_constraints,
1762        );
1763
1764        // close the account
1765        let mut data = Vec::new();
1766        caller_account.serialized_data = &mut data;
1767        *caller_account.ref_to_len_in_vm = 0;
1768        let mut owner = system_program::id();
1769        caller_account.owner = &mut owner;
1770        borrow_instruction_account!(callee_account, invoke_context, 0);
1771        update_callee_account(
1772            true, // check_aligned
1773            &caller_account,
1774            callee_account,
1775            stricter_abi_and_runtime_constraints,
1776            true, // account_data_direct_mapping
1777        )
1778        .unwrap();
1779        borrow_instruction_account!(callee_account, invoke_context, 0);
1780        assert_eq!(callee_account.get_data(), b"");
1781
1782        // growing beyond address_space_reserved_for_account
1783        *caller_account.ref_to_len_in_vm = (7 + MAX_PERMITTED_DATA_INCREASE) as u64;
1784        let result = update_callee_account(
1785            true, // check_aligned
1786            &caller_account,
1787            callee_account,
1788            stricter_abi_and_runtime_constraints,
1789            true, // account_data_direct_mapping
1790        );
1791        if stricter_abi_and_runtime_constraints {
1792            assert_matches!(
1793                result,
1794                Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1795            );
1796        } else {
1797            result.unwrap();
1798        }
1799    }
1800
1801    #[test_matrix([false, true])]
1802    fn test_update_callee_account_data_readonly(stricter_abi_and_runtime_constraints: bool) {
1803        let transaction_accounts =
1804            transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
1805        let account = transaction_accounts[1].1.clone();
1806
1807        mock_invoke_context!(
1808            invoke_context,
1809            transaction_context,
1810            b"instruction data",
1811            transaction_accounts,
1812            0,
1813            &[1]
1814        );
1815
1816        let mut mock_caller_account =
1817            MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1818        let mut caller_account = mock_caller_account.caller_account();
1819        borrow_instruction_account!(callee_account, invoke_context, 0);
1820
1821        // stricter_abi_and_runtime_constraints does not copy data in update_callee_account()
1822        caller_account.serialized_data[0] = b'b';
1823        assert_matches!(
1824            update_callee_account(
1825                true, // check_aligned
1826                &caller_account,
1827                callee_account,
1828                false, // stricter_abi_and_runtime_constraints
1829                false, // account_data_direct_mapping
1830            ),
1831            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
1832        );
1833
1834        // growing resize
1835        let mut data = b"foobarbaz".to_vec();
1836        *caller_account.ref_to_len_in_vm = data.len() as u64;
1837        caller_account.serialized_data = &mut data;
1838        borrow_instruction_account!(callee_account, invoke_context, 0);
1839        assert_matches!(
1840            update_callee_account(
1841                true, // check_aligned
1842                &caller_account,
1843                callee_account,
1844                stricter_abi_and_runtime_constraints,
1845                true, // account_data_direct_mapping
1846            ),
1847            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
1848        );
1849
1850        // truncating resize
1851        let mut data = b"baz".to_vec();
1852        *caller_account.ref_to_len_in_vm = data.len() as u64;
1853        caller_account.serialized_data = &mut data;
1854        borrow_instruction_account!(callee_account, invoke_context, 0);
1855        assert_matches!(
1856            update_callee_account(
1857                true, // check_aligned
1858                &caller_account,
1859                callee_account,
1860                stricter_abi_and_runtime_constraints,
1861                true, // account_data_direct_mapping
1862            ),
1863            Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
1864        );
1865    }
1866
1867    #[test]
1868    fn test_translate_accounts_rust() {
1869        let transaction_accounts =
1870            transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1871        let account = transaction_accounts[1].1.clone();
1872        let key = transaction_accounts[1].0;
1873        let original_data_len = account.data().len();
1874
1875        let vm_addr = MM_INPUT_START;
1876        let (_mem, region, account_metadata) =
1877            MockAccountInfo::new(key, &account).into_region(vm_addr);
1878
1879        let config = Config {
1880            aligned_memory_mapping: false,
1881            ..Config::default()
1882        };
1883        let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1884
1885        mock_invoke_context!(
1886            invoke_context,
1887            transaction_context,
1888            b"instruction data",
1889            transaction_accounts,
1890            0,
1891            &[1, 1]
1892        );
1893
1894        invoke_context
1895            .set_syscall_context(SyscallContext {
1896                allocator: BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1897                accounts_metadata: vec![account_metadata],
1898                trace_log: Vec::new(),
1899            })
1900            .unwrap();
1901
1902        invoke_context
1903            .transaction_context
1904            .configure_next_instruction_for_tests(
1905                0,
1906                vec![
1907                    InstructionAccount::new(1, false, true),
1908                    InstructionAccount::new(1, false, true),
1909                ],
1910                &[],
1911            )
1912            .unwrap();
1913        let accounts = SyscallInvokeSignedRust::translate_accounts(
1914            vm_addr,
1915            1,
1916            &memory_mapping,
1917            &mut invoke_context,
1918            true, // check_aligned
1919        )
1920        .unwrap();
1921        assert_eq!(accounts.len(), 1);
1922        let caller_account = &accounts[0].caller_account;
1923        assert_eq!(caller_account.serialized_data, account.data());
1924        assert_eq!(caller_account.original_data_len, original_data_len);
1925    }
1926
1927    type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1928    struct MockCallerAccount {
1929        lamports: u64,
1930        owner: Pubkey,
1931        vm_addr: u64,
1932        data: Vec<u8>,
1933        len: u64,
1934        regions: Vec<MemoryRegion>,
1935        stricter_abi_and_runtime_constraints: bool,
1936    }
1937
1938    impl MockCallerAccount {
1939        fn new(
1940            lamports: u64,
1941            owner: Pubkey,
1942            data: &[u8],
1943            stricter_abi_and_runtime_constraints: bool,
1944        ) -> MockCallerAccount {
1945            let vm_addr = MM_INPUT_START;
1946            let mut region_addr = vm_addr;
1947            let region_len = mem::size_of::<u64>()
1948                + if stricter_abi_and_runtime_constraints {
1949                    0
1950                } else {
1951                    data.len() + MAX_PERMITTED_DATA_INCREASE
1952                };
1953            let mut d = vec![0; region_len];
1954            let mut regions = vec![];
1955
1956            // always write the [len] part even when stricter_abi_and_runtime_constraints
1957            unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1958
1959            // write the account data when not stricter_abi_and_runtime_constraints
1960            if !stricter_abi_and_runtime_constraints {
1961                d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1962            }
1963
1964            // create a region for [len][data+realloc if !stricter_abi_and_runtime_constraints]
1965            regions.push(MemoryRegion::new_writable(&mut d[..region_len], vm_addr));
1966            region_addr += region_len as u64;
1967
1968            if stricter_abi_and_runtime_constraints {
1969                // create a region for the directly mapped data
1970                regions.push(MemoryRegion::new_readonly(data, region_addr));
1971                region_addr += data.len() as u64;
1972
1973                // create a region for the realloc padding
1974                regions.push(MemoryRegion::new_writable(
1975                    &mut d[mem::size_of::<u64>()..],
1976                    region_addr,
1977                ));
1978            } else {
1979                // caller_account.serialized_data must have the actual data length
1980                d.truncate(mem::size_of::<u64>() + data.len());
1981            }
1982
1983            MockCallerAccount {
1984                lamports,
1985                owner,
1986                vm_addr,
1987                data: d,
1988                len: data.len() as u64,
1989                regions,
1990                stricter_abi_and_runtime_constraints,
1991            }
1992        }
1993
1994        fn data_slice<'a>(&self) -> &'a [u8] {
1995            // lifetime crimes
1996            unsafe {
1997                slice::from_raw_parts(
1998                    self.data[mem::size_of::<u64>()..].as_ptr(),
1999                    self.data.capacity() - mem::size_of::<u64>(),
2000                )
2001            }
2002        }
2003
2004        fn caller_account(&mut self) -> CallerAccount {
2005            let data = if self.stricter_abi_and_runtime_constraints {
2006                &mut []
2007            } else {
2008                &mut self.data[mem::size_of::<u64>()..]
2009            };
2010            CallerAccount {
2011                lamports: &mut self.lamports,
2012                owner: &mut self.owner,
2013                original_data_len: self.len as usize,
2014                serialized_data: data,
2015                vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
2016                ref_to_len_in_vm: &mut self.len,
2017            }
2018        }
2019    }
2020
2021    fn transaction_with_one_writable_instruction_account(
2022        data: Vec<u8>,
2023    ) -> Vec<TestTransactionAccount> {
2024        let program_id = Pubkey::new_unique();
2025        let account = AccountSharedData::from(Account {
2026            lamports: 1,
2027            data,
2028            owner: program_id,
2029            executable: false,
2030            rent_epoch: 100,
2031        });
2032        vec![
2033            (
2034                program_id,
2035                AccountSharedData::from(Account {
2036                    lamports: 0,
2037                    data: vec![],
2038                    owner: bpf_loader::id(),
2039                    executable: true,
2040                    rent_epoch: 0,
2041                }),
2042                false,
2043            ),
2044            (Pubkey::new_unique(), account, true),
2045        ]
2046    }
2047
2048    fn transaction_with_one_readonly_instruction_account(
2049        data: Vec<u8>,
2050    ) -> Vec<TestTransactionAccount> {
2051        let program_id = Pubkey::new_unique();
2052        let account_owner = Pubkey::new_unique();
2053        let account = AccountSharedData::from(Account {
2054            lamports: 1,
2055            data,
2056            owner: account_owner,
2057            executable: false,
2058            rent_epoch: 100,
2059        });
2060        vec![
2061            (
2062                program_id,
2063                AccountSharedData::from(Account {
2064                    lamports: 0,
2065                    data: vec![],
2066                    owner: bpf_loader::id(),
2067                    executable: true,
2068                    rent_epoch: 0,
2069                }),
2070                false,
2071            ),
2072            (Pubkey::new_unique(), account, true),
2073        ]
2074    }
2075
2076    struct MockInstruction {
2077        program_id: Pubkey,
2078        accounts: Vec<AccountMeta>,
2079        data: Vec<u8>,
2080    }
2081
2082    impl MockInstruction {
2083        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2084            let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
2085
2086            let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
2087
2088            let mut data = vec![0; size];
2089
2090            let vm_addr = vm_addr as usize;
2091            let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
2092            let data_addr = accounts_addr + accounts_len;
2093
2094            let ins = Instruction {
2095                program_id: self.program_id,
2096                accounts: unsafe {
2097                    Vec::from_raw_parts(
2098                        accounts_addr as *mut _,
2099                        self.accounts.len(),
2100                        self.accounts.len(),
2101                    )
2102                },
2103                data: unsafe {
2104                    Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
2105                },
2106            };
2107            let ins = StableInstruction::from(ins);
2108
2109            unsafe {
2110                ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
2111                data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
2112                    slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
2113                );
2114                data[data_addr - vm_addr..].copy_from_slice(&self.data);
2115            }
2116
2117            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2118            (data, region)
2119        }
2120    }
2121
2122    fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2123        let vm_addr = vm_addr as usize;
2124
2125        // calculate size
2126        let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); // pointer size + length size
2127        let singers_length = signers.len();
2128        let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
2129
2130        // init data vec
2131        let total_size = fat_ptr_size_of_slice
2132            + singers_length * fat_ptr_size_of_slice
2133            + sum_signers_data_length;
2134        let mut data = vec![0; total_size];
2135
2136        // data is composed by 3 parts
2137        // A.
2138        // [ singers address, singers length, ...,
2139        // B.                                      |
2140        //                                         signer1 address, signer1 length, signer2 address ...,
2141        //                                         ^ p1 --->
2142        // C.                                                                                           |
2143        //                                                                                              signer1 data, signer2 data, ... ]
2144        //                                                                                              ^ p2 --->
2145
2146        // A.
2147        data[..fat_ptr_size_of_slice / 2]
2148            .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
2149        data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
2150            .clone_from_slice(&(singers_length).to_le_bytes());
2151
2152        // B. + C.
2153        let (mut p1, mut p2) = (
2154            fat_ptr_size_of_slice,
2155            fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
2156        );
2157        for signer in signers.iter() {
2158            let signer_length = signer.len();
2159
2160            // B.
2161            data[p1..p1 + fat_ptr_size_of_slice / 2]
2162                .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
2163            data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
2164                .clone_from_slice(&(signer_length).to_le_bytes());
2165            p1 += fat_ptr_size_of_slice;
2166
2167            // C.
2168            data[p2..p2 + signer_length].clone_from_slice(signer);
2169            p2 += signer_length;
2170        }
2171
2172        let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2173        (data, region)
2174    }
2175
2176    struct MockAccountInfo<'a> {
2177        key: Pubkey,
2178        is_signer: bool,
2179        is_writable: bool,
2180        lamports: u64,
2181        data: &'a [u8],
2182        owner: Pubkey,
2183        executable: bool,
2184        _unused: u64,
2185    }
2186
2187    impl MockAccountInfo<'_> {
2188        fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
2189            MockAccountInfo {
2190                key,
2191                is_signer: false,
2192                is_writable: false,
2193                lamports: account.lamports(),
2194                data: account.data(),
2195                owner: *account.owner(),
2196                executable: account.executable(),
2197                _unused: account.rent_epoch(),
2198            }
2199        }
2200
2201        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
2202            let size = mem::size_of::<AccountInfo>()
2203                + mem::size_of::<Pubkey>() * 2
2204                + mem::size_of::<RcBox<RefCell<&mut u64>>>()
2205                + mem::size_of::<u64>()
2206                + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
2207                + self.data.len();
2208            let mut data = vec![0; size];
2209
2210            let vm_addr = vm_addr as usize;
2211            let key_addr = vm_addr + mem::size_of::<AccountInfo>();
2212            let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
2213            let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
2214            let owner_addr = lamports_addr + mem::size_of::<u64>();
2215            let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
2216            let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
2217
2218            #[allow(deprecated)]
2219            #[allow(clippy::used_underscore_binding)]
2220            let info = AccountInfo {
2221                key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
2222                is_signer: self.is_signer,
2223                is_writable: self.is_writable,
2224                lamports: unsafe {
2225                    Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
2226                },
2227                data: unsafe {
2228                    Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
2229                },
2230                owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
2231                executable: self.executable,
2232                _unused: self._unused,
2233            };
2234
2235            unsafe {
2236                ptr::write_unaligned(data.as_mut_ptr().cast(), info);
2237                ptr::write_unaligned(
2238                    (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
2239                    self.key,
2240                );
2241                ptr::write_unaligned(
2242                    (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
2243                    RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
2244                );
2245                ptr::write_unaligned(
2246                    (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
2247                    self.lamports,
2248                );
2249                ptr::write_unaligned(
2250                    (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
2251                    self.owner,
2252                );
2253                ptr::write_unaligned(
2254                    (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
2255                    RcBox::new(RefCell::new(slice::from_raw_parts_mut(
2256                        data_addr as *mut u8,
2257                        self.data.len(),
2258                    ))),
2259                );
2260                data[data_addr - vm_addr..].copy_from_slice(self.data);
2261            }
2262
2263            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2264            (
2265                data,
2266                region,
2267                SerializedAccountMetadata {
2268                    original_data_len: self.data.len(),
2269                    vm_key_addr: key_addr as u64,
2270                    vm_lamports_addr: lamports_addr as u64,
2271                    vm_owner_addr: owner_addr as u64,
2272                    vm_data_addr: data_addr as u64,
2273                },
2274            )
2275        }
2276    }
2277
2278    #[repr(C)]
2279    struct RcBox<T> {
2280        strong: Cell<usize>,
2281        weak: Cell<usize>,
2282        value: T,
2283    }
2284
2285    impl<T> RcBox<T> {
2286        const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
2287        fn new(value: T) -> RcBox<T> {
2288            RcBox {
2289                strong: Cell::new(0),
2290                weak: Cell::new(0),
2291                value,
2292            }
2293        }
2294    }
2295
2296    fn is_zeroed(data: &[u8]) -> bool {
2297        data.iter().all(|b| *b == 0)
2298    }
2299}