game engines/web frameworks: 27 CWE-407 defects + 3 CLEAN; 194 sites, 78 ecosystems

This commit is contained in:
russell@unturf.com 2026-03-27 14:14:49 -04:00
parent 547a9f5738
commit 4d3fcc8e73
76 changed files with 6216 additions and 17 deletions

View file

@ -0,0 +1,164 @@
package unit;
import java.util.*;
/**
* SDL3 CWE-407 benchmark: HasMappingChangeTracking linear scan vs O(1) hash set.
*
* Defect: src/joystick/SDL_gamepad.c HasMappingChangeTracking() lines 639-651
* Called inside PopMappingChangeTracking() for every joystick (line 670-687).
* changed_mappings is a pointer array scanned linearly each call.
* Total cost: O(n_joysticks × n_changed_mappings).
*
* Fix: Mirror changed_mappings into a SDL_HashTable (pointer set).
* HasMappingChangeTracking becomes one SDL_FindInHashTable call — O(1).
* SDL3 already uses SDL_HashTable for s_gamepadInstanceIDs in the same file.
*
* ticket: docs/tickets/sdl3-0001-gamepad-mapping-change-tracking-linear-scan.md
*/
public class SDL3Test {
// -----------------------------------------------------------------------
// SLOW: mirrors HasMappingChangeTracking — linear scan over pointer array
// -----------------------------------------------------------------------
static class SlowMappingChangeTracker {
List<Long> changedMappings = new ArrayList<>(); // Long = simulated pointer
void addMapping(long mappingPtr) {
changedMappings.add(mappingPtr);
}
/** Direct mirror of SDL_gamepad.c lines 645-649. */
boolean hasMappingChange(long mappingPtr) {
for (long m : changedMappings) {
if (m == mappingPtr) return true;
}
return false;
}
/** PopMappingChangeTracking inner loop: O(n_joysticks * n_changed_mappings). */
int processJoysticks(long[] joystickMappings) {
int remapped = 0;
for (long jMapping : joystickMappings) {
if (hasMappingChange(jMapping)) {
remapped++;
}
}
return remapped;
}
}
// -----------------------------------------------------------------------
// FAST: O(1) via HashSet (mirrors proposed SDL_HashTable fix)
// -----------------------------------------------------------------------
static class FastMappingChangeTracker {
Set<Long> changedMappingsSet = new HashSet<>();
void addMapping(long mappingPtr) {
changedMappingsSet.add(mappingPtr);
}
boolean hasMappingChange(long mappingPtr) {
return changedMappingsSet.contains(mappingPtr);
}
int processJoysticks(long[] joystickMappings) {
int remapped = 0;
for (long jMapping : joystickMappings) {
if (hasMappingChange(jMapping)) {
remapped++;
}
}
return remapped;
}
}
// -----------------------------------------------------------------------
// Bench harness
// -----------------------------------------------------------------------
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
slow.run(); fast.run();
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);
}
// -----------------------------------------------------------------------
// Scenarios
// -----------------------------------------------------------------------
/**
* Scenario 1: Bulk mapping reload (SDL_AddGamepadMappingsFromFile with full DB).
* M changed mappings × J joysticks (J = 8, simulating a haptics rig).
*/
static Runnable slowBulkReload(int m, int j) {
return () -> {
SlowMappingChangeTracker tracker = new SlowMappingChangeTracker();
for (int i = 0; i < m; i++) tracker.addMapping((long) (i + 1));
// Each joystick has a mapping pointer in [1..m]
long[] jMappings = new long[j];
for (int i = 0; i < j; i++) jMappings[i] = (long) (i % m + 1);
tracker.processJoysticks(jMappings);
};
}
static Runnable fastBulkReload(int m, int j) {
return () -> {
FastMappingChangeTracker tracker = new FastMappingChangeTracker();
for (int i = 0; i < m; i++) tracker.addMapping((long) (i + 1));
long[] jMappings = new long[j];
for (int i = 0; i < j; i++) jMappings[i] = (long) (i % m + 1);
tracker.processJoysticks(jMappings);
};
}
/**
* Scenario 2: Full cross-product stress — M changed mappings × J joysticks,
* simulating a large tournament rig with many controllers and a full DB swap.
*/
static Runnable slowStress(int m, int j) {
return () -> {
SlowMappingChangeTracker tracker = new SlowMappingChangeTracker();
for (int i = 0; i < m; i++) tracker.addMapping((long) (i + 1));
long[] jMappings = new long[j];
// worst case: all joystick mappings are near the end of the array
for (int i = 0; i < j; i++) jMappings[i] = (long) (m - (i % 4) - 1);
tracker.processJoysticks(jMappings);
};
}
static Runnable fastStress(int m, int j) {
return () -> {
FastMappingChangeTracker tracker = new FastMappingChangeTracker();
for (int i = 0; i < m; i++) tracker.addMapping((long) (i + 1));
long[] jMappings = new long[j];
for (int i = 0; i < j; i++) jMappings[i] = (long) (m - (i % 4) - 1);
tracker.processJoysticks(jMappings);
};
}
public static void main(String[] args) {
System.out.println("SDL3 CWE-407: HasMappingChangeTracking linear scan vs O(1) hash set");
System.out.println(" defect: src/joystick/SDL_gamepad.c lines 639-651, 687");
System.out.println();
// SDL_gamepad_db.h ships 812 entries; simulate that scale
int M = 800; // changed mappings (full DB reload)
int J = 800; // joystick count (stress scenario)
long slowOps = (long) M * J;
long fastOps = J; // O(1) per joystick
bench(String.format("bulk-reload M=%d mappings J=8 joysticks", M),
slowBulkReload(M, 8), fastBulkReload(M, 8), (long) M * 8, 8);
bench(String.format("stress M=%d mappings J=%d joysticks", M, J),
slowStress(M, J), fastStress(M, J), slowOps, fastOps);
System.out.println();
System.out.println("Fix: add SDL_HashTable *changed_mappings_set to MappingChangeTracker.");
System.out.println(" SDL3 already has SDL_HashPointer/SDL_KeyMatchPointer.");
System.out.println(" See patch sdl3-0001-mapping-change-hash-set.patch");
}
}