3.8 Common Vulnerabilities

This section is a catalog of vulnerabilities — what they are, how attackers exploit them, what they cost when they're exploited, and how to write code that doesn't have them. Where Section 3.7 framed safety from the patterns angle ("here are the shapes of safe code"), this section frames it from the failure modes angle ("here are the shapes of broken code, and how each one breaks"). The two sections are complementary; many of the same underlying mechanisms appear in both, but the framing and the depth differ.

A developer reading 3.7 is asking "how should I write this?". A developer reading 3.8 is asking "why does that bug happen, and how do I prevent it in my code?". The book's overall philosophy assumes both questions get asked, often by the same person, and assumes some duplication between sections is the right trade-off for letting each section stand alone.

How This Section Relates to Section 4.11

Book 4 is the auditing book. Section 4.11 (Identifying Vulnerabilities) covers many of the same vulnerabilities catalogued here, but from the auditor's angle: how to detect each vulnerability during a security review, what heuristics surface them, what tooling helps. The same reentrancy bug is in both — Section 3.8.2 explains the mechanic and the developer-side defense; Section 4.11.8 explains how an auditor recognizes the pattern when reviewing code they didn't write.

If you are writing code, start here. If you are auditing someone else's code, start in Book 4. The cross-references between the two sections make it straightforward to move between the developer and auditor framings of any single vulnerability.

How This Section Relates to Section 3.10

Section 3.10 (Learning from Past Exploits) walks through specific historical incidents — The DAO, Parity, bZx, Nomad, Euler, and others — as case studies. The vulnerability that enabled each exploit is the subject of one of the subsections here. The case studies in 3.10 are the application of this section's material to specific real-world losses; this section provides the conceptual foundation that the case studies build on.

Section Structure

Each subsection follows a consistent template:

  1. What it is — the underlying mechanic
  2. Vulnerable example — minimal code that exhibits the vulnerability
  3. Fixed example — the same code with the defense applied
  4. Foundry test — pass-and-fail demonstration where useful
  5. Real-world context — historical exploits or named incidents
  6. Cross-references — pointers to related sections

The depth varies by vulnerability. Reentrancy (3.8.2) gets the most space because it has the most variants (direct, cross-function, cross-contract, read-only, cross-chain) and the most history. Solidity language pitfalls (3.8.1) covers many small issues briefly because each one is well-contained. Storage and delegatecall (3.8.9) gets dedicated treatment because the mechanics are subtle and the consequences are severe.

The Ten Subsections

3.8.1 Solidity Language Pitfalls covers the language-specific traps that produce bugs: variable shadowing, fallback/receive misuse, visibility defaults, immutable misinitialization, constructor vs initializer confusion. These are the bugs that happen because Solidity behaves slightly differently than the developer expected.

3.8.2 The Reentrancy Family covers the most-famous and most-persistent vulnerability class: direct reentrancy, cross-function reentrancy, cross-contract reentrancy, read-only reentrancy, and cross-chain reentrancy. Each variant has its own detection heuristic and its own appropriate defense.

3.8.3 Arithmetic & Precision covers the math bugs: overflow and underflow before and after Solidity 0.8.0, precision loss in integer division, rounding direction errors, division-before-multiplication, and the ERC-4626 inflation attack class.

3.8.4 Access Control Failures covers the missing-permission and wrong-permission vulnerabilities: uninitialized owners, missing modifiers, wrong msg.sender in proxy contexts, tx.origin for authentication, and authorization that depends on assumptions about caller type.

3.8.5 Oracle & Price Manipulation covers contracts that read state from external sources without enough validation: single-source oracles, spot-price manipulation, stale-data acceptance, and the bridge between this section and the deeper treatment in 3.11.1.

3.8.6 Denial of Service covers the patterns that prevent legitimate users from interacting with a contract: unbounded loops, gas griefing, force-fail callbacks, and storage-array growth that eventually exceeds the block gas limit.

3.8.7 Front-running & MEV Exposure covers the transaction-ordering vulnerabilities: classic front-running, sandwich attacks, transaction reordering, and the practical limits of mempool-level defenses.

3.8.8 Signature & Replay Issues covers cryptographic signing failures: malleability, missing chain ID, missing nonce, EIP-712 mistakes, and the specific class of bugs that have repeatedly hit signature-verification logic in bridges and multi-sigs.

3.8.9 Storage & Delegatecall covers the proxy-pattern hazards: storage layout collisions, uninitialized proxy logic, dangerous delegatecall to user-controlled addresses, and the specific failure mode behind the Parity multi-sig incidents.

3.8.10 Case-Study Walkthroughs closes the section with three to four short case studies, each framed from the developer angle: "this would have been caught by writing the right test." These are deliberately shorter than the case studies in Section 3.10; they exist to reinforce the conceptual material in 3.8.1–3.8.9.

Conventions

The same conventions used in Section 3.7 apply here:

  • Solidity ^0.8.20 is the default version pragma; version-specific behavior (especially the 0.8.0 arithmetic transition) is called out where relevant.
  • OpenZeppelin contracts are the default reference implementation.
  • Foundry is the primary test framework; Hardhat is noted where it differs.
  • Each subsection's "vulnerable" and "fixed" code is minimal — just enough to demonstrate the mechanic. Production code combining multiple patterns is shown in Section 3.7.

The Bigger Picture

The vulnerabilities in this section have been documented, exploited, and re-exploited across the entire history of Ethereum smart contracts. The patterns in Section 3.7 exist because of the vulnerabilities catalogued here; the case studies in Section 3.10 are the field reports from when these vulnerabilities reached production. Every vulnerability in this section has cost users real money. Every defense in this section was developed by a community that learned, painfully and repeatedly, from those losses.

A developer who reads this section closely and applies its lessons will not write a secure contract — security is a property of the whole system, not just the absence of these specific bugs. But they will avoid the specific failure modes that have dominated smart contract losses for nearly a decade. That is the minimum bar.

Sections 3.8.1 through 3.8.10 follow.