Everyone treats reentrancy like a solved problem. Checks-Effects-Interactions, a ReentrancyGuard import, done. Ship it.
That confidence is exactly why it keeps working. Reentrancy attacks persist despite being well understood since the 2016 DAO hack that drained $70 million, because developers still underestimate the risk in yield farming and lending protocols where complex token interactions open unexpected callback windows. The category didn't die. It moved.
Here are five patterns auditors kept flagging through 2025 and into 2026 that don't look anything like the textbook version.
1. Cross-function reentrancy through shared state
The classic guard protects one function. It says nothing about a second function that reads the same storage variable mid-call. An attacker calls withdraw(), gets reentered through a callback, and instead of calling withdraw() again, calls transfer() or claimReward() a different entry point touching the same unfinished state. Single-function guards miss this every time.
2. Read-only reentrancy
No funds move during the reentrant call, so nothing looks wrong on-chain. What actually happens: a view function gets queried mid-transaction, before state finishes updating, and an external protocol (a lending market, a price oracle wrapper) reads that stale value and prices something incorrectly. The exploited contract stays untouched. The damage lands somewhere else entirely, which makes this pattern brutal to trace back to source.
3. Cross-contract reentrancy via token hooks
ERC-777 and ERC-721 callback hooks (tokensReceived, onERC721Received) hand execution control to the receiving contract before the sender's accounting settles. Protocols that never touched a "vulnerable" function directly still get drained because a token transfer they trusted came with a hook attached. This mirrors what happened to dForce in 2023, where an attacker exploited execution logic to repeatedly withdraw funds before state updates completed, for a loss of roughly $3.6 million.
4. Reentrancy through governance and flash-loan-boosted voting
Borrow, vote, reenter the voting contract before the snapshot locks, repeat. The reentrant call doesn't touch a wallet balance, it touches voting weight. Governance modules built by teams focused entirely on treasury security tend to skip reentrancy checks here because nobody thinks of "vote" as a fund-moving function. It is, once flash loans are in the mix.
5. Upgrade-path reentrancy in proxy contracts
A reentrant call lands during the narrow window between a proxy delegatecall and an implementation upgrade. State gets written against the old logic while the new logic is mid-deployment. This one barely shows up in audit reports because it requires timing a call against an upgrade transaction, but teams running frequent proxy upgrades without a timelock are exposed to it.
Cross-function and read-only reentrancy in particular don't show up in a standard Slither pass catching them means someone manually mapping every place shared state gets read, not just where it gets written. That's architecture-review work, done before the contracts are frozen, not a post-launch patch job. Here's how we structure state and access checks during contract architecture review.
None of this replaces a real audit. Automated scanners like Slither and Mythril catch the textbook pattern; they largely miss the four variations above because those require understanding what the protocol is supposed to do, not just what the code says. Worth reading the current OWASP Smart Contract Top 10 breakdown on reentrancy if you want the formal classification, and this 2026 audit landscape report has good numbers on where the money's actually going missing.
The category total looks small next to access control failures reentrancy accounted for $35.7 million in documented losses in 2025, versus $953.2 million from access control issues but that number reflects how few teams still get hit by the obvious version. The five patterns above are the ones slipping through.
Guard the function. Then guard the state.
Top comments (0)