Skip to content

test: runtime sanitizers + static-analysis gate, and the bugs they found#37

Merged
Jo5ta merged 10 commits into
mainfrom
feat/sanitizers-and-analysis-ci
Jul 11, 2026
Merged

test: runtime sanitizers + static-analysis gate, and the bugs they found#37
Jo5ta merged 10 commits into
mainfrom
feat/sanitizers-and-analysis-ci

Conversation

@Jo5ta

@Jo5ta Jo5ta commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Stack 8/8 — based on the registration-index PR (#36).

Adds runtime sanitizers and a static-analysis gate, then fixes what they found.

Infrastructure

  • cmake/Sanitizers.cmake: ENABLE_ASAN/ENABLE_UBSAN/ENABLE_TSAN (ASan+TSan mutually exclusive, UBSan findings fatal). Presets unittests-asan (ASan+UBSan) and unittests-tsan, plus step_sanitizers.sh.
  • CI gains a sanitizers matrix (asan, tsan) and a static-analysis gate. clang-tidy runs diff-only (changed lines) so the decoder's large pre-existing backlog does not block new work; cppcheck runs full-tree and is clean.

Real bugs the sanitizers found (behavior-preserving fixes, 1.7.1)

  • Unaligned access on the byte-packed trace format in the writer and both decoders — *(uint64_t *)p on unpadded offsets is UB and faults on strict-alignment targets like s390x. Now memcpy, which the compiler folds back to a single load/store where the arch allows it.
  • Index leak: unique_stack_close never freed the lazily-allocated lookup index; it does now.
  • Offset-cache data race: concurrent first-callers of a tracepoint raced on the lazy offset cache word — now relaxed atomics (idempotent value, no ordering cost).

Also: genuine static-analysis fixes across library/examples/tools; sanitizer-clean tests (memcpy reads, TSan guards on fault-based death tests, a deterministic drain in the overwhelmed stress test); scoped suppressions for proven false positives and intentional patterns.

Verified in the CI container: normal build 680/680, ASan+UBSan clean, TSan clean, static gate green. (Four condition_variable::wait_for tests can't run under TSan on emulated aarch64 due to missing SVE support locally; they run on x86_64 CI.)

Jo5ta added 10 commits July 11, 2026 08:26
Adds runtime sanitizer verification, which matters more than static
lint for this library - it packs bytes, swaps endianness, uses 48-bit
bitfield offsets, and coordinates through shared-memory mutexes and
atomics, none of which single-TU static analysis can see.

- cmake/Sanitizers.cmake replaces ASAN.cmake: ENABLE_ASAN / ENABLE_UBSAN
  / ENABLE_TSAN (ASan+TSan mutually exclusive, UBSan composes, findings
  made fatal so CI can gate).
- Presets unittests-asan (ASan+UBSan) and unittests-tsan, both non-LTO.
- step_sanitizers.sh runs the suite under a sanitizer with halt-on-error.
- CI gains a static-analysis job (clang-tidy + cppcheck, --werror) and a
  sanitizers matrix (asan, tsan). Container gets libubsan, libtsan, clang.

Infrastructure only; findings fixed in follow-up commits.

Signed-off-by: Jo5ta <jo5ta@mail.de>
The trace format packs multi-byte values at their natural byte offset
with no padding, so reading or writing them through a typed pointer
(*(uint64_t *)p) is an unaligned access - undefined behavior that
UBSan flags and that faults on strict-alignment targets such as s390x.

Writer (arguments.c) and decoder (file.hpp get<>, TracepointInternal
get<>, Tracepoint span line, formatter dump size) now move the bytes
with memcpy, which the compiler folds back to a single load/store where
the architecture allows unaligned access. No format or behavior change.

Signed-off-by: Jo5ta <jo5ta@mail.de>
unique_stack_close reset the handler but never released the lazily
allocated in-memory lookup index, leaking it for any caller that closed
a stack without going through tracebuffer deinit. close() now drops the
index (it owns it), so the raw stack API is leak-free too.

Signed-off-by: Jo5ta <jo5ta@mail.de>
The lazy fallback that resolves a tracepoint's file offset writes a
static cache word; concurrent first-callers of the same tracepoint
raced on it. The value is idempotent (a deterministic offset), so the
reads and writes - in the tracepoint macros and in the startup batch
registration - are now relaxed atomics: race-free with no ordering cost.

Signed-off-by: Jo5ta <jo5ta@mail.de>
Genuine findings from clang-tidy/cppcheck: a dead store on the dump
path, implicit-widening multiplications used as offsets (md5,
tracepoint), else-after-return, a redundant member init, a
non-const-worthy value param, %lu -> %zu for size_t, value-init of a
returned struct on an error path, and a substr self-assignment. False
positives (crc8 table index, dirfd after NULL-check, signed-char in a
saturating cast) get scoped NOLINT with a reason.

Signed-off-by: Jo5ta <jo5ta@mail.de>
- read byte-packed data with memcpy helpers instead of misaligned
  typed pointers (UBSan) in the file, arguments, meta, and definition
  tests
- close the stack in the raw unique_stack tests so the index is freed
  (LeakSanitizer)
- guard the fault-based death tests under TSan (which does not fault on
  OOB/UAF like ASan) via a new CLLTK_TSAN_ENABLED flag
- drain the ringbuffer after joining writers in the overwhelmed stress
  test so the 'no pending' check is deterministic under TSan's slowdown
- value-init test structs to avoid reading uninitialized members

Signed-off-by: Jo5ta <jo5ta@mail.de>
- clang-tidy runs diff-only (changed lines) so the decoder's large
  pre-existing backlog does not block new work; cppcheck stays full-tree
- silence GCC-only warning flags fed to clang-tidy, mark Boost headers
  SYSTEM, and scope suppressions to proven false positives / intentional
  patterns (stack_alloc alloca, section-symbol compare, noexcept startup
  inits, separate-binary ODR, test-only alloca)
- drop the low-signal inconsistent-parameter-name check
- TSan suppressions file for the two intentional test races
- ASan: do not abort_on_error, so death tests observe the default exit(1)
- CLLTK_TSAN_ENABLED test flag and the asan/tsan container packages

Signed-off-by: Jo5ta <jo5ta@mail.de>
Behavior-preserving fixes surfaced by the new sanitizer legs (unaligned
access, index leak, offset-cache race). Patch level - no file layout or
API change.

Signed-off-by: Jo5ta <jo5ta@mail.de>
Earlier formatting used a different clang-format version; reformat with
the container's clang-format (21.1.8) so the format gate passes.

Signed-off-by: Jo5ta <jo5ta@mail.de>
Base automatically changed from feat/registration-index to main July 11, 2026 10:00
@Jo5ta Jo5ta merged commit b2026f0 into main Jul 11, 2026
10 checks passed
@Jo5ta Jo5ta deleted the feat/sanitizers-and-analysis-ci branch July 11, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant