Platform: Code4rena
Start Date: 25/08/2022
Pot Size: $75,000 USDC
Total HM: 35
Participants: 147
Period: 7 days
Judge: 0xean
Total Solo HM: 15
Id: 156
League: ETH
Rank: 33/147
Findings: 3
Award: $601.39
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: carlitox477
514.4616 DAI - $514.46
https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L265 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L278-L288
Given that the activeProposal change is done before the for loop, if this function is call through one kernel.executeAction(instruction,target) we can call the same instructions (in the same order) again and again, which may or may not affect funds (depending on the instructions).
For instance, if we install a new module, and this module has a vulnerability (even intentional), the next steps can by trigger:
Static Analysis
Use nonReentrant modifier or move the line activeProposal = ActivatedProposal(0, 0);
before the for loop.
#0 - fullyallocated
2022-09-04T03:10:26Z
I don't know if funds are going to be threatened, but this does allow for a re-entrancy. Warden is correct in resetting the active Proposal before the for loop based on the checks-effects-interactions code design pattern.
🌟 Selected for report: zzzitron
Also found by: 0x040, 0x1f8b, 0x52, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSky, 0xSmartContract, 0xkatana, 8olidity, Aymen0909, Bahurum, BipinSah, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chandr, Chom, CodingNameKiki, Deivitto, DimSon, Diraco, ElKu, EthLedger, Funen, GalloDaSballo, Guardian, IllIllI, JansenC, Jeiwan, Lambda, LeoS, Margaret, MasterCookie, PPrieditis, PaludoX0, Picodes, PwnPatrol, RaymondFam, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StevenL, The_GUILD, TomJ, Tomo, Trust, Waze, __141345__, ajtra, ak1, apostle0x01, aviggiano, bin2chen, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch13fd357r0y3r, cloudjunky, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, devtooligan, dipp, djxploit, durianSausage, eierina, enckrish, erictee, fatherOfBlocks, gogo, grGred, hansfriese, hyh, ignacio, indijanc, itsmeSTYJ, ladboy233, lukris02, martin, medikko, mics, natzuu, ne0n, nxrblsrpr, okkothejawa, oyc_109, p_crypt0, pfapostol, prasantgupta52, rajatbeladiya, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, sorrynotsorry, tnevler, tonisives, w0Lfrum, yixxas
54.3128 DAI - $54.31
It should change it name to _getModuleAddress
They should change their names to _OHM_ETH_PRICE_FEED, _RESERVE_ETH_PRICE_FEED, DECIMALS and _SCALE_FACTOR to respect naming convention.
They should change their names to OHM and RESERVE to respect naming convention.
They should change their names to instr and votes to respect convention.
🌟 Selected for report: pfapostol
Also found by: 0x040, 0x1f8b, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSmartContract, 0xkatana, Amithuddar, Aymen0909, Bnke0x0, CertoraInc, Chandr, CodingNameKiki, Deivitto, Dionysus, Diraco, ElKu, Fitraldys, Funen, GalloDaSballo, Guardian, IllIllI, JC, JansenC, Jeiwan, LeoS, Metatron, Noah3o6, RaymondFam, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, Saintcode_, Shishigami, Sm4rty, SooYa, StevenL, Tagir2003, The_GUILD, TomJ, Tomo, Waze, __141345__, ajtra, apostle0x01, aviggiano, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch0bu, chrisdior4, d3e4, delfin454000, djxploit, durianSausage, erictee, exolorkistis, fatherOfBlocks, gogo, grGred, hyh, ignacio, jag, karanctf, kris, ladboy233, lukris02, m_Rassska, martin, medikko, natzuu, ne0n, newfork01, oyc_109, peiw, rbserver, ret2basic, robee, rokinot, rvierdiiev, sikorico, simon135, tnevler, zishansami
32.6248 DAI - $32.62
Change i++
for ++i
. POC
Change i++
for ++i
. POC
It would be better to change this for this:
getPolicyIndex[policy_] = activePolicies.length; activePolicies.push(policy_);
It would be better to change this for this:
getDependentIndex[keycode][policy_] = moduleDependents[keycode].length; moduleDependents[keycode].push(policy_);
They can only be called by the admin, an external address. This will save gas. POC
This will save gas. It is not called by other function inside the contract POC
Copy it to a memory variable to lessen storage access.
function updateCapacity(bool high_, uint256 capacity_) external permissioned { Range __range=_range; if (high_) { // Update capacity __range.high.capacity = capacity_; // If the new capacity is below the threshold, deactivate the wall if they are currently active if (capacity_ < __range.high.threshold && __range.high.active) { // Set wall to inactive __range.high.active = false; __range.high.lastActive = uint48(block.timestamp); emit WallDown(true, block.timestamp, capacity_); } } else { // Update capacity __range.low.capacity = capacity_; // If the new capacity is below the threshold, deactivate the wall if they are currently active if (capacity_ < __range.low.threshold && __range.low.active) { // Set wall to inactive __range.low.active = false; __range.low.lastActive = uint48(block.timestamp); emit WallDown(false, block.timestamp, capacity_); } } _range=__range; }
Copy it to a memory variable to lessen storage access.
function updatePrices(uint256 movingAverage_) external permissioned { Range memory __range = range; // Cache the spreads uint256 wallSpread = __range.wall.spread; uint256 cushionSpread = __range.cushion.spread; // Calculate new wall and cushion values from moving average and spread __range.wall.low.price = (movingAverage_ * (FACTOR_SCALE - wallSpread)) / FACTOR_SCALE; __range.wall.high.price = (movingAverage_ * (FACTOR_SCALE + wallSpread)) / FACTOR_SCALE; __range.cushion.low.price = (movingAverage_ * (FACTOR_SCALE - cushionSpread)) / FACTOR_SCALE; __range.cushion.high.price = (movingAverage_ * (FACTOR_SCALE + cushionSpread)) / FACTOR_SCALE; emit PricesChanged( __range.wall.low.price, __range.cushion.low.price, __range.cushion.high.price, __range.wall.high.price ); _range = __range; }
This will save gas. It is not called by other function inside the contract POC
If amount_ == 0 is nonsense to continue the function execution. Adding require(amount_ != 0, 'amount_ can't be 0')
can save gas for a wrong execution.
Given that safeTransferFrom revert in case of an error, it seems nonsense the calculation of received. Meaning we can simplify our function to:
function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant { if (reserveDebt[token_][msg.sender] == 0) revert TRSRY_NoDebtOutstanding(); // Deposit from caller first (to handle nonstandard token transfers) token_.safeTransferFrom(msg.sender, address(this), amount_); // Subtract debt from caller reserveDebt[token_][msg.sender] -= amount_; totalDebt[token_] -= amount_; emit DebtRepaid(token_, msg.sender, amount_); }
We can grup the substraction and addition to avoid double memory access.
// undo any previous endorsement the user made on these instructions uint256 previousEndorsement = userEndorsementsForProposal[proposalId_][msg.sender]; // reapply user endorsements with most up-to-date votes userEndorsementsForProposal[proposalId_][msg.sender] = userVotes; totalEndorsementsForProposal[proposalId_] += (userVotes - previousEndorsement);
We can cache the state variable to avoid multiple storage access
We can cache the state variable to avoid multiple storage access
We can cache the state variable to avoid multiple storage access
function executeProposal() external { ActivatedProposal _activeProposal=activeProposal uint256 netVotes = yesVotesForProposal[_activeProposal.proposalId] - noVotesForProposal[_activeProposal.proposalId]; if (netVotes * 100 < VOTES.totalSupply() * EXECUTION_THRESHOLD) { revert NotEnoughVotesToExecute(); } if (block.timestamp < _activeProposal.activationTimestamp + EXECUTION_TIMELOCK) { revert ExecutionTimelockStillActive(); } Instruction[] memory instructions = INSTR.getInstructions(_activeProposal.proposalId); for (uint256 step; step < instructions.length; ) { kernel.executeAction(instructions[step].action, instructions[step].target); unchecked { ++step; } } emit ProposalExecuted(_activeProposal.proposalId); // deactivate the active proposal activeProposal = ActivatedProposal(0, 0); }