# Duplicate identifier accepted without uniqueness check

Is uniqueness of ids, indices, or keys enforced, or only presence and position?

_Area: Smart contract access control and control flow. Seen in 2 disclosed reports; latest 2025-03-24. Updated 2026-10-23._

## How it works
A verifier, registry, or guard assumes each identifier (an AIR id, a bus index, a signature slot) appears at most once. It checks that required entries are *present* and in the right *position*, but never that they are *unique*. When the input list is attacker-controlled, a duplicate is accepted as a second, independent instance. That duplicate can then be read in place of the legitimate one, so a check that reads "the" instance reads the attacker's copy. The broken assumption: presence plus ordering implies a single well-defined instance.

## Where it shows up
- Prover- or caller-supplied lists of ids/indices validated only by position ("0th is Program, 1st is Connector") [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.3.md)
- Index allocation tables where two constants can be assigned the same value [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md)
- Guards or modules that re-implement a base contract's validation loop over a different subset of inputs [E1](https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3.md)
- Fingerprints or keys built by positional weighting, where shorter tuples zero-pad into collisions [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md)

## How to review
Identify every identifier that selects an instance, then find the code that consumes it. The invariant to confirm: **the set of ids/keys is checked for uniqueness (or the consumer is proven duplicate-insensitive) before any instance is trusted.** Concretely: does a dedup or uniqueness `assert` exist, or does lookup silently return the first/last match? For re-implemented validation, confirm the guard checks the *same* subset as the underlying system [E1](https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3.md). For index tables, diff assigned values for collisions [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md). For positional lists, confirm a uniqueness pass over all ids, not just required slots [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.3.md).

## Variants seen
- Duplicate AIR/chip instances appended to a prover-controlled list, faking state [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.3.md)
- Two buses sharing one index, colliding via zero-padded fingerprints [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md)
- Guard counts valid signatures over all entries while the base checks only the first threshold [E1](https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3.md)

## Impact and severity
High: forged zkVM state and broken soundness [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.3.md)[E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md), and bypassed signer thresholds on a Safe guard [E1](https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3.md). Severity rises when the duplicate is silent (no error) and when downstream logic trusts the shadowed instance.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3.md
- E2: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.3.md
- E3: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.2.1.md
