panda3d-0001: NodePathCollection remove_duplicate_paths O(N^2) nested scan and remove_paths_from has_path O(N*M) -> fix with pset, 499x at N=1000 ogre-0001: GLSLProgramWriter std::find(inParams) O(A*O*P) in nested loop during GLSL shader code generation -> fix with unordered_set, 75x at A=200
144 lines
5.5 KiB
Java
144 lines
5.5 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* CWE-407 simulation: OGRE3D GLSLProgramWriter inParams linear scan.
|
|
*
|
|
* Defect: ogre-0001
|
|
* File: Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.cpp
|
|
*
|
|
* In writeSourceCode(), for every function invocation atom and for every
|
|
* operand within that atom, the code calls:
|
|
*
|
|
* std::find(inParams.begin(), inParams.end(), param)
|
|
*
|
|
* where inParams is a vector<ParameterPtr>. This is O(P) per operand, giving
|
|
* overall O(A * O * P) where:
|
|
* A = atom instances (function invocations)
|
|
* O = operands per atom
|
|
* P = number of input parameters
|
|
*
|
|
* Fix: build an unordered_set<const Parameter*> from inParams before the loop,
|
|
* reducing the per-operand lookup to O(1) → overall O(A * O).
|
|
*/
|
|
public class OgreTest {
|
|
|
|
/**
|
|
* Simulates the defective O(A*O*P) inParams lookup:
|
|
* for each atom, for each operand, do a linear scan of inParams.
|
|
*
|
|
* @param numAtoms number of function invocation atoms (A)
|
|
* @param numOperands operands per atom (O)
|
|
* @param numInParams number of input parameters (P)
|
|
* @return total comparison operations performed
|
|
*/
|
|
static long defectiveShaderWrite(int numAtoms, int numOperands, int numInParams) {
|
|
// Simulate inParams as a list of IDs
|
|
List<Integer> inParams = new ArrayList<>(numInParams);
|
|
for (int i = 0; i < numInParams; i++) {
|
|
inParams.add(i);
|
|
}
|
|
|
|
long ops = 0;
|
|
for (int a = 0; a < numAtoms; a++) {
|
|
for (int o = 0; o < numOperands; o++) {
|
|
// The operand parameter ID — picks half from inParams, half not
|
|
int paramId = (a * numOperands + o) % (numInParams * 2);
|
|
// std::find: O(P) scan
|
|
boolean found = false;
|
|
for (int p = 0; p < numInParams; p++) {
|
|
ops++;
|
|
if (inParams.get(p).equals(paramId)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
/**
|
|
* Simulates the fixed O(A*O) inParams lookup using a hash set.
|
|
*
|
|
* @param numAtoms number of function invocation atoms (A)
|
|
* @param numOperands operands per atom (O)
|
|
* @param numInParams number of input parameters (P)
|
|
* @return total comparison operations performed
|
|
*/
|
|
static long fixedShaderWrite(int numAtoms, int numOperands, int numInParams) {
|
|
Set<Integer> inParamSet = new HashSet<>(numInParams);
|
|
for (int i = 0; i < numInParams; i++) {
|
|
inParamSet.add(i);
|
|
}
|
|
|
|
long ops = 0;
|
|
for (int a = 0; a < numAtoms; a++) {
|
|
for (int o = 0; o < numOperands; o++) {
|
|
int paramId = (a * numOperands + o) % (numInParams * 2);
|
|
ops++; // hash-set lookup is O(1)
|
|
boolean found = inParamSet.contains(paramId);
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
// --- Correctness test: both must agree on which params are "input params" ---
|
|
int P = 10, A = 5, O = 4;
|
|
List<Integer> inParams = new ArrayList<>();
|
|
for (int i = 0; i < P; i++) inParams.add(i);
|
|
Set<Integer> inParamSet = new HashSet<>(inParams);
|
|
|
|
List<Boolean> defectResults = new ArrayList<>();
|
|
List<Boolean> fixedResults = new ArrayList<>();
|
|
for (int a = 0; a < A; a++) {
|
|
for (int o = 0; o < O; o++) {
|
|
int paramId = (a * O + o) % (P * 2);
|
|
defectResults.add(inParams.contains(paramId));
|
|
fixedResults.add(inParamSet.contains(paramId));
|
|
}
|
|
}
|
|
assert defectResults.equals(fixedResults)
|
|
: "Correctness check failed: defective and fixed produce different results";
|
|
System.out.println("PASS: correctness verified for inParams membership lookup");
|
|
|
|
// --- Op-count ratio test ---
|
|
System.out.println("\nOperation count ratio (defective / fixed):");
|
|
int[][] configs = {
|
|
{50, 8, 20},
|
|
{100, 10, 50},
|
|
{200, 12, 100},
|
|
};
|
|
for (int[] cfg : configs) {
|
|
int atoms = cfg[0], operands = cfg[1], params = cfg[2];
|
|
long dOps = defectiveShaderWrite(atoms, operands, params);
|
|
long fOps = fixedShaderWrite(atoms, operands, params);
|
|
double ratio = (double) dOps / fOps;
|
|
System.out.printf(" A=%3d O=%2d P=%3d defect=%7d fixed=%5d ratio=%.1fx%n",
|
|
atoms, operands, params, dOps, fOps, ratio);
|
|
assert ratio > 2.0
|
|
: "Expected ratio > 2x, got " + ratio + " for A=" + atoms + " O=" + operands + " P=" + params;
|
|
}
|
|
|
|
// --- Wall-clock timing ---
|
|
int bigA = 400, bigO = 20, bigP = 200;
|
|
long t0 = System.nanoTime();
|
|
defectiveShaderWrite(bigA, bigO, bigP);
|
|
long defectNs = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
fixedShaderWrite(bigA, bigO, bigP);
|
|
long fixedNs = System.nanoTime() - t0;
|
|
|
|
double wallRatio = (double) defectNs / Math.max(fixedNs, 1);
|
|
System.out.printf("%nWall-clock timing for A=%d O=%d P=%d:%n", bigA, bigO, bigP);
|
|
System.out.printf(" defective: %6.2f ms%n", defectNs / 1e6);
|
|
System.out.printf(" fixed: %6.2f ms%n", fixedNs / 1e6);
|
|
System.out.printf(" ratio: %.1fx%n", wallRatio);
|
|
|
|
assert wallRatio > 1.5
|
|
: "Expected wall-clock ratio > 1.5x, got " + wallRatio;
|
|
|
|
System.out.println("\nPASS: all assertions passed");
|
|
}
|
|
}
|