Thanks to Philip Daian, et al,
the analysis that followed the DAO hack turned up some pretty nasty anti-patterns classed as 're-entries'.
In short, a reentry is an external calling of a public function followed by a subsequent calling of a function in the same contract before the first function had finished execution. A recursive reentry is when ContractA.a() calls ContractB() which then calls ContractA.a() again and so on. If that's not bad enough, another anti-pattern that can trash a contract's state is when ContractA.a() calls ContractB.a() which then calls ContractA.b() and changes state without ContractA.a() being aware of the change.
To protect against this behaviour I offer the following mutual exclusion (mutex) pattern on the contracts as a whole.
contract MutexProtected
{
// Mutex contract on all state mutating external/public functions for protecting against
// Re-entry and `Solar Storm` attacks
modifier isMutexed() {
if(mutex) throw; // Exclusion already set so bail.
else mutex = true; // Set exclusion and continue with function code
_
delete mutex; // release contract from exclusion
return; // functions require single exit and return parameters
}
bool mutex; // the exclusion state variable
}
In the above, the
MutexProtected
contract has a bool
mutex
which is set upon entry into a modified function (see bellow). Any further attempts to enter that or any other public function will be thrown simply by having a contract inherit
MutexProtected
and applying the
isMutexed()
modifyier to public functions.
The only drawback is that an author must manage that mutex themselves if they want a public function to call another public function within their contract. However it is probably a more secure practice to simply limit the public API as best one can and rely on internals to do the bulk of the work.
Following is an example of a mutex protected contract:
contract Guff is MutexProtected
{
uint someGuff; // a state variable
function sendSomeGuff(uint guff)
isMutexed // the function modifier
returns (uint ret) // Requires return parameters
{
// This code cannot run if the mutex is true
someGuff += guff; // Mutate the state variable
msg.sender.send(guff); // Send some eth
ret = someGuff; // set the return parament
//note the missing return. It is in the modifier.
}
}
Other restrictions are that a function must have only a single exit and return parameters are to be used instead of single values.
Comments