RSR Unstake Denial of Service Due to Underflow Error #207
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
🤖_primary
AI based primary recommendation
sufficient quality report
This report is of sufficient quality
Lines of code
https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/p1/StRSR.sol#L648-L649
https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/p1/StRSR.sol#L324-L332
https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/p1/StRSR.sol#L463
Vulnerability details
Impact
Unstaking in the RSR contract would fail under certain circumstances when withdraw(...) and seizeRSR(...) functions have been previously called causing Denial of Service to innocent users who would like to unstake Due to Underflow Error in unstake(...) function.
Proof of Concept
The code above shows how pushDraft(...) is implemented before it is called during unstaking at L279 of the StRSR contract. it can be noted from the two pointers above that newTotalDrafts is derived from the multiplicative operation of draftRate & draftRSR, the protocol assumes newTotalDrafts would definitely be greater than previous totalDrafts as the draftRSR used to perform this multiplication is increased with incoming rsrAmount. This assumption is the reason behind the subtraction operation noted in the second pointer which assumes newTotalDrafts would definitely be greater than previous totalDrafts, this assumption is wrong as there is a flaw which can make draftRate lower than normal thereby making the increase of draftRSR irrelevant to the overall calculation of newTotalDrafts.
The next series of text in this report would try to break down why draftRate can be lower than usual.
The code above shows how there is reduction in Total draft and draftRSR when withdrawal is called but draftRSR is not completely reduced but slightly inflated by 1 i.e ...(draftRate - 1)) / draftRate; as noted in the pointer, it pushes up the value for the purpose of precision.
The implication of this slight increase of draftRSR would mean a decrease in the value of draftRate (i.e dividing by increased denominator of draftRSR), the protocol wrongly assumes the addition of (draftRSR - 1) to the numerator in the code above would coverup for the inflation in the denominator(i.e draftRSR ) but this would no longer be the case due to the multiplicative effect of the formula.
scenario
if
totalDrafts = 101
from the withdraw(...) function assuming draftRSR was suppose to be 4 for example it would be inflated to 5 so
draftRSR = 5
Therefore based on the seizeRSR(...) function
draftRate = (101+(5-1))/5 = 21
however (5-1) would not be enough to cover for the math discrepancies as 101/4 is suppose to be 25 which is completely far from 21 that the protocol formular would derive.
The effect of this would be felt in the pushDraft(...) function where protocol wrongly assumes that newTotalDrafts can never be lower than the previous totalDrafts thereby attempting to carry out a substration operation under this false assumption without any validations i.e
when in reality the increase of draftRSR in the formular would not always be enough to cover for the excessive decrease of draftRate which would make newTotalDrafts lower than old totalDrafts causing Dos when a user want to unstake at L279 of the contract
POC code
The test code should be added to the Withdrawals/Unstaking section of the ZZStRSR.test.ts test file.
The test would fail causing Dos when the second address wants to unstake.
Tools Used
Manual Review
Recommended Mitigation Steps
The solution to this bug is a little tricky as the addition of precision to draftRSR has it roles, so protocol could consider updating its documentation to cover this bug or make adjustment as provided in the pushDraft function below which puts the possibility of of lower new total drafts into consideration.
Assessed type
DoS
The text was updated successfully, but these errors were encountered: