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