QuantAMM

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

Integer Overflow in Fixed-Point Scaling Corrupts AMM Variance Calculations During High Volatility

Summary

The quantAMMUnpack32Array function in ScalarQuantAMMBaseStorage contains an integer overflow vulnerability in its numeric scaling operations. The function unpacks storage slots containing multiple 32-bit integers and applies scaling differently for the first unpacked value versus subsequent values:

https://github.com/Cyfrin/2024-12-quantamm/blob/main/pkg/pool-quantamm/contracts/QuantAMMStorage.sol#L143

// First value unpacked:
targetArray[targetIndex] = (sourceElem >> 224) * 1e9;
// Subsequent values:
targetArray[targetIndex] = int256(int32(sourceElem >> 192)) * 1e9;
targetArray[targetIndex] = int256(int32(sourceElem >> 160)) * 1e9;

And in the sticky end handling:

https://github.com/Cyfrin/2024-12-quantamm/blob/main/pkg/pool-quantamm/contracts/QuantAMMStorage.sol#L197

targetArray[i] = int256(int32(_sourceArray[stickyEndSourceElem] >> offset)) * 1e9;

This inconsistency in type casting before multiplication creates potential for overflow. The first value gets shifted and multiplied without int32 bounds, while subsequent values are constrained to int32 before multiplication. When matching this with the packing function:

int256 elem = _sourceArray[i] / 1e9;
require(elem <= MAX32 && elem >= MIN32, "Overflow");

This creates an asymmetric situation where values that were accepted during packing could overflow during unpacking due to the different handling of the first versus subsequent values.

Impact

Given this is used for AMM pool state calculations, the overflow could corrupt variance and gradient values used in price calculations. The inconsistent handling means the corruption would affect some values in a packed slot differently than others, leading to unpredictable mathematical behavior in pool operations.

Would you like me to continue with the mitigation steps?.

Recommended mitigation steps

  1. Perform the multiplication after casting to int256:

targetArray[i] = (int256(int32(sourceElem >> offset))) * 1e9;
  1. Add explicit bounds checking:

int256 unpackedValue = int256(int32(sourceElem >> offset));
require(unpackedValue <= type(int32).max / 1e9, "Value too large for scaling");
targetArray[i] = unpackedValue * 1e9;
  1. Make scaling consistent between pack and unpack operations:

// In pack function
int256 elem = _sourceArray[i] / 1e9;
require(elem >= type(int32).min && elem <= type(int32).max, "Value out of bounds");
// In unpack function
int256 unpackedValue = int256(int32(sourceElem >> offset));
require(unpackedValue * 1e9 >= type(int256).min && unpackedValue * 1e9 <= type(int256).max, "Scaling overflow");
targetArray[i] = unpackedValue * 1e9;
Updates

Lead Judging Commences

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

Support

FAQs

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