Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Single Transaction Failure in Batch Execution Causes Complete Revert in TimelockController

Summary

In TimelockController::executeBatch, the function executes multiple transactions in a single loop but reverts the entire batch if any single transaction fails. This creates a vulnerability where a malicious contract can intentionally revert to block the execution of all other legitimate transactions in the batch, effectively causing a denial of service.

The issue is particularly concerning because:

  1. The operation is marked as executed before the loop starts

  2. If any transaction fails, the entire batch fails

  3. The operation cannot be retried since it's already marked as executed

  4. All subsequent legitimate transactions are permanently blocked

Impact

  1. Permanent Loss of Functionality: Since operations are marked as executed before actual execution, a failed batch cannot be retried.

  2. Governance Disruption: Critical governance actions could be blocked if bundled with a malicious target.

  3. Denial of Service: A malicious actor could intentionally include a failing contract to prevent execution of important protocol updates.

  4. Time Loss: Failed batches require re-proposing and waiting through timelock periods again.

Recommendations

Approach 1: Implement Partial Success

function executeBatch(...) external {
// ...
- op.executed = true;
+ bool[] memory successes = new bool[](targets.length);
+ uint256 successCount = 0;
for (uint256 i = 0; i < targets.length; i++) {
(bool success, ) = targets[i].call{value: values[i]}(calldatas[i]);
- if (!success) {
- revert CallReverted(id, i);
- }
+ successes[i] = success;
+ if (success) successCount++;
}
+ op.executed = true;
+ emit BatchExecutionResults(id, successes, successCount);
}

Approach 2: Split Into Individual Operations

function executeBatch(...) external {
+ for (uint256 i = 0; i < targets.length; i++) {
+ bytes32 individualId = hashOperation(targets[i], values[i], calldatas[i], predecessor, salt);
+ _operations[individualId] = Operation({
+ timestamp: block.timestamp,
+ executed: false
+ });
+ }
}

Approach 3: Add Retry Mechanism

function executeBatch(...) external {
+ require(!op.attemptedExecution, "Already attempted");
+ op.attemptedExecution = true;
for (uint256 i = 0; i < targets.length; i++) {
(bool success, ) = targets[i].call{value: values[i]}(calldatas[i]);
if (!success) {
revert CallReverted(id, i);
}
}
+ op.executed = true;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!