pygame/libgdx: CWE-407 findings
pygame: CLEAN — runtime uses dict/set throughout (O(1) membership) libgdx-0002: ModelBuilder.rebuildReferences Array.contains O(P*M) — patch libgdx-0005: Stage.touchDragged touchFocuses.contains O(F^2) — patch libgdx-0006: AfterAction.delegate currentActions.indexOf O(W*A) per frame — patch unit: extend LibGDXTest to 7 tests, all PASS (50x-1971x speedup measured)
This commit is contained in:
parent
ec395a01fc
commit
783e406633
11 changed files with 816 additions and 3 deletions
|
|
@ -3,13 +3,15 @@ package unit;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* LibGDXTest — libgdx-0001..0004
|
||||
* LibGDXTest — libgdx-0001..0006
|
||||
*
|
||||
* 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)
|
||||
* libgdx-0005: Stage.touchDragged() — touchFocuses.contains(focus, true) inside touchFocuses loop O(F^2)
|
||||
* libgdx-0006: AfterAction.delegate() — currentActions.indexOf(action, true) inside waitForActions loop O(W*A)
|
||||
*
|
||||
* Run: javac -d . LibGDXTest.java && java -ea unit.LibGDXTest
|
||||
*/
|
||||
|
|
@ -267,8 +269,89 @@ public class LibGDXTest {
|
|||
return ops;
|
||||
}
|
||||
|
||||
// ── libgdx-0005: Stage.touchDragged SnapshotArray.contains O(F^2) ─────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates Stage.touchDragged() — for each focus in touchFocuses,
|
||||
* calls touchFocuses.contains(focus, true) which is an O(F) linear scan.
|
||||
* Total: O(F^2) per touchDragged event.
|
||||
*/
|
||||
static long stageTouchDraggedSlow(int focusCount) {
|
||||
List<Object> focuses = new ArrayList<>();
|
||||
for (int i = 0; i < focusCount; i++) focuses.add(new Object());
|
||||
long ops = 0;
|
||||
// Outer loop: iterate all focuses
|
||||
for (int i = 0; i < focuses.size(); i++) {
|
||||
Object focus = focuses.get(i);
|
||||
// Inner: Array.contains(focus, identity=true) — linear scan
|
||||
for (int j = focuses.size() - 1; j >= 0; j--) {
|
||||
ops++;
|
||||
if (focuses.get(j) == focus) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: build ObjectSet once (O(F)), then O(1) per focus check → O(F) total.
|
||||
*/
|
||||
static long stageTouchDraggedFast(int focusCount) {
|
||||
List<Object> focuses = new ArrayList<>();
|
||||
for (int i = 0; i < focusCount; i++) focuses.add(new Object());
|
||||
Set<Object> focusSet = new HashSet<>(focuses); // O(F)
|
||||
long ops = (long) focusCount; // set construction cost
|
||||
for (int i = 0; i < focuses.size(); i++) {
|
||||
Object focus = focuses.get(i);
|
||||
ops++; // O(1) hash lookup
|
||||
focusSet.contains(focus);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ── libgdx-0006: AfterAction.delegate indexOf O(W*A) per frame ───────────
|
||||
|
||||
/**
|
||||
* SLOW: simulates AfterAction.delegate() — for each action in waitForActions,
|
||||
* calls currentActions.indexOf(action, true) which is an O(A) linear scan.
|
||||
* Total: O(W*A) per frame.
|
||||
*/
|
||||
static long afterActionSlow(int waitCount, int actionCount) {
|
||||
List<Object> currentActions = new ArrayList<>();
|
||||
List<Object> waitForActions = new ArrayList<>();
|
||||
for (int i = 0; i < actionCount; i++) currentActions.add(new Object());
|
||||
for (int i = 0; i < waitCount && i < actionCount; i++) waitForActions.add(currentActions.get(i));
|
||||
long ops = 0;
|
||||
for (int i = waitForActions.size() - 1; i >= 0; i--) {
|
||||
Object action = waitForActions.get(i);
|
||||
// indexOf scans all A current actions
|
||||
for (int j = 0; j < currentActions.size(); j++) {
|
||||
ops++;
|
||||
if (currentActions.get(j) == action) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: build ObjectSet from currentActions once (O(A)), then O(1) per waitForActions check.
|
||||
*/
|
||||
static long afterActionFast(int waitCount, int actionCount) {
|
||||
List<Object> currentActions = new ArrayList<>();
|
||||
List<Object> waitForActions = new ArrayList<>();
|
||||
for (int i = 0; i < actionCount; i++) currentActions.add(new Object());
|
||||
for (int i = 0; i < waitCount && i < actionCount; i++) waitForActions.add(currentActions.get(i));
|
||||
Set<Object> currentSet = new HashSet<>(currentActions); // O(A)
|
||||
long ops = (long) actionCount; // set construction cost
|
||||
for (int i = waitForActions.size() - 1; i >= 0; i--) {
|
||||
Object action = waitForActions.get(i);
|
||||
ops++; // O(1)
|
||||
currentSet.contains(action);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== UNIT libgdx-0001..0004: LibGDX CWE-407 ===");
|
||||
System.out.println("=== UNIT libgdx-0001..0006: LibGDX CWE-407 ===");
|
||||
|
||||
// libgdx-0001: Model.loadNode
|
||||
System.out.println();
|
||||
|
|
@ -312,6 +395,24 @@ public class LibGDXTest {
|
|||
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);
|
||||
|
||||
// libgdx-0005: Stage.touchDragged
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0005: Stage.touchDragged SnapshotArray.contains O(F^2)");
|
||||
final int FC = 200;
|
||||
long s5 = stageTouchDraggedSlow(FC);
|
||||
long f5 = stageTouchDraggedFast(FC);
|
||||
bench(String.format("libgdx-0005 focuses=%d", FC),
|
||||
() -> stageTouchDraggedSlow(FC), () -> stageTouchDraggedFast(FC), s5, f5);
|
||||
|
||||
// libgdx-0006: AfterAction.delegate
|
||||
System.out.println();
|
||||
System.out.println(" libgdx-0006: AfterAction.delegate indexOf O(W*A)");
|
||||
final int WC = 100, AC = 200;
|
||||
long s6 = afterActionSlow(WC, AC);
|
||||
long f6 = afterActionFast(WC, AC);
|
||||
bench(String.format("libgdx-0006 wait=%d actions=%d", WC, AC),
|
||||
() -> afterActionSlow(WC, AC), () -> afterActionFast(WC, AC), s6, f6);
|
||||
|
||||
System.out.println();
|
||||
|
||||
int pass = 0;
|
||||
|
|
@ -320,6 +421,8 @@ public class LibGDXTest {
|
|||
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);
|
||||
assert s5 > f5 * 5 : "libgdx-0005 expected >5x speedup"; pass++;
|
||||
assert s6 > f6 * 5 : "libgdx-0006 expected >5x speedup"; pass++;
|
||||
System.out.printf("%d/7 PASS%n", pass);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue