# An attacker can redeposit gas after forceRevert() to freeze all deposited gas budget of Root Bridge Agent

Reentrant forceRevert() during virtual-account call withdraws Root Bridge Agent's Anycall gas budget, then a later callOut skips _payExecutionGas, freezing the withdrawn gas and DoSing cross-chain messaging.

- Payout: Contest reward
- Severity: high
- Program: Maia DAO Ecosystem
- Credited: xuwinnie
- Disclosed: 2023-09-18
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-05-maia-findings/issues/337
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-maia%3AH-23
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:dos vuln:reentrancy

**Target:** Root Bridge Agent / Anycall gas budget (cross-chain messaging)
**Root cause:** _forceRevert() withdraws the entire Anycall execution budget but does not revert the local call; when invoked inside a virtual-account multicall, initialGas remains set so a subsequent callOut deletes it and skips _payExecutionGas, leaving the withdrawn budget frozen.
**Steps:**
1. Attacker calls `callOutSigned` on a branch chain to bypass `lock`, routing a virtual-account multicall to Root Bridge Agent.
2. Virtual account makes external call 1: `retryDeposit` on Arbitrum Branch Bridge Agent with an already-executed nonce -> triggers `_forceRevert()`, which calls `anycallConfig.withdraw(executionBudget)` draining the budget; `initialGas` stays non-zero.
3. Virtual account makes external call 2: `callOut` on Arbitrum Branch Bridge Agent -> succeeds, `_payExecutionGas` runs `delete(initialGas)` and `delete(userFeeInfo)`.
4. Virtual account makes external call 3: `anycallConfig.deposit(address)` with small msg.value so the outer tx succeeds.
5. Original anyExecute proceeds; `_payExecutionGas` is skipped because initialGas==0, so withdrawn budget is never replenished and stays frozen.
**How found:** Code reading of anyExecute/_forceRevert/_payExecutionGas: noticed _forceRevert withdraws budget without reverting the local call, and that initialGas is only cleared in _payExecutionGas, so a virtual-account multicall could interleave a successful callOut between forceRevert and the original execution.
**Exploitation:** Precondition: Root Bridge Agent has a pre-deposited Anycall execution budget. Attacker uses callOutSigned from a branch chain to reach the root virtual account, sequences retryDeposit (forceRevert drains budget) then callOut (clears initialGas) then a tiny deposit to keep the tx alive. Result: all withdrawn gas budget is permanently frozen and, once budget hits zero, cross-chain messages stop executing until manually topped up.
**Escalation:** Repeating the sequence continuously depletes the budget to zero, DoSing all cross-chain message sending for the Root Bridge Agent since no user is incentivized to top it up.
**Insight:** When a 'revert' helper only withdraws funds instead of reverting, check whether a multicall/virtual-account context lets execution continue past it; state flags like initialGas cleared only in the payment path are the lever to skip accounting.
