Core Contracts

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

Denial of Service via Gas Limit Exhaustion in TimelockController Batch Execution

Summary

The TimelockController contract contains a critical vulnerability in its executeBatch function where processing a large number of targets in a single transaction may exceed Ethereum's block gas limit. This results in transaction failures, rendering governance proposals unexecutable and enabling denial-of-service (DoS) attacks against protocol operations.

Vulnerability Details

Affected Component

  • Function: executeBatch(address[],uint256[],bytes[],bytes32,bytes32)

  • Code Snippet:

    for (uint256 i = 0; i < targets.length; i++) {
    (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
    if (!success) {
    revert CallReverted(id, i);
    }
    }

Root Cause

The loop iterates over all targets and executes external calls without considering gas limitations. Each iteration consumes gas for:

  1. Accessing array elements (targets[i], values[i], calldatas[i]),

  2. Performing a low-level call operation,

  3. Checking success status.

Gas Cost Breakdown:

  • Base loop overhead: ~500 gas per iteration (increment i, condition checks),

  • Low-level call: ~2,100 gas (minimum for empty call),

  • Revert handling: ~20,000 gas (if calls fail),

  • Total per iteration: ~20,000 - 30,000 gas (conservative estimate for simple calls).

With Ethereum's block gas limit at ~30 million gas, a batch of 1,000 targets would require:

1,000 iterations × 30,000 gas = 30,000,000 gas

This exceeds the block limit, causing transaction reversion.

Impact

  • Failed Governance Actions: Legitimate proposals with large target sets become permanently stuck.

  • DoS Attack Vector: Malicious proposers can intentionally create unexecutable operations.

  • Protocol Paralysis: Critical updates (security patches, parameter changes) may be blocked.

Tools Used

  1. Manual Code Analysis: Identified gas-intensive loop structure.

  2. Gas Estimation: Used Remix IDE and Hardhat for gas profiling.

  3. Ethereum Block Explorer: Verified current block gas limits (30M gas).

Proof of Concept (PoC)

Scenario Setup

  1. User create Proposer:

    • A proposer schedules a batch with 1,500 targets using scheduleBatch.

    • Each target calls a minimal contract (e.g., empty fallback function).

  2. Execution Attempt:

    • After the timelock delay, an executor calls executeBatch.

    • Transaction consumes all available gas and reverts.

Step-by-Step Reproduction

  1. Deploy Test Contract:

    contract EmptyContract {
    fallback() external payable {}
    }
  2. Create Malicious Batch:

    const targets = Array(1500).fill(emptyContract.address);
    const values = Array(1500).fill(0);
    const calldatas = Array(1500).fill("0x");
    await timelock.scheduleBatch(
    targets,
    values,
    calldatas,
    ZERO_BYTES32,
    randomSalt,
    MIN_DELAY
    );
  3. Advance Time:

    await network.provider.send("evm_increaseTime", [MIN_DELAY]);
  4. Execute Batch:

    // Transaction will fail
    await timelock.executeBatch(
    targets,
    values,
    calldatas,
    ZERO_BYTES32,
    randomSalt
    );

Expected Outcome

  • Transaction reverts with out of gas error.

  • Batch remains in pending state indefinitely.

Recommendations

Short-Term Mitigation

  1. Add Target Limit:

    uint256 public constant MAX_BATCH_SIZE = 100;
    function scheduleBatch(...) external {
    require(targets.length <= MAX_BATCH_SIZE, "Too many targets");
    // ...
    }
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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