Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: high
Invalid

Gas Consumption Vulnerability in Sorting Function

Summary

The FundFlowController.sol contract contains a sorting function _sortIndexesDescending implemented with nested loops, leading to high gas consumption for large input arrays. This inefficiency can result in failed transactions, causing a Denial of Service (DoS) on critical contract functionalities such as deposits and withdrawals.

Vulnerability Details

Inefficient Sorting Algorithm

function _sortIndexesDescending(
uint256[] memory _values
) internal pure returns (uint256[] memory) {
uint256 n = _values.length;
uint256[] memory indexes = new uint256[]();
for (uint256 i = 0; i < n; ++i) {
indexes[i] = i;
}
for (uint256 i = 0; i < n - 1; ++i) {
for (uint256 j = 0; j < n - i - 1; ++j) {
if (_values[j] < _values[j + 1]) {
(_values[j], _values[j + 1]) = (_values[j + 1], _values[j]);
(indexes[j], indexes[j + 1]) = (indexes[j + 1], indexes[j]);
}
}
}
return indexes;
}

Explanation:
The _sortIndexesDescending function employs a Bubble Sort algorithm to sort an array of values in descending order. This method uses nested loops, resulting in a quadratic time complexity of O(n²). As the size of the input array _values increases, the gas consumption grows exponentially, making the function highly inefficient for large datasets.

Function Invocation with Large Arrays

function _getVaultDepositOrder(
IVaultControllerStrategy _vcs,
uint256 _toDeposit
) internal view returns (uint64[] memory, uint256) {
// ... [omitted for brevity]
uint256[] memory groupDepositOrder = _sortIndexesDescending(depositRoom);
// ... [omitted for brevity]
}

Explanation:
The _sortIndexesDescending function is called within the _getVaultDepositOrder method, which is a critical function responsible for determining the order of vault deposits based on available deposit capacity. When the number of vaults increases, the size of the depositRoom array passed to the sorting function also increases, leading to significant gas consumption due to the nested loop structure.

Example of High Gas Consumption

uint256 arraySize = 1000; // Example array size
uint256[] memory testArray = new uint256[]();
// Initialize the array with sample values
for (uint256 i = 0; i < arraySize; i++) {
testArray[i] = uint256(keccak256(abi.encodePacked(i)));
}
// Invoke the sorting function
vaultControllerStrategy.testSortIndexesDescending(testArray);

Explanation:
In this example, an array of size 1,000 is initialized and passed to the _sortIndexesDescending function. Due to the O(n²) complexity of the Bubble Sort algorithm, this operation consumes a substantial amount of gas. Increasing the arraySize further exacerbates the gas consumption, potentially leading to transaction failures when the gas limit is exceeded.

Impact

The inefficient sorting algorithm can severely impact the contract's functionality by making essential operations like deposits and withdrawals unfeasible for large input sizes. Attackers could exploit this vulnerability to intentionally cause transactions to fail, resulting in a Denial of Service (DoS) that affects all users relying on these critical functions. This not only disrupts user experience but also undermines the platform's reliability and trustworthiness.

Tools Used

  • Manual Code Review

  • Foundry

Recommendations

1. Optimize the Sorting Algorithm

Action:
Replace the Bubble Sort algorithm with a more gas-efficient sorting algorithm, such as Heap Sort or Insertion Sort, which offer better time complexities (e.g., O(n log n)).

Code Fix Example:

function _sortIndexesDescendingOptimized(
uint256[] memory _values
) internal pure returns (uint256[] memory) {
uint256 n = _values.length;
uint256[] memory indexes = new uint256[]();
for (uint256 i = 0; i < n; ++i) {
indexes[i] = i;
}
// Implement Heap Sort or another efficient sorting algorithm here
// Example placeholder for optimized sorting logic
return indexes;
}

2. Limit Array Sizes

Action:
Introduce a maximum limit on the size of arrays that can be processed by the sorting function to prevent excessive gas consumption.

Code Fix Example:

uint256 constant MAX_ARRAY_SIZE = 500; // Define an appropriate limit
function _sortIndexesDescending(
uint256[] memory _values
) internal pure returns (uint256[] memory) {
require(_values.length <= MAX_ARRAY_SIZE, "Array size exceeds limit");
// Existing sorting logic
}

3. Off-Chain Sorting

Action:
Perform sorting operations off-chain and submit the sorted indices to the contract. The contract should include mechanisms to verify the correctness of the sorted data to prevent manipulation.

Code Fix Example:

function setSortedIndexes(uint256[] calldata _sortedIndexes) external onlyAuthorized {
// Verify that _sortedIndexes is a correct descending sort of the original array
// Implement verification logic here
// Set the sorted indexes as needed
}

4. Batch Processing

Action:
Instead of sorting large arrays in a single transaction, process them in smaller batches across multiple transactions to stay within gas limits.

Code Fix Example:

function _sortIndexesDescendingInBatches(
uint256[] memory _values,
uint256 batchSize
) internal pure returns (uint256[] memory) {
require(_values.length <= batchSize, "Array size exceeds batch limit");
// Implement batch-wise sorting logic here
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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