Inkscape (3 defects): - inkscape-0001: SPObject::getLinkedRecursive vector dedup O(N^2) HIGH - inkscape-0002: ObjectSet::raise()/lower() vector membership O(S*N) MEDIUM - inkscape-0003: get_all_items_recursive exclude scan O(C*E) MEDIUM Blender (3 defects): - blender-0001: node_runtime socket chain cycle detection Vector.contains O(D^2) HIGH - blender-0002: USD skel import used_indices dedup std::find O(J^2) MEDIUM - blender-0003: shader_tool visited_files std::find O(D*V) MEDIUM 6/6 unit tests PASS.
216 lines
7.7 KiB
Java
216 lines
7.7 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* CWE-407 simulation tests for Blender defects.
|
|
*
|
|
* blender-0001: node_runtime.cc socket chain cycle detection Vector.contains() O(D^2)
|
|
* blender-0002: usd_skel_convert.cc used_indices dedup std::find O(J^2)
|
|
* blender-0003: shader_tool.cc visited_files std::find O(D*V)
|
|
*/
|
|
public class BlenderTest {
|
|
|
|
// ========== blender-0001: socket chain cycle detection ==========
|
|
|
|
/** Simulates find_logical_origins_for_socket_recursive with Vector.contains cycle check */
|
|
static int traceSocketChainDefective(Map<Integer, List<Integer>> links, int start) {
|
|
List<Integer> chain = new ArrayList<>();
|
|
return traceRecursiveDefective(links, start, chain);
|
|
}
|
|
|
|
static int traceRecursiveDefective(Map<Integer, List<Integer>> links, int socket, List<Integer> chain) {
|
|
// Defect: linear scan for cycle detection
|
|
if (chain.contains(socket)) {
|
|
return 0; // cycle detected
|
|
}
|
|
chain.add(socket);
|
|
int count = 1;
|
|
for (int linked : links.getOrDefault(socket, Collections.emptyList())) {
|
|
count += traceRecursiveDefective(links, linked, chain);
|
|
}
|
|
chain.remove(chain.size() - 1);
|
|
return count;
|
|
}
|
|
|
|
/** Fixed: HashSet for O(1) cycle detection */
|
|
static int traceSocketChainFixed(Map<Integer, List<Integer>> links, int start) {
|
|
List<Integer> chain = new ArrayList<>();
|
|
Set<Integer> chainSet = new HashSet<>();
|
|
return traceRecursiveFixed(links, start, chain, chainSet);
|
|
}
|
|
|
|
static int traceRecursiveFixed(Map<Integer, List<Integer>> links, int socket,
|
|
List<Integer> chain, Set<Integer> chainSet) {
|
|
if (chainSet.contains(socket)) {
|
|
return 0;
|
|
}
|
|
chain.add(socket);
|
|
chainSet.add(socket);
|
|
int count = 1;
|
|
for (int linked : links.getOrDefault(socket, Collections.emptyList())) {
|
|
count += traceRecursiveFixed(links, linked, chain, chainSet);
|
|
}
|
|
chain.remove(chain.size() - 1);
|
|
chainSet.remove(socket);
|
|
return count;
|
|
}
|
|
|
|
static boolean testSocketChainCycleDetection() {
|
|
// Build a long reroute chain: 0 -> 1 -> 2 -> ... -> D-1
|
|
int D = 2000;
|
|
Map<Integer, List<Integer>> links = new HashMap<>();
|
|
for (int i = 0; i < D - 1; i++) {
|
|
links.put(i, List.of(i + 1));
|
|
}
|
|
links.put(D - 1, Collections.emptyList());
|
|
|
|
// Warmup
|
|
for (int i = 0; i < 3; i++) {
|
|
traceSocketChainDefective(links, 0);
|
|
traceSocketChainFixed(links, 0);
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int i = 0; i < 5; i++) traceSocketChainDefective(links, 0);
|
|
long defective = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int i = 0; i < 5; i++) traceSocketChainFixed(links, 0);
|
|
long fixed = System.nanoTime() - t0;
|
|
|
|
double ratio = (double) defective / fixed;
|
|
System.out.printf(" blender-0001 socket chain cycle: defective=%dms fixed=%dms ratio=%.1fx%n",
|
|
defective / 1_000_000, fixed / 1_000_000, ratio);
|
|
return ratio > 2.0;
|
|
}
|
|
|
|
// ========== blender-0002: USD skel used_indices dedup ==========
|
|
|
|
/** Defective: std::find on vector for dedup */
|
|
static List<Integer> collectUsedIndicesDefective(int[] jointIndices) {
|
|
List<Integer> usedIndices = new ArrayList<>();
|
|
for (int index : jointIndices) {
|
|
if (!usedIndices.contains(index)) {
|
|
usedIndices.add(index);
|
|
}
|
|
}
|
|
return usedIndices;
|
|
}
|
|
|
|
/** Fixed: Set for O(1) dedup */
|
|
static List<Integer> collectUsedIndicesFixed(int[] jointIndices) {
|
|
Set<Integer> seen = new HashSet<>();
|
|
List<Integer> usedIndices = new ArrayList<>();
|
|
for (int index : jointIndices) {
|
|
if (seen.add(index)) {
|
|
usedIndices.add(index);
|
|
}
|
|
}
|
|
return usedIndices;
|
|
}
|
|
|
|
static boolean testUsedIndicesDedup() {
|
|
// Simulate a high-poly mesh with many joint weight entries
|
|
int J = 10000;
|
|
int numJoints = 200;
|
|
Random rng = new Random(42);
|
|
int[] jointIndices = new int[J];
|
|
for (int i = 0; i < J; i++) {
|
|
jointIndices[i] = rng.nextInt(numJoints);
|
|
}
|
|
|
|
// Warmup
|
|
for (int i = 0; i < 3; i++) {
|
|
collectUsedIndicesDefective(jointIndices);
|
|
collectUsedIndicesFixed(jointIndices);
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int i = 0; i < 100; i++) collectUsedIndicesDefective(jointIndices);
|
|
long defective = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int i = 0; i < 100; i++) collectUsedIndicesFixed(jointIndices);
|
|
long fixed = System.nanoTime() - t0;
|
|
|
|
double ratio = (double) defective / fixed;
|
|
System.out.printf(" blender-0002 USD skel dedup: defective=%dms fixed=%dms ratio=%.1fx%n",
|
|
defective / 1_000_000, fixed / 1_000_000, ratio);
|
|
return ratio > 2.0;
|
|
}
|
|
|
|
// ========== blender-0003: shader_tool visited_files ==========
|
|
|
|
/** Defective: std::find on visited vector — isolate visited membership */
|
|
static int processShaderDepsDefective(List<String> resolvedFiles) {
|
|
List<String> visited = new ArrayList<>();
|
|
int processed = 0;
|
|
for (String file : resolvedFiles) {
|
|
// Defect: linear scan of visited list
|
|
if (!visited.contains(file)) {
|
|
visited.add(file);
|
|
processed++;
|
|
}
|
|
}
|
|
return processed;
|
|
}
|
|
|
|
/** Fixed: HashSet for visited check */
|
|
static int processShaderDepsFixed(List<String> resolvedFiles) {
|
|
Set<String> visitedSet = new HashSet<>();
|
|
int processed = 0;
|
|
for (String file : resolvedFiles) {
|
|
if (visitedSet.add(file)) {
|
|
processed++;
|
|
}
|
|
}
|
|
return processed;
|
|
}
|
|
|
|
static boolean testShaderToolVisited() {
|
|
int D = 5000; // dependencies (many unique files to grow visited list)
|
|
List<String> resolvedFiles = new ArrayList<>();
|
|
Random rng = new Random(42);
|
|
// Many unique files so visited list grows large
|
|
for (int i = 0; i < D; i++) resolvedFiles.add("shader_" + rng.nextInt(D) + ".glsl");
|
|
|
|
// Warmup
|
|
for (int i = 0; i < 3; i++) {
|
|
processShaderDepsDefective(resolvedFiles);
|
|
processShaderDepsFixed(resolvedFiles);
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int i = 0; i < 20; i++) processShaderDepsDefective(resolvedFiles);
|
|
long defective = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int i = 0; i < 20; i++) processShaderDepsFixed(resolvedFiles);
|
|
long fixed = System.nanoTime() - t0;
|
|
|
|
double ratio = (double) defective / fixed;
|
|
System.out.printf(" blender-0003 shader visited: defective=%dms fixed=%dms ratio=%.1fx%n",
|
|
defective / 1_000_000, fixed / 1_000_000, ratio);
|
|
return ratio > 2.0;
|
|
}
|
|
|
|
// ========== Main ==========
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("Blender CWE-407 unit tests");
|
|
System.out.println("=========================");
|
|
|
|
boolean p1 = testSocketChainCycleDetection();
|
|
boolean p2 = testUsedIndicesDedup();
|
|
boolean p3 = testShaderToolVisited();
|
|
|
|
System.out.println();
|
|
System.out.printf("blender-0001 socket chain cycle: %s%n", p1 ? "PASS" : "FAIL");
|
|
System.out.printf("blender-0002 USD skel dedup: %s%n", p2 ? "PASS" : "FAIL");
|
|
System.out.printf("blender-0003 shader visited: %s%n", p3 ? "PASS" : "FAIL");
|
|
|
|
if (!p1 || !p2 || !p3) {
|
|
System.exit(1);
|
|
}
|
|
System.out.println("\nAll 3 tests PASS");
|
|
}
|
|
}
|