comms/voip/smtp: 30 CWE-407 defects + 9 CLEAN; 224 sites, 101 ecosystems
This commit is contained in:
parent
4d3fcc8e73
commit
b3842ab6b8
86 changed files with 6516 additions and 5 deletions
280
defects/freeswitch/unit/FreeSWITCHTest.java
Normal file
280
defects/freeswitch/unit/FreeSWITCHTest.java
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
package unit;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CWE-407 benchmark for freeswitch defects:
|
||||
* freeswitch-0001: mod_conference.c relationship list scan inside per-sample audio mixing
|
||||
* O(S × M × M × R) — relationship linked-list scan per sample per member pair
|
||||
*
|
||||
* Simulates the audio mixing loop in C with Java equivalents:
|
||||
* - S = samples per frame (160 at 8kHz/20ms)
|
||||
* - M = conference members
|
||||
* - R = relationships per member (singly-linked list)
|
||||
*/
|
||||
public class FreeSWITCHTest {
|
||||
|
||||
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(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||||
label, sMs, sOps, fMs, fOps, r);
|
||||
}
|
||||
|
||||
// Simulated conference relationship (singly-linked list like C struct)
|
||||
static class Rel {
|
||||
int id; // member ID this relationship targets (0 = all)
|
||||
boolean canSpeak;
|
||||
boolean canHear;
|
||||
Rel next;
|
||||
Rel(int id, boolean speak, boolean hear) { this.id = id; canSpeak = speak; canHear = hear; }
|
||||
}
|
||||
|
||||
static class Member {
|
||||
int id;
|
||||
short[] frame;
|
||||
Rel relationships; // singly-linked list head
|
||||
Member(int id, int samples) {
|
||||
this.id = id;
|
||||
frame = new short[samples];
|
||||
Arrays.fill(frame, (short) 100);
|
||||
}
|
||||
void addRelationship(int targetId, boolean canSpeak, boolean canHear) {
|
||||
Rel r = new Rel(targetId, canSpeak, canHear);
|
||||
r.next = relationships;
|
||||
relationships = r;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// freeswitch-0001: relationship scan per sample
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Slow: O(S × M × M × R) — relationship linked-list scanned for every sample.
|
||||
* Matches the original mod_conference.c code structure.
|
||||
*/
|
||||
static long slowConferenceMix(List<Member> members, int samples, boolean hasRelationships) {
|
||||
long ops = 0;
|
||||
int[] mainFrame = new int[samples];
|
||||
|
||||
// Build main frame: sum all members' audio
|
||||
for (Member m : members) {
|
||||
for (int x = 0; x < samples; x++) {
|
||||
mainFrame[x] += m.frame[x];
|
||||
ops++;
|
||||
}
|
||||
}
|
||||
|
||||
// Per output member: subtract self, subtract excluded members
|
||||
for (Member omember : members) {
|
||||
int[] writeFrame = new int[samples];
|
||||
for (int x = 0; x < samples; x++) {
|
||||
ops++;
|
||||
int z = mainFrame[x] - omember.frame[x];
|
||||
|
||||
if (hasRelationships) {
|
||||
// Inner member loop
|
||||
for (Member imember : members) {
|
||||
if (imember == omember) continue;
|
||||
boolean found = false;
|
||||
// Scan imember->relationships linked list — O(R)
|
||||
for (Rel rel = imember.relationships; rel != null; rel = rel.next) {
|
||||
ops++;
|
||||
if ((rel.id == omember.id || rel.id == 0) && !rel.canSpeak) {
|
||||
z -= imember.frame[x];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// Scan omember->relationships — O(R)
|
||||
for (Rel rel = omember.relationships; rel != null; rel = rel.next) {
|
||||
ops++;
|
||||
if ((rel.id == imember.id || rel.id == 0) && !rel.canHear) {
|
||||
z -= imember.frame[x];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writeFrame[x] = z;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast: O(M² × R + S × M²) — pre-compute exclusion matrix outside sample loop.
|
||||
* Relationship scan moved out of the per-sample hot path.
|
||||
*/
|
||||
static long fastConferenceMix(List<Member> members, int samples, boolean hasRelationships) {
|
||||
long ops = 0;
|
||||
int[] mainFrame = new int[samples];
|
||||
|
||||
// Build main frame
|
||||
for (Member m : members) {
|
||||
for (int x = 0; x < samples; x++) {
|
||||
mainFrame[x] += m.frame[x];
|
||||
ops++;
|
||||
}
|
||||
}
|
||||
|
||||
// Per output member: pre-compute exclusion bitmask, then apply per sample
|
||||
for (Member omember : members) {
|
||||
// Pre-compute: which imembers are excluded for this omember?
|
||||
// O(M × R) — done ONCE per omember, not per sample
|
||||
boolean[] excludeAudio = new boolean[members.size()];
|
||||
if (hasRelationships) {
|
||||
for (int ii = 0; ii < members.size(); ii++) {
|
||||
Member imember = members.get(ii);
|
||||
if (imember == omember) continue;
|
||||
boolean found = false;
|
||||
for (Rel rel = imember.relationships; rel != null; rel = rel.next) {
|
||||
ops++;
|
||||
if ((rel.id == omember.id || rel.id == 0) && !rel.canSpeak) {
|
||||
excludeAudio[ii] = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (Rel rel = omember.relationships; rel != null; rel = rel.next) {
|
||||
ops++;
|
||||
if ((rel.id == imember.id || rel.id == 0) && !rel.canHear) {
|
||||
excludeAudio[ii] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-sample loop: O(S × M) with O(1) exclusion lookup
|
||||
int[] writeFrame = new int[samples];
|
||||
for (int x = 0; x < samples; x++) {
|
||||
ops++;
|
||||
int z = mainFrame[x] - omember.frame[x];
|
||||
if (hasRelationships) {
|
||||
for (int ii = 0; ii < members.size(); ii++) {
|
||||
ops++;
|
||||
if (excludeAudio[ii]) {
|
||||
z -= members.get(ii).frame[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
writeFrame[x] = z;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void main(String[] args) {
|
||||
int M = 20; // conference members
|
||||
int S = 160; // samples per frame (8kHz, 20ms)
|
||||
int R = 4; // relationships per member
|
||||
|
||||
// Build members
|
||||
List<Member> members = new ArrayList<>();
|
||||
for (int i = 0; i < M; i++) {
|
||||
members.add(new Member(i, S));
|
||||
}
|
||||
|
||||
// Add relationships: each member excludes 1-2 others (simulate mute/subconference)
|
||||
Random rng = new Random(42);
|
||||
for (int i = 0; i < M; i++) {
|
||||
for (int r = 0; r < R / 2; r++) {
|
||||
int target = rng.nextInt(M);
|
||||
if (target != i) {
|
||||
members.get(i).addRelationship(target, false, true); // can't speak to target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("=== freeswitch CWE-407 benchmark ===");
|
||||
System.out.printf(" M=%d members, S=%d samples/frame, R=%d relationships/member%n%n", M, S, R);
|
||||
|
||||
long sOps = slowConferenceMix(members, S, true);
|
||||
long fOps = fastConferenceMix(members, S, true);
|
||||
|
||||
bench("freeswitch-0001 conference mix [M=" + M + ",S=" + S + ",R=" + R + "] with rels",
|
||||
() -> slowConferenceMix(members, S, true),
|
||||
() -> fastConferenceMix(members, S, true),
|
||||
sOps, fOps);
|
||||
|
||||
// Also benchmark without relationships (baseline)
|
||||
long sOpsNoRel = slowConferenceMix(members, S, false);
|
||||
long fOpsNoRel = fastConferenceMix(members, S, false);
|
||||
bench("freeswitch-0001 conference mix [M=" + M + ",S=" + S + "] no rels (baseline)",
|
||||
() -> slowConferenceMix(members, S, false),
|
||||
() -> fastConferenceMix(members, S, false),
|
||||
sOpsNoRel, fOpsNoRel);
|
||||
|
||||
// Higher M to show quadratic growth
|
||||
int M2 = 40;
|
||||
List<Member> members2 = new ArrayList<>();
|
||||
for (int i = 0; i < M2; i++) {
|
||||
Member m2 = new Member(i, S);
|
||||
for (int r = 0; r < R / 2; r++) {
|
||||
int target = rng.nextInt(M2);
|
||||
if (target != i) m2.addRelationship(target, false, true);
|
||||
}
|
||||
members2.add(m2);
|
||||
}
|
||||
long sOps2 = slowConferenceMix(members2, S, true);
|
||||
long fOps2 = fastConferenceMix(members2, S, true);
|
||||
bench("freeswitch-0001 conference mix [M=" + M2 + ",S=" + S + ",R=" + R + "] with rels",
|
||||
() -> slowConferenceMix(members2, S, true),
|
||||
() -> fastConferenceMix(members2, S, true),
|
||||
sOps2, fOps2);
|
||||
|
||||
System.out.println();
|
||||
|
||||
// Assertions
|
||||
int pass = 0, total = 0;
|
||||
|
||||
// slow ops should scale as O(S*M*M*R), fast as O(S*M + M*M*R)
|
||||
// slow / fast > R (relationship scan eliminated from inner loop)
|
||||
total++;
|
||||
long expectedSlowOps = (long) S * M * M; // at minimum (without R)
|
||||
if (sOps > fOps * 2 && sOps >= expectedSlowOps) {
|
||||
System.out.printf(" freeswitch-0001 M=%d: PASS (slow=%,d >= S*M²=%,d, fast=%,d, ratio=%.1fx)%n",
|
||||
M, sOps, expectedSlowOps, fOps, (double)sOps/fOps);
|
||||
pass++;
|
||||
} else {
|
||||
System.out.printf(" freeswitch-0001 M=%d: FAIL (slow=%,d fast=%,d)%n", M, sOps, fOps);
|
||||
}
|
||||
|
||||
// At M2=40 slow ops should be roughly 4× M=20 (quadratic growth)
|
||||
total++;
|
||||
long expectedSlowOps2 = (long) S * M2 * M2;
|
||||
if (sOps2 > fOps2 * 2 && sOps2 >= expectedSlowOps2) {
|
||||
System.out.printf(" freeswitch-0001 M=%d: PASS (slow=%,d >= S*M²=%,d, fast=%,d, ratio=%.1fx)%n",
|
||||
M2, sOps2, expectedSlowOps2, fOps2, (double)sOps2/fOps2);
|
||||
pass++;
|
||||
} else {
|
||||
System.out.printf(" freeswitch-0001 M=%d: FAIL (slow=%,d fast=%,d)%n", M2, sOps2, fOps2);
|
||||
}
|
||||
|
||||
// Quadratic growth check: sOps2 / sOps should be roughly (M2/M)² = 4
|
||||
total++;
|
||||
double growthRatio = (double) sOps2 / sOps;
|
||||
double expectedGrowth = (double)(M2 * M2) / (M * M);
|
||||
if (growthRatio >= expectedGrowth * 0.5) {
|
||||
System.out.printf(" freeswitch-0001 quadratic growth: PASS (ratio=%.1fx, expected~%.1fx)%n",
|
||||
growthRatio, expectedGrowth);
|
||||
pass++;
|
||||
} else {
|
||||
System.out.printf(" freeswitch-0001 quadratic growth: FAIL (ratio=%.1fx, expected~%.1fx)%n",
|
||||
growthRatio, expectedGrowth);
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println(pass + "/" + total + (pass == total ? " PASS" : " FAIL"));
|
||||
if (pass != total) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue