The GmxProxy.sol contract contains a vulnerability where an order can become stuck in the queue if GMX Keepers fail to execute or cancel it, preventing the queue.requestKey from being cleared. This blocks subsequent orders initiated by the PerpetualVault, resulting in a temporary Denial of Service (DoS) condition that halts user interactions such as deposits and withdrawals. The issue arises from the absence of a timeout mechanism or robust fallback logic to handle unprocessed orders, representing a significant internal logic flaw exploitable under realistic market conditions.
Contract Affected: GmxProxy.sol
Functions Involved:
createOrder (https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/GmxProxy.sol#L371): Sets queue.requestKey without a timeout or check for existing orders.
afterOrderExecution (https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/GmxProxy.sol#L206): Clears queue only on successful execution.
afterOrderCancellation (https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/GmxProxy.sol#L292C12-L292C34): Clears queue only on cancellation.
afterOrderFrozen (https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/GmxProxy.sol#L320): Ineffectively handles frozen orders, leaving queue intact.
Root Cause:
The queue struct (OrderQueue { bytes32 requestKey; bool isSettle; }) is populated when an order is created via createOrder, but it is only cleared when GMX triggers a callback (afterOrderExecution or afterOrderCancellation).
If an order has an unachievable acceptablePrice (e.g., significantly below market price) and GMX Keepers do not execute or cancel it, no callback occurs, leaving queue.requestKey stuck indefinitely.
The PerpetualVault relies on GmxProxy to process orders and a stuck queue causes the vault’s flow to remain in progress (e.g., SIGNAL_CHANGE), blocking new actions due to the _noneFlow check in deposit.
Trigger Condition: Submitting an order with an acceptablePrice (e.g., 1000 USD for ETH when the market price is ~3386 USD) that GMX cannot execute immediately, resulting in the order remaining pending without a callback.
Proof of Code:
[PASS] test_QueueStuckDoS() (gas: 1779771)
Logs:
Stuck Request Key: 0x2e9b56aff7ad76a388482089a48c1ad1e043cb606088141ae8d535ca504b10cb
DoS confirmed: New deposit cannot proceed due to stuck queue
Users are unable to deposit or withdraw funds from the PerpetualVault as new orders are blocked by the stuck queue, leading to a temporary DoS.
Funds deposited in the vault remain locked until the stuck order is resolved, potentially causing financial losses if market conditions shift adversely.
The vault’s operational reliability is compromised, undermining user confidence in the Gamma Protocol.
Affected Parties: All users interacting with the PerpetualVault via GmxProxy, including depositors and withdrawers.
Real-world Scenario: During market volatility (e.g., ETH price fluctuates 20% in minutes), an order with a low acceptablePrice may not be executed promptly, stalling the vault for hours until manual intervention or GMX Keeper action occurs.
Manual Review
Foundry
Implement a Timeout Mechanism:
Add a timestamp field to the OrderQueue struct and allow PerpetualVault to clear stale orders after a specified duration (e.g., 1 hour).
Example:
Enhance Frozen Order Handling:
Ensure afterOrderFrozen clears the queue to prevent indefinite blocking:
Prevent Queue Overwrite:
Add a check to block new orders when queue is occupied:
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
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.