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: 188 206 91.3 %
Date: 2026-10-22 18:10:43 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         382 :            ulong               signers_cnt ) {
      11         390 :   for( ulong i=0UL; i<signers_cnt; i++ ) if( !memcmp( account->uc, signers[i].uc, sizeof(fd_pubkey_t) ) ) return 1;
      12           7 :   return 0;
      13         382 : }
      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        2031 :                            ulong                    signers_cnt ) {
      67             : 
      68             :   /* De-duplicate the instruction accounts, using the same logic as Solana */
      69        2031 :   ulong deduplicated_instruction_accounts_cnt = 0;
      70        2031 :   fd_instruction_account_t deduplicated_instruction_accounts[ FD_TXN_INSTR_ACCT_MAX ] = {0};
      71        2031 :   ulong duplicate_indicies_cnt                                                                     = 0;
      72        2031 :   ulong duplicate_indices[ FD_TXN_INSTR_ACCT_MAX ]                                    = {0};
      73        2031 :   uchar dedup_idx[ MAX_TX_ACCOUNT_LOCKS ]; /* index_in_transaction -> deduplicated index, UCHAR_MAX if none */
      74        2031 :   memset( dedup_idx, 0xff, sizeof(dedup_idx) );
      75             : 
      76             :   /* This function is either called by a true CPI or by a native cpi invocation.
      77             :      The native CPI invocation is never called with more than 3 instruction
      78             :      accounts, and the true CPI is never called with more than
      79             :      FD_TXN_INSTR_ACCT_MAX. */
      80        2031 :   if( FD_UNLIKELY( callee_instr->acct_cnt > FD_TXN_INSTR_ACCT_MAX ) ) {
      81           0 :     FD_LOG_CRIT(( "invariant violation: too many accounts %u", callee_instr->acct_cnt ));
      82           0 :   }
      83             : 
      84             :   /* Normalize the privileges of each instruction account in the callee, after de-duping
      85             :      the account references.
      86             :     https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L327-L367 */
      87        7243 :   for( ulong i=0UL; i<callee_instr->acct_cnt; i++ ) {
      88        5332 :     ushort index_in_transaction = callee_instr->accounts[i].index_in_transaction;
      89        5332 :     ushort index_in_caller      = callee_instr->accounts[i].index_in_caller;
      90             : 
      91        5332 :     if( index_in_transaction==USHORT_MAX ) {
      92             :       /* In this case the callee instruction is referencing an unknown account not listed in the
      93             :          transactions accounts. */
      94         120 :       FD_BASE58_ENCODE_32_BYTES( instr_acct_keys[i].uc, id_b58 );
      95         120 :       fd_log_collector_msg_many( instr_ctx, 2, "Instruction references an unknown account ", 42UL, id_b58, id_b58_len );
      96         120 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
      97         120 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
      98         120 :     }
      99             : 
     100             :     /* If there was an instruction account before this one which referenced the same
     101             :        transaction account index, find it's index in the deduplicated_instruction_accounts
     102             :        array. */
     103        5212 :     ulong duplicate_index = dedup_idx[ index_in_transaction ]!=UCHAR_MAX ? (ulong)dedup_idx[ index_in_transaction ] : ULONG_MAX;
     104             : 
     105             :     /* If this was account referenced in a previous iteration, update the flags to include those set
     106             :        in this iteration. This ensures that after all the iterations, the de-duplicated account flags
     107             :        for each account are the union of all the flags in all the references to that account in this instruction. */
     108             : 
     109             :     /* TODO: FD_UNLIKELY? Need to check which branch is more common by running against a larger mainnet ledger */
     110             :     /* TODO: this code would maybe be easier to read if we inverted the branches */
     111        5212 :     if( duplicate_index!=ULONG_MAX ) {
     112         227 :       if ( FD_UNLIKELY( duplicate_index >= deduplicated_instruction_accounts_cnt ) ) {
     113           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 );
     114           0 :         return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     115           0 :       }
     116             : 
     117         227 :       duplicate_indices[duplicate_indicies_cnt++] = duplicate_index;
     118         227 :       fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[duplicate_index];
     119         227 :       instruction_account->is_signer   = !!(instruction_account->is_signer   | callee_instr->accounts[i].is_signer);
     120         227 :       instruction_account->is_writable = !!(instruction_account->is_writable | callee_instr->accounts[i].is_writable);
     121        4985 :     } else {
     122             :       /* In the case where the callee instruction is NOT a duplicate, we need to
     123             :          create the deduplicated_instruction_accounts fd_instruction_account_t object. */
     124             : 
     125             :       /* Add the instruction account to the duplicate indices array */
     126        4985 :       dedup_idx[ index_in_transaction ] = (uchar)deduplicated_instruction_accounts_cnt;
     127        4985 :       duplicate_indices[duplicate_indicies_cnt++] = deduplicated_instruction_accounts_cnt;
     128             : 
     129             :       /* Initialize the instruction account in the deduplicated_instruction_accounts array */
     130        4985 :       fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[deduplicated_instruction_accounts_cnt++];
     131        4985 :       *instruction_account = fd_instruction_account_init( index_in_transaction,
     132        4985 :                                                           index_in_caller,
     133        4985 :                                                           (ushort)i,
     134        4985 :                                                           !!(callee_instr->accounts[i].is_writable),
     135        4985 :                                                           !!(callee_instr->accounts[i].is_signer) );
     136        4985 :     }
     137        5212 :   }
     138             : 
     139             :   /* Check the normalized account permissions for privilege escalation.
     140             :      https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L369-L419 */
     141        5679 :   for( ulong i = 0; i < deduplicated_instruction_accounts_cnt; i++ ) {
     142        4067 :     fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[i];
     143             : 
     144             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L393-L403 */
     145        4067 :     ushort caller_idx                 = instruction_account->index_in_caller;
     146        4067 :     fd_pubkey_t const * caller_pubkey = NULL;
     147        4067 :     int err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, caller_idx, &caller_pubkey );
     148        4067 :     if( FD_UNLIKELY( err ) ) {
     149           1 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
     150           1 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     151           1 :     }
     152             : 
     153             :     /* Check that the account is not read-only in the caller but writable in the callee */
     154        4066 :     if( FD_UNLIKELY( instruction_account->is_writable && !fd_instr_acc_is_writable_idx( instr_ctx->instr, caller_idx ) ) ) {
     155         291 :       FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
     156         291 :       fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s writable privilege escalated", 31UL );
     157         291 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
     158         291 :       return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
     159         291 :     }
     160             : 
     161             :     /* If the account is signed in the callee, it must be signed by the caller or the program */
     162        3775 :     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 ) ) ) ) {
     163           7 :       FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
     164           7 :       fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s signer privilege escalated", 29UL );
     165           7 :       FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
     166           7 :       return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
     167           7 :     }
     168        3775 :   }
     169             : 
     170             :   /* Copy the accounts with their normalized permissions over to the final instruction_accounts array,
     171             :      and set the callee_instr acct_flags. */
     172        5543 :   for (ulong i = 0; i < duplicate_indicies_cnt; i++) {
     173        3931 :     ulong duplicate_index = duplicate_indices[i];
     174             : 
     175             :     /* Failing this condition is technically impossible, but it is probably safest to keep this in
     176             :        so that we throw InstructionError::NotEnoughAccountKeys at the same point at Solana does,
     177             :        in the event any surrounding code is changed.
     178             :        https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L345-L366 */
     179        3931 :     if ( FD_LIKELY( duplicate_index < deduplicated_instruction_accounts_cnt ) ) {
     180        3931 :       instruction_accounts[i] = deduplicated_instruction_accounts[duplicate_index];
     181        3931 :       callee_instr->accounts[i].is_writable = !!(instruction_accounts[i].is_writable);
     182        3931 :       callee_instr->accounts[i].is_signer   = !!(instruction_accounts[i].is_signer);
     183        3931 :     } else {
     184           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 );
     185           0 :       return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     186           0 :     }
     187        3931 :   }
     188             : 
     189             :   /* Obtain the program account index and return a MissingAccount error if not found.
     190             :     https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L421-L436 */
     191        1612 :   ushort caller_map[ MAX_TX_ACCOUNT_LOCKS ];
     192        1612 :   fd_exec_instr_ctx_instr_account_map( instr_ctx, caller_map );
     193        1612 :   ulong program_idx_in_txn = fd_runtime_find_index_of_account( instr_ctx->txn_out, callee_program_id_pubkey );
     194        1612 :   ulong program_idx        = program_idx_in_txn!=ULONG_MAX && caller_map[ program_idx_in_txn ]!=USHORT_MAX ? caller_map[ program_idx_in_txn ] : ULONG_MAX;
     195        1612 :   if( FD_UNLIKELY( program_idx==ULONG_MAX ) ) {
     196          16 :     FD_BASE58_ENCODE_32_BYTES( callee_program_id_pubkey->uc, id_b58 );
     197          16 :     fd_log_collector_msg_many( instr_ctx, 2, "Unknown program ", 16UL, id_b58, id_b58_len );
     198          16 :     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          16 :     return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
     200          16 :   }
     201             : 
     202        1596 :   *instruction_accounts_cnt = duplicate_indicies_cnt;
     203             : 
     204        1596 :   return 0;
     205        1612 : }
     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        1330 : #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        1330 : get_cpi_max_account_infos( void ) {
     230        1330 :   return FD_CPI_MAX_ACCOUNT_INFOS;
     231        1330 : }
     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        2139 : get_cpi_invoke_unit_cost( void ) {
     236        2139 :   return FD_VM_INVOKE_UNITS;
     237        2139 : }
     238             : 
     239             : /* fd_vm_syscall_cpi_check_instruction contains common instruction acct
     240             :    count and data sz checks. */
     241             : 
     242             : static int
     243             : fd_vm_syscall_cpi_check_instruction( ulong           acct_cnt,
     244        1988 :                                      ulong           data_sz ) {
     245             :   /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/cpi.rs#L146-L161 */
     246        1988 :   if( FD_UNLIKELY( acct_cnt > FD_TXN_INSTR_ACCT_MAX ) ) {
     247             :     // SyscallError::MaxInstructionAccountsExceeded
     248           0 :     return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED;
     249           0 :   }
     250        1988 :   if( FD_UNLIKELY( data_sz>FD_RUNTIME_CPI_MAX_INSTR_DATA_LEN ) ) {
     251             :     // SyscallError::MaxInstructionDataLenExceeded
     252           0 :     return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_DATA_LEN_EXCEEDED;
     253           0 :   }
     254             : 
     255        1988 :   return FD_VM_SUCCESS;
     256        1988 : }
     257             : 
     258             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1276-L1311 */
     259             : static inline int
     260             : fd_vm_cpi_update_caller_account_region( fd_vm_t *                              vm,
     261             :                                         fd_vm_cpi_translated_account_t const * translated_account,
     262          13 :                                         fd_borrowed_account_t *                borrowed_account ) {
     263          13 :   fd_vm_cpi_caller_account_t const * caller_account = &translated_account->caller_account;
     264             : 
     265             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1283-L1290 */
     266          13 :   ulong address_space_reserved_for_account;
     267          13 :   if( vm->is_deprecated ) {
     268           0 :     address_space_reserved_for_account = caller_account->orig_data_len;
     269          13 :   } else {
     270          13 :     address_space_reserved_for_account = fd_ulong_sat_add( caller_account->orig_data_len, MAX_PERMITTED_DATA_INCREASE );
     271          13 :   }
     272             : 
     273             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1292-L1308 */
     274          13 :   if( address_space_reserved_for_account > 0UL ) {
     275             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1295-L1297 */
     276          13 :     fd_vm_acc_region_meta_t * acc_region_meta = &vm->acc_region_metas[ translated_account->index_in_caller ];
     277          13 :     fd_vm_input_region_t *    region          = &vm->input_mem_regions[ acc_region_meta->region_idx + 1UL ];
     278             : 
     279             :     /* Note that we don't special-case direct mapping here, as Agave does,
     280             :        because we do not create regions using CoW upon resize like Agave does.
     281             : 
     282             :        Therefore we do not need the logic in the Agave code to create a new
     283             :        region, as we have already created all the regions for each account.
     284             : 
     285             :        Therefore we do not have equivalents of Agave's
     286             :        modify_memory_region_of_account and create_memory_region_of_account
     287             :        functions, but we instead inline this logic directly below. */
     288             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1301-L1307 */
     289             :     /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/serialization.rs#L23-L35 */
     290          13 :     region->region_sz = (uint)fd_borrowed_account_get_data_len( borrowed_account );
     291             : 
     292          13 :     int err;
     293          13 :     int is_writable = fd_borrowed_account_can_data_be_changed( borrowed_account, &err );
     294             : 
     295          13 :     region->is_writable = (uchar)is_writable && ( err == FD_EXECUTOR_INSTR_SUCCESS );
     296          13 :   }
     297             : 
     298          13 :   return FD_VM_SUCCESS;
     299          13 : }
     300             : 
     301             : /**********************************************************************
     302             :   CROSS PROGRAM INVOCATION HELPERS
     303             :  **********************************************************************/
     304             : 
     305             : static inline int
     306             : fd_vm_syscall_cpi_check_id( fd_pubkey_t const * program_id,
     307       12912 :           uchar const * loader ) {
     308       12912 :   return !memcmp( program_id, loader, sizeof(fd_pubkey_t) );
     309       12912 : }
     310             : 
     311             : /* fd_vm_syscall_cpi_is_precompile returns true if the given program_id
     312             :    corresponds to a precompile. It does this by checking against a hardcoded
     313             :    list of known pre-compiles.
     314             : 
     315             :    This mirrors the behaviour in solana_sdk::precompiles::is_precompile
     316             :    https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/sdk/src/precompiles.rs#L93
     317             :  */
     318             : static inline int
     319        1844 : fd_vm_syscall_cpi_is_precompile( fd_pubkey_t const * program_id ) {
     320        1844 :   return fd_vm_syscall_cpi_check_id(program_id, fd_solana_keccak_secp_256k_program_id.key) |
     321        1844 :          fd_vm_syscall_cpi_check_id(program_id, fd_solana_ed25519_sig_verify_program_id.key) |
     322        1844 :          fd_vm_syscall_cpi_check_id(program_id, fd_solana_secp256r1_program_id.key);
     323        1844 : }
     324             : 
     325             : /* fd_vm_syscall_cpi_check_authorized_program corresponds to
     326             : solana_bpf_loader_program::syscalls::cpi::check_authorized_program:
     327             : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1032
     328             : 
     329             : It determines if the given program_id is authorized to execute a CPI call.
     330             : Returns 1 if authorized, 0 otherwise.
     331             : */
     332             : static inline int
     333             : fd_vm_syscall_cpi_check_authorized_program( fd_pubkey_t const * program_id,
     334             :                                             fd_bank_t *         bank,
     335             :                                             uchar const *       instruction_data,
     336        1845 :                                             ulong               instruction_data_len ) {
     337             :   /* FIXME: do this in a branchless manner? using bitwise comparison would probably be faster */
     338        1845 :   return !( fd_vm_syscall_cpi_check_id(program_id, fd_solana_native_loader_id.key) ||
     339        1845 :            fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_program_id.key) ||
     340        1845 :            fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_deprecated_program_id.key) ||
     341        1845 :            ( fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_upgradeable_program_id.key) &&
     342        1845 :              !(( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_UPGRADE ) ||
     343           3 :                ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY ) ||
     344           3 :                ( FD_FEATURE_ACTIVE_BANK( bank, enable_bpf_loader_set_authority_checked_ix ) &&
     345           1 :                  ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY_CHECKED )) ||
     346           3 :                ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_CLOSE ))) ||
     347        1845 :            fd_vm_syscall_cpi_is_precompile( program_id ) );
     348        1845 : }
     349             : 
     350             : /* The data and lamports fields are in an Rc<Refcell<T>> in the Rust ABI AccountInfo.
     351             :    These macros perform the equivalent of Rc<Refcell<T>>.as_ptr() in Agave.
     352             :    This function doesn't actually touch any memory.
     353             :    It performs pointer arithmetic.
     354             :  */
     355             : FD_FN_CONST static inline
     356        6529 : ulong vm_syscall_cpi_acc_info_rc_refcell_as_ptr( ulong rc_refcell_vaddr ) {
     357        6529 :   return rc_refcell_vaddr + offsetof(fd_vm_rc_refcell_t, payload);
     358        6529 : }
     359             : 
     360             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L508-L514
     361             :  */
     362             : FD_FN_CONST static inline
     363         185 : ulong vm_syscall_cpi_data_len_vaddr_c( ulong acct_info_vaddr, ulong data_len_haddr, ulong acct_info_haddr ) {
     364         185 :   return fd_ulong_sat_sub( fd_ulong_sat_add( acct_info_vaddr, data_len_haddr ), acct_info_haddr );
     365         185 : }
     366             : 
     367             : /**********************************************************************
     368             :   CROSS PROGRAM INVOCATION (C ABI)
     369             :  **********************************************************************/
     370             : 
     371             : #define VM_SYSCALL_CPI_ABI                     c
     372        1223 : #define VM_SYSCALL_CPI_INSTR_T                 fd_vm_c_instruction_t
     373             : #define VM_SYSCALL_CPI_INSTR_ALIGN             (FD_VM_C_INSTRUCTION_ALIGN)
     374             : #define VM_SYSCALL_CPI_INSTR_SIZE              (FD_VM_C_INSTRUCTION_SIZE)
     375        6401 : #define VM_SYSCALL_CPI_ACC_META_T              fd_vm_c_account_meta_t
     376             : #define VM_SYSCALL_CPI_ACC_META_ALIGN          (FD_VM_C_ACCOUNT_META_ALIGN)
     377             : #define VM_SYSCALL_CPI_ACC_META_SIZE           (FD_VM_C_ACCOUNT_META_SIZE)
     378         840 : #define VM_SYSCALL_CPI_ACC_INFO_T              fd_vm_c_account_info_t
     379             : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN          (FD_VM_C_ACCOUNT_INFO_ALIGN)
     380        1039 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE           (FD_VM_C_ACCOUNT_INFO_SIZE)
     381             : 
     382             : /* VM_SYSCALL_CPI_INSTR_T accessors */
     383             : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data_addr
     384        3458 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr )  instr->data_len
     385             : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts_addr
     386       10910 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr )  instr->accounts_len
     387             : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) \
     388        1223 :   FD_VM_MEM_HADDR_LD( vm, instr->program_id_addr, alignof(uchar), sizeof(fd_pubkey_t)  )
     389             : 
     390             : /* VM_SYSCALL_CPI_ACC_META_T accessors */
     391        4248 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
     392        4248 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
     393             : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) \
     394        5217 :   FD_VM_MEM_HADDR_LD( vm, acc_meta->pubkey_addr, alignof(uchar), sizeof(fd_pubkey_t) )
     395             : 
     396             : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
     397             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl ) \
     398         185 :   ulong decl = acc_info->lamports_addr;
     399             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl ) \
     400         185 :   void * decl = FD_VM_MEM_HADDR_ST( vm, acc_info->lamports_addr, alignof(ulong), sizeof(ulong) );
     401             : 
     402             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L304 */
     403             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl ) \
     404         185 :   ulong decl = acc_info->data_addr;
     405             : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl ) \
     406         171 :   uchar * decl = FD_VM_MEM_SLICE_HADDR_ST( vm, acc_info->data_addr, alignof(uchar), acc_info->data_sz ); \
     407         171 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
     408         171 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     409             : 
     410             : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl ) \
     411             :   ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
     412             :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     413             : 
     414             : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
     415           0 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
     416             : 
     417             : #include "fd_vm_syscall_cpi_common.c"
     418             : 
     419             : #undef VM_SYSCALL_CPI_ABI
     420             : #undef VM_SYSCALL_CPI_INSTR_T
     421             : #undef VM_SYSCALL_CPI_INSTR_ALIGN
     422             : #undef VM_SYSCALL_CPI_INSTR_SIZE
     423             : #undef VM_SYSCALL_CPI_ACC_META_T
     424             : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
     425             : #undef VM_SYSCALL_CPI_ACC_META_SIZE
     426             : #undef VM_SYSCALL_CPI_ACC_INFO_T
     427             : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
     428             : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
     429             : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
     430             : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
     431             : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
     432             : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
     433             : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
     434             : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
     435             : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
     436             : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
     437             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
     438             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
     439             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
     440             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
     441             : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
     442             : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN
     443             : 
     444             : /**********************************************************************
     445             :    CROSS PROGRAM INVOCATION (Rust ABI)
     446             :  **********************************************************************/
     447             : 
     448             : #define VM_SYSCALL_CPI_ABI                     rust
     449         916 : #define VM_SYSCALL_CPI_INSTR_T                 fd_vm_rust_instruction_t
     450             : #define VM_SYSCALL_CPI_INSTR_ALIGN             (FD_VM_RUST_INSTRUCTION_ALIGN)
     451             : #define VM_SYSCALL_CPI_INSTR_SIZE              (FD_VM_RUST_INSTRUCTION_SIZE)
     452        7470 : #define VM_SYSCALL_CPI_ACC_META_T              fd_vm_rust_account_meta_t
     453             : #define VM_SYSCALL_CPI_ACC_META_ALIGN          (FD_VM_RUST_ACCOUNT_META_ALIGN)
     454             : #define VM_SYSCALL_CPI_ACC_META_SIZE           (FD_VM_RUST_ACCOUNT_META_SIZE)
     455         491 : #define VM_SYSCALL_CPI_ACC_INFO_T              fd_vm_rust_account_info_t
     456             : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN          (FD_VM_RUST_ACCOUNT_INFO_ALIGN)
     457         498 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE           (FD_VM_RUST_ACCOUNT_INFO_SIZE)
     458             : 
     459             : /* VM_SYSCALL_CPI_INSTR_T accessors */
     460             : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data.addr
     461        2304 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr )  instr->data.len
     462             : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts.addr
     463       10349 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr )  instr->accounts.len
     464         915 : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) instr->pubkey
     465             : 
     466             : /* VM_SYSCALL_CPI_ACC_META_T accessors */
     467        6310 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
     468        6310 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
     469        6544 : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) acc_meta->pubkey
     470             : 
     471             : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
     472             : 
     473             : /* The lamports and the account data are stored behind RefCells,
     474             :    so we have an additional layer of indirection to unwrap. */
     475             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl )                                                                             \
     476         953 :     void const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                          \
     477         953 :       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) ); \
     478         953 :     /* Extract the vaddr embedded in the RefCell */                                                                                              \
     479         953 :     ulong decl = FD_LOAD( ulong, FD_EXPAND_THEN_CONCAT2(decl, _hptr_) );
     480             : 
     481             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L184-L195 */
     482             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl )                                                                                                     \
     483         934 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vaddr_) =                                                                                                                  \
     484         934 :       FD_LOAD( ulong, 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) ) ); \
     485         934 :     void * decl = FD_VM_MEM_HADDR_ST( vm, FD_EXPAND_THEN_CONCAT2(decl, _vaddr_), alignof(ulong), sizeof(ulong) );
     486             : 
     487             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L367-L378 */
     488             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl )                                                                                   \
     489         934 :     if( FD_UNLIKELY( vm->syscall_parameter_address_restrictions &&                                                                                 \
     490         934 :                      vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ) >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) {                \
     491           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );                                                                          \
     492           0 :       return FD_VM_SYSCALL_ERR_INVALID_POINTER;                                                                                                    \
     493           0 :     }                                                                                                                                              \
     494         934 :     /* Translate the vaddr to the slice */                                                                                                         \
     495         934 :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     496         934 :       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) ); \
     497         934 :     /* Extract the vaddr embedded in the slice */                                                                                                  \
     498         934 :     ulong decl = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;
     499             : 
     500             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L212-L221 */
     501             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR( vm, acc_info, decl ) \
     502         925 :     ulong decl = fd_ulong_sat_add( vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), sizeof(ulong) );
     503             : 
     504             : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl )                                                                                         \
     505             :     /* Translate the vaddr to the slice */                                                                                                         \
     506         896 :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     507         896 :       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) ); \
     508         896 :     /* Declare the vaddr of the slice's underlying byte array */                                                                                   \
     509         896 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;                                                     \
     510         896 :     /* Declare the size of the slice's underlying byte array */                                                                                    \
     511         896 :     ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;                                                          \
     512         896 :     /* Translate the vaddr to the underlying byte array */                                                                                         \
     513         896 :     uchar * decl = FD_VM_MEM_SLICE_HADDR_ST(                                                                                                       \
     514         896 :       vm, FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr, alignof(uchar), FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len );
     515             : 
     516             : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl )                                                                                     \
     517             :     /* Translate the vaddr to the slice */                                                                                                         \
     518             :     fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) =                                                                                     \
     519             :       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) ); \
     520             :     /* Declare the vaddr of the slice's underlying byte array */                                                                                   \
     521             :     ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;                                                     \
     522             :     /* Declare the size of the slice's underlying byte array */                                                                                    \
     523             :     ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
     524             : 
     525             : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR( vm, acc_info, decl ) \
     526         953 :     ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr );
     527             : 
     528             : #define VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR( vm, acc_info, decl ) \
     529             :     ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr );
     530             : 
     531             : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
     532           0 :   ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
     533             : 
     534             : #include "fd_vm_syscall_cpi_common.c"
     535             : 
     536             : #undef VM_SYSCALL_CPI_ABI
     537             : #undef VM_SYSCALL_CPI_INSTR_T
     538             : #undef VM_SYSCALL_CPI_INSTR_ALIGN
     539             : #undef VM_SYSCALL_CPI_INSTR_SIZE
     540             : #undef VM_SYSCALL_CPI_ACC_META_T
     541             : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
     542             : #undef VM_SYSCALL_CPI_ACC_META_SIZE
     543             : #undef VM_SYSCALL_CPI_ACC_INFO_T
     544             : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
     545             : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
     546             : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
     547             : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
     548             : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
     549             : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
     550             : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
     551             : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
     552             : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
     553             : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
     554             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
     555             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
     556             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
     557             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
     558             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR
     559             : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
     560             : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR
     561             : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR
     562             : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN

Generated by: LCOV version 1.14