Skip to content

fix(#920): gate the ThruSections test that hits #913's unpatched-kernel crash - #924

Merged
gsdali merged 1 commit into
mainfrom
fix/920-thrusections-unguarded-crash-test
Aug 16, 2026
Merged

fix(#920): gate the ThruSections test that hits #913's unpatched-kernel crash#924
gsdali merged 1 commit into
mainfrom
fix/920-thrusections-unguarded-crash-test

Conversation

@gsdali

@gsdali gsdali commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

What & why

Root causes #920 (and its duplicate #922, closed): the uncatchable SIGSEGV observed on CI across
multiple unrelated PRs since PR #912's round 2, with no test attribution possible from the crashing
process's own output.

generatedFaceIsMemberOfShapeAfterSuccessFailureSuccessOnReusedBuilder
(Tests/OCCTStressTests/StressBuilderLifecycleTests.swift, added in that same round 2)
unintentionally constructs #913's exact crash trigger, unguarded — checkCompatibility(false),
then a 4th section with more edges (a triangle, 3) than section 1 (a circle, 1), 4 sections
total, reaching BRepOffsetAPI_ThruSections::CreateSmoothed()'s fixed-stride array overrun that
patch 0027 (#913) fixes.

Package.swift's remote pin — what ci.yml's swift build + test (macOS) job resolves, and what
any fresh checkout with no local Libraries/ gets by default — is the v2.0.0 release asset: OCCT
V8_0_1 + only the fifteen patches that existed when that release was cut. Patches 0026 (#905) and
0027 (#913) both landed after and are only applied by kernel-integration.yml's from-source
rebuild — never by ci.yml. Every default swift test run exercises the unguarded test against a
kernel that doesn't have #913's fix.

Heap corruption from the array overrun is nondeterministic: sometimes it silently produces a wrong
answer (measured directly below), sometimes it corrupts memory a later, unrelated test's allocation
reuses, producing a SIGSEGV attributed to whatever part of the parallel suite happens to touch it
next — exactly matching every #920 occurrence (a different test cluster each time).

This also explains why local reproduction never worked across roughly ten attempts throughout the
#920 investigation: OCCTSWIFT_LOCAL=1 (used throughout, and also the default whenever a local
Libraries/OCCT.xcframework is present) resolves the locally-rebuilt kernel, which has all
seventeen current patches including 0027. Local runs were never testing the same kernel CI
actually uses.

Measured, not assumed: forced the exact remote kernel with OCCTSWIFT_REMOTE=1 (overriding the
local-directory auto-detection) and ran the suspect test directly:

OCCTSWIFT_REMOTE=1 swift test --filter generatedFaceIsMemberOfShapeAfterSuccessFailureSuccessOnReusedBuilder
✘ StressBuilderLifecycleTests.swift:591: Expectation failed: !(loft.build() → true)
✘ StressBuilderLifecycleTests.swift:592: Expectation failed: (loft.generatedFace(from: edge)) == nil
✘ StressBuilderLifecycleTests.swift:598: Expectation failed: loft.build()

build() on "build B" returned true instead of failing cleanly — matching #913's own "silently
misaligned... reporting build() == true for an invalid result" description of the unpatched
defect exactly. No other test file uses checkCompatibility(false) at all (checked directly), and
no Sources/ code path uses it either, so this is the sole trigger in the tree.

Fix

Gate the test behind OCCTSWIFT_LOCAL, the identical mechanism and reasoning already established
for #913's own equally-dangerous test (mismatchedSectionEdgeCountWithoutCheckFailsCleanly, gated
since PR #915's own review for this exact class of danger). Verified: OCCTSWIFT_REMOTE=1 now
correctly skips it; OCCTSWIFT_LOCAL=1 still runs and passes it, preserving the coverage
#912's round 2 intended.

Closes duplicate #922 (its patch-count observation — 17 patches on Scripts/patches/, only 15 in
the pinned release asset — was the key clue that cracked this).

CHANGELOG entry

None — test-only change, no public API or behavior change.

SemVer impact

None — test-only change.

Checklist

  • New or changed behavior is covered by a unit test in the same PR (not just manual
    verification): N/A in the usual sense — this PR fixes test coverage itself rather than
    adding source behavior. The fix's own correctness is verified below instead.
  • Every new test and every new --self-test case was run once with its subject broken, and the
    failure is reported here: inverted — the test's own failure against the unpatched kernel is
    the evidence the gate is needed (see the OCCTSWIFT_REMOTE=1 transcript above). Verified the
    gate itself works both directions: OCCTSWIFT_REMOTE=1 → skipped; OCCTSWIFT_LOCAL=1 → runs
    and passes.
  • The CHANGELOG entry above is complete, and docs/CHANGELOG.md is not in this diff.
  • The SemVer impact above is stated, and docs/SEMVER.md is not in this diff.

Verification

  • Full swift test forced against the exact remote kernel CI uses (OCCTSWIFT_REMOTE=1 swift test) run 6 times — clean every time, 0 crashes (was previously observed to crash across
    multiple independent CI runs on this exact kernel).
  • All 5 required static gates clean.
  • Searched the whole tree (Sources/ and Tests/) for any other checkCompatibility(false) usage
    or any other path that could reach the same defect — none found; this was the sole trigger.

Notes for the reviewer

Filed as a standalone PR rather than bundled into #918 (the code-style PR that also touches
ThruSections-adjacent code) since this blocks CI broadly — every PR, not just one — and is a
small, self-contained, urgent fix that shouldn't wait on a larger PR's review cycle.

The underlying kernel gap (patches 0026/0027 not yet in the pinned release asset) is not a new
defect to fix here — it's the normal, already-documented pre-release-vs-release-pin lifecycle
(CLAUDE.md's own release process re-points Package.swift at a final asset that includes
everything). This PR fixes the test that shouldn't have run unguarded against the interim state,
matching the precedent already established for #913's own test.

…el crash

Root cause of #920/#922 (uncatchable SIGSEGV, no test attribution,
observed on CI across multiple unrelated PRs since PR #912's round 2):
`generatedFaceIsMemberOfShapeAfterSuccessFailureSuccessOnReusedBuilder`
(added in that same round 2) unintentionally constructs #913's exact
crash trigger, unguarded — checkCompatibility(false), then a 4th
section with MORE edges (a triangle, 3) than section 1 (a circle, 1),
4 sections total, reaching CreateSmoothed()'s fixed-stride array
overrun that patch 0027 fixes.

Package.swift's remote pin (what ci.yml's swift build + test (macOS)
job resolves, and what any fresh checkout with no local Libraries/
gets by default) is the v2.0.0 release asset: OCCT V8_0_1 + only the
fifteen patches that existed when that release was cut. Patches 0026
(#905) and 0027 (#913) both landed after and are only applied by
kernel-integration.yml's from-source rebuild — never by ci.yml. Every
default `swift test` run exercises the unguarded test against a kernel
that doesn't have #913's fix.

Heap corruption from the array overrun is nondeterministic: sometimes
it silently produces a wrong answer (measured directly below),
sometimes it corrupts memory a later, unrelated test's allocation
reuses, producing a SIGSEGV attributed to whatever part of the
parallel suite happens to touch it next — exactly matching every #920
occurrence (different test cluster each time, no attribution possible
from the crashing process's own output).

This also explains why local reproduction never worked across ~10
attempts throughout this investigation: OCCTSWIFT_LOCAL=1 (used
throughout, and also the *default* whenever a local
Libraries/OCCT.xcframework is present) resolves the locally-rebuilt
kernel, which has all seventeen current patches including 0027. Local
runs were never testing the same kernel CI actually uses.

Measured, not assumed: forced the exact remote kernel with
OCCTSWIFT_REMOTE=1 (overriding the local-directory auto-detection) and
ran the suspect test directly — build B's `#expect(!loft.build())`
failed, `build()` returned `true` instead of failing cleanly, matching
#913's own "silently misaligned... reporting build() == true for an
invalid result" description of the unpatched defect exactly. No other
test file uses checkCompatibility(false) at all (checked directly), so
this is the sole trigger; no Sources/ code path uses it either.

Fix: gate the test behind OCCTSWIFT_LOCAL, the identical mechanism and
reasoning already established for #913's own equally-dangerous test
(mismatchedSectionEdgeCountWithoutCheckFailsCleanly, gated since PR
#915's review for this exact class of danger). Verified:
OCCTSWIFT_REMOTE=1 now correctly skips it; OCCTSWIFT_LOCAL=1 still
runs and passes it, preserving the coverage #912's round 2 intended.

Full suite run repeatedly against the forced remote kernel
(OCCTSWIFT_REMOTE=1 swift test, matching CI's resolution exactly) —
clean every time.

Closes duplicate #922 (its patch-count observation was the key clue).
No CHANGELOG entry: test-only change, no public API or behavior
change.
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