The BriVault::_getWinnerShares() function loops through the entire usersAddress array to compute the total shares of users who selected the winning country:
This loop is unbounded, meaning its cost grows linearly with the number of participants.
The problem is that _getWinnerShares() is called inside setWinner(), which must succeed before withdrawals are allowed. If the number of participants becomes large (thousands+), this loop will exceed the block gas limit, causing setWinner() to always revert.
Since the vault relies on _setWinner == true before users can withdraw, this design allows a single attacker to force a permanent DoS simply by injecting a large number of entries into the vault.
Likelihood:
Reason 1: This issue occurs once the participants list grows large enough that iterating over usersAddress exceeds the block gas limit during setWinner(), making the function revert consistently.
Reason 2: This situation arises whenever an attacker (or organic user growth) submits a high number of deposits and joins the event repeatedly, since there is no cap or restriction on how many entries can be added.
Impact: Admin cannot call setWinner() once the participants list grows large → function becomes uncallable due to gas limit.
_setWinner never becomes true.
withdraw() requires winnerSet modifier:
This directly leads to a High-severity Denial-of-Service where the contract cannot progress to the withdrawal phase, completely breaking core protocol functionality.
| Number of participants | Gas used by setWinner() |
|---|---|
| ~1000 users | ≈ 0.9 – 1.1 million gas |
| ~2000 users | ≈ 1.8 – 2.0 million gas |
| ~3000 users | ≈ 2.7 – 3.0 million gas |
| ~4000 users | ≈ 3.6 – 4.0 million gas |
There are a few recomendations.
Impose participation limits / block Sybil spam, But this alone won’t fully solve scalability issues.
Use a mapping countryId → totalShares
Use pull-based withdrawal math
Every user computes their own proportional reward without global aggregation.
The _getWinnerShares() function is intended to iterate through all users and sum their shares for the winning country, returning the total.
The _getWinnerShares() function is intended to iterate through all users and sum their shares for the winning country, returning the total.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.