QuantAMM

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

_quantAMMPack128Array fails to pack array of length one. Potential DoS due to over/underflow.

Summary

In the QuantAMMStorage.sol::ScalarRuleQuantAMMStorage contract, there's a function called _quantAMMPack128Array to pack 128 bits integers array. However, function fails to pack array of length 1.

Vulnerability Details

QuantAMMStorage.sol::ScalarRuleQuantAMMStorage::_quantAMMPack128Array:

function _quantAMMPack128Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
uint256 sourceArrayLength = _sourceArray.length;
uint256 targetArrayLength = sourceArrayLength;
uint256 storageIndex;
require(_sourceArray.length != 0, "LEN0");
if (_sourceArray.length % 2 == 0) {
unchecked {
targetArrayLength = (targetArrayLength) / 2;
}
targetArray = new int256[](targetArrayLength);
for (uint256 i; i < sourceArrayLength - 1;) {
targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
unchecked {
i += 2;
++storageIndex;
}
}
} else {
int256 lastArrayItem = _sourceArray[_sourceArray.length - 1];
// @info: revert message should be "Last array element Over/Underflow" not "Last array element overflow"
require(
(lastArrayItem >= int256(type(int128).min)) && (lastArrayItem <= int256(type(int128).max)),
"Last array element overflow"
);
unchecked {
targetArrayLength = ((targetArrayLength - 1) / 2) + 1;
}
targetArray = new int256[](targetArrayLength);
@> // @info: over/underflow when source array length is 1 valid
@> uint256 sourceArrayLengthMinusTwo = sourceArrayLength - 2;
for (uint256 i; i < sourceArrayLengthMinusTwo;) {
targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
unchecked {
i += 2;
++storageIndex;
}
}
targetArray[storageIndex] = int256(int128(_sourceArray[sourceArrayLength - 1]));
}
}

When the source array length is 1 then, in that case the local variable sourceArrayLengthMinusTwo can't be calculated because 1-2= -1 and sourceArrayLength - 2 = -1 and solidity version >=0.8.x has unchecked protection. So in that case there would be a Denial of Service (DoS) issue due to over/underflow error.

PoC

  1. Go to the test/foundry/QuantAMMStorage.t.sol

  2. Paste the following test snippet inside the testing contract.

function testDoSOn128ArrayPackFails() public view {
// Define the target values
int256[] memory targetValues = new int256[]();
targetValues[0] = 4e18;
// Call the ExternalEncodeDecode128Array function from the contract
int256[] memory redecoded = mockQuantAMMStorage.ExternalEncodeDecode128Array(targetValues, 1);
checkResult(redecoded, targetValues);
}
  1. Open bash terminal (make sure you're on the right directory 2024-12-quantamm/pkg/pool-quantamm)

forge test --mt testDoSOn128ArrayPackFails -vvvv
  1. See the result logs

[⠢] Compiling...
No files changed, compilation skipped
Ran 1 test for test/foundry/QuantAMMStorage.t.sol:QuantAMMStorageTest
[FAIL. Reason: panic: arithmetic underflow or overflow (0x11)] testDoSOn128ArrayPackFails() (gas: 6952)
Traces:
[1861769] QuantAMMStorageTest::setUp()
├─ [1804896] → new MockQuantAMMStorage@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
│ └─ ← [Return] 9015 bytes of code
└─ ← [Stop]
[6952] QuantAMMStorageTest::testDoSOn128ArrayPackFails()
├─ [1433] MockQuantAMMStorage::ExternalEncodeDecode128Array([4000000000000000000 [4e18]], 1) [staticcall]
│ └─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
└─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.44ms (142.10µs CPU time)
Ran 1 test suite in 1.02s (1.44ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/foundry/QuantAMMStorage.t.sol:QuantAMMStorageTest
[FAIL. Reason: panic: arithmetic underflow or overflow (0x11)] testDoSOn128ArrayPackFails() (gas: 6952)
Encountered a total of 1 failing tests, 0 tests succeeded

The MockQuantAMMStorage::ExternalEncodeDecode128Array calls the _quantAMMPack128Array function internally, this utility is brought to us by You. So we can see it's failing and a DoS occurs due to an over/underflow. This is an edge case.

Impact

  1. DoS due to over/underflow mishandling

  2. Severe disruption in protocol's functionality, almost all functionalities get affected due to this issue i.e., Pool creation, Weight updates, Swaps and liquidity operations etc.

Tools used

  1. Manual review

  2. chisel

  3. console

Recommendations

We can implement a check to allow to do such arithmetic operation when source array length is greater than 1. One similar solution is given below:

QuantAMMStorage.sol::ScalarRuleQuantAMMStorage::_quantAMMPack128Array:

function _quantAMMPack128Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
uint256 sourceArrayLength = _sourceArray.length;
uint256 targetArrayLength = sourceArrayLength;
uint256 storageIndex;
require(_sourceArray.length != 0, "LEN0");
if (_sourceArray.length % 2 == 0) {
unchecked {
targetArrayLength = (targetArrayLength) / 2;
}
targetArray = new int256[](targetArrayLength);
for (uint256 i; i < sourceArrayLength - 1;) {
targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
unchecked {
i += 2;
++storageIndex;
}
}
} else {
int256 lastArrayItem = _sourceArray[_sourceArray.length - 1];
require(
(lastArrayItem >= int256(type(int128).min)) && (lastArrayItem <= int256(type(int128).max)),
"Last array element overflow"
);
unchecked {
targetArrayLength = ((targetArrayLength - 1) / 2) + 1;
}
targetArray = new int256[](targetArrayLength);
+ if (sourceArrayLength > 1) {
uint256 sourceArrayLengthMinusTwo = sourceArrayLength - 2;
for (uint256 i; i < sourceArrayLengthMinusTwo;) {
targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
unchecked {
i += 2;
++storageIndex;
}
}
+ }
targetArray[storageIndex] = int256(int128(_sourceArray[sourceArrayLength - 1]));
}
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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

Give us feedback!