inkscape/blender: CWE-407 scan — 6 defects across 2 creative tool targets
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.
This commit is contained in:
parent
0b5409ff95
commit
bdfda13c7e
10 changed files with 679 additions and 0 deletions
216
defects/blender/unit/BlenderTest.java
Normal file
216
defects/blender/unit/BlenderTest.java
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
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");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue