Bid Beasts

First Flight #49
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Integer Overflow

Integer Overflow

Description

The requiredAmount:

requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);

has a multiplication done before the division which is an issue with large numbers but not realistically an issue with small numbers. This can cause loss of precision, replay/underbid risk, potential overflow and rounding semantics.

if (previousBidAmount == 0) {
requiredAmount = listing.minPrice;
require(msg.value > requiredAmount, "First bid must be > min price");
listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
emit AuctionExtended(tokenId, listing.auctionEnd);
} else {
@> requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
require(msg.value >= requiredAmount, "Bid not high enough");
uint256 timeLeft = 0;
if (listing.auctionEnd > block.timestamp) {
timeLeft = listing.auctionEnd - block.timestamp;
}
if (timeLeft < S_AUCTION_EXTENSION_DURATION) {
listing.auctionEnd = listing.auctionEnd + S_AUCTION_EXTENSION_DURATION;
emit AuctionExtended(tokenId, listing.auctionEnd);
}
}

Risk

Likelihood: High Risk.

  • Integer Division Truncation // WHEN Solidity uses integer division, it truncates (floors) any remainder.

  • Replay/underbid risk // WHEN a new bid is underestimated requiredAmount may allow a new bid that doesn't meet the intended minimum increment, which is a logic/security/economic bug in auction code.

  • Potential Overflow // WHEN previousBidAmount is very large and 100 + percent, it can overflow which makes the product exceed type(uint256).max

Impact:

  • Loss of Precision

  • Potential Overflow

  • Reply/underbid risk

  • Rounding semantics

Proof of Concept

It contains a vulnerable contract that uses the flawed formula.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
// Vulnerable auction
contract VulnerableAuction {
uint256 public highestBid;
uint256 public immutable INC; // increment pct
constructor(uint256 _inc) { INC = _inc; }
function placeBid() external payable {
if (highestBid == 0) return highestBid = msg.value;
uint256 required = (highestBid / 100) * (100 + INC); // flawed
require(msg.value >= required, "low bid");
highestBid = msg.value;
}
}
// Fixed auction
contract FixedAuction {
uint256 public highestBid;
uint256 public immutable INC;
constructor(uint256 _inc) { INC = _inc; }
function placeBid() external payable {
if (highestBid == 0) return highestBid = msg.value;
uint256 required = (highestBid * (100 + INC)) / 100; // correct
require(msg.value >= required, "low bid");
highestBid = msg.value;
}
}
// PoC Test
contract AuctionPoC is Test {
VulnerableAuction vuln;
FixedAuction fix;
address alice = address(1);
address bob = address(2);
function setUp() public {
vuln = new VulnerableAuction(10);
fix = new FixedAuction(10);
vm.deal(alice, 1 ether);
vm.deal(bob, 1 ether);
}
function testVulnUnderbid() public {
vm.prank(alice); vuln.placeBid{value: 101}();
vm.prank(bob); vuln.placeBid{value: 110}(); // succeeds, should need 111
assertEq(vuln.highestBid(), 110);
}
function testFixBlocksUnderbid() public {
vm.prank(alice); fix.placeBid{value: 101}();
vm.prank(bob);
vm.expectRevert();
fix.placeBid{value: 110}(); // reverts, needs 111
}
}

Recommended Mitigation

This can be fixed by refactoring the code or better still using openzeppelin libraries like the safe mulDiv library which has PRBMath, Openzeppelin's Math.

- remove this code
requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
+ add this code
requiredAmount = (previousBidAmount * (100 + S_MIN_BID_INCREMENT_PERCENTAGE)) / 100;
Updates

Lead Judging Commences

cryptoghost Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeasts Marketplace: Integer Division Precision Loss

Integer division in requiredAmount truncates fractions, allowing bids slightly lower than intended.

Support

FAQs

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