Overlay Protocol contest - gpersoon's results

A protocol for trading #DeFi data streams.

General Information

Platform: Code4rena

Start Date: 16/11/2021

Pot Size: $50,000 ETH

Total HM: 11

Participants: 17

Period: 7 days

Judge: LSDan

Total Solo HM: 8

Id: 49

League: ETH

Overlay Protocol

Findings Distribution

Researcher Performance

Rank: 3/17

Findings: 5

Award: $6,558.33

🌟 Selected for report: 9

🚀 Solo Findings: 2

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
2 (Med Risk)
sponsor disputed

Awards

0.4973 ETH - $2,297.86

External Links

Handle

gpersoon

Vulnerability details

Impact

The contract LogExpMath.sol seems to be a fork of the balancer LogExpMath.sol contract. It is mostly similar, except for checks for x and y being 0 in the beginning of the function pow(), see below.

This omission might lead to unexpected results.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/libraries/LogExpMath.sol#L93-L110

 function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
        _require(x < 2**255, Errors.X_OUT_OF_BOUNDS);

https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/solidity-utils/contracts/math/LogExpMath.sol#L93-L109

function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) {
            // We solve the 0^0 indetermination by making it equal one.
            return uint256(ONE_18);
        }

        if (x == 0) {
            return 0;
        }      
        _require(x < 2**255, Errors.X_OUT_OF_BOUNDS);

Tools Used

Check if the extra code of the balance contract is useful and if so add it.

#0 - commercium-sys

2021-12-07T21:35:09Z

Out of scope

#1 - dmvt

2021-12-18T13:17:46Z

I disagree with sponsor regarding scope. The Contracts section of the Contest Scope lists several contracts which rely on contracts/libraries/FixedPoint.sol. This contract uses the pow function containing the issue described. The warden has not described an exact attack but has show a math issue, which can certainly lead to a hypothetical loss of funds. Medium severity is appropriate and sponsor should definitely fix this.

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

0.4973 ETH - $2,297.86

External Links

Handle

gpersoon

Vulnerability details

Impact

The function disableCollateral of OverlayV1Mothership.sol doesn't set collateralActive[_collateral] = false; But it does revoke the roles.

Now enableCollateral can never be used because collateralActive[_collateral] ==true and it will never pass the second require. So you can never grant the roles again.

Note: enableCollateral also doesn't set collateralActive[_collateral] = true

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/mothership/OverlayV1Mothership.sol#L133-L153

 function enableCollateral (address _collateral) external onlyGovernor {
        require(collateralExists[_collateral], "OVLV1:!exists");
        require(!collateralActive[_collateral], "OVLV1:!disabled");
        OverlayToken(ovl).grantRole(OverlayToken(ovl).MINTER_ROLE(), _collateral);
        OverlayToken(ovl).grantRole(OverlayToken(ovl).BURNER_ROLE(), _collateral);
    }

    function disableCollateral (address _collateral) external onlyGovernor {
        require(collateralActive[_collateral], "OVLV1:!enabled");
        OverlayToken(ovl).revokeRole(OverlayToken(ovl).MINTER_ROLE(), _collateral);
        OverlayToken(ovl).revokeRole(OverlayToken(ovl).BURNER_ROLE(), _collateral);
    }

Tools Used

In function enableCollateral() add the following (after the require): collateralActive[_collateral] = true;

In function disableCollateral add the following (after the require): collateralActive[_collateral] = false;

Findings Information

🌟 Selected for report: gpersoon

Also found by: WatchPug, cmichel, defsec, harleythedog, hubble, xYrYuYx

Labels

bug
question
2 (Med Risk)
sponsor confirmed

Awards

0.0378 ETH - $174.45

External Links

Handle

gpersoon

Vulnerability details

Impact

The functions _transferMint() and _transferBurn() of OverlayToken.sol don't update _totalSupply. Whereas the similar functions _mint() and _burn() do update _totalSupply.

