Reproduced Exploit
Forte Float128 — [H-02] Sqrt silently stops the entire call frame on packed 0
1. Mathematically, sqrt(0) = 0. 2. Float128 uses assembly stop() on packed zero — equivalent to return(0,0) for the entire call frame. 3. Because sqrt is internal pure, it is inlined: any protocol function that calls sqrt(0) then settles/transfers silently skips the rest while still reporting succe…
Chain
Other
Category
untagged
Date
Apr 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 55704-h-02-sqrt-function-silently-reverts-the-entire-control-flow. Standalone Foundry PoC and full write-up: 55704-h-02-sqrt-function-silently-reverts-the-entire-control-flow_exp in the
evm-hack-registrymirror.
Vulnerability classes: integer-bounds · logic/wrong-condition · impact/dos/selective · misassumption/math-is-safe
Reproduction: self-contained Foundry PoC with only
forge-std— no fork, no RPC. Full trace: output.txt.
Key info#
| Impact | HIGH — sqrt(0) terminates the whole call frame with empty success; post-sqrt logic never runs |
| Protocol | Forte Float128 solidity library |
| Vulnerable code | Float128.sqrt zero guard — Yul stop() |
| Bug class | Wrong control-flow primitive (stop vs return 0) |
| Finding | Code4rena 2025-04-forte-float128-solidity-library · #55704 · 0xpetern |
| Report | code4rena.com/reports/2025-04-forte-float128-solidity-library |
| Source | AuditVault |
| Compiler | ^0.8.24 |
TL;DR#
- Mathematically,
sqrt(0) = 0. - Float128 uses assembly
stop()on packed zero — equivalent toreturn(0,0)for the entire call frame. - Because
sqrtisinternal pure, it is inlined: any protocol function that callssqrt(0)then settles/transfers silently skips the rest while still reporting success to its caller.
The vulnerable code#
if iszero(a) {
stop() // @> VULN: ends the whole call frame
}
Fix:
if iszero(a) {
- stop()
+ r := 0
+ leave
}
Root cause#
stop() is not a function-local return. It aborts the current message call with empty
returndata. Inlined into a protocol external function, that kills settlement code that
follows sqrt.
Preconditions#
- A call path invokes
sqrtwith packedFloat0(e.g. empty balance, zero rate).
Attack walkthrough#
- Non-zero control path: guard falls through, settlement flag set.
- Zero path:
stop()succeeds the subcall with empty returndata. - Settlement body never runs (
settledstays false) — silent partial execution.
Diagrams#
Impact#
Financial protocols that call sqrt mid-function can leave state half-updated or skip
payouts without a revert — hard-to-debug silent failures.
Taxonomy (AuditVault)#
[[integer-bounds]]·[[logic/wrong-condition]]·[[impact/dos/selective]]·[[misassumption/math-is-safe]]
Sources#
- AuditVault finding #55704
- Code4rena report
- Reduced from code-423n4/2025-04-forte
@4d6694f/src/Float128.sol~L712
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 55704-h-02-sqrt-function-silently-reverts-the-entire-control-flow_exp (evm-hack-registry mirror).
- AuditVault finding: 55704-h-02-sqrt-function-silently-reverts-the-entire-control-flow.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.