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…

Apr 2025Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Apr 2025

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

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.

Loading fork state…

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-registry mirror.


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#

ImpactHIGHsqrt(0) terminates the whole call frame with empty success; post-sqrt logic never runs
ProtocolForte Float128 solidity library
Vulnerable codeFloat128.sqrt zero guard — Yul stop()
Bug classWrong control-flow primitive (stop vs return 0)
FindingCode4rena 2025-04-forte-float128-solidity-library · #55704 · 0xpetern
Reportcode4rena.com/reports/2025-04-forte-float128-solidity-library
SourceAuditVault
Compiler^0.8.24

TL;DR#

  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 success to its caller.

The vulnerable code#

SOLIDITY
if iszero(a) {
    stop() // @> VULN: ends the whole call frame
}

Fix:

DIFF
  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 sqrt with packedFloat 0 (e.g. empty balance, zero rate).

Attack walkthrough#

  1. Non-zero control path: guard falls through, settlement flag set.
  2. Zero path: stop() succeeds the subcall with empty returndata.
  3. Settlement body never runs (settled stays false) — silent partial execution.

Diagrams#

sequenceDiagram participant P as Protocol.computeAndSettle participant S as Float128.sqrt inlined P->>S: a = 0 Note over S: stop equals return 0,0 S-->>P: empty success Note over P: settlement body never runs

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#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

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.