Add a simple example for reentrancy and solutions with mutex or CEI

This commit is contained in:
bt3gl 2022-09-17 17:55:39 -07:00 committed by GitHub
parent e89d152887
commit 2bde8bfbb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,12 +3,83 @@
<br>
### TL; DR
* When a contract calls an external function, that external function may itself call the calling function.
* A reentrancy attack may occur when a function makes an external call to another untrusted contract. Then, the unstrusted contract makes a recursive callback to the vulnerable contract function to steal funds.
* To prevent this attack, a contract can implement a lock in storage that prevents re-entrant calls.
<br>
### References to learn about reentrancy
---
### Example of re-entrancy attack
<br>
For example, suppose this method:
```
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
(bool success, ) = msg.sender.call.value(amountToWithdraw)("");
requires(success);
userBalances[msg.sender] = 0;
}
```
and this exploit:
```
function() public payable {
if(msg.sender == address(vulnContract)) {
vulnContract.withdrawBalance();
}
}
```
How to fix?
#### Option 1: Adding a mutex locking:
```
modifier noReentrant() {
require(!locked, "nooooope");
locked = true;
_;
locked = false;
}
```
so
```
function withdrawBalance() public noReentrant {
...
}
```
<br>
#### Option 2: CEI (checks effects interaction) pattern
```
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0; // update state first
(bool success, ) = msg.sender.call.value(amountToWithdraw)("");
requires(success);
}
```
<br>
----
### Resources
<br>