From ec6fadc17d060dd7a4f8b6b741968b361966b243 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:43:07 +0000 Subject: [PATCH 1/4] Initial plan From 3e50ff4065a05cc16a3ba6c5359d606aa73c5406 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:48:38 +0000 Subject: [PATCH 2/4] Fix critical division by zero and array bounds issues Co-authored-by: markaren <6005031+markaren@users.noreply.github.com> --- include/threepp/loaders/AssimpLoader.hpp | 27 ++++++++++++++++++----- src/threepp/extras/curves/SplineCurve.cpp | 10 +++++++-- src/threepp/geometries/LatheGeometry.cpp | 5 +++++ src/threepp/loaders/SVGLoader.cpp | 1 + src/threepp/loaders/svg/SVGFunctions.hpp | 7 +++++- src/threepp/math/Lut.cpp | 16 ++++++++++++-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/include/threepp/loaders/AssimpLoader.hpp b/include/threepp/loaders/AssimpLoader.hpp index 57f58225..cbcb7ab0 100644 --- a/include/threepp/loaders/AssimpLoader.hpp +++ b/include/threepp/loaders/AssimpLoader.hpp @@ -395,15 +395,30 @@ namespace threepp { } sum = std::sqrt(sum); - for (unsigned i = 0; i < 4; i++) { + // Only normalize if sum is not zero + if (sum > 0.0f) { + for (unsigned i = 0; i < 4; i++) { + + pairs[i].second = pairs[i].second / sum; - pairs[i].second = pairs[i].second / sum; + while (indexes.size() <= i) indexes.emplace_back(); + while (weights.size() <= i) weights.emplace_back(); - while (indexes.size() <= i) indexes.emplace_back(); - while (weights.size() <= i) weights.emplace_back(); + indexes[i] = pairs[i].first; + weights[i] = pairs[i].second; + } + } else { + // If all weights are zero, distribute equally + for (unsigned i = 0; i < 4; i++) { - indexes[i] = pairs[i].first; - weights[i] = pairs[i].second; + pairs[i].second = 0.25f; + + while (indexes.size() <= i) indexes.emplace_back(); + while (weights.size() <= i) weights.emplace_back(); + + indexes[i] = pairs[i].first; + weights[i] = pairs[i].second; + } } } diff --git a/src/threepp/extras/curves/SplineCurve.cpp b/src/threepp/extras/curves/SplineCurve.cpp index 8c23de30..70b6e296 100644 --- a/src/threepp/extras/curves/SplineCurve.cpp +++ b/src/threepp/extras/curves/SplineCurve.cpp @@ -14,6 +14,12 @@ SplineCurve::SplineCurve(std::vector points) void SplineCurve::getPoint(float t, Vector2& point) const { + // Validate input: need at least 1 point + if (points.empty()) { + point.set(0.0f, 0.0f); + return; + } + const auto p = static_cast(points.size() - 1) * t; const auto intPoint = static_cast(std::floor(p)); @@ -21,8 +27,8 @@ void SplineCurve::getPoint(float t, Vector2& point) const { const auto p0 = points[intPoint == 0 ? intPoint : intPoint - 1]; const auto p1 = points[intPoint]; - const auto p2 = points[intPoint > points.size() - 2 ? points.size() - 1 : intPoint + 1]; - const auto p3 = points[intPoint > points.size() - 3 ? points.size() - 1 : intPoint + 2]; + const auto p2 = points[intPoint >= points.size() - 1 ? points.size() - 1 : intPoint + 1]; + const auto p3 = points[intPoint >= points.size() - 2 ? points.size() - 1 : intPoint + 2]; point.set( interpolants::CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), diff --git a/src/threepp/geometries/LatheGeometry.cpp b/src/threepp/geometries/LatheGeometry.cpp index 1287413f..347feee4 100644 --- a/src/threepp/geometries/LatheGeometry.cpp +++ b/src/threepp/geometries/LatheGeometry.cpp @@ -8,6 +8,11 @@ using namespace threepp; LatheGeometry::LatheGeometry(const std::vector& points, unsigned int segments, float phiStart, float phiLength) { + // Validate input: need at least 2 points to create geometry + if (points.size() < 2) { + return; + } + // clamp phiLength so it's in range of [ 0, 2PI ] phiLength = std::clamp(phiLength, 0.f, math::TWO_PI); diff --git a/src/threepp/loaders/SVGLoader.cpp b/src/threepp/loaders/SVGLoader.cpp index 25a8d409..07328008 100644 --- a/src/threepp/loaders/SVGLoader.cpp +++ b/src/threepp/loaders/SVGLoader.cpp @@ -282,6 +282,7 @@ unsigned int pointsToStrokeWithBuffers( float strokeWidth2 = style.strokeWidth / 2; + // Safe division since we already checked numPoints >= 2 float deltaU = 1.f / static_cast(numPoints - 1); float u0 = 0, u1; diff --git a/src/threepp/loaders/svg/SVGFunctions.hpp b/src/threepp/loaders/svg/SVGFunctions.hpp index e4388b2a..e6cc5a7a 100644 --- a/src/threepp/loaders/svg/SVGFunctions.hpp +++ b/src/threepp/loaders/svg/SVGFunctions.hpp @@ -363,9 +363,14 @@ namespace threepp::svg { t = bx / ax; - } else { + } else if (ay != 0) { t = by / ay; + + } else { + // Both ax and ay are 0, degenerate case + classifyResult.loc = IntersectionLocationType::BEHIND; + return; } classifyResult.loc = IntersectionLocationType::BETWEEN; diff --git a/src/threepp/math/Lut.cpp b/src/threepp/math/Lut.cpp index 0eb481cf..54e4b742 100644 --- a/src/threepp/math/Lut.cpp +++ b/src/threepp/math/Lut.cpp @@ -29,6 +29,11 @@ Lut& Lut::setColorMap(const std::string& colormap, int numberofcolors) { this->map = ColorMapKeywords[colormap]; this->n = numberofcolors; + // Validate input: need at least 1 color + if (this->n <= 0) { + this->n = 1; + } + const float step = 1.f / static_cast(this->n); @@ -49,7 +54,9 @@ Lut& Lut::setColorMap(const std::string& colormap, int numberofcolors) { Color minColor(this->map[j].second); Color maxColor(this->map[j + 1].second); - Color color = minColor.lerp(maxColor, (alpha - min) / (max - min)); + // Avoid division by zero if min == max + float t = (max - min > 0.0f) ? (alpha - min) / (max - min) : 0.0f; + Color color = minColor.lerp(maxColor, t); this->lut.emplace_back(color); } @@ -72,7 +79,12 @@ void Lut::copy(const Lut& lut) { Color Lut::getColor(float alpha) const { alpha = std::clamp(alpha, this->minV, this->maxV); - alpha = (alpha - this->minV) / (this->maxV - this->minV); + // Avoid division by zero if minV == maxV + if (this->maxV - this->minV > 0.0f) { + alpha = (alpha - this->minV) / (this->maxV - this->minV); + } else { + alpha = 0.0f; + } const int colorPosition = static_cast(std::round(alpha * static_cast(this->n))); return this->lut[colorPosition]; From ebfebed7654c667ece9835a153a4c1b7fa3157b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:53:39 +0000 Subject: [PATCH 3/4] Address code review feedback: improve bounds checking and add documentation Co-authored-by: markaren <6005031+markaren@users.noreply.github.com> --- include/threepp/loaders/AssimpLoader.hpp | 4 +++- src/threepp/math/Lut.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/threepp/loaders/AssimpLoader.hpp b/include/threepp/loaders/AssimpLoader.hpp index cbcb7ab0..5ddd2389 100644 --- a/include/threepp/loaders/AssimpLoader.hpp +++ b/include/threepp/loaders/AssimpLoader.hpp @@ -408,7 +408,9 @@ namespace threepp { weights[i] = pairs[i].second; } } else { - // If all weights are zero, distribute equally + // Fallback: If all weights are zero (degenerate case), distribute equally. + // This maintains compatibility with downstream code expecting 4 weights, + // though in practice this should rarely occur in well-formed models. for (unsigned i = 0; i < 4; i++) { pairs[i].second = 0.25f; diff --git a/src/threepp/math/Lut.cpp b/src/threepp/math/Lut.cpp index 54e4b742..09291e84 100644 --- a/src/threepp/math/Lut.cpp +++ b/src/threepp/math/Lut.cpp @@ -29,7 +29,8 @@ Lut& Lut::setColorMap(const std::string& colormap, int numberofcolors) { this->map = ColorMapKeywords[colormap]; this->n = numberofcolors; - // Validate input: need at least 1 color + // Validate input: need at least 1 color. Clamp to 1 to prevent division by zero. + // Using silent correction to maintain backward compatibility with existing code. if (this->n <= 0) { this->n = 1; } @@ -86,8 +87,11 @@ Color Lut::getColor(float alpha) const { alpha = 0.0f; } const int colorPosition = static_cast(std::round(alpha * static_cast(this->n))); + + // Clamp colorPosition to valid range [0, n-1] to prevent out-of-bounds access + const int clampedPosition = std::min(colorPosition, static_cast(this->n - 1)); - return this->lut[colorPosition]; + return this->lut[clampedPosition]; } void Lut::addColorMap(const std::string& name, const std::vector>& arrayOfColors) { From 77b47f81ac97423be0c2cc7f21b93b742f665c59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:54:45 +0000 Subject: [PATCH 4/4] Fix trailing whitespace issues Co-authored-by: markaren <6005031+markaren@users.noreply.github.com> --- src/threepp/loaders/svg/SVGFunctions.hpp | 2 +- src/threepp/math/Lut.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/threepp/loaders/svg/SVGFunctions.hpp b/src/threepp/loaders/svg/SVGFunctions.hpp index e6cc5a7a..b2e45c8b 100644 --- a/src/threepp/loaders/svg/SVGFunctions.hpp +++ b/src/threepp/loaders/svg/SVGFunctions.hpp @@ -366,7 +366,7 @@ namespace threepp::svg { } else if (ay != 0) { t = by / ay; - + } else { // Both ax and ay are 0, degenerate case classifyResult.loc = IntersectionLocationType::BEHIND; diff --git a/src/threepp/math/Lut.cpp b/src/threepp/math/Lut.cpp index 09291e84..3ead3253 100644 --- a/src/threepp/math/Lut.cpp +++ b/src/threepp/math/Lut.cpp @@ -87,7 +87,7 @@ Color Lut::getColor(float alpha) const { alpha = 0.0f; } const int colorPosition = static_cast(std::round(alpha * static_cast(this->n))); - + // Clamp colorPosition to valid range [0, n-1] to prevent out-of-bounds access const int clampedPosition = std::min(colorPosition, static_cast(this->n - 1));