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).
166 lines
6.9 KiB
Java
166 lines
6.9 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* CWE-407 simulation: FreeSWITCH switch_loadable_module_get_codecs_sorted
|
|
* O(N²) codec preference deduplication.
|
|
*
|
|
* Defect: switch_loadable_module_get_codecs_sorted() iterates prefs[0..preflen)
|
|
* and for each entry x, re-parses prefs[0..x-1] via switch_parse_codec_buf
|
|
* to check for duplicates — O(N²) with N = number of codec preferences.
|
|
*
|
|
* This function is called on every SIP call setup (switch_core_media_prepare_codecs).
|
|
* With N=SWITCH_MAX_CODECS=50 and high call volumes, this becomes a hot inner loop.
|
|
*
|
|
* Fix: pre-parse all prefs into a struct array in one O(N) pass, then check
|
|
* against the pre-parsed array — eliminates redundant string parsing from O(N²)
|
|
* to O(N) parsing + O(N²) comparison (but compare is just strcmp not full parse).
|
|
*/
|
|
public class FreeSWITCHTest {
|
|
|
|
// Simulate a parsed codec preference entry (simplified)
|
|
static class CodecPref {
|
|
final String name;
|
|
final int interval;
|
|
final int rate;
|
|
final int channels;
|
|
final String fmtp;
|
|
|
|
CodecPref(String spec) {
|
|
// Simulate switch_parse_codec_buf: "PCMU@20i@8000h" → name=PCMU, interval=20, rate=8000
|
|
String[] parts = spec.split("@");
|
|
this.name = parts[0];
|
|
int iv = 0, r = 0, ch = 1;
|
|
String fmt = "";
|
|
for (int i = 1; i < parts.length; i++) {
|
|
String p = parts[i];
|
|
if (p.endsWith("i")) iv = Integer.parseInt(p.substring(0, p.length()-1));
|
|
else if (p.endsWith("h")) r = Integer.parseInt(p.substring(0, p.length()-1));
|
|
else if (p.endsWith("c")) ch = Integer.parseInt(p.substring(0, p.length()-1));
|
|
else fmt = p;
|
|
}
|
|
this.interval = iv == 0 ? defaultPtime(name) : iv;
|
|
this.rate = r == 0 ? defaultRate(name) : r;
|
|
this.channels = ch;
|
|
this.fmtp = fmt;
|
|
}
|
|
|
|
static int defaultPtime(String name) { return 20; }
|
|
static int defaultRate(String name) { return 8000; }
|
|
|
|
boolean matches(CodecPref o) {
|
|
return this.name.equalsIgnoreCase(o.name)
|
|
&& this.interval == o.interval
|
|
&& this.rate == o.rate
|
|
&& this.channels == o.channels
|
|
&& this.fmtp.equalsIgnoreCase(o.fmtp);
|
|
}
|
|
}
|
|
|
|
// --- Defect: re-parse each prefs[j] for every x (O(N²) parsing) ---
|
|
static List<String> deduplicateDefect(String[] prefs) {
|
|
List<String> result = new ArrayList<>();
|
|
for (int x = 0; x < prefs.length; x++) {
|
|
CodecPref cur = new CodecPref(prefs[x]); // parse prefs[x]
|
|
boolean dup = false;
|
|
for (int j = 0; j < x; j++) {
|
|
CodecPref prev = new CodecPref(prefs[j]); // re-parse prefs[j] every time!
|
|
if (cur.matches(prev)) { dup = true; break; }
|
|
}
|
|
if (!dup) result.add(prefs[x]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// --- Fix: pre-parse all prefs once, then O(N²) compare only ---
|
|
static List<String> deduplicateFix(String[] prefs) {
|
|
// Pre-pass: parse once
|
|
CodecPref[] parsed = new CodecPref[prefs.length];
|
|
for (int i = 0; i < prefs.length; i++) {
|
|
parsed[i] = new CodecPref(prefs[i]);
|
|
}
|
|
// Dedup using pre-parsed structs
|
|
List<String> result = new ArrayList<>();
|
|
outer:
|
|
for (int x = 0; x < prefs.length; x++) {
|
|
for (int j = 0; j < x; j++) {
|
|
if (parsed[x].matches(parsed[j])) continue outer;
|
|
}
|
|
result.add(prefs[x]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static String[] buildPrefs(int n, int dupRate) {
|
|
// Build N codec prefs; dupRate% are duplicates
|
|
String[] base = {"PCMU@20i@8000h", "PCMA@20i@8000h", "G729@20i@8000h",
|
|
"G722@20i@16000h", "OPUS@20i@48000h", "G723@30i@8000h",
|
|
"G726@20i@8000h", "GSM@20i@8000h", "ILBC@30i@8000h",
|
|
"G728@20i@8000h"};
|
|
String[] prefs = new String[n];
|
|
Random rng = new Random(42);
|
|
for (int i = 0; i < n; i++) {
|
|
if (i > 0 && rng.nextInt(100) < dupRate) {
|
|
prefs[i] = prefs[rng.nextInt(i)]; // duplicate
|
|
} else {
|
|
prefs[i] = base[i % base.length] + (i >= base.length ? "@v" + i : "");
|
|
}
|
|
}
|
|
return prefs;
|
|
}
|
|
|
|
static long timeDefect(String[] prefs, int iterations) {
|
|
long start = System.nanoTime();
|
|
for (int i = 0; i < iterations; i++) deduplicateDefect(prefs);
|
|
return System.nanoTime() - start;
|
|
}
|
|
|
|
static long timeFix(String[] prefs, int iterations) {
|
|
long start = System.nanoTime();
|
|
for (int i = 0; i < iterations; i++) deduplicateFix(prefs);
|
|
return System.nanoTime() - start;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== CWE-407: FreeSWITCH get_codecs_sorted O(N²) re-parse dedup ===");
|
|
System.out.println();
|
|
|
|
// --- Correctness: both produce same dedup result ---
|
|
int N = 50; // SWITCH_MAX_CODECS
|
|
String[] prefs50 = buildPrefs(N, 20);
|
|
List<String> defectResult = deduplicateDefect(prefs50);
|
|
List<String> fixResult = deduplicateFix(prefs50);
|
|
assert defectResult.equals(fixResult)
|
|
: "Dedup results differ: " + defectResult + " vs " + fixResult;
|
|
System.out.printf("PASS: defect and fix produce identical dedup (%d → %d unique)%n",
|
|
N, defectResult.size());
|
|
|
|
// --- Performance: warm up ---
|
|
int ITER = 20_000;
|
|
for (int w = 0; w < 3; w++) { timeDefect(prefs50, ITER); timeFix(prefs50, ITER); }
|
|
|
|
long defectNs = timeDefect(prefs50, ITER);
|
|
long fixNs = timeFix(prefs50, ITER);
|
|
double ratio = (double) defectNs / fixNs;
|
|
|
|
System.out.printf("Defect (re-parse O(N²)): %,d ns over %,d iterations%n", defectNs, ITER);
|
|
System.out.printf("Fix (pre-parse O(N)): %,d ns over %,d iterations%n", fixNs, ITER);
|
|
System.out.printf("Speedup ratio: %.1fx%n", ratio);
|
|
System.out.println();
|
|
|
|
// --- Complexity demo: varies N ---
|
|
System.out.println("Complexity scaling (20% duplicates):");
|
|
System.out.printf(" %-6s %-14s %-14s %-8s%n", "N", "Defect(ns)", "Fix(ns)", "Ratio");
|
|
for (int n : new int[]{5, 10, 20, 30, 50}) {
|
|
String[] p = buildPrefs(n, 20);
|
|
// warm
|
|
for (int w = 0; w < 2; w++) { timeDefect(p, ITER); timeFix(p, ITER); }
|
|
long d = timeDefect(p, ITER);
|
|
long f = timeFix(p, ITER);
|
|
System.out.printf(" %-6d %-14d %-14d %-8.1f%n", n, d, f, (double) d / f);
|
|
}
|
|
|
|
assert ratio >= 1.5 : String.format("Expected speedup >= 1.5x, got %.1fx", ratio);
|
|
System.out.println();
|
|
System.out.println("PASS: all assertions satisfied");
|
|
}
|
|
}
|