# Sentinel default value mismatch in lookups

Lookup returns zero or max-uint default while caller checks a different not-found sentinel

_Area: DeFi oracles, liquidations and flash loans. Seen in 3 disclosed reports; latest 2023-05-05. Updated 2026-10-24._

## How it works
A lookup is supposed to signal "not found" with a sentinel value, and the caller checks for that sentinel. The assumption that breaks: the sentinel the caller tests for is not the value the lookup actually returns on a miss. Solidity mappings default to zero, so a miss yields `0` (or `uint(-1)` is tested but never produced). If `0` is also a valid index or round, the existence check silently passes and the caller uses the first entry, or a wrong round, as if it were real. In binary searches the same class appears as a termination condition that returns `0` without validating the lower-bound candidate.

## Where it shows up
- Getters that compare an index against `-1` or `uint(-1)` while the internal lookup returns a mapping default of `0` [E2](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-02.md).
- Lookups where `0` is a legitimate index (first token, first config, first round) [E2](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-02.md).
- Binary search loops that stop on `minRoundId+1 !< maxRoundId` and only validate `maxRoundId` [E1](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md).
- Oracle/config code paths where a miss feeds a price or round selection rather than reverting.

## How to review
For every lookup, find the exact value returned on a miss and the exact value the caller tests. The invariant: the not-found sentinel must be unreachable by any valid entry, and the caller must test the same value the lookup emits. If `0` is valid, reserve it, shift indices, or track initialization separately. For binary searches, confirm the lower-bound candidate is validated before returning and that a single valid element in range is handled [E1](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md). Trace the miss path to its consumer: does it revert, or silently use entry zero?

## Variants seen
- Caller checks `-1`, lookup returns `0`; unknown token resolves to first token's config, corrupting oracle prices [E2](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md).
- Caller checks `uint(-1)`, unreachable given zero-default; `0` collides with valid first index [E3](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-02.md).
- Binary search returns `0` when one valid round exists in a new phase, wrong roundId or temporary oracle DOS [E1](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md).

## Impact and severity
Wrong config or round selection corrupts oracle prices and can temporarily DOS the oracle [E1](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md)[E2](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md). Severity is high where the miss path silently substitutes a real entry instead of reverting; worse when the substituted value drives pricing or liquidation decisions.

## Why fixes fail
A fix that only changes the caller's sentinel without ensuring the lookup can never return a valid index on a miss leaves the collision intact [E2](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-02.md).

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md
- E2: https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-01.md
- E3: https://bugbountylibrary.com/r/c4%3A2021-04-basedloans%3AH-02.md
