# Queue and array index corruption

Stale or miscomputed array/queue indices after removal, merge, or wrap that misroute funds or freeze positions

_Area: DeFi accounting and math bugs. Seen in 38 disclosed reports; latest 2026-03-13. Updated 2026-10-23._

## How it works
Code keeps a parallel index (a mapping from object to array slot, a cursor, or a per-bucket last-ID) that must stay in sync with the array or queue it describes. The assumption that breaks is that this index is always rewritten correctly on every mutation. When an element is removed, moved, merged, or a ring buffer wraps, the index is left stale, points at the wrong element, or is compared in the wrong units. Later logic trusts the index to find the right bucket or entry, so funds or positions are attributed to the wrong place or become unreachable.

## Where it shows up
- Swap-and-pop removal where the index write targets the slot's new value instead of the moved element [E8](https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AH-8.md)
- Heap root-replacement that relies on swap() to fix indices, skipping the write when no swap happens [E4](https://bugbountylibrary.com/r/c4%3A2023-12-revolutionprotocol%3AM-07.md)
- Removal that decrements count but leaves the vacated storage slot populated [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-rio-network-core-protocol%3AH-8.md)
- Bucket lookup by comparing a monotonic ID against per-bucket last-IDs, where merges mint new IDs [E2](https://bugbountylibrary.com/r/c4%3A2024-04-gondi%3AH-01.md)
- Ring buffers indexed by the same ID but written at different frequencies, so a window boundary lands on an overwritten slot [E3](https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AH-1.md)
- Loops mixing an absolute cursor with a relative count [E6](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-07.md)
- Length/emptiness checks placed after the removal call [E7](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-13.md)
- Sentinel slot (e.g. ids[0]) left empty while live entries exist elsewhere [E1](https://bugbountylibrary.com/r/c4%3A2022-11-debtdao%3AH-05.md)

## How to review
The invariant: for every element in the array/queue, its cached index equals its actual slot, and every removal rewrites the index for both the removed and the moved element. Tick each mutation path:
- Swap-and-pop: moved element's index updated, removed one cleared; test last-element removal [E8](https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AH-8.md).
- Heap extract: moved leaf's index written unconditionally before heapify, even on tie/early-return [E4](https://bugbountylibrary.com/r/c4%3A2023-12-revolutionprotocol%3AM-07.md).
- Storage removal: vacated slot cleared, not just count decremented [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-rio-network-core-protocol%3AH-8.md).
- Merge/split/ID minting: bucket re-derived, not just compared to last-IDs [E2](https://bugbountylibrary.com/r/c4%3A2024-04-gondi%3AH-01.md).
- Ring buffers: window boundary validated against the buffer actually written at that frequency [E3](https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AH-1.md).
- Loop bounds: cursor and count in the same units [E6](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-07.md).
- Length checks: run before removal, not after [E7](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-13.md).
- Sentinel slots: no path leaves slot 0 empty while debt exists [E1](https://bugbountylibrary.com/r/c4%3A2022-11-debtdao%3AH-05.md).

## Variants seen
- Merge mints new loanId, breaking queue lookup [E2](https://bugbountylibrary.com/r/c4%3A2024-04-gondi%3AH-01.md)
- Ring buffer window lands on overwritten slot [E3](https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AH-1.md)
- Heap index not updated when moved leaf ties its children [E4](https://bugbountylibrary.com/r/c4%3A2023-12-revolutionprotocol%3AM-07.md)
- Stale operator ID left in vacated slot, later read as live [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-rio-network-core-protocol%3AH-8.md)
- Absolute cursor vs relative count loop bound [E6](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-07.md)
- Post-removal length check off by one [E7](https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-13.md)
- Last-element removal leaves stale index [E8](https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AH-8.md)
- Sentinel slot 0 empty while debt exists, blocking liquidation [E1](https://bugbountylibrary.com/r/c4%3A2022-11-debtdao%3AH-05.md)

## Impact and severity
Funds frozen or misallocated across queues, locked LP positions, debt tracked wrongly enabling collateral withdrawal or blocking liquidation, and repayments credited to the wrong pool. Severity is high because corruption is silent and persists; worse when the bad index feeds access checks or fund routing, or when it triggers a revert that disables liquidation entirely [E1](https://bugbountylibrary.com/r/c4%3A2022-11-debtdao%3AH-05.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2022-11-debtdao%3AH-05.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-04-gondi%3AH-01.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2024-05-pooltogether%3AH-1.md
- E4: https://bugbountylibrary.com/r/c4%3A2023-12-revolutionprotocol%3AM-07.md
- E5: https://bugbountylibrary.com/r/sherlock%3A2024-02-rio-network-core-protocol%3AH-8.md
- E6: https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-07.md
- E7: https://bugbountylibrary.com/r/sherlock%3A2025-05-lend-audit-contest%3AH-13.md
- E8: https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AH-8.md
