# An attacker can overflow the count of messages in a bottom up message batch leading to bottom up checkpoint execution failure and halting

Committing 21 bottom-up messages in one block overflows a batch past maxMsgsPerBottomUpBatch, making the checkpoint permanently unexecutable and halting bottom-up checkpoints.

- Payout: Contest reward
- Severity: high
- Program: Recall
- Credited: sin1st3r__
- Disclosed: 2026-03-13
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2025-02-recall#h-04
- Library page: https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-04
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** LibGateway commitBottomUpMsg / SubnetActorCheckpointingFacet submitCheckpoint
**Root cause:** When a full batch is cut, storeBottomUpMsgBatch pushes into the current-block batch without checking its existing length, so a batch at block.number can exceed maxMsgsPerBottomUpBatch. ensureValidCheckpoint then always reverts on that oversized batch.
**Steps:**
1. Deploy a malicious IPC contract on the child subnet that calls sendContractXnetMessage() 21 times in one block.
2. First 10 messages fill the next-epoch batch, triggering the cut branch and copying 10 msgs into a batch keyed at block.number.
3. Next 10 messages refill the next-epoch batch to full again.
4. 21st message triggers the cut branch again, pushing 10 more msgs into the existing block.number batch, yielding 11 msgs.
5. submitCheckpoint on that batch reverts with MaxMsgsPerBatchExceeded, so the checkpoint can never be committed.
**How found:** Code review of commitBottomUpMsg's cut branch and storeBottomUpMsgBatch, noticing the store pushes into s.bottomUpMsgBatches[batch.blockHeight] without checking existing length, then tracing the 21-message sequence to exceed the limit.
**Exploitation:** Attacker spams 21 bottom-up messages in a single block via a malicious child-subnet contract, overpopulating the block.number batch; the resulting checkpoint exceeds the limit and is permanently rejected by ensureValidCheckpoint, halting bottom-up checkpoint execution.
**Insight:** When a 'cut' copies a full batch into a new key (block.number) and later cuts in the same block reuse that key, the second store appends to an already-full array; look for batch/queue rotation logic keyed on block.number that lacks a length guard on re-entry.
