feat(engine): UBSan sanitizer baseline + first UB fix (E1)#14
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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(addressdefault = the unchanged ASan gate,undefined= UBSan,address,undefined= both). Sanitizer flags added to the Debug link line for the shared lib.-fsanitize=vptr: frePPLe's object model uses a hand-rolledMetaClassRTTI (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 withruntest.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 wayengine-asan.ymlstarted informational and became blocking after its crashes were fixed.The one real fix
src/model/operationdependency.cpp—set{Operation,BlockedBy}each calledaddDependency()on a possibly-null receiver (:99,:122, symmetric). UBSan caught:122;:99is its unexercised mirror. Behaviour-preserving fix (guard the call):Operation::addDependencyalready 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.-fno-sanitize=vptr(excluded)operator*null-binding:99,:122)Verification
operation_dependencygolden test runs clean (no abort) post-fix.engine-asan+ubuntu24suites (output comparison) are the safety net for the operationdependency behaviour-preservation claim.