# vtk-0001: CWE-407 — O(C×npts²) linear dedup scan in vtkStaticCleanPolyData # vtk-0002: CWE-407 — O(numPts×numLabels) linear label-set scan in vtkGeneralizedSurfaceNets3D --- ## vtk-0001 ### Severity HIGH ### File `Filters/Core/vtkStaticCleanPolyData.cxx:257,293,343,403` ### Description `vtkStaticCleanPolyData::RequestData()` deduplicates point IDs within each cell using a `std::vector cellIds` maintained per cell. For each point in the cell the code calls `std::find(cellIds.begin(), cellIds.end(), ptId)` before appending to the vector. The pattern appears four times for the four cell types: verts, lines, polys, and triangle strips. The outer loop iterates over all C cells in the mesh. For each cell the inner loop iterates npts times and each iteration does an O(k) linear scan (where k grows from 0 to npts−1). Total cost per cell: O(npts²). Total cost: O(C × npts²). For a triangulated surface with C = 10 000 000 triangles this is O(C × 9) ≈ 90 M ops — manageable. But for strip-based or high-valence mesh data (npts up to 64–256) the cost becomes O(C × npts²) = O(C × 65536) ops, a 7 000× regression over the expected O(C × npts) cost. The source comment even notes: "Just use a vector to keep track of unique ids — it's a small set so find() will execute relatively fast." This is only true when npts is always small, but VTK processes arbitrary meshes. ### Root Cause `cellIds` is a `std::vector`. Deduplication via linear scan is O(k) per point. Replacing with `std::unordered_set` reduces the membership test to O(1) amortized, cutting deduplication from O(npts²) to O(npts) per cell. ### Fix Replace `std::vector cellIds` with `std::unordered_set seenIds` for the dedup check; accumulate the insertion-ordered result in a separate `std::vector` only when building the output cell. ### Speedup At npts = 64 (strip with 64 points): 64²/64 = 64× per cell. At npts = 256: 256× per cell. Asymptotic: O(C × npts²) → O(C × npts). ### Affected Operations - `vtkStaticCleanPolyData::RequestData()` — used as a preprocessing step before virtually every VTK geometry pipeline (surface nets, voronoi, ghost-cell generation, smoothing, etc.) --- ## vtk-0002 ### Severity HIGH ### File `Filters/Meshing/vtkGeneralizedSurfaceNets3D.cxx:1150` ### Description When no explicit labels are provided, `RequestData()` collects the unique set of region IDs by iterating all numPts point scalars and calling `std::find(autoLabels.begin(), autoLabels.end(), regionId)` before appending to `autoLabels`: ```cpp for (vtkIdType i = 0; i < numPts; ++i) { double regionId = static_cast(regions->GetValue(i)); if (regionId >= 0 && std::find(autoLabels.begin(), autoLabels.end(), regionId) == autoLabels.end()) { autoLabels.push_back(regionId); } } ``` Cost: O(numPts × numLabels). For a 50 M-point segmentation volume with 200 tissue classes: 50M × 200 = 10^10 comparisons where O(numPts) suffices. ### Root Cause `autoLabels` is a `std::vector`. The uniqueness check is a linear scan. An `std::unordered_set` provides O(1) membership. ### Fix Use `std::unordered_set` for the existence check during collection, then move the sorted result into a vector if ordering is needed. ### Speedup At numPts = 50 000 000, numLabels = 200: 200× speedup. Asymptotic: O(numPts × numLabels) → O(numPts). ### Affected Operations - `vtkGeneralizedSurfaceNets3D::RequestData()` — called when processing multi-label segmentation volumes (medical imaging, simulation data)