Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

totalAmountCollected is not calculated correctly making impossible to select winner in some cases

Vulnerability Details

totalAmountCollected is an important variable, other variables depend on it's value to carry the proper execution of the program. This variable is calculated by measuring the length of the players array. In the scenario that no single players gets refunded, nothing happens the program executes as normal, but if a single player gets refunded, the entranceFee is refunded to the player, and the players position in the Array is set to address(0), but the players array length doesn't change, so when calculating totalAmountCollected it would get an incorrect value, and making impossible to have a winner, and the funds of the players can only be accesed by getting refunded.

PoC

  1. Copy the test into PuppyRaffleTest.t.sol

  2. Run the test

    1. The test should fail


Code

Copy this test into PuppyRaffleTest.t.sol.

function testCanYouMakeRaffleIfAPlayerRefunds() public playersEntered {
uint256 indexOfPlayer = puppyRaffle.getActivePlayerIndex(playerOne);
vm.prank(playerOne);
puppyRaffle.refund(indexOfPlayer);
vm.warp(puppyRaffle.raffleStartTime() + puppyRaffle.raffleDuration());
puppyRaffle.selectWinner();
}

Run the test, it should fail.

Impact

High - compromising the protocol's functionality

Recommendations

There's two possible solutions to this problem;

  1. 'Sanitize' the players array every time a players get refunded

  2. 'Sanitize' the players array in the selectWinner() function, and check for non Address(0) adresses to be greater or equal to 4.

I recommend for the second solution as it can consume less gas.

I would add some lines of code to selectWinner().

These will have the following execution flow;

Runs a for loop through the players array and counts the number of invalid addresses

  • If the number is bigger than zero

    • create a new players array and populated with the valid addresses from the players array

    • change the players array to this 'cleaned' array.

    • execute selectWinner()

  • If the number is zero

    • execute selectWinner()


Code

Code to add to selectWinner()

We would need to change the one require() statement in selectWinner(), for the second statement we will need to change the variable players to our new variable cleanPlayers. For simplicity I added both requirement statements, so anyone testing would only need to erase the requirement statements and paste the code below.

address[] memory tmpPlayers = players;
uint256 lengthPlayers = tmpPlayers.length;
uint256 invalidAddresses;
for (uint256 i = 0; i < lengthPlayers; i++) {
if (tmpPlayers[i] == address(0)) {
invalidAddresses++;
}
}
address[] memory cleanPlayers;
// If there's any invalid address we populate cleanPlayers with valid adresses
if (invalidAddresses != 0) {
cleanPlayers = new address[](lengthPlayers - invalidAddresses);
uint256 cleanPlayersIndex;
for (uint256 i = 0; i < lengthPlayers; i++) {
if (tmpPlayers[i] != address(0)) {
cleanPlayers[cleanPlayersIndex] = tmpPlayers[i];
cleanPlayersIndex++;
}
}
} else {
// If there are no invalid adresses we copy the players array
cleanPlayers = tmpPlayers;
}
require(block.timestamp >= raffleStartTime + raffleDuration, "PuppyRaffle: Raffle not over");
cleanPlayers.length >= 4, "PuppyRaffle: Need at least 4 players");
// rest code

After this modification we can re-run the test and it should pass this time

Updates

Lead Judging Commences

patrickalphac Lead Judge
over 1 year ago
Hamiltonite Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

refund-doesnt-reduce-players-array-size-causing-protocol-to-freeze

zero address can win the raffle

Funds are locked to no one. If someone gets the refund issue, they also got this issue. IMPACT: High Likelihood: High

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.