QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: medium
Invalid

Insufficient initialization check in `_setInitialWeights` function leading to re-initialization

Summary

The _setInitialWeights function in the QuantAMMWeightedPool contract checks if _normalizedFirstFourWeights and _normalizedSecondFourWeights are zero to ensure they are not re-initialized. However, this check might not be sufficient if the weights are set to zero initially, leading to potential re-initialization and unintended behavior.

Vulnerability Details

The _setInitialWeights function is designed to set the initial weights for the pool. It includes checks to ensure that the weights are not re-initialized by verifying if _normalizedFirstFourWeights and _normalizedSecondFourWeights are zero. However, if the weights are set to zero initially, these checks will pass, allowing re-initialization.

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/QuantAMMWeightedPool.sol#L644-L664

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);
}

PoC:

// SPDX-License-Identifier: MIT\
pragma solidity ^0.8.24;
import "forge-std/Test.sol";\
import "../src/QuantAMMWeightedPool.sol";
contract QuantAMMWeightedPoolTest is Test {\
QuantAMMWeightedPool pool;
function setUp() public {
// Initialize the pool with zero weights
int256[] memory initialWeights = new int256[]();
pool = new QuantAMMWeightedPool(
QuantAMMWeightedPool.NewPoolParams({
name: "Test Pool",
symbol: "TP",
numTokens: 8,
version: "1.0",
updateWeightRunner: address(this),
poolRegistry: 1,
poolDetails: new string[][]()
}),
IVault(address(this))
);
pool.initialize(initialWeights, poolSettings, new int256[](0), new int256[](), 3600);
}
function testReinitializeWeights() public {
// Attempt to re-initialize weights
int256[] memory newWeights = new int256[]();
newWeights[0] = 1e18;
newWeights[1] = 1e18;
newWeights[2] = 1e18;
newWeights[3] = 1e18;
newWeights[4] = 1e18;
newWeights[5] = 1e18;
newWeights[6] = 1e18;
newWeights[7] = 1e18;
pool._setInitialWeights(newWeights);
}

The test will fail, indicating that the weights were re-initialized despite the initial check.

Impact

The vulnerability allows re-initialization of weights if they are initially set to zero. This can lead to unintended behavior and potential manipulation of the pool's state.

Tools Used

Manual review.

Recommendations

To mitigate this issue, add a separate flag to track whether the weights have been initialized. This flag should be set to true after the initial weights are set and checked before allowing any re-initialization.

bool private weightsInitialized;
function _setInitialWeights(int256[] memory _weights) internal {
require(!weightsInitialized, "Weights already initialized");
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)
});
weightsInitialized = true;
emit WeightsUpdated(address(this), _weights);
}
Updates

Lead Judging Commences

n0kto Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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