undefect. CWE-407 — 92 sites, 42 ecosystems
B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
This commit is contained in:
parent
0a580b313d
commit
db29a08762
1311 changed files with 371202 additions and 1188 deletions
310
defects/bird/unit/BirdRoutingTest.java
Normal file
310
defects/bird/unit/BirdRoutingTest.java
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* BirdRoutingTest — unit tests for BIRD CWE-407 defects.
|
||||
*
|
||||
* BIRD-001 (HIGH): OSPF SPF candidate list insertion sort O(E*V) vs heap O((E+V) log V).
|
||||
* BIRD-002 (MEDIUM): BGP community linear scan O(n) vs bsearch O(log n).
|
||||
*
|
||||
* No external dependencies. Run with: java -ea unit.BirdRoutingTest
|
||||
*/
|
||||
public class BirdRoutingTest {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Instrumented comparison counter
|
||||
// -------------------------------------------------------------------------
|
||||
static long comparisons;
|
||||
|
||||
static void resetComparisons() { comparisons = 0; }
|
||||
static long getComparisons() { return comparisons; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// BIRD-001 model: Dijkstra with instrumented candidate list
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Defective: sorted LinkedList insertion — O(n) per insert (mirrors WALK_LIST).
|
||||
*/
|
||||
static int[] dijkstraLinkedList(int[][] adj, int src) {
|
||||
int V = adj.length;
|
||||
int[] dist = new int[V];
|
||||
Arrays.fill(dist, Integer.MAX_VALUE);
|
||||
dist[src] = 0;
|
||||
|
||||
// Candidate list: sorted ascending by distance — insertion sort like BIRD
|
||||
LinkedList<Integer> cand = new LinkedList<>();
|
||||
cand.add(src);
|
||||
|
||||
while (!cand.isEmpty()) {
|
||||
int u = cand.removeFirst();
|
||||
for (int v = 0; v < V; v++) {
|
||||
if (adj[u][v] == 0) continue;
|
||||
int nd = dist[u] + adj[u][v];
|
||||
if (nd < dist[v]) {
|
||||
dist[v] = nd;
|
||||
// Remove existing entry if present — mirrors rem_node
|
||||
cand.remove(Integer.valueOf(v));
|
||||
// Insertion sort: walk list to find position — O(n), CWE-407 defect
|
||||
ListIterator<Integer> it = cand.listIterator();
|
||||
boolean inserted = false;
|
||||
while (it.hasNext()) {
|
||||
comparisons++; // instrument
|
||||
int cur = it.next();
|
||||
if (dist[cur] > nd) {
|
||||
it.previous();
|
||||
it.add(v);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inserted) cand.addLast(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed: PriorityQueue min-heap — O(log n) per insert (mirrors HEAP_INSERT).
|
||||
*/
|
||||
static int[] dijkstraHeap(int[][] adj, int src) {
|
||||
int V = adj.length;
|
||||
int[] dist = new int[V];
|
||||
Arrays.fill(dist, Integer.MAX_VALUE);
|
||||
dist[src] = 0;
|
||||
|
||||
// min-heap keyed on distance — mirrors cand_push / cand_pop
|
||||
PriorityQueue<int[]> heap = new PriorityQueue<>(Comparator.comparingInt(e -> e[1]));
|
||||
heap.offer(new int[]{src, 0});
|
||||
|
||||
while (!heap.isEmpty()) {
|
||||
int[] top = heap.poll();
|
||||
int u = top[0], d = top[1];
|
||||
if (d > dist[u]) continue; // stale entry
|
||||
for (int v = 0; v < V; v++) {
|
||||
if (adj[u][v] == 0) continue;
|
||||
int nd = dist[u] + adj[u][v];
|
||||
if (nd < dist[v]) {
|
||||
dist[v] = nd;
|
||||
comparisons++; // one heap comparison per insertion (amortised)
|
||||
heap.offer(new int[]{v, nd});
|
||||
}
|
||||
}
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
// Build a random connected sparse graph (adjacency matrix)
|
||||
static int[][] buildGraph(int V, int E, Random rng) {
|
||||
int[][] adj = new int[V][V];
|
||||
// Guarantee connectivity: chain 0→1→2→…→V-1
|
||||
for (int i = 0; i < V - 1; i++) {
|
||||
int w = 1 + rng.nextInt(10);
|
||||
adj[i][i + 1] = w;
|
||||
adj[i + 1][i] = w;
|
||||
}
|
||||
// Add random extra edges
|
||||
int added = V - 1;
|
||||
while (added < E) {
|
||||
int u = rng.nextInt(V);
|
||||
int v = rng.nextInt(V);
|
||||
if (u != v && adj[u][v] == 0) {
|
||||
int w = 1 + rng.nextInt(10);
|
||||
adj[u][v] = w;
|
||||
adj[v][u] = w;
|
||||
added++;
|
||||
}
|
||||
}
|
||||
return adj;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// BIRD-002 model: community membership linear scan vs bsearch
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Defective: linear scan — O(n), mirrors int_set_contains before patch.
|
||||
*/
|
||||
static boolean communityContainsLinear(int[] communities, int val) {
|
||||
for (int c : communities) {
|
||||
comparisons++;
|
||||
if (c == val) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed: binary search — O(log n), mirrors bsearch after patch.
|
||||
* Requires sorted input (enforced on creation by qsort in the C patch).
|
||||
*/
|
||||
static boolean communityContainsBsearch(int[] sorted, int val) {
|
||||
int lo = 0, hi = sorted.length - 1;
|
||||
while (lo <= hi) {
|
||||
comparisons++;
|
||||
int mid = (lo + hi) >>> 1;
|
||||
if (sorted[mid] == val) return true;
|
||||
if (sorted[mid] < val) lo = mid + 1;
|
||||
else hi = mid - 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Test methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Test 1: Defective Dijkstra produces correct shortest distances.
|
||||
*/
|
||||
static void testLinkedListDijkstraCorrectness() {
|
||||
int[][] adj = {
|
||||
{0, 4, 0, 0, 8},
|
||||
{4, 0, 8, 0, 0},
|
||||
{0, 8, 0, 7, 0},
|
||||
{0, 0, 7, 0, 9},
|
||||
{8, 0, 0, 9, 0},
|
||||
};
|
||||
resetComparisons();
|
||||
int[] dist = dijkstraLinkedList(adj, 0);
|
||||
assert dist[0] == 0 : "BIRD-001 defective: dist[0] wrong";
|
||||
assert dist[1] == 4 : "BIRD-001 defective: dist[1] wrong";
|
||||
assert dist[2] == 12 : "BIRD-001 defective: dist[2] wrong";
|
||||
assert dist[3] == 17 : "BIRD-001 defective: dist[3] wrong";
|
||||
assert dist[4] == 8 : "BIRD-001 defective: dist[4] wrong";
|
||||
System.out.println("PASS test1_linkedlist_dijkstra_correctness");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: Fixed (heap) Dijkstra produces identical correct shortest distances.
|
||||
*/
|
||||
static void testHeapDijkstraCorrectness() {
|
||||
int[][] adj = {
|
||||
{0, 4, 0, 0, 8},
|
||||
{4, 0, 8, 0, 0},
|
||||
{0, 8, 0, 7, 0},
|
||||
{0, 0, 7, 0, 9},
|
||||
{8, 0, 0, 9, 0},
|
||||
};
|
||||
resetComparisons();
|
||||
int[] dist = dijkstraHeap(adj, 0);
|
||||
assert dist[0] == 0 : "BIRD-001 fixed: dist[0] wrong";
|
||||
assert dist[1] == 4 : "BIRD-001 fixed: dist[1] wrong";
|
||||
assert dist[2] == 12 : "BIRD-001 fixed: dist[2] wrong";
|
||||
assert dist[3] == 17 : "BIRD-001 fixed: dist[3] wrong";
|
||||
assert dist[4] == 8 : "BIRD-001 fixed: dist[4] wrong";
|
||||
System.out.println("PASS test2_heap_dijkstra_correctness");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: At V=200 / E=600, heap comparison count < linked-list comparison count
|
||||
* by at least 5x. Models O(E*V) vs O((E+V) log V).
|
||||
*/
|
||||
static void testDijkstraComplexityRatio() {
|
||||
final int V = 200, E = 600;
|
||||
Random rng = new Random(42L);
|
||||
int[][] adj = buildGraph(V, E, rng);
|
||||
|
||||
resetComparisons();
|
||||
dijkstraLinkedList(adj, 0);
|
||||
long listComps = getComparisons();
|
||||
|
||||
resetComparisons();
|
||||
dijkstraHeap(adj, 0);
|
||||
long heapComps = getComparisons();
|
||||
|
||||
double ratio = (double) listComps / heapComps;
|
||||
System.out.printf(
|
||||
"BIRD-001 V=%d E=%d: list_comparisons=%d heap_comparisons=%d ratio=%.1fx%n",
|
||||
V, E, listComps, heapComps, ratio);
|
||||
|
||||
assert ratio > 5.0 : String.format(
|
||||
"BIRD-001 ratio %.1fx < 5x threshold — heap speedup not demonstrated", ratio);
|
||||
System.out.println("PASS test3_dijkstra_complexity_ratio");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Community linear scan and bsearch agree on membership for random queries.
|
||||
*/
|
||||
static void testCommunityContainsCorrectness() {
|
||||
int C = 100;
|
||||
int[] communities = new int[C];
|
||||
Random rng = new Random(7L);
|
||||
for (int i = 0; i < C; i++) communities[i] = rng.nextInt(65536);
|
||||
int[] sorted = communities.clone();
|
||||
Arrays.sort(sorted);
|
||||
|
||||
// Test membership for 50 known-present and 50 random values
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int val = communities[rng.nextInt(C)]; // definitely present
|
||||
boolean lin = communityContainsLinear(communities, val);
|
||||
boolean bin = communityContainsBsearch(sorted, val);
|
||||
assert lin == bin : "BIRD-002 mismatch on present value " + val;
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int val = 65536 + rng.nextInt(65536); // out of range — absent
|
||||
boolean lin = communityContainsLinear(communities, val);
|
||||
boolean bin = communityContainsBsearch(sorted, val);
|
||||
assert lin == bin : "BIRD-002 mismatch on absent value " + val;
|
||||
}
|
||||
System.out.println("PASS test4_community_contains_correctness");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 5: At C=100 communities, 1000 lookups — bsearch uses >5x fewer comparisons.
|
||||
*/
|
||||
static void testCommunityComplexityRatio() {
|
||||
final int C = 100, LOOKUPS = 1000;
|
||||
Random rng = new Random(13L);
|
||||
int[] communities = new int[C];
|
||||
for (int i = 0; i < C; i++) communities[i] = i * 3; // deterministic, no duplicates
|
||||
int[] sorted = communities.clone();
|
||||
Arrays.sort(sorted);
|
||||
|
||||
resetComparisons();
|
||||
for (int i = 0; i < LOOKUPS; i++) {
|
||||
int val = rng.nextInt(C * 4); // mix of hits and misses
|
||||
communityContainsLinear(communities, val);
|
||||
}
|
||||
long linearComps = getComparisons();
|
||||
|
||||
resetComparisons();
|
||||
for (int i = 0; i < LOOKUPS; i++) {
|
||||
rng = new Random(13L); // same seed — identical query sequence
|
||||
int val = rng.nextInt(C * 4);
|
||||
communityContainsBsearch(sorted, val);
|
||||
}
|
||||
|
||||
// Re-run with same RNG sequence for a fair comparison
|
||||
rng = new Random(13L);
|
||||
resetComparisons();
|
||||
for (int i = 0; i < LOOKUPS; i++) {
|
||||
int val = rng.nextInt(C * 4);
|
||||
communityContainsBsearch(sorted, val);
|
||||
}
|
||||
long bsearchComps = getComparisons();
|
||||
|
||||
double ratio = (double) linearComps / bsearchComps;
|
||||
System.out.printf(
|
||||
"BIRD-002 C=%d lookups=%d: linear_comparisons=%d bsearch_comparisons=%d ratio=%.1fx%n",
|
||||
C, LOOKUPS, linearComps, bsearchComps, ratio);
|
||||
|
||||
assert ratio > 5.0 : String.format(
|
||||
"BIRD-002 ratio %.1fx < 5x threshold — bsearch speedup not demonstrated", ratio);
|
||||
System.out.println("PASS test5_community_complexity_ratio");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Entry point
|
||||
// -------------------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== BirdRoutingTest ===");
|
||||
testLinkedListDijkstraCorrectness();
|
||||
testHeapDijkstraCorrectness();
|
||||
testDijkstraComplexityRatio();
|
||||
testCommunityContainsCorrectness();
|
||||
testCommunityComplexityRatio();
|
||||
System.out.println("=== ALL TESTS PASSED ===");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue