Skip to content

All the Safeties ​

"Safety is a hot topic. What do we mean by 'safety'? How does it relate to correctness? What about security?"

Overview ​

Sean Parent's "All the Safeties" keynote (C++ on Sea 2023, CppNow 2023) provides a rigorous taxonomy of safety concepts in programming. The talk clarifies the often-conflated terms of safety, correctness, and security, and examines how these concepts apply to C++ specifically.

This is particularly relevant given the industry focus on memory safety and government recommendations about programming language choice.

Defining Terms ​

Safety vs Correctness ​

Safety and correctness are often confused but are distinct:

  • Safety: Nothing bad happens (no undefined behavior, no crashes)
  • Correctness: The right thing happens (meets specification)
cpp
// Safe but incorrect
int divide(int a, int b) {
    if (b == 0) return 0;  // Safe (no crash), but wrong answer
    return a / b;
}

// Correct but unsafe (in C++)
int divide(int a, int b) {
    return a / b;  // Correct when b != 0, undefined behavior otherwise
}

// Both safe and correct
std::optional<int> divide(int a, int b) {
    if (b == 0) return std::nullopt;  // Safe
    return a / b;  // Correct
}

Lamport's Definitions ​

Sean Parent references Leslie Lamport's formal definitions:

  • Safety property: Nothing bad ever happens
  • Liveness property: Something good eventually happens

A program must satisfy both to be correct.

The Safety Taxonomy ​

Memory Safety ​

Prevents invalid memory operations:

ViolationDescriptionExample
Buffer overflowAccess beyond boundsarr[n] where n >= size
Use-after-freeAccess freed memorydelete p; *p = 1;
Double freeFree same memory twicedelete p; delete p;
Null dereferenceAccess through null pointernullptr->method()
Uninitialized readRead before writeint x; return x;
cpp
// Memory unsafe code
void danger() {
    int* p = new int(42);
    delete p;
    *p = 10;  // Use-after-free!
}

// Memory safe alternative
void safe() {
    auto p = std::make_unique<int>(42);
    // p automatically managed
}

Type Safety ​

Ensures values are used according to their type:

ViolationDescriptionExample
Type confusionWrong type interpretationCasting int* to float*
Union misuseReading wrong union memberActive member mismatch
Aliasing violationIncompatible pointer accessStrict aliasing rules
cpp
// Type unsafe
void unsafe() {
    int i = 42;
    float* f = reinterpret_cast<float*>(&i);
    *f = 3.14f;  // Type punning, undefined behavior
}

// Type safe
void safe() {
    int i = 42;
    float f;
    std::memcpy(&f, &i, sizeof(f));  // Defined behavior
}

Thread Safety ​

Prevents data races and synchronization errors:

ViolationDescriptionExample
Data raceConcurrent unsynchronized accessTwo threads writing same variable
Race conditionTiming-dependent bugCheck-then-act patterns
DeadlockCircular wait for locksLock ordering violation
cpp
// Thread unsafe
int counter = 0;
void increment() { ++counter; }  // Data race if called from multiple threads

// Thread safe
std::atomic<int> counter{0};
void increment() { ++counter; }  // Atomic operation

Resource Safety ​

Ensures proper resource management:

ViolationDescriptionExample
Resource leakFailure to releaseMemory, file handles, sockets
Use-after-closeUse released resourceFile operations after close
Double releaseRelease same resource twiceDouble close
cpp
// Resource unsafe
void unsafe() {
    FILE* f = fopen("file.txt", "r");
    if (error_condition) return;  // Leak!
    fclose(f);
}

// Resource safe (RAII)
void safe() {
    std::ifstream f("file.txt");
    if (error_condition) return;  // Automatically closed
}

Exception Safety ​

Guarantees about state when exceptions are thrown:

LevelGuarantee
No-throwNever throws exceptions
StrongIf exception thrown, state unchanged
BasicInvariants maintained, no leaks
NoneNo guarantees
cpp
// Strong exception safety (copy-and-swap)
class Container {
public:
    Container& operator=(Container other) {  // Copy made
        swap(*this, other);                   // No-throw swap
        return *this;                         // Old data destroyed
    }
};

The Security Dimension ​

Security differs from safety:

  • Safety: Protection from accidental misuse
  • Security: Protection from intentional attack

Memory safety issues often become security vulnerabilities:

