Borsh Length DoS Remediation
Overview
Related Detector: Borsh Length DoS
Unbounded Borsh length prefixes enable compute budget exhaustion. The fix is to validate the length prefix against a maximum bound before deserialization and to use the account-validation framework’s max_len attribute for compile-time safety.
Impact. A length prefix the program trusts without bounding lets an attacker declare an enormous collection and force allocation and deserialization of it, burning through the transaction’s compute budget until the instruction reverts. Aimed at a critical path, this is a denial of service — legitimate operations such as withdrawals or liquidations fail for as long as the attacker keeps submitting oversized input.
Recommended Fix
Before (Vulnerable)
let items: Vec<Item> = BorshDeserialize::deserialize(&mut &data[..])?;
After (Fixed)
const MAX_ITEMS: usize = 100;
let len = u32::from_le_bytes(data[0..4].try_into()?) as usize;
require!(len <= MAX_ITEMS, ErrorCode::TooManyItems);
let items: Vec<Item> = BorshDeserialize::deserialize(&mut &data[..])?;
Alternative Mitigations
Anchor max_len
#[account]
#[derive(InitSpace)]
pub struct MyData {
#[max_len(100)]
pub items: Vec<Item>,
}
Common Mistakes
Mistake: Checking After Deserialization
// WRONG: allocation already happened
let items: Vec<Item> = BorshDeserialize::deserialize(&mut &data[..])?;
require!(items.len() <= MAX_ITEMS, TooMany);
Check the length prefix bytes before calling deserialize.