Skip to main content
Sigvex

Conditional Storage Collision Remediation

How to prevent path-dependent storage slot collisions between a proxy and its implementation by anchoring administrative state in standard EIP-1967 slots.

Conditional Storage Collision Remediation

Overview

A conditional storage collision is a slot conflict between a proxy and its implementation that only manifests on certain paths — when a mapping entry is first written, or a rarely taken branch executes. Because it does not surface in routine testing, it behaves like a latent fault that corrupts state once the triggering condition occurs. The fix is the same discipline that prevents unconditional collisions: keep proxy-level administrative state out of the implementation’s sequential layout by storing it at fixed, hashed EIP-1967 slots.

Related Detector: Conditional Storage Collision

Before (Vulnerable)

// Implementation's first variable lands on a slot the proxy also uses
contract Implementation {
    address public admin;   // slot 0 — overlaps proxy bookkeeping
}

After (Fixed)

contract Implementation {
    // EIP-1967 admin slot: keccak256("eip1967.proxy.admin") - 1
    bytes32 private constant _ADMIN_SLOT =
        0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    function _admin() internal view returns (address a) {
        assembly { a := sload(_ADMIN_SLOT) }
    }
}

Administrative fields now live at slots derived from a hash, far from the sequential range where implementation variables and mapping/array data are written.

Alternative Mitigations

Namespaced storage structs — group implementation state into a struct anchored at keccak256("myapp.main.storage") (the same pattern used for diamond facets). This removes the entire implementation from the low sequential slots that collide with proxy bookkeeping.

Use an audited proxy base — inherit from a reviewed proxy implementation (for example OpenZeppelin’s transparent or UUPS proxies) that already places admin and implementation pointers in EIP-1967 slots, rather than managing those pointers by hand.

Common Mistakes

Declaring a “storage gap” but still adding fields before it — gaps only help if new variables are appended after existing layout. Inserting a field ahead of stored data shifts every later slot and can create a collision that only fires for already-populated entries.

Assuming a branch-only write is safe — a slot written in just one rare path still corrupts whatever shares it the moment that path runs. Audit conditional writes, not only unconditional ones.

References