sherlock-audit / 2023-04-gmx-judging

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

IllIllI - Operations may overflow when sign is flipped from negative to positive

sherlock-admin opened this issue · comments

IllIllI

medium

Operations may overflow when sign is flipped from negative to positive

Summary

Operations may overflow when signs are flipped from negatives to positives, causing orders to get canceled incorrectly

Vulnerability Detail

There are many places in the code where the sign of an int256 is flipped, and the value is assigned to a uint256. The way this is done is by prepending a negative sign to the value, then casting, which is not a safe operation and may trigger an overflow.

Most places are unable to overflow because they don't get negative enough, but one of the places where it's possible, and there is protection attempted to prevent it, is when the virtual inventory is assigned as the open interest in getNextOpenInterestForVirtualInventory()

Impact

The assignment will revert with an overflow exception, which will cause market orders to get canceled when it was not necessary for them to be, including liquidation and ADL orders, which may have prevented subsequent overflows.

Code Snippet

Negation without an unchecked block happens prior to the overflow adjustment

// File: gmx-synthetics/contracts/pricing/PositionPricingUtils.sol : PositionPricingUtils.getNextOpenInterestForVirtualInventory()   #1

310            // if virtualInventory is more than zero it means that
311            // tokens were virtually sold to the pool, so set shortOpenInterest
312            // to the virtualInventory value
313            // if virtualInventory is less than zero it means that
314            // tokens were virtually bought from the pool, so set longOpenInterest
315            // to the virtualInventory value
316            if (virtualInventory > 0) {
317                shortOpenInterest = virtualInventory.toUint256();
318            } else {
319 @>             longOpenInterest = (-virtualInventory).toUint256();
320            }
321    
322            // the virtual long and short open interest is adjusted by the usdDelta
323            // to prevent an overflow in getNextOpenInterestParams
324            // price impact depends on the change in USD balance, so offsetting both
325            // values equally should not change the price impact calculation
326 @>         if (params.usdDelta < 0) {
327                uint256 offset = (-params.usdDelta).toUint256();
328                longOpenInterest += offset;
329:               shortOpenInterest += offset;

https://github.com/sherlock-audit/2023-04-gmx/blob/main/gmx-synthetics/contracts/pricing/PositionPricingUtils.sol#L310-L329

This test shows that using negation will result in an overflow when the value is equal to type(int256).min, unless the operation is in an unchecked block:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import "forge-std/Test.sol";

contract It is Test {
    function testIt() external view returns (uint256) {
	uint256 tmp;
	console.log("max:\t");
	console.logInt(type(int256).max);
	console.log("min:\t");
	console.logInt(type(int256).min);
	unchecked {
	    tmp = uint256(-type(int256).min); // doesn't overflow
	    console.log("tmp:\t");
	    console.log(tmp);
	}
	console.log("prior to no unchecked block");
	tmp = uint256(-type(int256).min); // overflows
	return tmp;
    }
}

Output:

$ forge test -vv
[⠑] Compiling...
No files changed, compilation skipped

Running 1 test for src/T.sol:It
[FAIL. Reason: Arithmetic over/underflow] testIt():(uint256) (gas: 6346)
Logs:
  max:	
  57896044618658097711785492504343953926634992332820282019728792003956564819967
  min:	
  -57896044618658097711785492504343953926634992332820282019728792003956564819968
  tmp:	
  57896044618658097711785492504343953926634992332820282019728792003956564819968
  prior to no unchecked block

Test result: FAILED. 0 passed; 1 failed; finished in 807.41µs

Failing tests:
Encountered 1 failing test in src/T.sol:It
[FAIL. Reason: Arithmetic over/underflow] testIt():(uint256) (gas: 6346)

Encountered a total of 1 failing tests, 0 tests succeeded

Tool used

Manual Review

Recommendation

Use OpenZeppelin's SignedMath.abs(), which does the flip under an unchecked block, everywhere you currently use negative signs to change a negative value to a positive one.

would classify this as a low since it is very unlikely for overflow to occur for those values as well

"The vulnerability must be something that is not considered an acceptable risk by a reasonable protocol team." https://docs.sherlock.xyz/audits/judging/judging . This one is marked as disputed and there is no fix, so closing