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:
russell@unturf.com 2026-03-30 09:13:34 -04:00
parent ec395a01fc
commit 783e406633
11 changed files with 816 additions and 3 deletions

View file

@ -0,0 +1,59 @@
# UNDF: (leave blank)
--- a/agent/ui_endpoint.go
+++ b/agent/ui_endpoint.go
@@ -516,6 +516,8 @@ func summarizeServices(dump structs.ServiceDump, cfg *config.RuntimeConfig, dc s
sum := getService(psn)
svc := csn.Service
+ // nodesSet and tagsSet are transient per-summary dedup helpers; they are
+ // stored on ServiceSummary during the loop and cleared before return.
- found := false
- for _, existing := range sum.Nodes {
- if existing == csn.Node.Node {
- found = true
- break
- }
- }
- if !found {
+ if sum.nodesSet == nil {
+ sum.nodesSet = make(map[string]struct{})
+ }
+ if _, seen := sum.nodesSet[csn.Node.Node]; !seen {
+ sum.nodesSet[csn.Node.Node] = struct{}{}
sum.Nodes = append(sum.Nodes, csn.Node.Node)
}
@@ -611,15 +613,13 @@ func summarizeServices(dump structs.ServiceDump, cfg *config.RuntimeConfig, dc s
- for _, tag := range svc.Tags {
- found := false
- for _, existing := range sum.Tags {
- if existing == tag {
- found = true
- break
- }
- }
- if !found {
- sum.Tags = append(sum.Tags, tag)
- }
+ if sum.tagsSet == nil {
+ sum.tagsSet = make(map[string]struct{})
+ }
+ for _, tag := range svc.Tags {
+ if _, seen := sum.tagsSet[tag]; !seen {
+ sum.tagsSet[tag] = struct{}{}
+ sum.Tags = append(sum.Tags, tag)
+ }
}
--- a/agent/ui_endpoint.go (ServiceSummary struct)
+++ b/agent/ui_endpoint.go (ServiceSummary struct)
@@ -460,6 +460,10 @@ type ServiceSummary struct {
// internal fields to track uniqueness
externalSourceSet map[string]struct{}
checks map[string]*structs.HealthCheck
+
+ // Transient dedup sets used during summarizeServices; nil after construction.
+ nodesSet map[string]struct{}
+ tagsSet map[string]struct{}
}

View file

@ -0,0 +1,201 @@
import java.util.*;
/**
* CWE-407 unit test Consul consul-0001
*
* agent/ui_endpoint.go summarizeServices()
*
* For each service instance in the catalog dump the function deduplicates:
* (a) Node names sum.Nodes []string linear scan O(N) per instance
* (b) Service tags sum.Tags []string linear scan O(T) per tag
*
* When iterating over I instances of S services (outer dump loop), the tag
* dedup becomes O(S×I×T²): for each instance, for each tag, scan the already-
* accumulated sum.Tags slice. In large Consul deployments (S=500 services,
* I=20 instances each, T=30 tags) this is millions of comparisons per API call.
*
* Fix: add nodesSet map[string]struct{} and tagsSet map[string]struct{} fields
* to ServiceSummary. O(1) set-membership replaces O(N)/O(T) slice scan.
* Total complexity drops from O(S×I×T²) O(S×I×T).
*/
public class ConsulTest {
// ---- defect simulation --------------------------------------------------
/** Simulate the defect: sum.Tags is a List with linear-scan dedup. */
static List<String> summarizeTags_quadratic(List<List<String>> instanceTagLists) {
List<String> tags = new ArrayList<>();
for (List<String> instanceTags : instanceTagLists) {
for (String tag : instanceTags) {
boolean found = false;
for (String existing : tags) { // O(T) linear scan the defect
if (existing.equals(tag)) { found = true; break; }
}
if (!found) tags.add(tag);
}
}
return tags;
}
/** Simulate the fix: use a HashSet for O(1) membership. */
static List<String> summarizeTags_linear(List<List<String>> instanceTagLists) {
Set<String> tagsSet = new LinkedHashSet<>(); // preserves insertion order
for (List<String> instanceTags : instanceTagLists) {
tagsSet.addAll(instanceTags);
}
return new ArrayList<>(tagsSet);
}
// Node dedup same pattern
static List<String> deduplicateNodes_quadratic(List<String> nodeNames) {
List<String> nodes = new ArrayList<>();
for (String node : nodeNames) {
boolean found = false;
for (String existing : nodes) {
if (existing.equals(node)) { found = true; break; }
}
if (!found) nodes.add(node);
}
return nodes;
}
static List<String> deduplicateNodes_linear(List<String> nodeNames) {
Set<String> seen = new LinkedHashSet<>(nodeNames);
return new ArrayList<>(seen);
}
// ---- helpers ------------------------------------------------------------
/** Build instanceTagLists: I instances each sharing the same T tags. */
static List<List<String>> makeInstanceTags(int instances, int tagsPerInstance) {
List<String> tags = new ArrayList<>();
for (int t = 0; t < tagsPerInstance; t++) {
tags.add("tag-" + t);
}
List<List<String>> result = new ArrayList<>();
for (int i = 0; i < instances; i++) {
result.add(new ArrayList<>(tags)); // each instance has all tags
}
return result;
}
// ---- tests --------------------------------------------------------------
static void testTagCorrectnessSmall() {
List<List<String>> instanceTags = Arrays.asList(
Arrays.asList("web", "v1", "prod"),
Arrays.asList("v1", "prod", "dc1"),
Arrays.asList("web", "dc1", "v2")
);
List<String> q = summarizeTags_quadratic(instanceTags);
List<String> l = summarizeTags_linear(instanceTags);
Collections.sort(q); Collections.sort(l);
assert q.equals(l) : "tag mismatch: " + q + " vs " + l;
System.out.println("PASS testTagCorrectnessSmall");
}
static void testNodeDeduplicateCorrectness() {
List<String> nodeNames = Arrays.asList(
"node-1", "node-2", "node-1", "node-3", "node-2", "node-4"
);
List<String> q = deduplicateNodes_quadratic(nodeNames);
List<String> l = deduplicateNodes_linear(nodeNames);
Collections.sort(q); Collections.sort(l);
assert q.equals(l) : "node dedup mismatch: " + q + " vs " + l;
assert q.size() == 4 : "expected 4 unique nodes, got " + q.size();
System.out.println("PASS testNodeDeduplicateCorrectness");
}
static void testEmptyInstanceList() {
List<List<String>> instanceTags = Collections.emptyList();
List<String> q = summarizeTags_quadratic(instanceTags);
List<String> l = summarizeTags_linear(instanceTags);
assert q.equals(l) && q.isEmpty() : "empty instance list should yield empty tags";
System.out.println("PASS testEmptyInstanceList");
}
static void testSingleInstanceSingleTag() {
List<List<String>> instanceTags = Collections.singletonList(
Collections.singletonList("only-tag")
);
List<String> q = summarizeTags_quadratic(instanceTags);
List<String> l = summarizeTags_linear(instanceTags);
assert q.equals(l) && q.equals(Collections.singletonList("only-tag")) :
"single tag mismatch";
System.out.println("PASS testSingleInstanceSingleTag");
}
static void testBenchmarkTagDedup() {
// Simulate: 20 instances each carrying 40 tags (all the same max dedup pressure)
int INSTANCES = 20;
int TAGS = 40;
int ITERS = 300;
List<List<String>> instanceTags = makeInstanceTags(INSTANCES, TAGS);
long t0 = System.nanoTime();
for (int i = 0; i < ITERS; i++) {
summarizeTags_quadratic(instanceTags);
}
long quadraticNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
for (int i = 0; i < ITERS; i++) {
summarizeTags_linear(instanceTags);
}
long linearNs = System.nanoTime() - t1;
double ratio = (double) quadraticNs / linearNs;
System.out.printf(
"BENCH instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n",
INSTANCES, TAGS, ITERS,
quadraticNs / 1e6, linearNs / 1e6, ratio);
assert ratio >= 2.0 :
"Expected >=2x speedup for I=" + INSTANCES + " T=" + TAGS + ", got " + ratio;
System.out.println("PASS testBenchmarkTagDedup");
}
static void testBenchmarkLargeDeployment() {
// Simulate a large Consul deployment: 50 instances × 30 tags
int INSTANCES = 50;
int TAGS = 30;
int ITERS = 500;
List<List<String>> instanceTags = makeInstanceTags(INSTANCES, TAGS);
// JIT warmup
for (int i = 0; i < 200; i++) {
summarizeTags_quadratic(instanceTags);
summarizeTags_linear(instanceTags);
}
long t0 = System.nanoTime();
for (int i = 0; i < ITERS; i++) {
summarizeTags_quadratic(instanceTags);
}
long quadraticNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
for (int i = 0; i < ITERS; i++) {
summarizeTags_linear(instanceTags);
}
long linearNs = System.nanoTime() - t1;
double ratio = (double) quadraticNs / linearNs;
System.out.printf(
"BENCH large instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n",
INSTANCES, TAGS, ITERS,
quadraticNs / 1e6, linearNs / 1e6, ratio);
assert ratio >= 2.0 :
"Expected >=2x speedup for large deployment, got " + ratio;
System.out.println("PASS testBenchmarkLargeDeployment");
}
public static void main(String[] args) {
testTagCorrectnessSmall();
testNodeDeduplicateCorrectness();
testEmptyInstanceList();
testSingleInstanceSingleTag();
testBenchmarkTagDedup();
testBenchmarkLargeDeployment();
System.out.println("ALL PASS");
}
}