Claude Skill

sota-c-cpp

State-of-the-art C and C++ engineering rules (2026 baseline) that Claude applies when writing or auditing C/C++. Covers modern idioms (RAII, value semantics, smart pointers, C++23), memory safety (lifetimes, bounds, sanitizers, hardening flags), undefined behavior, security (SEI

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download martinholovsky-SOTA-skills-skills_sota-c-cpp-ec2abf6.zip · 29 KB
Part of martinholovsky/sota-skills — 39 skills

Install

skills CLI npx skills add https://github.com/martinholovsky/SOTA-skills/tree/main/skills/sota-c-cpp
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install martinholovsky-sota-skills@llmmart
Git git clone https://github.com/martinholovsky/SOTA-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole martinholovsky/sota-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

SOTA C & C++ (2026)

Expert-level rules for producing and auditing production C and C++. C and C++ are memory-unsafe by default: the compiler will not stop you from reading freed memory, overrunning a buffer, or invoking undefined behavior (UB) that the optimizer then weaponizes. These rules exist to claw back the safety the language doesn't give you — through RAII, the type system, sanitizers, hardened build flags, and disciplined review. Baseline: C23 (ISO/IEC 14882:2024) and C17/C23; flag where a control needs a newer toolchain. C26 is feature-complete (DIS ballot 2026) — contracts, reflection, erroneous behavior for uninitialized reads, and a standardized hardened standard library; the last is usable today via libc++/libstdc++ hardening flags (rules/02). Every rule states the why; every rules file ends with an audit checklist of grep/clang-tidy/ sanitizer patterns.

Purpose

Two consumers, one source of truth:

  • BUILD mode — generating new C/C++: follow the rules as defaults, not suggestions. Prefer C++ with RAII over raw C idioms unless the target is C. Deviate only with a comment justifying it.
  • AUDIT mode — reviewing existing C/C++: hunt violations using the audit checklists, classify by severity, report in the finding format below. Memory- safety and UB findings are presumed exploitable until proven otherwise.

What this skill covers about embedded work, and what it does not. It carries the safety-critical coding standards — MISRA C:2025 / C++:2023 and SEI CERT (rules/04), freestanding builds with exceptions disabled (rules/01 §7), banned APIs and hardened build flags — so a firmware codebase gets real coverage of the language layer. It does not cover the systems layer that makes embedded work its own discipline: interrupt service routines and reentrancy, DMA buffer coherency, memory-mapped I/O and volatile semantics against a peripheral, RTOS task scheduling and priority inversion, WCET and timing analysis, or linker scripts and startup code. No skill in this library owns those today. Say so when the task reaches them rather than generalising from the desktop rules — guidance that sounds authoritative outside its scope is the failure sota-skill-security exists to catch.

BUILD mode

  1. Before writing, read the rules files relevant to the task (see index). A parser handling untrusted bytes needs 02, 03, 04; a threaded service needs 05.
  2. Apply the top-10 non-negotiables (below) unconditionally.
  3. New projects: CMake (≥3.20) with -Wall -Wextra -Wpedantic -Werror, the OpenSSF hardening flags (rules/04), a debug build wired to ASan+UBSan, clang-tidy + clang-format configs, and CI running all of it from day one (rules/06).
  4. Prefer the standard library and RAII types over hand-rolled allocation/ownership. Every new/malloc/fopen/mutex.lock() should be owned by a destructor (unique_ptr, container, lock_guard), not a manual matching call you can forget on an early return or exception.
  5. Treat warnings as errors. A clean -Wall -Wextra build is the floor, not the goal — also run a static analyzer and the sanitizers (rules/06).
  6. When you must use a sharp tool (raw pointer arithmetic, reinterpret_cast, unsafe C interop, manual lifetime), leave a // NOTE(sota): comment explaining the invariant you're upholding so auditors don't flag it blind.

AUDIT mode

Work through each relevant rules file's audit checklist against the target. Run the listed grep/clang-tidy/sanitizer commands; confirm each hit manually (greps are recall-oriented). Where feasible, build with -fsanitize=address, undefined and run the test suite — a sanitizer abort is ground truth.

Severity conventions

Severity Meaning Examples
CRITICAL Exploitable memory corruption or guaranteed UB on reachable input Heap/stack buffer overflow on attacker data, use-after-free, double-free, OOB write, format-string with user-controlled fmt, system() with interpolated input, data race on a pointer
HIGH Likely corruption, crash, or security weakness Unchecked malloc/new size from input, integer overflow feeding an allocation or index, missing bounds check, strcpy/sprintf/gets, TOCTOU on a path, missing RAII so a leak/UB occurs on the exception path
MEDIUM Correctness/maintainability hazard, latent bug Raw owning pointers, manual new/delete pairs, C-style casts, narrowing conversions, memcpy where a typed copy fits, missing override/= delete, signed/unsigned comparison
LOW Idiom/perf debt, works but wrong shape Pass-by-value of large objects, needless copies instead of std::move, using namespace std in headers, macros where constexpr/inline fits
INFO Style/doc/hygiene clang-format drift, naming, missing [[nodiscard]], include hygiene

Finding format

[SEVERITY] file.cpp:LINE — short title
  Rule: rules/NN-name.md § section
  Evidence: the offending line(s), verbatim
  Impact: one sentence — what corrupts/leaks/races, under what input
  Fix: concrete replacement code or action
  Effort: trivial | small | medium | large

Group findings by severity, CRITICAL first. End with: counts per severity, the three highest-leverage fixes, and which checklists/sanitizers were run.

Rules index

File Read this when...
rules/01-idioms.md Writing/reviewing any C++: RAII and the rule of zero/five, ownership with unique_ptr/shared_ptr, value semantics and move, const/constexpr, references vs pointers, casts, enum class, error handling (exceptions vs std::expected vs error codes), in-band sentinels (absence encoded as -1/0/"") incl. EOF in a char — broken only where char is unsigned, C-vs-C++ idiom choices
rules/02-memory-safety.md Anything touching pointers, buffers, lifetimes, or allocation: bounds, use-after-free/return, dangling references and views (string_view/span), iterator invalidation, ownership discipline, sanitizers (ASan/MSan), _FORTIFY_SOURCE/_GLIBCXX_ASSERTIONS
rules/03-undefined-behavior.md Reasoning about UB and the optimizer: integer overflow, strict aliasing, uninitialized reads, null/misaligned access, signed shifts, data races as UB, unsigned arithmetic, UBSan, why "it worked in debug" proves nothing
rules/04-security.md Any input crossing a trust boundary: CERT C/C++ + MISRA, banned functions (gets/strcpy/sprintf/system), integer-overflow-to-allocation, format strings, path traversal/TOCTOU, command injection, deserialization/parsers, CSPRNG, the OpenSSF hardening flag set
rules/05-concurrency.md Anything with threads, atomics, or shared state: the C++ memory model, data races, std::atomic and memory orders, mutex/lock_guard/scoped_lock, deadlock ordering, condition variables, std::jthread/stop tokens, TSan
rules/06-build-tooling-ci.md Setting up or auditing builds/CI: CMake hygiene, warning flags, clang-tidy/clang-format, static analysis (clang-analyzer, cppcheck, Coverity), sanitizer CI matrix, fuzzing (libFuzzer/OSS-Fuzz), dependencies and supply chain (vcpkg/Conan, pinning, SBOM). Test strategy lives in sota-testing; this file owns C/C++ build/test mechanics.
rules/07-performance.md Latency/throughput/memory work: profiling (perf, VTune, Callgrind), allocation reduction and custom allocators, move/copy elision, cache locality and data-oriented layout, <algorithm> over hand loops, LTO/PGO, micro-benchmarking pitfalls

Top-10 non-negotiables

  1. Every resource is owned by a destructor (RAII). No new/delete or malloc/free pairs you have to match by hand; no bare owning pointers. Use unique_ptr, containers, lock_guard/scoped_lock, RAII wrappers. A leak or UB on the exception/early-return path is the default failure mode of manual cleanup. (rules/01, rules/02)
  2. No buffer touches memory it doesn't own. Bounds-check every index/ length derived from input; use std::span/std::string/containers and .at() or explicit checks, never raw pointer + length you assume. Overflow on attacker input is CRITICAL. (rules/02)
  3. No use-after-free / dangling. A pointer, reference, iterator, string_view, or span must not outlive its storage. Never return a reference/view to a local or to a temporary. (rules/02)
  4. Undefined behavior is a bug even if it "works". Signed integer overflow, strict-aliasing violations, uninitialized reads, OOB, data races are UB the optimizer may exploit. Build with UBSan; treat any UBSan diagnostic as CRITICAL/HIGH. (rules/03)
  5. Integers feeding an allocation, index, or memcpy size are overflow-checked and the right signedness. Validate ranges before use; prefer unsigned for sizes, check for wrap. Overflow-to-undersize-alloc is a classic RCE primitive. (rules/03, rules/04)
  6. Banned functions are banned. No gets, strcpy/strcat/sprintf (use bounded forms or std::string/std::format), no system() with interpolated input (use posix_spawn/exec* with an argv array). (rules/04)
  7. Build hardened, by default. -Wall -Wextra -Werror plus the OpenSSF set (-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-protector-strong -fstack-clash-protection -fcf-protection -Wl,-z,relro,-z,now). Missing hardening on a network-facing binary is a HIGH finding. (rules/04, rules/06)
  8. Shared mutable state is synchronized; data races are CRITICAL. Guard with a mutex/scoped_lock or use std::atomic with a justified memory order. A -fsanitize=thread failure is not flaky noise. (rules/05)
  9. Sanitizers and a static analyzer gate CI. A debug/test job runs ASan+UBSan (and TSan for threaded code); clang-tidy + cppcheck run on every PR. Untrusted-input parsers get a fuzz target. (rules/06)
  10. Prefer the type system to convention. enum class over macros, constexpr/inline over #define, gsl::span/std::span over pointer+ length, [[nodiscard]] on must-check returns, explicit on single-arg constructors, override/final. Make misuse fail to compile. (rules/01)
Files (sota-skills)
  • rules
    • 01-idioms.md 10.8 KB
      # 01 — Idioms: RAII, ownership, value semantics, error handling
      
      Modern C++ is a different language from "C with classes". The through-line is
      **let the type system and destructors enforce correctness** so that the happy
      path and every error/exception path clean up identically. References:
      [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)
      (cited as `CG <id>` below) and [cppreference](https://en.cppreference.com/).
      
      ## 1. RAII and the rule of zero/five
      
      - **Rule of zero** (CG C.20): the best class manages no raw resources — it
        composes `std::string`, `std::vector`, `unique_ptr`, etc., and needs *no*
        user-declared destructor, copy, or move. Default everything. Reach for this
        first.
      - **Rule of five** (CG C.21): if you declare *any* of destructor, copy ctor,
        copy assign, move ctor, move assign, declare or `= default`/`= delete` all
        five. A class that owns a raw handle and defines only a destructor silently
        gets memberwise copy → double-free.
      - Wrap every C resource (FILE*, fd, mutex, malloc'd block, library handle) in
        an RAII type once, then use it by value. The destructor is the only place
        cleanup lives, so early `return`, `throw`, and normal exit all release.
      
      ```cpp
      // GOOD — RAII wrapper; closes on every exit path, non-copyable, movable
      class File {
        std::FILE* f_{};
      public:
        explicit File(const char* p, const char* m) : f_(std::fopen(p, m)) {
          if (!f_) throw std::system_error(errno, std::generic_category(), p);
        }
        ~File() { if (f_) std::fclose(f_); }
        File(const File&) = delete;
        File& operator=(const File&) = delete;
        File(File&& o) noexcept : f_(std::exchange(o.f_, nullptr)) {}
        File& operator=(File&& o) noexcept { std::swap(f_, o.f_); return *this; }
        std::FILE* get() const noexcept { return f_; }
      };
      ```
      
      ## 2. Ownership: smart pointers, not raw owning pointers
      
      - `std::unique_ptr<T>` is the default owner — zero overhead, move-only, clear
        single ownership. `std::make_unique<T>(...)` (never `new`).
      - `std::shared_ptr<T>` only when ownership is genuinely *shared* and lifetime
        is dynamic; it has atomic-refcount cost. `std::make_shared` for one
        allocation. Break cycles with `std::weak_ptr`.
      - **Raw pointers and references are non-owning** (CG R.3, F.7): they observe,
        never delete. A function takes `T*`/`T&`/`std::span`/`string_view` to borrow;
        it takes `unique_ptr<T>` (by value) only to take ownership.
      - Never `delete` a raw pointer in modern code; never store a `new`'d pointer in
        a bare member. Owning raw pointers are a MEDIUM finding (CRITICAL if they
        leak/double-free on a path).
      
      ## 3. Value semantics and move
      
      - Prefer values and containers over pointers. Copies are explicit and safe;
        moves transfer ownership cheaply.
      - Pass **cheap-to-copy** types (int, `string_view`, small structs) by value;
        pass large objects by `const&` to read, by `&` to mutate, by value + `std::
        move` when the function will store a copy (the "sink" idiom).
      - `std::move` is a *cast*, not a move; it only enables the move. Don't `move` a
        `const` object (silently copies), don't use a moved-from object except to
        reassign/destroy, don't `return std::move(local)` — it pessimizes (N)RVO.
      - Mark move operations `noexcept` or `std::vector` falls back to copying on
        reallocation (CG C.66).
      
      ## 4. const, constexpr, and immutability
      
      - `const` by default — parameters, locals, methods that don't mutate, member
        data that's set once. `const` is documentation the compiler enforces.
      - `constexpr`/`consteval` for compile-time constants and functions; prefer over
        macros and over runtime computation of fixed values.
      - Avoid `const_cast` away constness on an object actually declared `const` —
        that's UB if you then write (`rules/03`).
      
      ## 5. References vs pointers, and casts
      
      - Prefer references where null is not a valid state; use pointers (or
        `std::optional`/`std::expected`) where absence is meaningful.
      - **No C-style casts** (`(T)x`) in C++ — they silently become whichever of
        `static/const/reinterpret_cast` compiles, hiding intent and danger (CG
        ES.49). Use the named casts; `reinterpret_cast` is a red flag requiring a
        comment and a strict-aliasing review (`rules/03`).
      - `static_cast` for related types; never use it to "fix" a warning about
        signed/unsigned or narrowing without checking the value first.
      
      ## 6. Type-system leverage
      
      - `enum class` over plain `enum` and over integer/macro constants — scoped,
        typed, no implicit conversions (CG Enum.3).
      - `[[nodiscard]]` on functions whose return must be checked (error codes,
        `expected`, allocations, `empty()`); `explicit` on single-argument
        constructors and conversion operators (CG C.46) to stop surprise conversions.
      - `override` on every overrider and `final` where appropriate; declare
        destructors `virtual` in polymorphic base classes (CG C.35) — deleting a
        derived object through a base pointer without a virtual destructor is UB.
      - Prefer `using` aliases to `typedef`; prefer `inline`/`constexpr` to
        function-like macros (macros ignore scope and types).
      
      ## 7. Error handling: exceptions vs expected vs codes
      
      - Within a codebase, pick one strategy per layer and be consistent (CG E.1+).
      - **Exceptions** are the C++ default for errors that can't be handled locally;
        they compose with RAII so stack unwinding releases resources. Throw by value,
        catch by `const&`. Don't use exceptions for normal control flow.
      - **`std::expected<T,E>`** (C++23) for expected, recoverable failures in hot or
        exception-averse paths (parsers, lookups) — explicit, allocation-free, forces
        the caller to handle `E`. `std::optional<T>` when there's no error detail.
      - **Error codes** (C return-int, `std::error_code`) at C ABI boundaries and in
        freestanding/embedded where exceptions are disabled.
      - A function that can fail must make failure unignorable: `[[nodiscard]]`
        return, `expected`, or a thrown exception — never a silently-ignored global
        `errno` the caller forgets to check.
      - `noexcept` on functions that truly can't throw (destructors, swaps, moves);
        a `throw` escaping `noexcept` calls `std::terminate`.
      
      ## 7a. In-band sentinels, and the platform that changes the answer
      
      C has no option type, so the in-band sentinel (`sota-architecture` rules/02 §8a) is
      the *native* idiom — and its two classic bugs are both about the sentinel's type
      rather than its value.
      
      - `atoi("x")` and `atoi("0")` both return `0` (verified, clang 21, macOS): failure
        and a legitimate parse are indistinguishable. Use `strtol` + `errno`/`endptr`
        (already in rules/04's banned-API table).
      - **`EOF` is `-1` as an `int`, and storing it in a `char` breaks the comparison —
        on some platforms only.** Measured, clang 21 on x86-64 Darwin: with the default
        **signed** `char`, `(char)EOF == EOF` is **true** and the code works; compiled
        `-funsigned-char` (the default on ARM and PowerPC Linux) it is **false**, and the
        read loop never terminates. Note which way the diagnostic runs: clang warns
        (`-Wtautological-constant-out-of-range-compare`) only in the **broken**
        configuration, so a developer on a signed-`char` machine sees neither the bug nor
        the warning. Always `int c; while ((c = getchar()) != EOF)`. This is the
        location-dependent silence class — `sota-code-security` rules/13 §5.
      - POSIX's `-1`-plus-`errno` is a genuine out-of-band pair; it only degrades into an
        in-band sentinel when the caller keeps the `-1` and drops `errno`.
      - **C++ has the alternatives — use them:** `std::optional<T>` for absent,
        `std::expected<T,E>` (C++23) for failed. A function returning `int` where `-1`
        means "no result" is a C++ API bug, not a style preference.
      
      ## 8. C-specific idioms (when the target is C)
      
      - Initialize every variable at declaration; designated initializers (C99+) for
        structs. Use `const` and `static` aggressively to limit scope/linkage.
      - One allocation owner per resource; pair every `malloc`/`fopen`/`open` with a
        single `free`/`fclose`/`close` reached on all paths (goto-cleanup idiom is
        acceptable and idiomatic in C). Check every allocation return.
      - Prefer `sizeof(*ptr)` over `sizeof(Type)` in allocations so the size tracks
        the pointer's type. Use bounded string functions (`snprintf`, `strlcpy` where
        available); see `rules/04` for the banned list.
      
      ## Audit checklist
      
      ```bash
      # Owning raw pointers / manual new-delete — MEDIUM (CRITICAL if leak/double-free)
      grep -rnE '\bnew\b[^=]*;' --include='*.cpp' --include='*.h' --include='*.hpp' . | grep -v make_
      grep -rnE '\bdelete\b\s' --include='*.cpp' --include='*.hpp' .
      grep -rn 'malloc\|calloc\|realloc\|free(' --include='*.c' --include='*.cpp' .
      
      # C-style casts and reinterpret_cast — MEDIUM/HIGH
      grep -rnE '\([[:space:]]*[A-Za-z_][A-Za-z0-9_:<> ]*[*&]?[[:space:]]*\)[[:space:]]*[A-Za-z_(]' --include='*.cpp' .  # heuristic, expect FPs
      grep -rn 'reinterpret_cast\|const_cast' --include='*.cpp' --include='*.hpp' .
      
      # Rule of five violations — class with destructor but not all 5 special members
      clang-tidy --checks='cppcoreguidelines-special-member-functions,cppcoreguidelines-rule-of-*' <files>
      
      # Missing virtual destructor in polymorphic base — HIGH (UB on delete-via-base)
      clang-tidy --checks='cppcoreguidelines-virtual-class-destructor,hicpp-use-override' <files>
      
      # Move/idiom smells — LOW
      grep -rn 'return std::move' --include='*.cpp' .          # pessimizes RVO
      grep -rn 'using namespace std;' --include='*.h' --include='*.hpp' .  # in headers: bad
      grep -rnE '#define [A-Z_]+\(' --include='*.h' .          # function-like macros → constexpr/inline
      
      # Error handling (§7) — the section had no probe at all until 2026-08-21
      grep -rnE 'catch[[:space:]]*\([^)]*\)[[:space:]]*\{[[:space:]]*\}' --include='*.cpp' .  # empty catch — HIGH
      grep -rn 'catch (...)' --include='*.cpp' .               # swallow-all: needs a rethrow or a logged reason
      clang-tidy --checks='bugprone-empty-catch,bugprone-exception-escape,misc-throw-by-value-catch-by-reference' <files>
      # Ignored error returns — the C half of §7, and the one nobody greps
      clang-tidy --checks='bugprone-unused-return-value,cert-err33-c' <files>   # cert-err33-c aliases the former
      grep -rn 'std::expected\|absl::Status\|tl::expected' --include='*.cpp' --include='*.hpp' . | head
      # ^ then ask the §7 question a grep cannot: is ONE error model used across a
      #   given boundary, or do exceptions, codes and expected<> meet at an ABI seam?
      #   Mixed models at a boundary is the finding, not any one of them.
      
      # Broad idiom enforcement (the canonical config)
      clang-tidy --checks='cppcoreguidelines-*,modernize-*,bugprone-*' <files>
      
      # In-band sentinels (§7/§8) — absence encoded as a value
      grep -rnE 'return -1;' --include='*.c' --include='*.cpp' .      # producer: same constant from 2 branches?
      grep -rn 'atoi(\|atol(' --include='*.c' --include='*.cpp' .      # 0 on garbage == 0 on "0" (rules/04)
      grep -rnE 'char[[:space:]]+[a-z_]+[[:space:]]*=[[:space:]]*getchar' --include='*.c' .  # EOF in a char: breaks only where char is UNSIGNED
      ```
      
    • 02-memory-safety.md 7 KB
      # 02 — Memory safety: lifetimes, bounds, use-after-free, sanitizers
      
      C and C++ give you no runtime guard against reading freed memory, walking off
      the end of a buffer, or aliasing storage you no longer own. ~70% of CVEs in
      large C/C++ codebases are memory-safety bugs (per Microsoft and Chromium
      telemetry; see [Chromium memory-safety](https://www.chromium.org/Home/chromium-security/memory-safety/)).
      These are the highest-severity findings in any C/C++ audit: presume
      exploitable. The defense is ownership discipline (`rules/01`) plus the tools
      below.
      
      ## 1. The bug classes
      
      - **Buffer overflow/underflow** — index or pointer outside an object's bounds.
        Read = info leak; write = corruption/RCE.
      - **Use-after-free (UAF)** — dereferencing a pointer to freed/destroyed
        storage. Includes use-after-`return` (pointer to a local) and
        use-after-move-then-reuse-of-internal-buffer.
      - **Double-free / invalid free** — freeing twice or freeing non-heap/offset
        pointers; corrupts the allocator.
      - **Dangling reference/view** — `T&`, `string_view`, `span`, iterator, or
        pointer outliving its storage.
      - **Uninitialized read** — using memory before it holds a value (also UB,
        `rules/03`).
      - **Memory leak** — lost ownership; rarely exploitable but a reliability/DoS
        issue.
      
      ## 2. Bounds: never index memory you didn't size-check
      
      - Use containers and views that carry their size: `std::vector`, `std::array`,
        `std::span<T>` (C++20), `std::string`, `std::string_view`. `span`/`string_
        view` are *non-owning* — see §4.
      - Index with `.at()` (checked) on cold paths; on hot paths bounds-check once at
        the boundary then use `operator[]`. Never compute `ptr + n` from
        attacker-controlled `n` without validating `n` against the real length.
      - In C, always pass length alongside the pointer and check it; prefer
        `snprintf`/`memcpy_s`-style bounded ops. `-D_FORTIFY_SOURCE=3` adds runtime
        bounds checks to many libc calls (`rules/04`).
      - Enable hardened standard-library assertions so OOB container access traps
        instead of corrupting: libstdc++ `-D_GLIBCXX_ASSERTIONS`, libc++
        `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE` (LLVM 18+ — verify
        for your toolchain; EXTENSIVE for debug/test, FAST in production — `rules/04` §5).
      
      ```cpp
      // BAD — trusts len from the wire; OOB read/write
      void parse(const uint8_t* p, size_t len) { uint8_t b = p[off]; /* off unchecked */ }
      
      // GOOD — span carries size; checked access
      void parse(std::span<const uint8_t> buf) {
        if (off >= buf.size()) throw std::out_of_range("off");
        uint8_t b = buf[off];
      }
      ```
      
      ## 3. Lifetimes: own clearly, observe carefully
      
      - Single owner via `unique_ptr`/container (`rules/01`). The owner's destructor
        is the one free; observers never free.
      - **Never return a pointer/reference/view to a local or temporary** (CG F.43):
      
      ```cpp
      std::string_view bad() { std::string s = make(); return s; }   // dangling on return
      const std::string& worse(std::map<K,V>& m, K k) { return m[k].name; } // ok only while m & entry live
      ```
      
      - Beware references/views captured into objects or lambdas that outlive the
        referent (CG F.50, ES.61). A lambda capturing `[&]` stored past the enclosing
        scope dangles.
      - `std::string_view`/`std::span` parameters are great for *borrowing within a
        call*; do not store them unless you control and outlive the backing storage.
        Binding `string_view` to a temporary `std::string` (e.g. from `+`) dangles.
      
      ## 4. Iterator and reference invalidation
      
      - Mutating a container can invalidate iterators, pointers, and references into
        it: `vector` push/insert/reserve invalidates on reallocation; `erase`
        invalidates at/after the point; `unordered_*` rehash invalidates iterators.
        Re-acquire after mutation; don't cache across a modifying call (CG ES.62).
      - The classic bug: iterating and erasing — use the return of `erase`
        (`it = v.erase(it)`) or `std::erase_if` (C++20).
      
      ## 5. Sanitizers — the ground truth
      
      Build a dedicated job with sanitizers and run the full test/fuzz suite. They
      catch what review and `-Wall` cannot. (Clang/GCC; see
      [Clang sanitizers](https://clang.llvm.org/docs/index.html).)
      
      | Sanitizer | Flag | Catches | Notes |
      |---|---|---|---|
      | **ASan** | `-fsanitize=address` | heap/stack/global overflow, UAF, double-free, leaks | ~2x slowdown; not with Valgrind; the workhorse |
      | **UBSan** | `-fsanitize=undefined` | overflow, misalignment, null deref, bad casts, OOB (some) | pair with `-fno-sanitize-recover=all` to abort |
      | **MSan** | `-fsanitize=memory` | uninitialized reads | Clang only; needs instrumented libs |
      | **TSan** | `-fsanitize=thread` | data races, lock-order issues | `rules/05`; mutually exclusive with ASan |
      
      - ASan and TSan can't run together — use two jobs. MSan needs an instrumented
        libc++ to avoid false positives.
      - Add `-fsanitize-address-use-after-scope` to catch use of out-of-scope locals.
      - **Valgrind/Memcheck** is the no-recompile fallback (catches UAF/leaks/
        uninit), but slower and misses stack/global overflows ASan catches. Prefer
        ASan+UBSan in CI; keep Valgrind for third-party binaries you can't rebuild.
      
      ## 6. Allocation hygiene
      
      - Check every allocation: `new` throws `std::bad_alloc` (handle or let it
        propagate to a boundary); `malloc`/`calloc`/`realloc` return NULL — check
        before use. Unchecked `malloc` of an input-derived size is HIGH.
      - `realloc` returning NULL must not overwrite the original pointer (else leak);
        use a temp. Free with the matching deallocator (`free`↔`malloc`,
        `delete`↔`new`, `delete[]`↔`new[]`; mismatches are UB).
      - Prefer not to mix manual allocation with C++ at all — `make_unique`,
        `vector`, `string` remove the whole class.
      
      ## Audit checklist
      
      ```bash
      # Banned/dangerous buffer ops — HIGH/CRITICAL (also rules/04)
      grep -rnE '\b(strcpy|strcat|sprintf|gets|stpcpy|vsprintf)\b' --include='*.c' --include='*.cpp' .
      grep -rnE '\b(memcpy|memmove|memset|strncpy)\b' --include='*.c' --include='*.cpp' .  # verify size provenance
      
      # Dangling: returning address/ref/view of a local — HIGH
      grep -rnE 'return &[A-Za-z_]' --include='*.cpp' --include='*.c' .
      grep -rnE 'return (std::)?(string_view|span)' --include='*.cpp' .   # verify backing outlives
      clang-tidy --checks='bugprone-dangling-handle,bugprone-use-after-move,clang-analyzer-cplusplus.*' <files>
      
      # Iterator invalidation / erase-in-loop — MEDIUM
      grep -rnE 'for *\(.*begin\(\).*\).*\.(erase|push_back|insert|clear)\(' --include='*.cpp' .
      
      # Allocation checks — HIGH
      grep -rnE '=\s*(malloc|calloc|realloc)\(' --include='*.c' --include='*.cpp' .  # confirm NULL-check follows
      grep -rnE '(new|new\[\])' --include='*.cpp' . | grep -v make_                  # confirm RAII ownership
      
      # Sanitizer/hardening presence in the build — HIGH if a network binary lacks them
      grep -rn 'fsanitize' . ; grep -rn '_GLIBCXX_ASSERTIONS\|_LIBCPP_HARDENING\|_FORTIFY_SOURCE' .
      
      # Build & run the suite under sanitizers (ground truth)
      #   cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all"
      #   ctest   # any ASan/UBSan abort == CRITICAL/HIGH finding
      ```
      
    • 03-undefined-behavior.md 6.3 KB
      # 03 — Undefined behavior and the optimizer
      
      Undefined behavior (UB) is not "implementation-defined" or "works on my
      machine" — it is the standard granting the compiler permission to assume the
      program *never* does the thing, and to optimize on that assumption. A single
      UB on a reachable path can delete your bounds check, miscompile a loop, or open
      a vulnerability. "It worked in a debug build" proves nothing: optimizers
      exploit UB at `-O2`. Treat any UBSan diagnostic as CRITICAL/HIGH. Reference:
      [cppreference UB](https://en.cppreference.com/w/cpp/language/ub),
      [SEI CERT C/C++](https://wiki.sei.cmu.edu/confluence/display/seccode).
      
      ## 1. The high-frequency UB catalog
      
      - **Signed integer overflow** (CERT INT32-C) — `INT_MAX + 1` is UB; the
        compiler may assume `x + 1 > x` always and remove your overflow check.
        Unsigned overflow is *defined* (modular), so it's safe but can still produce
        logic bugs.
      - **Out-of-bounds access** — indexing/pointer past an object (incl. one-past-
        the-end deref). Often the optimizer assumes in-bounds and reorders.
      - **Use of uninitialized values** (CERT EXP33-C) — reading an automatic
        variable before assignment. Initialize at declaration. C++26 (P2795)
        downgrades this from UB to defined "erroneous behavior" — still a bug, but
        no longer optimizer-exploitable once you compile as C++26.
      - **Null / misaligned / invalid pointer deref** — incl. calling a method on a
        null `this`. The optimizer may assume a dereferenced pointer is non-null and
        delete subsequent null checks.
      - **Strict aliasing violation** (§3) — accessing an object through an
        incompatible type.
      - **Data races** (§5, `rules/05`) — concurrent access where ≥1 is a write,
        without synchronization, is UB.
      - **Invalid shifts** — shifting by ≥ width, or shifting a negative/by-negative
        (CERT INT34-C). `x << 32` on a 32-bit `int` is UB.
      - **Signed→unsigned surprises, modifying a `const` object, infinite loops with
        no side effects, calling through a wrong function-pointer type.**
      
      ## 2. Integer overflow and conversions
      
      - Validate ranges *before* arithmetic that could overflow, especially when the
        result feeds an allocation size, array index, or loop bound — overflow-to-
        small-allocation is a classic exploit primitive (`rules/04`).
      - Use checked arithmetic: GCC/Clang `__builtin_add_overflow`/`mul_overflow`,
        or C23 `<stdckdint.h>` `ckd_add`/`ckd_mul`. For C++ prefer typed wrappers or
        range checks; C++26 adds saturating helpers in `<numeric>`
        (`std::add_sat`/`sub_sat`/`mul_sat`, P0543).
      - Avoid implicit narrowing/sign conversions; compile with `-Wconversion
        -Wsign-conversion`. Brace-init (`int x{expr};`) rejects narrowing at compile
        time.
      - Index/size types: prefer unsigned (`size_t`) for sizes but beware unsigned
        *wraparound* in subtractions (`a - b` when `b > a` is a huge number) — guard
        the order.
      
      ```cpp
      // BAD — n*size can overflow to a small value; tiny alloc, then huge copy
      T* p = (T*)malloc(n * sizeof(T));
      // GOOD — checked
      size_t bytes;
      if (__builtin_mul_overflow(n, sizeof(T), &bytes)) return err();
      T* p = (T*)malloc(bytes);
      ```
      
      ## 3. Strict aliasing and type punning
      
      - The compiler assumes objects of unrelated types don't alias, and reorders/
        caches loads accordingly. Reading the bytes of a `float` through an `int*`
        is UB (CERT EXP39-C).
      - **Correct type punning**: `std::bit_cast<To>(from)` (C++20, constexpr, both
        trivially copyable, same size) or `memcpy` into a destination object. Not a
        pointer cast, not a union read-of-other-member in C++ (defined in C, UB in
        C++).
      - `char`, `unsigned char`, and `std::byte` may alias anything — that's how you
        inspect raw bytes legally.
      - `reinterpret_cast` does not bless aliasing; it's the usual source of these
        bugs. Each use needs a comment justifying why it's defined.
      
      ## 4. Alignment, object lifetime, and pointer provenance
      
      - Don't access an object through a misaligned pointer; `alignas`/`alignof` and
        proper allocation matter for SIMD and some ABIs.
      - An object's lifetime begins at initialization and ends at destruction;
        accessing storage outside that window is UB even if the memory is still
        mapped (this is what UAF *is* at the language level). Placement-new + manual
        destructor must bracket any reuse of storage.
      
      ## 5. Data races are UB (see rules/05)
      
      Two threads accessing the same non-atomic object, at least one writing, with no
      happens-before relation, is UB — not "a stale read". Use `std::atomic` or a
      mutex. Build threaded code under TSan.
      
      ## 6. Tooling: make UB visible
      
      - **UBSan** (`-fsanitize=undefined -fno-sanitize-recover=all`) traps signed
        overflow, OOB (some), misalignment, null deref, bad enum/bool values, invalid
        shifts — run the test/fuzz suite under it.
      - `-fsanitize=integer` (Clang) additionally flags *defined-but-suspicious*
        unsigned wrap. `-ftrapv` is a blunter alternative for signed overflow.
      - `-Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wcast-align` catch
        many at compile time. Static analyzers (clang-analyzer, cppcheck) and
        Coverity find aliasing/uninit paths (`rules/06`).
      - Do **not** "fix" a UBSan report by casting it away — fix the arithmetic or
        the access.
      
      ## Audit checklist
      
      ```bash
      # Signed-overflow-prone arithmetic feeding sizes/indices — HIGH
      grep -rnE '(malloc|calloc|alloca|new)[^;]*[*+][^;]*' --include='*.c' --include='*.cpp' .  # size math → check overflow
      grep -rn '__builtin_.*_overflow\|ckd_add\|ckd_mul' . || echo "no checked-arithmetic helpers found"
      
      # Type punning / strict-aliasing — HIGH
      grep -rn 'reinterpret_cast' --include='*.cpp' --include='*.hpp' .
      grep -rnE '\*\s*\(\s*[A-Za-z_][A-Za-z0-9_ ]*\*\s*\)' --include='*.c' .   # C pointer-cast deref (heuristic)
      grep -rn 'union' --include='*.cpp' .                                     # union type-pun is UB in C++
      
      # Bad shifts / conversions — MEDIUM/HIGH
      grep -rnE '<<|>>' --include='*.c' --include='*.cpp' . | grep -vE '(cout|cerr|<<=|stream)'  # verify shift amounts
      # Build with conversion warnings:
      #   -Wconversion -Wsign-conversion -Wshadow -Wcast-align -Wshift-overflow=2
      
      # Uninitialized — MEDIUM
      clang-tidy --checks='cppcoreguidelines-init-variables,clang-analyzer-core.uninitialized.*' <files>
      
      # Ground truth: run under UBSan, aborting on first diagnostic
      #   cmake -DCMAKE_CXX_FLAGS="-fsanitize=undefined,integer -fno-sanitize-recover=all"
      #   ctest   # any abort == CRITICAL/HIGH
      ```
      
    • 04-security.md 7.8 KB
      # 04 — Security: CERT/MISRA, banned APIs, injection, hardened builds
      
      Every byte from network, file, env, argv, IPC, or another process is untrusted
      until validated. In C/C++ the memory-safety classes (`rules/02`) and UB
      (`rules/03`) are themselves the dominant vulnerability surface; this file adds
      the input-handling, API, and build-hardening controls. Standards:
      [SEI CERT C](https://wiki.sei.cmu.edu/confluence/display/c) /
      [CERT C++](https://cmu-sei.github.io/secure-coding-standards/sei-cert-cpp-coding-standard/),
      [MISRA C:2025 / C++:2023](https://misra.org.uk/) (safety-critical),
      [OpenSSF Compiler Hardening Guide](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html).
      
      ## 1. Banned and dangerous functions
      
      Replace on sight (CERT STR/FIO; MISRA):
      
      | Banned | Why | Use instead |
      |---|---|---|
      | `gets` | no bounds; removed in C11 | `fgets`, bounded reader |
      | `strcpy`/`strcat` | no bounds → overflow | `std::string`; or `snprintf`/`strlcpy` |
      | `sprintf`/`vsprintf` | no bounds | `snprintf`/`vsnprintf`; `std::format` (C++20) |
      | `scanf("%s")` | unbounded | width-limited `%Ns`, or parse manually |
      | `system`/`popen` | shell injection | `posix_spawn`/`exec*` with argv array |
      | `strtok` | not reentrant | `strtok_r`/`strtok_s` |
      | `alloca`/VLA on input size | stack overflow | fixed cap or heap + check |
      | `atoi`/`atol` | no error report | `strtol` + range/errno check |
      
      - Prefer C++ types that eliminate the class entirely: `std::string`,
        `std::vector`, `std::format`/`std::print` (C++23), `std::filesystem`.
      
      ## 2. Input validation at the boundary
      
      - Validate once, at the trust boundary, into a typed/bounded value; interior
        code trusts its types. Allowlist (enums, ranges, lengths), not denylist.
      - Bounds- and overflow-check every length/count/offset from input *before*
        using it to allocate, index, or copy (`rules/03` §2). This is the single
        most important control against C/C++ RCE.
      - For binary parsers: never trust an embedded length field; cap it against the
        remaining buffer. Fuzz the parser (`rules/06`).
      - **`assert()` is compiled out by `-DNDEBUG`** — verified: a program whose
        `assert(x > 0)` aborted in a normal build printed `passed` when rebuilt with
        `-DNDEBUG`, which release presets set by default — CMake's
        `Modules/Compiler/GNU.cmake` appends `-DNDEBUG` to the `RELEASE`,
        `RELWITHDEBINFO` **and** `MINSIZEREL` init flags, so every non-Debug build type
        strips them. A bounds or validation check written as `assert`
        therefore does not exist in the shipped binary. Use an explicit `if` that
        returns/aborts, or a hardened contract macro that survives release flags; keep
        `assert` for impossible internal states. Class: `sota-code-security` rules/11 §4.
      
      ## 3. Format-string and injection
      
      - **Never** pass user data as the format string: `printf(user)` is a
        format-string vuln (read/write via `%n`). Use `printf("%s", user)`. Compile
        with `-Wformat -Wformat=2 -Werror=format-security` to catch it.
      - **Command injection**: don't build shell strings. Use `posix_spawn`/`execve`
        with an explicit argument vector and no shell; never `system("cmd " + input)`.
      - **SQL/other injection**: parameterized queries / prepared statements only
        (the DB client API), never string-concatenated SQL — see `sota-databases`.
      - **Path traversal / TOCTOU** (CERT FIO): canonicalize with
        `std::filesystem::weakly_canonical`/`realpath` and verify the result stays
        under an allowed root; prefer `openat`/`O_NOFOLLOW` and operate on fds to
        avoid check-then-use races on the path.
      
      ## 4. Cryptography and randomness
      
      - **Never** use `rand()`/`random()`/`std::mt19937` for security
        (keys, tokens, IVs, salts) — they're predictable. Use the OS CSPRNG:
        `getrandom(2)` / `arc4random_buf` / `BCryptGenRandom`, or a vetted library
        (libsodium, OpenSSL `RAND_bytes`). `std::random_device` is *not* guaranteed
        cryptographic and may be deterministic on some libs.
      - Don't roll your own crypto or protocols; use libsodium/OpenSSL/BoringSSL.
        Constant-time compare for secrets (`sodium_memcmp`, `CRYPTO_memcmp`), never
        `memcmp` on a MAC/token (timing leak). See `sota-code-security` rules/04.
      - Zero secrets after use with a *guaranteed* wipe (`explicit_bzero`,
        `sodium_memzero`, `SecureZeroMemory`) — plain `memset` can be optimized away.
      
      ## 5. Hardened build (the OpenSSF baseline)
      
      Turn these on for production builds (GCC/Clang); missing them on a
      network-facing or setuid binary is a HIGH finding. From the OpenSSF guide:
      
      ```
      -O2 -Wall -Wextra -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough \
      -Werror=format-security \
      -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3      # libc fortified bounds checks
      -D_GLIBCXX_ASSERTIONS                       # libstdc++ bounds assertions
      -fstack-protector-strong                    # stack canaries
      -fstack-clash-protection                    # large-stack probing
      -fcf-protection=full                        # CET: indirect-branch protection
      -fstrict-flex-arrays=3                      # only true flex arrays are unbounded
      -ftrivial-auto-var-init=zero                # zero-init locals (kills uninit reads)
      -fzero-init-padding-bits=all                # zero padding bits too (GCC 15+)
      -mbranch-protection=standard                # AArch64 PAC/BTI (-fcf-protection analogue)
      -fPIE -pie                                  # ASLR for the executable
      -Wl,-z,relro -Wl,-z,now                     # full RELRO (GOT read-only)
      -Wl,-z,noexecstack -Wl,-z,nodlopen          # non-exec stack, no dlopen
      ```
      
      - libc++ builds: production uses hardening mode FAST
        (`-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST`, cheap checks); the
        EXTENSIVE mode (`rules/02`) is for debug/test builds.
      - Add `-fsanitize=address,undefined` to the *debug/test* build (not prod).
        Consider `-fhardened` (GCC 14+) as a shorthand umbrella — verify your
        compiler version supports it.
      - Treat warnings as errors (`-Werror`) in CI; a clean `-Wall -Wextra` is the
        floor, not the ceiling — also run a static analyzer (`rules/06`).
      
      ## 6. Memory-safety strategy (the meta-control)
      
      - Where feasible, move new untrusted-input-parsing code to a memory-safe
        language (Rust), or isolate the C/C++ parser (sandbox/seccomp, separate
        process) — see `sota-sandboxing`. CISA/NSA and the OpenSSF now treat "C/C++
        for new attack-surface code" as a risk decision, not a default.
      - Use `std::span`/`std::string_view`/containers instead of pointer+length
        everywhere they fit; enable libc++/libstdc++ hardened mode (`rules/02`).
      
      ## Audit checklist
      
      ```bash
      # Banned functions — HIGH/CRITICAL
      grep -rnwE '(gets|strcpy|strcat|sprintf|vsprintf|stpcpy|scanf|system|popen|strtok|atoi|atol)' \
        --include='*.c' --include='*.cpp' --include='*.h' .
      grep -rnE '\balloca\b|\[[^]]*\] *= *\{?' --include='*.c' .   # VLA/alloca on dynamic size
      
      # Format string — CRITICAL (user-controlled fmt)
      grep -rnE '(printf|fprintf|snprintf|syslog|err|warn)\s*\([^,"]*\)' --include='*.c' --include='*.cpp' .
      # build with: -Wformat=2 -Werror=format-security
      
      # Command/path injection, TOCTOU — HIGH/CRITICAL
      grep -rnE 'system\(|popen\(|exec[lv]p?\(' --include='*.c' --include='*.cpp' .
      grep -rnE 'fopen|open\(|realpath|access\(' --include='*.c' --include='*.cpp' .  # check-then-use races
      
      # Insecure randomness for security — HIGH
      grep -rnE '\b(rand|random|srand|mt19937|random_device)\b' --include='*.cpp' --include='*.c' .
      grep -rn 'memcmp' --include='*.cpp' . | grep -iE 'mac|hmac|token|secret|sig|digest'  # timing leak
      
      # Hardening flags present? — HIGH if missing on network/setuid binary
      grep -rnE '_FORTIFY_SOURCE|stack-protector|relro|cf-protection|_GLIBCXX_ASSERTIONS|fPIE' \
        . --include='CMakeLists.txt' --include='*.cmake' --include='Makefile*' || echo "no hardening flags found"
      
      # Static + safety-standard analysis
      clang-tidy --checks='cert-*,bugprone-*,clang-analyzer-security.*' <files>
      cppcheck --enable=warning,portability --addon=cert <src>
      ```
      
    • 05-concurrency.md 5.3 KB
      # 05 — Concurrency: the memory model, races, atomics, locks
      
      A **data race** — two threads accessing the same non-atomic object, at least
      one writing, with no happens-before ordering — is undefined behavior in both C
      and C++ (`rules/03`), not merely a stale read. The optimizer assumes
      race-freedom, so a race can corrupt unrelated state. Synchronize all shared
      mutable state, or make it `atomic`. Build threaded code under ThreadSanitizer.
      Reference: [cppreference memory model](https://en.cppreference.com/w/cpp/language/memory_model).
      
      ## 1. Default: don't share mutable state
      
      - Prefer message passing, ownership transfer (move a `unique_ptr` to the
        worker), or immutable shared data over shared mutable state. The cheapest
        race to fix is the one you don't create.
      - Confine mutable data to one thread; communicate via queues. If you must
        share, every access goes through one synchronization discipline documented at
        the type.
      
      ## 2. Mutexes and RAII locking
      
      - Lock with RAII: `std::lock_guard` (simple scope), `std::scoped_lock` (one or
        more mutexes, deadlock-free acquisition), `std::unique_lock` (when you need
        to unlock early or move). Never bare `mutex.lock()`/`unlock()` — an exception
        or early return leaks the lock.
      - **Lock ordering**: acquire multiple mutexes in a single global order, or use
        `std::scoped_lock(m1, m2)` which avoids the deadlock. Document the order.
      - Keep critical sections small; never call user callbacks, allocate heavily, or
        block on I/O while holding a lock. Don't hold a lock across a `condition_
        variable` wait except via the `unique_lock` it manages.
      - `std::condition_variable`: always wait with a predicate
        (`cv.wait(lk, [&]{ return ready; })`) to handle spurious wakeups and lost
        wakeups; signal after mutating the shared state under the lock.
      
      ```cpp
      // GOOD — RAII lock, predicate wait
      std::mutex m; std::condition_variable cv; bool ready=false;
      void producer(){ { std::lock_guard lk(m); ready=true; } cv.notify_one(); }
      void consumer(){ std::unique_lock lk(m); cv.wait(lk, []{return ready;}); use(); }
      ```
      
      ## 3. Atomics and memory order
      
      - `std::atomic<T>` for lock-free flags/counters. Default operations use
        `memory_order_seq_cst` — correct and the right starting point. Only weaken
        (acquire/release, relaxed) with a written justification and a model in mind;
        relaxed atomics are an expert tool and a frequent source of subtle bugs.
      - Use atomics for the *synchronization variable*; the data it publishes is made
        visible by the acquire/release pairing. A non-atomic flag checked across
        threads is a race even if "it's just a bool".
      - `volatile` is **not** for threading — it does not provide atomicity or
        ordering (it's for memory-mapped I/O / signal handlers). Using `volatile` as
        a thread-sync mechanism is a bug.
      - Prefer higher-level tools where they fit: `std::atomic_ref` (C++20) for
        atomic ops on non-atomic storage, `std::latch`/`std::barrier`/`std::
        counting_semaphore` (C++20) for coordination.
      
      ## 4. Threads, futures, and cancellation
      
      - Prefer `std::jthread` (C++20) over `std::thread`: it joins in its destructor
        (no `std::terminate` from a forgotten join) and carries a `std::stop_token`
        for cooperative cancellation. A bare `std::thread` not joined/detached before
        destruction calls `std::terminate`.
      - Pass data to threads by value or via owned handles; capturing references/
        `[&]` into a thread that outlives the scope is a dangling-reference race
        (`rules/02`).
      - `std::async` with `std::launch::async` for simple fan-out + `future.get()`;
        beware the returned future's destructor blocks. For pools, use a vetted
        library (oneTBB, a thread-pool lib) rather than hand-rolling.
      - Watch for false sharing: hot per-thread counters on the same cache line
        serialize; pad/align to `std::hardware_destructive_interference_size`
        (`rules/07`).
      
      ## 5. Tooling
      
      - **TSan** (`-fsanitize=thread`) is the ground truth for races and lock-order
        inversions — run the concurrent tests under it in CI. It's mutually exclusive
        with ASan (separate job) and has memory/latency overhead.
      - `-Wthread-safety` (Clang thread-safety annotations: `GUARDED_BY`,
        `REQUIRES`) gives compile-time race checking when you annotate. Helgrind
        (Valgrind) is a no-recompile alternative to TSan.
      
      ## Audit checklist
      
      ```bash
      # Bare lock/unlock (no RAII) — MEDIUM/HIGH (lock leak on exception)
      grep -rnE '\.(lock|unlock)\(\)' --include='*.cpp' .            # prefer lock_guard/scoped_lock
      grep -rn 'pthread_mutex_lock' --include='*.c' --include='*.cpp' .
      
      # volatile used for threading — HIGH (not a sync primitive)
      grep -rn 'volatile' --include='*.cpp' --include='*.c' . | grep -iE 'flag|ready|done|count|shared'
      
      # Unjoined std::thread / detached without lifetime reasoning — MEDIUM
      grep -rn 'std::thread' --include='*.cpp' . | grep -v jthread     # verify join/detach + arg lifetimes
      grep -rn '\.detach()' --include='*.cpp' .
      
      # Relaxed/weak memory order without justification — MEDIUM
      grep -rnE 'memory_order_(relaxed|acquire|release|consume)' --include='*.cpp' .
      
      # condition_variable wait without predicate — MEDIUM (spurious/lost wakeup)
      grep -rnE 'cv?\.wait\([^,)]*\)' --include='*.cpp' .              # one-arg wait == no predicate
      
      # Ground truth: run concurrent tests under TSan
      #   cmake -DCMAKE_CXX_FLAGS="-fsanitize=thread" && ctest   # any race report == CRITICAL
      ```
      
    • 06-build-tooling-ci.md 5.4 KB
      # 06 — Build, tooling, and CI
      
      A C/C++ project's safety is only as good as its build and CI gates. The
      toolchain is where warnings-as-errors, static analysis, sanitizers, fuzzing,
      and supply-chain controls are enforced. This file owns build/test *mechanics*;
      test **strategy** (suite shape, doubles, coverage philosophy) lives in
      `sota-testing`.
      
      ## 1. CMake hygiene (the de-facto standard)
      
      - Use modern, target-based CMake (≥3.20): `target_link_libraries`,
        `target_compile_features(tgt PUBLIC cxx_std_23)`,
        `target_compile_options`/`target_include_directories` with `PRIVATE`/
        `PUBLIC`/`INTERFACE` scoping. Avoid global `include_directories`,
        `link_libraries`, and `CMAKE_CXX_FLAGS` mutation.
      - Set the standard explicitly and require it:
        `set(CMAKE_CXX_STANDARD 23)`, `CMAKE_CXX_STANDARD_REQUIRED ON`,
        `CMAKE_CXX_EXTENSIONS OFF` (no `-std=gnu++23` unless you mean it).
      - Treat compiler/linker warnings as errors in CI builds. Generate
        `compile_commands.json` (`CMAKE_EXPORT_COMPILE_COMMANDS ON`) so clang-tidy/
        clang-analyzer see exact flags.
      - Pin the toolchain (compiler version) in CI; build with multiple compilers
        (GCC + Clang, and MSVC if you ship Windows) — each finds different bugs.
      - CMake 4.x removed compatibility with policy versions <3.5:
        `cmake_minimum_required(<3.5)` now errors (`CMAKE_POLICY_VERSION_MINIMUM` is
        the escape hatch). Audit legacy subprojects and FetchContent deps for old
        floors before a CMake 4 toolchain bump.
      
      ## 2. Warnings and static analysis
      
      - Baseline flags: `-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion
        -Wshadow -Wcast-align -Wnull-dereference -Wdouble-promotion
        -Wimplicit-fallthrough -Werror`. MSVC: `/W4 /permissive- /WX`.
      - **clang-tidy** with a curated set is the primary linter:
        `bugprone-*, cppcoreguidelines-*, cert-*, performance-*, modernize-*,
        clang-analyzer-*, misc-*` (tune noisy checks). Commit a `.clang-tidy`.
      - **cppcheck** (`--enable=warning,performance,portability --addon=cert`) and the
        **Clang Static Analyzer** (`scan-build` or via clang-tidy) catch path-
        sensitive bugs the compiler misses. Commercial: Coverity, PVS-Studio for
        deeper interprocedural analysis.
      - **clang-format** with a committed `.clang-format`; enforce in CI
        (`--dry-run --Werror`) so style never enters review.
      
      ## 3. Sanitizers in CI (non-negotiable)
      
      - A dedicated job builds Debug with `-fsanitize=address,undefined
        -fno-sanitize-recover=all` and runs the full test suite; any abort fails CI
        (`rules/02`, `rules/03`). A second job runs `-fsanitize=thread` for
        concurrent code (`rules/05`). MSan optionally (needs instrumented libs).
      - Set `ASAN_OPTIONS=detect_leaks=1:strict_string_checks=1` and
        `UBSAN_OPTIONS=print_stacktrace=1` in CI.
      - Sanitizer builds are for test/CI, not production; production uses the
        hardened flag set (`rules/04` §5).
      
      ## 4. Fuzzing for input parsers
      
      - Any code parsing untrusted bytes (network, file formats, decoders) gets a
        fuzz target: prefer **AFL++** or **FuzzTest**/Centipede for new targets —
        libFuzzer is in maintenance mode (bug fixes only, per the LLVM docs; its
        authors moved to Centipede), though the `-fsanitize=fuzzer` interface and
        existing libFuzzer targets remain supported. Run continuously; enroll
        high-value OSS in [OSS-Fuzz](https://google.github.io/oss-fuzz/).
      - Keep a seed corpus and regression corpus in-repo; a new crash is a CRITICAL
        finding. Pair fuzzing with ASan/UBSan so memory/UB bugs surface.
      
      ## 5. Dependencies and supply chain
      
      - Use a real package/dependency manager: **vcpkg** or **Conan** with a
        *manifest* and a **lockfile** (`vcpkg.json`+baseline / `conan.lock`) so
        builds are reproducible and versions are pinned. Avoid vendoring random
        source or system-package drift.
      - Pin versions; review and update deliberately (Dependabot/Renovate where
        supported). Verify checksums/signatures of fetched artifacts. Generate an
        **SBOM** (CycloneDX/SPDX) for releases and scan dependencies for known CVEs.
        See `sota-devsecops`.
      - Minimize the dependency tree; each header-only or binary dep is attack
        surface and a build-integrity risk. Prefer the standard library.
      
      ## 6. Reproducible, deterministic builds
      
      - Avoid timestamps/paths leaking into binaries (`-ffile-prefix-map`,
        `SOURCE_DATE_EPOCH`); enable LTO for release (`-flto`) but verify it doesn't
        mask UBSan. Keep debug info (`-g`) and ship split symbols.
      
      ## Audit checklist
      
      ```bash
      # Warnings-as-errors and standard pinned?
      grep -rnE 'Werror|/WX' . --include='CMakeLists.txt' --include='*.cmake' --include='Makefile*' || echo "no -Werror"
      grep -rnE 'CXX_STANDARD|cxx_std_|std=c\+\+' CMakeLists.txt 2>/dev/null
      
      # clang-tidy / clang-format / cppcheck configs present?
      ls .clang-tidy .clang-format 2>/dev/null | grep -q . || echo "missing lint/format config"
      test -f compile_commands.json || grep -rn EXPORT_COMPILE_COMMANDS CMakeLists.txt
      
      # Sanitizer & fuzzing jobs in CI?
      grep -rniE 'fsanitize|asan|ubsan|tsan|libfuzzer|oss-fuzz|scan-build' .github/ ci/ 2>/dev/null \
        || echo "no sanitizer/fuzz job found — HIGH for input-parsing code"
      
      # Dependency manager + lockfile?
      ls vcpkg.json conan.lock conanfile.* 2>/dev/null | grep -q . || echo "no pinned dependency manifest/lockfile"
      grep -rni 'FetchContent\|ExternalProject\|git submodule' CMakeLists.txt .gitmodules 2>/dev/null  # verify pinning
      
      # Global (non-target) CMake anti-patterns — LOW/MEDIUM
      grep -rnE 'include_directories\(|link_libraries\(|^set\(CMAKE_CXX_FLAGS' CMakeLists.txt 2>/dev/null
      ```
      
    • 07-performance.md 5 KB
      # 07 — Performance: profile, allocate less, respect the cache
      
      C++ gives you control over memory layout and dispatch that few languages do —
      which means the wins come from *data layout and allocation*, not micro-tweaks.
      Measure first: optimize against a profiler and a benchmark, never a hunch.
      Cross-reference `sota-performance` for the discipline (latency budgets,
      regression gates); this file is the C/C++ specifics.
      
      ## 1. Measure before optimizing
      
      - Profile a release build (`-O2`/`-O3 -g`) under realistic load: **perf**
        (Linux, `perf record`/`report`, flame graphs), **Callgrind/KCachegrind**
        (instruction-level), **VTune** (Intel, microarchitecture), or **Instruments**
        (macOS). Find the hot function/loop; don't guess.
      - Benchmark with a real harness — **Google Benchmark** — not `chrono` around a
        loop. Beware the optimizer deleting your benchmark: use
        `benchmark::DoNotOptimize`/`ClobberMemory`. Pin frequency/affinity; report
        distribution, not a single number (see `sota-performance`).
      
      ## 2. Allocation is the usual bottleneck
      
      - Heap allocation (`new`/`malloc`) is expensive and a contention point. Reduce
        allocations on hot paths:
        - `reserve()` containers to final size; avoid repeated `push_back`
          reallocation.
        - Reuse buffers across iterations instead of reallocating per call.
        - Prefer stack/`std::array` for small fixed sizes; small-buffer-optimized
          types (`std::string`) avoid heap for short data.
        - Custom allocators / memory pools / `std::pmr` (polymorphic memory
          resources, `monotonic_buffer_resource`) for allocation-heavy phases.
      - Avoid hidden copies: pass big objects by `const&`; `std::move` into sinks;
        use `emplace_back` to construct in place; watch implicit copies in
        range-`for` (`for (auto x : v)` copies — use `const auto&`).
      
      ## 3. Copy elision and move
      
      - Return by value and rely on (N)RVO — do **not** `return std::move(local)`,
        which disables NRVO (`rules/01`). Guaranteed copy elision (C++17) makes
        returning prvalues free.
      - Make types cheaply movable (`noexcept` moves) so containers move instead of
        copy on growth.
      
      ## 4. Cache locality and data-oriented design
      
      - Memory latency dominates. Favor contiguous storage (`vector`/`array`) over
        node-based containers (`list`, `map`, pointer-chasing trees) on hot paths;
        `std::flat_map`/`flat_set` (C++23) are cache-friendly alternatives.
      - Structure-of-arrays (SoA) over array-of-structures (AoS) when you iterate one
        field across many elements — packs the hot field into cache lines.
      - Avoid false sharing: align/pad per-thread hot data to
        `std::hardware_destructive_interference_size` (`rules/05`). Keep
        frequently-accessed fields together; cold fields elsewhere.
      - Branch-predictable, vectorizable loops beat clever branchy code; `[[likely]]`/
        `[[unlikely]]` only with profile evidence.
      
      ## 5. Use the standard library and the compiler
      
      - Prefer `<algorithm>`/ranges (C++20) over hand-rolled loops — they're
        optimized, vectorizable, and correct. `std::sort`, `std::ranges::*`.
      - Let the compiler do the work: `-O2` (usually the sweet spot), `-march=native`
        only when you control the target CPU. **LTO** (`-flto`) for cross-TU
        inlining. **PGO** (profile-guided optimization,
        `-fprofile-generate`/`-fprofile-use`) for measurable wins on hot workloads.
      - `constexpr`/`consteval` move work to compile time. `[[nodiscard]]` and
        `[[gnu::pure]]`/`const` attributes can enable optimization.
      
      ## 6. Common pitfalls
      
      - Premature optimization that hurts readability for unmeasured gain (LOW
        finding — but so is shipping an obvious O(n²) on a hot path that a profiler
        would catch).
      - `std::endl` in loops (flushes every call — use `'\n'`); `shared_ptr` where
        `unique_ptr` suffices (atomic refcount cost); virtual calls in tight inner
        loops; `std::function` where a template/`auto` lambda would inline.
      - Debug-build performance numbers (sanitizers/`-O0` are 2–50x slower — never
        benchmark them).
      
      ## Audit checklist
      
      ```bash
      # Copies that should be references/moves — LOW
      grep -rnE 'for *\( *auto [A-Za-z_]+ *:' --include='*.cpp' .     # range-for by value → const auto&
      grep -rn 'return std::move' --include='*.cpp' .                 # disables NRVO
      grep -rnE '\.push_back\(' --include='*.cpp' .                   # reserve()? emplace_back?
      
      # Allocation on hot paths / node containers — LOW/MEDIUM (verify with profiler)
      grep -rnE 'std::(list|map|set|unordered_map|unordered_set)<' --include='*.cpp' .  # cache-unfriendly?
      grep -rn 'shared_ptr' --include='*.cpp' .                       # needed, or unique_ptr?
      
      # Flush-per-line — LOW
      grep -rn 'std::endl' --include='*.cpp' .
      
      # Benchmark hygiene — verify release build + DoNotOptimize
      grep -rn 'chrono::' --include='*.cpp' . | grep -i bench         # prefer Google Benchmark
      grep -rn 'DoNotOptimize\|ClobberMemory' --include='*.cpp' .
      
      # Optimization flags for release?
      grep -rnE '\-O[23]|-flto|fprofile-(generate|use)|march=' CMakeLists.txt cmake/ 2>/dev/null
      
      # Profile first (no static grep): perf record -g ./bench && perf report
      ```
      
  • SKILL.md 11.3 KB
    ---
    name: sota-c-cpp
    description: >-
      State-of-the-art C and C++ engineering rules (2026 baseline) that Claude
      applies when writing or auditing C/C++. Covers modern idioms (RAII, value
      semantics, smart pointers, C++23), memory safety (lifetimes, bounds,
      sanitizers, hardening flags), undefined behavior, security (SEI CERT C/C++,
      MISRA, integer/buffer/format-string, injection), concurrency (C/C++ memory
      model, atomics, data races), build/tooling/CI (CMake, clang-tidy, cppcheck,
      ASan/UBSan/TSan, vcpkg/Conan, supply chain), and performance. Trigger keywords
      - C, C++, RAII, smart pointer, unique_ptr, shared_ptr, undefined
      behavior, UB, buffer overflow, use-after-free, sanitizer, ASan,
      UBSan, TSan, valgrind, CMake, clang-tidy, cppcheck, MISRA, CERT
      C, memory safety, std::thread, atomics, std::move. Use for BOTH building
      C/C++ libraries/systems and reviewing or auditing them. Owns firmware's LANGUAGE
      layer; does NOT own its SYSTEMS layer — ISRs, DMA coherency, MMIO, RTOS
      scheduling, priority inversion, WCET, linker scripts — unowned library-wide.
    ---
    
    # SOTA C & C++ (2026)
    
    Expert-level rules for producing and auditing production C and C++. C and C++
    are *memory-unsafe by default*: the compiler will not stop you from reading
    freed memory, overrunning a buffer, or invoking undefined behavior (UB) that
    the optimizer then weaponizes. These rules exist to claw back the safety the
    language doesn't give you — through RAII, the type system, sanitizers, hardened
    build flags, and disciplined review. Baseline: C++23 (ISO/IEC 14882:2024) and
    C17/C23; flag where a control needs a newer toolchain. C++26 is feature-complete
    (DIS ballot 2026) — contracts, reflection, erroneous behavior for uninitialized
    reads, and a standardized hardened standard library; the last is usable today
    via libc++/libstdc++ hardening flags (`rules/02`). Every rule states the
    *why*; every rules file ends with an audit checklist of grep/clang-tidy/
    sanitizer patterns.
    
    ## Purpose
    
    Two consumers, one source of truth:
    
    - **BUILD mode** — generating new C/C++: follow the rules as defaults, not
      suggestions. Prefer C++ with RAII over raw C idioms unless the target is C.
      Deviate only with a comment justifying it.
    - **AUDIT mode** — reviewing existing C/C++: hunt violations using the audit
      checklists, classify by severity, report in the finding format below. Memory-
      safety and UB findings are presumed exploitable until proven otherwise.
    
    **What this skill covers about embedded work, and what it does not.** It carries the
    safety-critical *coding* standards — MISRA C:2025 / C++:2023 and SEI CERT (`rules/04`),
    freestanding builds with exceptions disabled (`rules/01` §7), banned APIs and hardened
    build flags — so a firmware codebase gets real coverage of the language layer. It does
    **not** cover the systems layer that makes embedded work its own discipline: interrupt
    service routines and reentrancy, DMA buffer coherency, memory-mapped I/O and `volatile`
    semantics against a peripheral, RTOS task scheduling and priority inversion, WCET and
    timing analysis, or linker scripts and startup code. No skill in this library owns those
    today. Say so when the task reaches them rather than generalising from the desktop rules —
    guidance that sounds authoritative outside its scope is the failure `sota-skill-security`
    exists to catch.
    
    ## BUILD mode
    
    1. Before writing, read the rules files relevant to the task (see index). A
       parser handling untrusted bytes needs `02`, `03`, `04`; a threaded service
       needs `05`.
    2. Apply the **top-10 non-negotiables** (below) unconditionally.
    3. New projects: CMake (≥3.20) with `-Wall -Wextra -Wpedantic -Werror`, the
       [OpenSSF hardening flags](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html)
       (`rules/04`), a debug build wired to ASan+UBSan, clang-tidy + clang-format
       configs, and CI running all of it from day one (`rules/06`).
    4. Prefer the standard library and RAII types over hand-rolled
       allocation/ownership. Every `new`/`malloc`/`fopen`/`mutex.lock()` should be
       owned by a destructor (`unique_ptr`, container, `lock_guard`), not a manual
       matching call you can forget on an early return or exception.
    5. Treat warnings as errors. A clean `-Wall -Wextra` build is the floor, not
       the goal — also run a static analyzer and the sanitizers (`rules/06`).
    6. When you must use a sharp tool (raw pointer arithmetic, `reinterpret_cast`,
       `unsafe` C interop, manual lifetime), leave a `// NOTE(sota):` comment
       explaining the invariant you're upholding so auditors don't flag it blind.
    
    ## AUDIT mode
    
    Work through each relevant rules file's audit checklist against the target.
    Run the listed grep/clang-tidy/sanitizer commands; confirm each hit manually
    (greps are recall-oriented). Where feasible, build with `-fsanitize=address,
    undefined` and run the test suite — a sanitizer abort is ground truth.
    
    ### Severity conventions
    
    | Severity | Meaning | Examples |
    |---|---|---|
    | **CRITICAL** | Exploitable memory corruption or guaranteed UB on reachable input | Heap/stack buffer overflow on attacker data, use-after-free, double-free, OOB write, format-string with user-controlled fmt, `system()` with interpolated input, data race on a pointer |
    | **HIGH** | Likely corruption, crash, or security weakness | Unchecked `malloc`/`new` size from input, integer overflow feeding an allocation or index, missing bounds check, `strcpy`/`sprintf`/`gets`, TOCTOU on a path, missing RAII so a leak/UB occurs on the exception path |
    | **MEDIUM** | Correctness/maintainability hazard, latent bug | Raw owning pointers, manual `new`/`delete` pairs, C-style casts, narrowing conversions, `memcpy` where a typed copy fits, missing `override`/`= delete`, signed/unsigned comparison |
    | **LOW** | Idiom/perf debt, works but wrong shape | Pass-by-value of large objects, needless copies instead of `std::move`, `using namespace std` in headers, macros where `constexpr`/`inline` fits |
    | **INFO** | Style/doc/hygiene | clang-format drift, naming, missing `[[nodiscard]]`, include hygiene |
    
    ### Finding format
    
    ```
    [SEVERITY] file.cpp:LINE — short title
      Rule: rules/NN-name.md § section
      Evidence: the offending line(s), verbatim
      Impact: one sentence — what corrupts/leaks/races, under what input
      Fix: concrete replacement code or action
      Effort: trivial | small | medium | large
    ```
    
    Group findings by severity, CRITICAL first. End with: counts per severity, the
    three highest-leverage fixes, and which checklists/sanitizers were run.
    
    ## Rules index
    
    | File | Read this when... |
    |---|---|
    | `rules/01-idioms.md` | Writing/reviewing any C++: RAII and the rule of zero/five, ownership with `unique_ptr`/`shared_ptr`, value semantics and move, `const`/`constexpr`, references vs pointers, casts, `enum class`, error handling (exceptions vs `std::expected` vs error codes), **in-band sentinels (absence encoded as `-1`/`0`/`""`)** incl. **`EOF` in a `char` — broken only where `char` is unsigned**, C-vs-C++ idiom choices |
    | `rules/02-memory-safety.md` | Anything touching pointers, buffers, lifetimes, or allocation: bounds, use-after-free/return, dangling references and views (`string_view`/`span`), iterator invalidation, ownership discipline, sanitizers (ASan/MSan), `_FORTIFY_SOURCE`/`_GLIBCXX_ASSERTIONS` |
    | `rules/03-undefined-behavior.md` | Reasoning about UB and the optimizer: integer overflow, strict aliasing, uninitialized reads, null/misaligned access, signed shifts, data races as UB, `unsigned` arithmetic, UBSan, why "it worked in debug" proves nothing |
    | `rules/04-security.md` | Any input crossing a trust boundary: CERT C/C++ + MISRA, banned functions (`gets`/`strcpy`/`sprintf`/`system`), integer-overflow-to-allocation, format strings, path traversal/TOCTOU, command injection, deserialization/parsers, CSPRNG, the OpenSSF hardening flag set |
    | `rules/05-concurrency.md` | Anything with threads, atomics, or shared state: the C++ memory model, data races, `std::atomic` and memory orders, `mutex`/`lock_guard`/`scoped_lock`, deadlock ordering, condition variables, `std::jthread`/stop tokens, TSan |
    | `rules/06-build-tooling-ci.md` | Setting up or auditing builds/CI: CMake hygiene, warning flags, clang-tidy/clang-format, static analysis (clang-analyzer, cppcheck, Coverity), sanitizer CI matrix, fuzzing (libFuzzer/OSS-Fuzz), dependencies and supply chain (vcpkg/Conan, pinning, SBOM). **Test *strategy* lives in `sota-testing`; this file owns C/C++ build/test mechanics.** |
    | `rules/07-performance.md` | Latency/throughput/memory work: profiling (perf, VTune, Callgrind), allocation reduction and custom allocators, move/copy elision, cache locality and data-oriented layout, `<algorithm>` over hand loops, LTO/PGO, micro-benchmarking pitfalls |
    
    ## Top-10 non-negotiables
    
    1. **Every resource is owned by a destructor (RAII).** No `new`/`delete` or
       `malloc`/`free` pairs you have to match by hand; no bare owning pointers.
       Use `unique_ptr`, containers, `lock_guard`/`scoped_lock`, RAII wrappers.
       A leak or UB on the exception/early-return path is the default failure mode
       of manual cleanup. (`rules/01`, `rules/02`)
    2. **No buffer touches memory it doesn't own.** Bounds-check every index/
       length derived from input; use `std::span`/`std::string`/containers and
       `.at()` or explicit checks, never raw pointer + length you assume. Overflow
       on attacker input is CRITICAL. (`rules/02`)
    3. **No use-after-free / dangling.** A pointer, reference, iterator,
       `string_view`, or `span` must not outlive its storage. Never return a
       reference/view to a local or to a temporary. (`rules/02`)
    4. **Undefined behavior is a bug even if it "works".** Signed integer
       overflow, strict-aliasing violations, uninitialized reads, OOB, data races
       are UB the optimizer may exploit. Build with UBSan; treat any UBSan
       diagnostic as CRITICAL/HIGH. (`rules/03`)
    5. **Integers feeding an allocation, index, or `memcpy` size are
       overflow-checked and the right signedness.** Validate ranges before use;
       prefer unsigned for sizes, check for wrap. Overflow-to-undersize-alloc is a
       classic RCE primitive. (`rules/03`, `rules/04`)
    6. **Banned functions are banned.** No `gets`, `strcpy`/`strcat`/`sprintf`
       (use bounded forms or `std::string`/`std::format`), no `system()` with
       interpolated input (use `posix_spawn`/`exec*` with an argv array). (`rules/04`)
    7. **Build hardened, by default.** `-Wall -Wextra -Werror` plus the OpenSSF
       set (`-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-protector-strong
       -fstack-clash-protection -fcf-protection -Wl,-z,relro,-z,now`). Missing
       hardening on a network-facing binary is a HIGH finding. (`rules/04`,
       `rules/06`)
    8. **Shared mutable state is synchronized; data races are CRITICAL.** Guard
       with a `mutex`/`scoped_lock` or use `std::atomic` with a justified memory
       order. A `-fsanitize=thread` failure is not flaky noise. (`rules/05`)
    9. **Sanitizers and a static analyzer gate CI.** A debug/test job runs
       ASan+UBSan (and TSan for threaded code); clang-tidy + cppcheck run on every
       PR. Untrusted-input parsers get a fuzz target. (`rules/06`)
    10. **Prefer the type system to convention.** `enum class` over macros,
        `constexpr`/`inline` over `#define`, `gsl::span`/`std::span` over pointer+
        length, `[[nodiscard]]` on must-check returns, `explicit` on single-arg
        constructors, `override`/`final`. Make misuse fail to compile. (`rules/01`)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related