Skip to content
Security
Skill

/dos-griefing-analysis

Detects Denial of Service and griefing vulnerabilities in smart contracts. Covers unbounded loop DoS, block gas limit exhaustion, external call failure DoS, insufficient gas griefing (63/64 rule), storage bloat attacks, timestamp griefing, self-destruct force-feeding, and push

BOOST
From plugin
quillshield-skills
12911 skills
Install
$ npx -y skills add quillai-network/quillshield_skills --skill dos-griefing-analysis --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/dos-griefing-analysis

Context preview

The summary Claude sees to decide when to auto-load this skill.

Detects Denial of Service and griefing vulnerabilities in smart contracts. Covers unbounded loop DoS, block gas limit exhaustion, external call failure DoS, insufficient gas griefing (63/64 rule), storage bloat attacks, timestamp griefing, self-destruct force-feeding, and push

SKILL.md

dos-griefing-analysis.SKILL.md
name: dos-griefing-analysis
description: Detects Denial of Service and griefing vulnerabilities in smart contracts. Covers unbounded loop DoS, block gas limit exhaustion, external call failure DoS, insufficient gas griefing (63/64 rule), storage bloat attacks, timestamp griefing, self-destruct force-feeding, and push vs pull payment pattern analysis. Use when auditing contracts with batch operations, loops over user data, reward distribution, dividend systems, or any logic that depends on address(this).balance or iterates over growing collections.

DoS & Griefing Analysis

Detect vulnerabilities that allow attackers to **make contracts unusable** (Denial of Service) or **harm other users at low cost** (griefing). These attacks don't steal funds directly but can permanently brick contracts or block critical operations.

When to Use

  • Auditing contracts with loops over dynamic arrays or mappings
  • Reviewing batch operations (airdrops, reward distribution, liquidation)
  • Analyzing contracts that rely on `address(this).balance` for logic
  • Verifying that individual user failures don't block system-wide operations
  • Checking for gas-based attack vectors (insufficient gas, storage bloat)

When NOT to Use

  • Direct fund theft analysis (use behavioral-state-analysis)
  • Access control consistency (use semantic-guard-analysis)
  • Reentrancy detection (use reentrancy-pattern-analysis)

Seven DoS & Griefing Vulnerability Classes

Class 1: Unbounded Loop DoS

Loops that iterate over collections that grow with contract usage. As the collection grows, gas cost increases until the function exceeds the block gas limit and becomes permanently uncallable.

// VULNERABLE: Loop over all users — grows forever
address[] public allUsers;

function distributeRewards() external {
    for (uint i = 0; i < allUsers.length; i++) {
        // If allUsers has 10,000+ entries, this exceeds block gas limit
        token.transfer(allUsers[i], calculateReward(allUsers[i]));
    }
}

// SAFE: Paginated processing
function distributeRewards(uint256 startIndex, uint256 batchSize) external {
    uint256 end = min(startIndex + batchSize, allUsers.length);
    for (uint i = startIndex; i < end; i++) {
        token.transfer(allUsers[i], calculateReward(allUsers[i]));
    }
}

// SAFER: Pull pattern
mapping(address => uint256) public pendingRewards;

function claimReward() external {
    uint256 reward = pendingRewards[msg.sender];
    pendingRewards[msg.sender] = 0;
    token.transfer(msg.sender, reward);
}

**Detection:**

For each loop in the contract:
  1. What determines the loop bound?
     - Fixed constant → SAFE
     - Constructor parameter → SAFE (if reasonable)
     - Dynamic array length → POTENTIALLY VULNERABLE
     - Mapping iteration → VULNERABLE (can't iterate mappings, but workaround arrays are vulnerable)
  2. Can the loop bound grow with contract usage?
  3. What is the gas cost per iteration?
  4. At what size does total gas exceed 30M? (block gas limit)

If loop_bound is unbounded AND gas_per_iteration > 30M / estimated_max_users:
  → UNBOUNDED LOOP DOS

Class 2: External Call Failure DoS

A single failed external call in a batch operation blocks all other operations.

// VULNERABLE: One blacklisted user blocks ALL distributions
function distributeToAll(address[] calldata users, uint256[] calldata amounts) external {
    for (uint i = 0; i < users.length; i++) {
        // If users[5] is USDC-blacklisted, this reverts for ALL users
        require(token.transfer(users[i], amounts[i]), "Transfer failed");
    }
}

// SAFE: Handle failures individually
function distributeToAll(address[] calldata users, uint256[] calldata amounts) external {
    for (uint i = 0; i < users.length; i++) {
        try IERC20(token).transfer(users[i], amounts[i]) returns (bool success) {
            if (!success) emit TransferFailed(users[i], amounts[i]);
        } catch {
            emit TransferFailed(users[i], amounts[i]);
        }
    }
}

**Detection:**

For each loop containing external calls:
  1. Does a failed call revert the entire transaction? (require/revert)
  2. Is there try/catch or success-check-and-skip?
  3. Can any single address/user cause the call to fail?
     - Blacklisted address
     - Contract that reverts in receive()
     - Address that runs out of gas
  If yes → EXTERNAL CALL FAILURE DOS

Class 3: Insufficient Gas Griefing (63/64 Rule)

EIP-150's 63/64 rule: when making an external call, only 63/64 of remaining gas is forwarded. An attacker can supply just enough gas for the outer function to succeed while the inner call fails.

// VULNERABLE: Relayer pattern without gas check
function executeMetaTx(address target, bytes calldata data) external {
    // Attacker (relayer) provides just enough gas for this function
    // but NOT enough for target.call(data) to succeed
    (bool success, ) = target.call(data);
    // success = false (ran out of gas), but function doesn't revert!

    // Mark meta-tx as executed even though it failed
    executedTxs[txHash] = true; // Meta-tx permanently "used" but never executed
}

// SAFE: Verify sufficient gas and check success
function executeMetaTx(address target, bytes calldata data, uint256 gasLimit) external {
    require(gasleft() >= gasLimit * 64 / 63 + 5000, "Insufficient gas");
    (bool success, ) = target.call{gas: gasLimit}(data);
    require(success, "Execution failed");
}

**Detection:**

For each function that makes external calls:
  1. Does the function check the success of the call?
  2. If success is not required, does failure cause permanent state changes?
  3. Is the function called by untrusted relayers?
  4. Is there a minimum gas check before the external call?

  If no success check AND permanent state change on failure:
    → INSUFFICIENT GAS GRIEFING

Class 4: Storage Bloat Attack

An attacker fills storage arrays/mappings to increase gas

Read more
Ships withquillshield-skills

AI agent skills for advanced smart contract security auditing. These skills teach AI agents (Claude, Cursor) the QuillShield methodology for detecting vulnerabilities that traditional static analysis tools miss.

Get the whole plugin

Other skills on quillshield-skills.