Skip to content

Commit fa23bff

Browse files
committed
funk, flamenco: reverify program cache after epoch boundary
1 parent 70a7175 commit fa23bff

15 files changed

+451
-148
lines changed

src/discof/replay/fd_replay_tile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,12 @@ exec_slice( fd_replay_tile_ctx_t * ctx,
16911691
if( FD_UNLIKELY( !pay_sz || !txn_sz || txn_sz > FD_TXN_MTU ) ) {
16921692
FD_LOG_ERR(( "failed to parse transaction in replay" ));
16931693
}
1694+
1695+
/* Reverify invoked programs for this epoch, if needed
1696+
FIXME: this should be done during txn parsing so that we don't have to loop
1697+
over all accounts a second time. */
1698+
fd_runtime_reverify_cached_programs( ctx->slot_ctx, &txn_p, ctx->runtime_spad );
1699+
16941700
fd_memcpy( txn_p.payload, ctx->mbatch + ctx->slice_exec_ctx.wmark, pay_sz );
16951701
txn_p.payload_sz = pay_sz;
16961702
ctx->slice_exec_ctx.wmark += pay_sz;

src/flamenco/runtime/fd_acc_mgr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ fd_funk_get_acc_meta_mutable( fd_funk_t * funk,
6767
/* the record does not exist in the current funk transaction */
6868
if( !rec ) {
6969
/* clones a record from an ancestor transaction */
70-
rec = fd_funk_rec_clone( funk, txn, &id, out_prepare, &funk_err );
70+
rec = fd_funk_rec_clone( funk, txn, &id, out_prepare, NULL, &funk_err );
7171

7272
if( rec == NULL ) {
7373
/* the record does not exist at all */

src/flamenco/runtime/fd_executor.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc
110110
return fd_native_precompile_program_fn_lookup_tbl_query( pubkey, &null_function )->fn;
111111
}
112112

113+
uchar
114+
fd_executor_pubkey_is_bpf_loader( fd_pubkey_t const * pubkey ) {
115+
return !memcmp( pubkey, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ||
116+
!memcmp( pubkey, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||
117+
!memcmp( pubkey, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ||
118+
!memcmp( pubkey, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) );
119+
}
120+
113121
/* fd_executor_lookup_native_program returns the appropriate instruction processor for the given
114122
native program ID. Returns NULL if given ID is not a recognized native program.
115123
https://github.com/anza-xyz/agave/blob/v2.2.6/program-runtime/src/invoke_context.rs#L520-L544 */
@@ -134,10 +142,7 @@ fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc,
134142
int is_native_program = !memcmp( owner, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) );
135143

136144
if( !is_native_program && FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, remove_accounts_executable_flag_checks ) ) {
137-
if ( FD_UNLIKELY( memcmp( owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) &&
138-
memcmp( owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) &&
139-
memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) &&
140-
memcmp( owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
145+
if( FD_UNLIKELY( !fd_executor_pubkey_is_bpf_loader( owner ) ) ) {
141146
return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
142147
}
143148
}

src/flamenco/runtime/fd_executor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ typedef int (* fd_exec_instr_fn_t)( fd_exec_instr_ctx_t * ctx );
4444
fd_exec_instr_fn_t
4545
fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc );
4646

47+
uchar
48+
fd_executor_pubkey_is_bpf_loader( fd_pubkey_t const * pubkey );
49+
4750
int
4851
fd_executor_check_transactions( fd_exec_txn_ctx_t * txn_ctx );
4952

src/flamenco/runtime/fd_runtime.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,6 +2928,47 @@ fd_runtime_parse_microblock_hdr( void const * buf FD_PARAM_UNUSED,
29282928
return 0;
29292929
}
29302930

2931+
void
2932+
fd_runtime_reverify_cached_programs( fd_exec_slot_ctx_t * slot_ctx,
2933+
fd_txn_p_t const * txn_p,
2934+
fd_spad_t * runtime_spad ) {
2935+
fd_txn_t const * txn_descriptor = TXN( txn_p );
2936+
2937+
/* Iterate over account keys referenced directly in the transaction first */
2938+
fd_acct_addr_t const * acc_addrs = fd_txn_get_acct_addrs( txn_descriptor, txn_p );
2939+
for( ushort acc_idx=0; acc_idx<txn_descriptor->acct_addr_cnt; acc_idx++ ) {
2940+
fd_pubkey_t const * account = fd_type_pun_const( &acc_addrs[acc_idx] );
2941+
fd_bpf_program_reverify( slot_ctx, account, runtime_spad );
2942+
}
2943+
2944+
if( txn_descriptor->transaction_version==FD_TXN_V0 ) {
2945+
2946+
/* Iterate over account keys referenced in ALUTs */
2947+
fd_acct_addr_t alut_accounts[256];
2948+
fd_slot_hashes_global_t const * slot_hashes_global = fd_sysvar_cache_slot_hashes( slot_ctx->sysvar_cache, slot_ctx->runtime_wksp );
2949+
if( FD_UNLIKELY( !slot_hashes_global ) ) {
2950+
return;
2951+
}
2952+
2953+
fd_slot_hash_t * slot_hash = deq_fd_slot_hash_t_join( (uchar *)slot_hashes_global + slot_hashes_global->hashes_offset );
2954+
2955+
if( FD_UNLIKELY( fd_runtime_load_txn_address_lookup_tables( txn_descriptor,
2956+
txn_p->payload,
2957+
slot_ctx->funk,
2958+
slot_ctx->funk_txn,
2959+
slot_ctx->slot_bank.slot,
2960+
slot_hash,
2961+
alut_accounts ) ) ) {
2962+
return;
2963+
}
2964+
2965+
for( ushort alut_idx=0; alut_idx<txn_descriptor->addr_table_adtl_cnt; alut_idx++ ) {
2966+
fd_pubkey_t const * account = fd_type_pun_const( &alut_accounts[alut_idx] );
2967+
fd_bpf_program_reverify( slot_ctx, account, runtime_spad );
2968+
}
2969+
}
2970+
}
2971+
29312972
/* if we are currently in the middle of a batch, batch_cnt will include the current batch.
29322973
if we are at the start of a batch, batch_cnt will include the current batch. */
29332974
static fd_raw_block_txn_iter_t
@@ -4089,6 +4130,11 @@ fd_runtime_block_execute_tpool( fd_exec_slot_ctx_t * slot_ctx,
40894130

40904131
if( !mblock_txn_cnt ) continue;
40914132

4133+
/* Reverify programs for this epoch if needed */
4134+
for( ulong txn_idx=0UL; txn_idx<mblock_txn_cnt; txn_idx++ ) {
4135+
fd_runtime_reverify_cached_programs( slot_ctx, &mblock_txn_ptrs[txn_idx], runtime_spad );
4136+
}
4137+
40924138
res = fd_runtime_process_txns_in_microblock_stream( slot_ctx,
40934139
capture_ctx,
40944140
mblock_txn_ptrs,

src/flamenco/runtime/fd_runtime.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,18 @@ fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
602602
fd_spad_t * runtime_spad,
603603
int * is_epoch_boundary );
604604

605+
/* When the cluster's feature set changes at an epoch, there is a possibility that existing programs
606+
fail new SBPF / ELF header checks. Therefore, after every epoch, we should reverify all programs
607+
and replenish our program cache so that users cannot invoke those old programs. Since iterating through
608+
all programs every single epoch is expensive, we adopt a lazy approach where we reverify programs as they
609+
are referenced in transactions, since only a small subset of all programs are actually referenced at any
610+
time. We also make sure each program is only verified once per epoch, so repeated executions of a
611+
program within the same epoch will only trigger verification once at the very first invocation. */
612+
void
613+
fd_runtime_reverify_cached_programs( fd_exec_slot_ctx_t * slot_ctx,
614+
fd_txn_p_t const * txn_p,
615+
fd_spad_t * runtime_spad );
616+
605617
/* Debugging Tools ************************************************************/
606618

607619
void

0 commit comments

Comments
 (0)