The _calculateQuantAMMVariance function in the QuantAMMVarianceBasedRule contract contains a critical vulnerability that leads to an out-of-bounds array access error when the following conditions are met: 1) the number of assets in the pool is odd, and 2) a vector of lambda values is used (i.e., _poolParameters.lambda.length > 1). This error occurs because the loop index locals.nMinusOne is not correctly decremented before the main loop when these conditions are satisfied. The vulnerability results in a denial-of-service (DoS) for a core functionality of the protocol: updating pool weights based on the Minimum Variance rule. The bug also makes the weight calculation fail with normal data when the above two conditions are satisfied, which will also impact the normal usage of this function.
The vulnerability resides in the _calculateQuantAMMVariance function of the QuantAMMVarianceBasedRule contract (QuantammVarianceBasedRule.sol#L174), specifically in how the loop termination condition and array indices are handled when the number of assets is odd and a vector lambda is used.
The function has two main code paths: one for when _poolParameters.lambda.length == 1 (scalar lambda) and another for when _poolParameters.lambda.length > 1 (vector lambda). Within each path, there's a conditional check for locals.notDivisibleByTwo, indicating an odd number of assets.
In the scalar lambda case:
locals.nMinusOne is decremented before the main loop, within the if (locals.notDivisibleByTwo) block (QuantammVarianceBasedRule.sol#L74).
locals.nMinusOne is incremented after the loop, within the if (locals.notDivisibleByTwo) block (QuantammVarianceBasedRule.sol#L116).
In the vector lambda case:
locals.nMinusOne is not decremented before the main loop (QuantammVarianceBasedRule.sol#L130).
locals.nMinusOne is incremented after the loop, within the if (locals.notDivisibleByTwo) block (QuantammVarianceBasedRule.sol#L174).
This discrepancy leads to the "Out-of-Bounds Access" issue when _poolParameters.lambda.length > 1 and the number of assets is odd: after the loop, when locals.notDivisibleByTwo is true, locals.nMinusOne is incremented, making it equal to the number of assets. Subsequent array accesses using this index (e.g., _poolParameters.lambda[locals.nMinusOne], _newData[locals.nMinusOne], _poolParameters.movingAverage[locals.n + locals.nMinusOne], locals.intermediateVarianceState[locals.nMinusOne]) will be out-of-bounds, as array indices are zero-based.
This issue causes the function to revert whenever the pool has an odd number of assets and uses a vector lambda, effectively breaking the minimum variance update rule under these conditions. The normal data that will cause this revert and the core functionality of updating weights will be disabled.
The vulnerability has a critical impact on the functionality of QuantAMM pools that utilize the minimum variance update rule with an odd number of assets and a vector lambda configuration. Specifically, it leads to the following:
Denial of Service: The _calculateQuantAMMVariance function, which is crucial for calculating new weights based on the minimum variance rule, will consistently revert due to the out-of-bounds array access. This effectively prevents the pool from updating its weights, rendering the minimum variance strategy unusable under these conditions.
Broken Core Functionality: Weight updates are a core component of the QuantAMM protocol. The inability to update weights based on the minimum variance rule disrupts the intended behavior of the pool and prevents it from adapting to market conditions as designed.
Loss of LP Value: As a strategy pool, the QuantAMM pool is designed to rebalance the pool weights automatically to generate higher LP performance than standard CFMM pools, if the core strategy is broken, the LP value can no longer be guaranteed.
The provided Proof of Concept (PoC) test code, testVarianceOddAssetsAndVectorLambdaExpectRevert in QuantAMMVarianceTest, successfully demonstrates this vulnerability:
NOTE
please manually modify the test code before run the test, as to:
initialize int256[] memory priceData with length numAssets
initialize int256[] memory movingAverage with length numAssets * 2
initialize int256[] memory initialVariance with length numAssets
initialize int128[] memory lambda with length numAssets
since platform system will automatically overwrite these array length to empty when save the upload.
The test sets up the conditions for the vulnerability: an odd number of assets (numAssets = 5) and a vector lambda with length equal to the number of assets.
It then calls the vulnerable function externalCalculateQuantAMMVariance (which internally calls _calculateQuantAMMVariance) and asserts that the call will revert using vm.expectRevert().
The PoC test is executed with the specified command:
The PoC test result confirms the expected revert with a panic: array out-of-bounds access (0x32) error, validating the presence of the vulnerability:
The inability to update weights based on the minimum variance rule under these conditions constitutes a high-severity issue as it breaks core protocol functionality and potentially impacts the intended behavior and performance of QuantAMM pools.
Manual Review
The core issue stems from the inconsistent handling of the locals.nMinusOne variable when dealing with an odd number of assets and a vector lambda. To resolve this, the following code change should be implemented:
Correctly Decrement locals.nMinusOne for Vector Lambda:
Inside the _calculateQuantAMMVariance function, within the else block (where _poolParameters.lambda.length > 1), add the following code snippet before the main loop, similar to how it is done in the scalar lambda (_poolParameters.lambda.length == 1) branch:
This change ensures that locals.nMinusOne is correctly decremented when the number of assets is odd, aligning the loop termination condition and index calculations with the intended logic. By doing so, the loop will iterate the correct number of times, and the subsequent accesses to array elements using locals.nMinusOne will remain within bounds.
This single modification effectively addresses the root cause of the out-of-bounds access and ensures the proper functioning of the minimum variance update rule when using a vector lambda with an odd number of assets.
Likelihood: Medium/High, odd asset number + lambda is a vector. Impact: Medium/High, DoS the update.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.