Summary
The batchRemoveLiquidity function is vulnerable to a Denial of Service (DoS) attack through gas exhaustion due to unbounded array processing, potentially preventing users from removing liquidity if an attacker submits a transaction with an excessively large array.
Vulnerability Details
The batchRemoveLiquidity function processes an array of RemoveLiquidityArgs without imposing any limit on the array size:
function batchRemoveLiquidity(
RemoveLiquidityArgs[] calldata _removeLiquidityArgs
) external override nonReentrant returns (uint256[] memory) {
uint256 _length = _removeLiquidityArgs.length;
uint256[] memory _amountsReturned = new uint256[]();
for (uint256 i = 0; i < _length; i++) {
_amountsReturned[i] = _removeLiquidity(
_removeLiquidityArgs[i].poolId,
_removeLiquidityArgs[i].positionTokenAmount,
_removeLiquidityArgs[i].recipient
);
}
return _amountsReturned;
}
This design allows an attacker to submit a transaction with a large array of removal requests, causing the transaction to consume more gas than the block gas limit allows.
POC
RemoveLiquidityArgs[] memory largeArray = new RemoveLiquidityArgs[]();
for(uint i = 0; i < 1000; i++) {
largeArray[i] = RemoveLiquidityArgs({
poolId: bytes32(i),
positionTokenAmount: 1000,
recipient: address(0x1234)
});
}
await aaveDIVAWrapper.batchRemoveLiquidity(largeArray);
Recommendations
Implement a maximum limit on the number of liquidity removals that can be processed in a single batch transaction:
uint256 private constant MAX_BATCH_SIZE = 20;
function batchRemoveLiquidity(
RemoveLiquidityArgs[] calldata _removeLiquidityArgs
) external override nonReentrant returns (uint256[] memory) {
uint256 _length = _removeLiquidityArgs.length;
require(_length <= MAX_BATCH_SIZE, "Batch size exceeds limit");
uint256[] memory _amountsReturned = new uint256[]();
for (uint256 i = 0; i < _length; i++) {
_amountsReturned[i] = _removeLiquidity(
_removeLiquidityArgs[i].poolId,
_removeLiquidityArgs[i].positionTokenAmount,
_removeLiquidityArgs[i].recipient
);
}
return _amountsReturned;
}
This ensures the maximum batch size can be processed within block gas limits.