Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

Denial Of Service attack

Denial of Service attck from unbounded for-loop leading to unbounded gas consumption

Description

  • The 'enterRaffle' function uses an array to enable players to enter the raffle, it also uses a nested for loop on the array elements to check for duplicate players.

  • The issue is that the player arrays are growing with time and as time oes on using these for loops to loop over the arrays will lead to unbounded gas consumption potentially making the contract unusale or very expensive.

// Root cause in the codebase with @> marks to highlight the relevant section
function enterRaffle(address[] memory newPlayers) public payable {
require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
@> for (uint256 i = 0; i < newPlayers.length; i++) {
players.push(newPlayers[i]);
}
// Check for duplicates
for (uint256 i = 0; i < players.length - 1; i++) {
for (uint256 j = i + 1; j < players.length; j++) {
require(players[i] != players[j], "PuppyRaffle: Duplicate player");
}
}
emit RaffleEnter(newPlayers);
}

Risk

Likelihood:

  • As the number of players increase, the players array will increase significantly in length leading to the issue

Impact:

  • This will eventually lead to increase in the gas consumption and cause the denial of service issue which could shut down the program making it unusable or potentialy more expensive for users that come later to be players

Proof of Concept

The following Proof of Concept displays the gas price at the start before a multitude of new players are added to the raffle, it then the gas at the end of the addition of the players and calculates the gas used.

function test_denialOfService() public {
//address[] memory players = new address[](1);
//players[0] = playerOne;
//puppyRaffle.enterRaffle{value: entranceFee}(players);
//assertEq(puppyRaffle.players(0), playerOne);
vm.txGasPrice(1);
//Let's enter 100 players
uint256 playersNum = 100;
address[] memory players = new address[](playersNum);
for (uint256 i=0; i <playersNum; i++){
players[i] = address(i);
}
//see how much gas it costs to enter 100 players
uint256 gasStart = gasleft();
puppyRaffle.enterRaffle{value: entranceFee * players.length}(players);
uint256 gasEnd = gasleft();
uint256 gasUsed = (gasStart - gasEnd);
console.log("Gas cost of the first 100 players: ", gasUsed);
}

Recommended Mitigation

Avoid using for loops to add the players or atleast limit for loops

Another additional mitigation would be to limit the number of players if the gas is not affordable

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!