DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Invalid

Unsafe typecasting of `uint256` to `uint88` can result in protocol losing high amount in gasFee

Summary

When user has consumed a lot of gas and value of gasUsed is high, they are incorectly charged a very less gasFee due to unsafe typecasting from uint256 to uint88. As per protocol comments: By basing gasFee off of baseFee instead of priority, adversaries are prevent from draining the TAPP - this is violated in such cases.

Vulnerability Details

m.gasFee is calculated as:

File: contracts/facets/MarginCallPrimaryFacet.sol
250 //@dev manually setting basefee to 1,000,000 in foundry.toml;
251 //@dev By basing gasFee off of baseFee instead of priority, adversaries are prevent from draining the TAPP
252 @> m.gasFee = uint88(gasUsed * block.basefee); // @dev(safe-cast)

Data types of these variables are:

uint88 m.gasFee

uint256 gasUsed

block.basefee is uint256

block.basefee is set as 1_000_000_000 on local chain (is even higher on mainnet). For any value of around gasUsed >= 0.30949 ether, the value of m.gasFee would be calculated as disproportionately low.

Note that these lines of code are inside the _performForcedBid() function which calls createForcedBid() on Line 239 which has a shortHintArray param. An attacker could pass a large array here and cause such high gas usage.

PoC

Create a new file under test/ folder named MathCastingGasFee.t.sol and run the following code via forge test --mt test_casting_gas -vv:

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;
import {console} from "contracts/libraries/console.sol";
contract MathCastingGasFee {
uint256 private gasUsed;
function setUp() public {
// we could easily keep the below value of `gasUsed` as `type(uint256).max`, but
// let's demo with something smaller
gasUsed = 0.30949 ether;
}
/* solhint-disable no-console */
function test_casting_gas() public view {
console.log("Current block.basefee =", block.basefee); // On mainnet fork, it's around 8497767262. On local it has been set to 1000000000.
uint256 gasFee256 = (gasUsed * block.basefee);
uint88 gasFee88 = uint88(gasUsed * block.basefee);
console.log("\ngasFee256 =");
console.log(gasFee256);
console.log("\ngasFee88 =");
console.log(gasFee88);
}
}

Output:

Logs:
Current block.basefee = 1000000000
gasFee256 =
309490000000000000000000000
gasFee88 =
4990178654931275218944

gasFee256 is the actual value while gasFee88 is the down-casted one used by the protocol.

Impact

By basing gasFee off of baseFee instead of priority, adversaries are prevent from draining the TAPP - this protocol objective is violated when adversary consumes a lot of gas.

Tools Used

Manual inspection.

Recommendations

Use a safe typecasting library or a custom function which reverts in such cases. Example of such a function could be:

function safeU88(uint256 n) internal pure returns (uint88) {
if (n > type(uint88).max) revert Errors.InvalidUInt88();
return uint88(n);
}

And then use it in code:

File: contracts/facets/MarginCallPrimaryFacet.sol
250 //@dev manually setting basefee to 1,000,000 in foundry.toml;
251 //@dev By basing gasFee off of baseFee instead of priority, adversaries are prevent from draining the TAPP
- 252 m.gasFee = uint88(gasUsed * block.basefee); // @dev(safe-cast)
+ 252 m.gasFee = safeU88(gasUsed * block.basefee); // @audit : safe-casted now
Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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