LCOV - code coverage report
Current view: top level - flamenco/vm/syscall - fd_vm_syscall_cpi.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 179 206 86.9 %
Date: 2026-08-06 05:23:51 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include "fd_vm_syscall.h"
       2             : #include "../../runtime/fd_borrowed_account.h"
       3             : #include "../../runtime/fd_system_ids.h"
       4             : #include "../../runtime/fd_executor.h"
       5             : 
       6             : /* FIXME: ALGO EFFICIENCY */
       7             : static inline int
       8             : fd_vm_syscall_cpi_is_signer( fd_pubkey_t const * account,
       9             :            fd_pubkey_t const * signers,
      10         102 :            ulong               signers_cnt ) {
      11         150 :   for( ulong i=0UL; i<signers_cnt; i++ ) if( !memcmp( account->uc, signers[i].uc, sizeof(fd_pubkey_t) ) ) return 1;
      12          48 :   return 0;
      13         102 : }
      14             : 
      15             : /*
      16             : fd_vm_prepare_instruction populates instruction_accounts and instruction_accounts_cnt
      17             : with the instruction accounts ready for execution.
      18             : 
      19             : The majority of this logic is taken from
      20             : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L311-L455,
      21             : and is not vm-specific, but a part of the runtime.
      22             : TODO: should we move this out of the CPI section?
      23             : 
      24             : The bulk of the logic is concerned with unifying the privileges for each duplicated account,
      25             : ensuring that each duplicate account referenced has the same privileges. It also performs some
      26             : privilege checks, for example ensuring the necessary signatures are present.
      27             : 
      28             : TODO: instruction calling convention: const parameters after non-const.
      29             : 
      30             : Assumptions:
      31             : - We do not have more than 256 unique accounts in the callee_instr.
      32             :   This limit comes from the fact that a Solana transaction cannot
      33             :   refefence more than 256 unique accounts, due to the transaction
      34             :   serialization format.
      35             : - callee_instr is not null.
      36             : - callee_instr->acct_pubkeys is at least as long as callee_instr->acct_cnt
      37             : - instr_ctx->txn_ctx->accounts_cnt is less than UCHAR_MAX.
      38             :   This is likely because the transaction is limited to 256 accounts.
      39             : - callee_instr->program_id is set to UCHAR_MAX if account is not in instr_ctx->txn_ctx.
      40             : - instruction_accounts is a 256-length empty array.
      41             : 
      42             : Parameters:
      43             : - callee_instr
      44             : - instr_ctx
      45             : - instruction_accounts
      46             : - instruction_accounts_cnt
      47             : - signers
      48             : - signers_cnt
      49             : 
      50             : Returns:
      51             : - instruction_accounts
      52             : - instruction_accounts_cnt
      53             : Populated with the instruction accounts with normalized permissions.
      54             : 
      55             : TODO: is it possible to pass the transaction indexes of the accounts in?
      56             : This would allow us to make some of these algorithms more efficient.
      57             : */
      58             : int
      59             : fd_vm_prepare_instruction( fd_instr_info_t *        callee_instr,
      60             :                            fd_exec_instr_ctx_t *    instr_ctx,
      61             :                            fd_pubkey_t const *      callee_program_id_pubkey,
      62             :                            fd_pubkey_t const        instr_acct_keys[ FD_TXN_INSTR_ACCT_MAX ],
      63             :                            fd_instruction_account_t instruction_accounts[ FD_TXN_INSTR_ACCT_MAX ],
      64             :                            ulong *                  instruction_accounts_cnt,
      65             :                            fd_pubkey_t const *      signers,
      66        3228 :                            ulong                    signers_cnt ) {
      67             : 
      68             :   /* De-duplicate the instruction accounts, using the same logic as Solana */
      69        3228 :   ulong deduplicated_instruction_accounts_cnt = 0;
      70        3228 :   fd_instruction_account_t deduplicated_instruction_accounts[ FD_TXN_INSTR_ACCT_MAX ] = {0};
      71        3228 :   ulong duplicate_indicies_cnt                                                                     = 0;
      72        3228 :   ulong duplicate_indices[ FD_TXN_INSTR_ACCT_MAX ]                                    = {0};
      73             : 
      74             :   /* This function is either called by a true CPI or by a native cpi invocation.
      75             :      The native CPI invocation is never called with more than 3 instruction
      76             :      accounts, and the true CPI is never called with more than
      77             :      FD_TXN_INSTR_ACCT_MAX. */
      78        3228 :   if( FD_UNLIKELY( callee_instr->acct_cnt > FD_TXN_INSTR_ACCT_MAX ) ) {
      79           0 :     FD_LOG_CRIT(( "invariant violation: too many accounts %u", callee_instr->acct_cnt ));
      80           0 :   }
      81             : 
      82             :   /* Normalize the privileges of each instruction account in the callee, after de-duping
      83             :      the account references.
      84             :     https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L327-L367 */
      85        7632 :   for( ulong i=0UL; i<callee_instr->acct_cnt; i++ ) {
      86        4404 :     ushort index_in_transaction = callee_instr->accounts[i].index_in_transaction;
      87        4404 :     ushort index_in_caller      = callee_instr->accounts[i].index_in_caller;
      88             : 
      89        4404 :     if( index_in_transaction==USHORT_MAX ) {
      90             :       /* In this case the callee instruction is referencing an unknown account not listed in the
      91             :          transactions accounts. */
      92           0 :       FD_BASE58_ENCODE_32_BYTES( instr_acct_keys[i].uc, id_b58 );
      93           0 :       fd_log_collector_msg_many( instr_ctx, 2, "Instruction references an unknown account ", 42UL, id_b58, id_b58_len );
      94           0 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
      95           0 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
      96           0 :     }
      97             : 
      98             :     /* If there was an instruction account before this one which referenced the same
      99             :        transaction account index, find it's index in the deduplicated_instruction_accounts
     100             :        array. */
     101        4404 :     ulong duplicate_index = ULONG_MAX;
     102        7650 :     for( ulong j=0UL; j<deduplicated_instruction_accounts_cnt; j++ ) {
     103        3390 :       if( deduplicated_instruction_accounts[j].index_in_transaction==index_in_transaction ) {
     104         144 :         duplicate_index = j;
     105         144 :         break;
     106         144 :       }
     107        3390 :     }
     108             : 
     109             :     /* If this was account referenced in a previous iteration, update the flags to include those set
     110             :        in this iteration. This ensures that after all the iterations, the de-duplicated account flags
     111             :        for each account are the union of all the flags in all the references to that account in this instruction. */
     112             : 
     113             :     /* TODO: FD_UNLIKELY? Need to check which branch is more common by running against a larger mainnet ledger */
     114             :     /* TODO: this code would maybe be easier to read if we inverted the branches */
     115        4404 :     if( duplicate_index!=ULONG_MAX ) {
     116         144 :       if ( FD_UNLIKELY( duplicate_index >= deduplicated_instruction_accounts_cnt ) ) {
     117           0 :         FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
     118           0 :         return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     119           0 :       }
     120             : 
     121         144 :       duplicate_indices[duplicate_indicies_cnt++] = duplicate_index;
     122         144 :       fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[duplicate_index];
     123         144 :       instruction_account->is_signer   = !!(instruction_account->is_signer   | callee_instr->accounts[i].is_signer);
     124         144 :       instruction_account->is_writable = !!(instruction_account->is_writable | callee_instr->accounts[i].is_writable);
     125        4260 :     } else {
     126             :       /* In the case where the callee instruction is NOT a duplicate, we need to
     127             :          create the deduplicated_instruction_accounts fd_instruction_account_t object. */
     128             : 
     129             :       /* Add the instruction account to the duplicate indices array */
     130        4260 :       duplicate_indices[duplicate_indicies_cnt++] = deduplicated_instruction_accounts_cnt;
     131             : 
     132             :       /* Initialize the instruction account in the deduplicated_instruction_accounts array */
     133        4260 :       fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[deduplicated_instruction_accounts_cnt++];
     134        4260 :       *instruction_account = fd_instruction_account_init( index_in_transaction,
     135        4260 :                                                           index_in_caller,
     136        4260 :                                                           (ushort)i,
     137        4260 :                                                           !!(callee_instr->accounts[i].is_writable),
     138        4260 :                                                           !!(callee_instr->accounts[i].is_signer) );
     139        4260 :     }
     140        4404 :   }
     141             : 
     142             :   /* Check the normalized account permissions for privilege escalation.
     143             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L369-L419 */
     144        7392 :   for( ulong i = 0; i < deduplicated_instruction_accounts_cnt; i++ ) {
     145        4260 :     fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[i];
     146             : 
     147             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L393-L403 */
     148        4260 :     ushort caller_idx                 = instruction_account->index_in_caller;
     149        4260 :     fd_pubkey_t const * caller_pubkey = NULL;
     150        4260 :     int err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, caller_idx, &caller_pubkey );
     151        4260 :     if( FD_UNLIKELY( err ) ) {
     152           0 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
     153           0 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     154           0 :     }
     155             : 
     156             :     /* Check that the account is not read-only in the caller but writable in the callee */
     157        4260 :     if( FD_UNLIKELY( instruction_account->is_writable && !fd_instr_acc_is_writable_idx( instr_ctx->instr, caller_idx ) ) ) {
     158          48 :       FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
     159          48 :       fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s writable privilege escalated", 31UL );
     160          48 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
     161          48 :       return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
     162          48 :     }
     163             : 
     164             :     /* If the account is signed in the callee, it must be signed by the caller or the program */
     165        4212 :     if ( FD_UNLIKELY( instruction_account->is_signer && !( fd_instr_acc_is_signer_idx( instr_ctx->instr, caller_idx, NULL ) || fd_vm_syscall_cpi_is_signer( caller_pubkey, signers, signers_cnt ) ) ) ) {
     166          48 :       FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
     167          48 :       fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s signer privilege escalated", 29UL );
     168          48 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
     169          48 :       return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
     170          48 :     }
     171        4212 :   }
     172             : 
     173             :   /* Copy the accounts with their normalized permissions over to the final instruction_accounts array,
     174             :      and set the callee_instr acct_flags. */
     175        7440 :   for (ulong i = 0; i < duplicate_indicies_cnt; i++) {
     176        4308 :     ulong duplicate_index = duplicate_indices[i];
     177             : 
     178             :     /* Failing this condition is technically impossible, but it is probably safest to keep this in
     179             :        so that we throw InstructionError::NotEnoughAccountKeys at the same point at Solana does,
     180             :        in the event any surrounding code is changed.
     181             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L345-L366 */
     182        4308 :     if ( FD_LIKELY( duplicate_index < deduplicated_instruction_accounts_cnt ) ) {
     183        4308 :       instruction_accounts[i] = deduplicated_instruction_accounts[duplicate_index];
     184        4308 :       callee_instr->accounts[i].is_writable = !!(instruction_accounts[i].is_writable);
     185        4308 :       callee_instr->accounts[i].is_signer   = !!(instruction_accounts[i].is_signer);
     186        4308 :     } else {
     187           0 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
     188           0 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     189           0 :     }
     190        4308 :   }
     191             : 
     192             :   /* Obtain the program account index and return a MissingAccount error if not found.
     193             :     https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L421-L436 */
     194        3132 :   ulong program_idx = fd_exec_instr_ctx_find_idx_of_instr_account( instr_ctx, callee_program_id_pubkey );
     195        3132 :   if( FD_UNLIKELY( program_idx==ULONG_MAX ) ) {
     196           0 :     FD_BASE58_ENCODE_32_BYTES( callee_program_id_pubkey->uc, id_b58 );
     197           0 :     fd_log_collector_msg_many( instr_ctx, 2, "Unknown program ", 16UL, id_b58, id_b58_len );
     198           0 :     FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
     199           0 :     return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     200           0 :   }
     201             : 
     202        3132 :   *instruction_accounts_cnt = duplicate_indicies_cnt;
     203             : 
     204        3132 :   return 0;
     205        3132 : }
     206             : 
     207             : /**********************************************************************
     208             :    CROSS PROGRAM INVOCATION (Generic logic)
     209             :  **********************************************************************/
     210             : 
     211             : /* FD_CPI_MAX_SIGNER_CNT is the max amount of PDA signer addresses that
     212             :    a cross-program invocation can include in an instruction.
     213             : 
     214             :    https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/mod.rs#L80 */
     215             : 
     216             : #define FD_CPI_MAX_SIGNER_CNT              (16UL)
     217             : 
     218             : /* "Maximum number of account info structs that can be used in a single CPI
     219             :    invocation. A limit on account info structs is effectively the same as
     220             :    limiting the number of unique accounts.
     221             : 
     222             :    https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/sdk/program/src/syscalls/mod.rs#L25
     223             :    https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L1011 */
     224             : 
     225        3087 : #define FD_CPI_MAX_ACCOUNT_INFOS (255UL)
     226             : 
     227             : /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/cpi.rs#L168-L180 */
     228             : static inline ulong
     229        3087 : get_cpi_max_account_infos( void ) {
     230        3087 :   return FD_CPI_MAX_ACCOUNT_INFOS;
     231        3087 : }
     232             : 
     233             : /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/execution_budget.rs#L25-L31 */
     234             : static inline ulong
     235        3249 : get_cpi_invoke_unit_cost( void ) {
     236        3249 :   return FD_VM_INVOKE_UNITS;
     237        3249 : }
     238             : 
     239             : /* fd_vm_syscall_cpi_check_instruction contains common instruction acct
     240             :    count and data sz checks.  Also consumes compute units proportional
     241             :    to instruction data size. */
     242             : 
     243             : static int
     244             : fd_vm_syscall_cpi_check_instruction( ulong           acct_cnt,
     245        3249 :                                      ulong           data_sz ) {
     246             :   /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/cpi.rs#L146-L161 */
     247        3249 :   if( FD_UNLIKELY( acct_cnt > FD_TXN_INSTR_ACCT_MAX ) ) {
     248             :     // SyscallError::MaxInstructionAccountsExceeded
     249           6 :     return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED;
     250           6 :   }
     251        3243 :   if( FD_UNLIKELY( data_sz>FD_RUNTIME_CPI_MAX_INSTR_DATA_LEN ) ) {
     252             :     // SyscallError::MaxInstructionDataLenExceeded
     253           0 :     return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_DATA_LEN_EXCEEDED;
     254           0 :   }
     255             : 
     256        3243 :   return FD_VM_SUCCESS;
     257        3243 : }
     258             : 
     259             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1276-L1311 */
     260             : static inline int
     261             : fd_vm_cpi_update_caller_account_region( fd_vm_t *                              vm,
     262             :                                         fd_vm_cpi_translated_account_t const * translated_account,
     263        1263 :                                         fd_borrowed_account_t *                borrowed_account ) {
     264        1263 :   fd_vm_cpi_caller_account_t const * caller_account = &translated_account->caller_account;
     265             : 
     266             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1283-L1290 */
     267        1263 :   ulong address_space_reserved_for_account;
     268        1263 :   if( vm->is_deprecated ) {
     269         570 :     address_space_reserved_for_account = caller_account->orig_data_len;
     270         693 :   } else {
     271         693 :     address_space_reserved_for_account = fd_ulong_sat_add( caller_account->orig_data_len, MAX_PERMITTED_DATA_INCREASE );
     272         693 :   }
     273             : 
     274             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1292-L1308 */
     275        1263 :   if( address_space_reserved_for_account > 0UL ) {
     276             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1295-L1297 */
     277        1251 :     fd_vm_acc_region_meta_t * acc_region_meta = &vm->acc_region_metas[ translated_account->index_in_caller ];
     278        1251 :     fd_vm_input_region_t *    region          = &vm->input_mem_regions[ acc_region_meta->region_idx + 1UL ];
     279             : 
     280             :     /* Note that we don't special-case direct mapping here, as Agave does,
     281             :        because we do not create regions using CoW upon resize like Agave does.
     282             : 
     283             :        Therefore we do not need the logic in the Agave code to create a new
     284             :        region, as we have already created all the regions for each account.
     285             : 
     286             :        Therefore we do not have equivalents of Agave's
     287             :        modify_memory_region_of_account and create_memory_region_of_account
     288             :        functions, but we instead inline this logic directly below. */
     289             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1301-L1307 */
     290             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/serialization.rs#L23-L35 */
     291        1251 :     region->region_sz = (uint)fd_borrowed_account_get_data_len( borrowed_account );
     292             : 
     293        1251 :     int err;
     294        1251 :     int is_writable = fd_borrowed_account_can_data_be_changed( borrowed_account, &err );
     295             : 
     296        1251 :     region->is_writable = (uchar)is_writable && ( err == FD_EXECUTOR_INSTR_SUCCESS );
     297        1251 :   }
     298             : 
     299        1263 :   return FD_VM_SUCCESS;
     300        1263 : }
     301             : 
     302             : /**********************************************************************
     303             :   CROSS PROGRAM INVOCATION HELPERS
     304             :  **********************************************************************/
     305             : 
     306             : static inline int
     307             : fd_vm_syscall_cpi_check_id( fd_pubkey_t const * program_id,
     308       22575 :           uchar const * loader ) {
     309       22575 :   return !memcmp( program_id, loader, sizeof(fd_pubkey_t) );
     310       22575 : }
     311             : 
     312             : /* fd_vm_syscall_cpi_is_precompile returns true if the given program_id
     313             :    corresponds to a precompile. It does this by checking against a hardcoded
     314             :    list of known pre-compiles.
     315             : 
     316             :    This mirrors the behaviour in solana_sdk::precompiles::is_precompile
     317             :    https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/sdk/src/precompiles.rs#L93
     318             :  */
     319             : static inline int
     320        3225 : fd_vm_syscall_cpi_is_precompile( fd_pubkey_t const * program_id ) {
     321        3225 :   return fd_vm_syscall_cpi_check_id(program_id, fd_solana_keccak_secp_256k_program_id.key) |
     322        3225 :          fd_vm_syscall_cpi_check_id(program_id, fd_solana_ed25519_sig_verify_program_id.key) |
     323        3225 :          fd_vm_syscall_cpi_check_id(program_id, fd_solana_secp256r1_program_id.key);
     324        3225 : }
     325             : 
     326             : /* fd_vm_syscall_cpi_check_authorized_program corresponds to
     327             : solana_bpf_loader_program::syscalls::cpi::check_authorized_program:
     328             : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1032
     329             : 
     330             : It determines if the given program_id is authorized to execute a CPI call.
     331             : Returns 1 if authorized, 0 otherwise.
     332             : */
     333             : static inline int
     334             : fd_vm_syscall_cpi_check_authorized_program( fd_pubkey_t const * program_id,
     335             :                                             fd_bank_t *         bank,
     336             :                                             uchar const *       instruction_data,
     337        3225 :                                             ulong               instruction_data_len ) {
     338             :   /* FIXME: do this in a branchless manner? using bitwise comparison would probably be faster */
     339        3225 :   return !( fd_vm_syscall_cpi_check_id(program_id, fd_solana_native_loader_id.key) ||
     340        3225 :            fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_program_id.key) ||
     341        3225 :            fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_deprecated_program_id.key) ||
     342        3225 :            ( fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_upgradeable_program_id.key) &&
     343        3225 :              !(( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_UPGRADE ) ||
     344           0 :                ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY ) ||
     345           0 :                ( FD_FEATURE_ACTIVE_BANK( bank, enable_bpf_loader_set_authority_checked_ix ) &&
     346           0 :                  ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY_CHECKED )) ||
     347           0 :                ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_CLOSE ))) ||
     348        3225 :            fd_vm_syscall_cpi_is_precompile( program_id ) );
     349        3225 : }
     350             : 
     351             : /* The data and lamports fields are in an Rc<Refcell<T>> in the Rust ABI AccountInfo.
     352             :    These macros perform the equivalent of Rc<Refcell<T>>.as_ptr() in Agave.
     353             :    This function doesn't actually touch any memory.
     354             :    It performs pointer arithmetic.
     355             :  */
     356             : FD_FN_CONST static inline
     357       11685 : ulong vm_syscall_cpi_acc_info_rc_refcell_as_ptr( ulong rc_refcell_vaddr ) {
     358       11685 :   return rc_refcell_vaddr + offsetof(fd_vm_rc_refcell_t, payload);
     359       11685 : }
     360             : 
     361             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L508-L514
     362             :  */
     363             : FD_FN_CONST static inline
     364        1830 : ulong vm_syscall_cpi_data_len_vaddr_c( ulong acct_info_vaddr, ulong data_len_haddr, ulong acct_info_haddr ) {
     365        1830 :   return fd_ulong_sat_sub( fd_ulong_sat_add( acct_info_vaddr, data_len_haddr ), acct_info_haddr );
     366        1830 : }
     367             : 
     368             : /**********************************************************************
     369             :   CROSS PROGRAM INVOCATION (C ABI)
     370             :  **********************************************************************/
     371             : 
     372             : #define VM_SYSCALL_CPI_ABI                     c
     373        1563 : #define VM_SYSCALL_CPI_INSTR_T                 fd_vm_c_instruction_t
     374             : #define VM_SYSCALL_CPI_INSTR_ALIGN             (FD_VM_C_INSTRUCTION_ALIGN)
     375             : #define VM_SYSCALL_CPI_INSTR_SIZE              (FD_VM_C_INSTRUCTION_SIZE)
     376        5829 : #define VM_SYSCALL_CPI_ACC_META_T              fd_vm_c_account_meta_t
     377             : #define VM_SYSCALL_CPI_ACC_META_ALIGN          (FD_VM_C_ACCOUNT_META_ALIGN)
     378             : #define VM_SYSCALL_CPI_ACC_META_SIZE           (FD_VM_C_ACCOUNT_META_SIZE)
     379        1482 : #define VM_SYSCALL_CPI_ACC_INFO_T              fd_vm_c_account_info_t
     380             : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN          (FD_VM_C_ACCOUNT_INFO_ALIGN)
     381        3330 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE           (FD_VM_C_ACCOUNT_INFO_SIZE)
     382             : 
     383             : /* VM_SYSCALL_CPI_INSTR_T accessors */
     384             : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data_addr
     385        4671 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr )  instr->data_len
     386             : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts_addr
     387       12045 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr )  instr->accounts_len
     388             : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) \
     389        1563 :   FD_VM_MEM_HADDR_LD( vm, instr->program_id_addr, alignof(uchar), sizeof(fd_pubkey_t)  )
     390             : 
     391             : /* VM_SYSCALL_CPI_ACC_META_T accessors */
     392        4254 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
     393        4254 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
     394             : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) \
     395        4266 :   FD_VM_MEM_HADDR_LD( vm, acc_meta->pubkey_addr, alignof(uchar), sizeof(fd_pubkey_t) )
     396             : 
     397             : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
     398             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl ) \
     399        1887 :   ulong decl = acc_info->lamports_addr;
     400             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl ) \
     401        1869 :   ulong * decl = FD_VM_MEM_HADDR_ST( vm, acc_info->lamports_addr, alignof(ulong), sizeof(ulong) );
     402             : 
     403             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L304 */
     404             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl ) \
     405        1866 :   ulong decl = acc_info->data_addr;
     406             : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl ) \
     407         936 :   uchar * decl = FD_VM_MEM_SLICE_HADDR_ST( vm, acc_info->data_addr, alignof(uchar), acc_info->data_sz ); \
     408         930 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
     409         930 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     410             : 
     411             : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl ) \
     412             :   ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
     413             :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     414             : 
     415             : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
     416         492 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     417             : 
     418             : #include "fd_vm_syscall_cpi_common.c"
     419             : 
     420             : #undef VM_SYSCALL_CPI_ABI
     421             : #undef VM_SYSCALL_CPI_INSTR_T
     422             : #undef VM_SYSCALL_CPI_INSTR_ALIGN
     423             : #undef VM_SYSCALL_CPI_INSTR_SIZE
     424             : #undef VM_SYSCALL_CPI_ACC_META_T
     425             : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
     426             : #undef VM_SYSCALL_CPI_ACC_META_SIZE
     427             : #undef VM_SYSCALL_CPI_ACC_INFO_T
     428             : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
     429             : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
     430             : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
     431             : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
     432             : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
     433             : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
     434             : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
     435             : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
     436             : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
     437             : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
     438             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
     439             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
     440             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
     441             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
     442             : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
     443             : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN
     444             : 
     445             : /**********************************************************************
     446             :    CROSS PROGRAM INVOCATION (Rust ABI)
     447             :  **********************************************************************/
     448             : 
     449             : #define VM_SYSCALL_CPI_ABI                     rust
     450        1686 : #define VM_SYSCALL_CPI_INSTR_T                 fd_vm_rust_instruction_t
     451             : #define VM_SYSCALL_CPI_INSTR_ALIGN             (FD_VM_RUST_INSTRUCTION_ALIGN)
     452             : #define VM_SYSCALL_CPI_INSTR_SIZE              (FD_VM_RUST_INSTRUCTION_SIZE)
     453        6204 : #define VM_SYSCALL_CPI_ACC_META_T              fd_vm_rust_account_meta_t
     454             : #define VM_SYSCALL_CPI_ACC_META_ALIGN          (FD_VM_RUST_ACCOUNT_META_ALIGN)
     455             : #define VM_SYSCALL_CPI_ACC_META_SIZE           (FD_VM_RUST_ACCOUNT_META_SIZE)
     456        1605 : #define VM_SYSCALL_CPI_ACC_INFO_T              fd_vm_rust_account_info_t
     457             : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN          (FD_VM_RUST_ACCOUNT_INFO_ALIGN)
     458        1623 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE           (FD_VM_RUST_ACCOUNT_INFO_SIZE)
     459             : 
     460             : /* VM_SYSCALL_CPI_INSTR_T accessors */
     461             : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data.addr
     462        5040 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr )  instr->data.len
     463             : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts.addr
     464       12912 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr )  instr->accounts.len
     465        1686 : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) instr->pubkey
     466             : 
     467             : /* VM_SYSCALL_CPI_ACC_META_T accessors */
     468        4506 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
     469        4506 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
     470        4518 : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) acc_meta->pubkey
     471             : 
     472             : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
     473             : 
     474             : /* The lamports and the account data are stored behind RefCells,
     475             :    so we have an additional layer of indirection to unwrap. */
     476             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl )                                                                             \
     477        2013 :     ulong const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                         \
     478        2013 :       FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(ulong) ); \
     479        2013 :     /* Extract the vaddr embedded in the RefCell */                                                                                              \
     480        2013 :     ulong decl = *FD_EXPAND_THEN_CONCAT2(decl, _hptr_);
     481             : 
     482             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L184-L195 */
     483             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl )                                                                                                     \
     484        1977 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vaddr_) =                                                                                                                  \
     485        1977 :       *((ulong const *)FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(ulong) )); \
     486        1977 :     ulong * decl = FD_VM_MEM_HADDR_ST( vm, FD_EXPAND_THEN_CONCAT2(decl, _vaddr_), alignof(ulong), sizeof(ulong) );
     487             : 
     488             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L367-L378 */
     489             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl )                                                                                   \
     490        1968 :     if( FD_UNLIKELY( vm->syscall_parameter_address_restrictions &&                                                                                 \
     491        1968 :                      vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ) >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) {                \
     492          18 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );                                                                          \
     493          18 :       return FD_VM_SYSCALL_ERR_INVALID_POINTER;                                                                                                    \
     494          18 :     }                                                                                                                                              \
     495        1968 :     /* Translate the vaddr to the slice */                                                                                                         \
     496        1968 :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     497        1950 :       FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
     498        1950 :     /* Extract the vaddr embedded in the slice */                                                                                                  \
     499        1950 :     ulong decl = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;
     500             : 
     501             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L212-L221 */
     502             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR( vm, acc_info, decl ) \
     503        1908 :     ulong decl = fd_ulong_sat_add( vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), sizeof(ulong) );
     504             : 
     505             : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl )                                                                                         \
     506             :     /* Translate the vaddr to the slice */                                                                                                         \
     507        1011 :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     508        1011 :       FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
     509        1011 :     /* Declare the vaddr of the slice's underlying byte array */                                                                                   \
     510        1011 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;                                                     \
     511        1011 :     /* Declare the size of the slice's underlying byte array */                                                                                    \
     512        1011 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;                                                          \
     513        1011 :     /* Translate the vaddr to the underlying byte array */                                                                                         \
     514        1011 :     uchar * decl = FD_VM_MEM_SLICE_HADDR_ST(                                                                                                       \
     515        1005 :       vm, FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr, alignof(uchar), FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len );
     516             : 
     517             : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl )                                                                                     \
     518             :     /* Translate the vaddr to the slice */                                                                                                         \
     519             :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     520             :       FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
     521             :     /* Declare the vaddr of the slice's underlying byte array */                                                                                   \
     522             :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;                                                     \
     523             :     /* Declare the size of the slice's underlying byte array */                                                                                    \
     524             :     ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
     525             : 
     526             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR( vm, acc_info, decl ) \
     527        1431 :     ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr );
     528             : 
     529             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR( vm, acc_info, decl ) \
     530             :     ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr );
     531             : 
     532             : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
     533         573 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
     534             : 
     535             : #include "fd_vm_syscall_cpi_common.c"
     536             : 
     537             : #undef VM_SYSCALL_CPI_ABI
     538             : #undef VM_SYSCALL_CPI_INSTR_T
     539             : #undef VM_SYSCALL_CPI_INSTR_ALIGN
     540             : #undef VM_SYSCALL_CPI_INSTR_SIZE
     541             : #undef VM_SYSCALL_CPI_ACC_META_T
     542             : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
     543             : #undef VM_SYSCALL_CPI_ACC_META_SIZE
     544             : #undef VM_SYSCALL_CPI_ACC_INFO_T
     545             : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
     546             : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
     547             : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
     548             : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
     549             : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
     550             : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
     551             : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
     552             : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
     553             : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
     554             : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
     555             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
     556             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
     557             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
     558             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
     559             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR
     560             : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
     561             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR
     562             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR
     563             : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN

Generated by: LCOV version 1.14