game engines/web frameworks: 27 CWE-407 defects + 3 CLEAN; 194 sites, 78 ecosystems
This commit is contained in:
parent
547a9f5738
commit
4d3fcc8e73
76 changed files with 6216 additions and 17 deletions
325
defects/libgdx/unit/LibGDXTest.java
Normal file
325
defects/libgdx/unit/LibGDXTest.java
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* LibGDXTest — libgdx-0001..0004
|
||||
*
|
||||
* Proves CWE-407 in libGDX:
|
||||
* libgdx-0001: Model.loadNode() — nested for-loop string-ID scan O(parts × meshes + parts × materials)
|
||||
* libgdx-0002: ModelBuilder.rebuildReferences() — Array.contains() in node-part loop O(parts × materials)
|
||||
* libgdx-0003: ModelInstance.invalidate() — Array.contains() in node-part loop O(parts × materials)
|
||||
* libgdx-0004: Kerning.readSubtable2() — IntArray.contains() in GPOS coverage loop O(coverage × classes × K)
|
||||
*
|
||||
* Run: javac -d . LibGDXTest.java && java -ea unit.LibGDXTest
|
||||
*/
|
||||
public class LibGDXTest {
|
||||
|
||||
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
|
||||
slow.run(); fast.run(); // warmup
|
||||
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
|
||||
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
|
||||
double r = fOps > 0 ? (double) sOps / fOps : 0;
|
||||
System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||||
label, sMs, sOps, fMs, fOps, r);
|
||||
}
|
||||
|
||||
// ── libgdx-0001: Model.loadNode nested for-loop string scan ──────────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates Model.loadNode() — for each node-part, scan all meshParts
|
||||
* (by string ID) and all materials (by string ID).
|
||||
* O(parts × (meshCount + materialCount))
|
||||
*/
|
||||
static long loadNodeSlow(int nodePartCount, int meshCount, int materialCount) {
|
||||
// Build arrays of String IDs
|
||||
String[] meshIds = new String[meshCount];
|
||||
String[] materialIds = new String[materialCount];
|
||||
for (int i = 0; i < meshCount; i++) meshIds[i] = "mesh_" + i;
|
||||
for (int i = 0; i < materialCount; i++) materialIds[i] = "mat_" + i;
|
||||
|
||||
long ops = 0;
|
||||
// Simulate loading nodePartCount node-parts, each referencing the last mesh/material
|
||||
String targetMesh = meshIds[meshCount - 1]; // worst case: always last
|
||||
String targetMat = materialIds[materialCount - 1];
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
// for (MeshPart part : meshParts) { if id.equals(...) ... } — O(M)
|
||||
for (int m = 0; m < meshCount; m++) {
|
||||
ops++;
|
||||
if (meshIds[m].equals(targetMesh)) break;
|
||||
}
|
||||
// for (Material mat : materials) { if id.equals(...) ... } — O(T)
|
||||
for (int t = 0; t < materialCount; t++) {
|
||||
ops++;
|
||||
if (materialIds[t].equals(targetMat)) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: simulates fixed Model.loadNode() — build HashMap<String, index> once
|
||||
* before the node loop, use O(1) get() per node-part.
|
||||
*/
|
||||
static long loadNodeFast(int nodePartCount, int meshCount, int materialCount) {
|
||||
String[] meshIds = new String[meshCount];
|
||||
String[] materialIds = new String[materialCount];
|
||||
for (int i = 0; i < meshCount; i++) meshIds[i] = "mesh_" + i;
|
||||
for (int i = 0; i < materialCount; i++) materialIds[i] = "mat_" + i;
|
||||
|
||||
// Build lookup maps once — O(M + T)
|
||||
Map<String, Integer> meshById = new HashMap<>(meshCount * 2);
|
||||
for (int i = 0; i < meshCount; i++) meshById.put(meshIds[i], i);
|
||||
Map<String, Integer> matById = new HashMap<>(materialCount * 2);
|
||||
for (int i = 0; i < materialCount; i++) matById.put(materialIds[i], i);
|
||||
|
||||
String targetMesh = meshIds[meshCount - 1];
|
||||
String targetMat = materialIds[materialCount - 1];
|
||||
|
||||
long ops = 0;
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
ops++; meshById.get(targetMesh); // O(1)
|
||||
ops++; matById.get(targetMat); // O(1)
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ── libgdx-0002: ModelBuilder.rebuildReferences Array.contains ───────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates rebuildReferences() — for each node-part, call Array.contains()
|
||||
* (linear scan by identity) to check if material/meshPart/mesh is already added.
|
||||
* O(parts × (materials + meshParts + meshes))
|
||||
*/
|
||||
static long rebuildRefsSlow(int nodePartCount, int materialCount) {
|
||||
List<Object> materials = new ArrayList<>();
|
||||
List<Object> meshParts = new ArrayList<>();
|
||||
List<Object> meshes = new ArrayList<>();
|
||||
|
||||
// Pre-create unique objects
|
||||
Object[] matObjs = new Object[materialCount];
|
||||
Object[] partObjs = new Object[materialCount];
|
||||
Object[] meshObjs = new Object[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) {
|
||||
matObjs[i] = new Object();
|
||||
partObjs[i] = new Object();
|
||||
meshObjs[i] = new Object();
|
||||
}
|
||||
|
||||
long ops = 0;
|
||||
// Each node-part references materials/parts in round-robin (causes dedup checks)
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
Object mat = matObjs[p % materialCount];
|
||||
Object part = partObjs[p % materialCount];
|
||||
Object mesh = meshObjs[p % materialCount];
|
||||
|
||||
// Array.contains — linear scan by identity — O(M)
|
||||
boolean hasMat = false;
|
||||
for (Object m : materials) { ops++; if (m == mat) { hasMat = true; break; } }
|
||||
if (!hasMat) materials.add(mat);
|
||||
|
||||
boolean hasPart = false;
|
||||
for (Object pp : meshParts) { ops++; if (pp == part) { hasPart = true; break; } }
|
||||
if (!hasPart) {
|
||||
meshParts.add(part);
|
||||
boolean hasMesh = false;
|
||||
for (Object msh : meshes) { ops++; if (msh == mesh) { hasMesh = true; break; } }
|
||||
if (!hasMesh) meshes.add(mesh);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: simulates fixed rebuildReferences() — IdentityHashMap for O(1) dedup.
|
||||
*/
|
||||
static long rebuildRefsFast(int nodePartCount, int materialCount) {
|
||||
IdentityHashMap<Object, Boolean> matSeen = new IdentityHashMap<>();
|
||||
IdentityHashMap<Object, Boolean> partSeen = new IdentityHashMap<>();
|
||||
IdentityHashMap<Object, Boolean> meshSeen = new IdentityHashMap<>();
|
||||
List<Object> materials = new ArrayList<>();
|
||||
List<Object> meshParts = new ArrayList<>();
|
||||
List<Object> meshes = new ArrayList<>();
|
||||
|
||||
Object[] matObjs = new Object[materialCount];
|
||||
Object[] partObjs = new Object[materialCount];
|
||||
Object[] meshObjs = new Object[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) {
|
||||
matObjs[i] = new Object();
|
||||
partObjs[i] = new Object();
|
||||
meshObjs[i] = new Object();
|
||||
}
|
||||
|
||||
long ops = 0;
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
Object mat = matObjs[p % materialCount];
|
||||
Object part = partObjs[p % materialCount];
|
||||
Object mesh = meshObjs[p % materialCount];
|
||||
|
||||
ops++;
|
||||
if (matSeen.put(mat, Boolean.TRUE) == null) materials.add(mat); // O(1)
|
||||
ops++;
|
||||
if (partSeen.put(part, Boolean.TRUE) == null) {
|
||||
meshParts.add(part);
|
||||
ops++;
|
||||
if (meshSeen.put(mesh, Boolean.TRUE) == null) meshes.add(mesh); // O(1)
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ── libgdx-0003: ModelInstance.invalidate Array.contains ─────────────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates ModelInstance.invalidate() — for each node-part, call
|
||||
* materials.contains(part.material, identity=true) — O(T) linear scan.
|
||||
*/
|
||||
static long invalidateSlow(int nodePartCount, int materialCount) {
|
||||
List<Object> materials = new ArrayList<>();
|
||||
Object[] matObjs = new Object[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) matObjs[i] = new Object();
|
||||
|
||||
long ops = 0;
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
Object mat = matObjs[p % materialCount];
|
||||
boolean found = false;
|
||||
for (Object m : materials) { ops++; if (m == mat) { found = true; break; } }
|
||||
if (!found) materials.add(mat);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: simulates fixed invalidate() — IdentityHashMap dedup, O(1) per part.
|
||||
*/
|
||||
static long invalidateFast(int nodePartCount, int materialCount) {
|
||||
IdentityHashMap<Object, Boolean> seen = new IdentityHashMap<>();
|
||||
List<Object> materials = new ArrayList<>();
|
||||
Object[] matObjs = new Object[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) matObjs[i] = new Object();
|
||||
|
||||
long ops = 0;
|
||||
for (int p = 0; p < nodePartCount; p++) {
|
||||
Object mat = matObjs[p % materialCount];
|
||||
ops++;
|
||||
if (seen.put(mat, Boolean.TRUE) == null) materials.add(mat); // O(1)
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ── libgdx-0004: Kerning GPOS IntArray.contains ──────────────────────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates GPOS coverage loop — for each covered glyph, scan all class
|
||||
* definitions with IntArray.contains() (linear scan) to find class membership.
|
||||
* O(coverageLen × class1Count × avg_glyphs_per_class)
|
||||
*/
|
||||
static long kerningGposSlow(int coverageLen, int class1Count, int glyphsPerClass) {
|
||||
// Build class arrays: class c contains glyphs [c*glyphsPerClass .. (c+1)*glyphsPerClass)
|
||||
int[][] glyphsByClass = new int[class1Count][];
|
||||
for (int c = 1; c < class1Count; c++) {
|
||||
glyphsByClass[c] = new int[glyphsPerClass];
|
||||
for (int k = 0; k < glyphsPerClass; k++)
|
||||
glyphsByClass[c][k] = c * glyphsPerClass + k;
|
||||
}
|
||||
glyphsByClass[0] = new int[0]; // class 0 = unclassified
|
||||
|
||||
long ops = 0;
|
||||
// Coverage glyphs: worst case, each one is at the end of the last class (or unclassified)
|
||||
for (int i = 0; i < coverageLen; i++) {
|
||||
int glyph = (class1Count - 1) * glyphsPerClass + (i % glyphsPerClass); // last class
|
||||
boolean found = false;
|
||||
for (int j = 1; j < class1Count && !found; j++) {
|
||||
// IntArray.contains — linear scan
|
||||
for (int k = 0; k < glyphsByClass[j].length; k++) {
|
||||
ops++;
|
||||
if (glyphsByClass[j][k] == glyph) { found = true; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: simulates fixed GPOS handler — build IntIntMap (glyph→class) once,
|
||||
* then O(1) lookup per covered glyph.
|
||||
*/
|
||||
static long kerningGposFast(int coverageLen, int class1Count, int glyphsPerClass) {
|
||||
int[][] glyphsByClass = new int[class1Count][];
|
||||
for (int c = 1; c < class1Count; c++) {
|
||||
glyphsByClass[c] = new int[glyphsPerClass];
|
||||
for (int k = 0; k < glyphsPerClass; k++)
|
||||
glyphsByClass[c][k] = c * glyphsPerClass + k;
|
||||
}
|
||||
|
||||
// Build reverse map once — O(class1Count × glyphsPerClass)
|
||||
Map<Integer, Integer> glyphToClass = new HashMap<>((class1Count * glyphsPerClass) * 2);
|
||||
for (int c = 1; c < class1Count; c++)
|
||||
for (int k = 0; k < glyphsByClass[c].length; k++)
|
||||
glyphToClass.put(glyphsByClass[c][k], c);
|
||||
|
||||
long ops = 0;
|
||||
for (int i = 0; i < coverageLen; i++) {
|
||||
int glyph = (class1Count - 1) * glyphsPerClass + (i % glyphsPerClass);
|
||||
ops++;
|
||||
glyphToClass.containsKey(glyph); // O(1)
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== UNIT libgdx-0001..0004: LibGDX CWE-407 ===");
|
||||
|
||||
// libgdx-0001: Model.loadNode
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0001: Model.loadNode nested string-ID scan");
|
||||
final int NP1 = 500, M1 = 100, T1 = 50;
|
||||
long s0 = loadNodeSlow(NP1, M1, T1);
|
||||
long f0 = loadNodeFast(NP1, M1, T1);
|
||||
bench(String.format("libgdx-0001 parts=%d meshes=%d mats=%d", NP1, M1, T1),
|
||||
() -> loadNodeSlow(NP1, M1, T1), () -> loadNodeFast(NP1, M1, T1), s0, f0);
|
||||
|
||||
final int NP2 = 2000, M2 = 200, T2 = 100;
|
||||
long s1 = loadNodeSlow(NP2, M2, T2);
|
||||
long f1 = loadNodeFast(NP2, M2, T2);
|
||||
bench(String.format("libgdx-0001 parts=%d meshes=%d mats=%d", NP2, M2, T2),
|
||||
() -> loadNodeSlow(NP2, M2, T2), () -> loadNodeFast(NP2, M2, T2), s1, f1);
|
||||
|
||||
// libgdx-0002: ModelBuilder.rebuildReferences
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0002: ModelBuilder.rebuildReferences Array.contains");
|
||||
final int RNP = 1000, RM = 50;
|
||||
long s2 = rebuildRefsSlow(RNP, RM);
|
||||
long f2 = rebuildRefsFast(RNP, RM);
|
||||
bench(String.format("libgdx-0002 parts=%d materials=%d", RNP, RM),
|
||||
() -> rebuildRefsSlow(RNP, RM), () -> rebuildRefsFast(RNP, RM), s2, f2);
|
||||
|
||||
// libgdx-0003: ModelInstance.invalidate
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0003: ModelInstance.invalidate Array.contains");
|
||||
final int INP = 1000, IM = 50;
|
||||
long s3 = invalidateSlow(INP, IM);
|
||||
long f3 = invalidateFast(INP, IM);
|
||||
bench(String.format("libgdx-0003 parts=%d materials=%d", INP, IM),
|
||||
() -> invalidateSlow(INP, IM), () -> invalidateFast(INP, IM), s3, f3);
|
||||
|
||||
// libgdx-0004: Kerning GPOS
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0004: Kerning GPOS IntArray.contains");
|
||||
final int KC = 1000, KN = 100, KG = 20;
|
||||
long s4 = kerningGposSlow(KC, KN, KG);
|
||||
long f4 = kerningGposFast(KC, KN, KG);
|
||||
bench(String.format("libgdx-0004 coverage=%d classes=%d glyphs/class=%d", KC, KN, KG),
|
||||
() -> kerningGposSlow(KC, KN, KG), () -> kerningGposFast(KC, KN, KG), s4, f4);
|
||||
|
||||
System.out.println();
|
||||
|
||||
int pass = 0;
|
||||
assert s0 > f0 * 5 : "libgdx-0001 (small) expected >5x speedup"; pass++;
|
||||
assert s1 > f1 * 5 : "libgdx-0001 (large) expected >5x speedup"; pass++;
|
||||
assert s2 > f2 * 5 : "libgdx-0002 expected >5x speedup"; pass++;
|
||||
assert s3 > f3 * 5 : "libgdx-0003 expected >5x speedup"; pass++;
|
||||
assert s4 > f4 * 5 : "libgdx-0004 expected >5x speedup"; pass++;
|
||||
System.out.printf("%d/5 PASS%n", pass);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue