Storage Collision

Critical vulnerability in proxy contracts where proxy and implementation use the same storage slot for different variables, causing state corruption.

Storage collision is a critical vulnerability in upgradeable smart contracts that occurs when a proxy and its implementation contract inadvertently use the same storage slot for different state variables. Because delegatecall executes implementation code using the proxy's storage, misaligned storage layouts cause variables to overwrite each other—potentially corrupting critical state, bricking contracts, or enabling complete protocol takeover. Storage collisions represent one of the most severe risks in upgradeable contract architectures.

How storage collisions occur

The EVM stores state variables in sequential 32-byte slots. When a proxy uses delegatecall to an implementation, both contracts share the same storage space:

1// Proxy contract
2contract Proxy {
3 address public implementation; // Slot 0
4 address public admin; // Slot 1
5}
6
7// Implementation contract
8contract ImplementationV1 {
9 uint256 public totalSupply; // Slot 0 - COLLISION!
10 mapping(address => uint256) public balances; // Slot 1 - COLLISION!
11}

When the implementation writes to totalSupply, it actually overwrites the proxy's implementation address. This can:

  • Redirect all future calls to an arbitrary address
  • Brick the contract by pointing to invalid code
  • Enable attackers to inject malicious implementation addresses

Real-world collision scenarios

Upgrade-induced collision

A common scenario occurs during upgrades when V2 introduces new variables:

1// V1 Implementation (working correctly)
2contract V1 {
3 uint256 public value; // Slot 0
4 address public owner; // Slot 1
5}
6
7// V2 Implementation (introduces collision)
8contract V2 {
9 address public newAdmin; // Slot 0 - interprets value as address!
10 uint256 public value; // Slot 1 - reads owner as uint256!
11 address public owner; // Slot 2
12}

After upgrading, V2 misinterprets V1's data: value (a uint256) is read as newAdmin (an address), causing unpredictable behavior.

Proxy admin collision

If the proxy stores admin variables in low slots:

1contract Proxy {
2 address implementation; // Slot 0
3 address admin; // Slot 1
4 bool initialized; // Slot 2
5}
6
7contract Implementation {
8 uint256 balance; // Slot 0 - overwrites implementation!
9 uint256 totalSupply; // Slot 1 - overwrites admin!
10}

A simple balance = 0 in the implementation could zero out the proxy's implementation address, bricking the entire contract.

Prevention: Unstructured storage (EIP-1967)

Modern proxy standards prevent collisions by storing administrative variables in pseudo-random slots derived from hashes:

1// EIP-1967 standard slots
2bytes32 constant IMPLEMENTATION_SLOT =
3 bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);
4// = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
5
6bytes32 constant ADMIN_SLOT =
7 bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1);
8
9function _getImplementation() internal view returns (address impl) {
10 bytes32 slot = IMPLEMENTATION_SLOT;
11 assembly {
12 impl := sload(slot)
13 }
14}

The probability of a Solidity variable naturally landing on these slots is cryptographically negligible (~1 in 2^256).

Prevention: Storage gaps

For upgradeable base contracts, reserve storage slots for future variables:

1contract BaseV1 {
2 uint256 public value;
3 address public owner;
4
5 // Reserve 50 slots for future upgrades
6 uint256[50] private __gap;
7}
8
9contract BaseV2 is BaseV1 {
10 uint256 public newValue; // Uses slot from __gap
11
12 // Reduce gap by 1
13 uint256[49] private __gap;
14}

This ensures new variables in upgrades don't shift existing storage layout.

Prevention: EIP-7201 namespaced storage

The latest standard uses struct-based namespaced storage:

1/// @custom:storage-location erc7201:myprotocol.main
2struct MainStorage {
3 uint256 totalSupply;
4 mapping(address => uint256) balances;
5}
6
7// Storage location calculated as:
8// keccak256(abi.encode(uint256(keccak256("myprotocol.main")) - 1)) & ~bytes32(uint256(0xff))

Each storage namespace gets its own isolated slot range, preventing cross-contract collisions entirely.

Detection during audits

Static analysis tools

Tools like Slither detect potential collisions:

1slither . --detect storage-layout

Foundry can inspect storage layouts:

1forge inspect ContractName storage-layout

Manual verification checklist

When auditing upgradeable contracts:

  • Proxy uses EIP-1967 or similar unstructured storage
  • Implementation inherits same base contracts as previous version
  • New variables are added at the end, not inserted
  • Storage gaps are properly maintained
  • No reordering of existing variables
  • Mappings and dynamic arrays don't shift

Storage collision vs type collision

Storage collision (same slot, different variables) differs from type collision (same slot, different types):

1// Type collision example
2contract V1 {
3 uint256 public value; // Slot 0: stored as 256-bit integer
4}
5
6contract V2 {
7 address public value; // Slot 0: interpreted as 160-bit address
8}

V2 reads V1's uint256 as an address, potentially extracting valid-looking addresses from arbitrary numbers—enabling attackers to craft specific values that become exploitable addresses.

Why storage collisions are critical

Storage collisions rank among the most severe smart contract vulnerabilities because they:

  1. Bypass all access controls - Overwriting admin variables grants unauthorized access
  2. Are often silent - Contracts may appear functional while corrupted
  3. Can be irreversible - Bricked proxies may trap funds permanently
  4. Affect all users simultaneously - One collision impacts every user of the proxy
  5. Occur during routine operations - Normal function calls can trigger corruption

Understanding storage collision is essential for anyone building or auditing upgradeable protocols. The separation of storage and logic that enables upgradeability also creates this unique attack surface requiring careful layout management across all implementation versions.

Need expert guidance on Storage Collision?

Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.

Get a Quote

oog
zealynx

Subscribe to Our Newsletter

Stay updated with our latest security insights and blog posts

© 2024 Zealynx