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

Generated by: LCOV version 1.14