7 routes42 sections324 units

Unit 7.6 · about 4 h

47 / 324

Undefined behavior, sanitizers and debugger evidence

Undefined behavior has no promised result, including a crash. Tool evidence helps locate a violated contract without turning one observed output into a language rule.

  • measure

You can now

  • Identify bounds, lifetime, double-free, uninitialized-read and overflow hazards in C++20 code.
  • Build with warnings, debug symbols, ASan and UBSan, then interpret a report's failing access and lifetime history.
  • Locate a failure in a debugger, repair its root cause and preserve a regression test.

Covers

  • Out-of-bounds, lifetime, uninitialized access and undefined behavior
  • Compiler diagnostics, sanitizers and debugger evidence

Study, in this order

4 steps

  1. Undefined behavior
    cppreference

    Explanation; UB and optimization: Signed overflow, Access out of bounds and Uninitialized scalar

    Depth · Use the C++20 rules. Do not mistake displayed assembly for guaranteed behavior; the C++26 erroneous-behavior distinction is outside this unit.

  2. AddressSanitizer
    Clang AddressSanitizer and UndefinedBehaviorSanitizer

    Introduction; Usage and example_UseAfterFree.cc; Symbolizing the Reports; Additional Checks: Stack Use After Return and Memory leak detection; Limitations

  3. UndefinedBehaviorSanitizer
    Clang AddressSanitizer and UndefinedBehaviorSanitizer

    Introduction; Usage; Available checks, including signed-integer-overflow and the undefined group; Stack traces and report symbolization

    Depth · Skip runtime suppression, minimal runtime and newer overflow-behavior attributes.

  4. Debugger backtraces
    GDB manual: Backtraces

    8.2 Backtraces: bt, -full, frame numbering and optimized-out arguments

    Depth · Skip frame filters and backtrace formatting options.

Focus on

  • Bounds and lifetime are separate contracts. A stale pointer may still point inside mapped memory; reading it remains invalid.
  • ASan reports memory-access violations with allocation and deallocation stacks. UBSan checks selected undefined operations, including signed overflow; the default undefined group is not a check for every possible defect.
  • Preserve the command, compiler version, input and first report. Repair the invalid lifetime or index calculation rather than suppressing the report.
  • A debugger gives frames and state, not proof that a program has defined behavior. A clean dynamic-tool run covers only executed paths and enabled checks.

Reproduce closed-book

Run LLVM's example_UseAfterFree.cc with no extra arguments, using clang++ 21.1 -std=c++20 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer. Preserve the report: the checked run reports 'AddressSanitizer: heap-use-after-free' and 'READ of size 4', with the allocation, delete[] and subsequent read identified. The unsanitized program has undefined behavior and no particular output is guaranteed. Repair it using initialized owned storage that remains alive until after the read, and rerun with the same flags. In a separate program, add argc to numeric_limits<int>::max(): the same diagnostic build reports "signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'" on this Linux x86-64 toolchain. A default recoverable UBSan report need not produce a nonzero exit; use -fno-sanitize-recover=undefined for a failing test target. On a repaired -O0 -g build, run gdb, break main, run and bt to connect the source location to its frame.

Traps

  • ASan and UBSan do not generally detect uninitialized-value use. MemorySanitizer is a separate tool, not part of -fsanitize=address,undefined.
  • A data race is outside ASan/UBSan's usual checks. ThreadSanitizer requires a separate build and cannot be combined with AddressSanitizer.
  • A successful exit is not enough: UBSan can recover after reporting a violation, and an unexecuted bad path remains unchecked.

Practice

AlgoMaster Exercise: Memory Leaks

Exercises 1 to 5: a single heap id, an early-return path, an exception path with unique_ptr, a partially loaded vector and a linked list

Build each unfixed starter with clang++ -fsanitize=address -g and keep the LeakSanitizer report before repairing it; the fixed build must report nothing.

Self-test

  1. A use-after-free program prints the expected value. Is the read valid?

  2. What evidence distinguishes an invalid index from a freed-buffer access?

  3. Why can a UBSan-instrumented test exit zero despite a signed-overflow report?

  4. Does a clean ASan/UBSan run prove there are no uninitialized reads or data races?

Completion criterion

Stop when

You explain the failing access from its report, preserve a regression that fails on the seeded defect and passes after repair, and name what the enabled tools still cannot prove.

Mark passed, then open 7.7 Only mark this when you can do it without notes.

Leads to

Comes after

Resources for this unit

  • cppreference
    cppreference communitysupportreference
    Consult language lifetime, object model, value categories, memory model, atomics, containers and iterator invalidation while reading or modifying code.
  • Clang AddressSanitizer and UndefinedBehaviorSanitizer
    LLVMrequiredimplementation
    Compile seeded memory defects with AddressSanitizer and UndefinedBehaviorSanitizer, preserve the reports, repair the root cause and keep regression tests.
  • GDB manual: Backtraces
    GNU Projectsupportreference
    Use section 8.2 for frame numbers, bt, bt -full and optimized-out arguments when inspecting a native failure.

Also in this section

  • AlgoMaster C++
    AlgoMasterprimarycourse
    Use the sections on compilation, pointers and references, memory, RAII and smart pointers, copy and move, STL containers, templates, errors, concurrency and paired exercises. Skip competitive-programming breadth already covered in Algorithms.
  • C++ Core Guidelines
    Standard C++ Foundation communityrequiredreference
    Use the Philosophy, Interfaces, Resource management, Expressions, Containers and concurrency rule groups to review ownership and safety decisions in the native component.
  • pybind11 documentation
    pybind11 projectrequiredimplementation
    Follow First steps and NumPy support to expose one small native function with explicit shape, dtype, stride, ownership, GIL and exception behavior.
  • CMake tutorial
    CMakesupportimplementation
    Use the introductory steps for targets, libraries, testing and install rules. Produce one clean debug build and one optimized build without copied one-off commands.
  • Algorithmica - AoS and SoA
    Sergey Slotinsupportapplication
    Read the AoS and SoA experiment for a measured case where keeping each record together wins, the counterpart to a single-field scan.
  • `ld.so`: dynamic linker/loader
    Linux man-pages projectsupportreference
    Read DESCRIPTION, Dynamic string tokens and LD_DEBUG to distinguish link-time symbols from runtime shared-library discovery on Linux.
  • Memory Layout Transformations
    Intelsupportreference
    Read Overview and Topics through AOS Form and SOA Form for field-wise access and locality trade-offs. Skip Intel array-notation syntax and the LIBOR example.

Quick find

Search Michi

Type to search every route, section, unit, topic and resource.

Enter opens the first result Esc closes

Reference

Keyboard shortcuts

n
Continue to the first unit not passed
j / k
Next / previous unit
1 2 3
Toggle learned, practised, passed
/
Open search
?
Show this help
Esc
Close open panels