Summary
The _splitWeightAndMultipliers function assumes that the input array weights has an even length and can be evenly divided into two halves. When weights.length is odd (e.g., weights.length = 9), the function exhibits logical inconsistencies, leaving splitWeights[1] uninitialized (filled with zeros) and potentially causing issues downstream. This is not explicitly validated or handled in the code.
Vulnerability Details
The main issue with weights.length = 9 is that the function does not handle cases where weights.length is not evenly divisible by 2. It assumes weights can always be split into two equal halves, which is not true for odd-length arrays.
function _setInitialWeights(int256[] memory _weights) internal {
require(_normalizedFirstFourWeights == 0, "init");
require(_normalizedSecondFourWeights == 0, "init");
InputHelpers.ensureInputLengthMatch(_totalTokens, _weights.length);
int256 normalizedSum;
int256[] memory _weightsAndBlockMultiplier = new int256[]();
for (uint i; i < _weights.length; ) {
if (_weights[i] < int256(uint256(absoluteWeightGuardRail))) {
revert MinWeight();
}
_weightsAndBlockMultiplier[i] = _weights[i];
normalizedSum += _weights[i];
_weightsAndBlockMultiplier[i + _weights.length] = int256(0);
unchecked {
++i;
}
}
if (uint256(normalizedSum) != FixedPoint.ONE) {
revert NormalizedWeightInvariant();
}
if (_weightsAndBlockMultiplier.length > 8) {
int256[][] memory splitWeights = _splitWeightAndMultipliers(_weightsAndBlockMultiplier);
_normalizedFirstFourWeights = quantAMMPack32Array(splitWeights[0])[0];
_normalizedSecondFourWeights = quantAMMPack32Array(splitWeights[1])[0];
} else {
_normalizedFirstFourWeights = quantAMMPack32Array(_weightsAndBlockMultiplier)[0];
}
poolSettings.quantAMMBaseInterpolationDetails = QuantAMMBaseInterpolationVariables({
lastPossibleInterpolationTime: uint40(block.timestamp),
lastUpdateIntervalTime: uint40(block.timestamp)
});
emit WeightsUpdated(address(this), _weights);
}
function initialize(
int256[] memory _initialWeights,
PoolSettings memory _poolSettings,
int256[] memory _initialMovingAverages,
int256[] memory _initialIntermediateValues,
uint _oracleStalenessThreshold
) public initializer {
require(_poolSettings.assets.length > 0 && _poolSettings.assets.length == _initialWeights.length, "INVASSWEIG");
assets = _poolSettings.assets;
poolSettings.assets = new address[](_poolSettings.assets.length);
for (uint i; i < _poolSettings.assets.length; ) {
poolSettings.assets[i] = address(_poolSettings.assets[i]);
unchecked {
++i;
}
}
oracleStalenessThreshold = _oracleStalenessThreshold;
updateInterval = _poolSettings.updateInterval;
_setRule(_initialWeights, _initialIntermediateValues, _initialMovingAverages, _poolSettings);
@> _setInitialWeights(_initialWeights);
}
function _setInitialWeights(int256[] memory _weights) internal {
require(_normalizedFirstFourWeights == 0, "init");
require(_normalizedSecondFourWeights == 0, "init");
InputHelpers.ensureInputLengthMatch(_totalTokens, _weights.length);
int256 normalizedSum;
int256[] memory _weightsAndBlockMultiplier = new int256[]();
for (uint i; i < _weights.length; ) {
if (_weights[i] < int256(uint256(absoluteWeightGuardRail))) {
revert MinWeight();
}
_weightsAndBlockMultiplier[i] = _weights[i];
normalizedSum += _weights[i];
_weightsAndBlockMultiplier[i + _weights.length] = int256(0);
unchecked {
++i;
}
}
if (uint256(normalizedSum) != FixedPoint.ONE) {
revert NormalizedWeightInvariant();
}
if (_weightsAndBlockMultiplier.length > 8) {
@> int256[][] memory splitWeights = _splitWeightAndMultipliers(_weightsAndBlockMultiplier);
_normalizedFirstFourWeights = quantAMMPack32Array(splitWeights[0])[0];
_normalizedSecondFourWeights = quantAMMPack32Array(splitWeights[1])[0];
} else {
_normalizedFirstFourWeights = quantAMMPack32Array(_weightsAndBlockMultiplier)[0];
}
poolSettings.quantAMMBaseInterpolationDetails = QuantAMMBaseInterpolationVariables({
lastPossibleInterpolationTime: uint40(block.timestamp),
lastUpdateIntervalTime: uint40(block.timestamp)
});
emit WeightsUpdated(address(this), _weights);
}
function _splitWeightAndMultipliers(
int256[] memory weights
) internal pure returns (int256[][] memory splitWeights) {
uint256 tokenLength = weights.length / 2;
splitWeights = new int256[][]();
splitWeights[0] = new int256[]();
for (uint i; i < 4; ) {
splitWeights[0][i] = weights[i];
splitWeights[0][i + 4] = weights[i + tokenLength];
unchecked {
i++;
}
}
splitWeights[1] = new int256[]();
uint256 moreThan4Tokens = tokenLength - 4;
for (uint i = 0; i < moreThan4Tokens; ) {
uint256 i4 = i + 4;
splitWeights[1][i] = weights[i4];
splitWeights[1][i + moreThan4Tokens] = weights[i4 + tokenLength];
unchecked {
i++;
}
}
}
When weights.length = 9, the function assumes weights are evenly divisible into two halves of size tokenLength. However:
uint256 moreThan4Tokens = tokenLength - 4;
moreThan4Tokens = 4 - 4 = 0.
Second Loop:
for (uint i = 0; i < moreThan4Tokens; ) {
}
Impact
Users might be able to create pool and updateRunner might be able to update weight
Tools Used
Manual review
Recommendations
Handle odd-length explicitly and refactor the second loop