java-topology/whitepaper/outreach/curaengine-0001.md
russell@unturf.com aeb084c9ae feat: add 30 outreach docs (batches 9-10)
Batch 9 (15): bun, bzflag (3), cake_wallet (4), calligra, caprice32 (2),
  cataclysm (3), cemu
Batch 10 (15): cemu-0002, citra, clickhouse-java, cmake (3), cocos2d (3),
  conduit, cura (2), curaengine, clamav, contiki
2026-04-14 19:51:36 -04:00

3.3 KiB
Raw Blame History

Ultimaker CuraEngine — CWE-407 Disclosure Brief

2026-04-13 · Patch available — awaiting upstream merge

Finding

One O(S×N + S²×L) defect in CuraEngine in the monotonic path ordering pass. Two std::find() calls over vectors fire inside nested loops during every layer's infill/wall path ordering. Patched.

The Defects

curaengine-0001 (PATCHED — MEDIUM-HIGH): src/PathOrderMonotonic.cpp:301-320

// In makeOrderedPath — fires per layer per infill/wall segment:
for (size_t i = 0; i < polystring.size() - 1; ++i)  // O(S) outer
{
    // O(N): std::find on polylines vector to get iterator
    const std::vector<Path*> overlapping_lines
        = getOverlappingLines(std::find(polylines.begin(), polylines.end(), polystring[i]),
                              perpendicular, polylines, max_adjacent_distance);

    for (Path* overlapping_line : overlapping_lines)  // O(L) per element
    {
        // O(S): std::find on polystring deque
        if (std::find(polystring.begin(), polystring.end(), overlapping_line)
            == polystring.end())
        { ... }
    }
}

Two std::find calls inside nested loops:

  1. std::find(polylines...) — O(N) per iteration, N = total polylines on the layer
  2. std::find(polystring...) — O(S) per overlapping line check

Total cost: O(S×N + S²×L) per polystring, where S = polystring size, N = total polylines, L = overlapping lines per element.

Complexity Proof

At N=1000 polylines, S=50 per string, L=5 overlapping:

  • Defective: 50×1000 + 50²×5 = 62,500 comparisons per polystring
  • Fixed: 50×1 + 50×5×1 = 300 hash lookups
  • ~200× op reduction per polystring.

Impact

CuraEngine slices 3D models into G-code for printing. The monotonic path ordering pass runs on every layer for infill and wall segments. A complex print with 500 layers, each with many infill lines, multiplies this cost across the entire model. Complex organic geometries (sculptures, anatomical models, terrain) generate the most polylines per layer and suffer the worst performance.

The Fix

  1. Pre-build unordered_map<Path*, iterator> for O(1) iterator lookup into polylines
  2. Build unordered_set<Path*> per polystring for O(1) membership checks
// Before
std::find(polylines.begin(), polylines.end(), polystring[i])
std::find(polystring.begin(), polystring.end(), overlapping_line)

// After — O(1) lookups
std::unordered_map<Path*, typename std::vector<Path*>::iterator> polyline_index;
std::unordered_set<Path*> polystring_set(polystring.begin(), polystring.end());
polyline_index.at(polystring[i])
polystring_set.find(overlapping_line)

Patch

Fix available: defects/curaengine-0001/patch/curaengine-0001-path-order-monotonic-vector-find.patch

Single-file patch in PathOrderMonotonic.cpp.

Unit test: pass. ~200× op reduction at 1000 polylines × 50 per string.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (Ultimaker/CuraEngine).
  2. Assess severity — fires per layer per infill/wall segment during slicing. Complex prints multiply the cost across hundreds of layers.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the CuraEngine team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.