bird-0003/0004 + frrouting-0003/0004: BGP community set ops O(N×M)/O(N²); OpenBGPD CLEAN; count 595→599

This commit is contained in:
russell@unturf.com 2026-03-27 22:30:39 -04:00
parent 3f8c5e6d3e
commit ff7bd27654
11 changed files with 671 additions and 4 deletions

View file

@ -0,0 +1,189 @@
package unit;
import java.util.*;
/**
* Models FRRouting community_uniq_sort and ecommunity_include defects.
*
* frrouting-0003: community_uniq_sort O(N²) contains check per element during dedup
* frrouting-0004: ecommunity_include O(E1×E2) nested loop cross-membership
*
* SLOW: O(N²) dedup / O(E1×E2) include
* FAST: O(N log N) sort+dedup / O(E1+E2) HashSet
*
* CWE-407: bgpd/bgp_community.c:143, bgpd/bgp_ecommunity.c:1534
*/
public class FrrCommunityUniqSortAlgorithmTest {
// -------------------------------------------------------------------------
// frrouting-0003: community_uniq_sort slow path
// -------------------------------------------------------------------------
static class SlowCommunitySort {
long scanOps = 0;
boolean include(int[] arr, int n, int val) {
for (int i = 0; i < n; i++) {
scanOps++;
if (arr[i] == val) return true;
}
return false;
}
/** O(N²): include-check per element then qsort at end */
int[] uniqSort(int[] input) {
int[] result = new int[input.length];
int size = 0;
for (int val : input) {
if (!include(result, size, val)) {
result[size++] = val;
}
}
int[] out = Arrays.copyOf(result, size);
Arrays.sort(out);
return out;
}
long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; }
}
static class FastCommunitySort {
long scanOps = 0;
/** O(N log N): sort first, then linear dedup */
int[] uniqSort(int[] input) {
int[] sorted = Arrays.copyOf(input, input.length);
Arrays.sort(sorted); // O(N log N) counted implicitly
// Count ops for linear dedup pass
int out = 0;
int[] result = new int[sorted.length];
for (int i = 0; i < sorted.length; i++) {
scanOps++;
if (i == 0 || sorted[i] != sorted[i - 1]) {
result[out++] = sorted[i];
}
}
return Arrays.copyOf(result, out);
}
long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; }
}
// -------------------------------------------------------------------------
// frrouting-0004: ecommunity_include slow path
// -------------------------------------------------------------------------
static class SlowEcommunityInclude {
long cmpOps = 0;
boolean include(long[] e1, long[] e2) {
for (long v1 : e1) {
for (long v2 : e2) {
cmpOps++;
if (v1 == v2) return true;
}
}
return false;
}
long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; }
}
static class FastEcommunityInclude {
long cmpOps = 0;
boolean include(long[] e1, long[] e2) {
Set<Long> set = new HashSet<>();
for (long v : e2) { set.add(v); cmpOps++; }
for (long v : e1) {
cmpOps++;
if (set.contains(v)) return true;
}
return false;
}
long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; }
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
static int[] makeWithDups(int uniqueVals, int totalSize) {
Random rng = new Random(42);
int[] arr = new int[totalSize];
for (int i = 0; i < totalSize; i++) arr[i] = rng.nextInt(uniqueVals);
return arr;
}
static long[] makeLong(int n, int offset) {
long[] a = new long[n];
for (int i = 0; i < n; i++) a[i] = offset + i;
return a;
}
public static void main(String[] args) {
SlowCommunitySort slowSort = new SlowCommunitySort();
FastCommunitySort fastSort = new FastCommunitySort();
SlowEcommunityInclude slowInc = new SlowEcommunityInclude();
FastEcommunityInclude fastInc = new FastEcommunityInclude();
int passed = 0, total = 0;
System.out.println("=== frrouting-0003: community_uniq_sort O(N²) ===");
int[] sizes = {50, 100, 200, 500};
for (int n : sizes) {
int[] input = makeWithDups(n / 2, n); // ~50% dups
long s = slowSort.ops(input);
long f = fastSort.ops(input);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 3.0;
System.out.printf("N=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
n, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both produce same sorted unique result
int[] input = makeWithDups(50, 100);
int[] sr = slowSort.uniqSort(input);
int[] fr = fastSort.uniqSort(input);
total++;
boolean correct1 = Arrays.equals(sr, fr);
System.out.printf("uniqSort correctness: %s%n", correct1 ? "PASS" : "FAIL");
if (correct1) passed++;
System.out.println("=== frrouting-0004: ecommunity_include O(E1×E2) ===");
int[] esizes = {20, 50, 100, 200};
for (int e : esizes) {
long[] e1 = makeLong(e, 0);
long[] e2 = makeLong(e, e); // disjoint forces full scan
long s = slowInc.ops(e1, e2);
long f = fastInc.ops(e1, e2);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 3.0;
System.out.printf("E1=%3d E2=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
e, e, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: include detects overlap
long[] a = makeLong(50, 0), b = makeLong(50, 25); // overlap at 25-49
total++;
boolean correct2 = slowInc.include(a, b) == fastInc.include(a, b);
System.out.printf("ecommunity_include correctness (overlap): %s%n",
correct2 ? "PASS" : "FAIL");
if (correct2) passed++;
total++;
long[] c = makeLong(50, 100); // disjoint
boolean correct3 = slowInc.include(a, c) == fastInc.include(a, c);
System.out.printf("ecommunity_include correctness (disjoint): %s%n",
correct3 ? "PASS" : "FAIL");
if (correct3) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}