denial-of-service vulnerability
A denial-of-service vulnerability lets an attacker jam a contract so that legitimate users cannot use it, often without the attacker stealing anything directly. This is griefing: the goal is to break or freeze functionality rather than profit. Because every operation on-chain costs gas and a block has a hard gas limit, a contract that can be pushed into doing unbounded or externally-controllable work can be made to revert for everyone, sometimes permanently locking the funds inside it.
Several patterns recur. An unbounded loop over an array that anyone can grow, paying out to every entrant or iterating a list of users, eventually needs more gas than a block allows, and then no transaction touching that loop can succeed. A DoS-with-revert arises when a single recipient in a payout loop is a contract that always reverts (for example its receive function throws), blocking the entire batch from completing, the original King of the Ether flaw. Other variants include relying on a specific external call that an attacker can make fail, or unexpected reverts in a critical path.
The standard defense is the pull-over-push pattern: rather than the contract pushing funds to a list of recipients in one loop, let each user pull their own balance in a separate transaction, so one bad actor can only block themselves. More generally, avoid unbounded iteration over user-controlled data structures, never let one external call's failure brick a shared process, and design critical functions so that no single participant can hold the whole system hostage. Cap loop sizes, isolate failures, and make withdrawals individual.
// VULNERABLE: one reverting recipient blocks all
for (uint i = 0; i < bidders.length; i++) {
bidders[i].transfer(refunds[bidders[i]]); // can revert
}
// SAFE: pull pattern
function withdrawRefund() external {
uint256 r = refunds[msg.sender];
refunds[msg.sender] = 0;
(bool ok, ) = msg.sender.call{value: r}("");
require(ok);
}Let users pull funds individually instead of pushing to all.
The classic cure is pull over push: never loop paying everyone in one transaction, because one malicious or reverting recipient can revert the whole batch. Instead let each user withdraw their own balance individually.