SWC-126
Title
Insufficient Gas Griefing
Relationships
CWE-691: Insufficient Control Flow Management
Description
Insufficient gas griefing attacks can be performed on contracts which accept data and use it in a sub-call on another contract. If the sub-call fails, either the whole transaction is reverted, or execution is continued. In the case of a relayer contract, the user who executes the transaction, the 'forwarder', can effectively censor transactions by using just enough gas to execute the transaction, but not enough for the sub-call to succeed.
Remediation
There are two options to prevent insufficient gas griefing:
- Only allow trusted users to relay transactions.
- Require that the forwarder provides enough gas.
References
- Consensys Smart Contract Best Practices
- What does griefing mean?
- Griefing Attacks: Are they profitable for the attacker?
Contract Samples
relayer.sol
/*
* @source: https://consensys.github.io/smart-contract-best-practices/known_attacks/#insufficient-gas-griefing
* @author: ConsenSys Diligence
* Modified by Kaden Zipfel
*/
pragma solidity ^0.5.0;
contract Relayer {
uint transactionId;
struct Tx {
bytes data;
bool executed;
}
mapping (uint => Tx) transactions;
function relay(Target target, bytes memory _data) public returns(bool) {
// replay protection; do not call the same transaction twice
require(transactions[transactionId].executed == false, 'same transaction twice');
transactions[transactionId].data = _data;
transactions[transactionId].executed = true;
transactionId += 1;
(bool success, ) = address(target).call(abi.encodeWithSignature("execute(bytes)", _data));
return success;
}
}
// Contract called by Relayer
contract Target {
function execute(bytes memory _data) public {
// Execute contract code
}
}
relayer.yaml
description: Relayer contract without protection against insufficient gas griefing
issues:
- id: SWC-126
count: 1
locations:
- bytecode_offsets: {}
line_numbers:
relayer.sol: [27]
relayer_fixed.sol
/*
* @source: https://consensys.github.io/smart-contract-best-practices/known_attacks/#insufficient-gas-griefing
* @author: ConsenSys Diligence
* Modified by Kaden Zipfel
*/
pragma solidity ^0.5.0;
contract Relayer {
uint transactionId;
struct Tx {
bytes data;
bool executed;
}
mapping (uint => Tx) transactions;
function relay(Target target, bytes memory _data, uint _gasLimit) public {
// replay protection; do not call the same transaction twice
require(transactions[transactionId].executed == false, 'same transaction twice');
transactions[transactionId].data = _data;
transactions[transactionId].executed = true;
transactionId += 1;
address(target).call(abi.encodeWithSignature("execute(bytes)", _data, _gasLimit));
}
}
// Contract called by Relayer
contract Target {
function execute(bytes memory _data, uint _gasLimit) public {
require(gasleft() >= _gasLimit, 'not enough gas');
// Execute contract code
}
}
relayer_fixed.yaml
description: Relayer contract with protection against insufficient gas griefing
issues:
- id: SWC-126
count: 0
locations: []