From f338b0cd42113691566a000d44cc6432a46c8a02 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Tue, 17 Sep 2024 09:12:14 +0000 Subject: [PATCH] Add CPI and instruction stack prechecks --- .../runtime/context/fd_exec_txn_ctx.h | 58 ++++++------- src/flamenco/runtime/fd_executor.c | 84 +++++++++++++------ 2 files changed, 90 insertions(+), 52 deletions(-) diff --git a/src/flamenco/runtime/context/fd_exec_txn_ctx.h b/src/flamenco/runtime/context/fd_exec_txn_ctx.h index f721507b1c..8f0ce96a35 100644 --- a/src/flamenco/runtime/context/fd_exec_txn_ctx.h +++ b/src/flamenco/runtime/context/fd_exec_txn_ctx.h @@ -51,6 +51,8 @@ typedef struct fd_exec_instr_trace_entry fd_exec_instr_trace_entry_t; /* https://github.com/anza-xyz/agave/blob/0d34a1a160129c4293dac248e14231e9e773b4ce/program-runtime/src/compute_budget.rs#L139 */ #define FD_MAX_INSTRUCTION_TRACE_LENGTH (64UL) +/* https://github.com/firedancer-io/agave/blob/f70ab5598ccd86b216c3928e4397bf4a5b58d723/compute-budget/src/compute_budget.rs#L13 */ +#define FD_MAX_INSTRUCTION_STACK_DEPTH (5UL) struct __attribute__((aligned(8UL))) fd_exec_txn_ctx { ulong magic; /* ==FD_EXEC_TXN_CTX_MAGIC */ @@ -63,36 +65,36 @@ struct __attribute__((aligned(8UL))) fd_exec_txn_ctx { fd_valloc_t valloc; ulong paid_fees; - ulong compute_unit_limit; /* Compute unit limit for this transaction. */ - ulong compute_unit_price; /* Compute unit price for this transaction. */ - ulong compute_meter; /* Remaining compute units */ - ulong heap_size; /* Heap size for VMs for this transaction. */ - ulong loaded_accounts_data_size_limit; /* Loaded accounts data size limit for this transaction. */ - uint prioritization_fee_type; /* The type of prioritization fee to use. */ - fd_txn_t const * txn_descriptor; /* Descriptor of the transaction. */ - fd_rawtxn_b_t _txn_raw[1]; /* Raw bytes of the transaction. */ - uint custom_err; /* When a custom error is returned, this is where the numeric value gets stashed */ - uchar instr_stack_sz; /* Current depth of the instruction execution stack. */ - fd_exec_instr_ctx_t instr_stack[6]; /* Instruction execution stack. */ + ulong compute_unit_limit; /* Compute unit limit for this transaction. */ + ulong compute_unit_price; /* Compute unit price for this transaction. */ + ulong compute_meter; /* Remaining compute units */ + ulong heap_size; /* Heap size for VMs for this transaction. */ + ulong loaded_accounts_data_size_limit; /* Loaded accounts data size limit for this transaction. */ + uint prioritization_fee_type; /* The type of prioritization fee to use. */ + fd_txn_t const * txn_descriptor; /* Descriptor of the transaction. */ + fd_rawtxn_b_t _txn_raw[1]; /* Raw bytes of the transaction. */ + uint custom_err; /* When a custom error is returned, this is where the numeric value gets stashed */ + uchar instr_stack_sz; /* Current depth of the instruction execution stack. */ + fd_exec_instr_ctx_t instr_stack[FD_MAX_INSTRUCTION_STACK_DEPTH]; /* Instruction execution stack. */ fd_exec_instr_ctx_t * failed_instr; int instr_err_idx; - ulong accounts_cnt; /* Number of account pubkeys accessed by this transaction. */ - fd_pubkey_t accounts[128]; /* Array of account pubkeys accessed by this transaction. */ - ulong executable_cnt; /* Number of BPF upgradeable loader accounts. */ - fd_borrowed_account_t executable_accounts[128]; /* Array of BPF upgradeable loader program data accounts */ - fd_borrowed_account_t borrowed_accounts[128]; /* Array of borrowed accounts accessed by this transaction. */ - uchar nonce_accounts[128]; /* Nonce accounts in the txn to be saved */ - uint num_instructions; /* Counter for number of instructions in txn */ - fd_txn_return_data_t return_data; /* Data returned from `return_data` syscalls */ - fd_vote_account_cache_t * vote_accounts_map; /* Cache of bank's deserialized vote accounts to support fork choice */ - fd_vote_account_cache_entry_t * vote_accounts_pool; /* Memory pool for deserialized vote account cache */ - ulong accounts_resize_delta; /* Transaction level tracking for account resizing */ - fd_hash_t blake_txn_msg_hash; /* Hash of raw transaction message used by the status cache */ - ulong execution_fee; /* Execution fee paid by the fee payer in the transaction */ - ulong priority_fee; /* Priority fee paid by the fee payer in the transaction */ - - uchar dirty_vote_acc : 1; /* 1 if this transaction maybe modified a vote account */ - uchar dirty_stake_acc : 1; /* 1 if this transaction maybe modified a stake account */ + ulong accounts_cnt; /* Number of account pubkeys accessed by this transaction. */ + fd_pubkey_t accounts[128]; /* Array of account pubkeys accessed by this transaction. */ + ulong executable_cnt; /* Number of BPF upgradeable loader accounts. */ + fd_borrowed_account_t executable_accounts[128]; /* Array of BPF upgradeable loader program data accounts */ + fd_borrowed_account_t borrowed_accounts[128]; /* Array of borrowed accounts accessed by this transaction. */ + uchar nonce_accounts[128]; /* Nonce accounts in the txn to be saved */ + uint num_instructions; /* Counter for number of instructions in txn */ + fd_txn_return_data_t return_data; /* Data returned from `return_data` syscalls */ + fd_vote_account_cache_t * vote_accounts_map; /* Cache of bank's deserialized vote accounts to support fork choice */ + fd_vote_account_cache_entry_t * vote_accounts_pool; /* Memory pool for deserialized vote account cache */ + ulong accounts_resize_delta; /* Transaction level tracking for account resizing */ + fd_hash_t blake_txn_msg_hash; /* Hash of raw transaction message used by the status cache */ + ulong execution_fee; /* Execution fee paid by the fee payer in the transaction */ + ulong priority_fee; /* Priority fee paid by the fee payer in the transaction */ + + uchar dirty_vote_acc : 1; /* 1 if this transaction maybe modified a vote account */ + uchar dirty_stake_acc : 1; /* 1 if this transaction maybe modified a stake account */ fd_capture_ctx_t * capture_ctx; diff --git a/src/flamenco/runtime/fd_executor.c b/src/flamenco/runtime/fd_executor.c index 3c07304b9a..97241a895f 100644 --- a/src/flamenco/runtime/fd_executor.c +++ b/src/flamenco/runtime/fd_executor.c @@ -909,31 +909,68 @@ dump_instr_to_protobuf( fd_exec_txn_ctx_t *txn_ctx, } FD_SCRATCH_SCOPE_END; } +/* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L319-L357 */ +int +fd_txn_ctx_push( fd_exec_txn_ctx_t * txn_ctx ) { + /* Earlier checks in the permalink are redundant since Agave maintains instr stack and trace accounts separately + https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L347-L351 */ + if( txn_ctx->instr_trace_length>=FD_MAX_INSTRUCTION_TRACE_LENGTH ) { + return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED; + } + txn_ctx->instr_trace_length++; + + /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/sdk/src/transaction_context.rs#L352-L356 */ + if( txn_ctx->instr_stack_sz>=FD_MAX_INSTRUCTION_STACK_DEPTH ) { + return FD_EXECUTOR_INSTR_ERR_CALL_DEPTH; + } + txn_ctx->instr_stack_sz++; + + return FD_EXECUTOR_INSTR_SUCCESS; +} + +/* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L246-L290 */ +int +fd_instr_stack_push( fd_exec_txn_ctx_t * txn_ctx, + fd_instr_info_t const * instr ) { + /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L256-L286 */ + if( txn_ctx->instr_stack_sz ) { + /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L261-L285 */ + uchar contains = 0; + uchar is_last = 0; + for( uchar level=0; levelinstr_stack_sz; level++ ) { + fd_exec_instr_ctx_t * instr_ctx = &txn_ctx->instr_stack[level]; + if( !memcmp( instr->program_id_pubkey.uc, instr_ctx->instr->program_id_pubkey.uc, sizeof(fd_pubkey_t) ) ) { + if( level == txn_ctx->instr_stack_sz-1 ) { + is_last = 1; + } + contains = 1; + } + } + /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L282-L285 */ + if( FD_UNLIKELY( contains && !is_last ) ) { + return FD_EXECUTOR_INSTR_ERR_REENTRANCY_NOT_ALLOWED; + } + } + /* https://github.com/anza-xyz/agave/blob/c4b42ab045860d7b13b3912eafb30e6d2f4e593f/program-runtime/src/invoke_context.rs#L289 */ + return fd_txn_ctx_push( txn_ctx ); +} + int fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx, fd_instr_info_t * instr ) { FD_SCRATCH_SCOPE_BEGIN { - ulong max_num_instructions = FD_FEATURE_ACTIVE( txn_ctx->slot_ctx, limit_max_instruction_trace_length ) ? FD_MAX_INSTRUCTION_TRACE_LENGTH : ULONG_MAX; - if( txn_ctx->num_instructions >= max_num_instructions ) { - return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED; - } - txn_ctx->num_instructions++; fd_pubkey_t const * txn_accs = txn_ctx->accounts; - ulong starting_lamports_h = 0; - ulong starting_lamports_l = 0; - int err = fd_instr_info_sum_account_lamports( instr, &starting_lamports_h, &starting_lamports_l ); - if( err ) { - return err; - } - instr->starting_lamports_h = starting_lamports_h; - instr->starting_lamports_l = starting_lamports_l; - fd_exec_instr_ctx_t * parent = NULL; if( txn_ctx->instr_stack_sz ) parent = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz - 1 ]; - fd_exec_instr_ctx_t * ctx = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz++ ]; + int err = fd_instr_stack_push( txn_ctx, instr ); + if( FD_UNLIKELY( err ) ) { + return err; + } + + fd_exec_instr_ctx_t * ctx = &txn_ctx->instr_stack[ txn_ctx->instr_stack_sz - 1 ]; *ctx = (fd_exec_instr_ctx_t) { .instr = instr, .txn_ctx = txn_ctx, @@ -948,21 +985,20 @@ fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx, .child_cnt = 0U, }; - /* Add the instruction to the trace */ - txn_ctx->instr_trace[ txn_ctx->instr_trace_length++ ] = (fd_exec_instr_trace_entry_t) { + txn_ctx->instr_trace[ txn_ctx->instr_trace_length - 1 ] = (fd_exec_instr_trace_entry_t) { .instr_info = instr, .stack_height = txn_ctx->instr_stack_sz, }; - // defense in depth - if( instr->program_id >= txn_ctx->txn_descriptor->acct_addr_cnt + txn_ctx->txn_descriptor->addr_table_adtl_cnt ) { - FD_LOG_WARNING(( "INVALID PROGRAM ID, RUNTIME BUG!!!" )); - int exec_result = FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS; + ulong starting_lamports_h = 0; + ulong starting_lamports_l = 0; + err = fd_instr_info_sum_account_lamports( instr, &starting_lamports_h, &starting_lamports_l ); + if( FD_UNLIKELY( err ) ) { txn_ctx->instr_stack_sz--; - - FD_LOG_WARNING(( "instruction executed unsuccessfully: error code %d", exec_result )); - return exec_result; + return err; } + instr->starting_lamports_h = starting_lamports_h; + instr->starting_lamports_l = starting_lamports_l; fd_exec_instr_fn_t native_prog_fn = fd_executor_lookup_native_program( &txn_ctx->borrowed_accounts[ instr->program_id ] ); fd_pubkey_t const * program_id = &txn_accs[ instr->program_id ];