#501/#544 fixed a heap buffer overflow and an uncatchable SIGSEGV reachable through four bridge functions, by guarding the bridge layer (occtSamplerKept/occtSamplerIndex/occtValidSampleCount in OCCTBridge_Internal.h). That fix is bridge-only: no Scripts/patches/*.patch was carried, OCCT.xcframework was not rebuilt, and neither defect is filed upstream against Open-Cascade-SAS/OCCT. The two underlying kernel bugs still exist in the pinned source (verified directly in Libraries/occt-src) — this issue tracks patching and filing them properly, following this project's usual practice for confirmed kernel-level memory-safety defects (e.g. #310, #317, #318, #341).
Defect 1: GCPnts_UniformAbscissa::NbPoints() is not bounded by the requested count
GCPnts_UniformAbscissa::initialize (ModelingData/TKGeomBase/GCPnts/GCPnts_UniformAbscissa.cxx:515) sizes its internal parameter array at theNbPoints + 5:
const int aSize = theNbPoints + 5;
...
myParams = new NCollection_HArray1<double>(1, aSize);
but the arc-length walk (Perform/PerformLengthParametrized, same file) fills the array as far as it runs and sets myNbPoints = anIndex (:155, :217) with no clamp against the original theNbPoints. NbPoints() (GCPnts_UniformAbscissa.hxx:216-219) just returns myNbPoints directly — so any caller that sizes its own output buffer from the requested count rather than the actual NbPoints() after construction can be handed more points than it asked for, bounded only by the +5 padding.
GCPnts_QuasiUniformAbscissa::initialize (GCPnts_QuasiUniformAbscissa.cxx:117-120) inherits this for every curve that is not Bezier/BSpline, since it forwards straight to GCPnts_UniformAbscissa:
if (theC.GetType() != GeomAbs_BezierCurve && theC.GetType() != GeomAbs_BSplineCurve)
{
GCPnts_UniformAbscissa aUA(theC, theNbPoints, theU1, theU2);
myNbPoints = aUA.NbPoints();
...
}
Reproduced: on an ellipse with major radius 1e6 and minor radius 1e-3, the arc-length walk lands roughly 1.6e-8 short of the end parameter — far outside the sampler's own local epsilon (Resolution(1e-7), about 1e-13 there) — so it takes one extra step and snaps that step to the true end parameter. 22 of the first 59 requested point counts overshoot the request by exactly one. Full measured tables at Scripts/repro/501-quasiuniform-buffer-overflow/.
This is a real footgun for any OCCT consumer (not just OCCTSwift) that follows the documented contract of "allocate theNbPoints slots, call Parameter(i) for i in 1...theNbPoints" — the class does not actually guarantee that bound.
Defect 2: degenerate point count SIGSEGVs the Bezier/BSpline branch
GCPnts_QuasiUniformAbscissa::initialize's Bezier/BSpline branch (GCPnts_QuasiUniformAbscissa.cxx:128-158) guards theNbPoints <= 1 with:
Standard_ConstructionError_Raise_if(
theNbPoints <= 1,
"GCPnts_QuasiUniformAbscissa::Initialize(), number of points should be >= 2");
*_Raise_if macros compile to nothing under No_Exception, which is how this project's pinned Release OCCT.xcframework is built (see #487). With that guard neutralized and theNbPoints == 0:
aLP is constructed as NCollection_Array1<gp_Pnt2d>(1, 2*theNbPoints) = (1, 0), an empty range — harmless on its own, since the fill loop (for (i = 0; i < 2*theNbPoints; ++i)) never executes.
aLength stays 0.0, so the IsEqual(aLength, 0.0) branch is taken.
myParams = new NCollection_HArray1<double>(1, theNbPoints) = (1, 0), also an empty range.
- The very next line, unconditional:
myParams->SetValue(1, theU1); — writes index 1 into an array whose valid range is empty. Uncatchable out-of-bounds store; SIGSEGVs.
Reproduced on a 4-pole Bezier, an all-coincident-pole Bezier, and an 8-point BSpline fit, all with nbPoints = 0. (On non-Bezier/BSpline curves — a line, circle, ellipse — nbPoints = 0 does not crash; it silently returns IsDone() with 5 points, a separate but less severe misbehavior of the same missing precondition.)
Status
Suggested fix
Following the pattern already carried in Scripts/patches/:
- Defect 1: clamp
myNbPoints (or anIndex) to theNbPoints in GCPnts_UniformAbscissa::Perform/PerformLengthParametrized, or document/enforce the true upper bound (theNbPoints + 5) so callers can size correctly instead of being surprised.
- Defect 2: an explicit, always-enforced range check before
SetValue(1, theU1) in the Bezier/BSpline branch of GCPnts_QuasiUniformAbscissa::initialize — not reliant on Raise_if compiling in.
Carry both as a kernel patch (Scripts/patches/0018-* or next available number), rebuild OCCT.xcframework, and file upstream as a repro + fix PR, matching the project's established practice for confirmed kernel-level memory-safety defects.
#501/#544 fixed a heap buffer overflow and an uncatchable SIGSEGV reachable through four bridge functions, by guarding the bridge layer (
occtSamplerKept/occtSamplerIndex/occtValidSampleCountinOCCTBridge_Internal.h). That fix is bridge-only: noScripts/patches/*.patchwas carried,OCCT.xcframeworkwas not rebuilt, and neither defect is filed upstream againstOpen-Cascade-SAS/OCCT. The two underlying kernel bugs still exist in the pinned source (verified directly inLibraries/occt-src) — this issue tracks patching and filing them properly, following this project's usual practice for confirmed kernel-level memory-safety defects (e.g. #310, #317, #318, #341).Defect 1:
GCPnts_UniformAbscissa::NbPoints()is not bounded by the requested countGCPnts_UniformAbscissa::initialize(ModelingData/TKGeomBase/GCPnts/GCPnts_UniformAbscissa.cxx:515) sizes its internal parameter array attheNbPoints + 5:but the arc-length walk (
Perform/PerformLengthParametrized, same file) fills the array as far as it runs and setsmyNbPoints = anIndex(:155,:217) with no clamp against the originaltheNbPoints.NbPoints()(GCPnts_UniformAbscissa.hxx:216-219) just returnsmyNbPointsdirectly — so any caller that sizes its own output buffer from the requested count rather than the actualNbPoints()after construction can be handed more points than it asked for, bounded only by the+5padding.GCPnts_QuasiUniformAbscissa::initialize(GCPnts_QuasiUniformAbscissa.cxx:117-120) inherits this for every curve that is not Bezier/BSpline, since it forwards straight toGCPnts_UniformAbscissa:Reproduced: on an ellipse with major radius 1e6 and minor radius 1e-3, the arc-length walk lands roughly 1.6e-8 short of the end parameter — far outside the sampler's own local epsilon (
Resolution(1e-7), about 1e-13 there) — so it takes one extra step and snaps that step to the true end parameter. 22 of the first 59 requested point counts overshoot the request by exactly one. Full measured tables atScripts/repro/501-quasiuniform-buffer-overflow/.This is a real footgun for any OCCT consumer (not just OCCTSwift) that follows the documented contract of "allocate
theNbPointsslots, callParameter(i)foriin1...theNbPoints" — the class does not actually guarantee that bound.Defect 2: degenerate point count SIGSEGVs the Bezier/BSpline branch
GCPnts_QuasiUniformAbscissa::initialize's Bezier/BSpline branch (GCPnts_QuasiUniformAbscissa.cxx:128-158) guardstheNbPoints <= 1with:*_Raise_ifmacros compile to nothing underNo_Exception, which is how this project's pinned ReleaseOCCT.xcframeworkis built (see #487). With that guard neutralized andtheNbPoints == 0:aLPis constructed asNCollection_Array1<gp_Pnt2d>(1, 2*theNbPoints)=(1, 0), an empty range — harmless on its own, since the fill loop (for (i = 0; i < 2*theNbPoints; ++i)) never executes.aLengthstays0.0, so theIsEqual(aLength, 0.0)branch is taken.myParams = new NCollection_HArray1<double>(1, theNbPoints)=(1, 0), also an empty range.myParams->SetValue(1, theU1);— writes index 1 into an array whose valid range is empty. Uncatchable out-of-bounds store; SIGSEGVs.Reproduced on a 4-pole Bezier, an all-coincident-pole Bezier, and an 8-point BSpline fit, all with
nbPoints = 0. (On non-Bezier/BSpline curves — a line, circle, ellipse —nbPoints = 0does not crash; it silently returnsIsDone()with 5 points, a separate but less severe misbehavior of the same missing precondition.)Status
occtValidSampleCount, OCCT Class Cross-Reference Index (OCCTBridge.h:19-525) is systemically stale — points to non-existent symbols and hides an orphaned duplicate GCPnts_QuasiUniformAbscissa bridge function #501/fix(#501): the orphaned quasi-uniform duplicate, and the buffer overflow it was hiding #544) — no OCCTSwift-reachable code path hits either defect anymore.GCPnts_UniformAbscissa/GCPnts_QuasiUniformAbscissa— including OCCT's own Draw commands or a future OCCTSwift bridge function that forgets the guard — remains exposed.Open-Cascade-SAS/OCCT.Suggested fix
Following the pattern already carried in
Scripts/patches/:myNbPoints(oranIndex) totheNbPointsinGCPnts_UniformAbscissa::Perform/PerformLengthParametrized, or document/enforce the true upper bound (theNbPoints + 5) so callers can size correctly instead of being surprised.SetValue(1, theU1)in the Bezier/BSpline branch ofGCPnts_QuasiUniformAbscissa::initialize— not reliant onRaise_ifcompiling in.Carry both as a kernel patch (
Scripts/patches/0018-*or next available number), rebuildOCCT.xcframework, and file upstream as a repro + fix PR, matching the project's established practice for confirmed kernel-level memory-safety defects.