Skip to content

Commit 7382604

Browse files
committed
funk, flamenco: reverify program cache after epoch boundary
1 parent 6437b3a commit 7382604

15 files changed

+453
-146
lines changed

src/discof/replay/fd_replay_tile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,12 @@ exec_slice( fd_replay_tile_ctx_t * ctx,
16831683
if( FD_UNLIKELY( !pay_sz || !txn_sz || txn_sz > FD_TXN_MTU ) ) {
16841684
FD_LOG_ERR(( "failed to parse transaction in replay" ));
16851685
}
1686+
1687+
/* Reverify invoked programs for this epoch, if needed
1688+
FIXME: this should be done during txn parsing so that we don't have to loop
1689+
over all accounts a second time. */
1690+
fd_runtime_reverify_cached_programs( ctx->slot_ctx, &txn_p, ctx->runtime_spad );
1691+
16861692
fd_memcpy( txn_p.payload, ctx->mbatch + ctx->slice_exec_ctx.wmark, pay_sz );
16871693
txn_p.payload_sz = pay_sz;
16881694
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
@@ -2932,6 +2932,47 @@ fd_runtime_parse_microblock_hdr( void const * buf FD_PARAM_UNUSED,
29322932
return 0;
29332933
}
29342934

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

40994140
if( !mblock_txn_cnt ) continue;
41004141

4142+
/* Reverify programs for this epoch if needed */
4143+
for( ulong txn_idx=0UL; txn_idx<mblock_txn_cnt; txn_idx++ ) {
4144+
fd_runtime_reverify_cached_programs( slot_ctx, &mblock_txn_ptrs[txn_idx], runtime_spad );
4145+
}
4146+
41014147
res = fd_runtime_process_txns_in_microblock_stream( slot_ctx,
41024148
capture_ctx,
41034149
mblock_txn_ptrs,

src/flamenco/runtime/fd_runtime.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,18 @@ fd_runtime_block_pre_execute_process_new_epoch( fd_exec_slot_ctx_t * slot_ctx,
615615
fd_spad_t * runtime_spad,
616616
int * is_epoch_boundary );
617617

618+
/* When the cluster's feature set changes at an epoch, there is a possibility that existing programs
619+
fail new SBPF / ELF header checks. Therefore, after every epoch, we should reverify all programs
620+
and replenish our program cache so that users cannot invoke those old programs. Since iterating through
621+
all programs every single epoch is expensive, we adopt a lazy approach where we reverify programs as they
622+
are referenced in transactions, since only a small subset of all programs are actually referenced at any
623+
time. We also make sure each program is only verified once per epoch, so repeated executions of a
624+
program within the same epoch will only trigger verification once at the very first invocation. */
625+
void
626+
fd_runtime_reverify_cached_programs( fd_exec_slot_ctx_t * slot_ctx,
627+
fd_txn_p_t const * txn_p,
628+
fd_spad_t * runtime_spad );
629+
618630
/* Debugging Tools ************************************************************/
619631

620632
void

0 commit comments

Comments
 (0)