From 4c9d5989a41089328838ce6268b25a8a226353c6 Mon Sep 17 00:00:00 2001 From: wizardengineer Date: Wed, 1 Apr 2026 14:01:32 -0400 Subject: [PATCH] Add modern-cpp plugin for C++20/23/26 best practices Modern C++ skill guiding Claude toward modern idioms with a security emphasis. Mirrors modern-python in spirit but focuses on language standards rather than toolchain. Features tiered by practical usability: - Tier 1 (Use Today): C++20/23 features with solid compiler support - Tier 2 (Deploy Now): Compiler hardening, sanitizers, hardened libc++ - Tier 3 (Plan For): C++26 reflection - Tier 4 (Watch): Contracts, std::execution Includes SKILL.md entry point + 6 reference docs: - anti-patterns.md (30+ legacy-to-modern swaps) - cpp20-features.md (concepts, ranges, span, format, coroutines) - cpp23-features.md (expected, print, deducing this, flat_map) - cpp26-features.md (reflection, contracts, memory safety) - compiler-hardening.md (flags, sanitizers, hardened libc++) - safe-idioms.md (security patterns by vulnerability class) Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude-plugin/marketplace.json | 11 + .codex/skills/modern-cpp | 1 + CODEOWNERS | 1 + README.md | 1 + plugins/modern-cpp/.claude-plugin/plugin.json | 10 + plugins/modern-cpp/README.md | 41 +++ plugins/modern-cpp/skills/modern-cpp/SKILL.md | 187 +++++++++++ .../modern-cpp/references/anti-patterns.md | 80 +++++ .../references/compiler-hardening.md | 241 ++++++++++++++ .../modern-cpp/references/cpp20-features.md | 268 ++++++++++++++++ .../modern-cpp/references/cpp23-features.md | 269 ++++++++++++++++ .../modern-cpp/references/cpp26-features.md | 238 ++++++++++++++ .../modern-cpp/references/safe-idioms.md | 299 ++++++++++++++++++ 13 files changed, 1647 insertions(+) create mode 120000 .codex/skills/modern-cpp create mode 100644 plugins/modern-cpp/.claude-plugin/plugin.json create mode 100644 plugins/modern-cpp/README.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/SKILL.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/anti-patterns.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/compiler-hardening.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/cpp20-features.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/cpp23-features.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/cpp26-features.md create mode 100644 plugins/modern-cpp/skills/modern-cpp/references/safe-idioms.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 9d5e0f7..25c368a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -383,6 +383,17 @@ "url": "https://github.com/trailofbits" }, "source": "./plugins/dimensional-analysis" + }, + { + "name": "modern-cpp", + "version": "1.0.0", + "description": "Modern C++ best practices (C++20/23/26). Use when writing C++ code, creating new C++ projects, or modernizing legacy C++ patterns.", + "author": { + "name": "Julius Alexandre", + "email": "opensource@trailofbits.com", + "url": "https://github.com/trailofbits" + }, + "source": "./plugins/modern-cpp" } ] } diff --git a/.codex/skills/modern-cpp b/.codex/skills/modern-cpp new file mode 120000 index 0000000..7e34b3b --- /dev/null +++ b/.codex/skills/modern-cpp @@ -0,0 +1 @@ +../../plugins/modern-cpp/skills/modern-cpp \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 64c24fb..cf231c4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,6 +22,7 @@ /plugins/git-cleanup/ @hbrodin @dguido /plugins/insecure-defaults/ @dariushoule @dguido /plugins/let-fate-decide/ @tob-scott-a @dguido +/plugins/modern-cpp/ @wizardengineer @dguido /plugins/modern-python/ @Ninja3047 @dguido /plugins/mutation-testing/ @bohendo @dguido /plugins/property-based-testing/ @hbrodin @dguido diff --git a/README.md b/README.md index a7ca3a4..1ead2ac 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ cd /path/to/parent # e.g., if repo is at ~/projects/skills, be in ~/projects | [gh-cli](plugins/gh-cli/) | Intercept GitHub URL fetches and redirect to the authenticated `gh` CLI | | [git-cleanup](plugins/git-cleanup/) | Safely clean up git worktrees and local branches with gated confirmation workflow | | [let-fate-decide](plugins/let-fate-decide/) | Draw Tarot cards using cryptographic randomness to add entropy to vague planning | +| [modern-cpp](plugins/modern-cpp/) | Modern C++ best practices (C++20/23/26) with compiler hardening and safe idioms | | [modern-python](plugins/modern-python/) | Modern Python tooling and best practices with uv, ruff, and pytest | | [seatbelt-sandboxer](plugins/seatbelt-sandboxer/) | Generate minimal macOS Seatbelt sandbox configurations | | [second-opinion](plugins/second-opinion/) | Run code reviews using external LLM CLIs (OpenAI Codex, Google Gemini) on changes, diffs, or commits. Bundles Codex's built-in MCP server. | diff --git a/plugins/modern-cpp/.claude-plugin/plugin.json b/plugins/modern-cpp/.claude-plugin/plugin.json new file mode 100644 index 0000000..b105b97 --- /dev/null +++ b/plugins/modern-cpp/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "modern-cpp", + "version": "1.0.0", + "description": "Modern C++ best practices (C++20/23/26). Use when writing C++ code, creating new C++ projects, or modernizing legacy C++ patterns.", + "author": { + "name": "Julius Alexandre", + "email": "opensource@trailofbits.com", + "url": "https://github.com/trailofbits" + } +} diff --git a/plugins/modern-cpp/README.md b/plugins/modern-cpp/README.md new file mode 100644 index 0000000..bf16cbd --- /dev/null +++ b/plugins/modern-cpp/README.md @@ -0,0 +1,41 @@ +# modern-cpp + +Modern C++ best practices plugin for Claude Code, guiding AI-assisted development toward C++20/23/26 idioms with a security emphasis from Trail of Bits. + +## When to Use + +- Writing new C++ code (functions, classes, libraries) +- Modernizing legacy C++ patterns (pre-C++20) +- Working on security-critical C++ code +- Reviewing C++ code for modern idiom adoption +- Choosing between legacy and modern approaches to a problem + +## What It Covers + +### Language Features (Tiered by Usability) + +| Tier | Standard | What | +|------|----------|------| +| Use Today | C++20 | Concepts, ranges, `std::span`, `std::format`, coroutines, `<=>` | +| Use Today | C++23 | `std::expected`, `std::print`, deducing `this`, `std::flat_map` | +| Deploy Now | Any | Compiler hardening flags, sanitizers, hardened libc++ | +| Plan For | C++26 | Reflection (eliminates serialization boilerplate and code generators) | +| Watch | C++26 | Contracts, `std::execution` (promising but needs compiler maturity) | + +### Security + +- Compiler hardening flags (GCC + Clang, per OpenSSF guidelines) +- Hardened libc++ modes (bounds-checking with ~0.3% overhead) +- Sanitizer setup (ASan, UBSan, TSan, MSan) +- Safe idioms organized by vulnerability class (memory, type, integer, concurrency) + +### Anti-Patterns + +30+ legacy-to-modern pattern replacements with rationale, covering memory management, type safety, error handling, concurrency, and metaprogramming. + +## Installation + +``` +/plugin marketplace add trailofbits/skills +/plugin install modern-cpp +``` diff --git a/plugins/modern-cpp/skills/modern-cpp/SKILL.md b/plugins/modern-cpp/skills/modern-cpp/SKILL.md new file mode 100644 index 0000000..eeb43ab --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/SKILL.md @@ -0,0 +1,187 @@ +--- +name: modern-cpp +description: Guides C++ code toward modern idioms (C++20/23/26). Use when writing new C++ code, modernizing legacy patterns, or working on security-critical C++. Replaces raw pointers with smart pointers, SFINAE with concepts, printf with std::print, error codes with std::expected. +--- + +# Modern C++ + +Guide for writing modern C++ using C++20, C++23, and C++26 idioms. Focuses on patterns that eliminate vulnerability classes and reduce boilerplate, with a security emphasis from Trail of Bits. + +## When to Use This Skill + +- Writing new C++ functions, classes, or libraries +- Modernizing existing C++ code (pre-C++20 patterns) +- Choosing between legacy and modern approaches +- Working on security-critical or safety-sensitive C++ +- Reviewing C++ code for modern idiom adoption + +## When NOT to Use This Skill + +- **User explicitly requires older standard**: Respect constraints (embedded, legacy ABI) +- **Pure C code**: This skill is C++-specific +- **Build system questions**: CMake, Meson, Bazel configuration is out of scope +- **Non-C++ projects**: Mixed codebases where C++ isn't primary + +## Anti-Patterns to Avoid + +| Avoid | Use Instead | Why | +|-------|-------------|-----| +| `new`/`delete` | `std::make_unique`, `std::make_shared` | Eliminates leaks, double-free | +| Raw owning pointers | `std::unique_ptr`, `std::shared_ptr` | RAII ownership semantics | +| C arrays (`int arr[N]`) | `std::array` | Bounds-aware, value semantics | +| Pointer + length params | `std::span` | Non-owning, bounds-checkable | +| `printf` / `sprintf` | `std::format`, `std::print` | Type-safe, no buffer overflow | +| C-style casts `(int)x` | `static_cast(x)` | Explicit intent, auditable | +| `#define` constants | `constexpr` variables | Scoped, typed, debuggable | +| SFINAE / `enable_if` | Concepts + `requires` | Readable constraints and errors | +| Error codes + out params | `std::expected` | Composable, type-safe errors | +| `union` | `std::variant` | Type-safe, no silent UB | +| Raw `mutex.lock()/unlock()` | `std::scoped_lock` | Exception-safe, no deadlocks | +| `std::thread` | `std::jthread` | Auto-join, stop token support | +| `assert()` macro | `contract_assert` (C++26) | Visible to tooling, configurable | +| Manual CRTP | Deducing `this` (C++23) | Simpler, no template boilerplate | +| Macro code generation | Reflection (C++26) | Zero-overhead, composable | + +See [anti-patterns.md](./references/anti-patterns.md) for the full table (30+ patterns). + +## Decision Tree + +``` +What are you doing? +| ++-- Writing new C++ code? +| +-- Use modern idioms by default (C++20/23) +| +-- Choose the newest standard your compiler supports +| +-- See Feature Tiers below +| ++-- Modernizing existing code? +| +-- Start with Tier 1 (C++20/23) replacements +| +-- Prioritize by security impact (memory > types > style) +| +-- See anti-patterns.md for the migration table +| ++-- Security-critical code? +| +-- Enable compiler hardening flags (see below) +| +-- Enable hardened libc++ mode +| +-- Run sanitizers in CI +| +-- See safe-idioms.md and compiler-hardening.md +| ++-- Using C++26 features? + +-- Reflection: YES, plan for it (GCC 16+) + +-- Contracts: cautiously, for new API boundaries + +-- std::execution: wait for ecosystem maturity + +-- See cpp26-features.md +``` + +## Feature Tiers + +Features are ranked by **practical usability today**, not by standard version. + +### Tier 1: Use Today (C++20/23, solid compiler support) + +| Feature | Replaces | Standard | +|---------|----------|----------| +| Concepts + `requires` | SFINAE, `enable_if` | C++20 | +| Ranges + views | Raw iterator loops | C++20 | +| `std::span` | Pointer + length | C++20 | +| `std::format` | `sprintf`, iostream chains | C++20 | +| Three-way comparison `<=>` | Manual comparison operators | C++20 | +| `std::jthread` | `std::thread` + manual join | C++20 | +| Designated initializers | Positional struct init | C++20 | +| `std::expected` | Error codes, exceptions at boundaries | C++23 | +| `std::print` / `std::println` | `printf`, `std::cout <<` | C++23 | +| Deducing `this` | CRTP, const/non-const duplication | C++23 | +| `std::flat_map` | `std::map` for read-heavy use | C++23 | +| Monadic `std::optional` | Nested if-checks on optionals | C++23 | + +See [cpp20-features.md](./references/cpp20-features.md) and [cpp23-features.md](./references/cpp23-features.md). + +### Tier 2: Deploy Now (no standard bump needed) + +These improve safety without changing your C++ standard version: + +- **Compiler hardening flags** — `-D_FORTIFY_SOURCE=3`, `-fstack-protector-strong`, `-ftrivial-auto-var-init=zero` +- **Hardened libc++** — `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` for ~0.3% overhead bounds-checking +- **Sanitizers in CI** — ASan + UBSan as minimum; TSan for concurrent code +- **Warning flags** — `-Wall -Wextra -Wpedantic -Werror` + +See [compiler-hardening.md](./references/compiler-hardening.md). + +### Tier 3: Plan For (C++26, worth restructuring around) + +**Reflection** is the single most transformative C++26 feature. It eliminates: +- Serialization boilerplate (one generic function replaces per-struct `to_json`) +- Code generators (protobuf codegen, Qt MOC) +- Macro-based registration and enum-to-string hacks + +GCC 16 (April 2026) has reflection merged. Plan new code to benefit from it. + +### Tier 4: Watch (C++26, needs maturation) + +- **Contracts** (`pre`/`post`/`contract_assert`) — Better than `assert()`, but no virtual function support and limited compiler support. Adopt cautiously for new API boundaries. +- **std::execution** (senders/receivers) — Powerful async framework, but steep learning curve, no scheduler ships with it, and poor documentation. Wait for ecosystem maturity. + +See [cpp26-features.md](./references/cpp26-features.md). + +## Compiler Hardening Quick Reference + +### Essential Flags (GCC + Clang) + +``` +-Wall -Wextra -Wpedantic -Werror +-D_FORTIFY_SOURCE=3 +-fstack-protector-strong +-fstack-clash-protection +-ftrivial-auto-var-init=zero +-fPIE -pie +-Wl,-z,relro,-z,now +``` + +### Clang-Specific + +``` +-Wunsafe-buffer-usage +``` + +### Hardened libc++ (Clang/libc++ only) + +``` +-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST +``` + +Google deployed this across Chrome and their server fleet: ~0.3% overhead, 1000+ bugs found, 30% reduction in production segfaults. + +See [compiler-hardening.md](./references/compiler-hardening.md) for the full guide. + +## Rationalizations to Reject + +| Rationalization | Why It's Wrong | +|----------------|----------------| +| "It compiles without warnings" | Warnings depend on which flags you enable. Add `-Wall -Wextra -Wpedantic`. | +| "ASan is too slow for production" | Use GWP-ASan for sampling-based production detection (~0% overhead). | +| "We only use safe containers" | Iterator invalidation and unchecked `optional` access are still exploitable. | +| "Smart pointers are slower" | `std::unique_ptr` has zero overhead vs raw pointers. Measure before claiming. | +| "Our code doesn't have memory bugs" | Google found 1000+ bugs when enabling hardened libc++. So did everyone else. | +| "C++26 features aren't available yet" | C++20/23 features are. Hardening flags work on any standard. Start there. | +| "Modern C++ is harder to read" | `std::expected` is more readable than checking error codes across 5 out-params. | + +## Best Practices Checklist + +- [ ] Use smart pointers for ownership, raw pointers only for non-owning observation +- [ ] Prefer `std::span` over pointer + length for function parameters +- [ ] Use `std::expected` for functions that can fail with typed errors +- [ ] Constrain templates with concepts, not SFINAE +- [ ] Enable compiler hardening flags and hardened libc++ in all builds +- [ ] Run ASan + UBSan in CI; add TSan for concurrent code +- [ ] Use `constexpr` / `consteval` where possible (UB-free by design) +- [ ] Mark functions `[[nodiscard]]` when ignoring the return value is likely a bug +- [ ] Prefer value semantics; use `std::variant` over `union`, `enum class` over `enum` +- [ ] Initialize all variables at declaration + +## Read Next + +- [anti-patterns.md](./references/anti-patterns.md) — Full legacy-to-modern migration table (30+ patterns) +- [cpp20-features.md](./references/cpp20-features.md) — Concepts, ranges, span, format, coroutines +- [cpp23-features.md](./references/cpp23-features.md) — expected, print, deducing this, flat_map +- [cpp26-features.md](./references/cpp26-features.md) — Reflection, contracts, memory safety improvements +- [compiler-hardening.md](./references/compiler-hardening.md) — Flags, sanitizers, hardened libc++ +- [safe-idioms.md](./references/safe-idioms.md) — Security patterns by vulnerability class diff --git a/plugins/modern-cpp/skills/modern-cpp/references/anti-patterns.md b/plugins/modern-cpp/skills/modern-cpp/references/anti-patterns.md new file mode 100644 index 0000000..4db422b --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/anti-patterns.md @@ -0,0 +1,80 @@ +# Anti-Patterns: Legacy to Modern C++ + +Comprehensive reference of legacy C++ patterns and their modern replacements. Each entry explains WHY the modern version is better — not just that it exists. + +## Memory Management + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| `new T` / `delete p` | `std::make_unique()` | C++14 | Eliminates memory leaks, double-free; exception-safe construction | +| `new T[]` / `delete[] p` | `std::vector` or `std::make_unique(n)` | C++14 | Automatic sizing, bounds-aware with `.at()` | +| Raw owning `T*` | `std::unique_ptr` | C++11 | RAII ownership; zero overhead vs raw pointer | +| Raw shared `T*` | `std::shared_ptr` via `std::make_shared` | C++11 | Reference-counted lifetime; thread-safe refcount | +| C arrays `int arr[N]` | `std::array` | C++11 | Value semantics, `.size()`, no decay to pointer | +| `malloc`/`free` | Containers or smart pointers | C++11 | Type-safe, exception-safe, RAII | +| `memcpy(dst, src, n)` | `std::copy(src, src+n, dst)` or container assignment | C++11 | Type-safe, works with non-trivial types | +| `memset(buf, 0, n)` | Value initialization or `std::fill` | C++11 | No risk of zeroing non-trivially-constructible types | +| Pointer + length parameter pairs | `std::span` | C++20 | Carries size, bounds-checkable with hardened mode | +| `const char*` for non-owning strings | `std::string_view` | C++17 | Carries length, no null-terminator assumption | +| Nullable `T*` for optional values | `std::optional` | C++17 | Explicit intent, no null dereference risk | + +## Type Safety + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| C-style cast `(int)x` | `static_cast(x)` | C++11 | Explicit intent, greppable, won't silently reinterpret | +| `reinterpret_cast` for type punning | `std::bit_cast(x)` | C++20 | Defined behavior, `constexpr`-compatible | +| `union` for variants | `std::variant` | C++17 | Type-safe access via `std::visit`, no silent UB | +| `void*` type erasure | `std::any`, `std::variant`, or templates | C++17 | Type-safe, no manual casting | +| Plain `enum` | `enum class` | C++11 | Scoped, no implicit int conversion | +| `NULL` or `0` | `nullptr` | C++11 | Unambiguous null pointer, no overload confusion | +| Implicit single-arg constructors | Mark `explicit` | C++11 | Prevents surprising implicit conversions | +| Unchecked return values | `[[nodiscard]]` | C++17 | Compiler warns when return value is ignored | +| `= delete` without message | `= delete("reason")` | C++26 | Documents why the overload is forbidden | + +## Error Handling + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| Error codes + out-params | `std::expected` | C++23 | Composable with `.and_then()`, `.transform()`, `.or_else()` | +| `assert()` macro | `contract_assert` | C++26 | Visible to tooling, configurable enforcement modes | +| Unchecked `errno` | `std::expected` or exceptions | C++23 | Forces caller to handle the error path | +| Throwing in constructor for expected failures | Factory returning `std::expected` | C++23 | No exception overhead for expected failure paths | + +## I/O and Formatting + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| `printf` / `sprintf` | `std::format` / `std::print` | C++20/23 | Type-safe, no format string vulnerabilities | +| `snprintf` for string building | `std::format` | C++20 | Returns `std::string`, no buffer management | +| `std::cout << a << b << c` | `std::println("{} {} {}", a, b, c)` | C++23 | Readable, no stream state issues, faster | + +## Concurrency + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| `mutex.lock()` / `mutex.unlock()` | `std::scoped_lock` | C++17 | Exception-safe, supports multiple mutexes (no deadlock) | +| `std::thread` + manual `.join()` | `std::jthread` | C++20 | Auto-joins on destruction, supports stop tokens | +| `volatile` for thread sync | `std::atomic` | C++11 | Actually guarantees atomic operations and memory ordering | +| Manual condition variable notify | `std::latch`, `std::barrier` | C++20 | Higher-level, less error-prone synchronization | +| Hand-rolled atomic CAS loops | `std::atomic::fetch_max/fetch_min` | C++26 | Standard, correct, optimized | + +## Metaprogramming + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| `std::enable_if` / SFINAE | Concepts + `requires` | C++20 | Readable constraints, clear error messages | +| CRTP for static polymorphism | Deducing `this` | C++23 | No template boilerplate, works with lambdas | +| Macros for code generation | Static reflection | C++26 | Zero-overhead, composable, type-safe | +| `std::tuple_element` for pack access | Pack indexing `T...[N]` | C++26 | Direct access, no recursive template machinery | +| `std::function` for non-owning callbacks | `std::function_ref` | C++26 | No allocation, passable in registers | + +## Containers + +| Avoid | Use Instead | Standard | Why | +|-------|-------------|----------|-----| +| `std::map` for read-heavy lookups | `std::flat_map` | C++23 | Cache-friendly, 8-17x faster lookup for small-medium maps | +| `boost::static_vector` | `std::inplace_vector` | C++26 | Standard, no Boost dependency, fallible API | +| `std::function` (when non-owning) | `std::function_ref` | C++26 | Zero allocation overhead | +| Raw pointer polymorphism | `std::indirect`, `std::polymorphic` | C++26 | Value semantics, copyable, no manual lifetime management | +| Manual iterator loops | `std::ranges::` algorithms + views | C++20 | Composable, lazy, no off-by-one | diff --git a/plugins/modern-cpp/skills/modern-cpp/references/compiler-hardening.md b/plugins/modern-cpp/skills/modern-cpp/references/compiler-hardening.md new file mode 100644 index 0000000..c3daf16 --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/compiler-hardening.md @@ -0,0 +1,241 @@ +# Compiler Hardening + +Security-focused compiler and linker configuration for C++ projects. Based on the [OpenSSF Compiler Options Hardening Guide](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html) and Trail of Bits recommendations. + +## Essential Compiler Flags + +### Warnings + +| Flag | Purpose | GCC | Clang | +|------|---------|-----|-------| +| `-Wall` | Core warnings | Yes | Yes | +| `-Wextra` | Additional warnings | Yes | Yes | +| `-Wpedantic` | Strict ISO compliance | Yes | Yes | +| `-Werror` | Treat warnings as errors | Yes | Yes | +| `-Wconversion` | Implicit narrowing conversions | Yes | Yes | +| `-Wsign-conversion` | Signed/unsigned conversion | Yes | Yes | +| `-Wformat=2` | Format string issues | Yes | Yes | +| `-Wunsafe-buffer-usage` | Raw pointer arithmetic | No | Yes | + +### Stack Protection + +| Flag | Purpose | GCC | Clang | +|------|---------|-----|-------| +| `-fstack-protector-strong` | Stack buffer overflow detection | Yes | Yes | +| `-fstack-clash-protection` | Prevent stack clash attacks | Yes | Yes | + +### Fortification + +``` +-D_FORTIFY_SOURCE=3 +``` + +Adds runtime bounds checks to `memcpy`, `strcpy`, `sprintf`, and other libc functions. Level 3 (GCC 12+, Clang 16+) provides the most coverage. + +**Warning:** This flag is silently ignored if optimization is disabled (`-O0`). Always verify it's active: + +```bash +# Check that _FORTIFY_SOURCE is defined in preprocessor output +echo '#include ' | gcc -E -dM -D_FORTIFY_SOURCE=3 -O2 - | grep FORTIFY +``` + +Trail of Bits found [20+ projects on GitHub](https://blog.trailofbits.com/2023/04/20/typos-that-omit-security-features-and-how-to-test-for-them/) where `_FORTIFY_SOURCE` was misspelled (e.g., `-FORTIFY_SOURCE=2` instead of `-D_FORTIFY_SOURCE=2`). + +### Auto-Initialization + +``` +-ftrivial-auto-var-init=zero +``` + +Zero-initializes all local variables that would otherwise be uninitialized. Eliminates information leaks from stack variables at minimal performance cost. + +### Position-Independent Code + +``` +-fPIE -pie # for executables +-fPIC -shared # for shared libraries +``` + +Required for ASLR to work effectively. + +### Linker Hardening + +| Flag | Purpose | +|------|---------| +| `-Wl,-z,relro` | Read-only relocations (partial RELRO) | +| `-Wl,-z,now` | Immediate binding (full RELRO, protects GOT) | +| `-Wl,-z,noexecstack` | Non-executable stack | + +### Control Flow Integrity + +``` +-fcf-protection=full # GCC/Clang on x86 (Intel CET) +``` + +## Hardened libc++ (Clang/libc++) + +libc++ provides hardening modes that add bounds-checking to standard containers with minimal overhead. + +### Modes + +| Mode | Flag | What It Checks | Overhead | +|------|------|---------------|----------| +| Fast | `_LIBCPP_HARDENING_MODE_FAST` | Bounds on `[]`, `front()`, `back()` | ~0.3% | +| Extensive | `_LIBCPP_HARDENING_MODE_EXTENSIVE` | Fast + iterator validity, internal invariants | Higher | +| Debug | `_LIBCPP_HARDENING_MODE_DEBUG` | All checks including O(n) validations | Significant | + +### How to Enable + +``` +-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST +``` + +### Recommendation + +- **All builds**: Enable `_FAST` mode — Google measured 0.3% overhead across their fleet +- **Security-critical code**: Enable `_EXTENSIVE` mode +- **Development/testing**: Enable `_DEBUG` mode + +### Google's Results + +Deployed across Chrome (2022) and entire server fleet: +- ~0.3% average performance overhead +- 1000+ previously undetected bugs found +- 30% reduction in production segfault rate + +## Sanitizers + +Runtime instrumentation for detecting bugs that static analysis misses. + +### AddressSanitizer (ASan) + +Detects: heap/stack buffer overflow, use-after-free, double-free, memory leaks. + +``` +-fsanitize=address -fno-omit-frame-pointer +``` + +Overhead: ~2x slowdown, ~3x memory. Use in CI, not production. + +### UndefinedBehaviorSanitizer (UBSan) + +Detects: signed integer overflow, null pointer dereference, division by zero, misaligned access, out-of-bounds array access. + +``` +-fsanitize=undefined -fno-sanitize-recover=all +``` + +Overhead: minimal (~5-10%). Safe for most CI runs. + +### ThreadSanitizer (TSan) + +Detects: data races, lock-order inversions, deadlocks. + +``` +-fsanitize=thread +``` + +Overhead: ~5-15x slowdown. Use for concurrent code testing. + +### MemorySanitizer (MSan) + +Detects: reads of uninitialized memory. + +``` +-fsanitize=memory -fno-omit-frame-pointer +``` + +Overhead: ~3x slowdown. Clang-only. Requires all linked code to be instrumented. + +### Combining Sanitizers + +| Combination | Compatible? | +|-------------|-------------| +| ASan + UBSan | Yes — recommended default | +| ASan + TSan | **No** — mutually exclusive | +| ASan + MSan | **No** — mutually exclusive | +| TSan + UBSan | Yes | +| MSan + UBSan | Yes | + +### CI Integration + +Recommended CI matrix: + +``` +1. Build with ASan + UBSan → run all tests +2. Build with TSan → run concurrency tests +3. (Optional) Build with MSan → run all tests (Clang only) +``` + +### Production: GWP-ASan + +For production environments where full ASan is too expensive, use GWP-ASan — a sampling-based allocator that detects heap bugs with near-zero overhead. + +- Enabled by default in Chrome, Android, Apple platforms +- Catches use-after-free and buffer overflow in production +- See [Trail of Bits guide to GWP-ASan](https://blog.trailofbits.com/2025/12/16/use-gwp-asan-to-detect-exploits-in-production-environments/) + +## Verifying Hardening + +Don't trust that flags are applied — verify the binary: + +### checksec + +```bash +checksec --file=./my_binary +``` + +Checks: RELRO, stack canary, NX, PIE, FORTIFY. + +### readelf + +```bash +# Check for RELRO +readelf -l ./my_binary | grep GNU_RELRO + +# Check for stack canary symbols +readelf -s ./my_binary | grep __stack_chk + +# Check for FORTIFY +readelf -s ./my_binary | grep _chk +``` + +### Compiler Explorer + +For quick verification during development, compile on [Compiler Explorer](https://godbolt.org/) and inspect the generated assembly for canary checks and fortified function calls. + +## Complete Hardened Compilation Example + +```bash +# GCC +g++ -std=c++23 \ + -Wall -Wextra -Wpedantic -Werror -Wconversion \ + -D_FORTIFY_SOURCE=3 -O2 \ + -fstack-protector-strong -fstack-clash-protection \ + -ftrivial-auto-var-init=zero \ + -fcf-protection=full \ + -fPIE -pie \ + -Wl,-z,relro,-z,now -Wl,-z,noexecstack \ + main.cpp -o my_binary + +# Clang (with hardened libc++) +clang++ -std=c++23 -stdlib=libc++ \ + -Wall -Wextra -Wpedantic -Werror -Wconversion \ + -Wunsafe-buffer-usage \ + -D_FORTIFY_SOURCE=3 -O2 \ + -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST \ + -fstack-protector-strong -fstack-clash-protection \ + -ftrivial-auto-var-init=zero \ + -fcf-protection=full \ + -fPIE -pie \ + -Wl,-z,relro,-z,now -Wl,-z,noexecstack \ + main.cpp -o my_binary +``` + +## References + +- [OpenSSF Compiler Options Hardening Guide](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html) +- [libc++ Hardening Modes](https://libcxx.llvm.org/Hardening.html) +- [Trail of Bits: Typos That Omit Security Features](https://blog.trailofbits.com/2023/04/20/typos-that-omit-security-features-and-how-to-test-for-them/) +- [Trail of Bits: Use GWP-ASan in Production](https://blog.trailofbits.com/2025/12/16/use-gwp-asan-to-detect-exploits-in-production-environments/) +- [Google: Retrofitting Spatial Safety](https://security.googleblog.com/2024/11/retrofitting-spatial-safety-to-hundreds.html) diff --git a/plugins/modern-cpp/skills/modern-cpp/references/cpp20-features.md b/plugins/modern-cpp/skills/modern-cpp/references/cpp20-features.md new file mode 100644 index 0000000..2e9ad0a --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/cpp20-features.md @@ -0,0 +1,268 @@ +# C++20 Features (Default Practice) + +These features are mature, well-supported (GCC 12+, Clang 14+, MSVC 17.0+), and should be the default way to write C++. + +## Concepts + +Constrain templates with readable, declarative requirements. + +### What They Replace + +SFINAE and `std::enable_if` — the arcane template tricks that produced unreadable error messages. + +### Standard Concepts + +Use concepts from `` before writing custom ones: + +```cpp +#include + +template +T gcd(T a, T b) { return b == 0 ? a : gcd(b, a % b); } + +template F> +void apply(F&& fn, int value) { fn(value); } +``` + +Key standard concepts: `std::integral`, `std::floating_point`, `std::invocable`, `std::copyable`, `std::movable`, `std::regular`, `std::predicate`, `std::ranges::range`. + +### Custom Concepts + +```cpp +template +concept Serializable = requires(T obj, std::ostream& os) { + { obj.serialize(os) } -> std::same_as; + { T::deserialize(os) } -> std::same_as; +}; + +void save(const Serializable auto& obj, std::ostream& os) { + obj.serialize(os); +} +``` + +### requires Clauses vs concept Keyword + +- **`concept`**: reusable, named constraint — define when used in 2+ places +- **`requires`**: inline, one-off constraint + +```cpp +// One-off: use requires +template + requires (sizeof(T) <= 16) +void small_copy(T value); + +// Reusable: define a concept +template +concept SmallType = sizeof(T) <= 16; +``` + +### Before / After + +```cpp +// BEFORE: SFINAE — cryptic, fragile +template >> +T square(T x) { return x * x; } +// Error: "no matching function for call to 'square'" + 50 lines of template noise + +// AFTER: concepts — readable, clear errors +template + requires std::is_arithmetic_v +T square(T x) { return x * x; } +// Error: "constraint 'is_arithmetic_v' not satisfied" +``` + +## Ranges + +Composable, lazy algorithms that operate on range objects instead of iterator pairs. + +### Key Patterns + +```cpp +#include +#include + +std::vector data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +// View pipeline — lazy, no intermediate allocations +auto result = data + | std::views::filter([](int x) { return x % 2 == 0; }) + | std::views::transform([](int x) { return x * x; }); + +// Range algorithms — accept ranges directly +std::ranges::sort(data); +auto it = std::ranges::find(data, 42); +auto [mn, mx] = std::ranges::minmax(data); +``` + +To materialize a lazy view into a concrete container, use `std::ranges::to<>()` from C++23 — see [cpp23-features.md](./cpp23-features.md). + +### Useful Views + +| View | Purpose | +|------|---------| +| `views::filter` | Keep elements matching predicate | +| `views::transform` | Apply function to each element | +| `views::take(n)` | First n elements | +| `views::drop(n)` | Skip first n elements | +| `views::split(delim)` | Split range on delimiter | +| `views::join` | Flatten nested ranges | +| `views::enumerate` | Pairs of (index, element) — C++23 | +| `views::zip` | Zip multiple ranges — C++23 | +| `views::chunk(n)` | Group into chunks of n — C++23 | +| `views::concat` | Concatenate ranges — C++26 | + +## std::span + +Non-owning view over contiguous memory. Replaces pointer + length pairs. + +```cpp +#include + +// BEFORE: dangerous — no size information +void process(int* data, size_t len); + +// AFTER: carries size, bounds-checkable +void process(std::span data) { + for (auto& val : data) { /* safe iteration */ } + data[0]; // unchecked (fast) + data.at(0); // bounds-checked with hardened libc++ +} + +// Works with any contiguous container +std::vector vec = {1, 2, 3}; +std::array arr = {4, 5, 6}; +int c_arr[] = {7, 8, 9}; + +process(vec); // implicit conversion +process(arr); // implicit conversion +process(c_arr); // implicit conversion +``` + +Use `std::span` for read-only views. + +## std::format + +Type-safe string formatting, replacing `sprintf` and iostream chains. + +```cpp +#include + +std::string s = std::format("Hello, {}!", name); +std::string t = std::format("{:>10.2f}", 3.14159); // right-aligned, 2 decimal +std::string u = std::format("{:#x}", 255); // 0xff +std::string v = std::format("{:%Y-%m-%d}", std::chrono::system_clock::now()); +``` + +### Custom Formatters + +```cpp +template <> +struct std::formatter { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + auto format(const Point& p, format_context& ctx) const { + return std::format_to(ctx.out(), "({}, {})", p.x, p.y); + } +}; + +// Now works: +std::println("Position: {}", my_point); +``` + +## Three-Way Comparison (<=>) + +Generate all comparison operators from one declaration. + +```cpp +#include + +struct Point { + int x, y; + auto operator<=>(const Point&) const = default; + // Generates: ==, !=, <, >, <=, >= +}; +``` + +For custom ordering: +```cpp +struct Version { + int major, minor, patch; + std::strong_ordering operator<=>(const Version& other) const { + if (auto cmp = major <=> other.major; cmp != 0) return cmp; + if (auto cmp = minor <=> other.minor; cmp != 0) return cmp; + return patch <=> other.patch; + } + bool operator==(const Version&) const = default; +}; +``` + +## Designated Initializers + +Named field initialization for aggregates. + +```cpp +struct Config { + int port = 8080; + bool verbose = false; + int max_connections = 100; +}; + +// Clear which fields are being set +Config cfg = {.port = 3000, .verbose = true}; +// .max_connections gets its default value (100) +``` + +## std::jthread + +Thread with automatic joining and cooperative cancellation. + +```cpp +#include + +// BEFORE: forgetting to join causes std::terminate +std::thread t(work); +// ... if exception thrown here, t.join() never called → crash + +// AFTER: auto-joins on destruction +std::jthread t(work); +// destructor calls request_stop() then join() — always safe + +// Cooperative cancellation via stop token +std::jthread worker([](std::stop_token stoken) { + while (!stoken.stop_requested()) { + do_work(); + } +}); +worker.request_stop(); // signal the thread to exit +// destructor waits for thread to finish +``` + +## Coroutines + +Suspendable functions for lazy computation and async I/O. + +### When to Use + +- I/O-bound async work (network, file system) +- Lazy sequence generation (use `std::generator` from C++23) +- State machines that would otherwise be complex + +### When NOT to Use + +- CPU-bound parallel work (coroutines cooperate, they don't parallelize) +- Simple synchronous code (unnecessary complexity) +- Reimplementing coroutine types (`generator`, `task`) that libraries already provide +- Custom awaiters are fine when adapting async libraries to coroutines — that's often the only way to bridge callback-based APIs + +### With std::generator (C++23) + +```cpp +std::generator range(int start, int end) { + for (int i = start; i < end; ++i) + co_yield i; +} + +for (auto val : range(0, 10)) { + std::println("{}", val); +} +``` diff --git a/plugins/modern-cpp/skills/modern-cpp/references/cpp23-features.md b/plugins/modern-cpp/skills/modern-cpp/references/cpp23-features.md new file mode 100644 index 0000000..1ff5657 --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/cpp23-features.md @@ -0,0 +1,269 @@ +# C++23 Features (Usable Today) + +These features have solid compiler support (GCC 13+, Clang 17+, MSVC 17.4+) and deliver immediate value. Adopt them now. + +## std::expected + +Type-safe error handling with monadic chaining. The most impactful C++23 feature for everyday code. + +### What It Replaces + +Error codes + output parameters, nullable returns, exceptions at API boundaries where failure is expected (not exceptional). + +### Basic Usage + +```cpp +#include + +enum class ParseError { empty_input, invalid_format, overflow }; + +std::expected parse_int(std::string_view s) { + if (s.empty()) + return std::unexpected(ParseError::empty_input); + // ... parsing logic ... + return 42; +} + +// Caller must handle both paths +auto result = parse_int("123"); +if (result) { + use(*result); // or result.value() +} else { + handle(result.error()); +} +``` + +### Monadic Chaining + +Chain operations without nested if-checks: + +```cpp +std::expected load_config(const Path& p) { + return read_file(p) // expected + .and_then(parse_toml) // expected + .and_then(validate_config) // expected + .transform(apply_defaults); // expected +} +``` + +- `.and_then(f)` — if value, call `f(value)` which returns a new `expected` +- `.transform(f)` — if value, call `f(value)` and wrap result in `expected` +- `.or_else(f)` — if error, call `f(error)` which returns a new `expected` +- `.transform_error(f)` — if error, call `f(error)` and wrap result + +### When to Use vs Exceptions + +- **Use `std::expected`**: expected failures (file not found, parse error, validation failure), performance-critical paths, codebases that ban exceptions +- **Use exceptions**: truly exceptional conditions (out of memory, programmer error), deep call stacks where propagation cost matters + +### Compiler Support + +GCC 13+, Clang 17+, MSVC 17.4+ + +## std::print / std::println + +Type-safe formatted output. Essentially `fmt::print` standardized. + +### Basic Usage + +```cpp +#include + +std::println("Hello, {}!", name); // with newline +std::print("x = {}, y = {}\n", x, y); // without auto-newline +std::println("{:>10.2f}", 3.14159); // format spec: right-aligned, 2 decimal +std::println("{0} is {0}", value); // reuse argument by index +``` + +### What It Replaces + +```cpp +// BEFORE: printf (type-unsafe, format string vulnerabilities) +printf("Name: %s, Age: %d\n", name.c_str(), age); + +// BEFORE: iostream (verbose, stateful, slow) +std::cout << "Name: " << name << ", Age: " << age << std::endl; + +// AFTER: type-safe, readable, fast +std::println("Name: {}, Age: {}", name, age); +``` + +### Relationship to {fmt} + +`std::print` IS the `{fmt}` library adopted into the standard, by the same author (Victor Zverovich). If you already use `{fmt}`, switching to `std::print` removes one dependency. `{fmt}` still has extras `std::print` lacks (color output, named arguments). + +### Compiler Support + +GCC 14+, Clang 18+, MSVC 17.4+ + +## Deducing this + +Eliminates const/non-const overload duplication and simplifies CRTP. + +### The Problem It Solves + +```cpp +// BEFORE: duplicated logic for const and non-const +class Buffer { + std::vector data_; +public: + char& operator[](size_t i) { return data_[i]; } + const char& operator[](size_t i) const { return data_[i]; } + // Often 4 overloads: const/non-const x lvalue/rvalue +}; + +// AFTER: one function handles all qualifications +class Buffer { + std::vector data_; +public: + template + auto&& operator[](this Self&& self, size_t i) { + return std::forward(self).data_[i]; + } +}; +``` + +### Simplified CRTP + +```cpp +// BEFORE: arcane template pattern +template +struct Base { + void interface() { + static_cast(this)->implementation(); + } +}; + +// AFTER: no template parameter needed +struct Base { + void interface(this auto&& self) { + self.implementation(); + } +}; +``` + +### Recursive Lambdas + +```cpp +// BEFORE: std::function or Y-combinator +std::function fib = [&](int n) { + return n <= 1 ? n : fib(n-1) + fib(n-2); +}; + +// AFTER: direct recursion +auto fib = [](this auto&& self, int n) -> int { + return n <= 1 ? n : self(n-1) + self(n-2); +}; +``` + +### Compiler Support + +GCC 14+, Clang 18+, MSVC 19.32+ + +## std::ranges::to (Materializing Views) + +Converts lazy range views into concrete containers. The missing piece from C++20 ranges. + +```cpp +#include + +auto vec = data + | std::views::filter(is_valid) + | std::views::transform(process) + | std::ranges::to(); // materialize into a vector + +// Works with any container +auto set = data + | std::views::transform(normalize) + | std::ranges::to(); +``` + +Without `std::ranges::to`, you had to manually construct containers from view iterators — awkward and error-prone. This completes the C++20 ranges story. + +### Compiler Support + +GCC 14+, Clang 17+, MSVC 17.4+ + +## std::flat_map / std::flat_set + +Cache-friendly sorted containers backed by contiguous storage. + +### When to Prefer Over std::map + +- **Read-heavy workloads** — lookup is 8-17x faster than `std::map` for small-medium sizes +- **Small-to-medium maps** (up to ~1000 elements) +- **Cache-sensitive code** — contiguous memory vs node-based tree + +### When NOT to Use + +- **Insert-heavy workloads** — insertion is O(n) due to shifting elements +- **Large maps with frequent mutations** — `std::map`'s O(log n) insert wins +- **Iterator stability required** — flat containers invalidate iterators on insert + +### Usage + +```cpp +#include + +std::flat_map scores; +scores["alice"] = 100; +scores["bob"] = 85; + +auto it = scores.find("alice"); // fast binary search on sorted vector +``` + +### Compiler Support + +GCC 15+, Clang 18+, MSVC 17.6+ + +## std::generator + +Lazy coroutine generators for producing sequences on demand. + +### Usage + +```cpp +#include + +std::generator fibonacci() { + int a = 0, b = 1; + while (true) { + co_yield a; + auto next = a + b; + a = b; + b = next; + } +} + +// Lazy — only computes as many values as consumed +for (auto val : fibonacci() | std::views::take(10)) { + std::println("{}", val); +} +``` + +### Compiler Support + +MSVC 17.4+, GCC 14+ (partial), Clang catching up. Wait for broader support before relying on it. + +## import std + +Import the entire standard library as a module. + +```cpp +import std; // replaces ALL #include <...> for standard headers + +int main() { + std::println("Hello from modules!"); +} +``` + +### Caveats + +- Build system support varies (CMake 3.28+ with some generators) +- Third-party libraries are mostly not module-ready +- Mixing headers and modules requires care +- Best for new projects; migration of existing code is nontrivial + +### Compiler Support + +GCC 15+, Clang 18+ (partial), MSVC 17.5+ diff --git a/plugins/modern-cpp/skills/modern-cpp/references/cpp26-features.md b/plugins/modern-cpp/skills/modern-cpp/references/cpp26-features.md new file mode 100644 index 0000000..39bf2f9 --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/cpp26-features.md @@ -0,0 +1,238 @@ +# C++26 Features + +C++26 was finalized in March 2026 — the most significant release since C++11. This reference covers the features worth knowing about, ranked by practical impact. + +## Reflection (Game-Changer) + +Static reflection is the single most impactful C++26 feature. It lets programs inspect types, members, and functions at compile time and generate code with zero runtime overhead. + +### What It Eliminates + +- Per-struct serialization functions (JSON, protobuf, XML) +- Code generators (protobuf codegen, Qt MOC, flatbuffers) +- Macro-based registration systems (enum-to-string, type registries) +- Verbose template metaprogramming with `std::tuple` tricks + +### Core Syntax + +The `^^` operator ("reflection operator") reflects a construct into a `std::meta::info` value: + +```cpp +#include + +constexpr auto type_info = ^^int; // reflect the type 'int' +constexpr auto member_info = ^^MyStruct::field; // reflect a member +``` + +Key `std::meta::` functions: +- `nonstatic_data_members_of(^^T)` — enumerate struct members +- `identifier_of(member)` — get member name as string +- `type_of(member)` — get member type +- `enumerators_of(^^E)` — enumerate enum values + +The splice operator `[:expr:]` converts a reflection back into code. + +### Before / After + +```cpp +// BEFORE: write this for EVERY struct, update when fields change +json to_json(const Player& p) { + return {{"name", p.name}, {"health", p.health}, + {"x", p.x}, {"y", p.y}, {"score", p.score}}; +} + +// AFTER: one function, works for ANY struct, never needs updating +template +json to_json(const T& obj) { + json result; + template for (constexpr auto member : + std::meta::nonstatic_data_members_of(^^T)) { + result[std::meta::identifier_of(member)] = obj.[:member:]; + } + return result; +} +``` + +### Enum-to-String (Classic Pain Point) + +```cpp +// BEFORE: manual mapping, breaks when you add values +const char* to_string(Color c) { + switch (c) { + case Color::Red: return "Red"; + case Color::Green: return "Green"; + case Color::Blue: return "Blue"; + } +} + +// AFTER: works for any enum, automatically +template + requires std::is_enum_v +std::string to_string(E value) { + template for (constexpr auto e : std::meta::enumerators_of(^^E)) { + if (value == [:e:]) + return std::string(std::meta::identifier_of(e)); + } + return ""; +} +``` + +### Compiler Support + +- **GCC 16** (April 2026): Reflection merged in trunk +- **Clang**: Bloomberg experimental fork ([github.com/bloomberg/clang-p2996](https://github.com/bloomberg/clang-p2996)), mainline catching up +- **MSVC**: Not yet + +### When to Use / Not Use + +- **Use for**: Serialization, deserialization, ORM mapping, debug printing, enum conversion, logging, plugin systems +- **Don't use for**: Simple code where templates or concepts suffice; reflection adds compile-time complexity + +## Contracts + +Language-level preconditions, postconditions, and assertions. + +### Syntax + +```cpp +// Precondition — checked before function body executes +void transfer(Account& from, Account& to, int amount) + pre (amount > 0) + pre (from.balance >= amount) + post (from.balance >= 0) +{ + from.balance -= amount; + to.balance += amount; +} + +// In-body assertion +void process(std::span data) { + contract_assert(!data.empty()); + // ... +} +``` + +### Enforcement Modes + +| Mode | Behavior | +|------|----------| +| `enforce` | Check, call violation handler, terminate if it returns | +| `observe` | Check, call violation handler, continue execution | +| `ignore` | Skip the check entirely | +| `quick_enforce` | Check, terminate immediately (no handler) | + +Mode is selected at build time, not per-contract. + +### Before / After + +```cpp +// BEFORE: preconditions buried in body, invisible to callers +int divide(int a, int b) { + assert(b != 0); // only in debug builds, invisible to IDE + return a / b; +} + +// AFTER: preconditions on declaration, visible to callers and tools +int divide(int a, int b) + pre (b != 0); +``` + +### Honest Limitations + +- **No virtual function contracts** — deferred to a future standard +- **Controversial design** — Bjarne Stroustrup has publicly criticized the design +- **Compiler support is experimental** — GCC trunk has it, not production-ready +- **Side effects in contract expressions are unspecified** + +### Recommendation + +Adopt cautiously. Use for new API boundaries where preconditions should be visible. Don't rewrite existing assert() usage wholesale until compilers mature. + +## Memory Safety Improvements + +### Erroneous Behavior for Uninitialized Reads + +Uninitialized local variable reads are no longer undefined behavior. They become "erroneous behavior" — well-defined (the variable holds a valid but unspecified value) but diagnosable. + +**Practical impact:** Compilers can no longer use "this read is UB, so I can optimize away your null check" reasoning. Just recompiling as C++26 gives you this protection. + +**Opt-out:** `[[indeterminate]]` attribute for performance-critical code where you can prove the variable is written before read. + +### Hardened Standard Library + +Bounds-checking preconditions added to `operator[]`, `front()`, `back()`, etc. on `std::vector`, `std::span`, `std::string`, `std::string_view`, `std::array`, `std::optional`, `std::expected`. + +**Google's deployment results:** +- Deployed across Chrome and entire server fleet +- ~0.3% average performance overhead +- 1000+ previously undetected bugs found +- 30% reduction in production segfault rate + +**Enable today** (no need to wait for C++26): +``` +-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST +``` + +See [compiler-hardening.md](./compiler-hardening.md) for details. + +## Smaller Wins + +### #embed + +Embed binary data at compile time without external tools: +```cpp +// BEFORE: xxd -i data.bin > data.h (slow, 70+ seconds for 40MB) +static const unsigned char data[] = { 0x89, 0x50, 0x4E, ... }; + +// AFTER: instant, handled by compiler +static constexpr unsigned char data[] = { + #embed "data.bin" +}; +``` + +GCC 15+ and Clang 19+ support `#embed`. + +### std::inplace_vector + +Fixed-capacity, dynamically-resizable vector with no heap allocation. Replaces `boost::static_vector`. + +```cpp +std::inplace_vector small_buf; // max 64 elements, stack-allocated +small_buf.push_back(42); // works like vector +// small_buf.push_back() when full: throws (or use try_push_back for fallible API) +``` + +### std::function_ref + +Non-owning, non-allocating callable wrapper. The `std::string_view` of callables. + +```cpp +// BEFORE: std::function allocates, type-erases, is nullable +void for_each(std::span data, std::function fn); + +// AFTER: zero allocation, passable in registers +void for_each(std::span data, std::function_ref fn); +``` + +### std::indirect and std::polymorphic + +Value-semantic wrappers for heap-allocated objects: +- `std::indirect` — like `unique_ptr` but copyable (deep copies) +- `std::polymorphic` — adds virtual dispatch with value semantics + +### Placeholder Variables + +```cpp +auto [x, _, _] = get_triple(); // ignore second and third +std::lock_guard _(mutex); // unnamed RAII guard +for (auto _ : std::views::iota(0, 5)) { /* repeat 5 times */ } +``` + +### Other Notable Additions + +- **Pack indexing** `T...[N]` — direct variadic pack access +- **Saturation arithmetic** — `std::add_sat`, `std::sub_sat`, `std::mul_sat` +- **`= delete("reason")`** — document why an overload is deleted +- **`std::hive`** — bucket container with stable pointers and O(1) insert/erase +- **``** — data-parallel SIMD types +- **``** — BLAS-based linear algebra diff --git a/plugins/modern-cpp/skills/modern-cpp/references/safe-idioms.md b/plugins/modern-cpp/skills/modern-cpp/references/safe-idioms.md new file mode 100644 index 0000000..2a44369 --- /dev/null +++ b/plugins/modern-cpp/skills/modern-cpp/references/safe-idioms.md @@ -0,0 +1,299 @@ +# Safe C++ Idioms + +Security patterns organized by vulnerability class. Each section explains what exploitable bugs the pattern prevents and what modern C++ features eliminate them. + +## Memory Safety + +### Ownership: Smart Pointers + +Use smart pointers for all owning relationships. Raw pointers are only for non-owning observation. + +```cpp +// Unique ownership (default choice) +auto widget = std::make_unique(args...); +process(*widget); // borrow via reference + +// Shared ownership (only when genuinely shared) +auto shared = std::make_shared(args...); +auto copy = shared; // reference count incremented + +// Non-owning observation +Widget* observer = widget.get(); // does NOT own, must not delete +std::weak_ptr watcher = shared; // non-owning, expires safely +``` + +**Selection guide:** +- `std::unique_ptr` — default. Zero overhead vs raw pointer. Use for exclusive ownership. +- `std::shared_ptr` — only when multiple owners genuinely need to extend lifetime. Has overhead (refcount, control block). +- `std::weak_ptr` — observe shared objects without preventing destruction. Use to break cycles. +- Raw `T*` / `T&` — non-owning only. Never `delete` a raw pointer you didn't `new`. + +### Non-Owning Views + +```cpp +// DANGEROUS: pointer + length — no size information +void process(const char* data, size_t len); + +// SAFE: carries size, bounds-checkable +void process(std::span data); + +// DANGEROUS: null-terminated assumption +void log(const char* message); + +// SAFE: carries length, works with string and string_view +void log(std::string_view message); +``` + +**Rules:** +- Function parameters: `std::span` for arrays, `std::string_view` for strings +- Return values: return owned types (`std::vector`, `std::string`), not views +- Never return a `span` or `string_view` to a local + +### Optional vs Nullable Pointers + +```cpp +// DANGEROUS: null pointer used as "no value" +Widget* find(int id); // caller might forget to check + +// SAFE: explicit optionality +std::optional find(int id); // caller must unwrap + +// Check before use +if (auto w = find(42)) { + use(w.value()); // or *w +} +``` + +### Value Semantics by Default + +Prefer value types over heap-allocated pointer indirection. C++26 adds `std::indirect` and `std::polymorphic` for cases where heap allocation is needed but value semantics are desired. + +## Type Safety + +### Variant Over Union + +```cpp +// DANGEROUS: union — accessing wrong member is UB +union Value { int i; double d; std::string s; }; // UB: string in union + +// SAFE: variant — type-checked access +std::variant value; +value = "hello"; + +// Visit pattern — compiler ensures all types handled +std::visit([](auto&& v) { std::println("{}", v); }, value); + +// Throws std::bad_variant_access if wrong type +auto& s = std::get(value); +``` + +### Safe Type Punning + +```cpp +// DANGEROUS: reinterpret_cast — undefined behavior for most types +float f = 3.14f; +int bits = *reinterpret_cast(&f); // UB! + +// SAFE: bit_cast — defined behavior, constexpr-compatible +int bits = std::bit_cast(f); // OK, well-defined +``` + +### Enum Class + +```cpp +// DANGEROUS: plain enum — implicit int conversion, leaks names +enum Color { Red, Green, Blue }; +int x = Red; // compiles silently +if (Red == 0) // compiles silently + +// SAFE: enum class — scoped, no implicit conversion +enum class Color : uint8_t { Red, Green, Blue }; +// int x = Color::Red; // compile error +// if (Color::Red == 0) // compile error +auto c = Color::Red; // must use scope +``` + +### Preventing Misuse + +```cpp +// Mark functions where ignoring the return is likely a bug +[[nodiscard]] std::expected load(const Path& p); + +// Prevent implicit conversions +class UserId { +public: + explicit UserId(int64_t id) : id_(id) {} + // UserId u = 42; // compile error — must be explicit +private: + int64_t id_; +}; + +// Document why an overload is forbidden (C++26) +void process(std::string_view s); +void process(std::nullptr_t) = delete("passing nullptr is a bug — use empty string"); +``` + +## Integer Safety + +### Narrowing Detection + +```cpp +#include + +void safe_convert(int64_t big_value) { + // CHECK: is the value representable in the target type? + if (std::in_range(big_value)) { + int32_t small = static_cast(big_value); + use(small); + } else { + handle_overflow(); + } +} +``` + +### Saturation Arithmetic (C++26) + +```cpp +#include + +// For signal processing, image manipulation, counters +uint8_t pixel = std::add_sat(200, 100); // 255, not 44 + +// Useful for counters that should not wrap +size_t count = std::add_sat(count, increment); +``` + +### Compiler Warnings + +Enable `-Wconversion` and `-Wsign-conversion` to catch implicit narrowing at compile time. These flags alone prevent a significant class of integer bugs. + +## Concurrency Safety + +### Lock Management + +```cpp +// DANGEROUS: manual lock/unlock — exception-unsafe, easy to forget +std::mutex mtx; +mtx.lock(); +do_work(); // if this throws, mutex is never unlocked +mtx.unlock(); + +// SAFE: scoped_lock — exception-safe, supports multiple mutexes +{ + std::scoped_lock lock(mtx); + do_work(); // mutex released when lock goes out of scope +} + +// Multiple mutexes — deadlock-free (sorted lock acquisition) +std::scoped_lock lock(mutex_a, mutex_b); +``` + +### Thread Management + +```cpp +// DANGEROUS: std::thread — forgetting join() calls std::terminate +std::thread t(work); +// if exception before join → crash + +// SAFE: jthread — auto-joins, supports cooperative cancellation +std::jthread t([](std::stop_token stoken) { + while (!stoken.stop_requested()) { + do_work(); + } +}); +// destructor calls request_stop() then join() +``` + +### Atomics + +```cpp +// DANGEROUS: volatile does NOT provide atomicity or ordering +volatile int counter = 0; // data race in multi-threaded code + +// SAFE: atomic with explicit memory ordering +std::atomic counter{0}; +counter.fetch_add(1, std::memory_order_relaxed); // specify ordering +``` + +## Initialization Safety + +### constexpr / consteval + +Computation at compile time is UB-free by design — the compiler rejects any undefined behavior in constant expressions. + +```cpp +constexpr int factorial(int n) { + // If this has UB (e.g., signed overflow), it's a compile error + int result = 1; + for (int i = 2; i <= n; ++i) result *= i; + return result; +} + +consteval int must_be_compile_time(int n) { + return factorial(n); // guaranteed compile-time evaluation +} +``` + +### Always Initialize + +```cpp +// DANGEROUS: uninitialized variable +int x; // indeterminate value (erroneous behavior in C++26) +use(x); // bug + +// SAFE: initialize at declaration +int x = 0; +int y{}; // value-initialized (zero for scalars) +auto z = compute_value(); +``` + +Enable `-ftrivial-auto-var-init=zero` to catch cases you miss. + +### Rule of Five / Zero + +```cpp +// Rule of Zero: prefer this — let the compiler generate everything +struct Config { + std::string name; + std::vector values; + // No destructor, copy/move constructors, or assignment needed + // Compiler-generated versions do the right thing +}; + +// Rule of Five: if you must manage a resource manually +class FileHandle { + int fd_; +public: + ~FileHandle(); + FileHandle(const FileHandle&); + FileHandle& operator=(const FileHandle&); + FileHandle(FileHandle&&) noexcept; + FileHandle& operator=(FileHandle&&) noexcept; +}; +// But prefer RAII wrappers (unique_ptr with custom deleter) over this +``` + +## Iterator Safety + +### Prefer Algorithms Over Raw Loops + +```cpp +// RISKY: off-by-one, iterator invalidation +for (auto it = vec.begin(); it != vec.end(); ++it) { + if (should_remove(*it)) { + vec.erase(it); // BUG: invalidates iterator + } +} + +// SAFE: erase-remove idiom +std::erase_if(vec, should_remove); // C++20 + +// SAFE: range-based algorithms +auto results = data + | std::views::filter(is_valid) + | std::views::transform(process); +``` + +### Iterator Invalidation Awareness + +Know which operations invalidate iterators on which containers. When in doubt, use indices or rebuild the container. Trail of Bits' [Itergator](https://github.com/trailofbits/itergator) tool detects iterator invalidation bugs via CodeQL.