Snowman Merkle Airdrop

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

Integer Overflow in buySnow() Enables Unexpected Reverts or Minting Logic Bypass

Description

Root Cause + Impact Description

The buySnow() function performs unchecked multiplication of s_buyFee * amount to calculate the total payment required in either ETH or WETH. Under extreme input values, this multiplication can cause an integer overflow, which leads to either:

  • Unexpected revert due to overflow under Solidity 0.8+'s default checks

  • Incorrect fee logic if overflow handling is later disabled using unchecked for gas optimizations

This bug is particularly dangerous in payment logic because it may allow users to mint a large number of tokens for a small ETH/WETH amount, or cause denial-of-service (DoS) due to automatic reverts on overflow.


Normal Behavior

The function is expected to multiply s_buyFee by amount, then either accept ETH (msg.value) or transfer WETH accordingly, and mint tokens.


Vulnerable Behavior

If amount is large enough, the multiplication s_buyFee * amount will overflow. Solidity 0.8+ reverts on overflow, causing the entire transaction to fail. If this multiplication is ever wrapped in unchecked, it could silently overflow and allow minting tokens for very low value.


Vulnerable Code Snippet

function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Risk

Likelihood:

  • Will trigger on very large amount values due to Solidity 0.8+ overflow checking

  • Likely to go unnoticed until DoS or if someone disables the safety using unchecked

Impact:

  • Denial-of-service: All large amount calls revert unexpectedly

  • Potential underpayment if unchecked logic is used

  • Affects critical financial flow (minting against payment)


Proof of Concept

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;
import "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
contract TestIntegerOverflow is Test {
Snow public snow;
MockWETH public weth;
address public collector;
function setUp() public {
weth = new MockWETH();
collector = address(0xBEEF);
uint256 fee = 1 ether; // buy fee = 1 ETH
snow = new Snow(address(weth), fee, collector);
}
function testOverflowBuySnow() public {
uint256 fee = snow.s_buyFee();
uint256 overflowAmount = type(uint256).max / fee + 1;
vm.expectRevert(); // Reverts due to overflow
snow.buySnow(overflowAmount);
}
}

###Output

Compiler run successful!
Ran 1 test for test/Testintigeroverflow.sol:TestIntegerOverflow
[PASS] testOverflowBuySnow() (gas: 15064)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 419.32µs (48.02µs CPU time)
Ran 1 test suite in 3.18ms (419.32µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

Use safe multiplication and include a precondition check:

function buySnow(uint256 amount) external payable canFarmSnow {
uint256 totalFee = s_buyFee * amount;
require(amount == 0 || totalFee / amount == s_buyFee, "Overflow detected");
if (msg.value == totalFee) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), totalFee);
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Alternatively, use OpenZeppelin's SafeMath (for earlier versions) or apply an internal _safeMul() utility to avoid unsafe operations.


Updates

Lead Judging Commences

yeahchibyke Lead Judge
3 months ago
yeahchibyke Lead Judge
3 months ago
yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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