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

Generated by: LCOV version 1.14