Nihavent - Curated Vault allocators cannot reallocate()
a pool to zero due to attempting to withdraw 0 tokens from the underlying pool
#434
Labels
Has Duplicates
A valid issue with 1+ other issues describing the same vulnerability
Medium
A Medium severity issue.
Reward
A payout will be made for this issue
Nihavent
Medium
Curated Vault allocators cannot
reallocate()
a pool to zero due to attempting to withdraw 0 tokens from the underlying poolSummary
The
reallocate()
function is the primary way a curated vault can remove liquidity from an underlying pool, so being unable to fully remove the liquidity is problematic. However, due to a logic issue in the implementation, any attempt toreallocate()
liquidity in a pool to zero will revert.Root Cause
The function
CuratedVault::reallocate()
is callable by an allocator and reallocates funds between underlying pools. As shown below,toWithdraw
is the difference betweensupplyAssets
(total assets in the underlying pool controlled by the curated vault) and theallocation.assets
which is the target allocation for this pool. Therefore, iftoWithdraw
is greater than zero, a withdrawal is required from that pool:The issue arrises when for any given pool,
allocation.assets
is set to 0, meaning the allocator wishes to empty that pool and allocate the liquidity to another pool. Under this scenario,toWithdraw
is set to 0, and passed intoPool::withdrawSimple()
. This is a logic mistake to attempt to withdraw 0 instead of thesupplyAssets
when attempting to withdraw all liquidity to a pool. The call will revert due to a check in the pool's withdraw flow that ensures the amount being withdrawn is greater than 0.It seems this bug is a result of forking the Metamorpho codebase which implements the same logic. However, Metamorpho's underlying withdraw() function can take either an asset amount or share amount, but ZeroLend's
Pool::withdrawSimple()
only accepts an amount of assets.Internal pre-conditions
supplyQueue
External pre-conditions
No response
Attack Path
supplyQueue
MarketAllocation
withdrawing all liquidity from the first pool and depositing the same amount to the second poolImpact
reallocate()
function. The natspec comments indicate that emptying a pool to zero through thereallocate()
function is the first step of the intended way to remove a pool.PoC
Paste and run the below test in /test/forge/core/vaults/. It shows a simple 2-pool vault with a single depositors. An allocator attempts to reallocate the liquidity in the first pool to the second pool, but reverts due to the described issue.
Mitigation
The comment "Guarantees that unknown frontrunning donations can be withdrawn, in order to disable a market" does not seem to apply to Zerolend pools as a donation to the pool would not disable the market, nor would it affect the amount of assets the curated vault can withdraw from the underlying pool. With this in mind, Metamorpho's safety check can be removed:
The text was updated successfully, but these errors were encountered: