Closed sherlock-admin2 closed 4 months ago
Request PoC to faciilitate discussion
Likely Invalid, sponsor comments:
- invalid, the binary search is only used if there is no observation available at the draw ID in question
- see these two checks:
- observation at startDrawId not available: https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-prize-pool/src/libraries/DrawAccumulatorLib.sol#L182
- observation at endDrawId not available: https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-prize-pool/src/libraries/DrawAccumulatorLib.sol#L200
PoC requested from @MiloTruck
Requests remaining: 7
The binary search is only used if an observation for the draw in question is not available in the mapping (see the links in the comment above).
MiloTruck
high
Inconsistent result from
DrawAccumulator.binarySearch()
causes prize pool calculations to be incorrectSummary
The result returned from
DrawAccumulator.binarySearch()
is not consistent, causingDrawAccumulator.getDisbursedBetween()
to wrongly exclude the start and/or end draw ID under certain conditions. This causes all prize calculation in the prize pool to be incorrect.Vulnerability Detail
In
DrawAccumulator.binarySearch()
, the terminating condition (ie. the condition at which the binary search stops) is as shown:DrawAccumulatorLib.sol#L257-L262
As seen from above, the terminating condition is
beforeOrAtDrawId <= targetId <= afterOrAtDrawId
. However, whentargetId
is in the draw ring buffer, the result returned bybinarySearch()
may be inconsistent. For example:[1, 2, 3, ...]
.targetId = 2
.binarySearch()
could return either:beforeOrAtDrawId = 1
andafterOrAtDrawId = 2
asbeforeOrAtDrawId < targetId = afterOrAtDrawId
.beforeOrAtDrawId = 2
andafterOrAtDrawId = 3
asbeforeOrAtDrawId = targetId < afterOrAtDrawId
.The result returned depends on the length of the draw ring buffer, more specifically, the difference between
_oldestIndex
and_newestIndex
. This is demonstrated in the POC below.binarySearch()
is used inDrawAccumulator.getDisbursedBetween()
to find the total balance between a start and end draw, inclusive of both draws:DrawAccumulatorLib.sol#L183-L190
Since
afterOrAtDrawId
is taken as the start draw without checking ifbeforeOrAtDrawId
is equal to_startDrawId
, it is possible forafterOrAtDrawId
to be greater than_startDrawId
when_startDrawId
is present indrawRingBuffer
. For example:_startDrawId = 2
.binarySearch()
returnsbeforeOrAtDrawId = 2
andafterOrAtDrawId = 3
.afterOrAtDrawId = 3
as the starting draw.getDisbursedBetween()
is inclusive of_startDrawId
,beforeOrAtDrawId
should be taken as the starting draw instead.Similarly, the ending draw returned could wrongly exclude
_endDrawId
even when it is present indrawRingBuffer
, sincebeforeOrAtDrawId
is always used:DrawAccumulatorLib.sol#L201-L208
As a result,
getDisbursedBetween()
could return a balance far smaller than the actual disbursed balance between_startDrawId
and_endDrawId
.The following POC demonstrates how the results returned by
binarySearch()
are inconsistent:Impact
DrawAccumulatorLib.getDisbursedBetween()
could return a balance much smaller than the actual balance between two draws. It is used in many crucial calculations of the prize pool:PrizePool.awardDraw()
to find the total amount of new prize liquidity to distribute.PrizePool._getVaultShares()
to find the amount of liquidity contributed by a vault and the total amount of liquidity between two draws.Due to the bug in
getDisbursedBetween()
, all calculations in the prize pool involving the allocation/distribution of prize liquidity will be incorrect, causing a loss of funds as users will receive incorrect prize amounts.Code Snippet
https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-prize-pool/src/libraries/DrawAccumulatorLib.sol#L257-L262
https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-prize-pool/src/libraries/DrawAccumulatorLib.sol#L183-L190
https://github.com/sherlock-audit/2024-05-pooltogether/blob/1aa1b8c028b659585e4c7a6b9b652fb075f86db3/pt-v5-prize-pool/src/libraries/DrawAccumulatorLib.sol#L201-L208
Tool used
Manual Review
Recommendation
When finding the start draw in
binarySearch()
, consider checking ifbeforeOrAtDrawId
equals to_startDrawId
before usingafterOrAtDrawId
:Similarly, when finding the end draw, check if
afterOrAtDrawId
equals to_endDrawId
before usingbeforeOrAtDrawId
: