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 2 months 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.

Give us feedback!