From b2e626b731c613eff6388d9b64eb41a9bcba5be9 Mon Sep 17 00:00:00 2001 From: AMATH <116212274+amathxbt@users.noreply.github.com> Date: Sun, 3 May 2026 02:37:43 -0700 Subject: [PATCH] fix: prevent u64 underflow in validator_claim_fees and use on-chain Rent sysvar Two related bugs in process_validator_claim_fees: 1. Rent::default() returns hardcoded default values that may differ from the actual on-chain rent schedule. Replaced with Rent::get()? which reads the current on-chain Sysvar. 2. Both occurrences of validator_fees_vault.lamports() - min_rent were plain u64 subtractions. If the vault somehow holds fewer lamports than min_rent (e.g. due to a prior accounting bug or a griefing vector that drains rent below the exemption threshold), the subtraction wraps around to a very large u64 in release builds, bypassing the insufficient-funds guard and potentially allowing an attacker to drain an arbitrary amount. Fix: use checked_sub(...).ok_or(ProgramError::InsufficientFunds) so the instruction returns an error instead of underflowing. --- src/processor/validator_claim_fees.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/processor/validator_claim_fees.rs b/src/processor/validator_claim_fees.rs index 516851ec..109d75b0 100644 --- a/src/processor/validator_claim_fees.rs +++ b/src/processor/validator_claim_fees.rs @@ -2,6 +2,7 @@ use borsh::BorshDeserialize; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, rent::Rent, + sysvar::Sysvar, }; use crate::{ @@ -49,18 +50,26 @@ pub fn process_validator_claim_fees( true, )?; - // Calculate the amount to transfer - let min_rent = Rent::default().minimum_balance(8); - let amount = args - .amount - .unwrap_or(validator_fees_vault.lamports() - min_rent); + // Use the on-chain Rent sysvar (not `Rent::default()`) and the actual + // account data length so the minimum-balance threshold is always accurate. + let min_rent = + Rent::get()?.minimum_balance(validator_fees_vault.data_len()); + + // Guard against underflow: if the vault somehow holds fewer lamports than + // the rent-exempt minimum, return an error instead of wrapping around. + let available = validator_fees_vault + .lamports() + .checked_sub(min_rent) + .ok_or(ProgramError::InsufficientFunds)?; + + let amount = args.amount.unwrap_or(available); // Ensure vault has enough lamports - if validator_fees_vault.lamports() - min_rent < amount { + if available < amount { msg!( "Vault ({}) has insufficient funds: {} < {}", validator_fees_vault.key, - validator_fees_vault.lamports() - min_rent, + available, amount ); return Err(ProgramError::InsufficientFunds);