Skip to content

Commit 5bbbaad

Browse files
committed
Merge branch 'Destiner-insufficient-gas-griefing'
2 parents 3a41c26 + ffe8fe6 commit 5bbbaad

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

docs/known_attacks.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,52 @@ This attack [was conducted](https://osolmaz.com/2018/10/18/anatomy-block-stuffin
344344

345345
A Block Stuffing attack can be used on any contract requiring an action within a certain time period. However, as with any attack, it is only profitable when the expected reward exceeds its cost. Cost of this attack is directly proportional to the number of blocks which need to be stuffed. If a large payout can be obtained by preventing actions from other participants, your contract will likely be targeted by such an attack.
346346

347+
## Insufficient gas griefing
348+
349+
This attack may be possible on a contract which accepts generic data and uses it to make a call another contract (a 'sub-call') via the low level `address.call()` function, as is often the case with multisignature and transaction relayer contracts.
350+
351+
If the call fails, the contract has two options:
352+
353+
1. revert the whole transaction
354+
2. continue execution.
355+
356+
Take the following example of a simplified `Relayer` contract which continues execution regardless of the outcome of the subcall:
357+
358+
```sol
359+
contract Relayer {
360+
mapping (bytes => bool) executed;
361+
362+
function relay(bytes _data) public {
363+
// replay protection; do not call the same transaction twice
364+
require(executed[_data] == 0, "Duplicate call");
365+
executed[_data] = true;
366+
innerContract.call(bytes4(keccak256("execute(bytes)")), _data);
367+
}
368+
}
369+
```
370+
371+
This contract allows transaction relaying. Someone who wants to make a transaction but can't execute it by himself (e.g. due to the lack of ether to pay for gas) can sign data that he wants to pass and transfer the data with his signature over any medium. A third party "forwarder" can then submit this transaction to the network on behalf of the user.
372+
373+
If given just the right amount of gas, the `Relayer` would complete execution recording the `_data`argument in the `executed` mapping, but the subcall would fail because it received insufficient gas to complete execution.
374+
375+
Note: When a contract makes a sub-call to another contract, the EVM limits the gas forwarded to [to 63/64 of the remaining gas](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md),
376+
377+
An attacker can use this to censor transactions, causing them to fail by sending them with a low amount of gas. This attack is a form of "[griefing](https://en.wikipedia.org/wiki/Griefer)": It doesn't directly benefit the attacker, but causes grief for the victim. A dedicated attacker, willing to consistently spend a small amount of gas could theoretically censor all transactions this way, if they were the first to submit them to `Relayer`.
378+
379+
One way to address this is to implement logic requiring forwarders to provide enough gas to finish the subcall. If the miner tried to conduct the attack in this scenario, the `require` statement would fail and the inner call would revert. A user can specify a minimum gasLimit along with the other data (in this example, typically the `_gasLimit` value would be verified by a signature, but that is ommitted for simplicity in this case).
380+
381+
```
382+
// contract called by Relayer
383+
contract Executor {
384+
function execute(bytes _data, uint _gasLimit) {
385+
require(gasleft() >= _gasLimit);
386+
...
387+
}
388+
}
389+
```
390+
391+
Another solution is to permit only trusted accounts to mine the transaction.
392+
347393

348394
## Forcibly Sending Ether to a Contract
349395

0 commit comments

Comments
 (0)