# Attacker can block LayerZero channel due to variable gas cost of saving payload

Attacker sends max-size LayerZero payload whose destination execution drains forwarded gas, so NonblockingLzApp cannot store the failed message, permanently blocking the cross-chain channel.

- Payout: Contest reward
- Severity: high
- Program: Tapioca DAO
- Credited: windhustler
- Disclosed: 2023-11-16
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-07-tapioca-findings/issues/1220
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-tapioca%3AH-16
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:dos

**Target:** LayerZero cross-chain messaging (BaseTOFT/BaseUSDO/BaseTapOFT)
**Root cause:** NonblockingLzApp._blockingLzReceive stores the full payload in failedMessages after the external call, but EIP-150 leaves only 1/64 of gas for that store; a malicious destination contract can consume the 63/64 forwarded gas so the store runs out of gas and LayerZero marks the message StoredPayload.
**Steps:**
1. Deploy GasDrainingContract on destination chain with permitBorrow() that writes 100000 storage slots to burn gas.
2. Call sendToYBAndBorrow with approvals array padded to ~10k bytes; first approval target = GasDrainingContract, permitBorrow=true.
3. Set destination gasLimit (e.g. 1M) via airdropAdapterParams.
4. On destination, _nonblockingLzReceive forwards 63/64 gas to module; borrow() calls malicious permitBorrow and drains it.
5. Remaining 1/64 (~15k) is insufficient for failedMessages store + MessageFailed event (needs ~131k for 9999-byte payload), so _blockingLzReceive reverts and LayerZero stores payload, blocking the channel.
**How found:** Noticed NonblockingLzApp stores the full payload after an external call, reasoned about EIP-150 63/64 gas forwarding, and measured that storing a 9999-byte payload costs ~131k gas—more than the 1/64 reserve—so a gas-draining callee starves the store.
**Exploitation:** Attacker repeatedly sends cheap max-size payloads with a gas-draining destination contract, causing every message to fail at the failedMessages storage step; LayerZero then halts the pathway between the two chains, making the system unusable.
**Escalation:** Same pattern applies to all packet types in BaseTOFT, BaseUSDO, and BaseTapOFT (via malicious rewardToken implementing ISendFrom), so any cross-chain route can be blocked.
**Insight:** When a contract does external calls before persisting failure state, check whether the 1/64 EIP-150 reserve can cover the storage cost of the largest allowed payload; a callee that burns forwarded gas can make the fallback path itself revert, turning a non-blocking design into a channel DoS.
