HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

[M-03] DoS via Gas Limit Exhaustion in batchRemoveLiquidity

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

// Create an array of RemoveLiquidityArgs exceeding block gas limit
RemoveLiquidityArgs[] memory largeArray = new RemoveLiquidityArgs[]();
for(uint i = 0; i < 1000; i++) {
largeArray[i] = RemoveLiquidityArgs({
poolId: bytes32(i),
positionTokenAmount: 1000,
recipient: address(0x1234)
});
}
// Attempt to remove liquidity in batch will fail
await aaveDIVAWrapper.batchRemoveLiquidity(largeArray);

Recommendations

Implement a maximum limit on the number of liquidity removals that can be processed in a single batch transaction:

// Add constant at contract level
uint256 private constant MAX_BATCH_SIZE = 20; // sample limit
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.

Updates

Lead Judging Commences

bube Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.