RootCause & Where It Happens
Inside the internal function _totalAmount()
:
The contract unconditionally adds positionData.netValue / prices.shortTokenPrice.min
to its total. It assumes positionData.netValue
is non-negative. However, in many GMX-like designs, the “net value” of a position can be negative if the user is deeply underwater (i.e., the position’s losses exceed the collateral). If netValue
is an integer that can go below zero, the vault code here will be using an incorrect or unbounded interpretation in the following ways:
If netValue
is stored as a signed integer but cast (or truncated) to an unsigned integer, negative values can become huge positive integers (underflow).
If netValue
is zero-truncated in external calls but is actually negative in GMX’s internal accounting, then _totalAmount()
incorrectly treats the position as having zero or more remaining collateral—overstating real assets in the vault.
Either scenario leads to inaccurate reporting of total vault assets. Downstream functions that rely on _totalAmount()
—such as _mint()
for share calculation or any off-chain accounting—will be based on the vault having more assets than it truly does.
Issue Proof & Common Cases when it happens
In leveraged perps trading, a “net value” can indeed go negative if the position is undercollateralized or near liquidation.
The vault code never checks for negative or zeroed-out net value. Instead, it always adds positionData.netValue / prices.shortTokenPrice.min
.
If that net value is actually negative (or forcibly truncated to 0 by a mismatch in data types), the vault overstates its total. This can lead to share over-minting, inaccurate user PnL, or other mis-accounting that benefits some depositors at the expense of others.
Impact
High severity in edge cases where positions become net negative. The vault’s share/accounting logic will incorrectly assume additional collateral, effectively letting users withdraw or mint shares at inflated valuations.
Even if negative net value is rare, it is still a scenario that can occur during extreme market movements or partial liquidations. The vault logic should handle or revert in such conditions rather than silently mis-account.
Attack Example
Suppose a large short position is heavily underwater—GMX’s internal accounting shows net value of -500 USDC
for the vault’s position.
If vaultReader.getPositionInfo(...)
returns that negative netValue
incorrectly as a big number (due to integer underflow), _totalAmount()
sees a large positive integer and inflates the vault’s total.
Another user deposits or withdraws at this inflated total, receiving more shares or a bigger payout than they deserve, effectively stealing from the pool.
Recommended Fix
Handle Negative Net Value
If positionData.netValue
is intended to be signed, store and handle it as int256
. If it is negative, the vault code must reduce the total or revert if the position is beyond salvage.
Alternatively, if a negative net value is feasible, the vault must treat it as 0 or revert the accounting to prevent over-reporting.
Type-Safe Conversion
Confirm that positionData.netValue
in vaultReader.getPositionInfo(...)
is never forcibly cast from negative to large positive.
If netValue
is actually a uint256
, confirm GMX never returns a negative scenario. If it can, convert it properly (e.g., use a signed type).
Add a Safeguard
For example:
There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.