DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Queue Reset Bypass Enables Order Flow Manipulation

Summary

GmxProxy contract's queue state can be manipulated because queue reset logic isn't properly restricted. This allows unauthorized resetting of pending orders, potentially disrupting the order execution flow between PerpetualVault and GMX.

The issue centers around the GmxProxy's queue management. The contract maintains an OrderQueue struct that tracks pending GMX orders and their settlement status. Its requires that queue resets should only happen through valid GMX handlers (orderHandler, liquidationHandler, or adlHandler), but this invariant can be violated.

Think of it like a ticket validation system where only authorized ticket inspectors should be able to clear the queue, but there's a backdoor allowing anyone to reset it. We expects that when pendingOrderCount goes from >0 to 0 (queue reset), the msg.sender must be one of the valid GMX handlers. However, the issue is that while the validCallback modifier checks the caller, the queue deletion happens unconditionally after processing. Why isn't this protected by additional access controls?

Impact

If exploited, an attacker could:

  1. Wait for a legitimate order to be queued

  2. Force a queue reset through an unauthorized path

  3. Disrupt the normal order flow between PerpetualVault and GMX

This breaks the core assumption that order execution flow is strictly controlled through authorized GMX handlers.

Vulnerability Details

The GmxProxy contract acts as a critical bridge between the PerpetualVault and GMX's trading infrastructure like an order management system at a brokerage, it needs to ensure every trade request flows through proper channels and gets executed correctly.

When users want to open or close positions through the PerpetualVault, their requests get queued in GmxProxy. Only authorized GMX handlers (orderHandler, liquidationHandler, or adlHandler) should be able to process and clear these queues, similar to how only licensed brokers can execute trades on an exchange.

The queue can be reset (cleared to empty) by paths that bypass the authorized handlers. Here's how: GmxProxy.sol#afterOrderExecution

function afterOrderExecution(
bytes32 requestKey,
Order.Props memory order,
EventLogData memory eventData
) external override validCallback(requestKey, order) { // πŸ”’ Access control check
bytes32 positionKey = keccak256(
abi.encode(
address(this),
order.addresses.market,
order.addresses.initialCollateralToken,
order.flags.isLong
)
); // πŸ”‘ Position identifier generation
​
MarketPrices memory prices = getMarketPrices(order.addresses.market); // πŸ“Š Price data fetch
​
// πŸ’° Funding fee claims
if (order.numbers.orderType != Order.OrderType.MarketSwap) {
// ... funding fee claiming logic ...
}
​
// 🏦 Liquidation handling
if (order.numbers.orderType == Order.OrderType.Liquidation) {
// ... liquidation logic ...
}
// βš–οΈ ADL (Auto-Deleveraging) handling
else if (msg.sender == address(adlHandler)) {
// ... ADL logic ...
}
// πŸ”„ Normal order processing
else {
// ... order processing logic ...
IPerpetualVault(perpVault).afterOrderExecution(
requestKey,
positionKey,
orderResultData,
prices
);
​
delete queue; // 🚨 Vulnerable queue reset - Queue gets reset unconditionally
}
}
​

While the validCallback modifier checks the caller's authorization, the actual queue deletion happens unconditionally after processing. This breaks the core invariant that queue state should only be modified by GMX handlers.

When a vault user opens a position:

  1. PerpetualVault queues the order in GmxProxy

  2. An unauthorized path could clear this queue

  3. The position never gets executed on GMX

  4. User funds remain locked in an incomplete state

The PerpetualVault's entire order flow depends on GmxProxy maintaining strict queue integrity, like a broker ensuring trades only execute through licensed channels. The code assumed the validCallback modifier provided sufficient protection. However, while it checks the caller's authorization, the queue deletion happens unconditionally after processing, breaking the core invariant that only GMX handlers should reset queue state.

Impact

The protocol's asynchronous order flow (detailed in PerpetualVault.sol) makes state management tricky. The code was desing to focused on the happy path where orders flow through proper GMX handlers, but overlooked edge cases where queue state could be manipulated mid-flow.

The PerpetualVault system acts like an automated trading desk, where users deposit collateral and the protocol manages leveraged positions on GMX. At its core, the GmxProxy serves as the critical bridge between user intentions and actual GMX trades, maintaining order flow through a carefully managed queue system.

When examining the queue reset vulnerability, imagine a trading desk where order tickets mysteriously vanish before execution. The GmxProxy contract's queue management system, designed to track pending GMX orders and their settlement status, can be manipulated because its reset mechanism lacks proper access controls.

The technical heart of this issue lies in the afterOrderExecution function. While it includes a validCallback modifier to check caller authorization, the queue deletion happens unconditionally afterward. This creates a scenario where the queue state tracking up to 3x leveraged positions worth up to 100,000 USDC can be cleared without proper GMX handler authorization.

The real-world impact becomes clear when considering the protocol's automated trading flow. A user deposits USDC into the vault, expecting it to open a leveraged ETH position. The vault queues this order through GmxProxy, but an attacker exploits the queue reset vulnerability, causing the position to never execute while funds remain locked. This directly breaks the protocol's promise of automated position management and could affect multiple users' deposits simultaneously.

Recommendations

The vulnerability is in the interaction flow between these contracts:

  • PerpetualVault.sol initiates orders

  • GmxProxy.sol manages GMX interaction

  • IGmxProxy.sol defines the interface

  • IGmxReader.sol provides market data

We need proper queue state encapsulation with explicit handler checks, ensuring that only authorized GMX handlers (orderHandler, liquidationHandler, or adlHandler) can reset the queue state. This maintains the protocol's core invariant of controlled order flow between PerpetualVault and GMX.

// πŸŒ‰ GmxProxy.sol - Bridge between PerpetualVault and GMX
function afterOrderExecution(
bytes32 requestKey,
Order.Props memory order,
EventLogData memory eventData
) external override validCallback(requestKey, order) { // βœ… Initial auth check
// πŸ—οΈ Core Position Setup
bytes32 positionKey = keccak256(abi.encode(
address(this),
order.addresses.market,
order.addresses.initialCollateralToken,
order.flags.isLong
));
// πŸ“‘ Market Data Fetch via IGmxReader
MarketPrices memory prices = getMarketPrices(order.addresses.market);
​
// πŸ”„ Order Flow Management
if (order.numbers.orderType == Order.OrderType.Liquidation) {
// ... liquidation handling ...
IPerpetualVault(perpVault).afterLiquidationExecution(); // πŸ“¬ Notify vault
}
else if (msg.sender == address(adlHandler)) {
// ... ADL handling ...
}
else {
// πŸ“¦ Normal Order Processing
IPerpetualVault(perpVault).afterOrderExecution(
requestKey,
positionKey,
orderResultData,
prices
);
delete queue; // ⚠️ Queue reset vulnerability
}
}
​
// πŸ” Proposed Fix:
function resetQueue() internal {
require(
msg.sender == orderHandler ||
msg.sender == liquidationHandler ||
msg.sender == adlHandler,
"GmxProxy: Unauthorized queue reset"
);
delete queue;
}
Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Suppositions

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.

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Suppositions

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.

Support

FAQs

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

Give us feedback!