Skip to content

Section 44: Rust and Memory Safety — Overview

Purpose and Scope

This section examines memory safety as a systems property and Rust as the primary production mechanism for achieving it without runtime overhead. The treatment begins with the memory safety problem: what it is, why it matters statistically (70% of critical security vulnerabilities in large C/C++ codebases are memory safety issues), and why garbage-collected languages are an insufficient answer for systems programming. It then develops Rust's ownership, borrowing, and lifetime model from first principles, covers the role and risks of unsafe, and surveys the growing ecosystem of Rust-based systems software: OS kernels, drivers, embedded firmware, and hypervisors. The goal is to give the systems programmer the conceptual framework to evaluate where Rust is the right tool and where it imposes unjustifiable costs.

Prerequisites

  • Section 11: Memory Management — stack vs heap allocation, allocator design, dangling pointers, double-frees
  • Section 10: Synchronization — data races, mutex disciplines, shared state concurrency
  • Proficiency in C or C++ — this section contrasts Rust with C; fluency in C makes the contrast concrete
  • Section 41: Modern Kernel Challenges — Rust-in-Linux context

Learning Objectives

Upon completing this section, the reader will be able to:

  1. Enumerate the six classes of memory safety bugs (use-after-free, double-free, buffer overflow, buffer over-read, null pointer dereference, use of uninitialized memory) and explain the exploit primitives each enables
  2. Explain Rust's ownership model: the three rules (each value has one owner; owner is responsible for deallocation; ownership can be transferred), and why they eliminate use-after-free and double-free at compile time
  3. Explain the borrow checker: shared references (&T) vs exclusive references (&mut T); the aliasing XOR mutability invariant; why this eliminates data races at compile time
  4. Explain lifetimes: what they annotate, when they must be explicit, and how the borrow checker uses them to prevent dangling references
  5. Describe the unsafe subset: the five unsafe superpowers, the meaning of "safe abstraction over unsafe code," and what soundness requires
  6. Survey the Rust OS ecosystem: Redox, Tock, Theseus, writing Linux kernel modules in Rust
  7. Compare Rust and C for systems programming on the dimensions of safety, performance, ergonomics, ecosystem, and C interoperability
  8. Explain Rust's async/await model and the executor/reactor pattern; relate it to io_uring integration

Architecture Overview

MEMORY SAFETY BUG TAXONOMY
=============================

  C/C++ programs
       |
       +── use-after-free (UAF) ──────> type confusion, RCE
       +── double-free ───────────────> heap metadata corruption
       +── buffer overflow (write) ───> stack smashing, heap overflow
       +── buffer over-read ──────────> info leak (Heartbleed)
       +── null deref ────────────────> crash / info leak
       +── uninitialized memory ──────> info leak, logic error

RUST OWNERSHIP RULES
======================

  ┌─────────────────────────────────────────────────┐
  │  Rule 1: Every value has exactly one owner       │
  │  Rule 2: When owner goes out of scope → drop()   │
  │  Rule 3: Ownership can be moved, not copied      │
  │          (unless T: Copy)                        │
  └─────────────────────────────────────────────────┘
        |
        v  these three rules eliminate:
        ├── use-after-free   (no double ownership)
        ├── double-free      (one owner, one drop)
        └── memory leaks     (owner always drops)

BORROW CHECKER INVARIANT
==========================

  At any point in time, for a value v:
  EITHER: any number of shared references &v  (read-only, Send)
  OR:     exactly one exclusive reference &mut v  (read-write)
  NEVER:  both simultaneously

  This invariant eliminates:
  ├── data races     (&mut v cannot alias; no concurrent writes)
  └── iterator invalidation (cannot mutate while borrowed)

RUST LIFETIME FLOW
===================

  fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
      |                                          |
      |<── lifetime 'a means: returned ref ──>  |
          lives no longer than either input
          → borrow checker rejects dangling return

Key Concepts

  • Memory safety: the property that all memory accesses reference valid, live, allocated memory of the correct type; absence of UAF, double-free, buffer overflow, null deref, and uninit read
  • Ownership: a single owner responsible for deallocation; transferred (moved) on assignment; guarantees exactly-once deallocation
  • Move semantics: when ownership is transferred, the source variable becomes inaccessible; no implicit copying; forces explicit clone when sharing is needed
  • Borrow: a temporary, non-owning reference; either shared (&T, multiple readers) or exclusive (&mut T, single writer); the "aliasing XOR mutability" invariant
  • Lifetime: a region of code during which a reference is guaranteed valid; inferred by the compiler in most cases; explicit annotations required when the borrow checker cannot infer them
  • Borrow checker: the compiler component that enforces ownership and borrowing rules at compile time; no runtime cost
  • unsafe Rust: a subset allowing five additional capabilities: dereferencing raw pointers, calling unsafe functions, accessing mutable statics, implementing unsafe traits, and accessing union fields; programmer takes responsibility for upholding safety invariants
  • Safe abstraction: an unsafe implementation that presents a safe public API; the contract is that no safe code using the API can trigger undefined behavior; the standard library is built this way
  • Soundness: the property that no safe code can cause undefined behavior; an unsafe abstraction is unsound if there exists any safe code using it that triggers UB
  • Send and Sync: marker traits; Send means a type can be transferred to another thread; Sync means a type can be shared by reference across threads; the type system enforces these at compile time
  • Fearless concurrency: the combination of Send, Sync, and the borrow checker eliminates data races at compile time; the compiler rejects programs with concurrent mutable aliasing
  • Drop: the destructor trait; called automatically when value goes out of scope; RAII applied universally; no need for manual resource release
  • async/await: syntactic sugar over state machines; futures represent asynchronous computations; executor drives them to completion; enables high-concurrency I/O without threads
  • Tock OS: embedded OS in Rust; process isolation via hardware MPU; capsules enforce resource limits; safe kernel with untrusted user processes in Rust
  • Redox OS: Unix-like OS in Rust; microkernel; drivers in user space; scheme-based IPC; POSIX compatibility layer

Major Historical Milestones

Year Event Significance
2006 Graydon Hoare begins Rust Personal project addressing C++ safety pain points
2010 Mozilla sponsors Rust Language development accelerates; use case: browser engine Servo
2012 Ownership model stabilizes Core borrow checker design settles after several major revisions
2015 Rust 1.0 released Stability guarantee; ecosystem begins serious growth
2016 Tock OS 0.1 First Rust embedded OS; proves viability for resource-constrained systems
2017 Redox OS 0.1 First Unix-like OS in Rust; demonstrates full OS is feasible
2018 Rust 2018 edition Non-lexical lifetimes; improved ergonomics; async story begins
2018 NSA, Microsoft, Google endorse memory safety Major institutional validation; memory safety narrative shifts
2019 Rust async stabilized async/await syntax; tokio ecosystem; production-grade async I/O
2019 Android begins Rust adoption First Rust code in Android; Bluetooth stack rewritten
2020 Rust foundation created Mozilla, AWS, Google, Huawei, Microsoft; organizational stability
2021 Rust in Linux RFC accepted Linus accepts principle of Rust as second language
2022 Linux 6.1: Rust infrastructure merged First Rust code in mainline Linux; abstractions for kernel APIs
2022 Android 13: 21% new code in Rust Demonstrated at scale; zero memory safety vulnerabilities in Rust code
2023 NSA: switch to memory-safe languages NSA guidance explicitly recommends Rust for new systems code
2023 First Rust PHY driver in Linux mainline Concrete driver in production kernel
2024 Rust in Windows kernel Microsoft merges Rust into Windows kernel for selected components

Modern Relevance

Memory safety is the defining systems quality challenge of the 2020s. The US Cybersecurity and Infrastructure Security Agency (CISA), NSA, and major cloud vendors have all issued guidance recommending a transition away from memory-unsafe languages for new systems code. The empirical case is clear: Google's Project Zero, Microsoft's Security Response Center, and the Android security team all report ~70% of their critical vulnerabilities are memory safety issues.

Rust is not a panacea. unsafe code must be carefully audited. The borrow checker imposes real ergonomic costs, particularly in graph-shaped and self-referential data structures. C interop requires unsafe. Compile times are slow. The ecosystem, while growing rapidly, lacks the decades of battle-hardened libraries that C possesses.

Despite these limitations, the weight of evidence supports Rust as the best available tool for writing new systems software where memory safety matters and garbage collection overhead is unacceptable. The practitioner who understands Rust's model deeply—not just its syntax—is equipped to reason about the safety of both Rust and C code.

File Map

44-rust-and-memory-safety/
├── 00-overview.md                    ← This file
├── 01-memory-safety-problem.md
├── 02-ownership-model.md
├── 03-borrow-checker.md
├── 04-lifetimes.md
├── 05-unsafe-rust.md
├── 06-rust-in-linux-kernel.md
├── 07-rust-os-redox-theseus-tock.md
├── 08-rust-async-ecosystem.md
├── 09-rust-vs-c-comparison.md
├── 10-safe-concurrency.md
├── 11-rust-for-embedded.md
├── 12-rust-for-drivers.md
├── 13-memory-safety-bugs-prevented.md
├── 14-adoption-challenges.md
└── 15-c-interop-ffi.md

Cross-References

  • Section 10 (Synchronization): data races that Rust eliminates at compile time
  • Section 11 (Memory Management): allocator design, heap corruption patterns Rust prevents
  • Section 26 (Security): UAF and buffer overflow exploitation; Rust as mitigation
  • Section 40 (Failure History): historical memory safety disasters (Heartbleed, Shellshock, Morris Worm)
  • Section 41 (Modern Kernel Challenges): Rust-in-Linux current status
  • Section 42 (Future of Operating Systems): Rust OS landscape (Redox, Tock, Theseus)
  • Section 43 (Formal Verification): Rust type system as lightweight formal verification
  • Section 48 (Research Papers): Rust ownership paper, Tock OS paper, Theseus paper