diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index 8144f43a0..23dfb1c3c 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -905,5 +905,12 @@ "rpcs3-0001": "UNDF-2026-000000904", "rpcs3-0002": "UNDF-2026-000000905", "rpcs3-0003": "UNDF-2026-000000906", - "weechat-0001-0001": "UNDF-2026-000000907" + "weechat-0001-0001": "UNDF-2026-000000907", + "dask-project-0001": "UNDF-2026-000000908", + "dask-project-0002": "UNDF-2026-000000909", + "prusaslicer-0001-0001": "UNDF-2026-000000910", + "prusaslicer-0002-0002": "UNDF-2026-000000911", + "prusaslicer-0003-0003": "UNDF-2026-000000912", + "ray-project-0001": "UNDF-2026-000000913", + "transformers-0001": "UNDF-2026-000000914" } diff --git a/defects/dask-project/patch/dask-project-0001.patch b/defects/dask-project/patch/dask-project-0001.patch index 6ca3576c6..e266fd5be 100644 --- a/defects/dask-project/patch/dask-project-0001.patch +++ b/defects/dask-project/patch/dask-project-0001.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000908 # dask-project-0001: parquet/core.py filter_partitions disjunction O(P×O) dedup # CWE-407 — Algorithmic Complexity # diff --git a/defects/dask-project/patch/dask-project-0002.patch b/defects/dask-project/patch/dask-project-0002.patch index b49bf7beb..a235bb0fa 100644 --- a/defects/dask-project/patch/dask-project-0002.patch +++ b/defects/dask-project/patch/dask-project-0002.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000909 # dask-project-0002: methods.py describe_aggregate column name dedup O(C²) # CWE-407 — Algorithmic Complexity # diff --git a/defects/prusaslicer-0001/patch/prusaslicer-0001.patch b/defects/prusaslicer-0001/patch/prusaslicer-0001.patch new file mode 100644 index 000000000..718a69fa0 --- /dev/null +++ b/defects/prusaslicer-0001/patch/prusaslicer-0001.patch @@ -0,0 +1,21 @@ +# UNDF: UNDF-2026-000000910 +--- a/src/libslic3r/Preset.cpp ++++ b/src/libslic3r/Preset.cpp +@@ -259,14 +259,14 @@ + std::vector VendorProfile::families() const + { + std::vector res; +- unsigned num_familiies = 0; ++ std::unordered_set seen; + + for (auto &model : models) { +- if (std::find(res.begin(), res.end(), model.family) == res.end()) { ++ if (seen.insert(model.family).second) { + res.push_back(model.family); +- num_familiies++; + } + } + + return res; + } ++// include required diff --git a/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.class b/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.class new file mode 100644 index 000000000..154ed1ad6 Binary files /dev/null and b/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.class differ diff --git a/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.java b/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.java new file mode 100644 index 000000000..b00d7700f --- /dev/null +++ b/defects/prusaslicer-0001/test/PrusaSlicerPresetFamiliesTest.java @@ -0,0 +1,74 @@ +import java.util.*; + +/** + * Unit test for PrusaSlicer CWE-407 defect prusaslicer-0001: + * VendorProfile::families() uses std::find on a growing vector to deduplicate + * family names, resulting in O(N^2) behavior. Fix: use unordered_set for O(1) lookup. + * + * Defect location: src/libslic3r/Preset.cpp VendorProfile::families() + * Pattern: std::find(res.begin(), res.end(), model.family) inside for-each model loop + */ +public class PrusaSlicerPresetFamiliesTest { + + // --- DEFECTIVE: O(N^2) linear scan for dedup --- + static List familiesDefective(List modelFamilies) { + List res = new ArrayList<>(); + for (String family : modelFamilies) { + if (!res.contains(family)) { // O(N) scan per iteration + res.add(family); + } + } + return res; + } + + // --- FIXED: O(N) hash set for dedup --- + static List familiesFixed(List modelFamilies) { + List res = new ArrayList<>(); + Set seen = new HashSet<>(); + for (String family : modelFamilies) { + if (seen.add(family)) { // O(1) amortized + res.add(family); + } + } + return res; + } + + public static void main(String[] args) { + // Correctness test + List small = Arrays.asList("MK3S", "MINI", "MK3S", "XL", "MINI", "XL", "MK4"); + List defResult = familiesDefective(small); + List fixResult = familiesFixed(small); + assert defResult.equals(fixResult) : "Results must match"; + assert defResult.equals(Arrays.asList("MK3S", "MINI", "XL", "MK4")) : "Dedup must preserve order"; + System.out.println("PASS correctness: " + fixResult); + + // Performance test: N models with N/2 unique families + int N = 20_000; + List models = new ArrayList<>(); + for (int i = 0; i < N; i++) { + models.add("family-" + (i % (N / 2))); + } + + // Warmup + for (int i = 0; i < 3; i++) { + familiesDefective(models); + familiesFixed(models); + } + + long t0 = System.nanoTime(); + List rDef = familiesDefective(models); + long t1 = System.nanoTime(); + List rFix = familiesFixed(models); + long t2 = System.nanoTime(); + + double defMs = (t1 - t0) / 1e6; + double fixMs = (t2 - t1) / 1e6; + double ratio = defMs / fixMs; + + System.out.printf("PASS defective: %.1f ms, fixed: %.1f ms, ratio: %.1fx%n", defMs, fixMs, ratio); + assert rDef.equals(rFix) : "Large results must match"; + assert ratio > 2.0 : "Fixed should be at least 2x faster, got " + ratio + "x"; + System.out.println("PASS performance: ratio " + String.format("%.1f", ratio) + "x"); + System.out.println("ALL TESTS PASSED"); + } +} diff --git a/defects/prusaslicer-0002/patch/prusaslicer-0002.patch b/defects/prusaslicer-0002/patch/prusaslicer-0002.patch new file mode 100644 index 000000000..d39741cc9 --- /dev/null +++ b/defects/prusaslicer-0002/patch/prusaslicer-0002.patch @@ -0,0 +1,33 @@ +# UNDF: UNDF-2026-000000911 +--- a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp ++++ b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp +@@ -1994,7 +1994,8 @@ + // Queue of island nodes to propagate shortest distance into their neigbors + // contain indices into node_distances + std::vector process; ++ std::unordered_set process_set; + for (size_t i = 1; i < node_distances.size(); i++) process.push_back(i); // zero index is start ++ for (size_t i = 1; i < node_distances.size(); i++) process_set.insert(i); + size_t next_distance_index = 0; // zero index is start + size_t current_node_distance_index = -1; +@@ -2049,8 +2050,8 @@ + size_t item_index = node_distance_it - node_distances.begin(); + // process store unique indices into node_distances +- if(std::find(process.begin(), process.end(), item_index) != process.end()) ++ if(process_set.count(item_index)) + continue; // already in process + + if (next_distance_index < node_distances.size()) +- process.push_back(next_distance_index); // store for next processing ++ { ++ process.push_back(next_distance_index); ++ process_set.insert(next_distance_index); ++ } + next_distance_index = item_index; +@@ -2060,6 +2061,7 @@ + if (process.empty()) + break; // no more nodes to process + next_distance_index = process.back(); + process.pop_back(); ++ process_set.erase(next_distance_index); + prev_neighbor = nullptr; // do not know previous neighbor diff --git a/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.class b/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.class new file mode 100644 index 000000000..f9aa53a39 Binary files /dev/null and b/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.class differ diff --git a/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.java b/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.java new file mode 100644 index 000000000..e5ce52fe6 --- /dev/null +++ b/defects/prusaslicer-0002/test/PrusaSlicerSupportIslandWorklistTest.java @@ -0,0 +1,120 @@ +import java.util.*; + +/** + * Unit test for PrusaSlicer CWE-407 defect prusaslicer-0002: + * UniformSupportIsland graph traversal uses std::find on a worklist vector + * to check membership before adding nodes, resulting in O(N^2) overall. + * Fix: maintain a parallel unordered_set for O(1) membership checks. + * + * Defect location: src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp + * Pattern: std::find(process.begin(), process.end(), item_index) in while-true graph loop + */ +public class PrusaSlicerSupportIslandWorklistTest { + + // --- DEFECTIVE: O(N^2) worklist with linear membership check --- + static int traverseDefective(int nodeCount, int[][] neighbors) { + List process = new ArrayList<>(); + for (int i = 1; i < nodeCount; i++) process.add(i); + int processed = 0; + int nextIdx = 0; + + while (true) { + int current = nextIdx; + nextIdx = -1; + processed++; + + for (int neighbor : neighbors[current]) { + // Check if already in process — O(N) linear scan + if (process.contains(neighbor)) continue; + + if (nextIdx >= 0 && nextIdx < nodeCount) + process.add(nextIdx); + nextIdx = neighbor; + } + + if (nextIdx < 0 || nextIdx >= nodeCount) { + if (process.isEmpty()) break; + nextIdx = process.remove(process.size() - 1); + } + } + return processed; + } + + // --- FIXED: O(N) worklist with hash set membership --- + static int traverseFixed(int nodeCount, int[][] neighbors) { + List process = new ArrayList<>(); + Set processSet = new HashSet<>(); + for (int i = 1; i < nodeCount; i++) { + process.add(i); + processSet.add(i); + } + int processed = 0; + int nextIdx = 0; + + while (true) { + int current = nextIdx; + nextIdx = -1; + processed++; + + for (int neighbor : neighbors[current]) { + // Check if already in process — O(1) hash lookup + if (processSet.contains(neighbor)) continue; + + if (nextIdx >= 0 && nextIdx < nodeCount) { + process.add(nextIdx); + processSet.add(nextIdx); + } + nextIdx = neighbor; + } + + if (nextIdx < 0 || nextIdx >= nodeCount) { + if (process.isEmpty()) break; + int removed = process.remove(process.size() - 1); + processSet.remove(removed); + nextIdx = removed; + } + } + return processed; + } + + public static void main(String[] args) { + // Build a graph: chain with some back edges (simulates Voronoi island graph) + int N = 2000; + int[][] neighbors = new int[N][]; + Random rng = new Random(42); + for (int i = 0; i < N; i++) { + int degree = 2 + rng.nextInt(3); + neighbors[i] = new int[degree]; + for (int d = 0; d < degree; d++) { + neighbors[i][d] = (i + 1 + d) % N; + } + } + + // Correctness + int rDef = traverseDefective(N, neighbors); + int rFix = traverseFixed(N, neighbors); + assert rDef == rFix : "Processed count must match: " + rDef + " vs " + rFix; + System.out.println("PASS correctness: processed " + rFix + " nodes"); + + // Warmup + for (int i = 0; i < 3; i++) { + traverseDefective(N, neighbors); + traverseFixed(N, neighbors); + } + + long t0 = System.nanoTime(); + traverseDefective(N, neighbors); + long t1 = System.nanoTime(); + traverseFixed(N, neighbors); + long t2 = System.nanoTime(); + + double defMs = (t1 - t0) / 1e6; + double fixMs = (t2 - t1) / 1e6; + double ratio = defMs / fixMs; + + System.out.printf("PASS defective: %.1f ms, fixed: %.1f ms, ratio: %.1fx%n", defMs, fixMs, ratio); + assert ratio > 2.0 : "Fixed should be at least 2x faster, got " + ratio + "x"; + System.out.println("PASS performance: ratio " + String.format("%.1f", ratio) + "x"); + System.out.println("ALL TESTS PASSED"); + } +} diff --git a/defects/prusaslicer-0003/patch/prusaslicer-0003.patch b/defects/prusaslicer-0003/patch/prusaslicer-0003.patch new file mode 100644 index 000000000..e78f8d768 --- /dev/null +++ b/defects/prusaslicer-0003/patch/prusaslicer-0003.patch @@ -0,0 +1,29 @@ +# UNDF: UNDF-2026-000000912 +--- a/src/libslic3r/CutSurface.cpp ++++ b/src/libslic3r/CutSurface.cpp +@@ -3758,6 +3758,7 @@ + indexed_triangle_set priv::create_indexed_triangle_set( + const std::vector &faces, const CutMesh &mesh) + { ++ std::unordered_map vertex_map; // VI index -> position in vertices vector + std::vector vertices; + vertices.reserve(faces.size() * 2); + +@@ -3773,8 +3774,13 @@ + do { + VI vi = mesh.source(hi); +- auto res = std::find(vertices.begin(), vertices.end(), vi); +- t[ti++] = res - vertices.begin(); +- if (res == vertices.end()) vertices.push_back(vi); ++ uint32_t vi_idx = static_cast(vi); ++ auto it = vertex_map.find(vi_idx); ++ if (it != vertex_map.end()) { ++ t[ti++] = it->second; ++ } else { ++ int pos = static_cast(vertices.size()); ++ vertex_map[vi_idx] = pos; ++ vertices.push_back(vi); ++ t[ti++] = pos; ++ } + hi = mesh.next(hi); + } while (hi != hi_end); diff --git a/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.class b/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.class new file mode 100644 index 000000000..fbd7af900 Binary files /dev/null and b/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.class differ diff --git a/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.java b/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.java new file mode 100644 index 000000000..1fffbbaea --- /dev/null +++ b/defects/prusaslicer-0003/test/PrusaSlicerCutSurfaceVertexDedupTest.java @@ -0,0 +1,102 @@ +import java.util.*; + +/** + * Unit test for PrusaSlicer CWE-407 defect prusaslicer-0003: + * create_indexed_triangle_set uses std::find on a growing vertices vector + * to deduplicate vertex indices when building triangle sets from cut faces. + * O(F * V) where F = faces, V = unique vertices (~2F). Fix: use unordered_map. + * + * Defect location: src/libslic3r/CutSurface.cpp priv::create_indexed_triangle_set() + * Pattern: std::find(vertices.begin(), vertices.end(), vi) inside face iteration loop + */ +public class PrusaSlicerCutSurfaceVertexDedupTest { + + // Simulate creating indexed triangle set from face data + // Each face has 3 vertex indices; many are shared between faces + + // --- DEFECTIVE: O(F * V) linear scan for vertex dedup --- + static int[][] createITSDefective(int[][] faces) { + List vertices = new ArrayList<>(); + int[][] indices = new int[faces.length][3]; + + for (int f = 0; f < faces.length; f++) { + for (int v = 0; v < 3; v++) { + int vi = faces[f][v]; + int pos = vertices.indexOf(vi); // O(V) linear scan + if (pos == -1) { + pos = vertices.size(); + vertices.add(vi); + } + indices[f][v] = pos; + } + } + return indices; + } + + // --- FIXED: O(F) with hash map for vertex dedup --- + static int[][] createITSFixed(int[][] faces) { + Map vertexMap = new HashMap<>(); + List vertices = new ArrayList<>(); + int[][] indices = new int[faces.length][3]; + + for (int f = 0; f < faces.length; f++) { + for (int v = 0; v < 3; v++) { + int vi = faces[f][v]; + Integer pos = vertexMap.get(vi); // O(1) hash lookup + if (pos == null) { + pos = vertices.size(); + vertexMap.put(vi, pos); + vertices.add(vi); + } + indices[f][v] = pos; + } + } + return indices; + } + + public static void main(String[] args) { + // Correctness test with small mesh + int[][] smallFaces = { + {0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {0, 2, 4} + }; + int[][] rDef = createITSDefective(smallFaces); + int[][] rFix = createITSFixed(smallFaces); + assert Arrays.deepEquals(rDef, rFix) : "Results must match"; + System.out.println("PASS correctness"); + + // Performance test: mesh with F faces, ~2F unique vertices + int F = 20_000; + Random rng = new Random(42); + int maxVertex = F * 2; + int[][] faces = new int[F][3]; + for (int f = 0; f < F; f++) { + // Simulate shared vertices between adjacent faces + int base = f * 2; + faces[f][0] = base % maxVertex; + faces[f][1] = (base + 1) % maxVertex; + faces[f][2] = (base + 2) % maxVertex; + } + + // Warmup + for (int i = 0; i < 3; i++) { + createITSDefective(faces); + createITSFixed(faces); + } + + long t0 = System.nanoTime(); + int[][] defResult = createITSDefective(faces); + long t1 = System.nanoTime(); + int[][] fixResult = createITSFixed(faces); + long t2 = System.nanoTime(); + + double defMs = (t1 - t0) / 1e6; + double fixMs = (t2 - t1) / 1e6; + double ratio = defMs / fixMs; + + assert Arrays.deepEquals(defResult, fixResult) : "Large results must match"; + System.out.printf("PASS defective: %.1f ms, fixed: %.1f ms, ratio: %.1fx%n", defMs, fixMs, ratio); + assert ratio > 2.0 : "Fixed should be at least 2x faster, got " + ratio + "x"; + System.out.println("PASS performance: ratio " + String.format("%.1f", ratio) + "x"); + System.out.println("ALL TESTS PASSED"); + } +} diff --git a/defects/ray-project/patch/ray-project-0001.patch b/defects/ray-project/patch/ray-project-0001.patch index 9bb4660a1..0efd90945 100644 --- a/defects/ray-project/patch/ray-project-0001.patch +++ b/defects/ray-project/patch/ray-project-0001.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000913 # ray-project-0001: dag_node.py _get_toplevel_child_nodes O(A²) dedup # CWE-407 — Algorithmic Complexity # diff --git a/defects/transformers/patch/transformers-0001.patch b/defects/transformers/patch/transformers-0001.patch index 64b5ada84..6d0c604db 100644 --- a/defects/transformers/patch/transformers-0001.patch +++ b/defects/transformers/patch/transformers-0001.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000914 # transformers-0001: tokenization_python.py convert_ids_to_tokens O(T×S) # CWE-407 — Algorithmic Complexity #