solana_bpf_loader_program/syscalls/
cpi.rs

1use {
2    super::*,
3    crate::declare_syscall,
4    solana_sdk::{
5        feature_set::enable_bpf_loader_set_authority_checked_ix,
6        stable_layout::stable_instruction::StableInstruction,
7        syscalls::{
8            MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
9        },
10        transaction_context::BorrowedAccount,
11    },
12    std::mem,
13};
14
15/// Host side representation of AccountInfo or SolAccountInfo passed to the CPI syscall.
16///
17/// At the start of a CPI, this can be different from the data stored in the
18/// corresponding BorrowedAccount, and needs to be synched.
19struct CallerAccount<'a> {
20    lamports: &'a mut u64,
21    owner: &'a mut Pubkey,
22    original_data_len: usize,
23    data: &'a mut [u8],
24    // Given the corresponding input AccountInfo::data, vm_data_addr points to
25    // the pointer field and ref_to_len_in_vm points to the length field.
26    vm_data_addr: u64,
27    ref_to_len_in_vm: &'a mut u64,
28    // To be removed once `feature_set::move_serialized_len_ptr_in_cpi` is active everywhere
29    serialized_len_ptr: *mut u64,
30    executable: bool,
31    rent_epoch: u64,
32}
33
34impl<'a> CallerAccount<'a> {
35    // Create a CallerAccount given an AccountInfo.
36    fn from_account_info(
37        invoke_context: &InvokeContext,
38        memory_mapping: &MemoryMapping,
39        _vm_addr: u64,
40        account_info: &AccountInfo,
41        original_data_len: usize,
42    ) -> Result<CallerAccount<'a>, EbpfError> {
43        // account_info points to host memory. The addresses used internally are
44        // in vm space so they need to be translated.
45
46        let lamports = {
47            // Double translate lamports out of RefCell
48            let ptr = translate_type::<u64>(
49                memory_mapping,
50                account_info.lamports.as_ptr() as u64,
51                invoke_context.get_check_aligned(),
52            )?;
53            translate_type_mut::<u64>(memory_mapping, *ptr, invoke_context.get_check_aligned())?
54        };
55        let owner = translate_type_mut::<Pubkey>(
56            memory_mapping,
57            account_info.owner as *const _ as u64,
58            invoke_context.get_check_aligned(),
59        )?;
60
61        let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = {
62            // Double translate data out of RefCell
63            let data = *translate_type::<&[u8]>(
64                memory_mapping,
65                account_info.data.as_ptr() as *const _ as u64,
66                invoke_context.get_check_aligned(),
67            )?;
68
69            consume_compute_meter(
70                invoke_context,
71                (data.len() as u64)
72                    .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
73            )?;
74
75            let translated = translate(
76                memory_mapping,
77                AccessType::Store,
78                (account_info.data.as_ptr() as *const u64 as u64)
79                    .saturating_add(size_of::<u64>() as u64),
80                8,
81            )? as *mut u64;
82            let ref_to_len_in_vm = unsafe { &mut *translated };
83            let serialized_len_ptr = if invoke_context
84                .feature_set
85                .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
86            {
87                std::ptr::null_mut()
88            } else {
89                let ref_of_len_in_input_buffer =
90                    (data.as_ptr() as *const _ as u64).saturating_sub(8);
91                translate_type_mut::<u64>(
92                    memory_mapping,
93                    ref_of_len_in_input_buffer,
94                    invoke_context.get_check_aligned(),
95                )?
96            };
97            let vm_data_addr = data.as_ptr() as u64;
98            (
99                translate_slice_mut::<u8>(
100                    memory_mapping,
101                    vm_data_addr,
102                    data.len() as u64,
103                    invoke_context.get_check_aligned(),
104                    invoke_context.get_check_size(),
105                )?,
106                vm_data_addr,
107                ref_to_len_in_vm,
108                serialized_len_ptr,
109            )
110        };
111
112        Ok(CallerAccount {
113            lamports,
114            owner,
115            original_data_len,
116            data,
117            vm_data_addr,
118            ref_to_len_in_vm,
119            serialized_len_ptr,
120            executable: account_info.executable,
121            rent_epoch: account_info.rent_epoch,
122        })
123    }
124
125    // Create a CallerAccount given a SolAccountInfo.
126    fn from_sol_account_info(
127        invoke_context: &InvokeContext,
128        memory_mapping: &MemoryMapping,
129        vm_addr: u64,
130        account_info: &SolAccountInfo,
131        original_data_len: usize,
132    ) -> Result<CallerAccount<'a>, EbpfError> {
133        // account_info points to host memory. The addresses used internally are
134        // in vm space so they need to be translated.
135
136        let lamports = translate_type_mut::<u64>(
137            memory_mapping,
138            account_info.lamports_addr,
139            invoke_context.get_check_aligned(),
140        )?;
141        let owner = translate_type_mut::<Pubkey>(
142            memory_mapping,
143            account_info.owner_addr,
144            invoke_context.get_check_aligned(),
145        )?;
146        let vm_data_addr = account_info.data_addr;
147
148        consume_compute_meter(
149            invoke_context,
150            account_info
151                .data_len
152                .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
153        )?;
154
155        let data = translate_slice_mut::<u8>(
156            memory_mapping,
157            vm_data_addr,
158            account_info.data_len,
159            invoke_context.get_check_aligned(),
160            invoke_context.get_check_size(),
161        )?;
162
163        // we already have the host addr we want: &mut account_info.data_len.
164        // The account info might be read only in the vm though, so we translate
165        // to ensure we can write. This is tested by programs/sbf/rust/ro_modify
166        // which puts SolAccountInfo in rodata.
167        let data_len_vm_addr = vm_addr
168            .saturating_add(&account_info.data_len as *const u64 as u64)
169            .saturating_sub(account_info as *const _ as *const u64 as u64);
170        let data_len_addr = translate(
171            memory_mapping,
172            AccessType::Store,
173            data_len_vm_addr,
174            size_of::<u64>() as u64,
175        )?;
176        let ref_to_len_in_vm = unsafe { &mut *(data_len_addr as *mut u64) };
177
178        let ref_of_len_in_input_buffer =
179            (account_info.data_addr as *mut u8 as u64).saturating_sub(8);
180        let serialized_len_ptr = if invoke_context
181            .feature_set
182            .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
183        {
184            std::ptr::null_mut()
185        } else {
186            translate_type_mut::<u64>(
187                memory_mapping,
188                ref_of_len_in_input_buffer,
189                invoke_context.get_check_aligned(),
190            )?
191        };
192
193        Ok(CallerAccount {
194            lamports,
195            owner,
196            original_data_len,
197            data,
198            vm_data_addr,
199            ref_to_len_in_vm,
200            serialized_len_ptr,
201            executable: account_info.executable,
202            rent_epoch: account_info.rent_epoch,
203        })
204    }
205}
206
207type TranslatedAccounts<'a> = Vec<(IndexOfAccount, Option<CallerAccount<'a>>)>;
208
209/// Implemented by language specific data structure translators
210trait SyscallInvokeSigned {
211    fn translate_instruction(
212        addr: u64,
213        memory_mapping: &mut MemoryMapping,
214        invoke_context: &mut InvokeContext,
215    ) -> Result<StableInstruction, EbpfError>;
216    fn translate_accounts<'a>(
217        instruction_accounts: &[InstructionAccount],
218        program_indices: &[IndexOfAccount],
219        account_infos_addr: u64,
220        account_infos_len: u64,
221        memory_mapping: &mut MemoryMapping,
222        invoke_context: &mut InvokeContext,
223    ) -> Result<TranslatedAccounts<'a>, EbpfError>;
224    fn translate_signers(
225        program_id: &Pubkey,
226        signers_seeds_addr: u64,
227        signers_seeds_len: u64,
228        memory_mapping: &mut MemoryMapping,
229        invoke_context: &InvokeContext,
230    ) -> Result<Vec<Pubkey>, EbpfError>;
231}
232
233declare_syscall!(
234    /// Cross-program invocation called from Rust
235    SyscallInvokeSignedRust,
236    fn inner_call(
237        invoke_context: &mut InvokeContext,
238        instruction_addr: u64,
239        account_infos_addr: u64,
240        account_infos_len: u64,
241        signers_seeds_addr: u64,
242        signers_seeds_len: u64,
243        memory_mapping: &mut MemoryMapping,
244    ) -> Result<u64, EbpfError> {
245        cpi_common::<Self>(
246            invoke_context,
247            instruction_addr,
248            account_infos_addr,
249            account_infos_len,
250            signers_seeds_addr,
251            signers_seeds_len,
252            memory_mapping,
253        )
254    }
255);
256
257impl SyscallInvokeSigned for SyscallInvokeSignedRust {
258    fn translate_instruction(
259        addr: u64,
260        memory_mapping: &mut MemoryMapping,
261        invoke_context: &mut InvokeContext,
262    ) -> Result<StableInstruction, EbpfError> {
263        let ix = translate_type::<StableInstruction>(
264            memory_mapping,
265            addr,
266            invoke_context.get_check_aligned(),
267        )?;
268
269        check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?;
270
271        let accounts = translate_slice::<AccountMeta>(
272            memory_mapping,
273            ix.accounts.as_ptr() as u64,
274            ix.accounts.len() as u64,
275            invoke_context.get_check_aligned(),
276            invoke_context.get_check_size(),
277        )?
278        .to_vec();
279
280        let ix_data_len = ix.data.len() as u64;
281        if invoke_context
282            .feature_set
283            .is_active(&feature_set::loosen_cpi_size_restriction::id())
284        {
285            consume_compute_meter(
286                invoke_context,
287                (ix_data_len)
288                    .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
289            )?;
290        }
291
292        let data = translate_slice::<u8>(
293            memory_mapping,
294            ix.data.as_ptr() as u64,
295            ix_data_len,
296            invoke_context.get_check_aligned(),
297            invoke_context.get_check_size(),
298        )?
299        .to_vec();
300
301        Ok(StableInstruction {
302            accounts: accounts.into(),
303            data: data.into(),
304            program_id: ix.program_id,
305        })
306    }
307
308    fn translate_accounts<'a>(
309        instruction_accounts: &[InstructionAccount],
310        program_indices: &[IndexOfAccount],
311        account_infos_addr: u64,
312        account_infos_len: u64,
313        memory_mapping: &mut MemoryMapping,
314        invoke_context: &mut InvokeContext,
315    ) -> Result<TranslatedAccounts<'a>, EbpfError> {
316        let (account_infos, account_info_keys) = translate_account_infos(
317            account_infos_addr,
318            account_infos_len,
319            |account_info: &AccountInfo| account_info.key as *const _ as u64,
320            memory_mapping,
321            invoke_context,
322        )?;
323
324        translate_and_update_accounts(
325            instruction_accounts,
326            program_indices,
327            &account_info_keys,
328            account_infos,
329            account_infos_addr,
330            invoke_context,
331            memory_mapping,
332            CallerAccount::from_account_info,
333        )
334    }
335
336    fn translate_signers(
337        program_id: &Pubkey,
338        signers_seeds_addr: u64,
339        signers_seeds_len: u64,
340        memory_mapping: &mut MemoryMapping,
341        invoke_context: &InvokeContext,
342    ) -> Result<Vec<Pubkey>, EbpfError> {
343        let mut signers = Vec::new();
344        if signers_seeds_len > 0 {
345            let signers_seeds = translate_slice::<&[&[u8]]>(
346                memory_mapping,
347                signers_seeds_addr,
348                signers_seeds_len,
349                invoke_context.get_check_aligned(),
350                invoke_context.get_check_size(),
351            )?;
352            if signers_seeds.len() > MAX_SIGNERS {
353                return Err(SyscallError::TooManySigners.into());
354            }
355            for signer_seeds in signers_seeds.iter() {
356                let untranslated_seeds = translate_slice::<&[u8]>(
357                    memory_mapping,
358                    signer_seeds.as_ptr() as *const _ as u64,
359                    signer_seeds.len() as u64,
360                    invoke_context.get_check_aligned(),
361                    invoke_context.get_check_size(),
362                )?;
363                if untranslated_seeds.len() > MAX_SEEDS {
364                    return Err(SyscallError::InstructionError(
365                        InstructionError::MaxSeedLengthExceeded,
366                    )
367                    .into());
368                }
369                let seeds = untranslated_seeds
370                    .iter()
371                    .map(|untranslated_seed| {
372                        translate_slice::<u8>(
373                            memory_mapping,
374                            untranslated_seed.as_ptr() as *const _ as u64,
375                            untranslated_seed.len() as u64,
376                            invoke_context.get_check_aligned(),
377                            invoke_context.get_check_size(),
378                        )
379                    })
380                    .collect::<Result<Vec<_>, EbpfError>>()?;
381                let signer = Pubkey::create_program_address(&seeds, program_id)
382                    .map_err(SyscallError::BadSeeds)?;
383                signers.push(signer);
384            }
385            Ok(signers)
386        } else {
387            Ok(vec![])
388        }
389    }
390}
391
392/// Rust representation of C's SolInstruction
393#[derive(Debug)]
394#[repr(C)]
395struct SolInstruction {
396    program_id_addr: u64,
397    accounts_addr: u64,
398    accounts_len: u64,
399    data_addr: u64,
400    data_len: u64,
401}
402
403/// Rust representation of C's SolAccountMeta
404#[derive(Debug)]
405#[repr(C)]
406struct SolAccountMeta {
407    pubkey_addr: u64,
408    is_writable: bool,
409    is_signer: bool,
410}
411
412/// Rust representation of C's SolAccountInfo
413#[derive(Debug)]
414#[repr(C)]
415struct SolAccountInfo {
416    key_addr: u64,
417    lamports_addr: u64,
418    data_len: u64,
419    data_addr: u64,
420    owner_addr: u64,
421    rent_epoch: u64,
422    #[allow(dead_code)]
423    is_signer: bool,
424    #[allow(dead_code)]
425    is_writable: bool,
426    executable: bool,
427}
428
429/// Rust representation of C's SolSignerSeed
430#[derive(Debug)]
431#[repr(C)]
432struct SolSignerSeedC {
433    addr: u64,
434    len: u64,
435}
436
437/// Rust representation of C's SolSignerSeeds
438#[derive(Debug)]
439#[repr(C)]
440struct SolSignerSeedsC {
441    addr: u64,
442    len: u64,
443}
444
445declare_syscall!(
446    /// Cross-program invocation called from C
447    SyscallInvokeSignedC,
448    fn inner_call(
449        invoke_context: &mut InvokeContext,
450        instruction_addr: u64,
451        account_infos_addr: u64,
452        account_infos_len: u64,
453        signers_seeds_addr: u64,
454        signers_seeds_len: u64,
455        memory_mapping: &mut MemoryMapping,
456    ) -> Result<u64, EbpfError> {
457        cpi_common::<Self>(
458            invoke_context,
459            instruction_addr,
460            account_infos_addr,
461            account_infos_len,
462            signers_seeds_addr,
463            signers_seeds_len,
464            memory_mapping,
465        )
466    }
467);
468
469impl SyscallInvokeSigned for SyscallInvokeSignedC {
470    fn translate_instruction(
471        addr: u64,
472        memory_mapping: &mut MemoryMapping,
473        invoke_context: &mut InvokeContext,
474    ) -> Result<StableInstruction, EbpfError> {
475        let ix_c = translate_type::<SolInstruction>(
476            memory_mapping,
477            addr,
478            invoke_context.get_check_aligned(),
479        )?;
480
481        check_instruction_size(
482            ix_c.accounts_len as usize,
483            ix_c.data_len as usize,
484            invoke_context,
485        )?;
486        let program_id = translate_type::<Pubkey>(
487            memory_mapping,
488            ix_c.program_id_addr,
489            invoke_context.get_check_aligned(),
490        )?;
491        let meta_cs = translate_slice::<SolAccountMeta>(
492            memory_mapping,
493            ix_c.accounts_addr,
494            ix_c.accounts_len,
495            invoke_context.get_check_aligned(),
496            invoke_context.get_check_size(),
497        )?;
498
499        let ix_data_len = ix_c.data_len;
500        if invoke_context
501            .feature_set
502            .is_active(&feature_set::loosen_cpi_size_restriction::id())
503        {
504            consume_compute_meter(
505                invoke_context,
506                (ix_data_len)
507                    .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
508            )?;
509        }
510
511        let data = translate_slice::<u8>(
512            memory_mapping,
513            ix_c.data_addr,
514            ix_data_len,
515            invoke_context.get_check_aligned(),
516            invoke_context.get_check_size(),
517        )?
518        .to_vec();
519        let accounts = meta_cs
520            .iter()
521            .map(|meta_c| {
522                let pubkey = translate_type::<Pubkey>(
523                    memory_mapping,
524                    meta_c.pubkey_addr,
525                    invoke_context.get_check_aligned(),
526                )?;
527                Ok(AccountMeta {
528                    pubkey: *pubkey,
529                    is_signer: meta_c.is_signer,
530                    is_writable: meta_c.is_writable,
531                })
532            })
533            .collect::<Result<Vec<AccountMeta>, EbpfError>>()?;
534
535        Ok(StableInstruction {
536            accounts: accounts.into(),
537            data: data.into(),
538            program_id: *program_id,
539        })
540    }
541
542    fn translate_accounts<'a>(
543        instruction_accounts: &[InstructionAccount],
544        program_indices: &[IndexOfAccount],
545        account_infos_addr: u64,
546        account_infos_len: u64,
547        memory_mapping: &mut MemoryMapping,
548        invoke_context: &mut InvokeContext,
549    ) -> Result<TranslatedAccounts<'a>, EbpfError> {
550        let (account_infos, account_info_keys) = translate_account_infos(
551            account_infos_addr,
552            account_infos_len,
553            |account_info: &SolAccountInfo| account_info.key_addr,
554            memory_mapping,
555            invoke_context,
556        )?;
557
558        translate_and_update_accounts(
559            instruction_accounts,
560            program_indices,
561            &account_info_keys,
562            account_infos,
563            account_infos_addr,
564            invoke_context,
565            memory_mapping,
566            CallerAccount::from_sol_account_info,
567        )
568    }
569
570    fn translate_signers(
571        program_id: &Pubkey,
572        signers_seeds_addr: u64,
573        signers_seeds_len: u64,
574        memory_mapping: &mut MemoryMapping,
575        invoke_context: &InvokeContext,
576    ) -> Result<Vec<Pubkey>, EbpfError> {
577        if signers_seeds_len > 0 {
578            let signers_seeds = translate_slice::<SolSignerSeedsC>(
579                memory_mapping,
580                signers_seeds_addr,
581                signers_seeds_len,
582                invoke_context.get_check_aligned(),
583                invoke_context.get_check_size(),
584            )?;
585            if signers_seeds.len() > MAX_SIGNERS {
586                return Err(SyscallError::TooManySigners.into());
587            }
588            Ok(signers_seeds
589                .iter()
590                .map(|signer_seeds| {
591                    let seeds = translate_slice::<SolSignerSeedC>(
592                        memory_mapping,
593                        signer_seeds.addr,
594                        signer_seeds.len,
595                        invoke_context.get_check_aligned(),
596                        invoke_context.get_check_size(),
597                    )?;
598                    if seeds.len() > MAX_SEEDS {
599                        return Err(SyscallError::InstructionError(
600                            InstructionError::MaxSeedLengthExceeded,
601                        )
602                        .into());
603                    }
604                    let seeds_bytes = seeds
605                        .iter()
606                        .map(|seed| {
607                            translate_slice::<u8>(
608                                memory_mapping,
609                                seed.addr,
610                                seed.len,
611                                invoke_context.get_check_aligned(),
612                                invoke_context.get_check_size(),
613                            )
614                        })
615                        .collect::<Result<Vec<_>, EbpfError>>()?;
616                    Pubkey::create_program_address(&seeds_bytes, program_id)
617                        .map_err(|err| SyscallError::BadSeeds(err).into())
618                })
619                .collect::<Result<Vec<_>, EbpfError>>()?)
620        } else {
621            Ok(vec![])
622        }
623    }
624}
625
626fn translate_account_infos<'a, T, F>(
627    account_infos_addr: u64,
628    account_infos_len: u64,
629    key_addr: F,
630    memory_mapping: &mut MemoryMapping,
631    invoke_context: &mut InvokeContext,
632) -> Result<(&'a [T], Vec<&'a Pubkey>), EbpfError>
633where
634    F: Fn(&T) -> u64,
635{
636    let account_infos = translate_slice::<T>(
637        memory_mapping,
638        account_infos_addr,
639        account_infos_len,
640        invoke_context.get_check_aligned(),
641        invoke_context.get_check_size(),
642    )?;
643    check_account_infos(account_infos.len(), invoke_context)?;
644    let account_info_keys = account_infos
645        .iter()
646        .map(|account_info| {
647            translate_type::<Pubkey>(
648                memory_mapping,
649                key_addr(account_info),
650                invoke_context.get_check_aligned(),
651            )
652        })
653        .collect::<Result<Vec<_>, EbpfError>>()?;
654
655    Ok((account_infos, account_info_keys))
656}
657
658// Finish translating accounts, build CallerAccount values and update callee
659// accounts in preparation of executing the callee.
660fn translate_and_update_accounts<'a, T, F>(
661    instruction_accounts: &[InstructionAccount],
662    program_indices: &[IndexOfAccount],
663    account_info_keys: &[&Pubkey],
664    account_infos: &[T],
665    account_infos_addr: u64,
666    invoke_context: &mut InvokeContext,
667    memory_mapping: &MemoryMapping,
668    do_translate: F,
669) -> Result<TranslatedAccounts<'a>, EbpfError>
670where
671    F: Fn(&InvokeContext, &MemoryMapping, u64, &T, usize) -> Result<CallerAccount<'a>, EbpfError>,
672{
673    let transaction_context = &invoke_context.transaction_context;
674    let instruction_context = transaction_context
675        .get_current_instruction_context()
676        .map_err(SyscallError::InstructionError)?;
677    let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1));
678
679    let program_account_index = program_indices
680        .last()
681        .ok_or(SyscallError::InstructionError(
682            InstructionError::MissingAccount,
683        ))?;
684    accounts.push((*program_account_index, None));
685
686    // unwrapping here is fine: we're in a syscall and the method below fails
687    // only outside syscalls
688    let orig_data_lens = invoke_context.get_orig_account_lengths().unwrap();
689
690    for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate()
691    {
692        if instruction_account_index as IndexOfAccount != instruction_account.index_in_callee {
693            continue; // Skip duplicate account
694        }
695
696        let callee_account = instruction_context
697            .try_borrow_instruction_account(
698                transaction_context,
699                instruction_account.index_in_caller,
700            )
701            .map_err(SyscallError::InstructionError)?;
702        let account_key = invoke_context
703            .transaction_context
704            .get_key_of_account_at_index(instruction_account.index_in_transaction)
705            .map_err(SyscallError::InstructionError)?;
706
707        if callee_account.is_executable() {
708            // Use the known account
709            consume_compute_meter(
710                invoke_context,
711                (callee_account.get_data().len() as u64)
712                    .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
713            )?;
714
715            accounts.push((instruction_account.index_in_caller, None));
716        } else if let Some(caller_account_index) =
717            account_info_keys.iter().position(|key| *key == account_key)
718        {
719            let original_data_len = *orig_data_lens
720                .get(instruction_account.index_in_caller as usize)
721                .ok_or_else(|| {
722                    ic_msg!(
723                        invoke_context,
724                        "Internal error: index mismatch for account {}",
725                        account_key
726                    );
727                    SyscallError::InstructionError(InstructionError::MissingAccount)
728                })?;
729
730            // build the CallerAccount corresponding to this account.
731            let caller_account =
732                do_translate(
733                    invoke_context,
734                    memory_mapping,
735                    account_infos_addr.saturating_add(
736                        caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
737                    ),
738                    account_infos
739                        .get(caller_account_index)
740                        .ok_or(SyscallError::InvalidLength)?,
741                    original_data_len,
742                )?;
743
744            // before initiating CPI, the caller may have modified the
745            // account (caller_account). We need to update the corresponding
746            // BorrowedAccount (callee_account) so the callee can see the
747            // changes.
748            update_callee_account(invoke_context, &caller_account, callee_account)?;
749
750            let caller_account = if instruction_account.is_writable {
751                Some(caller_account)
752            } else {
753                None
754            };
755            accounts.push((instruction_account.index_in_caller, caller_account));
756        } else {
757            ic_msg!(
758                invoke_context,
759                "Instruction references an unknown account {}",
760                account_key
761            );
762            return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
763        }
764    }
765
766    Ok(accounts)
767}
768
769fn check_instruction_size(
770    num_accounts: usize,
771    data_len: usize,
772    invoke_context: &mut InvokeContext,
773) -> Result<(), EbpfError> {
774    if invoke_context
775        .feature_set
776        .is_active(&feature_set::loosen_cpi_size_restriction::id())
777    {
778        let data_len = data_len as u64;
779        let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN;
780        if data_len > max_data_len {
781            return Err(SyscallError::MaxInstructionDataLenExceeded {
782                data_len,
783                max_data_len,
784            }
785            .into());
786        }
787
788        let num_accounts = num_accounts as u64;
789        let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64;
790        if num_accounts > max_accounts {
791            return Err(SyscallError::MaxInstructionAccountsExceeded {
792                num_accounts,
793                max_accounts,
794            }
795            .into());
796        }
797    } else {
798        let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size;
799        let size = num_accounts
800            .saturating_mul(size_of::<AccountMeta>())
801            .saturating_add(data_len);
802        if size > max_size {
803            return Err(SyscallError::InstructionTooLarge(size, max_size).into());
804        }
805    }
806    Ok(())
807}
808
809fn check_account_infos(
810    num_account_infos: usize,
811    invoke_context: &mut InvokeContext,
812) -> Result<(), EbpfError> {
813    if invoke_context
814        .feature_set
815        .is_active(&feature_set::loosen_cpi_size_restriction::id())
816    {
817        let max_cpi_account_infos = if invoke_context
818            .feature_set
819            .is_active(&feature_set::increase_tx_account_lock_limit::id())
820        {
821            MAX_CPI_ACCOUNT_INFOS
822        } else {
823            64
824        };
825        let num_account_infos = num_account_infos as u64;
826        let max_account_infos = max_cpi_account_infos as u64;
827        if num_account_infos > max_account_infos {
828            return Err(SyscallError::MaxInstructionAccountInfosExceeded {
829                num_account_infos,
830                max_account_infos,
831            }
832            .into());
833        }
834    } else {
835        let adjusted_len = num_account_infos.saturating_mul(size_of::<Pubkey>());
836
837        if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size {
838            // Cap the number of account_infos a caller can pass to approximate
839            // maximum that accounts that could be passed in an instruction
840            return Err(SyscallError::TooManyAccounts.into());
841        };
842    }
843    Ok(())
844}
845
846fn check_authorized_program(
847    program_id: &Pubkey,
848    instruction_data: &[u8],
849    invoke_context: &InvokeContext,
850) -> Result<(), EbpfError> {
851    if native_loader::check_id(program_id)
852        || bpf_loader::check_id(program_id)
853        || bpf_loader_deprecated::check_id(program_id)
854        || (bpf_loader_upgradeable::check_id(program_id)
855            && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
856                || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
857                || (invoke_context
858                    .feature_set
859                    .is_active(&enable_bpf_loader_set_authority_checked_ix::id())
860                    && bpf_loader_upgradeable::is_set_authority_checked_instruction(
861                        instruction_data,
862                    ))
863                || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
864        || is_precompile(program_id, |feature_id: &Pubkey| {
865            invoke_context.feature_set.is_active(feature_id)
866        })
867    {
868        return Err(SyscallError::ProgramNotSupported(*program_id).into());
869    }
870    Ok(())
871}
872
873/// Call process instruction, common to both Rust and C
874fn cpi_common<S: SyscallInvokeSigned>(
875    invoke_context: &mut InvokeContext,
876    instruction_addr: u64,
877    account_infos_addr: u64,
878    account_infos_len: u64,
879    signers_seeds_addr: u64,
880    signers_seeds_len: u64,
881    memory_mapping: &mut MemoryMapping,
882) -> Result<u64, EbpfError> {
883    // CPI entry.
884    //
885    // Translate the inputs to the syscall and synchronize the caller's account
886    // changes so the callee can see them.
887    consume_compute_meter(
888        invoke_context,
889        invoke_context.get_compute_budget().invoke_units,
890    )?;
891
892    let instruction = S::translate_instruction(instruction_addr, memory_mapping, invoke_context)?;
893    let transaction_context = &invoke_context.transaction_context;
894    let instruction_context = transaction_context
895        .get_current_instruction_context()
896        .map_err(SyscallError::InstructionError)?;
897    let caller_program_id = instruction_context
898        .get_last_program_key(transaction_context)
899        .map_err(SyscallError::InstructionError)?;
900    let signers = S::translate_signers(
901        caller_program_id,
902        signers_seeds_addr,
903        signers_seeds_len,
904        memory_mapping,
905        invoke_context,
906    )?;
907    let (instruction_accounts, program_indices) = invoke_context
908        .prepare_instruction(&instruction, &signers)
909        .map_err(SyscallError::InstructionError)?;
910    check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
911    let mut accounts = S::translate_accounts(
912        &instruction_accounts,
913        &program_indices,
914        account_infos_addr,
915        account_infos_len,
916        memory_mapping,
917        invoke_context,
918    )?;
919
920    // Process the callee instruction
921    let mut compute_units_consumed = 0;
922    invoke_context
923        .process_instruction(
924            &instruction.data,
925            &instruction_accounts,
926            &program_indices,
927            &mut compute_units_consumed,
928            &mut ExecuteTimings::default(),
929        )
930        .map_err(SyscallError::InstructionError)?;
931
932    // re-bind to please the borrow checker
933    let transaction_context = &invoke_context.transaction_context;
934    let instruction_context = transaction_context
935        .get_current_instruction_context()
936        .map_err(SyscallError::InstructionError)?;
937
938    // CPI exit.
939    //
940    // Synchronize the callee's account changes so the caller can see them.
941    for (index_in_caller, caller_account) in accounts.iter_mut() {
942        if let Some(caller_account) = caller_account {
943            let callee_account = instruction_context
944                .try_borrow_instruction_account(transaction_context, *index_in_caller)
945                .map_err(SyscallError::InstructionError)?;
946            update_caller_account(
947                invoke_context,
948                memory_mapping,
949                caller_account,
950                &callee_account,
951            )?;
952        }
953    }
954
955    Ok(SUCCESS)
956}
957
958// Update the given account before executing CPI.
959//
960// caller_account and callee_account describe the same account. At CPI entry
961// caller_account might include changes the caller has made to the account
962// before executing CPI.
963//
964// This method updates callee_account so the CPI callee can see the caller's
965// changes.
966fn update_callee_account(
967    invoke_context: &InvokeContext,
968    caller_account: &CallerAccount,
969    mut callee_account: BorrowedAccount<'_>,
970) -> Result<(), EbpfError> {
971    let is_disable_cpi_setting_executable_and_rent_epoch_active = invoke_context
972        .feature_set
973        .is_active(&disable_cpi_setting_executable_and_rent_epoch::id());
974
975    if callee_account.get_lamports() != *caller_account.lamports {
976        callee_account
977            .set_lamports(*caller_account.lamports)
978            .map_err(SyscallError::InstructionError)?;
979    }
980
981    // The redundant check helps to avoid the expensive data comparison if we can
982    match callee_account
983        .can_data_be_resized(caller_account.data.len())
984        .and_then(|_| callee_account.can_data_be_changed())
985    {
986        Ok(()) => callee_account
987            .set_data_from_slice(caller_account.data)
988            .map_err(SyscallError::InstructionError)?,
989        Err(err) if callee_account.get_data() != caller_account.data => {
990            return Err(EbpfError::UserError(Box::new(BpfError::SyscallError(
991                SyscallError::InstructionError(err),
992            ))));
993        }
994        _ => {}
995    }
996
997    if !is_disable_cpi_setting_executable_and_rent_epoch_active
998        && callee_account.is_executable() != caller_account.executable
999    {
1000        callee_account
1001            .set_executable(caller_account.executable)
1002            .map_err(SyscallError::InstructionError)?;
1003    }
1004
1005    // Change the owner at the end so that we are allowed to change the lamports and data before
1006    if callee_account.get_owner() != caller_account.owner {
1007        callee_account
1008            .set_owner(caller_account.owner.as_ref())
1009            .map_err(SyscallError::InstructionError)?;
1010    }
1011
1012    // BorrowedAccount doesn't allow changing the rent epoch. Drop it and use
1013    // AccountSharedData directly.
1014    let index_in_transaction = callee_account.get_index_in_transaction();
1015    drop(callee_account);
1016    let callee_account = invoke_context
1017        .transaction_context
1018        .get_account_at_index(index_in_transaction)
1019        .map_err(SyscallError::InstructionError)?;
1020    if !is_disable_cpi_setting_executable_and_rent_epoch_active
1021        && callee_account.borrow().rent_epoch() != caller_account.rent_epoch
1022    {
1023        if invoke_context
1024            .feature_set
1025            .is_active(&enable_early_verification_of_account_modifications::id())
1026        {
1027            return Err(SyscallError::InstructionError(InstructionError::RentEpochModified).into());
1028        } else {
1029            callee_account
1030                .borrow_mut()
1031                .set_rent_epoch(caller_account.rent_epoch);
1032        }
1033    }
1034
1035    Ok(())
1036}
1037
1038// Update the given account after executing CPI.
1039//
1040// caller_account and callee_account describe to the same account. At CPI exit
1041// callee_account might include changes the callee has made to the account
1042// after executing.
1043//
1044// This method updates caller_account so the CPI caller can see the callee's
1045// changes.
1046fn update_caller_account(
1047    invoke_context: &InvokeContext,
1048    memory_mapping: &MemoryMapping,
1049    caller_account: &mut CallerAccount,
1050    callee_account: &BorrowedAccount<'_>,
1051) -> Result<(), EbpfError> {
1052    *caller_account.lamports = callee_account.get_lamports();
1053    *caller_account.owner = *callee_account.get_owner();
1054    let new_len = callee_account.get_data().len();
1055    if caller_account.data.len() != new_len {
1056        let data_overflow = new_len
1057            > caller_account
1058                .original_data_len
1059                .saturating_add(MAX_PERMITTED_DATA_INCREASE);
1060        if data_overflow {
1061            ic_msg!(
1062                invoke_context,
1063                "Account data size realloc limited to {} in inner instructions",
1064                MAX_PERMITTED_DATA_INCREASE
1065            );
1066            return Err(SyscallError::InstructionError(InstructionError::InvalidRealloc).into());
1067        }
1068        if new_len < caller_account.data.len() {
1069            caller_account
1070                .data
1071                .get_mut(new_len..)
1072                .ok_or(SyscallError::InstructionError(
1073                    InstructionError::AccountDataTooSmall,
1074                ))?
1075                .fill(0);
1076        }
1077        caller_account.data = translate_slice_mut::<u8>(
1078            memory_mapping,
1079            caller_account.vm_data_addr,
1080            new_len as u64,
1081            false, // Don't care since it is byte aligned
1082            invoke_context.get_check_size(),
1083        )?;
1084        // this is the len field in the AccountInfo::data slice
1085        *caller_account.ref_to_len_in_vm = new_len as u64;
1086
1087        // this is the len field in the serialized parameters
1088        if invoke_context
1089            .feature_set
1090            .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id())
1091        {
1092            let serialized_len_ptr = translate_type_mut::<u64>(
1093                memory_mapping,
1094                caller_account
1095                    .vm_data_addr
1096                    .saturating_sub(std::mem::size_of::<u64>() as u64),
1097                invoke_context.get_check_aligned(),
1098            )?;
1099            *serialized_len_ptr = new_len as u64;
1100        } else {
1101            unsafe {
1102                *caller_account.serialized_len_ptr = new_len as u64;
1103            }
1104        }
1105    }
1106    let to_slice = &mut caller_account.data;
1107    let from_slice = callee_account
1108        .get_data()
1109        .get(0..new_len)
1110        .ok_or(SyscallError::InvalidLength)?;
1111    if to_slice.len() != from_slice.len() {
1112        return Err(SyscallError::InstructionError(InstructionError::AccountDataTooSmall).into());
1113    }
1114    to_slice.copy_from_slice(from_slice);
1115
1116    Ok(())
1117}
1118
1119#[allow(clippy::indexing_slicing)]
1120#[allow(clippy::integer_arithmetic)]
1121#[cfg(test)]
1122mod tests {
1123    use {
1124        super::*,
1125        crate::allocator_bump::BpfAllocator,
1126        solana_rbpf::{
1127            aligned_memory::AlignedMemory, ebpf::MM_INPUT_START, memory_region::MemoryRegion,
1128            vm::Config,
1129        },
1130        solana_sdk::{
1131            account::{Account, AccountSharedData},
1132            clock::Epoch,
1133            instruction::Instruction,
1134            rent::Rent,
1135            transaction_context::{TransactionAccount, TransactionContext},
1136        },
1137        std::{
1138            cell::{Cell, RefCell},
1139            mem, ptr,
1140            rc::Rc,
1141            slice,
1142        },
1143    };
1144
1145    macro_rules! mock_invoke_context {
1146        ($invoke_context:ident,
1147         $transaction_context:ident,
1148         $instruction_data:expr,
1149         $transaction_accounts:expr,
1150         $program_accounts:expr,
1151         $instruction_accounts:expr) => {
1152            let program_accounts = $program_accounts;
1153            let instruction_data = $instruction_data;
1154            let instruction_accounts = $instruction_accounts
1155                .iter()
1156                .enumerate()
1157                .map(
1158                    |(index_in_callee, index_in_transaction)| InstructionAccount {
1159                        index_in_transaction: *index_in_transaction as IndexOfAccount,
1160                        index_in_caller: *index_in_transaction as IndexOfAccount,
1161                        index_in_callee: index_in_callee as IndexOfAccount,
1162                        is_signer: false,
1163                        is_writable: $transaction_accounts[*index_in_transaction as usize].2,
1164                    },
1165                )
1166                .collect::<Vec<_>>();
1167            let transaction_accounts = $transaction_accounts
1168                .into_iter()
1169                .map(|a| (a.0, a.1))
1170                .collect::<Vec<TransactionAccount>>();
1171
1172            let program_accounts = program_accounts;
1173            let mut $transaction_context =
1174                TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
1175
1176            let mut $invoke_context = InvokeContext::new_mock(&mut $transaction_context, &[]);
1177            $invoke_context
1178                .transaction_context
1179                .get_next_instruction_context()
1180                .unwrap()
1181                .configure(program_accounts, &instruction_accounts, instruction_data);
1182            $invoke_context.push().unwrap();
1183        };
1184    }
1185
1186    #[test]
1187    fn test_translate_instruction() {
1188        let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1189        mock_invoke_context!(
1190            invoke_context,
1191            transaction_context,
1192            b"instruction data",
1193            transaction_accounts,
1194            &[0],
1195            &[1]
1196        );
1197
1198        let program_id = Pubkey::new_unique();
1199        let accounts = vec![AccountMeta {
1200            pubkey: Pubkey::new_unique(),
1201            is_signer: true,
1202            is_writable: false,
1203        }];
1204        let data = b"ins data".to_vec();
1205        let vm_addr = MM_INPUT_START;
1206        let (_mem, region) = MockInstruction {
1207            program_id,
1208            accounts: accounts.clone(),
1209            data: data.clone(),
1210        }
1211        .into_region(vm_addr);
1212
1213        let config = Config {
1214            aligned_memory_mapping: false,
1215            ..Config::default()
1216        };
1217        let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1218
1219        let ins = SyscallInvokeSignedRust::translate_instruction(
1220            vm_addr,
1221            &mut memory_mapping,
1222            &mut invoke_context,
1223        )
1224        .unwrap();
1225        assert_eq!(ins.program_id, program_id);
1226        assert_eq!(ins.accounts, accounts);
1227        assert_eq!(ins.data, data);
1228    }
1229
1230    #[test]
1231    fn test_translate_signers() {
1232        let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1233        mock_invoke_context!(
1234            invoke_context,
1235            transaction_context,
1236            b"instruction data",
1237            transaction_accounts,
1238            &[0],
1239            &[1]
1240        );
1241
1242        let program_id = Pubkey::new_unique();
1243        let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1244
1245        let vm_addr = MM_INPUT_START;
1246        let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1247
1248        let config = Config {
1249            aligned_memory_mapping: false,
1250            ..Config::default()
1251        };
1252        let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1253
1254        let signers = SyscallInvokeSignedRust::translate_signers(
1255            &program_id,
1256            vm_addr,
1257            1,
1258            &mut memory_mapping,
1259            &invoke_context,
1260        )
1261        .unwrap();
1262        assert_eq!(signers[0], derived_key);
1263    }
1264
1265    #[test]
1266    fn test_caller_account_from_account_info() {
1267        let transaction_accounts = transaction_with_one_instruction_account(b"foo".to_vec());
1268        let account = transaction_accounts[1].1.clone();
1269        mock_invoke_context!(
1270            invoke_context,
1271            transaction_context,
1272            b"instruction data",
1273            transaction_accounts,
1274            &[0],
1275            &[1]
1276        );
1277
1278        let key = Pubkey::new_unique();
1279        let vm_addr = MM_INPUT_START;
1280        let (_mem, region) = MockAccountInfo::new(key, &account).into_region(vm_addr);
1281
1282        let config = Config {
1283            aligned_memory_mapping: false,
1284            ..Config::default()
1285        };
1286        let memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1287
1288        let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1289
1290        let caller_account = CallerAccount::from_account_info(
1291            &invoke_context,
1292            &memory_mapping,
1293            vm_addr,
1294            account_info,
1295            account.data().len(),
1296        )
1297        .unwrap();
1298        assert_eq!(*caller_account.lamports, account.lamports());
1299        assert_eq!(caller_account.owner, account.owner());
1300        assert_eq!(caller_account.original_data_len, account.data().len());
1301        assert_eq!(
1302            *caller_account.ref_to_len_in_vm as usize,
1303            account.data().len()
1304        );
1305        assert_eq!(caller_account.data, account.data());
1306        assert_eq!(caller_account.executable, account.executable());
1307        assert_eq!(caller_account.rent_epoch, account.rent_epoch());
1308    }
1309
1310    #[test]
1311    fn test_update_caller_account_lamports_owner() {
1312        let transaction_accounts = transaction_with_one_instruction_account(vec![]);
1313        let account = transaction_accounts[1].1.clone();
1314        mock_invoke_context!(
1315            invoke_context,
1316            transaction_context,
1317            b"instruction data",
1318            transaction_accounts,
1319            &[0],
1320            &[1]
1321        );
1322
1323        let instruction_context = invoke_context
1324            .transaction_context
1325            .get_current_instruction_context()
1326            .unwrap();
1327
1328        let mut mock_caller_account =
1329            MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1330
1331        let config = Config {
1332            aligned_memory_mapping: false,
1333            ..Config::default()
1334        };
1335        let memory_mapping =
1336            MemoryMapping::new(vec![mock_caller_account.region.clone()], &config).unwrap();
1337
1338        let mut caller_account = mock_caller_account.caller_account();
1339
1340        let mut callee_account = instruction_context
1341            .try_borrow_instruction_account(invoke_context.transaction_context, 0)
1342            .unwrap();
1343
1344        callee_account.set_lamports(42).unwrap();
1345        callee_account
1346            .set_owner(Pubkey::new_unique().as_ref())
1347            .unwrap();
1348
1349        update_caller_account(
1350            &invoke_context,
1351            &memory_mapping,
1352            &mut caller_account,
1353            &callee_account,
1354        )
1355        .unwrap();
1356
1357        assert_eq!(*caller_account.lamports, 42);
1358        assert_eq!(caller_account.owner, callee_account.get_owner());
1359    }
1360
1361    #[test]
1362    fn test_update_caller_account_data() {
1363        let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1364        let account = transaction_accounts[1].1.clone();
1365        let original_data_len = account.data().len();
1366
1367        mock_invoke_context!(
1368            invoke_context,
1369            transaction_context,
1370            b"instruction data",
1371            transaction_accounts,
1372            &[0],
1373            &[1]
1374        );
1375
1376        let instruction_context = invoke_context
1377            .transaction_context
1378            .get_current_instruction_context()
1379            .unwrap();
1380
1381        let mut mock_caller_account = MockCallerAccount::new(
1382            account.lamports(),
1383            *account.owner(),
1384            0xFFFFFFFF00000000,
1385            account.data(),
1386        );
1387
1388        let config = Config {
1389            aligned_memory_mapping: false,
1390            ..Config::default()
1391        };
1392        let memory_mapping =
1393            MemoryMapping::new(vec![mock_caller_account.region.clone()], &config).unwrap();
1394
1395        let data_slice = mock_caller_account.data_slice();
1396        let len_ptr = unsafe {
1397            data_slice
1398                .as_ptr()
1399                .offset(-(mem::size_of::<u64>() as isize))
1400        };
1401        let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1402        let mut caller_account = mock_caller_account.caller_account();
1403
1404        let mut callee_account = instruction_context
1405            .try_borrow_instruction_account(invoke_context.transaction_context, 0)
1406            .unwrap();
1407
1408        for (new_value, expected_realloc_size) in [
1409            (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1410            (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1411            (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1412        ] {
1413            assert_eq!(caller_account.data, callee_account.get_data());
1414            callee_account.set_data_from_slice(&new_value).unwrap();
1415
1416            update_caller_account(
1417                &invoke_context,
1418                &memory_mapping,
1419                &mut caller_account,
1420                &callee_account,
1421            )
1422            .unwrap();
1423
1424            let data_len = callee_account.get_data().len();
1425            assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
1426            assert_eq!(data_len, serialized_len());
1427            assert_eq!(data_len, caller_account.data.len());
1428            assert_eq!(callee_account.get_data(), &caller_account.data[..data_len]);
1429            assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1430            assert!(is_zeroed(&data_slice[data_len..]));
1431        }
1432
1433        callee_account
1434            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1435            .unwrap();
1436        update_caller_account(
1437            &invoke_context,
1438            &memory_mapping,
1439            &mut caller_account,
1440            &callee_account,
1441        )
1442        .unwrap();
1443        let data_len = callee_account.get_data().len();
1444        assert_eq!(data_slice[data_len..].len(), 0);
1445        assert!(is_zeroed(&data_slice[data_len..]));
1446
1447        callee_account
1448            .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1449            .unwrap();
1450        assert!(matches!(
1451            update_caller_account(
1452                &invoke_context,
1453                &memory_mapping,
1454                &mut caller_account,
1455                &callee_account,
1456            ),
1457            Err(EbpfError::UserError(error)) if error.downcast_ref::<BpfError>().unwrap() == &BpfError::SyscallError(
1458                SyscallError::InstructionError(InstructionError::InvalidRealloc)
1459            )
1460        ));
1461    }
1462
1463    macro_rules! borrow_instruction_account {
1464        ($invoke_context:expr, $index:expr) => {{
1465            let instruction_context = $invoke_context
1466                .transaction_context
1467                .get_current_instruction_context()
1468                .unwrap();
1469            instruction_context
1470                .try_borrow_instruction_account($invoke_context.transaction_context, $index)
1471                .unwrap()
1472        }};
1473    }
1474
1475    #[test]
1476    fn test_update_callee_account_lamports_owner() {
1477        let transaction_accounts = transaction_with_one_instruction_account(vec![]);
1478        let account = transaction_accounts[1].1.clone();
1479
1480        mock_invoke_context!(
1481            invoke_context,
1482            transaction_context,
1483            b"instruction data",
1484            transaction_accounts,
1485            &[0],
1486            &[1]
1487        );
1488
1489        let mut mock_caller_account =
1490            MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1491
1492        let caller_account = mock_caller_account.caller_account();
1493
1494        let callee_account = borrow_instruction_account!(invoke_context, 0);
1495
1496        *caller_account.lamports = 42;
1497        *caller_account.owner = Pubkey::new_unique();
1498
1499        update_callee_account(&invoke_context, &caller_account, callee_account).unwrap();
1500
1501        let callee_account = borrow_instruction_account!(invoke_context, 0);
1502        assert_eq!(callee_account.get_lamports(), 42);
1503        assert_eq!(caller_account.owner, callee_account.get_owner());
1504    }
1505
1506    #[test]
1507    fn test_update_callee_account_data() {
1508        let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1509        let account = transaction_accounts[1].1.clone();
1510
1511        mock_invoke_context!(
1512            invoke_context,
1513            transaction_context,
1514            b"instruction data",
1515            transaction_accounts,
1516            &[0],
1517            &[1]
1518        );
1519
1520        let mut mock_caller_account =
1521            MockCallerAccount::new(1234, *account.owner(), 0xFFFFFFFF00000000, account.data());
1522
1523        let mut caller_account = mock_caller_account.caller_account();
1524
1525        let callee_account = borrow_instruction_account!(invoke_context, 0);
1526
1527        let mut data = b"foo".to_vec();
1528        caller_account.data = &mut data;
1529
1530        update_callee_account(&invoke_context, &caller_account, callee_account).unwrap();
1531
1532        let callee_account = borrow_instruction_account!(invoke_context, 0);
1533        assert_eq!(callee_account.get_data(), caller_account.data);
1534    }
1535
1536    #[test]
1537    fn test_translate_accounts_rust() {
1538        let transaction_accounts = transaction_with_one_instruction_account(b"foobar".to_vec());
1539        let account = transaction_accounts[1].1.clone();
1540        let key = transaction_accounts[1].0;
1541        let original_data_len = account.data().len();
1542
1543        mock_invoke_context!(
1544            invoke_context,
1545            transaction_context,
1546            b"instruction data",
1547            transaction_accounts,
1548            &[0],
1549            &[1, 1]
1550        );
1551        invoke_context
1552            .set_syscall_context(
1553                true,
1554                true,
1555                vec![original_data_len],
1556                Rc::new(RefCell::new(BpfAllocator::new(
1557                    AlignedMemory::with_capacity(0),
1558                    0,
1559                ))),
1560            )
1561            .unwrap();
1562
1563        let vm_addr = MM_INPUT_START;
1564        let (_mem, region) = MockAccountInfo::new(key, &account).into_region(vm_addr);
1565
1566        let config = Config {
1567            aligned_memory_mapping: false,
1568            ..Config::default()
1569        };
1570        let mut memory_mapping = MemoryMapping::new(vec![region], &config).unwrap();
1571
1572        let accounts = SyscallInvokeSignedRust::translate_accounts(
1573            &[
1574                InstructionAccount {
1575                    index_in_transaction: 1,
1576                    index_in_caller: 0,
1577                    index_in_callee: 0,
1578                    is_signer: false,
1579                    is_writable: true,
1580                },
1581                InstructionAccount {
1582                    index_in_transaction: 1,
1583                    index_in_caller: 0,
1584                    index_in_callee: 0,
1585                    is_signer: false,
1586                    is_writable: true,
1587                },
1588            ],
1589            &[0],
1590            vm_addr,
1591            1,
1592            &mut memory_mapping,
1593            &mut invoke_context,
1594        )
1595        .unwrap();
1596        assert_eq!(accounts.len(), 2);
1597        assert!(accounts[0].1.is_none());
1598        let caller_account = accounts[1].1.as_ref().unwrap();
1599        assert_eq!(caller_account.data, account.data());
1600        assert_eq!(caller_account.original_data_len, original_data_len);
1601    }
1602
1603    pub type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1604
1605    struct MockCallerAccount {
1606        lamports: u64,
1607        owner: Pubkey,
1608        vm_addr: u64,
1609        data: Vec<u8>,
1610        len: u64,
1611        region: MemoryRegion,
1612    }
1613
1614    impl MockCallerAccount {
1615        fn new(lamports: u64, owner: Pubkey, vm_addr: u64, data: &[u8]) -> MockCallerAccount {
1616            // write [len][data] into a vec so we can check that they get
1617            // properly updated by update_caller_account()
1618            let mut d = vec![0; mem::size_of::<u64>() + data.len() + MAX_PERMITTED_DATA_INCREASE];
1619            unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1620            d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1621
1622            // the memory region must include the realloc data
1623            let region = MemoryRegion::new_writable(d.as_mut_slice(), vm_addr);
1624
1625            // caller_account.data must have the actual data length
1626            d.truncate(mem::size_of::<u64>() + data.len());
1627
1628            MockCallerAccount {
1629                lamports,
1630                owner,
1631                vm_addr,
1632                data: d,
1633                len: data.len() as u64,
1634                region,
1635            }
1636        }
1637
1638        fn data_slice<'a>(&self) -> &'a [u8] {
1639            // lifetime crimes
1640            unsafe {
1641                slice::from_raw_parts(
1642                    self.data[mem::size_of::<u64>()..].as_ptr(),
1643                    self.data.capacity() - mem::size_of::<u64>(),
1644                )
1645            }
1646        }
1647
1648        fn caller_account(&mut self) -> CallerAccount<'_> {
1649            let data = &mut self.data[mem::size_of::<u64>()..];
1650            CallerAccount {
1651                lamports: &mut self.lamports,
1652                owner: &mut self.owner,
1653                original_data_len: data.len(),
1654                data,
1655                vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
1656                ref_to_len_in_vm: &mut self.len,
1657                serialized_len_ptr: std::ptr::null_mut(),
1658                executable: false,
1659                rent_epoch: 0,
1660            }
1661        }
1662    }
1663
1664    fn transaction_with_one_instruction_account(data: Vec<u8>) -> Vec<TestTransactionAccount> {
1665        let program_id = Pubkey::new_unique();
1666        let account = AccountSharedData::from(Account {
1667            lamports: 1,
1668            data,
1669            owner: program_id,
1670            executable: false,
1671            rent_epoch: 100,
1672        });
1673        vec![
1674            (
1675                program_id,
1676                AccountSharedData::from(Account {
1677                    lamports: 0,
1678                    data: vec![],
1679                    owner: bpf_loader::id(),
1680                    executable: true,
1681                    rent_epoch: 0,
1682                }),
1683                false,
1684            ),
1685            (Pubkey::new_unique(), account, true),
1686        ]
1687    }
1688
1689    struct MockInstruction {
1690        program_id: Pubkey,
1691        accounts: Vec<AccountMeta>,
1692        data: Vec<u8>,
1693    }
1694
1695    impl MockInstruction {
1696        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1697            let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
1698
1699            let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
1700
1701            let mut data = vec![0; size];
1702
1703            let vm_addr = vm_addr as usize;
1704            let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
1705            let data_addr = accounts_addr + accounts_len;
1706
1707            let ins = Instruction {
1708                program_id: self.program_id,
1709                accounts: unsafe {
1710                    Vec::from_raw_parts(
1711                        accounts_addr as *mut _,
1712                        self.accounts.len(),
1713                        self.accounts.len(),
1714                    )
1715                },
1716                data: unsafe {
1717                    Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
1718                },
1719            };
1720            let ins = StableInstruction::from(ins);
1721
1722            unsafe {
1723                ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
1724                data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
1725                    slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
1726                );
1727                data[data_addr - vm_addr..].copy_from_slice(&self.data);
1728            }
1729
1730            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1731            (data, region)
1732        }
1733    }
1734
1735    fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1736        let slice_size = mem::size_of::<&[()]>();
1737        let size = signers
1738            .iter()
1739            .fold(slice_size, |size, signer| size + slice_size + signer.len());
1740
1741        let vm_addr = vm_addr as usize;
1742        let mut slices_addr = vm_addr + slice_size;
1743
1744        let mut data = vec![0; size];
1745        unsafe {
1746            ptr::write_unaligned(
1747                data.as_mut_ptr().cast(),
1748                slice::from_raw_parts::<&[&[u8]]>(slices_addr as *const _, signers.len()),
1749            );
1750        }
1751
1752        let mut signers_addr = slices_addr + signers.len() * slice_size;
1753
1754        for signer in signers {
1755            unsafe {
1756                ptr::write_unaligned(
1757                    (data.as_mut_ptr() as usize + slices_addr - vm_addr) as *mut _,
1758                    slice::from_raw_parts::<&[u8]>(signers_addr as *const _, signer.len()),
1759                );
1760            }
1761            slices_addr += slice_size;
1762            signers_addr += signer.len();
1763        }
1764
1765        let slices_addr = vm_addr + slice_size;
1766        let mut signers_addr = slices_addr + signers.len() * slice_size;
1767        for signer in signers {
1768            data[signers_addr - vm_addr..][..signer.len()].copy_from_slice(signer);
1769            signers_addr += signer.len();
1770        }
1771
1772        let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1773        (data, region)
1774    }
1775
1776    struct MockAccountInfo<'a> {
1777        key: Pubkey,
1778        is_signer: bool,
1779        is_writable: bool,
1780        lamports: u64,
1781        data: &'a [u8],
1782        owner: Pubkey,
1783        executable: bool,
1784        rent_epoch: Epoch,
1785    }
1786
1787    impl<'a> MockAccountInfo<'a> {
1788        fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
1789            MockAccountInfo {
1790                key,
1791                is_signer: false,
1792                is_writable: false,
1793                lamports: account.lamports(),
1794                data: account.data(),
1795                owner: *account.owner(),
1796                executable: account.executable(),
1797                rent_epoch: account.rent_epoch(),
1798            }
1799        }
1800
1801        fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
1802            let size = mem::size_of::<AccountInfo>()
1803                + mem::size_of::<Pubkey>() * 2
1804                + mem::size_of::<RcBox<RefCell<&mut u64>>>()
1805                + mem::size_of::<u64>()
1806                + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
1807                + self.data.len();
1808            let mut data = vec![0; size];
1809
1810            let vm_addr = vm_addr as usize;
1811            let key_addr = vm_addr + mem::size_of::<AccountInfo>();
1812            let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
1813            let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
1814            let owner_addr = lamports_addr + mem::size_of::<u64>();
1815            let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
1816            let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
1817
1818            let info = AccountInfo {
1819                key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
1820                is_signer: self.is_signer,
1821                is_writable: self.is_writable,
1822                lamports: unsafe {
1823                    Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
1824                },
1825                data: unsafe {
1826                    Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
1827                },
1828                owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
1829                executable: self.executable,
1830                rent_epoch: self.rent_epoch,
1831            };
1832
1833            unsafe {
1834                ptr::write_unaligned(data.as_mut_ptr().cast(), info);
1835                ptr::write_unaligned(
1836                    (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
1837                    self.key,
1838                );
1839                ptr::write_unaligned(
1840                    (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
1841                    RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
1842                );
1843                ptr::write_unaligned(
1844                    (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
1845                    self.lamports,
1846                );
1847                ptr::write_unaligned(
1848                    (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
1849                    self.owner,
1850                );
1851                ptr::write_unaligned(
1852                    (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
1853                    RcBox::new(RefCell::new(slice::from_raw_parts_mut(
1854                        data_addr as *mut u8,
1855                        self.data.len(),
1856                    ))),
1857                );
1858                data[data_addr - vm_addr..].copy_from_slice(self.data);
1859            }
1860
1861            let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
1862            (data, region)
1863        }
1864    }
1865
1866    #[repr(C)]
1867    struct RcBox<T> {
1868        strong: Cell<usize>,
1869        weak: Cell<usize>,
1870        value: T,
1871    }
1872
1873    impl<T> RcBox<T> {
1874        const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
1875        fn new(value: T) -> RcBox<T> {
1876            RcBox {
1877                strong: Cell::new(0),
1878                weak: Cell::new(0),
1879                value,
1880            }
1881        }
1882    }
1883
1884    fn is_zeroed(data: &[u8]) -> bool {
1885        data.iter().all(|b| *b == 0)
1886    }
1887}