# Unbounded loop in _removeNft could lead to a griefing/DOS attack

Visor vault's _removeNft loops over an unbounded nfts array; a malicious NFT contract can spam deposits and revert transfers, griefing all users with gas exhaustion.

- Payout: Audit contest
- Severity: high
- Program: Visor
- Found by: shw
- Disclosed: 2021-08-30
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-05-visorfinance-findings/issues/80
- Library page: https://bugbountylibrary.com/r/c4%3A2021-05-visorfinance%3AH-04
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** Visor vault ERC721 handling (_removeNft / onERC721Received)
**Root cause:** _removeNft iterates the entire nfts array to find one entry, and onERC721Received is permissionless so any NFT contract can push entries into that array without bound.
**Steps:**
1. Deploy a malicious NFT contract whose safeTransferFrom always reverts.
2. Call the vault's onERC721Received repeatedly (or have the contract send many NFTs) to grow the nfts array to a large size.
3. Benign users calling transferERC721/timeUnlockERC721 trigger _removeNft's unbounded loop, consuming excessive gas.
4. Because the malicious NFT cannot be transferred out (reverts), the array entry can never be removed, so gas cost persists and can exceed the block gas limit, reverting every unlock/transfer.
**How found:** Code review of _removeNft's linear array scan combined with the observation that onERC721Received is permissionless and accepts arbitrary NFTs, plus the fact that a reverting NFT contract blocks removal.
**Exploitation:** Attacker floods the vault's nfts array via the permissionless onERC721Received with NFTs from a contract that reverts on transfer, making _removeNft's linear scan unbounded and unremovable, so legitimate unlock/transfer transactions run out of gas or exceed the block gas limit.
**Insight:** When a contract stores user-supplied tokens in a single array and later linearly scans it, check whether the deposit hook is permissionless and whether entries can be made unremovable (e.g. reverting transfer) to turn the scan into a permanent gas griefing vector.

