You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Oct 1, 2023. It is now read-only.
sherlock-admin opened this issue
Mar 27, 2023
· 0 comments
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelHighA valid High severity issueRewardA payout will be made for this issue
After a user's rollover is complete, they are able to delist. This is problematic because rolloverQueue.length will decrease when users call delistInRollover(). Eventually, rolloverQueue.length will be small enough that the calculation will underflow.
Consider the case when there are 3 items in rolloverQueue:
User calls mintRollovers(_epochId, 2), and tries to mint 2 rollovers.
The following local variables will be used
length = rolloverQueue.length = 3
index = rolloverAccounting[_epochId] = 0
At the end, this storage will be set
rolloverAccounting[_epochId] = index = 2 (due to index++)
Two of the rollover users delist. The rolloverQueue.length will now be 1
User attempts to mint the last rollover. The following local variables will be used
length = rolloverQueue.length = 1
index = rolloverAccounting[_epochId] = 2
With the lowest _operation value as possible, 1, the if condition will execute since index +_operations > length. _operations will be calculated as length - index = 1 - 2, which will lead to underflow.
Impact
This is a medium impact. If users are partially rolled over, and there are delists, the other users will not be rolled over on a timely basis i.e., epoch may start.
Code Snippet
Here is a unit test. Add this to CarouselTest.t.sol, and run forge test --match-test testMintAndThenDelistRollover
function testMintAndThenDelistRollover() public {
// roll over users from testDepositIntoQueueMultiple testtestDepositIntoQueueMultiple();
// create new epochuint40 _epochBegin =uint40(block.timestamp+3 days);
uint40 _epochEnd =uint40(block.timestamp+4 days);
uint256 _epochId =3;
uint256 _emissions =100ether;
deal(emissionsToken, address(vault), 100 ether, true);
vault.setEpoch(_epochBegin, _epochEnd, _epochId);
vault.setEmissions( _epochId, _emissions);
uint256 prevEpochUserBalance =10ether- relayerFee;
uint256 prevEpoch =2;
// enlist in rollover for next epochhelperRolloverFromEpoch(prevEpoch, USER, prevEpochUserBalance);
helperRolloverFromEpoch(prevEpoch, USER2, prevEpochUserBalance);
helperRolloverFromEpoch(prevEpoch, USER3, prevEpochUserBalance);
// resolve prev epoch
vm.warp(block.timestamp+2days+1 hours); // warp to one hour after prev epoch end
vm.startPrank(controller);
vault.resolveEpoch(prevEpoch);
vm.stopPrank();
// simulate prev epoch win
stdstore
.target(address(vault))
.sig("claimTVL(uint256)")
.with_key(prevEpoch)
.checked_write(1000 ether);
// mint rollovers, this will change the index to 1
vault.mintRollovers(_epochId, 2);
// this will change the index to 1assertEq(vault.rolloverAccounting(_epochId), 2);
// Delist USER1 and USER2
vm.prank(USER);
vault.delistInRollover(USER);
vm.prank(USER2);
vault.delistInRollover(USER2);
// rolloverQueue.length will be 1// Should underflow when trying to rollover USER3
vm.expectRevert();
vault.mintRollovers(_epochId, 1);
}
Tool used
Manual Review
Recommendation
Consider some way of tracking if a user has rolled over already, possibly by deleting the ownerToRollOverQueueIndex[_owner] after it has been successfully rolled over.
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelHighA valid High severity issueRewardA payout will be made for this issue
ltyu
medium
Rollover delisting causes underflow
Summary
When a rollover is delisted, it can cause an underflow, and therefore block rollover minting.
Vulnerability Detail
Currently,
mintRollovers()
relies therolloverQueue.length
in calculating the max number of_operations
:https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L371-L372
https://github.com/sherlock-audit/2023-03-Y2K/blob/main/Earthquake/src/v2/Carousel/Carousel.sol#L377-L378
After a user's rollover is complete, they are able to delist. This is problematic because
rolloverQueue.length
will decrease when users calldelistInRollover()
. Eventually,rolloverQueue.length
will be small enough that the calculation will underflow.Consider the case when there are 3 items in
rolloverQueue
:mintRollovers(_epochId, 2)
, and tries to mint2
rollovers.length
=rolloverQueue.length
= 3index
=rolloverAccounting[_epochId]
= 0rolloverAccounting[_epochId]
=index
= 2 (due toindex++
)rolloverQueue.length
will now be 1length
=rolloverQueue.length
= 1index
=rolloverAccounting[_epochId]
= 2_operation
value as possible,1
, theif
condition will execute sinceindex +_operations > length
._operations
will be calculated aslength
-index
= 1 - 2, which will lead to underflow.Impact
This is a medium impact. If users are partially rolled over, and there are delists, the other users will not be rolled over on a timely basis i.e., epoch may start.
Code Snippet
Here is a unit test. Add this to CarouselTest.t.sol, and run
forge test --match-test testMintAndThenDelistRollover
Tool used
Manual Review
Recommendation
Consider some way of tracking if a user has rolled over already, possibly by deleting the
ownerToRollOverQueueIndex[_owner]
after it has been successfully rolled over.Duplicate of #72
The text was updated successfully, but these errors were encountered: