# Migration and upgrade storage layout mismatch

Migrate/upgrade paths that read or write through stale layouts, keys, or unset metadata fields

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2025-07-17. Updated 2026-11-02._

## How it works
An upgrade or migration assumes the new code reads and writes the new layout, but the accessors, registry keys, or metadata fields it depends on were never changed. The migration then runs against the old shape: it reads stale data and writes it back unchanged, or it registers/unregisters under a key that collides with itself. The result is a silent no-op or a wiped entry, not a crash, so the assumption "migration succeeded" is false while everything appears to run.

## Where it shows up
- A `migrate()`/upgrade function that calls `read()`/`store()` helpers unchanged from the prior version [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Storage libraries with hardcoded bit-shift offsets or slot numbers that must match a documented new struct [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Registry code that keys state by contract name and calls register before unregister [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md).
- Base executors that branch on metadata returned by pluggable hooks (e.g. `vaultBank()`, `dstChainId()`) [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md).
- Any hook or adapter that returns shares/value but leaves optional metadata fields at default `address(0)` [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md).

## How to review
- Diff the documented new layout against the actual encode/decode offsets in `read()`/`store()`; confirm the field moved to the new slot, not just that the function exists [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- For a registry keyed by name, check ordering: registering a new address under an existing name before unregistering the old one must not zero the shared key [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md).
- Enumerate every implementation of a hook interface and confirm each populates the metadata the base contract branches on; a missing setter silently skips a required approval [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md).
- Invariant: after migration, every field reads from its new location; after upgrade, every registry key maps to exactly one live address; every hook returns the metadata its consumer requires.

## How finders spotted it
- Compared the v2.3 migration runbook's documented 1-slot layout to the actual bit-shift offsets and noticed `maker` still sourced from slot1 [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Read the base executor's approval logic, then enumerated all hooks returning shares and diffed which set `vaultBank`/`dstChainId`; one was the outlier [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md).
- Read `upgradeExistingContract` ordering and traced how Storage keys derive from contract name; confirmed with tests [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md).

## Variants seen
- Migration no-op: accessors never updated, so `migrate()` rewrites the old layout [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Missing metadata in one hook implementation, skipping a downstream approval branch [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md).
- Registry self-collision: register-then-unregister under the same name zeroes the entry [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md).

## Why fixes fail
Renaming to dodge the registry collision forces cascading upgrades because names are hardcoded in modifiers, so the workaround breaks other contracts [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md).

## Impact and severity
Migrations silently fail to move state, leaving positions in the old layout [E1](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md); cross-chain claims skip required approvals and fail [E2](https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md); upgrades zero registry entries and become impractical, blocking protocol evolution [E3](https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md). Severity rises with how many contracts depend on the stale key or metadata and whether the failure is silent.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md
- E2: https://bugbountylibrary.com/r/cantina%3Asuperform_may2025%3A3.2.18.md
- E3: https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AM-02.md
