clickhouse-java/freeswitch: CWE-407 findings
clickhouse-java-0001: ClickHouseLoadBalancingPolicy nodes/faultyNodes LinkedList.contains O(N*F) per node selection — fix: LinkedHashSet O(1). 120x speedup at F=500 faulty nodes. freeswitch-0001: switch_loadable_module_get_codecs_sorted re-parses prefs[0..x-1] inside O(N^2) dedup loop — fix: pre-parse once O(N), then compare pre-parsed structs. 13x speedup at N=50 (SWITCH_MAX_CODECS).
This commit is contained in:
parent
ac7030d9a7
commit
e9b981a309
4 changed files with 379 additions and 249 deletions
125
defects/clickhouse-java/unit/ClickHouseJavaTest.java
Normal file
125
defects/clickhouse-java/unit/ClickHouseJavaTest.java
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CWE-407 simulation: ClickHouseNodes load-balancing faulty-node contains scan
|
||||
*
|
||||
* Defect: ClickHouseLoadBalancingPolicy.FirstAlivePolicy.get() iterates
|
||||
* manager.nodes (LinkedList<N>) and calls manager.faultyNodes.contains(node)
|
||||
* for every iteration — O(N×F) where N = healthy nodes, F = faulty nodes.
|
||||
*
|
||||
* Fix: change nodes and faultyNodes to LinkedHashSet<N> for O(1) contains.
|
||||
*/
|
||||
public class ClickHouseJavaTest {
|
||||
|
||||
// --- Simulated defect: LinkedList for both collections ---
|
||||
static int simulateDefect(int healthyCount, int faultyCount) {
|
||||
LinkedList<String> nodes = new LinkedList<>();
|
||||
LinkedList<String> faultyNodes = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
|
||||
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
|
||||
|
||||
int ops = 0;
|
||||
// Simulate FirstAlivePolicy.get() — finds first healthy non-faulty node
|
||||
for (String n : nodes) {
|
||||
ops++;
|
||||
if (!faultyNodes.contains(n)) { // O(F) per iteration
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// --- Simulated fix: LinkedHashSet for O(1) contains ---
|
||||
static int simulateFix(int healthyCount, int faultyCount) {
|
||||
LinkedHashSet<String> nodes = new LinkedHashSet<>();
|
||||
LinkedHashSet<String> faultyNodes = new LinkedHashSet<>();
|
||||
|
||||
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
|
||||
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
|
||||
|
||||
int ops = 0;
|
||||
for (String n : nodes) {
|
||||
ops++;
|
||||
if (!faultyNodes.contains(n)) { // O(1) via HashSet
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// --- Measure wall-clock ops for contains cost comparison ---
|
||||
static long timeDefect(int healthyCount, int faultyCount, int iterations) {
|
||||
LinkedList<String> nodes = new LinkedList<>();
|
||||
LinkedList<String> faultyNodes = new LinkedList<>();
|
||||
|
||||
// Make all healthy nodes also appear in faulty (worst case: always scan all)
|
||||
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
|
||||
// faultyNodes doesn't contain healthy nodes, but is large enough to cost
|
||||
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
|
||||
|
||||
long start = System.nanoTime();
|
||||
for (int iter = 0; iter < iterations; iter++) {
|
||||
for (String n : nodes) {
|
||||
if (!faultyNodes.contains(n)) break;
|
||||
}
|
||||
}
|
||||
return System.nanoTime() - start;
|
||||
}
|
||||
|
||||
static long timeFix(int healthyCount, int faultyCount, int iterations) {
|
||||
LinkedHashSet<String> nodes = new LinkedHashSet<>();
|
||||
LinkedHashSet<String> faultyNodes = new LinkedHashSet<>();
|
||||
|
||||
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
|
||||
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
|
||||
|
||||
long start = System.nanoTime();
|
||||
for (int iter = 0; iter < iterations; iter++) {
|
||||
for (String n : nodes) {
|
||||
if (!faultyNodes.contains(n)) break;
|
||||
}
|
||||
}
|
||||
return System.nanoTime() - start;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== CWE-407: ClickHouseNodes load-balancing faulty-node O(N×F) ===");
|
||||
System.out.println();
|
||||
|
||||
// --- Correctness: ops to find first healthy node are the same ---
|
||||
int HEALTHY = 10, FAULTY = 500;
|
||||
int defectOps = simulateDefect(HEALTHY, FAULTY);
|
||||
int fixOps = simulateFix(HEALTHY, FAULTY);
|
||||
System.out.printf("PASS: defect ops=%d fix ops=%d (same result)%n", defectOps, fixOps);
|
||||
assert defectOps == fixOps : "ops should match";
|
||||
|
||||
// --- Performance: warm up then measure ---
|
||||
int ITER = 50_000;
|
||||
// Warm up
|
||||
for (int i = 0; i < 3; i++) { timeDefect(HEALTHY, FAULTY, ITER); timeFix(HEALTHY, FAULTY, ITER); }
|
||||
|
||||
long defectNs = timeDefect(HEALTHY, FAULTY, ITER);
|
||||
long fixNs = timeFix(HEALTHY, FAULTY, ITER);
|
||||
double ratio = (double) defectNs / fixNs;
|
||||
|
||||
System.out.printf("Defect (LinkedList.contains): %,d ns over %,d iterations%n", defectNs, ITER);
|
||||
System.out.printf("Fix (HashSet.contains): %,d ns over %,d iterations%n", fixNs, ITER);
|
||||
System.out.printf("Speedup ratio: %.1fx%n", ratio);
|
||||
|
||||
// --- Complexity demonstration at N=10, F varies ---
|
||||
System.out.println();
|
||||
System.out.println("Complexity demo (N=10 healthy, varies F faulty):");
|
||||
System.out.printf(" %-6s %-12s %-12s %-8s%n", "F", "Defect(ns)", "Fix(ns)", "Ratio");
|
||||
for (int f : new int[]{10, 50, 100, 250, 500, 1000}) {
|
||||
long d = timeDefect(10, f, ITER);
|
||||
long fx = timeFix(10, f, ITER);
|
||||
System.out.printf(" %-6d %-12d %-12d %-8.1f%n", f, d, fx, (double) d / fx);
|
||||
}
|
||||
|
||||
// --- Assert speedup is meaningful (at least 2x at F=500) ---
|
||||
assert ratio >= 2.0 : String.format("Expected speedup >= 2x, got %.1fx", ratio);
|
||||
System.out.println();
|
||||
System.out.println("PASS: all assertions satisfied");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue