Hola, estaba revisando la documentación de solidez. Había un código que no pude entender, incluso después de investigar mucho, no pude encontrar un resultado satisfactorio. El código es el siguiente:
contract Mutex {
bool locked;
modifier noReentrancy() {
require(!locked);
locked = true;
_;
locked = false;
}
/// This function is protected by a mutex, which means that
/// reentrant calls from within `msg.sender.call` cannot call `f` again.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
function f() public noReentrancy returns (uint) {
require(msg.sender.call());
return 7;
}
}
¿Qué msg.sender.call()
hace? esta llamando de f()
nuevo? Si es así, ¿entonces cómo?
Llama a la función de reserva anónima en msg.sender
.
En un ataque de reentrada típico, sería algo así como una withdraw
función haciendo msg.sender.call.value(1 ether)()
. La persona que llama (un contrato inteligente) volvería a llamar a la función, de ahí el ataque de "reentrada". En este fragmento, call
no parece estar haciendo nada útil, pero solo está ahí para mostrar que las locked
variables protegen contra la reentrada.
msg.sender.call()
llama a la función de reserva en msg.sender
.
Aquí hay un ejemplo que se extiende con una canBeAttacked
función.
contract Mutex {
bool locked;
modifier noReentrancy() {
require(!locked);
locked = true;
_;
locked = false;
}
function canBeAttacked() public returns (uint) {
require(msg.sender.call.value(1 ether)());
return 7;
}
/// This function is protected by a mutex
function f() public noReentrancy returns (uint) {
require(msg.sender.call());
return 7;
}
}
2 cosas a tener en cuenta del código anterior.
de:
// attacker fallback
function() {
Mutex(msg.sender).canBeAttacked();
}
El contrato puede tener otra función que llame a canBeAttacked()
, y esto seguirá reingresando canBeAttacked()
y enviando al atacante 1 éter cada vez. (El respaldo necesita más código para evitar un bucle infinito y quedarse sin gasolina). Este es un ejemplo de un ataque de reentrada.
Ahora imagine un contrato de atacante que tiene una reserva de:
// attacker fallback
function() {
Mutex(msg.sender).f();
}
f()
, el respaldo del atacante fallará porque locked
es true
, por lo require(!locked)
que generará una excepción y revertirá la transacción completa.También tenga en cuenta que noReentrancy
evita que un atacante llame f()
y, en el respaldo del atacante, llame a canBeAttacked
. (Pero pueden atacar como el n. ° 1 anterior).
ética