TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: low
Valid

Missing `EpochInfo` Update Causes Denial of Service in `SpiceAuction::removeAuctionConfig`

Summary:

The SpiceAuction contract's setAuctionConfig function fails to update the epochs mapping with EpochInfo, causing the removeAuctionConfig function to always revert. This creates a Denial of Service (DoS) vulnerability that prevents the removal of auction configurations.

Vulnerability Details:

The setAuctionConfig function does not store EpochInfo in the epochs mapping, leading to a mismatch in expected state. Consequently, the removeAuctionConfig function cannot find the necessary EpochInfo, causing it to revert due to the first if condition always failing.

Code Snippet:

// setAuctionConfig function
function setAuctionConfig(SpiceAuctionConfig calldata _config) external onlyDAOExecutor {
uint256 currentEpochIdCache = _currentEpochId;
if (currentEpochIdCache > 0) {
EpochInfo storage info = epochs[currentEpochIdCache];
if (info.isActive()) revert InvalidConfigOperation();
}
if (
_config.duration < MINIMUM_AUCTION_PERIOD || _config.duration > MAXIMUM_AUCTION_DURATION
|| _config.waitPeriod > MAXIMUM_AUCTION_WAIT_PERIOD
) revert CommonEventsAndErrors.InvalidParam();
if (_config.waitPeriod == 0 || _config.minimumDistributedAuctionToken == 0) {
revert CommonEventsAndErrors.ExpectedNonZero();
}
if (_config.recipient == address(0)) revert CommonEventsAndErrors.InvalidAddress();
currentEpochIdCache += 1;
@> // Missing update here
auctionConfigs[currentEpochIdCache] = _config;
emit AuctionConfigSet(currentEpochIdCache, _config);
}
// removeAuctionConfig function
function removeAuctionConfig() external override onlyDAOExecutor {
uint256 id = _currentEpochId;
@> EpochInfo storage info = epochs[id];
if (info.startTime == 0) revert InvalidConfigOperation(); // Always reverts
if (info.isActive()) revert InvalidConfigOperation();
bool configSetButAuctionStartNotCalled = auctionConfigs[id + 1].duration > 0;
if (!configSetButAuctionStartNotCalled) {
if (info.hasEnded()) revert AuctionEnded();
delete auctionConfigs[id];
delete epochs[id];
_currentEpochId = id - 1;
emit AuctionConfigRemoved(id, id);
} else {
id += 1;
delete auctionConfigs[id];
emit AuctionConfigRemoved(id, 0);
}
}

Impact:

This vulnerability can significantly disrupt the normal operations of the SpiceAuction contract. By preventing the removal of outdated or unwanted auction configurations. Users and stakeholders are forced to participate in that auction configuration, and the contract administrators cannot update auction parameters. This breakdown in functionality undermines the trust and reliability of the auction platform, potentially causing financial loss and reputational damage to the managing DAO and the broader ecosystem.

Proof Of Concept:

  1. DAO administrators call the setAuctionConfig function to set a new auction configuration.

  2. The setAuctionConfig function increments the currentEpochIdCache and stores the configuration in the auctionConfigs mapping but fails to update the epochs mapping with EpochInfo.

  3. The removeAuctionConfig function is called to remove the auction configuration.

  4. The removeAuctionConfig function attempts to retrieve the EpochInfo from the epochs mapping using the _currentEpochId but fails to find the necessary information, causing the function to revert.

  5. The removeAuctionConfig function reverts, preventing the removal of the auction configuration and causing a Denial of Service (DoS) vulnerability.

Proof Of Code:

Place the following code in the SpiceAuction.t.sol contract:

To run the test, follow the commands below:

forge test --mt test_FortisAudits_removeAuctionConfig_Reverts -vvvv
function test_FortisAudits_removeAuctionConfig_Reverts() public {
// setting the auction config
ISpiceAuction.SpiceAuctionConfig memory config = _getAuctionConfig();
vm.startPrank(daoExecutor);
spice.setAuctionConfig(config);
vm.stopPrank();
uint256 id = spice.currentEpoch();
spice.getAuctionConfig(id);
assertEq(id, 0);
spice.getEpochInfo(id);
// removing auction config before starting the auction
// this will revert as the epoch id is not updated in setAuctionConfig
vm.startPrank(daoExecutor);
vm.expectRevert(abi.encodeWithSelector(ISpiceAuction.InvalidConfigOperation.selector));
spice.removeAuctionConfig();
vm.stopPrank();
// we cannot remove the auction config after the auction has started or ended
vm.startPrank(alice);
vm.warp(block.timestamp + config.waitPeriod);
IERC20 auctionToken = IERC20(_getAuctionToken(config.isTempleGoldAuctionToken, daiToken));
dealAdditional(auctionToken, address(spice), 100 ether);
spice.startAuction();
vm.warp(block.timestamp + config.duration + config.startCooldown + 1);
vm.stopPrank();
vm.startPrank(daoExecutor);
vm.expectRevert(abi.encodeWithSelector(IAuctionBase.AuctionEnded.selector));
spice.removeAuctionConfig();
vm.stopPrank();
}

Tools Used:

Manual code review

Foundry

Recommendation:

To mitigate this issue, the setAuctionConfig function should be updated to store EpochInfo in the epochs mapping. This update will allow the removeAuctionConfig function to retrieve the necessary information and remove auction configurations as intended.

Here is the recommended mitigation:

function setAuctionConfig(SpiceAuctionConfig calldata _config) external onlyDAOExecutor {
uint256 currentEpochIdCache = _currentEpochId;
if (currentEpochIdCache > 0) {
EpochInfo storage info = epochs[currentEpochIdCache];
if (info.isActive()) revert InvalidConfigOperation();
}
if (
_config.duration < MINIMUM_AUCTION_PERIOD || _config.duration > MAXIMUM_AUCTION_DURATION
|| _config.waitPeriod > MAXIMUM_AUCTION_WAIT_PERIOD
) revert CommonEventsAndErrors.InvalidParam();
if (_config.waitPeriod == 0 || _config.minimumDistributedAuctionToken == 0) {
revert CommonEventsAndErrors.ExpectedNonZero();
}
if (_config.recipient == address(0)) revert CommonEventsAndErrors.InvalidAddress();
currentEpochIdCache += 1;
+ EpochInfo storage info = epochs[currentEpochIdCache]; // Add this line
+ uint128 startTime = info.startTime = uint128(block.timestamp) + _config.startCooldown;
+ info.endTime = startTime + _config.duration;
+ info.totalAuctionTokenAmount = _config.minimumDistributedAuctionToken;
+ auctionConfigs[currentEpochIdCache] = _config;
emit AuctionConfigSet(currentEpochIdCache, _config);
}
function removeAuctionConfig() external override onlyDAOExecutor {
/// only delete latest epoch if auction is not started
uint256 id = _currentEpochId;
- EpochInfo storage info = epochs[id];
+ EpochInfo storage info = epochs[id + 1];
.
.
.
.
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

bluedragon Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`removeAuctionConfig` can't remove the first added `SpiceAuctionConfig` which in the end leads to inability to recover the funds associated to that auction

Support

FAQs

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