According to the GMX integration notes, there is a possibility that two handlers may temporarily exist at the same time. As a result, the callback contract must be able to accept calls from multiple handlers. However, the gmxProxy contract does not implement this functionality to whitelist multiple handlers, leading to a critical issue. This limitation can severely impact the vault, potentially rendering it completely inoperable.
According to the GMX integration notes, points 8 and 9 state:
When creating a callback contract, the callback contract may need to whitelist the DepositHandler, OrderHandler, or WithdrawalHandler. It should be noted that new versions of these handlers may be deployed as new code is added to the handlers. Additionally, it is possible for two handlers to temporarily exist at the same time, e.g., OrderHandler(1) and OrderHandler(2). Due to this, the callback contract should be able to whitelist and simultaneously accept callbacks from multiple DepositHandlers, OrderHandlers, and WithdrawalHandlers.
From this, it is evident that multiple OrderHandlers may coexist temporarily. However, the gmxProxy contract currently supports only a single handler at a time, as shown in the validCallback modifier:
While the updateGmxAddresses() function allows updating handler addresses, it does not account for scenarios where multiple handlers exist simultaneously. This limitation could lead to critical issues.
Consider the following scenario:
The PerpetualVault opens a long/short position on GMX V2.
Bob deposits 100 collateral tokens into the vault, triggering the creation of a MarketIncrease order to increase the position.
The keeper calls runNextAction, which successfully creates the order on GMX V2.
The GMX V2 keeper subsequently executes the order.
At this point, assume that two OrderHandlers exist: OrderHandler(1) and OrderHandler(2), but the gmxProxy contract only accepts callbacks from OrderHandler(1).
If OrderHandler(2) attempts to call afterOrderExecution, the transaction will revert, preventing the necessary state updates in the vault.
Since GMX V2 executes orders using a try/catch block, the order will be executed on GMX V2, and Bob’s funds will be transferred to the GMX V2 vault’s position. However, because the callback was never executed, the vault's state remains inconsistent:
The FLOW state remains in DEPOSIT, preventing further deposits.
Bob does not receive any shares.
If the keeper attempts to call cancelFlow, it will revert due to _gmxLock == true:
Because FLOW remains stuck in DEPOSIT, no further deposits will be possible:
Additionally, attempts to cancel the flow will fail:
POC :
To run the test in PerpetualVault.t.sol, use:
Logs :
Vault will be completely bricked
Loss of user funds
DOS for the users
Manual review
According to GMX integration note
a possible solution would be to validate the role of the msg.sender in the RoleStore, e.g. RoleStore.hasRole(msg.sender, Role.CONTROLLER), this would check that the msg.sender is a valid handler
Likelihood: Low, when multiple orderHandler exist at the same time. Impact: High, some orders won’t trigger `afterOrderExecution` when they should, until the migration is complete. Leading to DoS and no share minted. Deploying a new proxy won’t change anything during all the “migration” since you cannot know which handler will respond.
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.