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

Incorrect Calldata Validation in ParaSwapUtils Causes Denial of Service for All Paraswap Operations

Summary

The ParaSwapUtils._validateCallData() function attempts to validate the receiver address from Paraswap's simpleSwap() calldata, but incorrectly reads expectedAmount instead. This causes all validation checks to fail since it compares a uint256 value (implicitly cast to address) with address(this), effectively blocking all Paraswap operations in the PerpetualVault contract.

Vulnerability Details

The _validateCallData() function attempts to validate the calldata by checking if the receiver matches address(this):

function _validateCallData(address to, bytes memory callData) internal view {
require(to == address(0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57), "invalid paraswap callee");
address receiver;
assembly {
receiver := mload(add(callData, 196)) // INCORRECT: reads expectedAmount
}
require(receiver == address(this), "invalid paraswap calldata");
}

The SimpleData struct layout shows that offset 196 actually points to expectedAmount:

SimpleData struct layout:
[36:68] - fromToken
[68:100] - toToken
[100:132] - fromAmount
[132:164] - toAmount
[164:196] - expectedAmount <-- Currently being read as receiver
[196:228] - callees offset
...

Two critical issues:

  1. The SimpleData struct doesn't contain a receiver field

  2. The function reads expectedAmount (a uint256) and implicitly casts it to address before comparison

  3. The probability of a uint256 value matching address(this) is practically zero.

Impact Details

This validation error causes a complete denial of service for all Paraswap operations in the PerpetualVault contract. Users cannot perform deposits or withdrawals that require token swaps through Paraswap, as all such transactions will revert at the validation step. This effectively breaks core functionality of the protocol when Paraswap integration is needed.

Tools Used

Manual Review

Recommendations

Remove the receiver validation since SimpleData doesn't contain such a field. If additional validation is required, it should be based on actual fields in the SimpleData struct:

function _validateCallData(address to, bytes memory callData) internal view {
require(to == address(0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57), "invalid paraswap callee");
// Remove incorrect receiver validation
// Add other necessary validations based on actual SimpleData fields if needed
}

If receiver validation is deemed necessary, ensure proper documentation of the receiver's location in the calldata and verify that the validation logic aligns with Paraswap's actual implementation.

Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

invalid_ParaswapUtils_incorrect_offset_SimpleSwap

Keepers use megaSwap with this struct: struct MegaSwapSellData { address fromToken; uint256 fromAmount; uint256 toAmount; uint256 expectedAmount; address payable beneficiary; Utils.MegaSwapPath[] path; address payable partner; uint256 feePercent; bytes permit; uint256 deadline; bytes16 uuid; } 32 first bytes of callData bytes array → length of the bytes array. 4 bytes selector, 32 bytes → offset of the struct → 68 bytes before the fromToken.

Support

FAQs

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