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:
russell@unturf.com 2026-03-26 19:48:18 -04:00
parent 0a580b313d
commit db29a08762
1311 changed files with 371202 additions and 1188 deletions

View file

@ -0,0 +1,264 @@
package unit;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Unit test for ODL-001: DevicesGroupRegistry CWE-407 defect.
*
* Models both defective (ArrayList/O(N)) and fixed (HashSet/O(1))
* implementations of DevicesGroupRegistry and measures comparison counts
* using an instrumented counter no wall-clock timing needed.
*
* Defect: isGroupPresent() calls ArrayList.contains() which does a linear scan.
* Called from the outer reconciliation loop over G groups with N already-tracked
* groups per node, producing O(G×N) total comparisons per switch reconnect.
*
* Fix: Replace List<Uint32> with Set<Uint32> (HashSet). contains() becomes O(1).
*/
public class OdlGroupRegistryTest {
// -----------------------------------------------------------------------
// Instrumented counter incremented by every element comparison
// -----------------------------------------------------------------------
static final AtomicLong comparisonCounter = new AtomicLong(0);
// -----------------------------------------------------------------------
// Instrumented value type (stand-in for Uint32)
// -----------------------------------------------------------------------
static class TrackedId {
final long value;
TrackedId(long value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
comparisonCounter.incrementAndGet();
if (this == o) return true;
if (!(o instanceof TrackedId)) return false;
return value == ((TrackedId) o).value;
}
@Override
public int hashCode() {
// Standard hash NOT instrumented; only equals() counts comparisons.
return Long.hashCode(value);
}
}
// -----------------------------------------------------------------------
// Defective registry ArrayList per node (O(N) contains)
// -----------------------------------------------------------------------
static class DefectiveRegistry {
private final Map<String, List<TrackedId>> deviceGroupMapping = new ConcurrentHashMap<>();
public boolean isGroupPresent(String nodeId, TrackedId groupId) {
List<TrackedId> groups = deviceGroupMapping.get(nodeId);
return groups != null && groups.contains(groupId); // O(N) linear scan
}
public void storeGroup(String nodeId, TrackedId groupId) {
deviceGroupMapping.computeIfAbsent(nodeId, k -> new ArrayList<>()).add(groupId);
}
}
// -----------------------------------------------------------------------
// Fixed registry HashSet per node (O(1) contains) // CWE-407 fix
// -----------------------------------------------------------------------
static class FixedRegistry {
private final Map<String, Set<TrackedId>> deviceGroupMapping = new ConcurrentHashMap<>();
public boolean isGroupPresent(String nodeId, TrackedId groupId) {
Set<TrackedId> groups = deviceGroupMapping.get(nodeId);
return groups != null && groups.contains(groupId); // O(1) hash lookup
}
public void storeGroup(String nodeId, TrackedId groupId) {
deviceGroupMapping.computeIfAbsent(nodeId, k -> new HashSet<>()).add(groupId); // CWE-407 fix
}
}
// -----------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------
/** Pre-populate a DefectiveRegistry with N already-tracked groups for nodeId. */
static DefectiveRegistry buildDefective(String nodeId, int n) {
DefectiveRegistry reg = new DefectiveRegistry();
for (int i = 0; i < n; i++) {
reg.storeGroup(nodeId, new TrackedId(i));
}
return reg;
}
/** Pre-populate a FixedRegistry with N already-tracked groups for nodeId. */
static FixedRegistry buildFixed(String nodeId, int n) {
FixedRegistry reg = new FixedRegistry();
for (int i = 0; i < n; i++) {
reg.storeGroup(nodeId, new TrackedId(i));
}
return reg;
}
/**
* Simulate the reconciliation loop: for each of G groups-to-install,
* call isGroupPresent() using a TrackedId that is NOT present (worst-case
* for ArrayList must scan full list before returning false).
*
* Returns the number of equals() comparisons recorded.
*/
static long runDefectiveReconciliation(DefectiveRegistry reg, String nodeId, int g) {
comparisonCounter.set(0);
long absent = 1_000_000L; // ids that do not exist in the registry
for (int i = 0; i < g; i++) {
reg.isGroupPresent(nodeId, new TrackedId(absent + i));
}
return comparisonCounter.get();
}
static long runFixedReconciliation(FixedRegistry reg, String nodeId, int g) {
comparisonCounter.set(0);
long absent = 1_000_000L;
for (int i = 0; i < g; i++) {
reg.isGroupPresent(nodeId, new TrackedId(absent + i));
}
return comparisonCounter.get();
}
// -----------------------------------------------------------------------
// Test methods
// -----------------------------------------------------------------------
/**
* testDefectiveIsQuadratic:
* With G=200 installs and N=200 tracked, the defective ArrayList path
* must produce at least G*N/2 comparisons (i.e. at least N comparisons
* per miss for half the cases on average in practice all N because
* the id is absent).
*/
static void testDefectiveIsQuadratic() {
int G = 200, N = 200;
String node = "openflow:1";
DefectiveRegistry reg = buildDefective(node, N);
long comparisons = runDefectiveReconciliation(reg, node, G);
long expected = (long) G * N; // each miss scans all N entries
assert comparisons >= expected :
"testDefectiveIsQuadratic FAIL: expected >= " + expected + " comparisons, got " + comparisons;
System.out.printf(" testDefectiveIsQuadratic PASS G=%d N=%d comparisons=%d (expected>=%d)%n",
G, N, comparisons, expected);
}
/**
* testFixedIsLinear:
* With G=200 installs and N=200 tracked, the fixed HashSet path must
* produce at most G*2 comparisons (each hash-bucket lookup may hit at
* most a handful of equals() calls in a well-distributed set; in practice
* usually 0 or 1 for absent keys with no hash collisions).
*
* Upper bound: G * 4 comparisons very generous for a 200-entry HashSet.
*/
static void testFixedIsLinear() {
int G = 200, N = 200;
String node = "openflow:1";
FixedRegistry reg = buildFixed(node, N);
long comparisons = runFixedReconciliation(reg, node, G);
long upperBound = (long) G * 4;
assert comparisons <= upperBound :
"testFixedIsLinear FAIL: expected <= " + upperBound + " comparisons, got " + comparisons;
System.out.printf(" testFixedIsLinear PASS G=%d N=%d comparisons=%d (expected<=%d)%n",
G, N, comparisons, upperBound);
}
/**
* testRatioAtScale:
* Measures defective vs fixed comparison counts at G=200, N=200 and
* asserts the ratio is at least 20x, confirming the algorithmic
* complexity improvement.
*/
static void testRatioAtScale() {
int G = 200, N = 200;
String node = "openflow:1";
DefectiveRegistry defReg = buildDefective(node, N);
long defectiveCount = runDefectiveReconciliation(defReg, node, G);
FixedRegistry fixReg = buildFixed(node, N);
long fixedCount = runFixedReconciliation(fixReg, node, G);
// Avoid division by zero: fixed may be 0 comparisons (all hash misses with no collisions)
double ratio = fixedCount > 0 ? (double) defectiveCount / fixedCount : defectiveCount;
assert ratio >= 20.0 :
"testRatioAtScale FAIL: ratio=" + ratio + " (defective=" + defectiveCount
+ " fixed=" + fixedCount + "), expected >= 20x";
System.out.printf(" testRatioAtScale PASS defective=%d fixed=%d ratio=%.1fx%n",
defectiveCount, fixedCount, ratio);
}
/**
* testCorrectnessDefective:
* Verifies that the defective implementation still produces correct
* boolean results the defect is performance only, not correctness.
*/
static void testCorrectnessDefective() {
String node = "openflow:1";
DefectiveRegistry reg = new DefectiveRegistry();
TrackedId g1 = new TrackedId(10);
TrackedId g2 = new TrackedId(20);
TrackedId g3 = new TrackedId(30);
reg.storeGroup(node, g1);
reg.storeGroup(node, g2);
comparisonCounter.set(0);
assert reg.isGroupPresent(node, new TrackedId(10)) : "testCorrectnessDefective FAIL: g1 should be present";
assert reg.isGroupPresent(node, new TrackedId(20)) : "testCorrectnessDefective FAIL: g2 should be present";
assert !reg.isGroupPresent(node, new TrackedId(30)) : "testCorrectnessDefective FAIL: g3 should be absent";
assert !reg.isGroupPresent("openflow:2", new TrackedId(10)) : "testCorrectnessDefective FAIL: wrong node";
System.out.println(" testCorrectnessDefective PASS present/absent/wrong-node all correct");
}
/**
* testCorrectnessFixed:
* Same correctness assertions for the fixed HashSet implementation.
*/
static void testCorrectnessFixed() {
String node = "openflow:1";
FixedRegistry reg = new FixedRegistry();
reg.storeGroup(node, new TrackedId(10));
reg.storeGroup(node, new TrackedId(20));
comparisonCounter.set(0);
assert reg.isGroupPresent(node, new TrackedId(10)) : "testCorrectnessFixed FAIL: g1 should be present";
assert reg.isGroupPresent(node, new TrackedId(20)) : "testCorrectnessFixed FAIL: g2 should be present";
assert !reg.isGroupPresent(node, new TrackedId(30)) : "testCorrectnessFixed FAIL: g3 should be absent";
assert !reg.isGroupPresent("openflow:2", new TrackedId(10)) : "testCorrectnessFixed FAIL: wrong node";
System.out.println(" testCorrectnessFixed PASS present/absent/wrong-node all correct");
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("ODL-001 DevicesGroupRegistry CWE-407 unit tests");
System.out.println("================================================");
testCorrectnessDefective();
testCorrectnessFixed();
testDefectiveIsQuadratic();
testFixedIsLinear();
testRatioAtScale();
System.out.println("================================================");
System.out.println("ALL TESTS PASSED");
}
}