diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 5e81062c504e97..004ca448bf6d69 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -1432,6 +1432,27 @@ pub fn execute<'a, 'b: 'a>( Err(Box::new(error) as Box) } ProgramResult::Err(mut error) => { +<<<<<<< HEAD +======= + if invoke_context + .get_feature_set() + .is_active(&solana_feature_set::deplete_cu_meter_on_vm_failure::id()) + && !matches!(error, EbpfError::SyscallError(_)) + { + // when an exception is thrown during the execution of a + // Basic Block (e.g., a null memory dereference or other + // faults), determining the exact number of CUs consumed + // up to the point of failure requires additional effort + // and is unnecessary since these cases are rare. + // + // In order to simplify CU tracking, simply consume all + // remaining compute units so that the block cost + // tracker uses the full requested compute unit cost for + // this failed transaction. + invoke_context.consume(invoke_context.get_remaining()); + } + +>>>>>>> 11467d9221 (use new feature gate for cu depletion (#3994)) if direct_mapping { if let EbpfError::AccessViolation( AccessType::Store, diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index e7a51b6cdb151b..a20d9b0786cc95 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -4603,6 +4603,137 @@ fn test_cpi_invalid_account_info_pointers() { #[test] #[cfg(feature = "sbf_rust")] +<<<<<<< HEAD +======= +fn test_deplete_cost_meter_with_access_violation() { + solana_logger::setup(); + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(100_123_456_789); + + for deplete_cu_meter_on_vm_failure in [false, true] { + let mut bank = Bank::new_for_tests(&genesis_config); + let feature_set = Arc::make_mut(&mut bank.feature_set); + // by default test banks have all features enabled, so we only need to + // disable when needed + if !deplete_cu_meter_on_vm_failure { + feature_set.deactivate(&feature_set::deplete_cu_meter_on_vm_failure::id()); + } + let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); + let mut bank_client = BankClient::new_shared(bank.clone()); + let authority_keypair = Keypair::new(); + let (bank, invoke_program_id) = load_upgradeable_program_and_advance_slot( + &mut bank_client, + bank_forks.as_ref(), + &mint_keypair, + &authority_keypair, + "solana_sbf_rust_invoke", + ); + + let account_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let account_metas = vec![ + AccountMeta::new(mint_pubkey, true), + AccountMeta::new(account_keypair.pubkey(), false), + AccountMeta::new_readonly(invoke_program_id, false), + ]; + + let mut instruction_data = vec![TEST_WRITE_ACCOUNT, 2]; + instruction_data.extend_from_slice(3usize.to_le_bytes().as_ref()); + instruction_data.push(42); + + let instruction = Instruction::new_with_bytes( + invoke_program_id, + &instruction_data, + account_metas.clone(), + ); + + let compute_unit_limit = 10_000u32; + let message = Message::new( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), + instruction, + ], + Some(&mint_keypair.pubkey()), + ); + let tx = Transaction::new(&[&mint_keypair], message, bank.last_blockhash()); + + let result = load_execute_and_commit_transaction(&bank, tx).unwrap(); + + assert_eq!( + result.status.unwrap_err(), + TransactionError::InstructionError(1, InstructionError::ReadonlyDataModified) + ); + + if deplete_cu_meter_on_vm_failure { + assert_eq!(result.executed_units, u64::from(compute_unit_limit)); + } else { + assert!(result.executed_units < u64::from(compute_unit_limit)); + } + } +} + +#[test] +#[cfg(feature = "sbf_rust")] +fn test_program_sbf_deplete_cost_meter_with_divide_by_zero() { + solana_logger::setup(); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + + for deplete_cu_meter_on_vm_failure in [false, true] { + let mut bank = Bank::new_for_tests(&genesis_config); + let feature_set = Arc::make_mut(&mut bank.feature_set); + // by default test banks have all features enabled, so we only need to + // disable when needed + if !deplete_cu_meter_on_vm_failure { + feature_set.deactivate(&feature_set::deplete_cu_meter_on_vm_failure::id()); + } + let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); + let mut bank_client = BankClient::new_shared(bank.clone()); + let authority_keypair = Keypair::new(); + let (bank, program_id) = load_upgradeable_program_and_advance_slot( + &mut bank_client, + bank_forks.as_ref(), + &mint_keypair, + &authority_keypair, + "solana_sbf_rust_divide_by_zero", + ); + + let instruction = Instruction::new_with_bytes(program_id, &[], vec![]); + let compute_unit_limit = 10_000; + let message = Message::new( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), + instruction, + ], + Some(&mint_keypair.pubkey()), + ); + let tx = Transaction::new(&[&mint_keypair], message, bank.last_blockhash()); + + let result = load_execute_and_commit_transaction(&bank, tx).unwrap(); + + assert_eq!( + result.status.unwrap_err(), + TransactionError::InstructionError(1, InstructionError::ProgramFailedToComplete) + ); + + if deplete_cu_meter_on_vm_failure { + assert_eq!(result.executed_units, u64::from(compute_unit_limit)); + } else { + assert!(result.executed_units < u64::from(compute_unit_limit)); + } + } +} + +#[test] +#[cfg(feature = "sbf_rust")] +>>>>>>> 11467d9221 (use new feature gate for cu depletion (#3994)) fn test_deny_executable_write() { solana_logger::setup(); diff --git a/sdk/feature-set/src/lib.rs b/sdk/feature-set/src/lib.rs index fd7c2ad31fe18d..295c2a6deeb1e9 100644 --- a/sdk/feature-set/src/lib.rs +++ b/sdk/feature-set/src/lib.rs @@ -881,6 +881,17 @@ pub mod migrate_stake_program_to_core_bpf { solana_pubkey::declare_id!("6M4oQ6eXneVhtLoiAr4yRYQY43eVLjrKbiDZDJc892yk"); } +<<<<<<< HEAD +======= +pub mod deplete_cu_meter_on_vm_failure { + solana_pubkey::declare_id!("B7H2caeia4ZFcpE3QcgMqbiWiBtWrdBRBSJ1DY6Ktxbq"); +} + +pub mod reserve_minimal_cus_for_builtin_instructions { + solana_pubkey::declare_id!("C9oAhLxDBm3ssWtJx1yBGzPY55r2rArHmN1pbQn6HogH"); +} + +>>>>>>> 11467d9221 (use new feature gate for cu depletion (#3994)) lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -1096,6 +1107,11 @@ lazy_static! { (disable_account_loader_special_case::id(), "Disable account loader special case #3513"), (enable_secp256r1_precompile::id(), "Enable secp256r1 precompile SIMD-0075"), (migrate_stake_program_to_core_bpf::id(), "Migrate Stake program to Core BPF SIMD-0196 #3655"), +<<<<<<< HEAD +======= + (deplete_cu_meter_on_vm_failure::id(), "Deplete compute meter for vm errors SIMD-0182 #3993"), + (reserve_minimal_cus_for_builtin_instructions::id(), "Reserve minimal CUs for builtin instructions SIMD-170 #2562"), +>>>>>>> 11467d9221 (use new feature gate for cu depletion (#3994)) /*************** ADD NEW FEATURES HERE ***************/ ] .iter()