chore(#705): carry the upstream ChFi2d_Builder::AddChamfer kernel patch - #708
Conversation
The chamfer2D SIGSEGV fixed bridge-side in a prior PR is an upstream OCCT defect. ChFi2d_Builder::AddChamfer(E1, E2, D1, D2) calls ChFi2d::FindConnectedEdges to look up the pair's shared vertex and dereferences the two edges it returns without checking the returned status first. FindConnectedEdges leaves both edges null on every failure path, and the pair's second call fails it, because the shared vertex was already consumed chamfering the pair the first time. The sibling overload, AddChamfer(edge, vertex, distance, angle), checks the identical status correctly, and this patch makes the two-edge overload match it, four lines, reusing the null edge the function already returns on its other refusal paths. Two checks done before writing the patch: - How the five guarded call sites of FindConnectedEdges in this file return: each declares a default-constructed null value early and returns it verbatim on ChFi2d_ConnexionError. AddChamfer(E1, E2, D1, D2) already has such a variable, chamfer, and already uses it as its own declined signal on three earlier paths in the same function, so the patch returns that value rather than introducing a new one. - Whether OCCT's own tooling can reach the unguarded path: yes. BRepTest_Fillet2DCommands.cxx's chfi2d DRAW command loops over edge-name pairs from the command line and calls this same overload once per pair, so naming the same two edges twice in one invocation reaches the identical crash. Scripts/patches/0022-ChFi2d_Builder-AddChamfer-connexion-error-check-705.patch, verified with a debug single-TU override-link against the pinned v2.0.0-kernel.1 binary (no full rebuild): the standalone repro crashes, exit 139, on the second AddChamfer call against the stock TU, and completes cleanly, exit 0, with the patched TU linked before the OCCT archive, returning a null edge with Status() == ChFi2d_ConnexionError. clang-format reports only pre-existing, unrelated violations elsewhere in the file. Not rebuilding the xcframework or bumping the pin in this PR. The patch is carried and inert until a rebuild ships it; the bridge guard from the prior PR is what protects callers until then, per this repo's established bridge-mitigation-then-kernel-patch pattern (#298, #341, #344, #349). Filed upstream: Open-Cascade-SAS/OCCT#1431 (repro), OCCT#1432 (fix). Refs #705
Independent verificationThe patch applies cleanly to It is the right five lines. I proposed this shape before the work started and the result matches Upstream filed properly: issue OCCT#1431 Gates green, zero em-dashes. No Swift source changes, so the suite is unaffected. The one thing to be deliberate about on mergePatch So the sequencing matters: #706 should merge first. Merging this one alone would leave a patch in Whether to cut |
Code review (independent)Scope: the PR diff (+267/-0, 4 files), cross-checked against upstream OCCT at Independent verification (all confirmed)
CorrectnessThe fix is right and minimal. Returning the already-declared null Nits (documentation accuracy only, non-blocking)
Risks
Test coverageDeliberately no new Swift tests: any test asserting the fix would be red against the pinned asset (the exact trap the build guide warns about). Coverage sits where this repo's pattern puts it: the deterministic standalone reproducer with before/after evidence, and the CI kernel-rebuild run of the full suite against the patched kernel. A Swift-level regression test could not drive this path through the public API anyway once #706's duplicate-pair guard is in place, so the VerdictApprove. The root-cause analysis checks out against upstream |
Review finding, and it corrects my own analysis as well as the PR's. Both READMEs said FindConnectedEdges "leaves both edges null on every failure path". Reading the four returns, that is true of two of them and false of the other two: exactly one incident edge assigns E1 before returning ChFi2d_ConnexionError, and three or more assign both. The patch is unaffected, because it keys on the status, which IS uniform across all four. But the stated rationale was the reason to guard on nullness, and a guard written that way would have missed half the failure paths. The repro README now carries the four-way table and both say the status is what the fix checks and why. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
All three addressed, and the first one corrects me as much as the PR. Nit 1 is right and I repeated the error in my own review. I wrote that
The patch is unaffected, since it keys on the status, which is uniformly Nit 2 correct. Nit 3 is the one with teeth, and I have given it an owner. You are right that "Mid-release" says publish a Deferring is still right here for two reasons that section does not cover: this PR adds no Swift regression test, so Thanks for verifying the enum numerics and the DRAW reachability against |
Summary
Second PR in this workstream, after #706 (the bridge-side crash fix). Root-caused the
chamfer2DSIGSEGV (#705) upstream: it is a genuine OCCT defect, not a bridge bug, and this PRcarries the kernel patch and files it upstream, following this repo's established
bridge-mitigation-then-kernel-patch pattern (#298, #341, #344, #349).
Root cause
ChFi2d_Builder::AddChamfer(const TopoDS_Edge& E1, const TopoDS_Edge& E2, double D1, double D2)(
ChFi2d_Builder_0.cxx) callsChFi2d::FindConnectedEdgesto look up the pair's shared vertex anddereferences the two edges it returns without checking the returned status first:
FindConnectedEdgesleaves both edges null on every failure path, and a pair's second call failsit, because the pair's shared vertex was already consumed chamfering the pair the first time (the
first call's own
BuildNewWirerebuilds the face's wire, replacing that vertex). The siblingoverload,
AddChamfer(edge, vertex, distance, angle), calls the identicalFindConnectedEdgesandchecks the status correctly.
The two checks requested before writing the patch
FindConnectedEdgescall site inthis file (the sibling
AddChamferoverload, bothModifyChamferoverloads, and both callsites inside
RemoveChamfer) declares a default-constructed null value early and returns itverbatim on
ChFi2d_ConnexionError.AddChamfer(E1, E2, D1, D2)already has such a variable,chamfer, declared on entry and already used as this function's own "declined" signal on threeearlier refusal paths in the same function. The patch returns that existing value rather than
inventing a new one.
BRepTest_Fillet2DCommands.cxx's DRAWchfi2dcommand loops over edge-name pairs read from thecommand line and calls this same two-edge overload once per pair, so
chfi2d result face e1 e2 CD 1 1 e1 e2 CD 1 1(naming the same two edges twice in oneinvocation) reaches the identical crash through OCCT's own tooling, not just this bridge.
The patch
Scripts/patches/0022-ChFi2d_Builder-AddChamfer-connexion-error-check-705.patch: four lines,adding the same status check immediately after
FindConnectedEdges, matching the siblingoverload's idiom line for line.
Verification (override-link, no full rebuild)
Per the override-link technique documented in
Scripts/patches/README.md's#0001entry, whichthe new 0022 entry cites. (
docs/guides/building-occt.mdmentions override-link only as acontamination hazard and mandates the final proof with no override-linked TUs, so it is not the
source for the technique.) The single patched
.cxxcompiledstandalone (
-O0 -DNDEBUG -DNo_Exception -DOCC_CONVERT_SIGNALS, matching the production kernel'sown defines) and linked before
-lOCCT-macos, so the linker resolves this TU's symbols from theoverride rather than the stock archive member.
AddChamfercallIsNull=0,status=5(ChFi2d_IsDone)IsNull=0,status=5, unchangedAddChamfercall, same pairIsNull=1,status=7(ChFi2d_ConnexionError), exit 0clang-format --dry-run --Werroron the patched file reports only pre-existing, unrelatedviolations elsewhere in the file (shifted by 4 lines, same count and content before and after).
Reproducer:
Scripts/repro/705-chamfer2d-duplicate-pair/.What this PR does NOT do
pinned
v2.0.0-kernel.1until a rebuild ships it. The supporting text for patch-carried being aseparate event from patch-shipped is
docs/guides/building-occt.md's "Shipping a rebuild" intro,not its "Mid-release" section: that section's actual directive is to publish a
vX.Y.Z-kernel.Npre-release promptly rather than wait, and it says the divergence is fine"for a day", not for a release. Deferring is still the right call here, for two reasons the
section does not cover: this PR adds no Swift regression test, so
ci.ymlstays green on thepinned asset with nothing masked, and fix(#705): reject a repeated edge pair in chamfer2D instead of crashing #706's guard already protects every caller. But the
deferral is a decision with an expiry, not a default, so patch 0022 is added to Rebuild OCCT.xcframework with patch 0017 (#484 null-context guard) and ship a patch release #512's rebuild
scope to stop it drifting past the next release.
needs it; it becomes redundant, not wrong, once a rebuild ships this patch.
Shape+Geom2d.swiftorOCCTBridge_Modeling.mm. This PR is patches/reprofiles only, no Swift or bridge changes, no rebuild artifacts.
Filed upstream
Verify
git apply --check -p1and--reverse --check -p1both clean against the pristineV8_0_1tag.clang-format --dry-run --Werror: no new violations.swift build: unaffected (no source files changed), clean.Refs #705