This means that _totalSupply and totalSupply() will not show a realistic view of the total OVL tokens.

For the protocol itself it isn't such a problem because this value isn't used in the protocol (as far as I can see). But other protocols building on Overlay may use it, as well as user interfaces and analytic platforms.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/ovl/OverlayToken.sol#L349-L364

function _mint( address account, uint256 amount) internal virtual {
   ...
      _totalSupply += amount;

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/ovl/OverlayToken.sol#L376-L395
```JS
function _burn(address account, uint256 amount) internal virtual {
   ...
        _totalSupply -= amount;

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/ovl/OverlayToken.sol#L194-L212

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/ovl/OverlayToken.sol#L268-L286

## Tools Used

## Recommended Mitigation Steps
Update _totalSupply  in _transferMint() and _transferBurn() 

#0 - commercium-sys

2021-12-07T21:49:07Z

We're not sure if this is a 1 or a 2. Definitely, at least a one - this is an incorrect implementation of the spec.

But is it a two? It wouldn't lose funds with our contracts, we make no use of the total supply of OVL in our accounting.

This might prove to be a vulnerability if another protocol, like Ribbon, used us for a vault of theirs, made use of total supply, and failed to discern this problem.

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

0.1658 ETH - $765.95

External Links

Handle

gpersoon

Vulnerability details

Impact

The contract OverlayV1Market is abstract and includes several other contracts that are also abstract. The only deviation is the contract OverlayV1OI which isn't abstract.

The risk is that OverlayV1OI could be accidentally deployed separately.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Market.sol#L13

abstract contract OverlayV1Market is OverlayV1Governance {

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Governance.sol#L11-L14

abstract contract OverlayV1Governance is OverlayV1Comptroller,  OverlayV1OI,  OverlayV1PricePoint {

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Comptroller.sol#L9

abstract contract OverlayV1Comptroller {

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1OI.sol#L6

contract OverlayV1OI {    // only contract that isn't abstract

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1PricePoint.sol#L7

abstract contract OverlayV1PricePoint {

Tools Used

Make contract OverlayV1OI also abstract

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

0.1658 ETH - $765.95

External Links

Handle

gpersoon

Vulnerability details

Impact

Suppose you try to build a position and have set the leverage accidentally to 0 (which can be done if you call the smart contract directly). Then the function build() will call enterOI() which will revert when trying to calculate debtAdjusted .

However no user friendly error message is given.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Market.sol

function enterOI ( bool _isLong,  uint _collateral,  uint _leverage ) external onlyCollateral returns (...) {
 ...
        collateralAdjusted_ = _collateral - _impact - fee_;       // will be > 0
        oiAdjusted_ = collateralAdjusted_ * _leverage;            // if _leverage==0 then oiAdjusted_  == 0
        debtAdjusted_ = oiAdjusted_ - collateralAdjusted_;   // will be negative and thus will revert

Tools Used

Add something like the following to the function build() require(_leverage != 0, "OVLV1:leverage==0")

Findings Information

🌟 Selected for report: gpersoon

Also found by: 0x0x0x

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

0.0072 ETH - $33.42

External Links

Handle

gpersoon

Vulnerability details

Impact

The function roll() of OverlayV1Comptroller.sol can be simplified. This saves some gas and also makes the function easier to read. See below at "Recommended Mitigation Steps"

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Comptroller.sol#L352-L385

function roll( Roller[60] storage rollers, Roller memory _roller, uint _lastMoment, uint _cycloid ) internal returns ( uint cycloid_) {
        if (_roller.time != _lastMoment) {
            _cycloid += 1;
            if (_cycloid < CHORD) {
                rollers[_cycloid] = _roller;
            } else {
                _cycloid = 0;
                rollers[_cycloid] = _roller;
            }
        } else {
            rollers[_cycloid] = _roller;
        }
        cycloid_ = _cycloid;
    }

Tools Used

Change the function to:

    function roll (Roller[60] storage rollers,Roller memory _roller,uint _lastMoment,uint _cycloid) internal returns (uint cycloid_) {
        if (_roller.time != _lastMoment) 
             _cycloid = (_cycloid + 1) % CHORD;                              
        rollers[_cycloid] = _roller;
        cycloid_ = _cycloid;
    }

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

0.0161 ETH - $74.28

External Links

Handle

gpersoon

Vulnerability details

Impact

The function oiCap() of OverlayV1Comptroller.sol save the value of brrrrdExpected in a tmp variable _brrrrdExpected. Lateron brrrrdExpected is still used while _brrrrdExpected could also be used. This saves a bit of gas.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/market/OverlayV1Comptroller.sol#L255-L279

 function oiCap() public virtual view returns (  uint cap_ ) {
    ...
        uint _brrrrdExpected = brrrrdExpected;
    ...
        cap_ = _surpassed ? 0 : _burnt || _expected
            ? _oiCap(false, depth(), staticCap, 0, 0)
            : _oiCap(true, depth(), staticCap, _brrrrd, brrrrdExpected);  // can also use _brrrrdExpected 

Tools Used

Replace

     : _oiCap(true, depth(), staticCap, _brrrrd, brrrrdExpected);

with

     : _oiCap(true, depth(), staticCap, _brrrrd, _brrrrdExpected);

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

0.0161 ETH - $74.28

External Links

Handle

gpersoon

Vulnerability details

Impact

The function value() of OverlayV1OVLCollateral.sol doesn't explicitly check for liquidated positions. However because oiShares and debt are set to 0 during liquidation the resulting value will still be 0.

It seems more logical to check for liquidation in the beginning of the function and immediately return 0. This saves gas for the situation where the function value() is called from another smart contract.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/collateral/OverlayV1OVLCollateral.sol#L424-L448

function value ( uint _positionId ) public view returns ( uint256 value_) { Position.Info storage pos = positions[_positionId]; IOverlayV1Market _market = IOverlayV1Market(pos.market); ( uint _oi, uint _oiShares, uint _priceFrame ) = _market.positionInfo( pos.isLong, pos.pricePoint ); value_ = pos.value( _oi, _oiShares, _priceFrame ); }

Tools Used

Add something like the following to function value():

        if (pos.oiShares == 0) return 0; // liquidated

Findings Information

🌟 Selected for report: gpersoon

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

0.0161 ETH - $74.28

External Links

Handle

gpersoon

Vulnerability details

Impact

In the function unwind() of OverlayV1OVLCollateral.sol a tmp variable _userOiShares is used the store the value of _shares. However _shares is still uses multiple times in the function.

Using _userOiShares everywhere would save gas.

Proof of Concept

https://github.com/code-423n4/2021-11-overlay/blob/914bed22f190ebe7088194453bab08c424c3f70c/contracts/collateral/OverlayV1OVLCollateral.sol#L273-L336

 function unwind (  uint256 _positionId,  uint256 _shares ) external {
        require( 0 < _shares && _shares <= balanceOf(msg.sender, _positionId), "OVLV1:!shares");    // uses _shares
        ...
        uint _userOiShares = _shares; // move to start of the function
        uint _userNotional = _shares * pos.notional(_oi, _oiShares, _priceFrame) / _totalPosShares;    // uses _shares
        uint _userDebt = _shares * pos.debt / _totalPosShares;                                                          // uses _shares
        uint _userCost = _shares * pos.cost / _totalPosShares;                                                           // uses _shares
        uint _userOi = _shares * pos.oi(_oi, _oiShares) / _totalPosShares;                                          // uses _shares
...  
      _burn(msg.sender, _positionId, _shares);   // uses _shares

Tools Used

Move "uint _userOiShares = _shares;" to the start of function unwind() Replace all other instances of "_shares" with "_userOiShares"

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter