Status DataClose notification

Overlayer Disclosed Report

Permanent Funds-Stranding Risk in `OverlayerWrapCore` via `totalBridgedOut` Underflow on Destination Credit

Company
Created date
Mar 27 2026

Target

hidden

Vulnerability Details

Summary

OverlayerWrapCore tracks OFT bridge flow using totalBridgedOut.
The source-side _debit() increments it, while destination-side _credit() decrements it.

Because each chain has isolated storage, a destination chain can receive its first inbound transfer with totalBridgedOut == 0. In that case, _credit() executes:

  • totalBridgedOut -= amountReceivedLD

which underflows and reverts in Solidity ^0.8.x, reverting the destination credit execution.

In OFT flow, source-side debit burns tokens and destination-side credit mints tokens. When destination execution reverts deterministically, the message remains retryable but keeps failing until contract logic changes. This makes the issue a funds-stranding condition, not only a transient availability failure.

Affected Components

  • contracts/overlayer/OverlayerWrapCore.sol
    • _debit(...) increments totalBridgedOut
    • _credit(...) decrements totalBridgedOut

Vulnerability Details

Root Cause

The design assumes a single coherent totalBridgedOut counter, but it is actually per-chain state:

  • Chain A: _debit() updates Chain A storage
  • Chain B: _credit() updates Chain B storage

These are not the same slot/state.

Why Reachable

The system is explicitly intended to run on non-hub chains:

  • _initialize(...) has hub/non-hub branch behavior (hubChainId != block.chainid path exists)
  • mint/redeem manager flows are hub-restricted (onlyHubChain(block.chainid))

A fresh satellite deployment therefore starts with:

  • totalBridgedOut == 0

and has no local prerequisite requiring a prior _debit() before receiving inbound credits.

Impact

  • User bridges out from source chain (source-side _debit() burn path).
  • Destination-side _credit() deterministically reverts on totalBridgedOut == 0 state.
  • Retry attempts remain unexecutable on unchanged deployed logic.
  • Result: bridged funds are stranded because burn happened but destination mint cannot complete.

This is a direct user-fund impact path (effective loss/lock) on a core protocol flow, so Critical is justified.

Suggested Remediation

Apply one of:

  1. Saturating decrement
    • In _credit(), decrement by min(totalBridgedOut, amountReceivedLD), never underflow.
  2. Hub-scoped accounting
    • Only maintain this invariant where it is consumed (hub-side backing calculations), and avoid destination decrement on chains that do not track outbound debits.
  3. Bidirectional explicit accounting model
    • Track per-chain flow semantics separately instead of assuming a global bridged-out ledger in local storage.

Notes

  • This is a concrete burn-then-fail-credit path, not theoretical accounting drift.
  • No privileged role is required to reach the failing transition once inbound bridge traffic is processed.
  • Recovery caveat: triage may ask about operator rescue. In this codebase, OverlayerWrap/OverlayerWrapCore are constructor-based and there is no in-contract recovery hook for this accounting path, which strengthens permanence risk on deployed instances.

Validation steps

Proof of Concept

A reproducible Hardhat PoC is provided at:

  • OverlayerWrap_TotalBridgedOut_Underflow.poc.ts

It deploys a non-hub instance and directly exercises test-exposed _credit() before any local _debit(), demonstrating revert caused by arithmetic underflow.

Reproduction Steps

  1. Deploy OverlayerWrapMock with hubChainId != block.chainid.
  2. Confirm totalBridgedOut == 0.
  3. Call testCredit(receiver, amount, srcEid) with amount > 0.
  4. Observe revert (panic/underflow), proving inbound credit path failure from clean satellite state.

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
Critical
Bounty$69
Visibilitypartially
VulnerabilityBlockchain
Participants
hidden