From 3df54a34e5a90dd0b3f4e7350a98dc5310ad9e08 Mon Sep 17 00:00:00 2001 From: Tom Pointon Date: Mon, 15 Dec 2025 12:52:18 +0000 Subject: [PATCH] flamenco, runtime: implement static_instruction_limit --- src/flamenco/features/fd_features_generated.c | 8 ++ src/flamenco/features/fd_features_generated.h | 5 +- src/flamenco/features/feature_map.json | 3 +- src/flamenco/runtime/Local.mk | 2 + src/flamenco/runtime/fd_executor.c | 9 +- src/flamenco/runtime/fd_executor.h | 2 +- .../runtime/program/fd_builtin_programs.c | 2 +- .../runtime/program/fd_builtin_programs.h | 2 +- .../program/fd_compute_budget_program.c | 4 +- .../program/fd_compute_budget_program.h | 2 +- .../runtime/test_static_instruction_limit.c | 126 ++++++++++++++++++ .../runtime/tests/run_backtest_all.sh | 1 + 12 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 src/flamenco/runtime/test_static_instruction_limit.c diff --git a/src/flamenco/features/fd_features_generated.c b/src/flamenco/features/fd_features_generated.c index 0cf18009486..37a51b037f7 100644 --- a/src/flamenco/features/fd_features_generated.c +++ b/src/flamenco/features/fd_features_generated.c @@ -1721,6 +1721,12 @@ fd_feature_id_t const ids[] = { .name = "deprecate_rent_exemption_threshold", .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = offsetof(fd_features_t, static_instruction_limit)>>3, + .id = {"\x4b\x3e\xa0\x91\xa9\xb6\xb5\xda\x05\x3a\x32\x6f\x7c\x18\xd9\x0d\x60\x87\x99\x76\xfb\xc6\x6f\x18\xc1\xfa\x37\x38\x94\x41\xc1\xf9"}, + /* 64ixypL1HPu8WtJhNSMb9mSgfFaJvsANuRkTbHyuLfnx */ + .name = "static_instruction_limit", + .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = ULONG_MAX } }; /* TODO replace this with fd_map_perfect */ @@ -1979,6 +1985,7 @@ fd_feature_id_query( ulong prefix ) { case 0xab2a2311ca83eb09: return &ids[ 249 ]; case 0x55792888a8cf31ef: return &ids[ 250 ]; case 0xf4792febab30b80c: return &ids[ 251 ]; + case 0xdab5b6a991a03e4b: return &ids[ 252 ]; default: break; } return NULL; @@ -2236,4 +2243,5 @@ FD_STATIC_ASSERT( offsetof( fd_features_t, provide_instruction_data_offset_in_vm FD_STATIC_ASSERT( offsetof( fd_features_t, enforce_fixed_fec_set )>>3==249UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, increase_cpi_account_info_limit )>>3==250UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, deprecate_rent_exemption_threshold )>>3==251UL, layout ); +FD_STATIC_ASSERT( offsetof( fd_features_t, static_instruction_limit )>>3==252UL, layout ); FD_STATIC_ASSERT( sizeof( fd_features_t )>>3==FD_FEATURE_ID_CNT, layout ); diff --git a/src/flamenco/features/fd_features_generated.h b/src/flamenco/features/fd_features_generated.h index 0de9ec413d4..674a2fc0a4c 100644 --- a/src/flamenco/features/fd_features_generated.h +++ b/src/flamenco/features/fd_features_generated.h @@ -8,10 +8,10 @@ #endif /* FEATURE_ID_CNT is the number of features in ids */ -#define FD_FEATURE_ID_CNT (252UL) +#define FD_FEATURE_ID_CNT (253UL) /* Feature set ID calculated from all feature names */ -#define FD_FEATURE_SET_ID (2346311975U) +#define FD_FEATURE_SET_ID (3650511804U) union fd_features { ulong f[ FD_FEATURE_ID_CNT ]; @@ -268,5 +268,6 @@ union fd_features { /* 0xab2a2311ca83eb09 */ ulong enforce_fixed_fec_set; /* 0x55792888a8cf31ef */ ulong increase_cpi_account_info_limit; /* 0xf4792febab30b80c */ ulong deprecate_rent_exemption_threshold; + /* 0xdab5b6a991a03e4b */ ulong static_instruction_limit; }; }; diff --git a/src/flamenco/features/feature_map.json b/src/flamenco/features/feature_map.json index 83005d481f2..892a09c4eb7 100644 --- a/src/flamenco/features/feature_map.json +++ b/src/flamenco/features/feature_map.json @@ -250,5 +250,6 @@ {"name":"provide_instruction_data_offset_in_vm_r2","pubkey":"5xXZc66h4UdB6Yq7FzdBxBiRAFMMScMLwHxk2QZDaNZL"}, {"name":"enforce_fixed_fec_set","pubkey":"fixfecLZYMfkGzwq6NJA11Yw6KYztzXiK9QcL3K78in"}, {"name":"increase_cpi_account_info_limit","pubkey":"H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5"}, - {"name":"deprecate_rent_exemption_threshold","pubkey":"rent6iVy6PDoViPBeJ6k5EJQrkj62h7DPyLbWGHwjrC"} + {"name":"deprecate_rent_exemption_threshold","pubkey":"rent6iVy6PDoViPBeJ6k5EJQrkj62h7DPyLbWGHwjrC"}, + {"name":"static_instruction_limit","pubkey":"64ixypL1HPu8WtJhNSMb9mSgfFaJvsANuRkTbHyuLfnx"} ] diff --git a/src/flamenco/runtime/Local.mk b/src/flamenco/runtime/Local.mk index 160b123c105..e9b15dba4b2 100644 --- a/src/flamenco/runtime/Local.mk +++ b/src/flamenco/runtime/Local.mk @@ -58,6 +58,8 @@ ifdef FD_HAS_HOSTED ifdef FD_HAS_SECP256K1 $(call make-unit-test,test_bank,test_bank,fd_flamenco fd_funk fd_ballet fd_util) $(call run-unit-test,test_bank,) +$(call make-unit-test,test_static_instruction_limit,test_static_instruction_limit,fd_flamenco fd_funk fd_ballet fd_util) +$(call run-unit-test,test_static_instruction_limit,) endif endif diff --git a/src/flamenco/runtime/fd_executor.c b/src/flamenco/runtime/fd_executor.c index bb1efc861e4..2405de28fb4 100644 --- a/src/flamenco/runtime/fd_executor.c +++ b/src/flamenco/runtime/fd_executor.c @@ -401,11 +401,18 @@ fd_executor_check_transactions( fd_runtime_t * runtime, https://github.com/anza-xyz/agave/blob/v2.3.1/runtime/src/bank.rs#L5725-L5753 */ int -fd_executor_verify_transaction( fd_bank_t * bank, +fd_executor_verify_transaction( fd_bank_t const * bank, fd_txn_in_t const * txn_in, fd_txn_out_t * txn_out ) { int err = FD_RUNTIME_EXECUTE_SUCCESS; + /* SIMD-0160: enforce static limit on number of instructions. + https://github.com/anza-xyz/agave/blob/v3.1.4/runtime/src/bank.rs#L4710-L4716 */ + if( FD_UNLIKELY( FD_FEATURE_ACTIVE_BANK( bank, static_instruction_limit ) && + TXN( txn_in->txn )->instr_cnt > FD_MAX_INSTRUCTION_TRACE_LENGTH ) ) { + return FD_RUNTIME_TXN_ERR_SANITIZE_FAILURE; + } + /* https://github.com/anza-xyz/agave/blob/v2.2.13/svm/src/transaction_processor.rs#L566-L569 */ err = fd_executor_compute_budget_program_execute_instructions( bank, txn_in, txn_out ); if( FD_UNLIKELY( err ) ) return err; diff --git a/src/flamenco/runtime/fd_executor.h b/src/flamenco/runtime/fd_executor.h index b2a8d1ab8ce..0b0e55a9731 100644 --- a/src/flamenco/runtime/fd_executor.h +++ b/src/flamenco/runtime/fd_executor.h @@ -40,7 +40,7 @@ uchar fd_executor_pubkey_is_bpf_loader( fd_pubkey_t const * pubkey ); int -fd_executor_verify_transaction( fd_bank_t * bank, +fd_executor_verify_transaction( fd_bank_t const * bank, fd_txn_in_t const * txn_in, fd_txn_out_t * txn_out ); diff --git a/src/flamenco/runtime/program/fd_builtin_programs.c b/src/flamenco/runtime/program/fd_builtin_programs.c index a00fed3f33e..3fc5d1994cb 100644 --- a/src/flamenco/runtime/program/fd_builtin_programs.c +++ b/src/flamenco/runtime/program/fd_builtin_programs.c @@ -336,7 +336,7 @@ fd_num_precompiles( void ) { } uchar -fd_is_migrating_builtin_program( fd_bank_t * bank, +fd_is_migrating_builtin_program( fd_bank_t const * bank, fd_pubkey_t const * pubkey, uchar * migrated_yet ) { *migrated_yet = 0; diff --git a/src/flamenco/runtime/program/fd_builtin_programs.h b/src/flamenco/runtime/program/fd_builtin_programs.h index ce443b88a7e..571fdd09605 100644 --- a/src/flamenco/runtime/program/fd_builtin_programs.h +++ b/src/flamenco/runtime/program/fd_builtin_programs.h @@ -95,7 +95,7 @@ fd_num_stateless_builtins( void ); | 1 | 1 | Program is a migrating builtin program id, AND has been migrated to BPF | */ uchar -fd_is_migrating_builtin_program( fd_bank_t * bank, +fd_is_migrating_builtin_program( fd_bank_t const * bank, fd_pubkey_t const * pubkey, uchar * migrated_yet ); diff --git a/src/flamenco/runtime/program/fd_compute_budget_program.c b/src/flamenco/runtime/program/fd_compute_budget_program.c index a72c4875914..78b8be858c9 100644 --- a/src/flamenco/runtime/program/fd_compute_budget_program.c +++ b/src/flamenco/runtime/program/fd_compute_budget_program.c @@ -14,7 +14,7 @@ #define MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT (3000UL) FD_FN_PURE static inline uchar -get_program_kind( fd_bank_t * bank, +get_program_kind( fd_bank_t const * bank, fd_txn_in_t const * txn_in, fd_txn_instr_t const * instr ) { fd_acct_addr_t const * txn_accs = fd_txn_get_acct_addrs( TXN( txn_in->txn ), txn_in->txn->payload ); @@ -114,7 +114,7 @@ fd_sanitize_compute_unit_limits( fd_txn_out_t * txn_out ) { https://github.com/anza-xyz/agave/blob/v2.3.1/compute-budget-instruction/src/compute_budget_instruction_details.rs#L54-L99 */ int -fd_executor_compute_budget_program_execute_instructions( fd_bank_t * bank, +fd_executor_compute_budget_program_execute_instructions( fd_bank_t const * bank, fd_txn_in_t const * txn_in, fd_txn_out_t * txn_out ) { fd_compute_budget_details_t * details = &txn_out->details.compute_budget; diff --git a/src/flamenco/runtime/program/fd_compute_budget_program.h b/src/flamenco/runtime/program/fd_compute_budget_program.h index 650bd87ab56..aec261d8c44 100644 --- a/src/flamenco/runtime/program/fd_compute_budget_program.h +++ b/src/flamenco/runtime/program/fd_compute_budget_program.h @@ -30,7 +30,7 @@ int fd_sanitize_compute_unit_limits( fd_txn_out_t * txn_out ); int -fd_executor_compute_budget_program_execute_instructions( fd_bank_t * bank, +fd_executor_compute_budget_program_execute_instructions( fd_bank_t const * bank, fd_txn_in_t const * txn_in, fd_txn_out_t * txn_out ); diff --git a/src/flamenco/runtime/test_static_instruction_limit.c b/src/flamenco/runtime/test_static_instruction_limit.c new file mode 100644 index 00000000000..9ec3586c87a --- /dev/null +++ b/src/flamenco/runtime/test_static_instruction_limit.c @@ -0,0 +1,126 @@ +/* Test for SIMD-0160 static_instruction_limit */ + +#include "fd_executor.h" +#include "fd_bank.h" +#include "fd_runtime.h" +#include "fd_runtime_err.h" +#include "../features/fd_features.h" + +static void +init_bank( fd_bank_t * bank ) { + memset( bank, 0, sizeof(fd_bank_t) ); + fd_bank_slot_set( bank, 1UL ); +} + +static void +init_txn_with_instr_cnt( fd_txn_p_t * txn_p, + ushort instr_cnt ) { + fd_txn_t * txn = TXN( txn_p ); + + txn->transaction_version = FD_TXN_V0; + txn->instr_cnt = instr_cnt; + + for( ushort i=0; iinstr[i].program_id = 0; + txn->instr[i].acct_cnt = 0; + txn->instr[i].data_sz = 0; + txn->instr[i].acct_off = 0; + txn->instr[i].data_off = 0; + } +} + +static void +activate_static_instruction_limit( fd_bank_t * bank ) { + fd_bank_features_modify( bank )->static_instruction_limit = 0UL; +} + +static void +deactivate_static_instruction_limit( fd_bank_t * bank ) { + fd_bank_features_modify( bank )->static_instruction_limit = FD_FEATURE_DISABLED; +} + +static void +test_static_instruction_limit_deactivated( fd_bank_t * bank ) { + fd_txn_p_t txn_p[1] = {0}; + fd_txn_out_t txn_out[1] = {0}; + + init_bank( bank ); + deactivate_static_instruction_limit( bank ); + init_txn_with_instr_cnt( txn_p, 65 ); + + fd_txn_in_t txn_in = { .txn = txn_p }; + + FD_TEST( fd_executor_verify_transaction( bank, &txn_in, txn_out )==FD_RUNTIME_EXECUTE_SUCCESS ); +} + +static void +test_static_instruction_limit_exceeded( fd_bank_t * bank ) { + fd_txn_p_t txn_p[1] = {0}; + fd_txn_out_t txn_out[1] = {0}; + + init_bank( bank ); + activate_static_instruction_limit( bank ); + init_txn_with_instr_cnt( txn_p, 65 ); + + fd_txn_in_t txn_in = { .txn = txn_p }; + + FD_TEST( fd_executor_verify_transaction( bank, &txn_in, txn_out )==FD_RUNTIME_TXN_ERR_SANITIZE_FAILURE ); +} + +static void +test_static_instruction_limit_at_limit( fd_bank_t * bank ) { + fd_txn_p_t txn_p[1] = {0}; + fd_txn_out_t txn_out[1] = {0}; + + init_bank( bank ); + activate_static_instruction_limit( bank ); + init_txn_with_instr_cnt( txn_p, 64 ); + + fd_txn_in_t txn_in = { .txn = txn_p }; + + FD_TEST( fd_executor_verify_transaction( bank, &txn_in, txn_out )==FD_RUNTIME_EXECUTE_SUCCESS ); +} + +static void +test_static_instruction_limit_under_limit( fd_bank_t * bank ) { + fd_txn_p_t txn_p[1] = {0}; + fd_txn_out_t txn_out[1] = {0}; + + init_bank( bank ); + activate_static_instruction_limit( bank ); + init_txn_with_instr_cnt( txn_p, 1 ); + + fd_txn_in_t txn_in = { .txn = txn_p }; + + FD_TEST( fd_executor_verify_transaction( bank, &txn_in, txn_out )==FD_RUNTIME_EXECUTE_SUCCESS ); +} + +int +main( int argc, + char ** argv ) { + fd_boot( &argc, &argv ); + + char * _page_sz = "normal"; + ulong page_cnt = 710UL; + ulong numa_idx = fd_shmem_numa_idx( 0 ); + fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz( _page_sz ), + page_cnt, + fd_shmem_cpu_idx( numa_idx ), + "wksp", + 0UL ); + FD_TEST( wksp ); + + fd_bank_t * bank = fd_wksp_alloc_laddr( wksp, alignof(fd_bank_t), sizeof(fd_bank_t), 1UL ); + FD_TEST( bank ); + + test_static_instruction_limit_deactivated( bank ); + test_static_instruction_limit_exceeded( bank ); + test_static_instruction_limit_at_limit( bank ); + test_static_instruction_limit_under_limit( bank ); + + fd_wksp_delete_anonymous( wksp ); + + FD_LOG_NOTICE(( "pass" )); + fd_halt(); + return 0; +} diff --git a/src/flamenco/runtime/tests/run_backtest_all.sh b/src/flamenco/runtime/tests/run_backtest_all.sh index e8691f6fdc0..abbe32d7381 100755 --- a/src/flamenco/runtime/tests/run_backtest_all.sh +++ b/src/flamenco/runtime/tests/run_backtest_all.sh @@ -95,3 +95,4 @@ src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-376969880-r2 -y 1 - src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-376969880-simd-339 -y 1 -m 2000000 -e 376969900 -o H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5 src/flamenco/runtime/tests/run_ledger_backtest.sh -l breakpoint-385786458 -y 1 -m 2000000 -e 385786458 src/flamenco/runtime/tests/run_ledger_backtest.sh -l localnet-deprecate-rent-exemption-threshold -y 1 -m 1000 -e 260 +src/flamenco/runtime/tests/run_ledger_backtest.sh -l localnet-static-instruction-limit -y 1 -m 1000 -e 191