One of the core bugs that affect the DAO was coined “race to empty” by Peter Vessenes. It’s caused by a misordering of logic that lets the attacker withdraw more than they put in.
One of the things that make it tricky to catch is that it’s enabled by a feature of Solidity called re-entrancy, which is where when you call out to another smart contract, it can call back the the originating contract and call any public functions.
If you’re coming from a web background, this behavior is surprising. An analogy might be if that every time you called a web API, that web API could call back into your code by calling any public function. It’s even worse, because imagine if the API you’re calling could read your code and was actively, maliciously trying to change your internal state so they could steal your money.
This is an attack vector we don’t usually think of in other types of programming. Generally when we call a Web API there’s no way for that API to do anything to our program other than return junk results. Calling back our own functions is a whole new level of risk.
Let’s take a look at a concrete example. And keep in mind that this is a toy version of an actual problem within the DAO’s code. And in fact, the race-to-empty problem has historically been a common problem with several different Ethereum smart contracts.
Because it’s widespread, serious, and fairly easy to mitigate, it makes a great first example.
Here’s the idea: we’re going to create a Bank contract. The bank will hold a balance mapping which maps an address to an amount deposited by that address.