# Callback trusts unverified caller

Callback acts on caller-supplied pool, payer, or data without binding msg.sender to a canonical registry

_Area: Smart contract access control and control flow. Seen in 30 disclosed reports; latest 2026-09-21. Updated 2026-10-23._

## How it works
A protocol exposes a callback (swap, flash, pay, bridge-receive) meant to be reached only through a trusted contract. The callback then acts on caller-supplied state — a pool address, a payer, encoded data — trusting that whoever called it is legitimate. The broken assumption is "only my pool/protocol can reach this callback." When the entry point accepts an arbitrary pool or target and validates it only by shallow checks (code exists, token getters return something, not on an exclusion list), an attacker deploys a minimal contract that passes those checks and re-enters the callback. The callback then mints, transfers, or approves on the attacker's behalf [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md)[E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md)[E8](https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md).

## Where it shows up
- Swap entry takes a pool address and verifies only code existence, token0()/token1(), or an exclusion list — no factory/registry derivation [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md)[E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md).
- Callback checks `msg.sender == currentPool` where `currentPool` was set from caller input [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md).
- Callback decodes a `payer` or recipient from pool-echoed data and acts on it [E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md)[E8](https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md).
- Callback is externally callable with no caller check at all [E8](https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md).
- Bridge receiver callback (onTokenBridged) invokes message handling without validating the L1 sender or token [E5](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AH-02.md).
- Pay callback funds a withdrawal from a fixed/attacker-chosen address rather than the caller's own balance [E6](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).
- Related: identity field read from a user-supplied message instead of the authenticated context [E4](https://bugbountylibrary.com/r/h1%3A2976481.md).

## How to review
Identify every callback and ask: what binds this call to a trusted origin? The invariant: the callback must derive the trusted caller from a canonical source (factory.getPool, a registry, a stored address set at initialization) and compare msg.sender to it — never to a value the caller supplied. Concretely, trace where `currentPool`/`payer` is assigned and confirm it traces to msg.sender or a registry lookup, not calldata. Confirm the factory getter returns a nonzero address before relying on it [E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md). For bridge receivers, confirm the primitive actually authenticates the L1 sender; if it cannot, split token and data validation [E5](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AH-02.md). For pay callbacks, confirm the funding source is the caller's own balance [E6](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).

## Variants seen
- Fake V3 pool mints via unguarded callback; supply guard defeated by burn/re-mint round-trip [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md).
- Fake pool re-enters router callback to transferFrom approvals, draining ~62 WBNB [E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md).
- Callback with no access control lets any caller set recipient and drain USDC [E8](https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md).
- Bridge callback accepts arbitrary token+data, redirecting staking incentives [E5](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AH-02.md).
- Pay callback pulls from a fixed victim address across repeated lock iterations [E6](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).
- Sender taken from message rather than authenticated context [E4](https://bugbountylibrary.com/r/h1%3A2976481.md).

## Why fixes fail
Exclusion lists and token-getter checks are bypassable by a minimal fake contract [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md)[E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md); a supply guard is defeated by a burn/re-mint round-trip [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md).

## Impact and severity
Critical: arbitrary mint (925M INT) [E1](https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md), router approval drain [E3](https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md), full USDC drain [E8](https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md), ~$1.4M WBTC theft [E6](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md), incentive redirection [E5](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AH-02.md). Severity rises when the callback can move existing approvals or mint, and when shallow validation is the only gate.

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260921-internettoken.md
- E2: https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-0xbadc0de-mev-bot-september-2022.md
- E3: https://bugbountylibrary.com/r/dhl%3A20260907-routerdrain.md
- E4: https://bugbountylibrary.com/r/h1%3A2976481.md
- E5: https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AH-02.md
- E6: https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md
- E7: https://bugbountylibrary.com/r/cantina%3Acircuit_puzzles%3A3.3.8.md
- E8: https://bugbountylibrary.com/r/dhl%3A20250820-0x8d2e.md
