Skip to main content
Sigvex

DoS Compute Exhaustion Remediation

How to fix denial-of-service via compute unit exhaustion.

DoS Compute Exhaustion Remediation

Overview

Related Detector: DoS Compute Exhaustion

Unbounded loops and CPI calls controlled by user input can exhaust Solana’s compute budget. The fix is to impose explicit upper bounds on all iteration counts, validate input lengths before processing, and avoid CPI calls inside loops where possible.

Before (Vulnerable)

// Vulnerable: user controls iteration count
pub fn distribute(ctx: Context<Dist>, recipients: Vec<Pubkey>) -> Result<()> {
    for recipient in recipients.iter() {
        transfer_to(ctx.accounts, recipient, amount)?;
    }
    Ok(())
}

After (Fixed)

const MAX_RECIPIENTS: usize = 5;

pub fn distribute(ctx: Context<Dist>, recipients: Vec<Pubkey>) -> Result<()> {
    require!(recipients.len() <= MAX_RECIPIENTS, ErrorCode::TooManyRecipients);
    for recipient in recipients.iter() {
        transfer_to(ctx.accounts, recipient, amount)?;
    }
    Ok(())
}

Alternative Mitigations

Paginated Processing

For operations that must handle large datasets, use pagination:

pub fn process_batch(
    ctx: Context<Process>,
    start_index: u32,
    batch_size: u32,
) -> Result<()> {
    require!(batch_size <= MAX_BATCH_SIZE, ErrorCode::BatchTooLarge);
    let end = start_index.checked_add(batch_size).unwrap();
    for i in start_index..end {
        process_single(ctx.accounts, i)?;
    }
    Ok(())
}

Common Mistakes

Mistake: Trusting Account Data Length

// WRONG: attacker can create account with large data
let items: Vec<Item> = BorshDeserialize::deserialize(&mut &account.data.borrow()[..])?;
for item in items.iter() { /* ... */ }

Always validate deserialized collection lengths before iterating.

References