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

PerpetualVault Releases Lock Too Early in Order Cancellation Allowing State Corruption

Brief

The PerpetualVault's lock mechanism, controlled by the _gmxLock state variable, is prematurely released during order cancellation callbacks. This early unlock exposes a window where critical operations, such as flow state changes, nextAction updates, and execution fee handling, can occur without lock protection, potentially leading to inconsistent internal states or reentrancy vulnerabilities.

Details

The vulnerability exists because the callback function resets _gmxLock to false at the start, rather than after all critical state updates. The following snippet from PerpetualVault.sol highlights the core issue:

function afterOrderCancellation(
bytes32 requestKey,
Order.OrderType orderType,
IGmxProxy.OrderResultData memory orderResultData
) external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
_gmxLock = false; // Lock released too early
if (orderResultData.isSettle) {
nextAction.selector = NextActionSelector.SETTLE_ACTION;
} else if (orderType == Order.OrderType.MarketSwap) {
nextAction.selector = NextActionSelector.SWAP_ACTION;
nextAction.data = abi.encode(
swapProgressData.remaining,
swapProgressData.isCollateralToIndex
);
} else {
if (flow == FLOW.DEPOSIT) {
nextAction.selector = NextActionSelector.INCREASE_ACTION;
nextAction.data = abi.encode(beenLong);
} else if (flow == FLOW.WITHDRAW) {
nextAction.selector = NextActionSelector.WITHDRAW_ACTION;
} else {
delete flowData;
delete flow;
}
}
emit GmxPositionCallbackCalled(requestKey, false);
}

Here, _gmxLock is disabled (_gmxLock = false) before the subsequent logic moves several stateful variables (flow, nextAction, swapProgressData). If a reentrant call or unexpected execution path is triggered after _gmxLock is released, whether through internal calls, external callbacks, or contract interactions, those operations run while the contract is partially updated, risking corruption or inconsistent values.

Moreover, PerpetualVault relies on this lock to defend against reentrancy in other functions. Releasing it prematurely creates an interval in which the vault’s internal tracking for flows and nextAction might be manipulated without the intended lock constraints. While OpenZeppelin’s ReentrancyGuardUpgradeable helps protect against direct function reentrancy, the design still leaves crucial system transitions unprotected in this callback window.

Specific Impact

Early lock release in afterOrderCancellation can allow mid-execution state corruption (e.g., flow manipulation, incorrect nextAction updates, or reentrancy), putting user funds, trading positions, and the protocol’s operational integrity at significant risk.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
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!