Chapter 2
Classification and Analysis of Common SWC Vulnerabilities
Delve into the anatomy of the most consequential smart contract weaknesses as formalized in the SWC registry. This chapter unpacks not just what goes wrong, but why these vulnerabilities exploit the very logic and assumptions of decentralized computation-equipping advanced readers to recognize, dissect, and neutralize even the most insidious flaws found in high-stakes blockchain systems.
2.1 Reentrancy
Reentrancy (SWC-107) constitutes a fundamental vulnerability paradigm within Ethereum smart contracts, arising when an external call to a contract allows the callee to re-enter the caller's context before the completion of its original invocation. This enables adversaries to manipulate contract state unpredictably, often culminating in severe financial losses. The canonical manifestation involves a withdrawal pattern, whereby an attacker repeatedly invokes a vulnerable contract's payout function before its internal state update finalizes, thereby draining funds beyond intended limits.
The core mechanical cause of reentrancy is the interplay between state mutations and external calls executed via Solidity's call, send, or transfer functions. For instance, consider the following simplified contract snippet exemplifying the vulnerable withdrawal pattern:
contract VulnerableBank { mapping(address => uint) public balances; function deposit() external payable { balances[msg.sender] += msg.value; } function withdraw(uint _amount) external { require(balances[msg.sender] >= _amount, "Insufficient balance"); (bool success, ) = msg.sender.call{value: _amount}(""); require(success, "Transfer failed"); balances[msg.sender] -= _amount; } } In this example, the critical error lies in transferring Ether before updating balances. Because the external call msg.sender.call transfers control to the recipient, the attacker contract's fallback function can recursively invoke withdraw repeatedly, obtaining funds before the original balance deduction executes. This classic reentrancy exploit was infamously utilized in the 2016 DAO hack, resulting in a catastrophic loss exceeding 60 million USD at the time.
Modern exploit variants extend this foundational attack pattern, leveraging more complex control flows and multi-function reentrancy, termed cross-function or cross-contract reentrancy. These involve invoking different contract methods out-of-order or cascading calls across multiple contracts sharing a mutable state, thus subverting naive reentrancy guards. An illustrative example is a decentralized exchange (DEX) contract permitting token swaps, where an attacker exploits callback hooks within token transfer functions to recursively trigger state-dependent logic without proper state locking.
Further sophistication arises with gas-stipend-limited calls such as those used in transfer or send as opposed to call. Although these methods provide mitigations by restricting gas, enabling only minimal code execution in fallback functions, attackers have circumvented these with carefully crafted fallback logic to trigger reentrant calls through other mechanisms, demonstrating that gas-limit protections are not a panacea.
Analyzing post-mortem incident reports exemplifies the intricate manifestations of reentrancy. For example, the bZx flash loan attack combined reentrancy-withdrawal techniques with logically intricate state interdependencies. The attacker triggered recursive borrow and repay cycles with manipulated price data during reentrant states, effectively bypassing expected invariant assumptions. Similarly, the Compound protocol's smart contracts featured reentrancy-related edge conditions whereby provided hooks allowed attackers to disrupt protocol accounting through nested calls to external contracts.
Effective mitigation strategies against reentrancy vulnerabilities have evolved beyond simple checks-effects-interactions patterns. While ordering state updates prior to external calls remains foundational, additional constructs reinforce security:
...