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.
Recommended Fix
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.