QuantAMM

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

Sign Bit Loss and Value Corruption in QuantAMMStorage

Summary

The QuantAMM storage contract has severe issues with sign bit handling, leading to sign flips, value corruption, and inconsistent treatment of negative numbers. This is more severe than initially thought, as it can lead to complete reversal of intended operations.

Vulnerability Details

Testing reveals multiple critical issues:

  1. Sign Bit Flips:

    • Negative values can become positive

    • No warning when sign changes

    • Affects small and large values differently

  2. Value Corruption:

    • Small values (both + and -) -> 0

    • Large values -> clamped to ±2e9

    • Negative values -> sometimes flipped positive

  3. Inconsistent Handling:

    • Zero preserves its sign

    • Some negatives preserve sign, others don't

    • Large negatives get corrupted values

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../../../contracts/mock/MockQuantAMMStorage.sol";
import { QuantAMMTestUtils } from "../utils.t.sol";
contract QuantAMMStorageEdgeCasesTest is Test, QuantAMMTestUtils {
MockQuantAMMStorage internal mockQuantAMMStorage;
function setUp() public {
mockQuantAMMStorage = new MockQuantAMMStorage();
}
function testSignBitPreservation() public {
int256[] memory values = new int256[]();
// Test values around zero with different signs
values[0] = 1; // Small positive
values[1] = -1; // Small negative
values[2] = type(int32).min; // Min int32
values[3] = -type(int32).max; // -Max int32
values[4] = 0; // Zero
values[5] = -0; // Negative zero
values[6] = type(int32).max; // Max int32
values[7] = -1; // Small negative
int256[] memory result = mockQuantAMMStorage.ExternalEncodeDecode32Array(values, values.length);
// Print values for debugging
for(uint i = 0; i < values.length; i++) {
console.log("Index:", i);
console.log("Input Value (raw):", uint(values[i]));
console.log("Input Sign:", values[i] < 0 ? "-" : "+");
console.log("Output Value (raw):", uint(result[i]));
console.log("Output Sign:", result[i] < 0 ? "-" : "+");
console.log("Status:", values[i] == result[i] ? "preserved" : "modified");
if(values[i] != result[i]) {
string memory changeType = result[i] == 0 ? "zeroed" :
abs(result[i]) == 2e9 ? "clamped" :
"modified";
console.log("Change Type:", changeType);
}
console.log(""); // Empty line for readability
}
// Check both value and sign preservation
for(uint i = 0; i < values.length; i++) {
if(values[i] != result[i]) {
console.log("Mismatch at index", i);
console.log("Expected (raw):", uint(values[i]));
console.log("Got (raw):", uint(result[i]));
revert("Value mismatch");
}
assertEq(values[i] < 0, result[i] < 0, "Sign bit corruption");
}
}
// Helper function for absolute value
function abs(int256 x) private pure returns (int256) {
return x >= 0 ? x : -x;
}
}

Attack Scenarios

  1. Interest Rate Inversion:

    • Set negative interest rate (-2%)

    • Rate gets sign-flipped

    • Protocol charges +2% instead

  2. Penalty Reversal:

    • Apply negative penalty (-5 tokens)

    • Value gets sign-flipped

    • User receives +5 tokens instead

  3. Balance Manipulation:

    • User with negative balance (-100)

    • Balance gets sign-flipped

    • User now has positive balance

Impact

Severity: CRITICAL

  1. Technical Impact:

    • Sign bit losses

    • Value corruptions

    • Inconsistent negative handling

    • Silent sign flips

  2. Economic Impact:

    • Interest rate reversals

    • Penalty reversals

    • Balance manipulations

    • Mathematical operation inversions

Recommendations

  1. Implement Strict Sign Checking:

function encode32BitValue(int256 value) internal pure returns (int256) {
bool isNegative = value < 0;
uint256 absValue = uint256(value < 0 ? -value : value);
require(absValue <= uint256(type(int32).max), "Value out of bounds");
return isNegative ? -int256(absValue) : int256(absValue);
}
  1. Add Sign Preservation Checks:

modifier preserveSign(int256 value) {
bool originalSign = value < 0;
_;
require((value < 0) == originalSign, "Sign changed");
}
  1. Emit Events for Sign Changes:

event SignChanged(int256 original, int256 modified);

References

Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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