Skip to content

feat(engine): UBSan sanitizer baseline + first UB fix (E1)#14

Merged
dnplkndll merged 1 commit into
modernizationfrom
engine-e1-ubsan-baseline
Jun 18, 2026
Merged

feat(engine): UBSan sanitizer baseline + first UB fix (E1)#14
dnplkndll merged 1 commit into
modernizationfrom
engine-e1-ubsan-baseline

Conversation

@dnplkndll

Copy link
Copy Markdown

What changed

Adds the UndefinedBehaviorSanitizer half of Engine track E1 (ASan was already wired and blocking in engine-asan.yml; the golden suite is ASan-clean). Establishes a documented UBSan baseline over the golden test suite and fixes the one genuine UB bug it surfaced.

Sanitizer wiring

  • CMakeLists.txt — the Debug build's sanitizer is now selectable via -DFREPPLE_SANITIZER (address default = the unchanged ASan gate, undefined = UBSan, address,undefined = both). Sanitizer flags added to the Debug link line for the shared lib.
  • The UBSan build excludes -fsanitize=vptr: frePPLe's object model uses a hand-rolled MetaClass RTTI (downcast by type tag, not C++ polymorphism), which is fundamentally incompatible with the vptr check — it would flood every run with by-design "wrong dynamic type" reports and bury real findings.

CI

  • .github/workflows/engine-ubsan.yml — an advisory gate (halt_on_error=0): builds Debug+UBSan, runs the golden suite with runtest.py -d (so the sanitizer reports are visible, not swallowed by the test runner's pipe), and summarises distinct findings in the job step-summary. It fails on a genuine non-UB test break (pipefail) or a UBSan build/link regression, but not on the documented baseline findings. Flips to blocking in E2 once the iterator idiom is retired — the same way engine-asan.yml started informational and became blocking after its crashes were fixed.

The one real fix

  • src/model/operationdependency.cppset{Operation,BlockedBy} each called addDependency() on a possibly-null receiver (:99, :122, symmetric). UBSan caught :122; :99 is its unexercised mirror. Behaviour-preserving fix (guard the call): Operation::addDependency already early-returns on an incomplete dependency before touching the receiver's members, so guarding is identical in effect — golden output unchanged (verified by the blocking ASan/ubuntu24 suites).

Findings doc

  • tools/modernization/ubsan-baseline.md — full root-cause + severity for all three categories and the path to a blocking gate.

Baseline result

96 pure-engine (type-2) golden tests under UBSan. After excluding vptr (Finding 1, by-design) and fixing operationdependency (Finding 3, real/medium), the only remaining diagnostics are the two accepted iterator operator* null-bindings (timeline.h:293, model.h:8667) — UB by the letter, but the same pattern the standard library has for *v.end(), never actually dereferenced. Documented as low/accepted.

Finding Class Severity Disposition
vptr on MetaClass RTTI by-design false positive noise -fno-sanitize=vptr (excluded)
iterator operator* null-binding UB idiom (STL-parallel) low accepted; gate advisory; retire in E2
operationdependency null member-call real latent UB medium fixed (:99, :122)

Verification

  • Local Debug+UBSan build (g++, ubuntu:24.04) compiles + links with vptr excluded; re-run confirms 0 vptr and 0 operationdependency findings remain, only the two documented idioms.
  • operation_dependency golden test runs clean (no abort) post-fix.
  • The blocking engine-asan + ubuntu24 suites (output comparison) are the safety net for the operationdependency behaviour-preservation claim.

Add the UndefinedBehaviorSanitizer half of Engine track E1 (ASan was already
wired + blocking in engine-asan.yml). Establishes a documented UBSan baseline
over the golden test suite and fixes the one real undefined-behaviour bug it
surfaced.

- CMakeLists.txt: parameterise the Debug sanitizer via -DFREPPLE_SANITIZER
  (address default = unchanged ASan gate; undefined = UBSan; both supported).
  The UBSan build excludes -fsanitize=vptr: frePPLe's hand-rolled MetaClass
  RTTI (downcast by type tag, not C++ polymorphism) is incompatible with the
  vptr check, which would otherwise flood every run with by-design reports.
  Sanitizer flags added to the Debug link line for the shared lib.
- .github/workflows/engine-ubsan.yml: advisory gate (halt_on_error=0) that
  builds Debug+UBSan and runs the golden suite with -d so reports are visible,
  summarising distinct findings in the step summary. Proves the UBSan build
  keeps compiling/linking and tracks findings; flips to blocking in E2 once the
  iterator-idiom null-bindings are retired (mirrors how engine-asan started).
- src/model/operationdependency.cpp: fix a symmetric null member-call in
  set{Operation,BlockedBy} - both called addDependency() on a possibly-null
  receiver. Behaviour-preserving (addDependency no-ops on an incomplete
  dependency before touching the receiver), so the golden output is unchanged.
- tools/modernization/ubsan-baseline.md: full findings, root cause, and
  severity for the three categories (vptr noise / iterator idiom / the real
  fix), plus the path to a blocking gate.

Baseline: 96 type-2 golden tests under UBSan. After excluding vptr and fixing
operationdependency, the only remaining diagnostics are the two accepted
iterator operator* null-bindings (timeline.h, model.h) - STL-parallel UB,
documented.
@dnplkndll dnplkndll merged commit 45a7d1e into modernization Jun 18, 2026
7 checks passed
@dnplkndll dnplkndll deleted the engine-e1-ubsan-baseline branch June 18, 2026 11:21
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