# Possible index out of range in GetVoterIndex could cause ballot to never finalize due to panic

GetVoterIndex returns -1 for an observer removed via UpdateObserver, so AddVote/HasVoted index Votes[-1] and panic, stalling ballot finalization.

- Payout: Contest reward
- Severity: medium
- Program: ZetaChain
- Credited: dontonka
- Disclosed: 2024-09-06
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-11-zetachain-findings/issues/116
- Library page: https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AM-34
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:go technique:chained technique:source-review vuln:access-control vuln:dos

**Target:** Cosmos SDK observer module ballot voting (AddBlockHeader/UpdateObserver gRPC)
**Root cause:** Ballot.VoterList is snapshotted at creation; GetVoterIndex returns -1 when a voter address is no longer in the list, and callers index Votes[index] without checking, causing an out-of-range panic.
**Steps:**
1. Create ballot via `AddBlockHeader` from Observer A with observer list [A,B].
2. Admin calls `UpdateObserver` replacing B with C in ObserverMapper.
3. C (formerly B) calls `AddBlockHeader`; `AddVoteToBallot` -> `AddVote` -> `HasVoted` -> `GetVoterIndex(C)` returns -1.
4. `m.Votes[-1]` panics: `index out of range [-1]`.
**How found:** Code review of GetVoterIndex callers, noting callers assume success; traced AddVote path where ballot VoterList and voting address come from different sources, then wrote a unit test mixing AddBlockHeader with UpdateObserver to trigger the panic.
**Exploitation:** Requires admin (Group2 multisig) UpdateObserver while a ballot is in progress; subsequent vote from the renamed observer panics zetaclientd and the ballot cannot reach threshold, blocking CCTX consensus.
**Insight:** When a struct snapshots a membership list at creation but later lookups use a mutable external registry, the -1/not-found sentinel from the lookup will be used as an array index; grep for index-returning lookup helpers and check every caller for missing bounds checks.
