Throttle::useAvailable
should not update throttle.lastTimestamp
if limit * delta < ONE_HOUR
#22
Labels
bug
Something isn't working
disagree with severity
Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments)
downgraded by judge
Judge downgraded the risk level of this issue
grade-b
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
rainout
Used to specify findings that came in during the rained-out audit
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/reserve-protocol/protocol/blob/c4ec2473bbcb4831d62af55d275368e73e16b984/contracts/libraries/Throttle.sol#L62
https://github.com/reserve-protocol/protocol/blob/c4ec2473bbcb4831d62af55d275368e73e16b984/contracts/libraries/Throttle.sol#L73
Vulnerability details
Description
Function
Throttle::currentlyAvailable
calculates the current amount available for consumption. Even though the output is right, it has a problem when it is used insideThrottle::useAvailable
Considering
available = throttle.lastAvailable + (limit * delta) / ONE_HOUR;
, we can see there is a possible rounding error iflimit * delta < ONE_HOUR
, thenavailable = throttle.lastAvailable
. This rounding error is inevitable, but we must notice thatdelta = uint48(block.timestamp) - throttle.lastTimestamp;
.If then we consider that, inside
Throttle::useAvailable
the last line isthrottle.lastTimestamp = uint48(block.timestamp);
, we can force this rounding, DOSing the Throttle until its amount available become 0.Impact
DOS of
Throttle.lastAvailable
update iflimit * delta < ONE_HOUR
. We can DOS this update forever if we are allowed to sentamount = 0
POC
Let's suppose we have a Throttle with next values in Ethereum network:
And that supply is
280_000 * 1e18
If this was the case, the
limit = (supply * params.pctRate) / FIX_ONE_256 = 280_000 * 1e18 * 1000 / 1e18 = 280
Let's suppose that 1 block has passed, then
block.timestamp = 12
. If we callcurrentlyAvailable(throttle, limit)
:delta = block.timestamp - throttle.lastTimestamp = 12 - 0 = 12
available = throttle.lastAvailable + (limit * delta) / ONE_HOUR = 1000 + (280 * 12) / 3600 = 1000 + 3360 / 3600 = 1000
.Considering
amount = 1
, the amount available would be updated butthrottle.lastTimestamp = uint48(block.timestamp) = 12
not.If in next block we try to do the same call, the only change of state would be
lastTimestamp = uint48(block.timestamp) = 24
. Meaning we can DOSlastTimestamp
for 250 blocks. Which is equal to 50 minutes.Mitigation steps
Modify
Throttle::currentlyAvailable
andThrottle::useAvailable
in order to avoid this error. In particular I would suggest next refactor:Assessed type
Other
The text was updated successfully, but these errors were encountered: