Platform: Code4rena
Start Date: 16/02/2023
Pot Size: $144,750 USDC
Total HM: 17
Participants: 154
Period: 19 days
Judge: Trust
Total Solo HM: 5
Id: 216
League: ETH
Rank: 21/154
Findings: 3
Award: $941.07
π Selected for report: 0
π Solo Findings: 0
π Selected for report: peakbolt
Also found by: 0xTheC0der, 0xbepresent, 0xsomeone, codeislight, trustindistrust
443.5294 USDC - $443.53
https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/ReaperVaultV2.sol#L192 https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L157
The Ethos Vault ecosystem contains an access control inconsistency whereby a lesser role can have a significant impact by bypassing the role requirement via a different contract.
The ReaperBaseStrategyv4::setEmergencyExit
function is meant to set a strategy to "emergency" mode by setting its allocation to 0 and explicitly setting its emergency flag to true
. This function applies the GUARDIAN
role AC requirement which is higher than the STRATEGIST
role as defined in ReaperBaseStrategyv4::_cascadingAccessRoles
.
The ReaperVaultV2::updateStrategyAllocBPS
function is meant to set a strategy's allocation, enabling it to be set to 0. This function applies the STRATEGIST
role AC requirement which is lower than the GUARDIAN
role as defined in ReaperVaultV2::_cascadingAccessRoles
. This effectively permits a STRATEGIST
to bypass the GUARDIAN
requirement and enforce an effectual emergency mode on a strategy by setting its allocation to 0
.
The following code illustrates the issue in action:
contract AccessControlFlaw { bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); function demonstrate(IStrategy strategy, IVault vault) external { // Pre-Requisites require(vault.hasRole(address(this), STRATEGIST); require(strategy.hasRole(address(this), STRATEGIST); try strategy.setEmergencyExit() { revert(); } catch (bytes memory e) { // The `STRATEGIST` role is unable to invoke `ReaperBaseStrategyv4::setEmergencyExit` as expected } try vault.updateStrategyAllocBPS(address(strategy), 0) { revert("ISSUE: The `STRATEGIST` role was able to set the allocation of a strategy to 0, thereby simulating its emergency exit scenario"); } catch (bytes memory e) { } } }
Manual review of the codebase.
The adjustment required for the codebase depends on the business requirements of the Reaper ecosystem.
In our opinion, the current access control structure is meant to be retained in the code and the ReaperVaultV2::updateStrategyAllocBPS
function should enforce a minimum BPS that each strategy must possess.
This will prevent a strategy from being removed unless a higher access role is possessed, similar to how strategy additions are guarded by the highest role possible.
#0 - c4-judge
2023-03-08T16:36:58Z
trust1995 marked the issue as satisfactory
#1 - c4-judge
2023-03-08T16:37:04Z
trust1995 marked the issue as primary issue
#2 - tess3rac7
2023-03-14T16:39:26Z
the current access control structure is meant to be retained in the code and the ReaperVaultV2::updateStrategyAllocBPS function should enforce a minimum BPS that each strategy must possess.
This will prevent a strategy from being removed unless a higher access role is possessed, similar to how strategy additions are guarded by the highest role possible.
I like this idea, but recommending downgrading to low.
#3 - c4-sponsor
2023-03-14T16:39:33Z
tess3rac7 marked the issue as disagree with severity
#4 - c4-judge
2023-03-20T11:52:27Z
trust1995 marked the issue as duplicate of #332
π Selected for report: GalloDaSballo
Also found by: 0x3b, 0xAgro, 0xSmartContract, 0xTheC0der, 0xackermann, 0xnev, 0xsomeone, ABA, BRONZEDISC, Bjorn_bug, Bnke0x0, Breeje, Co0nan, CodeFoxInc, CodingNameKiki, DadeKuma, DeFiHackLabs, IceBear, Josiah, Kaysoft, Lavishq, MohammedRizwan, PaludoX0, PawelK, Phantasmagoria, Raiders, RaymondFam, Rickard, Rolezn, Sathish9098, SleepingBugs, SuperRayss, UdarTeam, Udsen, Viktor_Cortess, arialblack14, ast3ros, bin2chen, brgltd, btk, catellatech, ch0bu, chaduke, chrisdior4, codeislight, cryptonue, delfin454000, descharre, dontonka, emmac002, fs0c, hacker-dom, hansfriese, imare, lukris02, luxartvinsec, martin, matrix_0wl, peanuts, rbserver, shark, tnevler, trustindistrust, tsvetanovv, vagrant, yongskiws, zzzitron
455.4712 USDC - $455.47
This submission serves as a Q&A report of the Ethos Reserve codebase. The findings that were identified with a low / non-critical security impact are as follows:
CommunityIssuance.sol
An abnormally large distributionPeroid
coupled with a low amount of funds in a CommunityIssuance::fund
call will result in no rewards being distributed per second, causing fund loss that at most equates to distributionPeriod
in the token's denomination.
We advise any unaccounted-for reward to not be transferred from the msg.sender
to the contract by calculating the OathToken
to transfer as rewardPerSecond.mul(distributionPeriod)
.
The distribution period of the contract is not sanitized during a CommunityIssuance::updateDistributionPeriod
call.
We advise a non-zero validation to be performed as otherwise the contract's CommunityIssuance::fund
function will be inoperable until the misconfiguration is corrected.
CollateralConfig.sol
The CCR
configured for a particular market should at all times be greater than the MCR
as otherwise, the market will never achieve its critical-state, preventing the system's failsafe mechanisms from kicking in.
We advise the _CCRs[i]
value to be mandated as greater-than-or-equal-to the _MCRs[i]
value of a particular collateral. Depending on how this finding is viewed, it can be considered medium-risk as a misconfigured asset will never trigger the failsafe mechanisms of the protocol and thus never recover from a critical state.
ReaperBaseStrategyv4.sol
The referenced line invokes SafeERC20::safeApprove
which can cause upgrades to fail as the ReaperBaseStrategyv4
contract represents an upgradeable contract.
If the contract's logic is adjusted and attempted to be upgraded, a second SafeERC20::safeApprove
invocation that will be performed during the contract's re-initialization will fail due to the custom and deprecated logic present in the function.
We advise a direct approve
instruction to be utilized instead, potentially by evaluating the yielded bool
opportunistically similarly to how SafeERC20
achieves this.
The referenced negation operations (-a
) are performed unsafely as they do not apply any bound checks to the a
value.
As all linked negations are meant to convert a value from a negative to a positive value, an edge case manifests whereby the minimum int256
(type(int256).min
) will overflow when negated by one unit as the negative value ranges have one more member in signed integer types.
We advise the code to use a wrapper library that negates the value and applies a bound check to ensure that the negation will never result in an underflow.
ReaperStrategyGranarySupplyOnly.sol
The referenced casting operations are performed unsafely, potentially resulting in an uncaught casting overflow to occur.
We advise safe wrapper libraries to be utilized, such as OpenZeppelin's SafeCast
library. To note, Solidity's built-in safe arithmetic post-0.8 does not cover casting overflows.
The referenced negation operations (-a
) are performed unsafely as they do not apply any bound checks to the a
value.
As all linked negations are meant to convert a value from a negative to a positive value, an edge case manifests whereby the minimum int256
(type(int256).min
) will overflow when negated by one unit as the negative value ranges have one more member in signed integer types.
We advise the code to use a wrapper library that negates the value and applies a bound check to ensure that the negation will never result in an underflow.
ReaperVaultV2.sol
The referenced lines of code utilize the _multisigRoles
members without validating the array's length.
We advise sanitization to be imposed similarly to ReaperBaseStrategyv4::__ReaperBaseStrategy_init
to prevent improper role initializations.
The referenced casting operations are performed unsafely, potentially resulting in an uncaught casting overflow to occur.
We advise safe wrapper libraries to be utilized, such as OpenZeppelin's SafeCast
library. To note, Solidity's built-in safe arithmetic post-0.8 does not cover casting overflows.
The ReaperVaultV2::setWithdrawalQueue
function does not evaluate whether duplicate entries exist in the queue.
While the finding will have no impact as it will only result in a redundant increase in gas within ReaperVaultV2::_withdraw
, we still advise duplicate entries to be prohibited.
The active withdrawal queue of the system should at all times represent the strategies from which funds can be withdrawn in the system.
We advise the length
of the queue to be mandated as exactly equal to the number of active strategies, ensuring that an incorrect withdrawal queue cannot be specified.
The referenced negation operations (-a
) are performed unsafely as they do not apply any bound checks to the a
value.
As all linked negations are meant to convert a value from a negative to a positive value, an edge case manifests whereby the minimum int256
(type(int256).min
) will overflow when negated by one unit as the negative value ranges have one more member in signed integer types.
We advise the code to use a wrapper library that negates the value and applies a bound check to ensure that the negation will never result in an underflow.
ReaperVaultERC4626.sol
The ReaperVaultERC4626::withdraw
function meant to conform to the EIP standard accepts the input uint256
argument denominated in assets
, however, the ReaperVaultV2::withdraw
functions accept the input uint256
argument denominated in shares
.
We advise the codebase to become consistent, utilizing a different naming convention for ReaperVaultV2
(i.e. deposit
).
ActivePool.sol
The referenced casting operations are performed unsafely, potentially resulting in an uncaught casting overflow to occur.
We advise safe wrapper libraries to be utilized, such as OpenZeppelin's SafeCast
library. To note, Solidity's built-in safe arithmetic post-0.8 does not cover casting overflows.
The referenced negation operations (-a
) are performed unsafely as they do not apply any bound checks to the a
value.
As all linked negations are meant to convert a value from a negative to a positive value, an edge case manifests whereby the minimum int256
(type(int256).min
) will overflow when negated by one unit as the negative value ranges have one more member in signed integer types.
We advise the code to use a wrapper library that negates the value and applies a bound check to ensure that the negation will never result in an underflow.
The referenced error message of the require
check within ActivePool::_requireCallerIsBOorTroveMorSP
contains a misleading error message which does not list the redemptionHelper
.
We advise this message to be corrected, representing the actual condition evaluated by the require
check.
BorrowerOperations.sol
The BorrowerOperations::_adjustTrove
function will relay an incorrect "borrower" to its internal _moveTokensAndCollateralfromAdjustment
call when BorrowerOperations::moveCollateralGainToTrove
is invoked by the stability pool.
While it is inconsequential in the current code (no LUSD is withdrawn / repaid and the transaction represents a _isCollIncrease
), it is still advisable to relay the correct argument via a ternary (a ? b : c
) conditional.
StabilityPool.sol
The referenced calculations meant to assess the LUSD loss incurred by the deposit are unconditionally executed, potentially causing an underflow if the position somehow increases.
We evaluated the codebase and the linked calculation can never underflow in the current implementation, however, the code should evaluate whether any loss exists prior to calculating it via a conditional, prohibiting an underflow from halting deposits / withdrawals.
LUSDToken.sol
The ecrecover
mechanism within LUSDToken::permit
allows an invalid signature to result in the zero address incorrectly.
While an approval will still be prevented by how the LUSDToken::_approve
function disallows the from
address from being zero, we still advise invalid signatures to emit a proper error message rather than failing with an "approval from zero" error message.
The LUSDToken::returnFromPool
as well as the LUSDToken::mint
functions represent instances whereby a non-zero LUSD balance can be transmitted to a target recipient without the LUSDToken::_requireValidRecipient
check.
We advise both instances to properly apply the check, preventing LUSD tokens from being minted to incorrect addresses. The present use of the codebase disallows this as no code path will cause the check to fail, however, the check should be present regardless.
#0 - c4-judge
2023-03-08T13:20:08Z
trust1995 marked the issue as grade-a
#1 - c4-sponsor
2023-03-17T02:34:26Z
0xBebis marked the issue as sponsor confirmed
π Selected for report: c3phas
Also found by: 0x3b, 0x6980, 0x73696d616f, 0xSmartContract, 0xackermann, 0xhacksmithh, 0xsomeone, Bnke0x0, Bough, Budaghyan, Darshan, DeFiHackLabs, Deivitto, GalloDaSballo, JCN, LethL, Madalad, MiniGlome, Morraez, P-384, PaludoX0, Phantasmagoria, Praise, RHaO-sec, Rageur, RaymondFam, ReyAdmirado, Rickard, Rolezn, SaeedAlipoor01988, Saintcode_, Sathish9098, TheSavageTeddy, Tomio, Viktor_Cortess, abiih, arialblack14, atharvasama, banky, codeislight, cryptonue, ddimitrov22, dec3ntraliz3d, descharre, dharma09, emmac002, favelanky, hl_, hunter_w3b, kaden, kodyvim, matrix_0wl, oyc_109, pavankv, scokaf, seeu, yamapyblack
42.0697 USDC - $42.07
This submission serves as a gas optimization report of the Ethos Reserve codebase. The ways the codebase can be optimized are as follows:
CommunityIssuance.sol
The referenced statements read the same value from the contract's storage multiple times in the same function context.
We advise them to instead cache the variable's value to a local in-memory variable instead, reducing the total warm SLOAD
operations performed in the codebase.
SafeMath
(Affected Lines: L88, L107)The referenced operations are guaranteed to be performed safely by the logical constraints (i.e. if
clauses) that precede their execution.
We advise the usage of SafeMath
in these instances to be omitted, optimizing their gas cost.
The referenced TotalOATHIssuedUpdated
event is emitted regardless of whether the totalOATHIssued
was actually adjusted.
We advise it to be relocated within the if
clause, removing the potentially redundant SLOAD
operation and ensuring the event is emitted optimally.
LQTYStaking.sol
The referenced statements read the same value from the contract's storage multiple times in the same function context.
We advise them to instead cache the variable's value to a local in-memory variable instead, reducing the total warm SLOAD
operations performed in the codebase.
SafeMath
(Affected Lines: L155, L181)The referenced operations are guaranteed to be performed safely by the logical constraints (i.e. if
clauses) that precede their execution.
We advise the usage of SafeMath
in these instances to be omitted, optimizing their gas cost.
The referenced if
conditionals will cause their function to result in a no-op if they are not satisfied.
As such, we advise an else
clause to be introduced to them that immediately stops the function's execution (i.e. return
), optimizing their gas cost significantly.
The referenced mapping
variable usages do so using direct accessors (i.e. foo[bar][zoo]
) instead of storing the mapping
lookups to interim memory
/ storage
variables.
We advise the lookups to be cached to local variables as described above, significantly optimizing their usage cost as each mapping
lookup performs a keccak256
operation under-the-hood to assess the storage offset of the entry each time.
ReaperBaseStrategyv4.sol
The referenced lines perform unchecked increments of loop iterators, however, this is achieved via library calls which are delegatecall
instructions at the EVM level.
We advise them to be performed directly, avoiding the unnecessary gas overhead of delegatecall
instructions the current paradigm incurs.
The referenced statements perform Solidity's built-in safe arithmetics redundantly as their safety is guaranteed by the logical constraints (i.e. if
clauses) that precede their execution.
We advise the usage of unchecked
code blocks in these instances, optimizing their gas cost.
Based on the calculations in ReaperBaseStrategyv4::harvest
, the value of repayment
will be equal to amountFreed
if the roi
is negative. As such, we advise the assignment to be made directly to amountFreed
minimizing the calculations within the code.
More optimizations were identified, however, they were not able to be transcribed into legible text in time for the contest's duration. I am more than happy to provide them manually for the sponsor to review and consume within 24 hours of the contest's end.
#0 - c4-judge
2023-03-08T12:55:02Z
trust1995 marked the issue as grade-b