Safety IssueSecurity Exploit
Buffer overflowCode injection, ROP attacks
Use-after-freeArbitrary code execution
Integer overflowBuffer size miscalculation
Format stringInformation disclosure, code execution

Safety in C++ ​

Why C++ Is "Unsafe" ​

C++ provides:

  • Direct memory access
  • Pointer arithmetic
  • Manual memory management
  • Type casting
  • Undefined behavior by design

This enables performance but creates safety risks.

Making C++ Safer ​

1. Modern C++ Features ​

cpp
// Use smart pointers
std::unique_ptr<int> p = std::make_unique<int>(42);

// Use containers
std::vector<int> v;
v.at(i);  // Bounds-checked access

// Use string_view instead of char*
void process(std::string_view s);

// Use span for arrays
void process(std::span<int> data);

2. Static Analysis ​

Tools that find safety issues at compile time:

  • Clang-Tidy
  • PVS-Studio
  • Coverity
  • SonarQube

3. Runtime Sanitizers ​

Runtime detection of safety violations:

  • AddressSanitizer (ASan) — Memory errors
  • UndefinedBehaviorSanitizer (UBSan) — UB detection
  • ThreadSanitizer (TSan) — Data races
  • MemorySanitizer (MSan) — Uninitialized reads
bash
# Compile with sanitizers
clang++ -fsanitize=address,undefined -g program.cpp

4. Safe Coding Guidelines ​

Guidelines for Safe Code ​

1. Use RAII Everywhere ​

cpp
// BAD: Manual resource management
void bad() {
    int* p = new int(42);
    // ... might throw or return early ...
    delete p;
}

// GOOD: RAII
void good() {
    auto p = std::make_unique<int>(42);
    // Automatically cleaned up
}

2. Prefer Value Semantics ​

cpp
// BAD: Pointer relationships
class Node {
    Node* parent_;
    std::vector<Node*> children_;
};

// GOOD: Value/index relationships
class Tree {
    struct Node {
        size_t parent;
        std::vector<size_t> children;
    };
    std::vector<Node> nodes_;
};

3. Make Illegal States Unrepresentable ​

cpp
// BAD: Can be in invalid state
class Connection {
    Socket socket_;
    bool connected_ = false;
public:
    void send(Data d) {
        if (!connected_) throw ...;  // Runtime check
        socket_.send(d);
    }
};

// GOOD: Type system enforces validity
class DisconnectedConnection { /* ... */ };
class ConnectedConnection {
    Socket socket_;
public:
    void send(Data d) { socket_.send(d); }  // Always valid
};

4. Use const Liberally ​

cpp
// Const prevents accidental modification
void process(const std::vector<int>& data) {
    // Can't accidentally modify data
}

// Const member functions
class Widget {
public:
    int getValue() const { return value_; }  // Can't modify state
};

5. Validate at Boundaries ​

cpp
// Validate input at API boundaries
void processUserInput(std::string_view input) {
    if (input.size() > MAX_SIZE) {
        throw std::invalid_argument("Input too large");
    }
    if (!isValidFormat(input)) {
        throw std::invalid_argument("Invalid format");
    }
    // Now safe to process
    processValidated(input);
}

The Industry Context ​

Government Recommendations ​

Recent reports (NSA, CISA, etc.) recommend:

  • Using memory-safe languages where possible
  • Applying static analysis and sanitizers
  • Following secure coding guidelines

Statistics ​

Sean Parent cites statistics showing:

  • ~70% of Microsoft's CVEs are memory safety issues
  • Similar percentages at Google, Apple, Mozilla
  • Memory safety is the dominant source of vulnerabilities

The Path Forward ​

Options for improving C++ safety:

  1. Profiles — Subsets of C++ with safety guarantees
  2. Static analysis — Better tooling
  3. Runtime checks — Sanitizers, contracts
  4. Language evolution — Safer defaults, better abstractions
  5. Interop with safe languages — Rust, etc.

Summary Table ​

Safety TypeWhat It PreventsC++ Tools
MemoryBuffer overflow, use-after-freeSmart pointers, containers, ASan
TypeType confusion, aliasingstd::variant, std::any, concepts
ThreadData races, deadlocksstd::atomic, std::mutex, TSan
ResourceLeaks, double-freeRAII, smart pointers
ExceptionInconsistent stateStrong guarantee patterns

References ​

Primary Sources ​


"Safety is not optional. It's not a feature. It's a requirement." — Sean Parent