LCOV - code coverage report
Current view: top level - flamenco/vm/syscall - fd_vm_syscall_cpi_common.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 398 447 89.0 %
Date: 2026-06-08 09:27:03 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* This file contains all the logic that is common to both the C and Rust
       2             :    CPI syscalls (sol_invoke_signed_{rust/c}). As such, all of the functions in
       3             :    here are templated and will be instantiated for both the C and Rust CPI ABIs.
       4             : 
       5             :    The only difference between the C and Rust CPI syscalls is the ABI data layout
       6             :    of the parameters to these calls - all the logic is identical. As such, we have
       7             :    defined a series of macros to abstract away the ABI differences from the CPI implementation.
       8             : 
       9             :    The entry-point for these syscalls is VM_SYSCALL_CPI_ENTRYPOINT.
      10             : 
      11             :    Note that the code for these syscalls could be simplified somewhat, but we have opted to keep
      12             :    it as close to the Solana code as possible to make it easier to audit that we execute equivalently.
      13             :    Most of the top-level functions in this file correspond directly to functions in the Solana codebase
      14             :    and links to the source have been provided.
      15             :  */
      16             : 
      17             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L126-L144
      18             : 
      19             :    This is used for checking that the account info pointers given by the
      20             :    user match up with the addresses in the serialized account metadata.
      21             : 
      22             :    Field name length is restricted to 54 because
      23             :    127 - (37 + 18 + 18) leaves 54 characters for the field name
      24             :  */
      25             : #define VM_SYSCALL_CPI_CHECK_ACCOUNT_INFO_POINTER_FIELD_MAX_54(vm, vm_addr, expected_vm_addr, field_name) \
      26       11274 :   if( FD_UNLIKELY( vm_addr!=expected_vm_addr )) {                                                         \
      27         144 :     fd_log_collector_printf_dangerous_max_127( vm->instr_ctx,                                             \
      28         144 :       "Invalid account info pointer `%s': 0x%lx != 0x%lx", field_name, vm_addr, expected_vm_addr );         \
      29         144 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );                                   \
      30         144 :     return FD_VM_SYSCALL_ERR_INVALID_POINTER;                                                             \
      31         144 :   }
      32             : 
      33             : /* fd_vm_syscall_cpi_instruction_to_instr_{c/rust} takes the translated
      34             :    CPI ABI structures (instruction and account meta list), and uses these
      35             :    to populate a fd_instr_info_t struct. This struct can then be given to the
      36             :    FD runtime for execution.
      37             : 
      38             :    WARNING:  out_instr will be partially filled if there are unmatched account
      39             :    metas (i.e,. no corresponding entry in the transaction accounts list). This
      40             :    is not an error condition. fd_vm_prepare_instruction has to handle that case
      41             :    in order to match Agave's behavior of checking presence in both transaction
      42             :    accounts list and caller instruction accounts list in a single loop iteration.
      43             : 
      44             : Parameters:
      45             : - vm: handle to the vm
      46             : - cpi_instr: instruction to execute laid out in the CPI ABI format (Rust or C)
      47             : - cpi_acc_metas: list of account metas, again in the CPI ABI format
      48             : - signers: derived signers for this CPI call
      49             : - signers_cnt: length of the signers list
      50             : - cpi_instr_data: instruction data in host address space
      51             : 
      52             : TODO: return codes/errors?
      53             : */
      54        3255 : #define VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_instruction_to_instr_, VM_SYSCALL_CPI_ABI)
      55             : static int
      56             : VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC( fd_vm_t *                         vm,
      57             :                                           VM_SYSCALL_CPI_INSTR_T const *    cpi_instr,
      58             :                                           VM_SYSCALL_CPI_ACC_META_T const * cpi_acct_metas,
      59             :                                           fd_pubkey_t const *               program_id,
      60             :                                           uchar const *                     cpi_instr_data,
      61             :                                           fd_instr_info_t *                 out_instr,
      62        3255 :                                           fd_pubkey_t                       out_instr_acct_keys[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ] ) {
      63             : 
      64        3255 :   out_instr->program_id   = UCHAR_MAX;
      65        3255 :   out_instr->stack_height = (uchar)( vm->instr_ctx->runtime->instr.stack_sz+1 );
      66        3255 :   out_instr->data_sz      = (ushort)VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instr );
      67        3255 :   out_instr->acct_cnt     = (ushort)VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instr );
      68        3255 :   memcpy( out_instr->data, cpi_instr_data, out_instr->data_sz );
      69             : 
      70             :   /* Find the index of the CPI instruction's program account in the transaction */
      71        3255 :   int program_id_idx = fd_runtime_find_index_of_account( vm->instr_ctx->txn_out, program_id );
      72        3255 :   if( FD_LIKELY( program_id_idx != -1 ) ) {
      73        3255 :     out_instr->program_id = (uchar)program_id_idx;
      74        3255 :   }
      75             : 
      76        3255 :   uchar acc_idx_seen[ FD_TXN_ACCT_ADDR_MAX ] = {0};
      77             : 
      78        7707 :   for( ushort i=0; i<VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instr ); i++ ) {
      79        4452 :     VM_SYSCALL_CPI_ACC_META_T const * cpi_acct_meta = &cpi_acct_metas[i];
      80        6597 :     fd_pubkey_t const * pubkey = fd_type_pun_const( VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, cpi_acct_meta ) );
      81        6597 :     out_instr_acct_keys[i] = *pubkey;
      82             : 
      83             :     /* The parent flag(s) for is writable/signer is checked in
      84             :        fd_vm_prepare_instruction. Signer privilege is allowed iff the account
      85             :        is a signer in the caller or if it is a derived signer. */
      86             :     /* TODO: error if flags are wrong */
      87        6597 :     out_instr->accounts[i] = fd_instruction_account_init( USHORT_MAX,
      88        6597 :                                                           USHORT_MAX,
      89        6597 :                                                           USHORT_MAX,
      90        6597 :                                                           VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( cpi_acct_meta ),
      91        6597 :                                                           VM_SYSCALL_CPI_ACC_META_IS_SIGNER( cpi_acct_meta ) );
      92             : 
      93             :     /* Use USHORT_MAX to indicate account not found
      94             :         https://github.com/anza-xyz/agave/blob/v3.0.4/program-runtime/src/invoke_context.rs#L395-L397 */
      95        6597 :     int idx_in_txn    = fd_runtime_find_index_of_account( vm->instr_ctx->txn_out, pubkey );
      96        6597 :     int idx_in_caller = fd_exec_instr_ctx_find_idx_of_instr_account( vm->instr_ctx, pubkey );
      97             : 
      98        6597 :     fd_instr_info_setup_instr_account( out_instr,
      99        6597 :                                        acc_idx_seen,
     100        6597 :                                        idx_in_txn!=-1 ? (ushort)idx_in_txn : USHORT_MAX,
     101        6597 :                                        idx_in_caller!=-1 ? (ushort)idx_in_caller : USHORT_MAX,
     102        6597 :                                        i,
     103        6597 :                                        VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( cpi_acct_meta ),
     104        6597 :                                        VM_SYSCALL_CPI_ACC_META_IS_SIGNER( cpi_acct_meta ) );
     105             : 
     106        6597 :   }
     107             : 
     108        3255 :   return FD_VM_SUCCESS;
     109        3255 : }
     110             : 
     111             : /*
     112             : fd_vm_syscall_cpi_update_callee_acc_{rust/c} corresponds to solana_bpf_loader_program::syscalls::cpi::update_callee_account:
     113             : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1211-L1273
     114             : 
     115             : (the copy of the account stored in the instruction context's
     116             : borrowed accounts cache)
     117             : 
     118             : This function should be called before the CPI instruction is executed. Its purpose is to
     119             : update the callee account's view of the given account with any changes the caller may made
     120             : to the account before the CPI instruction is executed.
     121             : 
     122             : The callee's view of the account is the borrowed accounts cache, so to update the
     123             : callee account we look up the account in the borrowed accounts cache and update it.
     124             : 
     125             : Paramaters:
     126             : - vm: pointer to the virtual machine handle
     127             : - account_info: account info object
     128             : - callee_acc_pubkey: pubkey of the account. this is used to look up the account in the borrowed accounts cache
     129             :   (TODO: this seems redundant? we can probably remove this, as the account_info contains the pubkey)
     130             : */
     131        3774 : #define VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_update_callee_acc_, VM_SYSCALL_CPI_ABI)
     132             : static int
     133             : VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC( fd_vm_t *                          vm,
     134             :                                       fd_vm_cpi_caller_account_t const * caller_account,
     135             :                                       fd_borrowed_account_t *            callee_acc,
     136        3774 :                                       uchar *                            out_must_update_caller ) {
     137        3774 :   int err;
     138        3774 :   *out_must_update_caller = 0;
     139             : 
     140             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1222-L1224 */
     141        3774 :   if( fd_borrowed_account_get_lamports( callee_acc )!=*(caller_account->lamports) ) {
     142         303 :     err = fd_borrowed_account_set_lamports( callee_acc, *(caller_account->lamports) );
     143         303 :     if( FD_UNLIKELY( err ) ) {
     144           0 :       FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     145           0 :       return -1;
     146           0 :     }
     147         303 :   }
     148             : 
     149             :   /* With virtual_address_space_adjustments enabled, we validate account
     150             :      length changes and update the associated borrowed account with any
     151             :      changed made. If direct mapping is also enabled, we skip actually copying
     152             :      the data back to the borrowed account, as it is already updated in-place.
     153             : 
     154             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1226-L1255 */
     155        3774 :   if( vm->virtual_address_space_adjustments ) {
     156        1791 :     ulong prev_len = fd_borrowed_account_get_data_len( callee_acc );
     157        1791 :     ulong post_len = *caller_account->ref_to_len_in_vm;
     158             : 
     159             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1229-L1251 */
     160        1791 :     if( FD_UNLIKELY( prev_len!=post_len ) ) {
     161             :       /* If the account has been shrunk, we're going to zero the unused
     162             :          memory that was previously used. */
     163             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1230-L1247 */
     164           0 :       if( FD_UNLIKELY( !vm->direct_mapping && ( post_len < prev_len ) ) ) {
     165           0 :         fd_memset( caller_account->serialized_data + post_len, 0, prev_len - post_len );
     166           0 :       }
     167             : 
     168             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1248 */
     169           0 :       err = fd_borrowed_account_set_data_length( callee_acc, post_len );
     170           0 :       if( FD_UNLIKELY( err ) ) {
     171           0 :         FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     172           0 :         return -1;
     173           0 :       }
     174             :       /* Pointer to data may have changed, caller must be updated.
     175             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1248-L1250 */
     176           0 :       *out_must_update_caller = 1;
     177           0 :     }
     178             : 
     179             :     /* Without direct mapping, we need to copy the account data from the VM's
     180             :        serialized buffer back to the borrowed account. With direct mapping,
     181             :        data is modified in-place so no copy is needed.
     182             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1252-L1254 */
     183        1791 :     int err;
     184        1791 :     if( !vm->direct_mapping && fd_borrowed_account_can_data_be_changed( callee_acc, &err ) ) {
     185         828 :       err = fd_borrowed_account_set_data_from_slice( callee_acc, caller_account->serialized_data, caller_account->serialized_data_len );
     186         828 :       if( FD_UNLIKELY( err ) ) {
     187           0 :         FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     188           0 :         return -1;
     189           0 :       }
     190         828 :     }
     191        1983 :   } else {
     192             :     /* Direct mapping is not enabled, so we need to copy the account data
     193             :        from the VM's serialized buffer back to the borrowed account.
     194             : 
     195             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1255-L1264 */
     196        1983 :     int err;
     197        1983 :     if( fd_borrowed_account_can_data_be_resized( callee_acc, caller_account->serialized_data_len, &err ) &&
     198        1983 :         fd_borrowed_account_can_data_be_changed( callee_acc, &err ) ) {
     199             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1258 */
     200        1863 :       err = fd_borrowed_account_set_data_from_slice( callee_acc, caller_account->serialized_data, caller_account->serialized_data_len );
     201        1863 :       if( FD_UNLIKELY( err ) ) {
     202           0 :         FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     203           0 :         return -1;
     204           0 :       }
     205        1863 :     } else if( FD_UNLIKELY( caller_account->serialized_data_len!=fd_borrowed_account_get_data_len( callee_acc ) ||
     206         120 :                             (caller_account->serialized_data_len &&
     207         120 :                               memcmp( fd_borrowed_account_get_data( callee_acc ), caller_account->serialized_data, caller_account->serialized_data_len )) ) ) {
     208             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1259-L1261 */
     209           0 :       FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     210           0 :       return -1;
     211           0 :     }
     212        1983 :   }
     213             : 
     214             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1266-L1271 */
     215        3774 :   if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( callee_acc ), caller_account->owner, sizeof(fd_pubkey_t) ) ) ) {
     216         309 :     err = fd_borrowed_account_set_owner( callee_acc, caller_account->owner );
     217         309 :     if( FD_UNLIKELY( err ) ) {
     218           0 :       FD_VM_ERR_FOR_LOG_INSTR( vm, err );
     219           0 :       return -1;
     220           0 :     }
     221             :     /* Caller gave ownership and thus write access away, so caller must be updated.
     222             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1268-L1270 */
     223         309 :     *out_must_update_caller = 1;
     224         309 :   }
     225             : 
     226        3774 :   return FD_VM_SUCCESS;
     227        3774 : }
     228             : 
     229             : /*
     230             : fd_vm_syscall_cpi_translate_and_update_accounts_ mirrors the behaviour of
     231             : solana_program_runtime::cpi::SyscallInvokeSigned::translate_accounts_common:
     232             : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1049-L1193
     233             : 
     234             : It translates the caller accounts to the host address space, and then calls
     235             : fd_vm_syscall_cpi_update_callee_acc to update the callee borrowed account with any changes
     236             : the caller has made to the account during execution before this CPI call.
     237             : 
     238             : Parameters:
     239             : - vm: pointer to the virtual machine handle
     240             : - instruction_accounts: array of instruction accounts
     241             : - instruction_accounts_cnt: length of the instruction_accounts array
     242             : - account_infos: array of account infos
     243             : - account_infos_length: length of the account_infos array
     244             : 
     245             : Populates:
     246             : - translated_accounts: the translated account entries
     247             : - out_len: number of translated account entries
     248             : */
     249        3093 : #define VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_translate_and_update_accounts_, VM_SYSCALL_CPI_ABI)
     250             : static int
     251             : VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC(
     252             :                               fd_vm_t *                         vm,
     253             :                               fd_instruction_account_t const *  instruction_accounts,
     254             :                               ulong const                       instruction_accounts_cnt,
     255             :                               ulong                             acct_infos_va,
     256             :                               fd_pubkey_t const * *             account_info_keys, /* same length as account_infos_length */
     257             :                               VM_SYSCALL_CPI_ACC_INFO_T const * account_infos,
     258             :                               ulong const                       account_infos_length,
     259             :                               fd_vm_cpi_translated_account_t *  translated_accounts,
     260        3093 :                               ulong *                           out_len ) {
     261        7107 :   for( ulong i=0UL; i<instruction_accounts_cnt; i++ ) {
     262        4278 :     if( i!=instruction_accounts[i].index_in_callee ) {
     263             :       /* Skip duplicate accounts */
     264         144 :       continue;
     265         144 :     }
     266             : 
     267             :     /* `fd_vm_prepare_instruction()` will always set up a valid index for `index_in_caller`, so we can access the borrowed account directly.
     268             :        A borrowed account will always have non-NULL meta (if the account doesn't exist, `fd_executor_setup_accounts_for_txn()`
     269             :        will set its meta up) */
     270             : 
     271             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1102 */
     272        4134 :     fd_guarded_borrowed_account_t callee_acct = {0};
     273        4134 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( vm->instr_ctx, instruction_accounts[i].index_in_caller, &callee_acct );
     274             : 
     275        4128 :     fd_pubkey_t const *       account_key = callee_acct.pubkey;
     276        4128 :     fd_account_meta_t const * acc_meta    = fd_borrowed_account_get_acc_meta( &callee_acct );
     277             : 
     278             :     /* If the account is known and executable, we only need to consume the compute units.
     279             :        Executable accounts can't be modified, so we don't need to update the callee account. */
     280        4128 :     if( fd_borrowed_account_is_executable( &callee_acct ) ) {
     281             :       // FIXME: should this be FD_VM_CU_MEM_UPDATE? Changing this changes the CU behaviour from main (because of the base cost)
     282          96 :       FD_VM_CU_UPDATE( vm, acc_meta->dlen / FD_VM_CPI_BYTES_PER_UNIT );
     283          96 :       continue;
     284          96 :     }
     285             : 
     286             :     /* FIXME: we should not need to drop the account here to avoid a double borrow.
     287             :        Instead, we should borrow the account before entering this function. */
     288        4032 :     fd_borrowed_account_drop( &callee_acct );
     289             : 
     290             :     /* Find the indicies of the account in the caller and callee instructions */
     291        4032 :     uint found = 0;
     292       10959 :     for( ushort j=0; j<account_infos_length && !found; j++ ) {
     293        7137 :       fd_pubkey_t const * acct_addr = account_info_keys[ j ];
     294             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1117
     295             :        */
     296        7137 :       if( memcmp( account_key->uc, acct_addr->uc, sizeof(fd_pubkey_t) ) != 0 ) {
     297        3153 :         continue;
     298        3153 :       }
     299             : 
     300        3984 :       fd_vm_cpi_translated_account_t * translated_account = translated_accounts + *out_len;
     301        3984 :       fd_vm_cpi_caller_account_t *     caller_account     = &translated_account->caller_account;
     302        3984 :       ushort                           index_in_caller    = instruction_accounts[i].index_in_caller;
     303        3984 :       translated_account->index_in_caller                 = index_in_caller;
     304        3984 :       translated_account->update_caller_account_info      = (uchar)!!instruction_accounts[i].is_writable;
     305        3984 :       found = 1;
     306             : 
     307             :       /* Logically this check isn't ever going to fail due to how the
     308             :          account_info_keys array is set up.  We replicate the check for
     309             :          clarity and also to guard against accidental violation of the
     310             :          assumed invariant in the future.
     311             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1131-L1134
     312             :        */
     313        3984 :       if( FD_UNLIKELY( j >= account_infos_length ) ) {
     314           0 :         FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     315           0 :         return FD_VM_SYSCALL_ERR_INVALID_LENGTH;
     316           0 :       }
     317             : 
     318             :       /* The following implements the checks in from_account_info which
     319             :          is invoked as do_translate() in translate_and_update_accounts()
     320             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1135-L1146
     321             :        */
     322             :       ////// BEGIN from_account_info
     323             : 
     324        3984 :       fd_vm_acc_region_meta_t * acc_region_meta = &vm->acc_region_metas[index_in_caller];
     325             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L321-L334 */
     326        3984 :       if( FD_LIKELY( vm->syscall_parameter_address_restrictions ) ) {
     327             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L322-L327 */
     328        2859 :         ulong expected_pubkey_vaddr = acc_region_meta->vm_key_addr;
     329             :         /* Max msg_sz: 40 + 18 + 18 = 76 < 127 */
     330        2859 :         VM_SYSCALL_CPI_CHECK_ACCOUNT_INFO_POINTER_FIELD_MAX_54(vm, account_infos[j].pubkey_addr, expected_pubkey_vaddr, "key");
     331             : 
     332             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L328-L333 */
     333        2859 :         ulong expected_owner_vaddr = acc_region_meta->vm_owner_addr;
     334             :         /* Max msg_sz: 42 + 18 + 18 = 78 < 127 */
     335        2859 :         VM_SYSCALL_CPI_CHECK_ACCOUNT_INFO_POINTER_FIELD_MAX_54(vm, account_infos[j].owner_addr, expected_owner_vaddr, "owner");
     336        2823 :       }
     337             : 
     338             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L336-L358 */
     339        9987 :       VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, (account_infos + j), lamports_vaddr );
     340             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L345-L356  */
     341        9987 :       if( FD_LIKELY( vm->syscall_parameter_address_restrictions ) ) {
     342             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L346-L348
     343             :            Check that the account's lamports Rc<RefCell<&mut u64>> is not
     344             :            stored in the account region. Because a refcell is only present if
     345             :            the Rust SDK is used, we only need to check this for the Rust ABI. */
     346             :         #ifdef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR
     347        1431 :         VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR( vm, (account_infos + j), lamports_rc_vaddr )
     348        1431 :         if ( FD_UNLIKELY( lamports_rc_vaddr >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
     349          18 :           FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
     350          18 :           return FD_VM_SYSCALL_ERR_INVALID_POINTER;
     351          18 :         }
     352        1413 :         #endif
     353             : 
     354             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L350-L355 */
     355        1413 :         ulong expected_lamports_vaddr = acc_region_meta->vm_lamports_addr;
     356             :         /* Max msg_sz: 45 + 18 + 18 = 81 < 127 */
     357        2805 :         VM_SYSCALL_CPI_CHECK_ACCOUNT_INFO_POINTER_FIELD_MAX_54(vm, lamports_vaddr, expected_lamports_vaddr, "lamports");
     358        2769 :       }
     359             : 
     360             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L357
     361             :        */
     362       13707 :       VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, (account_infos + j), lamports_haddr );
     363       13707 :       caller_account->lamports = lamports_haddr;
     364             : 
     365             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L360-L364
     366             :        */
     367       13707 :       caller_account->owner = FD_VM_MEM_HADDR_ST( vm, (account_infos + j)->owner_addr, alignof(uchar), sizeof(fd_pubkey_t) );
     368             : 
     369             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L367-L378
     370             :        */
     371        7800 :       VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, (account_infos + j), data_vaddr );
     372             : 
     373             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L379-L386 */
     374        7800 :       if( vm->syscall_parameter_address_restrictions ) {
     375        2751 :         VM_SYSCALL_CPI_CHECK_ACCOUNT_INFO_POINTER_FIELD_MAX_54(
     376        2751 :           vm, data_vaddr, acc_region_meta->vm_data_addr, "data");
     377        2679 :       } else {
     378             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L388-L392 */
     379        1113 :         VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, (account_infos + j), data_vaddr );
     380        1113 :         FD_VM_CU_UPDATE( vm, data_vaddr_len / FD_VM_CPI_BYTES_PER_UNIT );
     381        1107 :       }
     382             : 
     383             :       #ifdef VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR
     384             :       /* Rust ABI
     385             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L395-L404 */
     386        1908 :       VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR( vm, (account_infos + j), data_len_vaddr );
     387        1908 :       (void)acct_infos_va;
     388             :       #else
     389             :       /* C ABI
     390             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L508-L514 */
     391        1878 :       ulong data_len_vaddr = vm_syscall_cpi_data_len_vaddr_c(
     392        1878 :         fd_ulong_sat_add( acct_infos_va, fd_ulong_sat_mul( j, VM_SYSCALL_CPI_ACC_INFO_SIZE ) ),
     393        1878 :         (ulong)&((account_infos + j)->data_sz),
     394        1878 :         (ulong)(account_infos + j)
     395        1878 :       );
     396        1878 :       #endif
     397             : 
     398             :       /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L397-L404
     399             :          C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L515-L522 */
     400        3786 :       if( FD_UNLIKELY( vm->syscall_parameter_address_restrictions && data_len_vaddr >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
     401           0 :         FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
     402           0 :         return FD_VM_SYSCALL_ERR_INVALID_POINTER;
     403           0 :       }
     404             : 
     405             :       /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L411
     406             :          C ABI:    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L545 */
     407        3786 :       caller_account->vm_data_vaddr = data_vaddr;
     408             : 
     409             :       /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L405-L406
     410             :          C ABI:    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L523-L524 */
     411        3786 :       ulong * data_len = FD_VM_MEM_HADDR_ST( vm, data_len_vaddr, 1UL, sizeof(ulong) );
     412        3786 :       caller_account->ref_to_len_in_vm = data_len;
     413             : 
     414             :       /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L408-L421
     415             :          C ABI:    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L525-L538
     416             : 
     417             :          Both ABIs call CallerAccount::get_serialized_data:
     418             :          https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L250-L299 */
     419             : 
     420             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L262-L272 */
     421        3786 :       if( vm->syscall_parameter_address_restrictions ) {
     422        2679 :         ulong address_space_reserved_for_account;
     423        2679 :         if( vm->is_deprecated ) {
     424        1338 :           address_space_reserved_for_account = acc_region_meta->original_data_len;
     425        1341 :         } else {
     426        1341 :           address_space_reserved_for_account = fd_ulong_sat_add( acc_region_meta->original_data_len, MAX_PERMITTED_DATA_INCREASE );
     427        1341 :         }
     428        2679 :         if( FD_UNLIKELY( *data_len > address_space_reserved_for_account ) ) {
     429           0 :           FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC );
     430           0 :           return -1;
     431           0 :         }
     432        2679 :       }
     433             : 
     434             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L273-L298
     435             : 
     436             :          With both virtual_address_space_adjustments and direct_mapping,
     437             :          account data is modified in-place so we don't track the
     438             :          serialized_data pointer.
     439             : 
     440             :          With virtual_address_space_adjustments only (no direct_mapping), data was copied into the input
     441             :          region buffer. We don't apply the extra memory translation checks, as
     442             :          we have checked the data pointer is valid above. So instead we add
     443             :          the vaddr to the start of the input region address space - copying
     444             :          this logic from Agave.
     445             : 
     446             :          In legacy mode, we translate the data pointer directly, as it just
     447             :          maps to a location in the single input region. */
     448        3786 :       if( vm->virtual_address_space_adjustments && vm->direct_mapping ) {
     449             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L273-L275 */
     450         903 :         caller_account->serialized_data     = NULL;
     451         903 :         caller_account->serialized_data_len = 0UL;
     452        2883 :       } else if( vm->virtual_address_space_adjustments ) {
     453             :         /* Skip translation checks here, following the Agave logic:
     454             :            https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L275-L291 */
     455        1776 :         uchar * serialization_ptr           = (uchar *)FD_VM_MEM_SLICE_HADDR_ST( vm, FD_VM_MEM_MAP_INPUT_REGION_START, alignof(uchar), 1UL );
     456        1776 :         caller_account->serialized_data     = serialization_ptr + fd_ulong_sat_sub( data_vaddr, FD_VM_MEM_MAP_INPUT_REGION_START );
     457        1776 :         caller_account->serialized_data_len = *data_len;
     458        1995 :       } else {
     459             :         /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L291-L298 */
     460        7011 :         VM_SYSCALL_CPI_ACC_INFO_DATA( vm, (account_infos + j), data_haddr );
     461        7011 :         (void)data_haddr_vm_addr;
     462        7011 :         caller_account->serialized_data     = data_haddr;
     463        7011 :         caller_account->serialized_data_len = data_haddr_len;
     464        7011 :       }
     465             : 
     466             :       /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L428
     467             :          C ABI:    https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L428 */
     468        3786 :       caller_account->orig_data_len = acc_region_meta->original_data_len;
     469             : 
     470             :       ////// END from_account_info
     471             : 
     472             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1148-L1156 */
     473        3774 :       if( vm->syscall_parameter_address_restrictions ) {
     474        2679 :         FD_VM_CU_UPDATE( vm, *data_len / FD_VM_CPI_BYTES_PER_UNIT );
     475        2679 :       }
     476             : 
     477             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1157-L1181 */
     478        3774 :       uchar update_caller = 0;
     479        3774 :       if( vm->syscall_parameter_address_restrictions ) {
     480        2679 :         update_caller = 1;
     481        2679 :       } else {
     482        1095 :         fd_guarded_borrowed_account_t callee_acc = {0};
     483        1095 :         FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( vm->instr_ctx, index_in_caller, &callee_acc );
     484        1095 :         int err = VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC( vm, caller_account, &callee_acc, &update_caller );
     485        1095 :         if( FD_UNLIKELY( err ) ) {
     486           0 :           return err;
     487           0 :         }
     488        1095 :       }
     489        3774 :       translated_account->update_caller_account_region =
     490        3774 :           (uchar)( translated_account->update_caller_account_info || update_caller );
     491        3774 :       (*out_len)++;
     492        3774 :     }
     493             : 
     494        3822 :     if( !found ) {
     495             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L1183-L1188 */
     496          48 :       FD_BASE58_ENCODE_32_BYTES( account_key->uc, id_b58 );
     497          48 :       fd_log_collector_msg_many( vm->instr_ctx, 2, "Instruction references an unknown account ", 42UL, id_b58, id_b58_len );
     498          48 :       FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_MISSING_ACC );
     499          48 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     500          48 :     }
     501        3822 :   }
     502             : 
     503        2829 :   return FD_VM_SUCCESS;
     504        3093 : }
     505             : 
     506             : /* fd_vm_cpi_update_caller_acc_{rust/c} mirrors the behaviour of
     507             : solana_bpf_loader_program::syscalls::cpi::update_caller_account:
     508             : https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1171-L1268
     509             : 
     510             : This method should be called after a CPI instruction execution has
     511             : returned. It updates the given caller account info with any changes the callee
     512             : has made to this account during execution, so that those changes are
     513             : reflected in the rest of the caller's execution.
     514             : 
     515             : Those changes will be in the instructions borrowed accounts cache.
     516             : 
     517             : Paramaters:
     518             : - vm: handle to the vm
     519             : - caller_acc_info: caller account info object, which should be updated
     520             : - borrowed_callee_acc: already-borrowed callee account
     521             : */
     522        2838 : #define VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC FD_EXPAND_THEN_CONCAT2(fd_vm_cpi_update_caller_acc_, VM_SYSCALL_CPI_ABI)
     523             : static int
     524             : VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC( fd_vm_t *                          vm,
     525             :                                        fd_vm_cpi_caller_account_t *       caller_account,
     526        2838 :                                        fd_borrowed_account_t *            borrowed_callee_acc ) {
     527             : 
     528        2838 :   fd_account_meta_t * callee_meta = borrowed_callee_acc->meta;
     529             :   /* Update the caller account lamports with the value from the callee
     530             :      https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1191 */
     531        2838 :   *(caller_account->lamports) = callee_meta->lamports;
     532             : 
     533             :   /* Update the caller account owner with the value from the callee
     534             :      https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1192 */
     535        2838 :   fd_pubkey_t const * updated_owner = (fd_pubkey_t const *)callee_meta->owner;
     536        2838 :   if( updated_owner ) *caller_account->owner = *updated_owner;
     537           0 :   else                fd_memset( caller_account->owner, 0,             sizeof(fd_pubkey_t) );
     538             : 
     539             :   /* Update the caller account data with the value from the callee
     540             :      https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1194-L1195 */
     541        2838 :   ulong prev_len = *caller_account->ref_to_len_in_vm;
     542        2838 :   ulong post_len = callee_meta->dlen;
     543             : 
     544             :   /* Calculate the address space reserved for the account. With syscall_parameter_address_restrictions
     545             :      and deprecated loader, the reserved space equals original length (no realloc space).
     546             :      Otherwise, we add MAX_PERMITTED_DATA_INCREASE for reallocation.
     547             :      https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1197-L1204 */
     548        2838 :   ulong address_space_reserved_for_account;
     549        2838 :   if( vm->syscall_parameter_address_restrictions && vm->is_deprecated ) {
     550         996 :     address_space_reserved_for_account = caller_account->orig_data_len;
     551        1842 :   } else {
     552        1842 :     address_space_reserved_for_account = fd_ulong_sat_add( caller_account->orig_data_len, MAX_PERMITTED_DATA_INCREASE );
     553        1842 :   }
     554             : 
     555             :   /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1206-L1216 */
     556        2838 :   if( post_len > address_space_reserved_for_account &&
     557        2838 :     ( vm->syscall_parameter_address_restrictions || prev_len != post_len ) ) {
     558         162 :     ulong max_increase = fd_ulong_sat_sub( address_space_reserved_for_account, caller_account->orig_data_len );
     559         162 :     fd_log_collector_printf_dangerous_max_127( vm->instr_ctx, "Account data size realloc limited to %lu in inner instructions", max_increase );
     560         162 :     FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC );
     561         162 :     return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
     562         162 :   }
     563             : 
     564             :   /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1218-L1252 */
     565        2676 :   if( prev_len != post_len ) {
     566             : 
     567             :     /* Without direct mapping, we need to adjust the serialized data buffer
     568             :        when the length changes.
     569             : 
     570             :        With direct mapping, data is mapped in-place so no buffer manipulation
     571             :        is needed.
     572             : 
     573             :        https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1219-L1239 */
     574         510 :     if( !( vm->virtual_address_space_adjustments && vm->direct_mapping ) ) {
     575             : 
     576             :       /* If the account has shrunk, zero out memory that was previously used
     577             :          https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1222-L1230 */
     578         408 :       if( post_len < prev_len ) {
     579             :         /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1227-L1228 */
     580         156 :         if( caller_account->serialized_data_len < post_len ) {
     581           0 :           FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL );
     582           0 :           return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
     583           0 :         }
     584             : 
     585             :         /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1225-L1229 */
     586         156 :         fd_memset( caller_account->serialized_data + post_len, 0, caller_account->serialized_data_len - post_len );
     587         156 :       }
     588             : 
     589             :       /* Set caller_account.serialized_data to post_len.
     590             :          https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1231-L1238 */
     591         408 :       if( vm->virtual_address_space_adjustments ) {
     592             :         /* Calculate the serialized data pointer from the input region base,
     593             :            as described above.
     594             : 
     595             :            https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L99-L115 */
     596         204 :         uchar * serialization_ptr           = (uchar *)FD_VM_MEM_SLICE_HADDR_ST( vm, FD_VM_MEM_MAP_INPUT_REGION_START, alignof(uchar), 1UL );
     597             :         /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1234 */
     598         204 :         caller_account->serialized_data     = serialization_ptr + fd_ulong_sat_sub( caller_account->vm_data_vaddr, FD_VM_MEM_MAP_INPUT_REGION_START );
     599         204 :         caller_account->serialized_data_len = post_len;
     600         306 :       } else {
     601             :         /* Translate the data pointer directly from the VM address, if
     602             :            virtual_address_space_adjustments (or direct mapping) is not
     603             :            enabled.
     604             : 
     605             :           https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L115-L122 */
     606         612 :         caller_account->serialized_data     = (uchar *)FD_VM_MEM_SLICE_HADDR_ST( vm, caller_account->vm_data_vaddr, alignof(uchar), post_len );
     607         612 :         caller_account->serialized_data_len = post_len;
     608         612 :       }
     609         408 :     }
     610             : 
     611             :     /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1240-L1241 */
     612         510 :     *caller_account->ref_to_len_in_vm = post_len;
     613             : 
     614             :     /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1243-L1251 */
     615         510 :     ulong * caller_len = FD_VM_MEM_HADDR_ST( vm, fd_ulong_sat_sub(caller_account->vm_data_vaddr, sizeof(ulong)), alignof(ulong), sizeof(ulong) );
     616         510 :     *caller_len = post_len;
     617         510 :   }
     618             : 
     619             :   /* Without direct mapping, copy the updated account data from the callee's
     620             :      account back to the caller's serialized data buffer. With direct mapping,
     621             :      data was modified in-place so no copy is needed.
     622             : 
     623             :      https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1254-L1265 */
     624        2676 :   if( !(vm->virtual_address_space_adjustments && vm->direct_mapping) ) {
     625             : 
     626             :     /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L1261-L1263 */
     627        2055 :     if( FD_UNLIKELY( caller_account->serialized_data_len!=post_len ) ) {
     628           3 :       FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL );
     629           3 :       return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
     630           3 :     }
     631             : 
     632        2052 :     fd_memcpy( caller_account->serialized_data, fd_account_data( callee_meta ), post_len );
     633        2052 :   }
     634             : 
     635             : 
     636        2673 :   return FD_VM_SUCCESS;
     637        2676 : }
     638             : 
     639             : /* fd_vm_syscall_cpi_{rust/c} is the entrypoint for the sol_invoke_signed_{rust/c} syscalls.
     640             : 
     641             : The bulk of the high-level logic mirrors Solana's cpi_common entrypoint function at
     642             : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L847-L977
     643             : The only differences should be in the order of the error checks, which does not affect consensus.
     644             : 
     645             : 100-foot flow:
     646             : - Translate the CPI ABI structures to the FD runtime's instruction format
     647             : - Update the callee accounts with any changes made by the caller prior to this CPI instruction
     648             : - Dispatch the instruction to the FD runtime (actually making the CPI call)
     649             : - Update the caller accounts with any changes made by the callee during CPI execution
     650             : 
     651             : Paramaters:
     652             : - vm: pointer to the virtual machine handle
     653             : - instruction_va: vm address of the instruction to execute, which will be in the language-specific ABI format.
     654             : - acct_infos_va: vm address of the account infos, which will be in the language-specific ABI format.
     655             : - acct_info_cnt: number of account infos
     656             : - signers_seeds_va: vm address of the signers seeds
     657             : - signers_seeds_cnt: number of signers seeds
     658             : - _ret: pointer to the return value
     659             : */
     660             : #define VM_SYSCALL_CPI_ENTRYPOINT FD_EXPAND_THEN_CONCAT2(fd_vm_syscall_cpi_, VM_SYSCALL_CPI_ABI)
     661             : int
     662             : VM_SYSCALL_CPI_ENTRYPOINT( void *  _vm,
     663             :                            ulong   instruction_va,
     664             :                            ulong   acct_infos_va,
     665             :                            ulong   acct_info_cnt,
     666             :                            ulong   signers_seeds_va,
     667             :                            ulong   signers_seeds_cnt,
     668        3285 :                            ulong * _ret ) {
     669        3285 :   long const regime0 = fd_tickcount();
     670             : 
     671        3285 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     672             : 
     673             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L859-L864 */
     674        3285 :   FD_VM_CU_UPDATE( vm, get_cpi_invoke_unit_cost( vm->instr_ctx->bank ) );
     675             : 
     676             :   /* Translate instruction ********************************************/
     677             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L878-L883
     678             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L575-L636
     679             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L709-L774 */
     680             : 
     681             :   /* Translating the CPI instruction
     682             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L581
     683             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L715 */
     684        3285 :   VM_SYSCALL_CPI_INSTR_T const * cpi_instruction =
     685        9855 :     FD_VM_MEM_HADDR_LD( vm, instruction_va, VM_SYSCALL_CPI_INSTR_ALIGN, VM_SYSCALL_CPI_INSTR_SIZE );
     686             : 
     687             :   /* This needs to be here for the C ABI
     688             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L717
     689             :    */
     690        9855 :   fd_pubkey_t const * program_id = (fd_pubkey_t *)VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, cpi_instruction );
     691             : 
     692             :   /* Translate CPI account metas
     693             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L582-L587
     694             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L718-L723 */
     695        4797 :   VM_SYSCALL_CPI_ACC_META_T const * cpi_account_metas =
     696        6570 :     FD_VM_MEM_SLICE_HADDR_LD( vm, VM_SYSCALL_CPI_INSTR_ACCS_ADDR( cpi_instruction ),
     697        6570 :                               VM_SYSCALL_CPI_ACC_META_ALIGN,
     698        6570 :                               fd_ulong_sat_mul( VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ), VM_SYSCALL_CPI_ACC_META_SIZE ) );
     699             : 
     700             :   /* Translate instruction data
     701             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L588-L593
     702             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L724 */
     703             : 
     704        6570 :   uchar const * data = FD_VM_MEM_SLICE_HADDR_LD(
     705        6570 :     vm, VM_SYSCALL_CPI_INSTR_DATA_ADDR( cpi_instruction ),
     706        6570 :     FD_VM_ALIGN_RUST_U8,
     707        6570 :     VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ));
     708             : 
     709             : 
     710             :   /* Instruction checks
     711             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L595
     712             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L726 */
     713             : 
     714        6570 :   int err = fd_vm_syscall_cpi_check_instruction( VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ), VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) );
     715        6570 :   if( FD_UNLIKELY( err ) ) {
     716           6 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, err );
     717           6 :     return err;
     718           6 :   }
     719             : 
     720             :   /* Agave consumes CU in translate_instruction
     721             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L597-L599
     722             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L728-L730 */
     723        3279 :   ulong total_cu_translation_cost = VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) / FD_VM_CPI_BYTES_PER_UNIT;
     724             : 
     725             :   /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L601-L613
     726             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L732-L745 */
     727        3279 :   if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->bank, increase_cpi_account_info_limit ) ) {
     728             :     /* Agave bills the same regardless of ABI */
     729          18 :     ulong account_meta_translation_cost =
     730          18 :       fd_ulong_sat_mul(
     731          18 :         VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ),
     732          18 :         FD_VM_RUST_ACCOUNT_META_SIZE ) /
     733          18 :       FD_VM_CPI_BYTES_PER_UNIT;
     734          18 :     total_cu_translation_cost = fd_ulong_sat_add( total_cu_translation_cost, account_meta_translation_cost );
     735          18 :   }
     736        3279 :   FD_VM_CU_UPDATE( vm, total_cu_translation_cost );
     737             : 
     738             :   /* Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L617-L629
     739             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L749-L767 */
     740        7755 :   for( ulong i=0UL; i<VM_SYSCALL_CPI_INSTR_ACCS_LEN( cpi_instruction ); i++ ) {
     741        4476 :     VM_SYSCALL_CPI_ACC_META_T const * cpi_acct_meta = &cpi_account_metas[i];
     742        4476 :     if( FD_UNLIKELY( cpi_acct_meta->is_signer > 1U || cpi_acct_meta->is_writable > 1U ) ) {
     743           0 :       FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_INVALID_ARG );
     744           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     745           0 :     }
     746             :     /* Rust ABI: no-op
     747             :        C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L760-L761
     748             :      */
     749        6633 :     (void)VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, cpi_acct_meta );
     750        6633 :   }
     751             : 
     752             :   /* Derive PDA signers
     753             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L887-L893
     754             :      Rust ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L665-L707
     755             :      C    ABI: https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L803-L845
     756             : 
     757             :      Note that we don't need any ABI-specific logic here, because the two ABIs are actually identical for the seeds.*/
     758        3279 :   fd_pubkey_t signers[ FD_CPI_MAX_SIGNER_CNT ] = {0};
     759        3279 :   fd_pubkey_t * caller_program_id = &vm->instr_ctx->txn_out->accounts.keys[ vm->instr_ctx->instr->program_id ];
     760        3279 :   if( FD_LIKELY( signers_seeds_cnt > 0UL ) ) {
     761         324 :     fd_vm_vec_t const * signers_seeds = FD_VM_MEM_SLICE_HADDR_LD( vm, signers_seeds_va, FD_VM_ALIGN_RUST_SLICE_U8_REF, fd_ulong_sat_mul( signers_seeds_cnt, FD_VM_VEC_SIZE ) );
     762         324 :     if( FD_UNLIKELY( signers_seeds_cnt > FD_CPI_MAX_SIGNER_CNT ) ) {
     763           6 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_TOO_MANY_SIGNERS );
     764           6 :       return FD_VM_SYSCALL_ERR_TOO_MANY_SIGNERS;
     765           6 :     }
     766             : 
     767         300 :     for( ulong i=0UL; i<signers_seeds_cnt; i++ ) {
     768             : 
     769             :       /* This function will precompute the memory translation required and do
     770             :         some preflight checks. */
     771         156 :       void const * signer_seed_haddrs[ FD_VM_PDA_SEEDS_MAX ];
     772         156 :       ulong        signer_seed_lens  [ FD_VM_PDA_SEEDS_MAX ];
     773             : 
     774         156 :       int err = fd_vm_translate_and_check_program_address_inputs( vm,
     775         156 :                                                                   signers_seeds[i].addr,
     776         156 :                                                                   signers_seeds[i].len,
     777         156 :                                                                   0UL,
     778         156 :                                                                   signer_seed_haddrs,
     779         156 :                                                                   signer_seed_lens ,
     780         156 :                                                                   NULL,
     781         156 :                                                                   0U );
     782         156 :       if( FD_UNLIKELY( err ) ) {
     783           6 :         return err;
     784           6 :       }
     785             : 
     786         150 :       err = fd_vm_derive_pda( vm, caller_program_id, signer_seed_haddrs, signer_seed_lens, signers_seeds[i].len, NULL, &signers[i] );
     787         150 :       if( FD_UNLIKELY( err ) ) {
     788           6 :         FD_TXN_PREPARE_ERR_OVERWRITE( vm->instr_ctx->txn_out );
     789           6 :         FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_BAD_SEEDS );
     790           6 :         return FD_VM_SYSCALL_ERR_BAD_SEEDS;
     791           6 :       }
     792         150 :     }
     793         156 :   }
     794             : 
     795             :   /* Authorized program check
     796             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L894 */
     797        3261 :   if( FD_UNLIKELY( !fd_vm_syscall_cpi_check_authorized_program( program_id, vm->instr_ctx->bank, data, VM_SYSCALL_CPI_INSTR_DATA_LEN( cpi_instruction ) ) ) ) {
     798           6 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_PROGRAM_NOT_SUPPORTED );
     799           6 :     return FD_VM_SYSCALL_ERR_PROGRAM_NOT_SUPPORTED;
     800           6 :   }
     801             : 
     802             :   /* Create the instruction to execute (in the input format the FD runtime expects) from
     803             :      the translated CPI ABI inputs.
     804             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L895 */
     805        3255 :   fd_pubkey_t cpi_instr_acct_keys[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ];
     806        3255 :   fd_instr_info_t * instruction_to_execute = &vm->instr_ctx->runtime->instr.trace[ vm->instr_ctx->runtime->instr.trace_length++ ];
     807             : 
     808        3255 :   err = VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC( vm, cpi_instruction, cpi_account_metas, program_id, data, instruction_to_execute, cpi_instr_acct_keys );
     809        3255 :   if( FD_UNLIKELY( err ) ) {
     810           0 :     return err;
     811           0 :   }
     812             : 
     813             :   /* Prepare the instruction for execution in the runtime. This is required by the runtime
     814             :      before we can pass an instruction to the executor. */
     815        3255 :   fd_instruction_account_t instruction_accounts[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ];
     816        3255 :   ulong instruction_accounts_cnt;
     817        3255 :   err = fd_vm_prepare_instruction( instruction_to_execute, vm->instr_ctx, program_id, cpi_instr_acct_keys, instruction_accounts, &instruction_accounts_cnt, signers, signers_seeds_cnt );
     818             :   /* Errors are propagated in the function itself. */
     819        3255 :   if( FD_UNLIKELY( err ) ) {
     820          96 :     return err;
     821          96 :   }
     822             : 
     823             :   /* Translate account infos
     824             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L897-L903
     825             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L987-L1047 */
     826             : 
     827             :   /* With syscall_parameter_address_restrictions, verify that the account_infos array
     828             :      is not inside the input region. This prevents programs from passing pointers to
     829             :      the serialized account data region as account_infos, which would allow them to
     830             :      bypass pointer validation checks.
     831             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1002-L1011 */
     832        3159 :   ulong acc_info_total_sz = fd_ulong_sat_mul( acct_info_cnt, VM_SYSCALL_CPI_ACC_INFO_SIZE );
     833        3159 :   if( vm->syscall_parameter_address_restrictions ) {
     834        2247 :     if( FD_UNLIKELY( fd_ulong_sat_add( acct_infos_va, acc_info_total_sz ) >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
     835          36 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
     836          36 :       return FD_VM_SYSCALL_ERR_INVALID_POINTER;
     837          36 :     }
     838        2247 :   }
     839             : 
     840             :   /* This is the equivalent of translate_slice in translate_account_infos */
     841        6246 :   VM_SYSCALL_CPI_ACC_INFO_T const * acc_infos = FD_VM_MEM_SLICE_HADDR_LD( vm, acct_infos_va, VM_SYSCALL_CPI_ACC_INFO_ALIGN, acc_info_total_sz );
     842             : 
     843             :   /* Right after translating, Agave checks the number of account infos */
     844        6246 :   if( FD_UNLIKELY( acct_info_cnt > get_cpi_max_account_infos( vm->instr_ctx->bank ) ) ) {
     845          18 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED );
     846          18 :     return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED;
     847          18 :   }
     848             : 
     849             :   /* Consume compute units proportional to the number of account infos, if
     850             :      increase_cpi_account_info_limit is active */
     851        3105 :   if( FD_FEATURE_ACTIVE_BANK( vm->instr_ctx->bank, increase_cpi_account_info_limit ) ) {
     852          12 :     ulong account_infos_bytes = fd_ulong_sat_mul( acct_info_cnt, FD_VM_ACCOUNT_INFO_BYTE_SIZE );
     853          12 :     FD_VM_CU_UPDATE( vm, account_infos_bytes / FD_VM_CPI_BYTES_PER_UNIT );
     854          12 :   }
     855             : 
     856        3105 :   fd_pubkey_t const * acct_info_keys[ FD_CPI_MAX_ACCOUNT_INFOS_SIMD_0339 ];
     857        9513 :   for( ulong acct_idx = 0UL; acct_idx < acct_info_cnt; acct_idx++ ) {
     858             :     /* Translate each pubkey address specified in account_infos.
     859             :        Failed translation should lead to an access violation and
     860             :        implies that obviously bad account_info has been supplied. */
     861       19236 :       acct_info_keys[ acct_idx ] = FD_VM_MEM_HADDR_LD( vm, acc_infos[ acct_idx ].pubkey_addr, alignof(uchar), sizeof(fd_pubkey_t) );
     862       19236 :   }
     863             : 
     864             :   /* translate_accounts_common ***************************************************************
     865             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1049-L1193 */
     866        3105 :   fd_vm_cpi_translated_account_t translated_accounts[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ];
     867        3093 :   ulong translated_accounts_len = 0UL;
     868        3093 :   err = VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC(
     869        3093 :     vm,
     870        3093 :     instruction_accounts,
     871        3093 :     instruction_accounts_cnt,
     872        3093 :     acct_infos_va,
     873        3093 :     acct_info_keys,
     874        3093 :     acc_infos,
     875        3093 :     acct_info_cnt,
     876        3093 :     translated_accounts,
     877        3093 :     &translated_accounts_len
     878        3093 :   );
     879             :   /* errors are propagated in the function itself. */
     880        3093 :   if( FD_UNLIKELY( err ) ) return err;
     881             : 
     882             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L905-L928 */
     883        2829 :   if( vm->syscall_parameter_address_restrictions ) {
     884        4674 :     for( ulong i=0UL; i<translated_accounts_len; i++ ) {
     885        2679 :       fd_vm_cpi_translated_account_t * translated_account = &translated_accounts[i];
     886        2679 :       fd_guarded_borrowed_account_t callee_acc = {0};
     887        2679 :       FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( vm->instr_ctx, translated_account->index_in_caller, &callee_acc );
     888        2679 :       uchar update_caller = 0;
     889        2679 :       err = VM_SYCALL_CPI_UPDATE_CALLEE_ACC_FUNC( vm, &translated_account->caller_account, &callee_acc, &update_caller );
     890        2679 :       if( FD_UNLIKELY( err ) ) {
     891           0 :         return err;
     892           0 :       }
     893        2679 :       translated_account->update_caller_account_region =
     894        2679 :           (uchar)( translated_account->update_caller_account_info || update_caller );
     895        2679 :     }
     896        1995 :   }
     897             : 
     898             :   /* Set the transaction compute meter to be the same as the VM's compute meter,
     899             :      so that the callee cannot use compute units that the caller has already used. */
     900        2829 :   vm->instr_ctx->txn_out->details.compute_budget.compute_meter = vm->cu;
     901             : 
     902        2829 :   long const regime1 = fd_tickcount();
     903             : 
     904             :   /* Execute the CPI instruction in the runtime */
     905        2829 :   int err_exec = fd_execute_instr( vm->instr_ctx->runtime, vm->instr_ctx->bank, vm->instr_ctx->txn_in, vm->instr_ctx->txn_out, instruction_to_execute );
     906        2829 :   ulong instr_exec_res = (ulong)err_exec;
     907             : 
     908        2829 :   long const regime2 = fd_tickcount();
     909        2829 :   vm->instr_ctx->runtime->metrics.cpi_setup_cum_ticks += (ulong)( regime1-regime0 );
     910             : 
     911             :   /* Set the CU meter to the instruction context's transaction context's compute meter,
     912             :      so that the caller can't use compute units that the callee has already used. */
     913        2829 :   vm->cu = vm->instr_ctx->txn_out->details.compute_budget.compute_meter;
     914             : 
     915        2829 :   *_ret = instr_exec_res;
     916             : 
     917             :   /* Errors are propagated in fd_execute_instr. */
     918        2829 :   if( FD_UNLIKELY( err_exec ) ) return err_exec;
     919             : 
     920             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L942-L957 */
     921        5334 :   for( ulong i=0UL; i<translated_accounts_len; i++ ) {
     922        3222 :     fd_vm_cpi_translated_account_t * translated_account = &translated_accounts[i];
     923        3222 :     fd_guarded_borrowed_account_t callee_acc = {0};
     924        3222 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( vm->instr_ctx, translated_account->index_in_caller, &callee_acc );
     925        3222 :     if( !translated_account->update_caller_account_info ) continue;
     926        2838 :     err = VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC( vm, &translated_account->caller_account, &callee_acc );
     927        2838 :     if( FD_UNLIKELY( err ) ) {
     928         165 :       return err;
     929         165 :     }
     930        2838 :   }
     931             : 
     932             :   /* With virtual_address_space_adjustments, update the caller's memory regions
     933             :      to reflect any changes the callee made to account data.
     934             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L959-L973 */
     935        2112 :   if( vm->virtual_address_space_adjustments ) {
     936        2370 :     for( ulong i=0UL; i<translated_accounts_len; i++ ) {
     937        1407 :       fd_vm_cpi_translated_account_t * translated_account = &translated_accounts[i];
     938        1407 :       fd_guarded_borrowed_account_t borrowed_callee_acc = {0};
     939        1407 :       err = fd_exec_instr_ctx_try_borrow_instr_account( vm->instr_ctx, translated_account->index_in_caller, &borrowed_callee_acc );
     940        1407 :       if( FD_UNLIKELY( err ) ) return err;
     941        1407 :       if( !translated_account->update_caller_account_region ) continue;
     942             : 
     943        1263 :       err = fd_vm_cpi_update_caller_account_region( vm, translated_account, &borrowed_callee_acc );
     944        1263 :       if( FD_UNLIKELY( err ) ) {
     945           0 :         return err;
     946           0 :       }
     947        1263 :     }
     948         963 :   }
     949             : 
     950        2112 :   long const regime3 = fd_tickcount();
     951        2112 :   vm->instr_ctx->runtime->metrics.cpi_commit_cum_ticks += (ulong)( regime3-regime2 );
     952             : 
     953        2112 :   return FD_VM_SUCCESS;
     954        2112 : }
     955             : 
     956             : #undef VM_SYSCALL_CPI_UPDATE_CALLER_ACC_FUNC
     957             : #undef VM_SYSCALL_CPI_FROM_ACC_INFO_FUNC
     958             : #undef VM_SYSCALL_CPI_TRANSLATE_AND_UPDATE_ACCOUNTS_FUNC
     959             : #undef VM_SYSCALL_CPI_INSTRUCTION_TO_INSTR_FUNC
     960             : #undef VM_SYSCALL_CPI_FUNC

Generated by: LCOV version 1.14