Platform: Code4rena
Start Date: 01/12/2022
Pot Size: $60,500 USDC
Total HM: 8
Participants: 27
Period: 11 days
Judge: kirk-baird
Total Solo HM: 6
Id: 187
League: ETH
Rank: 21/27
Findings: 1
Award: $59.84
π Selected for report: 0
π Solo Findings: 0
π Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0xDecorativePineapple, Chom, Deivitto, IllIllI, Jeiwan, Josiah, Mukund, RaymondFam, Rolezn, ajtra, cccz, chrisdior4, csanuragjain, hansfriese, ladboy233, minhquanym, pedr02b2, peritoflores, rvierdiiev, sakshamguruji, sces60107
59.8382 USDC - $59.84
https://github.com/code-423n4/2022-12-Stealth-Project/blob/main/maverick-v1/contracts/models/PoolInspector.sol#L40 https://github.com/code-423n4/2022-12-Stealth-Project/blob/main/maverick-v1/contracts/models/PoolInspector.sol#L90 https://github.com/code-423n4/2022-12-Stealth-Project/blob/main/router-v1/contracts/libraries/BytesLib.sol#L12
There is a known medium severity bug which affects memory writing in Solidity version 0.8.13. It would remove unused writes to memory and storage https://github.com/ethereum/solidity-blog/blob/499ab8abc19391be7b7b34f88953a067029a5b45/_posts/2022-06-15-inline-assembly-memory-side-effects-bug.md https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d
Since pragmas are floating in the contracts. the contracts may suffer from the issue. For example, the following contract has the issue. It only uses mstore
in the inline assembly block. So it will be removed in Solidity 0.8.13.
function _getBinsAtTick(IPool pool, int32 tick) internal view returns (IPool.BinState[] memory bins) { uint8 binCounter = NUMBER_OF_KINDS; bins = new IPool.BinState[](binCounter); for (uint8 i = 0; i < NUMBER_OF_KINDS; i++) { uint128 binId = pool.binPositions(tick, i); if (binId != 0) { IPool.BinState memory bin = pool.getBin(binId); bins[NUMBER_OF_KINDS - binCounter] = bin; binCounter--; } } if (binCounter != 0) { assembly { mstore(bins, sub(mload(bins), binCounter)) } } }
All the contracts use floating pragmas.
pragma solidity ^0.8.0;
And these contracts have the inline assembly blocks which have memory write that is never read again.
function getActiveBins(IPool pool, uint128 startBinIndex, uint128 endBinIndex) external view returns (BinInfo[] memory bins) { uint128 binCounter = pool.getState().binCounter; if (endBinIndex != 0) { binCounter = binCounter < endBinIndex ? binCounter : endBinIndex; } bins = new BinInfo[](binCounter); uint128 activeCounter = 0; for (uint128 i = startBinIndex; i < binCounter; i++) { IPool.BinState memory bin = pool.getBin(i + 1); if (pool.binPositions(bin.lowerTick, bin.kind) == i + 1 || bin.mergeId != 0) { bins[activeCounter] = BinInfo({id: i + 1, kind: bin.kind, lowerTick: bin.lowerTick, reserveA: bin.reserveA, reserveB: bin.reserveB, mergeId: bin.mergeId}); activeCounter++; } } if (activeCounter != 0) { uint128 binCountToRemove = binCounter - activeCounter; assembly { mstore(bins, sub(mload(bins), binCountToRemove)) } } }
function _getBinsAtTick(IPool pool, int32 tick) internal view returns (IPool.BinState[] memory bins) { uint8 binCounter = NUMBER_OF_KINDS; bins = new IPool.BinState[](binCounter); for (uint8 i = 0; i < NUMBER_OF_KINDS; i++) { uint128 binId = pool.binPositions(tick, i); if (binId != 0) { IPool.BinState memory bin = pool.getBin(binId); bins[NUMBER_OF_KINDS - binCounter] = bin; binCounter--; } } if (binCounter != 0) { assembly { mstore(bins, sub(mload(bins), binCounter)) } } }
None
use >= 0.8.14
instead of ^0.8.0
#0 - gte620v
2022-12-16T03:07:49Z
#1 - c4-sponsor
2022-12-16T03:07:53Z
gte620v marked the issue as disagree with severity
#2 - kirk-baird
2023-01-12T22:22:54Z
I agree with the sponsor this is a QA issue which is also stated in #5 as no PoC has been shown how this may cause loss of funds for users or otherwise impact the protocol.
#3 - c4-judge
2023-01-12T22:23:12Z
kirk-baird changed the severity to QA (Quality Assurance)
#4 - c4-judge
2023-01-18T22:18:15Z
kirk-baird marked the issue as grade-b