LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_bpf_program_util.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 222 371 59.8 %
Date: 2025-07-01 05:00:49 Functions: 12 17 70.6 %

          Line data    Source code
       1             : #include "fd_bpf_program_util.h"
       2             : #include "fd_bpf_loader_program.h"
       3             : #include "fd_loader_v4_program.h"
       4             : #include "../sysvar/fd_sysvar_epoch_schedule.h"
       5             : 
       6             : #include <assert.h>
       7             : 
       8             : fd_sbpf_validated_program_t *
       9          12 : fd_sbpf_validated_program_new( void * mem, fd_sbpf_elf_info_t const * elf_info ) {
      10          12 :   fd_sbpf_validated_program_t * validated_prog = (fd_sbpf_validated_program_t *)mem;
      11             : 
      12             :   /* Last verified epoch */
      13          12 :   validated_prog->last_epoch_verification_ran = ULONG_MAX;
      14             : 
      15             :   /* Failed verification flag */
      16          12 :   validated_prog->failed_verification = 0;
      17             : 
      18          12 :   ulong l = FD_LAYOUT_INIT;
      19             : 
      20             :   /* calldests backing memory */
      21          12 :   l = FD_LAYOUT_APPEND( l, alignof(fd_sbpf_validated_program_t), sizeof(fd_sbpf_validated_program_t) );
      22          12 :   validated_prog->calldests_shmem = (uchar *)mem + l;
      23          12 :   validated_prog->magic = FD_SBPF_VALIDATED_PROGRAM_MAGIC;
      24             : 
      25             :   /* rodata backing memory */
      26          12 :   l = FD_LAYOUT_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint(elf_info->rodata_sz/8UL) );
      27          12 :   validated_prog->rodata = (uchar *)mem + l;
      28             : 
      29             :   /* SBPF version */
      30          12 :   validated_prog->sbpf_version = elf_info->sbpf_version;
      31             : 
      32          12 :   return (fd_sbpf_validated_program_t *)mem;
      33          12 : }
      34             : 
      35             : ulong
      36           0 : fd_sbpf_validated_program_align( void ) {
      37           0 :   return alignof(fd_sbpf_validated_program_t);
      38           0 : }
      39             : 
      40             : ulong
      41           9 : fd_sbpf_validated_program_footprint( fd_sbpf_elf_info_t const * elf_info ) {
      42           9 :   ulong l = FD_LAYOUT_INIT;
      43           9 :   l = FD_LAYOUT_APPEND( l, alignof(fd_sbpf_validated_program_t), sizeof(fd_sbpf_validated_program_t) );
      44           9 :   l = FD_LAYOUT_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint(elf_info->rodata_sz/8UL) );
      45           9 :   l = FD_LAYOUT_APPEND( l, 8UL, elf_info->rodata_footprint );
      46           9 :   l = FD_LAYOUT_FINI( l, 128UL );
      47           9 :   return l;
      48           9 : }
      49             : 
      50             : static inline fd_funk_rec_key_t
      51          57 : fd_acc_mgr_cache_key( fd_pubkey_t const * pubkey ) {
      52          57 :   fd_funk_rec_key_t id;
      53          57 :   memcpy( id.uc, pubkey, sizeof(fd_pubkey_t) );
      54          57 :   memset( id.uc + sizeof(fd_pubkey_t), 0, sizeof(fd_funk_rec_key_t) - sizeof(fd_pubkey_t) );
      55             : 
      56          57 :   id.uc[ FD_FUNK_REC_KEY_FOOTPRINT - 1 ] = FD_FUNK_KEY_TYPE_ELF_CACHE;
      57             : 
      58          57 :   return id;
      59          57 : }
      60             : 
      61             : /* Similar to the below function, but gets the executable program content for the v4 loader.
      62             :    Unlike the v3 loader, the programdata is stored in a single program account. The program must
      63             :    NOT be retracted to be added to the cache. Returns a pointer to the programdata on success,
      64             :    and NULL on failure.
      65             : 
      66             :    Reasons for failure include:
      67             :    - The program state cannot be read from the account data or is in the `retracted` state. */
      68             : static uchar const *
      69             : fd_bpf_get_executable_program_content_for_v4_loader( fd_txn_account_t const * program_acc,
      70           0 :                                                      ulong *                  program_data_len ) {
      71           0 :   int err;
      72             : 
      73             :   /* Get the current loader v4 state. This implicitly also checks the dlen. */
      74           0 :   fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program_acc, &err );
      75           0 :   if( FD_UNLIKELY( err ) ) {
      76           0 :     return NULL;
      77           0 :   }
      78             : 
      79             :   /* The program must be deployed or finalized. */
      80           0 :   if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {
      81           0 :     return NULL;
      82           0 :   }
      83             : 
      84           0 :   *program_data_len = program_acc->vt->get_data_len( program_acc ) - LOADER_V4_PROGRAM_DATA_OFFSET;
      85           0 :   return program_acc->vt->get_data( program_acc ) + LOADER_V4_PROGRAM_DATA_OFFSET;
      86           0 : }
      87             : 
      88             : /* Gets the programdata for a v3 loader-owned account by decoding the account data
      89             :    as well as the programdata account. Returns a pointer to the programdata on success,
      90             :    and NULL on failure.
      91             : 
      92             :    Reasons for failure include:
      93             :    - The program account data cannot be decoded or is not in the `program` state.
      94             :    - The programdata account is not large enough to hold at least `PROGRAMDATA_METADATA_SIZE` bytes. */
      95             : static uchar const *
      96             : fd_bpf_get_executable_program_content_for_upgradeable_loader( fd_funk_t const *        funk,
      97             :                                                               fd_funk_txn_t const *    funk_txn,
      98             :                                                               fd_txn_account_t const * program_acc,
      99             :                                                               ulong *                  program_data_len,
     100           0 :                                                               fd_spad_t *              runtime_spad ) {
     101           0 :   FD_TXN_ACCOUNT_DECL( programdata_acc );
     102             : 
     103           0 :   fd_bpf_upgradeable_loader_state_t * program_account_state =
     104           0 :     fd_bincode_decode_spad(
     105           0 :       bpf_upgradeable_loader_state, runtime_spad,
     106           0 :       program_acc->vt->get_data( program_acc ),
     107           0 :       program_acc->vt->get_data_len( program_acc ),
     108           0 :       NULL );
     109           0 :   if( FD_UNLIKELY( !program_account_state ) ) {
     110           0 :     return NULL;
     111           0 :   }
     112           0 :   if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
     113           0 :     return NULL;
     114           0 :   }
     115             : 
     116           0 :   fd_pubkey_t * programdata_address = &program_account_state->inner.program.programdata_address;
     117             : 
     118           0 :   if( fd_txn_account_init_from_funk_readonly( programdata_acc, programdata_address, funk, funk_txn )!=FD_ACC_MGR_SUCCESS ) {
     119           0 :     return NULL;
     120           0 :   }
     121             : 
     122             :   /* We don't actually need to decode here, just make sure that the account
     123             :      can be decoded successfully. */
     124           0 :   fd_bincode_decode_ctx_t ctx_programdata = {
     125           0 :     .data    = programdata_acc->vt->get_data( programdata_acc ),
     126           0 :     .dataend = programdata_acc->vt->get_data( programdata_acc ) + programdata_acc->vt->get_data_len( programdata_acc ),
     127           0 :   };
     128             : 
     129           0 :   ulong total_sz = 0UL;
     130           0 :   if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_decode_footprint( &ctx_programdata, &total_sz ) ) ) {
     131           0 :     return NULL;
     132           0 :   }
     133             : 
     134           0 :   if( FD_UNLIKELY( programdata_acc->vt->get_data_len( programdata_acc )<PROGRAMDATA_METADATA_SIZE ) ) {
     135           0 :     return NULL;
     136           0 :   }
     137             : 
     138           0 :   *program_data_len = programdata_acc->vt->get_data_len( programdata_acc ) - PROGRAMDATA_METADATA_SIZE;
     139           0 :   return programdata_acc->vt->get_data( programdata_acc ) + PROGRAMDATA_METADATA_SIZE;
     140           0 : }
     141             : 
     142             : /* Gets the programdata for a v1/v2 loader-owned account by returning a pointer to the account data.
     143             :    Returns a pointer to the programdata on success. Given the txn account API always returns a handle
     144             :    to the account data, this function should NEVER return NULL (since the programdata of v1 and v2 loader)
     145             :    accounts start at the beginning of the data. */
     146             : static uchar const *
     147             : fd_bpf_get_executable_program_content_for_v1_v2_loaders( fd_txn_account_t const * program_acc,
     148          12 :                                                          ulong *                  program_data_len ) {
     149          12 :   *program_data_len = program_acc->vt->get_data_len( program_acc );
     150          12 :   return program_acc->vt->get_data( program_acc );
     151          12 : }
     152             : 
     153             : void
     154             : fd_bpf_get_sbpf_versions( uint *                sbpf_min_version,
     155             :                           uint *                sbpf_max_version,
     156             :                           ulong                 slot,
     157          12 :                           fd_features_t const * features ) {
     158          12 :   int disable_v0  = FD_FEATURE_ACTIVE( slot, features, disable_sbpf_v0_execution );
     159          12 :   int reenable_v0 = FD_FEATURE_ACTIVE( slot, features, reenable_sbpf_v0_execution );
     160          12 :   int enable_v0   = !disable_v0 || reenable_v0;
     161          12 :   int enable_v1   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v1_deployment_and_execution );
     162          12 :   int enable_v2   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v2_deployment_and_execution );
     163          12 :   int enable_v3   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v3_deployment_and_execution );
     164             : 
     165          12 :   *sbpf_min_version = enable_v0 ? FD_SBPF_V0 : FD_SBPF_V3;
     166          12 :   if( enable_v3 ) {
     167          12 :     *sbpf_max_version = FD_SBPF_V3;
     168          12 :   } else if( enable_v2 ) {
     169           0 :     *sbpf_max_version = FD_SBPF_V2;
     170           0 :   } else if( enable_v1 ) {
     171           0 :     *sbpf_max_version = FD_SBPF_V1;
     172           0 :   } else {
     173           0 :     *sbpf_max_version = FD_SBPF_V0;
     174           0 :   }
     175          12 : }
     176             : 
     177             : uchar const *
     178             : fd_bpf_get_programdata_from_account( fd_funk_t const *        funk,
     179             :                                      fd_funk_txn_t const *    funk_txn,
     180             :                                      fd_txn_account_t const * program_acc,
     181             :                                      ulong *                  out_program_data_len,
     182          12 :                                      fd_spad_t *              runtime_spad ) {
     183             :   /* v1/v2 loaders: Programdata is just the account data.
     184             :      v3 loader: Programdata lives in a separate account. Deserialize the program account
     185             :                 and lookup the programdata account. Deserialize the programdata account.
     186             :      v4 loader: Programdata lives in the program account, offset by LOADER_V4_PROGRAM_DATA_OFFSET. */
     187          12 :   if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
     188           0 :     return fd_bpf_get_executable_program_content_for_upgradeable_loader( funk, funk_txn, program_acc, out_program_data_len, runtime_spad );
     189          12 :   } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
     190           0 :     return fd_bpf_get_executable_program_content_for_v4_loader( program_acc, out_program_data_len );
     191          12 :   } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||
     192          12 :              !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) {
     193          12 :     return fd_bpf_get_executable_program_content_for_v1_v2_loaders( program_acc, out_program_data_len );
     194          12 :   }
     195           0 :   return NULL;
     196          12 : }
     197             : 
     198             : /* Parse ELF info from programdata. */
     199             : static int
     200             : fd_bpf_parse_elf_info( fd_sbpf_elf_info_t *       elf_info,
     201             :                        uchar const *              program_data,
     202             :                        ulong                      program_data_len,
     203          12 :                        fd_exec_slot_ctx_t const * slot_ctx ) {
     204          12 :   uint min_sbpf_version, max_sbpf_version;
     205          12 :   fd_bpf_get_sbpf_versions( &min_sbpf_version,
     206          12 :                             &max_sbpf_version,
     207          12 :                             slot_ctx->slot,
     208          12 :                             fd_bank_features_query( slot_ctx->bank ) );
     209          12 :   if( FD_UNLIKELY( !fd_sbpf_elf_peek( elf_info, program_data, program_data_len, /* deploy checks */ 0, min_sbpf_version, max_sbpf_version ) ) ) {
     210           3 :     FD_LOG_DEBUG(( "fd_sbpf_elf_peek() failed: %s", fd_sbpf_strerror() ));
     211           3 :     return -1;
     212           3 :   }
     213           9 :   return 0;
     214          12 : }
     215             : 
     216             : /* This function is used to validate an sBPF program and set the program's flags accordingly. The return
     217             :    code only signifies whether the program was successfully validated or not. Regardless of the return code,
     218             :    the program should still be added to the cache. `validated_prog` is expected to be a pre-allocated struct with
     219             :    enough space to hold its field members + calldests info.
     220             : 
     221             :    Reasons for failure include:
     222             :    - Insufficient memory in the spad to allocate memory for local objects.
     223             :    - The sBPF program fails to be loaded or validated validated.
     224             : 
     225             :    On a failure that doesn't kill the client, the `failed_verification` flag for the record is set to 1.
     226             : 
     227             :    On success, `validated_prog` is updated with the loaded sBPF program metadata, as well as the `last_verified_epoch`
     228             :    and `failed_verification` flags. */
     229             : static int
     230             : fd_bpf_validate_sbpf_program( fd_exec_slot_ctx_t const *    slot_ctx,
     231             :                               fd_sbpf_elf_info_t const *    elf_info,
     232             :                               uchar const *                 program_data,
     233             :                               ulong                         program_data_len,
     234             :                               fd_spad_t *                   runtime_spad,
     235           9 :                               fd_sbpf_validated_program_t * validated_prog /* out */ ) {
     236             :   /* Mark the program as validated for this epoch. */
     237             : 
     238           9 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
     239           9 :   validated_prog->last_epoch_verification_ran = fd_slot_to_epoch( epoch_schedule,
     240           9 :                                                                   slot_ctx->slot,
     241           9 :                                                                   NULL );
     242             : 
     243           9 :   ulong               prog_align     = fd_sbpf_program_align();
     244           9 :   ulong               prog_footprint = fd_sbpf_program_footprint( elf_info );
     245           9 :   fd_sbpf_program_t * prog           = fd_sbpf_program_new(  fd_spad_alloc( runtime_spad, prog_align, prog_footprint ), elf_info, validated_prog->rodata );
     246           9 :   if( FD_UNLIKELY( !prog ) ) {
     247           0 :     validated_prog->failed_verification = 1;
     248           0 :     return -1;
     249           0 :   }
     250             : 
     251             :   /* Allocate syscalls */
     252             : 
     253           9 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc( runtime_spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
     254           9 :   if( FD_UNLIKELY( !syscalls ) ) {
     255           0 :     FD_LOG_CRIT(( "Call to fd_sbpf_syscalls_new() failed" ));
     256           0 :   }
     257             : 
     258           9 :   fd_vm_syscall_register_slot( syscalls,
     259           9 :                                slot_ctx->slot,
     260           9 :                                fd_bank_features_query( slot_ctx->bank ),
     261           9 :                                0 );
     262             : 
     263             :   /* Load program. */
     264             : 
     265           9 :   if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, program_data, program_data_len, syscalls, false ) ) ) {
     266           0 :     FD_LOG_DEBUG(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() ));
     267           0 :     validated_prog->failed_verification = 1;
     268           0 :     return -1;
     269           0 :   }
     270             : 
     271             :   /* Validate the program. */
     272             : 
     273           9 :   fd_vm_t _vm[ 1UL ];
     274           9 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
     275           9 :   if( FD_UNLIKELY( !vm ) ) {
     276           0 :     FD_LOG_CRIT(( "fd_vm_new() or fd_vm_join() failed" ));
     277           0 :   }
     278             : 
     279           9 :   int direct_mapping = FD_FEATURE_ACTIVE( slot_ctx->slot, fd_bank_features_query( slot_ctx->bank ), bpf_account_data_direct_mapping );
     280             : 
     281           9 :   vm = fd_vm_init( vm,
     282           9 :                    NULL, /* OK since unused in `fd_vm_validate()` */
     283           9 :                    0UL,
     284           9 :                    0UL,
     285           9 :                    prog->rodata,
     286           9 :                    prog->rodata_sz,
     287           9 :                    prog->text,
     288           9 :                    prog->text_cnt,
     289           9 :                    prog->text_off,
     290           9 :                    prog->text_sz,
     291           9 :                    prog->entry_pc,
     292           9 :                    prog->calldests,
     293           9 :                    elf_info->sbpf_version,
     294           9 :                    syscalls,
     295           9 :                    NULL,
     296           9 :                    NULL,
     297           9 :                    NULL,
     298           9 :                    0U,
     299           9 :                    NULL,
     300           9 :                    0,
     301           9 :                    direct_mapping,
     302           9 :                    0 );
     303             : 
     304           9 :   if( FD_UNLIKELY( !vm ) ) {
     305           0 :     FD_LOG_CRIT(( "fd_vm_init() failed" ));
     306           0 :   }
     307             : 
     308           9 :   int res = fd_vm_validate( vm );
     309           9 :   if( FD_UNLIKELY( res ) ) {
     310           0 :     FD_LOG_DEBUG(( "fd_vm_validate() failed" ));
     311           0 :     validated_prog->failed_verification = 1;
     312           0 :     return -1;
     313           0 :   }
     314             : 
     315             :   /* FIXME: Super expensive memcpy. */
     316           9 :   fd_memcpy( validated_prog->calldests_shmem, prog->calldests_shmem, fd_sbpf_calldests_footprint( prog->rodata_sz/8UL ) );
     317             : 
     318           9 :   validated_prog->calldests           = fd_sbpf_calldests_join( validated_prog->calldests_shmem );
     319           9 :   validated_prog->entry_pc            = prog->entry_pc;
     320           9 :   validated_prog->text_off            = prog->text_off;
     321           9 :   validated_prog->text_cnt            = prog->text_cnt;
     322           9 :   validated_prog->text_sz             = prog->text_sz;
     323           9 :   validated_prog->rodata_sz           = prog->rodata_sz;
     324           9 :   validated_prog->failed_verification = 0;
     325             : 
     326           9 :   return 0;
     327           9 : }
     328             : 
     329             : /* Publishes an in-prepare funk record for a program that failed verification. Creates a default
     330             :    sBPF validated program with the `failed_verification` flag set to 1. The passed-in funk record
     331             :    is expected to be in a prepare. */
     332             : static void
     333             : fd_publish_failed_verification_rec( fd_funk_t *             funk,
     334             :                                     fd_funk_rec_prepare_t * prepare,
     335           3 :                                     fd_funk_rec_t *         rec ) {
     336             :   /* Truncate the record to have a minimal footprint */
     337           3 :   fd_sbpf_elf_info_t elf_info = {0};
     338           3 :   ulong record_sz = fd_sbpf_validated_program_footprint( &elf_info );
     339           3 :   void * data = fd_funk_val_truncate( rec, fd_funk_alloc( funk ), fd_funk_wksp( funk ), 0UL, record_sz, NULL );
     340           3 :   if( FD_UNLIKELY( data==NULL ) ) {
     341           0 :     FD_LOG_ERR(( "fd_funk_val_truncate() failed to truncate record to size %lu", record_sz ));
     342           0 :   }
     343             : 
     344             :   /* Initialize the validated program to default values. This is fine because the `failed_verification` flag indicates
     345             :      that the should not be executed. */
     346           3 :   fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( data, &elf_info );
     347           3 :   validated_prog->failed_verification = 1;
     348             : 
     349           3 :   fd_funk_rec_publish( funk, prepare );
     350           3 : }
     351             : 
     352             : /* Validates an SBPF program and adds it to the program cache. Verification failure reasons include:
     353             :    - The programdata cannot be read from the account or programdata account
     354             :    - The ELF info cannot be parsed from the programdata.
     355             :    - The sBPF program fails to be validated.
     356             : 
     357             :    The program will still be added to the cache even if verifications fail. This is to prevent a DOS
     358             :    vector where an attacker could spam invocations to programs that failed verification. */
     359             : static void
     360             : fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t *     slot_ctx,
     361             :                                        fd_txn_account_t const * program_acc,
     362           9 :                                        fd_spad_t *              runtime_spad ) {
     363           9 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     364             : 
     365             :     /* Prepare the funk record for the program cache. */
     366           9 :     fd_pubkey_t const * program_pubkey = program_acc->pubkey;
     367           9 :     fd_funk_t *       funk             = slot_ctx->funk;
     368           9 :     fd_funk_txn_t *   funk_txn         = slot_ctx->funk_txn;
     369           9 :     fd_funk_rec_key_t id               = fd_acc_mgr_cache_key( program_pubkey );
     370             : 
     371             :     /* This prepare should never fail. */
     372           9 :     int funk_err = FD_FUNK_SUCCESS;
     373           9 :     fd_funk_rec_prepare_t prepare[1];
     374           9 :     fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, funk_txn, &id, prepare, &funk_err );
     375           9 :     if( rec == NULL || funk_err != FD_FUNK_SUCCESS ) {
     376           0 :       FD_LOG_CRIT(( "fd_funk_rec_prepare() failed: %i-%s", funk_err, fd_funk_strerror( funk_err ) ));
     377           0 :     }
     378             : 
     379           9 :     ulong         program_data_len = 0UL;
     380           9 :     uchar const * program_data     = fd_bpf_get_programdata_from_account( funk, funk_txn, program_acc, &program_data_len, runtime_spad );
     381             : 
     382           9 :     if( FD_UNLIKELY( program_data==NULL ) ) {
     383           0 :       fd_publish_failed_verification_rec( funk, prepare, rec );
     384           0 :       return;
     385           0 :     }
     386             : 
     387           9 :     fd_sbpf_elf_info_t elf_info = {0};
     388           9 :     if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
     389           3 :       fd_publish_failed_verification_rec( funk, prepare, rec );
     390           3 :       return;
     391           3 :     }
     392             : 
     393           6 :     ulong val_sz = fd_sbpf_validated_program_footprint( &elf_info );
     394           6 :     void * val = fd_funk_val_truncate(
     395           6 :         rec,
     396           6 :         fd_funk_alloc( funk ),
     397           6 :         fd_funk_wksp( funk ),
     398           6 :         0UL,
     399           6 :         val_sz,
     400           6 :         &funk_err );
     401           6 :     if( FD_UNLIKELY( funk_err ) ) {
     402           0 :       FD_LOG_ERR(( "fd_funk_val_truncate(sz=%lu) for account failed (%i-%s)", val_sz, funk_err, fd_funk_strerror( funk_err ) ));
     403           0 :     }
     404             : 
     405             :     /* Note that the validated program points to the funk record data and writes into the record directly to avoid an expensive memcpy. */
     406           6 :     fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( val, &elf_info );
     407           6 :     int res = fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, validated_prog );
     408           6 :     if( FD_UNLIKELY( res ) ) {
     409           0 :       fd_publish_failed_verification_rec( funk, prepare, rec );
     410           0 :       return;
     411           0 :     }
     412             : 
     413           6 :     fd_funk_rec_publish( funk, prepare );
     414           9 :   } FD_SPAD_FRAME_END;
     415           9 : }
     416             : 
     417             : static int
     418             : fd_bpf_check_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
     419             :                                                  fd_pubkey_t const *  pubkey,
     420           0 :                                                  fd_spad_t *          runtime_spad ) {
     421           0 :   FD_TXN_ACCOUNT_DECL( exec_rec );
     422           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec, pubkey, slot_ctx->funk, slot_ctx->funk_txn ) != FD_ACC_MGR_SUCCESS ) ) {
     423           0 :     return -1;
     424           0 :   }
     425             : 
     426           0 :   if( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) {
     427           0 :     return -1;
     428           0 :   }
     429             : 
     430           0 :   fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
     431             : 
     432           0 :   return 0;
     433           0 : }
     434             : 
     435             : int
     436             : fd_bpf_scan_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
     437           0 :                                                 fd_spad_t *          runtime_spad ) {
     438           0 :   fd_funk_t * funk = slot_ctx->funk;
     439           0 :   ulong       cnt  = 0UL;
     440             : 
     441             :   /* Use random-ish xid to avoid concurrency issues */
     442           0 :   fd_funk_txn_xid_t cache_xid = fd_funk_generate_xid();
     443             : 
     444           0 :   fd_funk_txn_start_write( funk );
     445           0 :   fd_funk_txn_t * cache_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, &cache_xid, 1 );
     446           0 :   if( !cache_txn ) {
     447           0 :     FD_LOG_ERR(( "fd_funk_txn_prepare() failed" ));
     448           0 :     return -1;
     449           0 :   }
     450           0 :   fd_funk_txn_end_write( funk );
     451             : 
     452           0 :   fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
     453           0 :   slot_ctx->funk_txn = cache_txn;
     454             : 
     455           0 :   fd_funk_txn_start_read( funk );
     456           0 :   for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, funk_txn );
     457           0 :        NULL != rec;
     458           0 :        rec = fd_funk_txn_next_rec( funk, rec )) {
     459           0 :     if( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
     460           0 :       continue;
     461           0 :     }
     462             : 
     463           0 :     fd_pubkey_t const * pubkey = fd_type_pun_const( rec->pair.key[0].uc );
     464             : 
     465           0 :     int res = fd_bpf_check_and_create_bpf_program_cache_entry( slot_ctx, pubkey, runtime_spad );
     466             : 
     467           0 :     if( res==0 ) {
     468           0 :       cnt++;
     469           0 :     }
     470           0 :   }
     471           0 :   fd_funk_txn_end_read( funk );
     472             : 
     473           0 :   FD_LOG_DEBUG(( "loaded program cache: %lu", cnt));
     474             : 
     475           0 :   fd_funk_txn_start_write( funk );
     476           0 :   if( fd_funk_txn_publish_into_parent( funk, cache_txn, 1 ) != FD_FUNK_SUCCESS ) {
     477           0 :     FD_LOG_ERR(( "fd_funk_txn_publish_into_parent() failed" ));
     478           0 :     return -1;
     479           0 :   }
     480           0 :   fd_funk_txn_end_write( funk );
     481             : 
     482           0 :   slot_ctx->funk_txn = funk_txn;
     483           0 :   return 0;
     484           0 : }
     485             : 
     486             : int
     487             : fd_bpf_load_cache_entry( fd_funk_t const *                    funk,
     488             :                          fd_funk_txn_t const *                funk_txn,
     489             :                          fd_pubkey_t const *                  program_pubkey,
     490          30 :                          fd_sbpf_validated_program_t const ** valid_prog ) {
     491          30 :   fd_funk_rec_key_t id   = fd_acc_mgr_cache_key( program_pubkey );
     492             : 
     493          30 :   for(;;) {
     494          30 :     fd_funk_rec_query_t query[1];
     495          30 :     fd_funk_rec_t const * rec = fd_funk_rec_query_try_global(funk, funk_txn, &id, NULL, query);
     496             : 
     497          30 :     if( FD_UNLIKELY( !rec || !!( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) ) {
     498          15 :       if( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) {
     499          15 :         return -1;
     500          15 :       } else {
     501           0 :         continue;
     502           0 :       }
     503          15 :     }
     504             : 
     505          15 :     void const * data = fd_funk_val_const( rec, fd_funk_wksp(funk) );
     506             : 
     507          15 :     *valid_prog = (fd_sbpf_validated_program_t const *)data;
     508             : 
     509             :     /* This test is actually too early. It should happen after the
     510             :        data is actually consumed.
     511             : 
     512             :        TODO: this is likely fine because nothing else is modifying the
     513             :        program cache records at the same time. */
     514          15 :     if( FD_LIKELY( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) ) {
     515          15 :       if( FD_UNLIKELY( (*valid_prog)->magic != FD_SBPF_VALIDATED_PROGRAM_MAGIC ) ) FD_LOG_ERR(( "invalid magic" ));
     516          15 :       return 0;
     517          15 :     }
     518             : 
     519             :     /* Try again */
     520          15 :   }
     521          30 : }
     522             : 
     523             : void
     524             : fd_bpf_program_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
     525             :                                      fd_pubkey_t const *  program_pubkey,
     526          18 :                                      fd_spad_t *          runtime_spad ) {
     527          18 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
     528          18 :   FD_TXN_ACCOUNT_DECL( exec_rec );
     529          18 :   fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
     530             : 
     531             :   /* No need to touch the cache if the account no longer exists. */
     532          18 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec,
     533          18 :                                                            program_pubkey,
     534          18 :                                                            slot_ctx->funk,
     535          18 :                                                            slot_ctx->funk_txn ) ) ) {
     536           3 :     return;
     537           3 :   }
     538             : 
     539             :   /* The account owner must be a BPF loader to even be considered. */
     540          15 :   if( FD_UNLIKELY( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) ) {
     541           3 :     return;
     542           3 :   }
     543             : 
     544             :   /* If the program is not present in the cache yet, then we should run verifications and add it to the cache.
     545             :      `fd_bpf_create_bpf_program_cache_entry()` will insert the program into the cache and update the entry's flags
     546             :      accordingly if it fails verification. */
     547          12 :   fd_sbpf_validated_program_t const * prog = NULL;
     548          12 :   int err = fd_bpf_load_cache_entry( slot_ctx->funk, slot_ctx->funk_txn, program_pubkey, &prog );
     549          12 :   if( FD_UNLIKELY( err ) ) {
     550           9 :     fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
     551           9 :     return;
     552           9 :   }
     553             : 
     554             :   /* At this point, the program is in the cache. We need to check the last verified epoch now to determine if it needs to be reverified.
     555             :      If it has already been reverified for the current epoch, then there is no need to do anything. */
     556           3 :   fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
     557           3 :   ulong current_epoch = fd_slot_to_epoch( epoch_schedule, slot_ctx->slot, NULL );
     558           3 :   if( FD_LIKELY( prog->last_epoch_verification_ran==current_epoch ) ) {
     559           0 :     return;
     560           0 :   }
     561             : 
     562             :   /* At this point, the program is in the cache but has not been reverified for the current epoch.
     563             :      We need to run verifications and update the cache if it passes. */
     564             : 
     565             :   /* Copy the record (if needed) down into the current funk txn from one of its ancestors. It is safe to
     566             :      pass in min_sz=0 because the record is known to exist in the cache already, and the record size will not change */
     567           3 :   fd_funk_rec_try_clone_safe( slot_ctx->funk, slot_ctx->funk_txn, &id, 0UL, 0UL );
     568             : 
     569             :   /* Modify the record within the current funk txn */
     570           3 :   fd_funk_rec_query_t query[1];
     571           3 :   fd_funk_rec_t * rec = fd_funk_rec_modify( slot_ctx->funk, slot_ctx->funk_txn, &id, query );
     572             : 
     573           3 :   if( FD_UNLIKELY( !rec ) ) {
     574             :     /* The record does not exist (somehow). Ideally this should never happen since this function is called in a single-threaded context. */
     575           0 :     FD_LOG_CRIT(( "Failed to modify the BPF program cache record. Perhaps there is a race condition?" ));
     576           0 :   }
     577             : 
     578           3 :   void *                        data          = fd_funk_val( rec, fd_funk_wksp( slot_ctx->funk ) );
     579           3 :   fd_sbpf_elf_info_t            elf_info      = {0};
     580           3 :   fd_sbpf_validated_program_t * modified_prog = (fd_sbpf_validated_program_t *)data;
     581             : 
     582             :   /* Get the program data from the account */
     583           3 :   ulong         program_data_len = 0UL;
     584           3 :   uchar const * program_data     = fd_bpf_get_programdata_from_account( slot_ctx->funk,
     585           3 :                                                                         slot_ctx->funk_txn,
     586           3 :                                                                         exec_rec,
     587           3 :                                                                         &program_data_len,
     588           3 :                                                                         runtime_spad );
     589           3 :   if( FD_UNLIKELY( program_data==NULL ) ) {
     590           0 :     modified_prog->failed_verification = 1;
     591           0 :     fd_funk_rec_modify_publish( query );
     592           0 :     return;
     593           0 :   }
     594             : 
     595             :   /* Parse the ELF info */
     596           3 :   if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
     597           0 :     modified_prog->failed_verification = 1;
     598           0 :     fd_funk_rec_modify_publish( query );
     599           0 :     return;
     600           0 :   }
     601             : 
     602             :   /* Validate the sBPF program. This will set the program's flags accordingly. The return code does not matter here because we publish
     603             :      regardless of the return code. */
     604           3 :   modified_prog = fd_sbpf_validated_program_new( data, &elf_info );
     605           3 :   fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, modified_prog );
     606             : 
     607           3 :   if( modified_prog->failed_verification ) {
     608           0 :     FD_LOG_ERR(("program fialed veriifecation;"));
     609           0 :   }
     610             : 
     611             :   /* Finish modifying and release lock */
     612           3 :   fd_funk_rec_modify_publish( query );
     613             : 
     614          18 : } FD_SPAD_FRAME_END;
     615          18 : }

Generated by: LCOV version 1.14