Arbitrary Storage Write Remediation
Overview
EVM state lives in a flat 2^256-slot space. If a function lets the caller choose the slot index for an SSTORE, the caller can overwrite the owner variable, a proxy implementation address, or any balance, giving them the same power as the contract owner. The fix is to never write to a raw caller-supplied slot: route writes through mappings or arrays, whose slots are derived by hashing, and gate state changes with access control.
Related Detector: Arbitrary Storage Write
Recommended Fix
Before (Vulnerable)
// User controls storage slot
function writeSlot(uint256 slot, uint256 value) external {
assembly {
sstore(slot, value) // Attacker can overwrite any slot
}
}
After (Fixed)
mapping(uint256 => uint256) public data;
// Mapping hash prevents arbitrary slot access
function writeData(uint256 key, uint256 value) external onlyOwner {
data[key] = value;
}
A mapping computes its slot as keccak256(key, baseSlot), so the caller cannot target the owner slot or a proxy slot by choosing the key. Combined with onlyOwner, only authorized accounts can change state at all.
Alternative Mitigations
- If raw slot access is truly required (for example, a migration tool), validate the slot against an explicit allowlist of permitted slots before writing.
- For proxy patterns, use the standardized EIP-1967 slots rather than computing slots from input.
- Keep all assembly storage writes behind access-controlled functions and never derive the slot from calldata.
Common Mistakes
- Assuming an
assembly { sstore(...) }block is safe because it is “low level”; the slot is still attacker-controlled. - Bounds-checking the value but not the slot index.
- Writing to
array.lengthdirectly, which can expand an array over adjacent storage.